├── .gitignore ├── examples ├── create-react-app │ ├── .gitignore │ ├── custom.config.js │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.js │ │ ├── dist.css │ │ └── index.js └── nextjs │ ├── .babelrc.js │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── pages │ ├── _app.js │ ├── about.js │ └── index.js │ └── public │ ├── dist.css │ ├── favicon.ico │ └── vercel.svg ├── package.json ├── packages └── css-out-js │ ├── babel.js │ ├── index.js │ ├── logo.png │ ├── package.json │ └── readme.md ├── readme.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | 133 | packages/system-css/out.css# Logs 134 | logs 135 | *.log 136 | npm-debug.log* 137 | yarn-debug.log* 138 | yarn-error.log* 139 | lerna-debug.log* 140 | .pnpm-debug.log* 141 | 142 | # Diagnostic reports (https://nodejs.org/api/report.html) 143 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 144 | 145 | # Runtime data 146 | pids 147 | *.pid 148 | *.seed 149 | *.pid.lock 150 | 151 | # Directory for instrumented libs generated by jscoverage/JSCover 152 | lib-cov 153 | 154 | # Coverage directory used by tools like istanbul 155 | coverage 156 | *.lcov 157 | 158 | # nyc test coverage 159 | .nyc_output 160 | 161 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 162 | .grunt 163 | 164 | # Bower dependency directory (https://bower.io/) 165 | bower_components 166 | 167 | # node-waf configuration 168 | .lock-wscript 169 | 170 | # Compiled binary addons (https://nodejs.org/api/addons.html) 171 | build/Release 172 | 173 | # Dependency directories 174 | node_modules/ 175 | jspm_packages/ 176 | 177 | # Snowpack dependency directory (https://snowpack.dev/) 178 | web_modules/ 179 | 180 | # TypeScript cache 181 | *.tsbuildinfo 182 | 183 | # Optional npm cache directory 184 | .npm 185 | 186 | # Optional eslint cache 187 | .eslintcache 188 | 189 | # Optional stylelint cache 190 | .stylelintcache 191 | 192 | # Microbundle cache 193 | .rpt2_cache/ 194 | .rts2_cache_cjs/ 195 | .rts2_cache_es/ 196 | .rts2_cache_umd/ 197 | 198 | # Optional REPL history 199 | .node_repl_history 200 | 201 | # Output of 'npm pack' 202 | *.tgz 203 | 204 | # Yarn Integrity file 205 | .yarn-integrity 206 | 207 | # dotenv environment variable files 208 | .env 209 | .env.development.local 210 | .env.test.local 211 | .env.production.local 212 | .env.local 213 | 214 | # parcel-bundler cache (https://parceljs.org/) 215 | .cache 216 | .parcel-cache 217 | 218 | # Next.js build output 219 | .next 220 | out 221 | 222 | # Nuxt.js build / generate output 223 | .nuxt 224 | dist 225 | 226 | # Gatsby files 227 | .cache/ 228 | # Comment in the public line in if your project uses Gatsby and not Next.js 229 | # https://nextjs.org/blog/next-9-1#public-directory-support 230 | # public 231 | 232 | # vuepress build output 233 | .vuepress/dist 234 | 235 | # vuepress v2.x temp and cache directory 236 | .temp 237 | .cache 238 | 239 | # Docusaurus cache and generated files 240 | .docusaurus 241 | 242 | # Serverless directories 243 | .serverless/ 244 | 245 | # FuseBox cache 246 | .fusebox/ 247 | 248 | # DynamoDB Local files 249 | .dynamodb/ 250 | 251 | # TernJS port file 252 | .tern-port 253 | 254 | # Stores VSCode versions used for testing VSCode extensions 255 | .vscode-test 256 | 257 | # yarn v2 258 | .yarn/cache 259 | .yarn/unplugged 260 | .yarn/build-state.yml 261 | .yarn/install-state.gz 262 | .pnp.* 263 | # Logs 264 | logs 265 | *.log 266 | npm-debug.log* 267 | yarn-debug.log* 268 | yarn-error.log* 269 | lerna-debug.log* 270 | .pnpm-debug.log* 271 | 272 | # Diagnostic reports (https://nodejs.org/api/report.html) 273 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 274 | 275 | # Runtime data 276 | pids 277 | *.pid 278 | *.seed 279 | *.pid.lock 280 | 281 | # Directory for instrumented libs generated by jscoverage/JSCover 282 | lib-cov 283 | 284 | # Coverage directory used by tools like istanbul 285 | coverage 286 | *.lcov 287 | 288 | # nyc test coverage 289 | .nyc_output 290 | 291 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 292 | .grunt 293 | 294 | # Bower dependency directory (https://bower.io/) 295 | bower_components 296 | 297 | # node-waf configuration 298 | .lock-wscript 299 | 300 | # Compiled binary addons (https://nodejs.org/api/addons.html) 301 | build/Release 302 | 303 | # Dependency directories 304 | node_modules/ 305 | jspm_packages/ 306 | 307 | # Snowpack dependency directory (https://snowpack.dev/) 308 | web_modules/ 309 | 310 | # TypeScript cache 311 | *.tsbuildinfo 312 | 313 | # Optional npm cache directory 314 | .npm 315 | 316 | # Optional eslint cache 317 | .eslintcache 318 | 319 | # Optional stylelint cache 320 | .stylelintcache 321 | 322 | # Microbundle cache 323 | .rpt2_cache/ 324 | .rts2_cache_cjs/ 325 | .rts2_cache_es/ 326 | .rts2_cache_umd/ 327 | 328 | # Optional REPL history 329 | .node_repl_history 330 | 331 | # Output of 'npm pack' 332 | *.tgz 333 | 334 | # Yarn Integrity file 335 | .yarn-integrity 336 | 337 | # dotenv environment variable files 338 | .env 339 | .env.development.local 340 | .env.test.local 341 | .env.production.local 342 | .env.local 343 | 344 | # parcel-bundler cache (https://parceljs.org/) 345 | .cache 346 | .parcel-cache 347 | 348 | # Next.js build output 349 | .next 350 | out 351 | 352 | # Nuxt.js build / generate output 353 | .nuxt 354 | dist 355 | 356 | # Gatsby files 357 | .cache/ 358 | # Comment in the public line in if your project uses Gatsby and not Next.js 359 | # https://nextjs.org/blog/next-9-1#public-directory-support 360 | # public 361 | 362 | # vuepress build output 363 | .vuepress/dist 364 | 365 | # vuepress v2.x temp and cache directory 366 | .temp 367 | .cache 368 | 369 | # Docusaurus cache and generated files 370 | .docusaurus 371 | 372 | # Serverless directories 373 | .serverless/ 374 | 375 | # FuseBox cache 376 | .fusebox/ 377 | 378 | # DynamoDB Local files 379 | .dynamodb/ 380 | 381 | # TernJS port file 382 | .tern-port 383 | 384 | # Stores VSCode versions used for testing VSCode extensions 385 | .vscode-test 386 | 387 | # yarn v2 388 | .yarn/cache 389 | .yarn/unplugged 390 | .yarn/build-state.yml 391 | .yarn/install-state.gz 392 | .pnp.* 393 | -------------------------------------------------------------------------------- /examples/create-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/create-react-app/custom.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | babel: { 3 | plugins: [['css-out-js/babel', { path: 'src/dist.css' }]] 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /examples/create-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-create-react-app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "cracoConfig": "custom.config.js", 6 | "dependencies": { 7 | "@craco/craco": "^6.4.3", 8 | "css-out-js": "0.0.1", 9 | "react": "^18.0.0", 10 | "react-dom": "^18.0.0", 11 | "react-scripts": "5.0.0" 12 | }, 13 | "scripts": { 14 | "start": "craco start", 15 | "build": "craco build" 16 | }, 17 | "eslintConfig": { 18 | "extends": [ 19 | "react-app", 20 | "react-app/jest" 21 | ] 22 | }, 23 | "browserslist": { 24 | "production": [ 25 | ">0.2%", 26 | "not dead", 27 | "not op_mini all" 28 | ], 29 | "development": [ 30 | "last 1 chrome version", 31 | "last 1 firefox version", 32 | "last 1 safari version" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/create-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddharthkp/css-out-js/3d43e1c84fdc784008e9442e6e0e02391afa4b7a/examples/create-react-app/public/favicon.ico -------------------------------------------------------------------------------- /examples/create-react-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | React App 13 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/create-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddharthkp/css-out-js/3d43e1c84fdc784008e9442e6e0e02391afa4b7a/examples/create-react-app/public/logo192.png -------------------------------------------------------------------------------- /examples/create-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddharthkp/css-out-js/3d43e1c84fdc784008e9442e6e0e02391afa4b7a/examples/create-react-app/public/logo512.png -------------------------------------------------------------------------------- /examples/create-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/create-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/create-react-app/src/App.js: -------------------------------------------------------------------------------- 1 | import { css } from 'css-out-js'; 2 | import './dist.css'; 3 | 4 | const Title = () => { 5 | /** 6 | * css takes styles and returns a className 7 | * 8 | * if you use the babel plugin 9 | * 1. it extracts the styles into a css file and 10 | * 2. replaces the function call with className string 11 | * 3. removes the runtime import for system-css 12 | * 13 | */ 14 | return

