Beta Preview

Styling

React Spectrum includes a build-time style macro that generates atomic CSS and lets you apply Spectrum tokens directly in your components with type-safe autocompletion.

Style macro

The style macro runs at build time and returns a class name for applying Spectrum 2 design tokens (colors, spacing, sizing, typography, etc.).

import {style} from '@react-spectrum/s2/style' with {type: 'macro'};

<div className={style({backgroundColor: 'red-400', color: 'white'})}>
  {/* ... */}
</div>

Atomic output keeps your bundle small and scales well as your app grows. Each property/value pair is emitted once and reused everywhere.

.bJ { background-color: #ffbcb4 }
.ac { color: #fff }

Colocating styles with your component code means:

  • Develop more efficiently – no switching files or writing selectors.
  • Refactor with confidence – changes are isolated; deleting a component removes its styles.

Spectrum components

The styles prop accepts a limited set of CSS properties, including layout, spacing, sizing, and positioning. Other styles such as colors and internal padding cannot be customized within Spectrum components.

import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {Button} from '@react-spectrum/s2';

<Button styles={style({marginStart: 8})}>Edit</Button>

Supported CSS properties

  • margin
  • marginStart
  • marginEnd
  • marginTop
  • marginBottom
  • marginX
  • marginY
  • width
  • minWidth
  • maxWidth
  • flexGrow
  • flexShrink
  • flexBasis
  • justifySelf
  • alignSelf
  • order
  • gridArea
  • gridRow
  • gridRowStart
  • gridRowEnd
  • gridColumn
  • gridColumnStart
  • gridColumnEnd
  • position
  • zIndex
  • top
  • bottom
  • inset
  • insetX
  • insetY
  • insetStart
  • insetEnd
  • visibility

Values

The style macro supports a constrained set of values per property that conform to Spectrum 2. This improves consistency and maintainability. See the reference page for a full list of available style macro properties.

Colors

All Spectrum 2 color tokens are available across color properties (e.g., backgroundColor, color, borderColor).

Spacing

Spacing props like margin and padding accept values on a 4px grid. These are specified in px and get converted to rem. In addition to numbers, these named options are available:

  • edge-to-text – default spacing between the edge of a control and its text. Relative to control height.
  • pill – default spacing between the edge of a pill-shaped control and its text. Relative to control height.
  • text-to-control – default spacing between text and a control (e.g., label and input). Scales with font size.
  • text-to-visual – default spacing between text and a visual element (e.g., icon). Scales with font size.

Sizing

Size props like width and height accept arbitrary pixel values. Values are converted to rem and multiplied by 1.25x on touch devices to increase hit targets.

Typography

Spectrum 2 typography is applied via the font shorthand, which sets fontFamily, fontSize, fontWeight, lineHeight, and color. You can override any of these individually. Note that you should always specify the font at the element level, setting it globally is insufficient since this will often differ per component.

<main>
  <h1 className={style({font: 'heading-xl'})}>Heading</h1>
  <p className={style({font: 'body'})}>Body</p>
  <ul className={style({font: 'body-sm', fontWeight: 'bold'})}>
    <li>List item</li>
  </ul>
</main>

Type scales include: UI, Body, Heading, Title, Detail, and Code. Each scale has a default and additional t-shirt sizes (e.g., ui-sm, heading-2xl, code-xl).

There are several different type scales.

  • UI – use within interactive UI components.
  • Body – use for the content of pages that are primarily text.
  • Heading – use for headings in content pages.
  • Title – use for titles within UI components such as cards or panels.
  • Detail – use for less important metadata.
  • Code – use for source code.

Each type scale has a default size, and several t-shirt size modifiers for additional sizes.

  • ui-xs
  • ui-sm
  • ui
  • ui-lg
  • ui-xl
  • ui-2xl
  • ui-3xl
  • body-2xs
  • body-xs
  • body-sm
  • body
  • body-lg
  • body-xl
  • body-2xl
  • body-3xl
  • heading-2xs
  • heading-xs
  • heading-sm
  • heading
  • heading-lg
  • heading-xl
  • heading-2xl
  • heading-3xl
  • title-xs
  • title-sm
  • title
  • title-lg
  • title-xl
  • title-2xl
  • title-3xl
  • detail-sm
  • detail
  • detail-lg
  • detail-xl
  • code-sm
  • code
  • code-lg
  • code-xl

Breakpoints

The style macro has several predefined breakpoints, but you can use arbitrary CSS media or container queries as conditional values in your style macro as well.

CSS optimization

The style macro relies on CSS bundling and minification for optimal output. Follow these best practices:

  • Ensure styles are extracted into a CSS bundle; do not inject at runtime with <style> tags.
  • Use a CSS minifier like lightningcss to deduplicate common rules (consider in dev for easier debugging).
  • Bundle all CSS for S2 components and style macros into a single CSS bundle rather than code splitting to avoid duplicate rules across chunks.

Parcel

Parcel supports macros out of the box and optimizes CSS with Lightning CSS. You can bundle all S2 and macro CSS into a single file using manual shared bundles.

// package.json
{
  "@parcel/bundler-default": {
    "manualSharedBundles": [
      {
        "name": "s2-styles",
        "assets": [
          "**/@react-spectrum/s2/**",
          // Update this glob as needed to match your source files.
          "src/**/*.{js,jsx,ts,tsx}"
        ],
        "types": ["css"]
      }
    ]
  }
}

Webpack

See the webpack example for a full configuration.

Vite

See the Vite example for full configuration options.

CSS Resets

CSS resets are strongly discouraged. Global CSS selectors can unintentionally affect elements that were not intended to have their styles be modified, leading to style clashes. Since Spectrum 2 uses CSS Cascade Layers, global CSS outside a @layer will override S2's CSS. Therefore, if you cannot remove your CSS reset, it must be placed in a lower layer. This can be done by declaring your reset layer before the _ layer used by S2.

/* App.css */
@layer reset, _;
@import "reset.css" layer(reset);

Developing with style macros

Since style macros are quite different from using className/style directly, many may find it initially challenging to debug and develop against. Below are some useful tools that may benefit your developer experience:

  • The atomic-css-devtools extension presents an inspected element's atomic CSS rules in a non-atomic format, making it easier to scan.

  • This sandbox is preconfigured to support React Spectrum S2, React Aria Components, and the style macros for quick prototyping.

  • If you are using Cursor, we offer a set of Cursor rules to use when developing with style macros. Additionally, we have MCP servers for React Aria and React Spectrum respectively that interface with the docs.

FAQ