Griffel Documentation

repository·main·Indexed 23 days ago

https://github.com/microsoft/griffel

A high-performance CSS-in-JS library utilizing Atomic CSS with near-zero runtime. Griffel supports SSR, type-safe styling via csstype, and ahead-of-time (AOT) compilation. The ecosystem includes official React abstractions (@griffel/react), Babel presets, ESLint plugins, Jest serializers, and bundler integrations for Webpack and Vite.

Tokens
43.2K
Snippets
134
Records
224
Agent score
77%

What's inside Griffel

  1. Overview of Griffel features

    main

    Griffel is a CSS-in-JS library designed with near-zero runtime and SSR support. Key features include:

    • Zero config start: Supports both runtime and build-time implementations.
    • Performance: Optional build-time transforms (e.g., via Webpack loader) are available to improve performance.
    • Type-safety: Styles are type-safe via csstype.
    • Atomic CSS: Uses Atomic CSS to maximize style reuse and prevent CSS specificity issues.
    • CSS Extraction: Experimental CSS extraction is available via a Webpack plugin.
    • Debugging: Supports the Griffel DevTools browser extension for debugging styles.
  2. Handle RTL (Right-To-Left) support

    main

    Griffel uses rtl-css-js to automatically flip properties and values in Right-To-Left text directions, controlled by the TextDirectionProvider.

    • Automatic flipping: Properties like paddingLeft will automatically become paddingRight in RTL mode.
    • Preventing flips: To prevent a specific rule from being flipped, add the /* @noflip */ comment to the property value.
    import { makeStyles } from '@griffel/react';
    
    // This will flip in RTL
    const useFlipping = makeStyles({
      root: {
        paddingLeft: '10px',
      },
    });
    
    // This will NOT flip in RTL
    const useNoFlip = makeStyles({
      root: {
        paddingLeft: '10px /* @noflip */',
      },
    });
    import { makeStyles } from '@griffel/react';
    
    const useClasses = makeStyles({
      root: {
        paddingLeft: '10px',
      },
    });
    
    // ...
    
    const useNoFlip = makeStyles({
      root: {
        paddingLeft: '10px /* @noflip */',
      },
    });
  3. Use nesting selectors with `&`

    main

    You can use the & character to reference the parent selector. This is useful for targeting child elements or applying styles based on the parent's state/class.

    Note: Because Griffel uses Atomic CSS, nesting more selectors increases the number of generated styles. Use with caution.

    import { makeStyles } from '@griffel/react';
    
    const useClasses = makeStyles({
      root: {
        '& .foo': { color: 'green' },
        '&.bar': { color: 'red' },
      },
    });
    import { makeStyles } from '@griffel/react';
    
    const useClasses = makeStyles({
      root: {
        '& .foo': { color: 'green' },
        '&.bar': { color: 'red' },
      },
    });
  4. Handle style overrides with mergeClasses

    main

    Because Griffel's AOT (Ahead-of-Time) compilation cannot create new rules at runtime, style overrides are achieved by merging existing atomic classes.

    When multiple classes targeting the same property are applied to an element, Griffel ensures that only one set of properties is applied. The rule is that the last defined property-value pair wins. To ensure correct behavior when applying multiple sets of styles, use the mergeClasses API.

  5. Understand Griffel's Atomic CSS approach

    main

    Griffel uses an Atomic CSS approach where every property-value pair is written as a single, reusable CSS rule. This contrasts with monolithic classes that group multiple properties into one class.

    Benefits:

    • Reusability: CSS rules are reused across different components, reducing the total amount of defined CSS.
    • Reduced CSS Size: As the application grows, the amount of new CSS rules scales more slowly than the number of components.

    Trade-offs:

    • Larger HTML Markup: Every CSS rule adds a class to the HTML element, increasing the number of classes per element.
    • Recalculation Performance: Browsers experience linear performance degradation as the number of classes on an element increases. While rare, exceeding 100 classes (often via heavy use of nested selectors) can impact performance. It is recommended to avoid excessive nesting to keep class counts low.
  6. How Griffel handles pseudo-class ordering (LVFHA)

    main

    In CSS, pseudo-classes like :hover and :active have equal specificity, and their behavior is determined by the order of appearance in the cascade. Since atomic classes are inserted into the DOM based on usage/definition, the order can become non-deterministic.

    To ensure deterministic results, Griffel automatically orders common pseudo-classes in the following sequence:

    1. :link
    2. :visited
    3. :focus-within
    4. :focus
    5. :focus-visible
    6. :hover
    7. :active

    The last defined pseudo-class in this order will win if multiple states apply simultaneously.

  7. Control style precedence with mergeClasses argument order

    main

    Unlike native CSS where the order of class names in an HTML attribute does not affect style application, in Griffel, the order of arguments in mergeClasses() determines the result.

    If two classes apply the same style property, the latest class in the argument list wins. This allows you to control style overrides using JavaScript logic.

    import { mergeClasses, makeStyles } from '@griffel/react';
    
    const useClasses = makeStyles({
      blueBold: {
        color: 'blue',
        fontWeight: 'bold',
      },
      red: {
        color: 'red',
      },
    });
    
    function Component(props) {
      const { isBold } = props;
      const classes = useClasses();
    
      // Result: { color: 'red', fontWeight: 'bold' }
      // 'red' wins because it is the last argument
      const firstClassName = mergeClasses(isBold && classes.blueBold, classes.red);
    
      // Result: { color: 'blue', fontWeight: 'bold' }
      // 'blueBold' wins because it is the last argument
      const secondClassName = mergeClasses(classes.red, isBold && classes.blueBold);
    }
  8. How Griffel evaluates style expressions

    main

    Because makeStyles often uses imported tokens or helper functions, the Griffel build plugin must evaluate these expressions to determine the final styles. @griffel/transform uses a two-phase evaluation process:

    1. AST evaluation (fast path): The plugin statically analyzes the Abstract Syntax Tree (AST) to resolve simple expressions like literal values, plain objects, or template strings without expressions. If the style object is fully resolvable this way, no code execution is required.
    2. VM evaluation (fallback): For complex expressions that cannot be resolved statically (such as function calls, variable references, or dynamic values), the code is executed in a sandboxed Node.js vm context. During this phase, dependencies are resolved and tree-shaken by @griffel/transform-shaker before evaluation.
    // Example of code requiring VM evaluation due to imports and function calls
    // tokens.js
    export const PADDING = '1px';
    
    // helpers.js
    export const flexCenter = () => ({
      display: 'flex',
      justifyContent: 'center',
      alignItems: 'center',
    });
    
    // styles.js
    import { makeStyles } from '@griffel/react';
    import { PADDING } from './tokens';
    import { flexCenter } from './helpers';
    
    const useStyles = makeStyles({
      root: { paddingLeft: PADDING, ...flexCenter() },
    });
  9. How @griffel/jest-serializer works

    main

    Griffel generates atomic class names by hashing style declarations (e.g., ___1t65jhk_nkb4zh0). Because these hashes are implementation details that can change during Griffel upgrades or unrelated style changes, they often cause brittle snapshots.

    This serializer removes class names generated by both makeStyles() and makeResetStyles(), leaving only the markup you wrote. Any class names not generated by Griffel are preserved.

  10. Note on CSS snapshot formatting and redaction

    main

    When inspecting or updating CSS snapshots, be aware of two behaviors:

    1. Prettier: Snapshots are formatted using Prettier.
    2. Redaction: Class names for rules containing url() are redacted. This is because @griffel/transform hashes the resolved absolute asset path, which varies depending on the machine running the test.
  11. How the Griffel Webpack plugin works

    main

    The @griffel/webpack-extraction-plugin is a Webpack 5 plugin designed for applications to perform CSS extraction for @griffel/react.

    Prerequisites:

    • You must configure @griffel/webpack-loader first. This plugin relies on assets transformed by that loader.

    Key Behaviors:

    • It transforms code to remove generated CSS from JavaScript files and creates separate CSS assets.
    • Limitation: Currently, all CSS rules are extracted to a single CSS file. Webpack code splitting for extracted CSS is not supported at this time.