An "artificial" CSS-in-JS for those that want it all.

  • By 4Catalyzer
  • Last update: Nov 26, 2022
  • Comments: 17

astroturf

astroturf lets you write CSS in your JavaScript files without adding any runtime layer, and with your existing CSS processing pipeline.

  • Zero runtime CSS-in-JS. Get many of the same benefits as CSS-in-JS, but without the loss of flexibility in requiring framework-specific CSS processing, and while keeping your CSS fully static with no runtime style parsing.
  • Use your existing tools – Sass, PostCSS, Less – but still write your style definitions in your JavaScript files
  • Whole component in the single file. Write CSS in a template literal, then use it as if it were in a separate file

Checkout the docs for examples and API details: https://4catalyzer.github.io/astroturf/

Github

https://github.com/4Catalyzer/astroturf

Comments(17)

  • 1

    Support one class approach

    I would like to use astroturf in such way

    const titleClass = cls`
      color: red;
      background-color: green;
    `;
    <h1 className={titleClass}></h1>
    

    similar to styled but without component

  • 2

    Add styledTag option

    css-literal-loader has tagName option to replace css.

    But I need also the same for styled, because right now I am moving the big project to css-literal-loader and found 2 problems:

    1. We have “auto-import” Babel plugin which inserts import React from 'react', etc. It will be nice to have the same for styled (because we used it in every component), but it will not work with path.get('tag.callee').referencesImport('css-literal-loader/styled').
    2. We have automatic spelling check. To do this, we execute components in Node. Without babel loader, css throws “not in runtime” error. The fastest way to fix it just to replace import styled from '../lib/styled', where we will hack styled.css.

    I know, that both reasons are very unique. But the fact that we have 2 reasons for it in the single project shows that customizing styled tag could be very useful for advanced developers.

  • 3

    AstroTurfLoaderError: Cannot use the decorators and decorators-legacy plugin together

    I'm looking for css-in-js library for my project, linaria and astroturf are in my preference. astroturf looks better for me because we already use css modules using .less.

    The problem is when I use { legacy: true } for @babel/plugin-proposal-decorators build will crash with error message: AstroTurfLoaderError: Cannot use the decorators and decorators-legacy plugin together.

    My babel config is:

    {
        presets: [
            ['@babel/preset-env', {
                loose: true,
            }],
            '@babel/preset-react',
        ],
        plugins: [
            '@babel/plugin-syntax-dynamic-import',
            [
                '@babel/plugin-proposal-decorators',
                { legacy: true },
            ],
            '@babel/plugin-proposal-object-rest-spread',
            ['@babel/plugin-proposal-class-properties', { loose: true }],
            [
                '@babel/plugin-transform-runtime',
                {
                    corejs: false,
                    helpers: true,
                    regenerator: true,
                    useESModules: false,
                },
            ],
        ],
    }
    

    What i do wrong?

  • 4

    Component API props are passed to DOM element

    Hi!

    Thanks for the best css-in-js library! We're using Component API in our application, and I noticed odd behaviour in a new version: all props matching css classes are passed to DOM elements. As far as I remember astroturf filtered props earlier.

    Example:

    let Base = styled.div`
      position: relative;
      …
      &.isBig {
        size: 32px;
      }
    `
    
    …
    return React.createElement(Base, { isBig }, …)
    

    And now I'm getting:

    Warning: React does not recognize the `isBig` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `isbig` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
    

    Is it correct behaviour and we have to filter props manually?

    Thanks!

  • 5

    WIP: source maps

    @ai I'd appreciate your expertise here. I've what appears to be a functioning source-map being output on the files, but it seems like css-loader is bulldozing over it. Since it's using PostCSS internally you may have a better sense of what's going wrong

  • 6

    decss

    What do you think about decss project? (syntax sugar over CSS Modules with styled-component’s like React components).

    How can I combine decss and css-literar-loader?

  • 7

    Are BEM-style class names possible?

    Hi. I want to get classes like Card and Card--large. In the documentation I found an example like this:

    const Card = styled('form')`
    width: 150px;
    height: 120px;
    &.large {
      width: 300px;
      height: 240px;
    }
    `;
    // …
    return (<Card large />);
    

    It will generate somethins like this:

    <form class="UserCard-Card--3mYhX UserCard-Card--2O_am"></form>
    

    with webpack config

        module: {
          rules: [
            {
              test: /\.css$/,
              use: [
                { 
                  loader: "style-loader",
                },
                { 
                  loader: "astroturf/css-loader",
                  options: {
                    modules: true,
                    localIdentName: '[name]--[hash:base64:5]',
                  },
                }
              ],
            },
            {
              test: /\.jsx?$/,
              exclude: /node_modules/,
              use: ['babel-loader', 'astroturf/loader'],
            }
          ]
        },
    

    But is there any way to get canonical BEM .Card and .Card--large classes?

    <form class="Card__3mYhX Card--large__3mYhX"></form>
    

    Regards. Anton.

  • 8

    0.8: Cannot read property 'node' of null

    I updated astroturf and got this error during test:

        TypeError: Cannot read property 'node' of null
    
          at importNodes.forEach.path (node_modules/astroturf/plugin.js:180:18)
              at Array.forEach (<anonymous>)
          at PluginPass.post (node_modules/astroturf/plugin.js:175:19)
          at transformFile (node_modules/@babel/core/lib/transformation/index.js:94:27)
          at runSync (node_modules/@babel/core/lib/transformation/index.js:45:3)
          at transformSync (node_modules/@babel/core/lib/transform.js:43:38)
          at transform (node_modules/@babel/core/lib/transform.js:22:38)
    

    My Babel config:

    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "loose": true
          }
        ]
      ],
      "plugins": [
        "./builder/auto-import",
        "babel-plugin-transform-typograf",
        [
          "@babel/plugin-proposal-decorators",
          {
            "legacy": true
          }
        ],
        "@babel/plugin-syntax-dynamic-import",
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-proposal-object-rest-spread"
      ],
      "env": {
        "test": {
          "plugins": [
            "babel-plugin-dynamic-import-node",
            [
              "astroturf/plugin",
              {
                "allowGlobal": true,
                "writeFiles": false
              }
            ],
            [
              "babel-plugin-transform-rename-import",
              {
                "replacements": [
                  {
                    "original": "^.*\\.(css|less|sss)$",
                    "replacement": "identity-obj-proxy"
                  }
                ]
              }
            ]
          ]
        }
      }
    }
    
  • 9

    fix: 🐛 add virtual css timestamps to webpack compilation

    When webpack is run in watch mode, the system determines if a given module should be rebuilt by checking the modified timestamp of it's dependencies inside needRebuild

    https://github.com/webpack/webpack/blob/v4.36.1/lib/NormalModule.js#L522

    It was discovered during debugging that for the virtual css modules in astroturf, these timestamps are always undefined in the passed fileTimestamps map object.

    The effect/bug that this causes is that any astroturf dependant file will always be recompiled, and any change to a file will cause all astroturf dependant files to be be recompiled. This impacts performance on large projects, for example in storybook where there may be many components that could be reloaded by a single change.

    To understand why these files are not in the fileTimestamps map, we follow the trail of invocation in webpack and find that during watch mode, this map is passed to the compilation object (via the compiler) as a result of the watch callback:

    https://github.com/webpack/webpack/blob/v4.36.1/lib/Watching.js#L134

    By default this map of { [file]: timestamp } is gathered from the watcher directly:

    https://github.com/webpack/webpack/blob/v4.36.1/lib/node/NodeWatchFileSystem.js#L54

    And not via the webpack filesystem, which is what astroturf proxies to achieve a virtual file system.

    Unfortunately using the same technique to pass a filesystem further into the watcher (by default watchpack) is unlikely to be accepted:

    https://github.com/webpack/watchpack/issues/95

    This PR addresses this issue by manually updating the fileTimestamps map with the virtual css modules after they have been added to the in memory file system directly in the astroturf loader.

    In my testing, this has been a fairly harmless and safe fix in order to achieve the desired performance improvement.

  • 10

    Rewrite first paragraph

    The first paragraph is critical for open source tool promotion. We spend few days in rewriting main thee first sentence in PostCSS docs.

    The current first paragraph is good, but we can do it better:

    1. Explain what is a difference. Most styled-components users don’t know how it works inside.
    2. Use simple English since most of the developers are not English native speakers.
    3. Talk to users of non-CSS-in-JS solutions

    My first draft (it definitely should be changes, at least English should be fixed):

    astroturf is zero-runtime CSS-in-JS. In contrast to styled-components it doesn’t compile CSS in brower. astroturf use Babel to compile styled to static CSS once on deploy for better perfomance. It’s just works with CSS, PostCSS, Less, Sass, or any other css preprocessor, and plays nicely with existing style tooling like mini-css-extract-plugin.

    [code example]

    astroturf allows you to write CSS in JS files, so have the whole component in the single file. Like CSS Modules it has built-in CSS isolation for better code maintainability.

  • 11

    NextJS Example Outdated

    NextJS has supported CSS out of the box without the @next/css extension.

    https://github.com/vercel/next.js/tree/canary/examples/with-astroturf This example needs to be updated. It says its backward compatible but apparently it only allows global css imports in the _app.js file.

  • 12

    chore(deps): update dependency node-sass to v7 [security]

    Mend Renovate

    This PR contains the following updates:

    | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | node-sass | ^4.14.1 -> ^7.0.0 | age | adoption | passing | confidence |

    GitHub Vulnerability Alerts

    CVE-2020-24025

    Certificate validation in node-sass 2.0.0 to 6.0.1 is disabled when requesting binaries even if the user is not specifying an alternative download path.


    Release Notes

    sass/node-sass

    v7.0.0

    Compare Source

    Breaking changes
    Features
    Dependencies
    Community
    Misc

    Supported Environments

    | OS | Architecture | Node | | --- | --- | --- | | Windows | x86 & x64 | 12, 14, 16, 17 | | OSX | x64 | 12, 14, 16, 17 | | Linux* | x64 | 12, 14, 16, 17 | | Alpine Linux | x64 | 12, 14, 16, 17 | | FreeBSD | i386 amd64 | 12, 14 |

    *Linux support refers to major distributions like Ubuntu, and Debian

    v6.0.1

    Compare Source

    Dependencies
    Misc

    Supported Environments

    | OS | Architecture | Node | | --- | --- | --- | | Windows | x86 & x64 | 12, 14, 15, 16 | | OSX | x64 | 12, 14, 15, 16 | | Linux* | x64 | 12, 14, 15, 16 | | Alpine Linux | x64 | 12, 14, 15, 16 | | FreeBSD | i386 amd64 | 12, 14, 15 |

    *Linux support refers to major distributions like Ubuntu, and Debian

    v6.0.0

    Compare Source

    Breaking changes
    Features
    • Add support for Node 16
    Community

    Supported Environments

    | OS | Architecture | Node | | --- | --- | --- | | Windows | x86 & x64 | 12, 14, 15, 16 | | OSX | x64 | 12, 14, 15, 16 | | Linux* | x64 | 12, 14, 15, 16 | | Alpine Linux | x64 | 12, 14, 15, 16 | | FreeBSD | i386 amd64 | 12, 14, 15 |

    *Linux support refers to major distributions like Ubuntu, and Debian

    v5.0.0

    Compare Source

    Breaking changes
    Features
    • Add support for Node 15
    • New node-gyp version that supports building with Python 3
    Community
    Fixes

    Supported Environments

    | OS | Architecture | Node | | --- | --- | --- | | Windows | x86 & x64 | 10, 12, 14, 15 | | OSX | x64 | 10, 12, 14, 15 | | Linux* | x64 | 10, 12, 14, 15 | | Alpine Linux | x64 | 10, 12, 14, 15 | | FreeBSD | i386 amd64 | 10, 12, 14, 15 |

    *Linux support refers to major distributions like Ubuntu, and Debian


    Configuration

    📅 Schedule: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

    🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    🔕 Ignore: Close this PR and you won't be reminded about this update again.


    • [ ] If you want to rebase/retry this PR, check this box

    This PR has been generated by Mend Renovate. View repository job log here.

  • 13

    Clean dependencies

    This PR has been motivated by the issue

    • replace comsiconfig to lilconfig;
    • replace one large lodash with special lodash.x with a specific function;
    • remove @types/fs-extra
    • replace globby to fast-glob
  • 14

    Styles do not update in webpack watch mode

    Just like in #665 , but on most recent webpack 5.65.0, all consecutive changes after first one are ignored.

    Quick fiddling showed that apparently API has changed a bit, and now here must be fileWatcher.watcher.* instead of fileWatcher.*.

    image

  • 15

    Reduce Install size

    New version of astroturf has dependnecies for 18 MB. Of course, node_modules install size is less important resources than bundle size. But 18 MB is too big and will affect CI performance and FS performance of using node_modules (text editor will be very slow when you will try to open node_modules).

    It happens because astroturf use 140 (!) subdependnecies.

    Снимок экрана от 2021-12-21 22-10-12

    Seems like this problem can be fixed without big changes. Just by using dependencies a little more careful:

    1. Replacing cosmiconf to lilconfig (the same API but less subdependencies, we moved in PostCSS too).
    2. Replacing chalk with picocolors https://github.com/4Catalyzer/astroturf/issues/732
    3. Remove PostCSS plugins without maintaince using very old PostCSS: postcss-atroot and postcss-strip-inline-comments
    4. Using specific lodash.X packages instead of all-in-one lodash
    5. Cleaning some type dependencies. I am not sure that we need all of them. For instance, seems like we need @types/fs-extra only for astroturf development, not to use it always.
  • 16

    Dynamic styles in styled() components

    Hi. This is more of a question. I wasn't quite able to tell from the docs; is it possible to do something like the following:

    const Text = styled("span")<{color: string}>`
       color: ${props => props.color};
    `
    

    Thanks

  • 17

    css-loader 6.4.0 breaks astroturf

    See https://github.com/webpack-contrib/css-loader/issues/1384#issuecomment-942170586 for more details. The tl;dr is this probably needs to add more data to the virtual module so class names generate correctly.