Hello!

; 15 | }; 16 | 17 | const LearnMore = (props) => { 18 | // if there are dynamic styles 19 | const className = css({ color: props.color }); 20 | 21 | return ( 22 | 23 | Learn React 24 | 25 | ); 26 | }; 27 | 28 | const FONT_FAMILY = 'sans-serif'; 29 | 30 | function Home() { 31 | const className = css({ 32 | color: 'tomato', 33 | fontFamily: FONT_FAMILY 34 | }); 35 | 36 | return ( 37 |
38 | 39 | <p> 40 | Edit <code>src/App.js</code> and save to reload. 41 | </p> 42 | <LearnMore color="teal" /> 43 | </div> 44 | ); 45 | } 46 | 47 | export default Home; 48 | -------------------------------------------------------------------------------- /examples/create-react-app/src/dist.css: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by css-out-js ✨ 3 | Don't edit this file directly :) 4 | */ 5 | 6 | .Title-1bdbzov { 7 | font-size: 2em 8 | } 9 | .LearnMore-1anp2xb { 10 | color: var(--props-color-1trdzir) 11 | } 12 | .Home-2b9glr { 13 | color: tomato; font-family: var(--font_family-1o4m8p2) 14 | } -------------------------------------------------------------------------------- /examples/create-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | 4 | import App from './App'; 5 | 6 | const root = createRoot(document.getElementById('root')); 7 | 8 | root.render( 9 | <React.StrictMode> 10 | <App /> 11 | </React.StrictMode> 12 | ); 13 | -------------------------------------------------------------------------------- /examples/nextjs/.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['next/babel'], 3 | plugins: [['css-out-js/babel', { path: 'public/dist.css' }]] 4 | }; 5 | -------------------------------------------------------------------------------- /examples/nextjs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /examples/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | -------------------------------------------------------------------------------- /examples/nextjs/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /examples/nextjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-next", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "next": "12.1.4", 12 | "react": "18.0.0", 13 | "react-dom": "18.0.0", 14 | "css-out-js": "0.0.1" 15 | }, 16 | "devDependencies": { 17 | "eslint": "8.12.0", 18 | "eslint-config-next": "12.1.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/nextjs/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../public/dist.css'; 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return <Component {...pageProps} />; 5 | } 6 | 7 | export default MyApp; 8 | -------------------------------------------------------------------------------- /examples/nextjs/pages/about.js: -------------------------------------------------------------------------------- 1 | function About() { 2 | return ( 3 | <main> 4 | <h1>About</h1> 5 | <p>no thanks, i'm shy</p> 6 | </main> 7 | ); 8 | } 9 | 10 | export default About; 11 | -------------------------------------------------------------------------------- /examples/nextjs/pages/index.js: -------------------------------------------------------------------------------- 1 | import { css } from 'css-out-js'; 2 | 3 | const Title = () => { 4 | /** 5 | * css takes styles and returns a className 6 | * 7 | * if you use the babel plugin 8 | * 1. it extracts the styles into a css file and 9 | * 2. replaces the function call with className string 10 | * 3. removes the runtime import for css-out-js 11 | * 12 | */ 13 | return <h1 className={css({ fontSize: '2em' })}>Hello!</h1>; 14 | }; 15 | 16 | const LearnMore = (props) => { 17 | // if there are dynamic styles 18 | const className = css({ color: props.color }); 19 | 20 | return ( 21 | <a className={className} href="https://reactjs.org" target="_blank" rel="noopener noreferrer"> 22 | Learn React 23 | </a> 24 | ); 25 | }; 26 | 27 | const FONT_FAMILY = 'sans-serif'; 28 | 29 | function Home() { 30 | const className = css({ 31 | color: 'tomato', 32 | fontFamily: FONT_FAMILY 33 | }); 34 | 35 | return ( 36 | <div className={className}> 37 | <Title /> 38 | <p> 39 | Edit <code>src/App.js</code> and save to reload. 40 | </p> 41 | <LearnMore color="teal" /> 42 | </div> 43 | ); 44 | } 45 | 46 | export default Home; 47 | -------------------------------------------------------------------------------- /examples/nextjs/public/dist.css: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by css-out-js ✨ 3 | Don't edit this file directly :) 4 | */ 5 | 6 | .Title-1bdbzov { 7 | font-size: 2em 8 | } 9 | .LearnMore-qyvrpz { 10 | color: var(--props-color-1i957tr) 11 | } 12 | .Home-1khwc8n { 13 | color: tomato; font-family: var(--font_family-mgwka) 14 | } -------------------------------------------------------------------------------- /examples/nextjs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddharthkp/css-out-js/3d43e1c84fdc784008e9442e6e0e02391afa4b7a/examples/nextjs/public/favicon.ico -------------------------------------------------------------------------------- /examples/nextjs/public/vercel.svg: -------------------------------------------------------------------------------- 1 | <svg width="283" height="64" viewBox="0 0 283 64" fill="none" 2 | xmlns="http://www.w3.org/2000/svg"> 3 | <path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/> 4 | </svg> -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspace", 3 | "private": "true", 4 | "workspaces": [ 5 | "packages/*", 6 | "examples/*" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/css-out-js/babel.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const nodePath = require('path'); 3 | const { types } = require('@babel/core'); 4 | const { declare } = require('@babel/helper-plugin-utils'); 5 | const { default: hash } = require('@emotion/hash'); 6 | const { string: toString } = require('to-style'); 7 | 8 | const prefix = 'css'; 9 | let ensuredOutFileExists = false; 10 | 11 | /** ignore, dev mode debugger */ 12 | const log = (message = '') => { 13 | fs.appendFileSync(nodePath.join(__dirname, 'debug.log'), JSON.stringify(message, null, 2) + '\n', 'utf8'); 14 | }; 15 | 16 | const cssMap = new Map(); 17 | 18 | const appendCSS = ({ outFilePath, className, stringifiedStyles }) => { 19 | // already inserted 20 | if (cssMap.get(className) === stringifiedStyles) return; 21 | 22 | cssMap.set(className, stringifiedStyles); 23 | 24 | let contents = `/* 25 | This file is generated by css-out-js ✨ 26 | Don't edit this file directly :) 27 | */ 28 | `; 29 | cssMap.forEach((value, key) => { 30 | contents += ` 31 | .${key} { 32 | ${value} 33 | }`; 34 | }); 35 | 36 | // flush 37 | fs.writeFileSync(outFilePath, contents); 38 | }; 39 | 40 | module.exports = declare((api) => { 41 | api.assertVersion(7); 42 | 43 | const visitor = { 44 | Program: { 45 | enter(path, state) { 46 | const outFilePath = state.opts.path 47 | ? nodePath.join(state.cwd, state.opts.path) 48 | : nodePath.join(__dirname, 'out.css'); // should this be state.cwd and not __dirname? 49 | 50 | state.file.set('outFilePath', outFilePath); 51 | if (state.opts.addImport) state.file.set('moduleSpecifier', outFilePath); 52 | 53 | // reset outfile 54 | if (!ensuredOutFileExists) { 55 | fs.writeFileSync(outFilePath, ''); 56 | ensuredOutFileExists = true; 57 | } 58 | } 59 | }, 60 | CallExpression(path, state) { 61 | const { callee, arguments: args } = path.node; 62 | 63 | if (callee.name !== 'css') return; 64 | 65 | const [arg] = args; 66 | 67 | // if a className string passed, replace expression with className 68 | if (arg.type === 'Identifier') path.replaceWith(arg); 69 | 70 | if (arg.type !== 'ObjectExpression') return; 71 | 72 | const styles = {}; 73 | 74 | arg.properties.forEach((property) => { 75 | if (['NumericLiteral', 'StringLiteral'].includes(property.value.type)) { 76 | styles[property.key.name] = property.value.value; 77 | } else if (types.isExpression(property.value) || types.isIdentifier(property.value)) { 78 | const rawCode = state.file.code.slice(property.value.start, property.value.end); 79 | 80 | // we wan't an unique custom variable that is 81 | const uniq = hash(state.filename + rawCode); 82 | const cssVarName = '--' + rawCode.replace(/\./g, '-').toLowerCase() + '-' + uniq; // example: props-color-ioan3s 83 | 84 | styles[property.key.name] = `var(${cssVarName})`; 85 | 86 | if (path.container.type === 'JSXExpressionContainer') { 87 | setCSSVarInStyle(path, cssVarName, property); 88 | } else { 89 | injectRuntimeExpressionToSetCSSVar(path, cssVarName, property); 90 | } 91 | } 92 | }); 93 | 94 | const stringifiedStyles = toString(styles); 95 | 96 | /** 97 | * Add component name to the className for better naming 98 | * 99 | * TODO: for runtime enabled, pass componentName as the second parameter 100 | * in case there is a runtime so that the output matches 101 | */ 102 | let componentName; 103 | 104 | const functionParent = path.getFunctionParent(); 105 | if (types.isArrowFunctionExpression(functionParent)) componentName = functionParent.parent.id.name; 106 | else if (types.isFunctionDeclaration(functionParent)) componentName = functionParent.node.id.name; 107 | 108 | const className = (componentName || prefix) + '-' + hash(stringifiedStyles); 109 | 110 | /* Write css into outFile */ 111 | const outFilePath = state.file.get('outFilePath'); 112 | appendCSS({ outFilePath, className, stringifiedStyles }); 113 | 114 | /* Replace the function call with the className string */ 115 | path.replaceWith(types.stringLiteral(className)); 116 | }, 117 | ImportDeclaration(path, state) { 118 | if (state.opts.keepRuntime) return; 119 | 120 | if (path.node.source.value === 'css-out-js') { 121 | const moduleSpecifier = state.file.get('moduleSpecifier'); 122 | if (moduleSpecifier) { 123 | path.insertAfter(types.importDeclaration([], types.stringLiteral(moduleSpecifier))); 124 | } 125 | path.remove(); 126 | } 127 | } 128 | }; 129 | 130 | return { name: 'babel-plugin-css-out-js', visitor }; 131 | }); 132 | 133 | const setCSSVarInStyle = (path, cssVar, property) => { 134 | const jsxOpeningElement = path.findParent((path) => path.isJSXOpeningElement()); 135 | const styleAttr = jsxOpeningElement.node.attributes.find((attr) => attr.name.name === 'style'); 136 | 137 | const keyVal = types.objectProperty(types.stringLiteral(cssVar), property.value); 138 | 139 | if (styleAttr) { 140 | styleAttr.value.expression.properties.push(keyVal); 141 | } else { 142 | jsxOpeningElement.node.attributes.push( 143 | types.jsxAttribute(types.jsxIdentifier('style'), types.jsxExpressionContainer(types.objectExpression([keyVal]))) 144 | ); 145 | } 146 | }; 147 | 148 | const injectRuntimeExpressionToSetCSSVar = (path, cssVar, property) => { 149 | // TODO: test if (typeof document !== 'undefined') 150 | const test = types.binaryExpression( 151 | '!==', 152 | types.unaryExpression('typeof', types.identifier('document')), 153 | types.stringLiteral('undefined') 154 | ); 155 | 156 | // consonent: document.documentElement.style.setProperty('--custom', value); 157 | const consonent = types.expressionStatement( 158 | types.callExpression( 159 | types.memberExpression( 160 | types.memberExpression( 161 | types.memberExpression(types.identifier('document'), types.identifier('documentElement')), 162 | types.identifier('style') 163 | ), 164 | types.identifier('setProperty') 165 | ), 166 | [types.stringLiteral(cssVar), property.value] 167 | ) 168 | ); 169 | 170 | path.parentPath.parentPath.insertAfter(types.ifStatement(test, consonent)); 171 | }; 172 | -------------------------------------------------------------------------------- /packages/css-out-js/index.js: -------------------------------------------------------------------------------- 1 | export const css = () => {}; 2 | -------------------------------------------------------------------------------- /packages/css-out-js/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddharthkp/css-out-js/3d43e1c84fdc784008e9442e6e0e02391afa4b7a/packages/css-out-js/logo.png -------------------------------------------------------------------------------- /packages/css-out-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-out-js", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": {}, 7 | "keywords": [], 8 | "author": "siddharthkp", 9 | "license": "MIT", 10 | "dependencies": { 11 | "@emotion/hash": "^0.8.0", 12 | "to-style": "^1.3.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/css-out-js/readme.md: -------------------------------------------------------------------------------- 1 | <p align="center"> 2 | <img src="https://raw.githubusercontent.com/siddharthkp/css-out-js/main/packages/css-out-js/logo.png" height="80px"/> 3 | <br><br> 4 | <b>Author styles collocated in JS, pull them out into static CSS on build. No runtime dependency.</b> 5 | <br> 6 | </p> 7 | 8 |   9 | 10 | ### the big idea: 11 | 12 | I like to **author styles collocated with component code**, that's how I find them maintainable. 13 | 14 | But before shipping them to users, I'd like to pull styles out of components and ship them in **static CSS files**. No runtime dependency. 15 | 16 | ![animation showing code being pulled into dist.css](https://user-images.githubusercontent.com/1863771/178304291-c6d5e8d7-1218-43e5-897a-753ddf2a6a63.gif) 17 | 18 | prior art: This idea is not entirely new, there have been other similar interpretations of this idea before like [callstack/linaria](https://github.com/callstack/linaria) and [atlassian-labs/compiled](https://github.com/atlassian-labs/compiled) 19 | 20 | 21 | 22 |   23 | 24 | **input** 25 | 26 | `App.js` 27 | 28 | ```js 29 | import { css } from 'css-out-js'; 30 | 31 | const Title = (props) => { 32 | return <h1 className={css({ fontSize: '2em' })}>Hello!</h1>; 33 | }; 34 | ``` 35 | 36 | **output** 37 | 38 | The plugin extracts the styles into a css file<br> 39 | and replaces the function call with generated className string.<br> 40 | If there are any dynamic styles, it creates a css variable for them.<br> 41 | Finally, it removes the import for `css-out-js`, no runtime. 42 | 43 | `dist.css` 44 | 45 | ```diff 46 | +.Title-1bdbzov { 47 | + font-size: 2em; 48 | +} 49 | ``` 50 | 51 | `App.js` 52 | 53 | ```diff 54 | - import { css } from 'css-out-js'; 55 | 56 | const Title = (props) => { 57 | - return <h1 className={css({ fontSize: '2em' })}>Hello!</h1>; 58 | + return <h1 className="Title-1bdbzov">Hello!</h1>; 59 | }; 60 | ``` 61 | 62 |   63 | 64 | works with dynamic styles as well: 65 | 66 | **input** 67 | 68 | `App.js` 69 | 70 | ```js 71 | import { css } from 'css-out-js'; 72 | 73 | const Home = (props) => { 74 | const className = css({ color: props.color }); 75 | 76 | return ( 77 | <section className={className}> 78 | <p>Home sweet home</p> 79 | </section> 80 | ); 81 | }; 82 | ``` 83 | 84 | **output** 85 | 86 | Dynamic styles based on props are made possible by setting a css variable. 87 | 88 | `dist.css` 89 | 90 | ```diff 91 | +.Home-2b9glr { 92 | + color: var(--props-color-1trdzir); 93 | +} 94 | ``` 95 | 96 | `App.js` 97 | 98 | ```diff 99 | - import { css } from 'css-out-js'; 100 | 101 | const Home = (props) => { 102 | - const className = css({ color: props.color }); 103 | + const className = "Home-2b9glr" 104 | + document.documentElement.style.setProperty('--props-color-1trdzir', props.color), 105 | 106 | return ( 107 | <section className={className}> 108 | <p>Home sweet home</p> 109 | </section> 110 | ); 111 | } 112 | ``` 113 | 114 | ### setup 115 | 116 | 1. install 117 | 118 | ``` 119 | npm install css-out-js --save-dev 120 | 121 | # or 122 | 123 | yarn add css-out-js --dev 124 | ``` 125 | 126 | 2. Add plugin to babelrc, you can specify the location of your generated css file (hint: see [examples](https://github.com/siddharthkp/css-out-js/blob/main/examples) directory) 127 | 128 | ```js 129 | // .babelrc 130 | module.exports = { 131 | plugins: [['css-out-js/babel', { path: 'public/dist.css' }]] 132 | }; 133 | ``` 134 | 135 | 3. add css import 136 | 137 | this may differ based on the framework of your choice. (hint: see [examples](https://github.com/siddharthkp/css-out-js/blob/main/examples) directory) 138 | 139 | ```js 140 | // App.js 141 | import './dist.css'; 142 | ``` 143 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | packages/css-out-js/readme.md --------------------------------------------------------------------------------