# astroturf **Repository Path**: stlstl/astroturf ## Basic Information - **Project Name**: astroturf - **Description**: An "artificial" CSS-in-JS for those that want it all. - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-10-17 - **Last Updated**: 2021-03-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 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 - [Usage](#usage) - [Component API](#component-api) - [`css` prop](#css-prop) - [Component API Goals and Non-Goals](#component-api-goals-and-non-goals) - [Composition, variables, etc?](#composition-variables-etc) - [Referring to other Components](#referring-to-other-components) - [Sharing values between styles and JavaScript](#sharing-values-between-styles-and-javascript) - [Keyframes and global](#keyframes-and-global) - [Attaching Additional Props](#attaching-additional-props) - [`as` prop](#as-prop) - [Setup](#setup) - [Options](#options) - [Use with Gatsby](#use-with-gatsby) - [Use with Parcel](#use-with-parcel) - [Use with Preact](#use-with-preact) - [Use with Next.js](#use-with-nextjs) - [Use without webpack](#use-without-webpack) ## Usage ```js import React from 'react'; import { css } from 'astroturf'; const styles = css` .button { color: black; border: 1px solid black; background-color: white; } `; export default function Button({ children }) { return ; } ``` When processed, the `css` block will be extracted into a `.css` file, taking advantage of any and all of the other loaders configured to handle css. It even handles statically analyzable interpolations! ```js import { css } from 'astroturf'; const margin = 10; const height = 50; const bottom = height + margin; const styles = css` .box { height: ${height}px; margin-bottom: ${margin}px; } .footer { position: absolute; top: ${bottom}px; } `; ``` ## Component API For those that want something a bit more like styled-components or Emotion, there is a component API! ```js import styled, { css } from 'astroturf'; const Button = styled('button')` color: black; border: 1px solid black; background-color: white; &.primary { color: blue; border: 1px solid blue; } &.color-green { color: green; } `; ``` You can render this with: ```js render( , mountNode, ); ``` The above transpiles to something like: ```js const styles = css` .button { color: black; border: 1px solid black; background-color: white; &.primary { color: blue; border: 1px solid blue; } &.color-green { color: green; } } `; function Button({ primary, color, className, ...props }) { return ( ); } ``` ## `css` prop In addition to the `styled` helper, styles can be defined directly on components via the `css` prop. You first need to enable this feature via the `enableCssProp` option in your loader config ```jsx function Button({ variant, children }) { return ( ); } ``` Styles are still extracted to a separate file, any props matching other defined classes are passed to the `classNames()` library. At runtime `styled()` returns a React component with the static CSS classes applied. You can check out the ["runtime"](https://github.com/4Catalyzer/astroturf/blob/master/src/runtime/styled.js) it just creates a component. There are a whole bucket of caveats of course, to keep the above statically extractable, and limit runtime code. - We assume you are using css-modules in your css pipeline to return classes from the style files, we don't do any of that ourselves. - Prop value handling requires the nesting transform - All "top level" styles have any @import statements hoisted up (via a regex) ## Component API Goals and Non-Goals The goal of this API is not to mimic or reimplement the features of other css-in-js libraries, but to provide a more ergonomic way to write normal css/less/sass next to your javascript. What does that mean? css-in-js libraries are a _replacement_ for css preprocessors, in that they provide ways of doing variables, composition, mixins, imports etc. Usually they accomplish this by leaning on JS language features where appropriate, and adding their own domain-specific language bits when needed. astroturf **doesn't try to do any of that** because it's not trying to replace preprocessors, but rather, make component-centric javascript work better with **existing** styling tooling. This means at a minimum it needs to scope styles to the component (handled by css-modules) and map those styles to your component's API (props), which is what the above API strives for. This approach **gains** us: - No additional work to extract styles for further optimization (autoprefixer, minifying, moving them to a CDN, etc) - The smallest runtime, it's essentially zero - Blazing Fast™ because there is zero runtime evaluation of styles - Leverage the well-trod and huge css preprocesser ecosystems It also means we **sacrifice**: - A fine-grained style mapping to props. Props map to classes, its all very BEM-y but automated - Dynamism in sharing values between js and css - A unified JS-only headspace, you still need to think in terms of JS and CSS ## Composition, variables, etc? How you accomplish that is mostly up to your preprocessor. Leverage Sass variables, or Less mixins, or postcss nesting polyfills, or whatever. The css you're writing is treated exactly like a normal style file so all the tooling you're used to works as expected. For composition, specifically around classes, you can also use css-modules `composes` to compose styles and interpolation; ```js // Button.js const helpers = css` .heavy { font-weight: 900; } `; const Title = styled('h3')` composes: ${helpers.heavy}; font-size: 12%; `; ``` You don't have to define everything in a `.js` file. Where it makes sense just use normal css (or any other file type). ```scss // mixins.scss @mixin heavy() { font-weight: 900; } ``` and then: ```js // Button.js const Title = styled('h3')` @import './mixins.scss'; @include heavy(); font-size: 12%; `; ``` ## Referring to other Components One limitation to fully encapsulated styles is that it's hard to contextually style components without them referencing each other. In astroturf you can use a component in a selector as if it were referencing a class selector. > Note: Referencing stylesheets or styled components from other files has a few caveats: > [cross-file-dependencies](/docs/cross-file-dependencies.md) ```js const Link = styled.a` display: flex; align-items: center; padding: 5px 10px; background: papayawhip; color: palevioletred; `; const Icon = styled.svg` flex: none; transition: fill 0.25s; width: 48px; height: 48px; ${Link}:hover & { fill: rebeccapurple; } `; ``` ## Sharing values between styles and JavaScript We've found that in practice, you rarely have to share values between the two, but there are times when it's very convenient. Astroturf ofters two ways to do this, the first is string interpolations. ```js const DURATION = 500; const ColorTransition = styled('nav')` color: red; transition: color ${DURATION}ms; &.blue { color: blue; } `; class App extends React.Component { state = { blue: false } toggle = () => { this.setState(s => ({ blue: !s.blue }), () => { setTimeout(() => console.log('done!'), DURATION) }) } render() { const { blue } = this.state