nano-css

repository·master·Indexed 19 days ago

https://github.com/streamich/nano-css

A high-performance, ultra-lightweight (0.5 Kb) 5th generation CSS-in-JS library. Designed to be library-agnostic and isomorphic, nano-css supports server-side rendering (SSR) and browser hydration. It utilizes a modular addon-based architecture to keep the core small while providing extended features such as @media queries, @keyframes animations, auto-prefixing, and CSS extraction. It is compatible with frameworks like React, Preact, and Vue.js.

Tokens
40.5K
Snippets
195
Records
242
Agent score
64%

What's inside nano-css

  1. Overview of nano-css

    master

    nano-css is a tiny (approx. 0.5 Kb base) 5th generation CSS-in-JS library designed for production use. Its core philosophy is to remain as small as possible by providing features through an addon-based architecture.

    Key Features:

    • Library-agnostic: Works standalone or with frameworks like React, Preact, and Vue.js.
    • Isomorphic: Supports server-side rendering (SSR) and browser hydration with stable class names.
    • Performant: Uses .insertRule() for high-performance CSS injection and caches styles to avoid unnecessary work. It does not use wrapper components or inline styles.
    • Feature-rich via Addons: Supports @media queries, @keyframes animations, auto-prefixing, and CSS extraction into external stylesheets.

    For a pre-configured experience, you can use nano-theme, which is built on top of nano-css.

  2. Overview of nano-css layers and capabilities

    master

    nano-css is organized into functional layers that allow you to pick only the features you need to keep your bundle size small.

    LayerExamplesDescription
    Coreput(), putRaw()The minimal renderer — inject CSS by selector
    Rulesrule(), drule(), sheet(), dsheet()Generate class names from CSS objects
    Stylingjsx(), style(), styled()Create styled components for virtual DOM libraries
    Utilitiesatoms, nesting, keyframes, prefixerEnhance CSS authoring with shortcuts and features
    Advancedvirtual, vcssom, hydrate, extractPerformance optimizations and build-time tools
  3. How the `virtual` Addon works

    master

    The virtual Addon implements Virtual CSS by splitting all CSS rules into atomic single declarations. Each unique declaration is assigned a specific class name and reused across the application. This prevents duplication of CSS rules by ensuring that if multiple components use the same property-value pair (e.g., color: red), they share the same atomic class name.

    const classNames1 = nano.rule({
        color: 'red',
        border: '1px solid red',
        textAlign: 'center'
    });
    // _a _b _c
    
    const classNames2 = nano.rule({
        border: '1px solid red',
    });
    // _b
    
    // Resulting HTML:
    // <div class="_a _b _c" />
    // <div class="_b" />
  4. Use the `atoms` addon for CSS property shorthands

    master

    The atoms addon provides shorthand keys for common CSS properties when composing CSS-like objects in nano-css. Instead of using full kebab-case (e.g., 'border-top') or camel-case (e.g., borderTop) strings, you can use short atom keys. This improves Developer Experience (DX) and reduces bundle size as the shorthand keys are shorter than their full property name counterparts.

    // Standard kebab-case
    const className = rule({
        'border-top': '1px solid red'
    });
    
    // Standard camel-case
    const className = rule({
        borderTop: '1px solid red'
    });
    
    // Using atoms shorthand
    const className = rule({
        bdt: '1px solid red'
    });
  5. Use virtual CSS for atomic single declarations

    master

    The virtual addon enables a mode that splits CSS rules into atomic single declarations. Each declaration is assigned its own unique class name and is reused across different rules whenever the same property-value pair is encountered. This reduces the total amount of CSS generated by preventing duplication of common styles.

    const classNames1 = nano.rule({
      color: 'red',
      border: '1px solid red',
      textAlign: 'center',
    });
    // → '_a _b _c'
    
    const classNames2 = nano.rule({
      border: '1px solid red',
    });
    // → '_b'  (reused!)
  6. Use the `amp` Addon to enforce AMP restrictions

    master

    The amp addon helps ensure your CSS is compatible with AMP (Accelerated Mobile Pages) by enforcing size limits and removing prohibited declarations or selectors.

    When active, the addon:

    • Limits the style sheet size (defaults to 50Kb).
    • Removes !important modifiers.
    • Removes banned declarations like behavior or -moz-binding.
    • Removes CSS rules using reserved selectors (e.g., .-amp-* or i-admp-*).

    In development mode, the addon will display error messages if you attempt to use !important, banned declarations, or reserved selectors.

    import {addon as addonAmp} from 'nano-css/addon/amp';
    
    addonAmp(nano, {
        limit: 50000,
        removeImportant: true,
        removeReserved: true,
        removeBanned: true,
    });
  7. Concatenate `rule()` class names with other classes

    master

    nano-css class names returned by rule() always include a leading space. This allows you to safely concatenate them with other class strings using standard string concatenation.

    const otherClass = 'foo';
    const className = rule(css);
    
    // Results in class="foo _xuhuadsf"
    <div className={otherClass + className}>Hello world!</div
  8. Compose components using `jsx()`

    master

    You can create new styling blocks by passing an existing component as the first argument to jsx(). This allows for style inheritance.

    Note on Best Practices: While you can compose styling blocks directly, it is often better to use the css prop for dynamic variations to keep the component logic cleaner.

    Direct Composition

    const BaseButton = jsx('button', {
        color: 'red',
        border: '1px solid red',
    });
    
    const SmallButton = jsx(BaseButton, {
        fontSize: '11px',
    });
    const BaseButton = jsx('button', {
        color: 'red',
        border: '1px solid red',
    });
    
    const Button = (props) => {
        const {small, ...rest} = props;
        const css = {};
    
        if (small) {
            css.fontSize = '11px';
        }
    
        return <BaseButton {...rest} css={css} />;
    };
  9. How nano-css works: The renderer and addon pattern

    master

    nano-css uses a modular architecture where you start with a minimal core renderer and extend its functionality using addons. The core provides basic CSS injection, while addons add higher-level features like rule generation, atomic CSS, or nesting.

    To use nano-css, you follow this pattern:

    1. Create a renderer instance using create().
    2. Pass the renderer instance to addon functions to extend it.
    3. Use the newly added methods to generate styles.
    import { create } from 'nano-css';
    import { addon as addonRule } from 'nano-css/addon/rule';
    import { addon as addonAtoms } from 'nano-css/addon/atoms';
    
    const nano = create();
    addonRule(nano);
    addonAtoms(nano);
    
    // Now you can use methods provided by the addons
    const className = nano.rule({
      col: 'red',        // atoms shorthand for "color"
      bdrad: '4px',      // atoms shorthand for "border-radius"
    });
  10. Understand the leading space in rule() class names

    master

    The rule() function always returns class names with a leading space. This design choice simplifies string concatenation when combining multiple classes (e.g., otherClass + className) without needing to manually manage spaces between them.

    const otherClass = 'foo';
    const className = rule(css);
    
    <div className={otherClass + className}>Hello world!</div>
    // → <div class="foo _xuhuadsf">
  11. Inject global CSS using the `:global` selector

    master

    The :global selector allows you to define CSS rules that are not scoped to the parent element. When used inside a nano.put call, any styles nested under the :global key will be emitted as top-level global CSS rules rather than being scoped to the generated class name.

    const className = nano.put('.foo', {
        color: 'red',
        '.nested': {
            fontWeight: 'bold'
        },
        ':global': {
            '.global_class': {
                border: '1px solid red'
            }
        }
    });
    
    // Results in:
    // .foo { color: red; }
    // .foo .nested { font-weight: bold; }
    // .global_class { border: 1px solid red; }
  12. Extract the CSS object or evaluate styles with `snake`

    master

    When using the snake addon, you can retrieve the generated style object or convert the chain into usable CSS/class names.

    Retrieve the raw object

    Use the .obj property at the end of your chain to get the plain JavaScript object representing the CSS.

    Evaluate to class names

    To inject the styles into the DOM and get a string of class names, you can "evaluate" the chain using several methods:

    • .valueOf()
    • .toString()
    • String coercion (e.g., + '' or String(styles))

    Note: If you intend to evaluate styles using .valueOf() or .toString(), you must also install the cache addon.

    Lazy Evaluation

    You can define a style chain and store it in a variable. The actual CSS injection and class name generation will only occur when the variable is evaluated (e.g., during rendering).

    // Get the object
    const obj = nano.s.col('red').bd('1px solid red').obj;
    
    // Evaluate to class names (requires 'cache' addon)
    <div className={nano.s.col('red').valueOf()}>foobar</div>
    <div className={nano.s.col('red').toString()}>foobar</div>
    <div className={'' + nano.s.col('red')}>foobar</div>
    
    // Lazy evaluation
    const styles = nano.s.pointer.col('blue').bd('1px solid red');
    <div className={'' + styles}>foobar</div>