---
title: "CSS variables"
description: "Root token variables, variables* utilities and sizing-* / theme-* scopes."
requested_language: fr
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/fr/styling/css-variables
version: unreleased (main)
docs_index: https://docs.systhema.app/fr/llms.txt
---
> This page isn't translated yet. Showing English.


Every token becomes a CSS custom property. Systhema declares them on `:root`, binds the color system to `data-theme`, and ships classes that re-declare a set of variables on any element, so you can scope a color mode or one breakpoint's sizes to part of a page, or read a token from your own CSS.

## Quick reference

Breakpoint and design-width variables on `:root`, per breakpoint (default tokens):

<!-- generated:utilities root-variables -->

| Variable               | sm     | md     | lg     | xl     | 2xl    | 3xl    |
| ---------------------- | ------ | ------ | ------ | ------ | ------ | ------ |
| `--breakpoint-current` | `0`    | `640`  | `1192` | `1192` | `1192` | `1192` |
| `--breakpoint-sm`      | `0`    | `0`    | `0`    | `0`    | `0`    | `0`    |
| `--breakpoint-md`      | `640`  | `640`  | `640`  | `640`  | `640`  | `640`  |
| `--breakpoint-lg`      | `1192` | `1192` | `1192` | `1192` | `1192` | `1192` |
| `--breakpoint-xl`      | `1366` | `1366` | `1366` | `1366` | `1366` | `1366` |
| `--breakpoint-2xl`     | `1728` | `1728` | `1728` | `1728` | `1728` | `1728` |
| `--breakpoint-3xl`     | `2048` | `2048` | `2048` | `2048` | `2048` | `2048` |
| `--screen-current`     | `390`  | `768`  | `1192` | `1192` | `1192` | `1192` |
| `--screen-sm`          | `390`  | `390`  | `390`  | `390`  | `390`  | `390`  |
| `--screen-md`          | `768`  | `768`  | `768`  | `768`  | `768`  | `768`  |
| `--screen-lg`          | `1192` | `1192` | `1192` | `1192` | `1192` | `1192` |
| `--screen-xl`          | `1440` | `1440` | `1440` | `1440` | `1440` | `1440` |
| `--screen-2xl`         | `1920` | `1920` | `1920` | `1920` | `1920` | `1920` |
| `--screen-3xl`         | `2560` | `2560` | `2560` | `2560` | `2560` | `2560` |

<!-- /generated -->

Classes that re-declare token variables on an element:

<!-- generated:utilities variables-utilities -->

| Class              | Styles                                                                   |
| ------------------ | ------------------------------------------------------------------------ |
| `variables`        | Declares 1434 custom properties across 3 rules (themes and breakpoints). |
| `variables-themes` | Declares 873 custom properties across 3 rules (themes and breakpoints).  |
| `variables-sizing` | Declares 432 custom properties.                                          |
| `theme-default`    | Declares 291 custom properties.                                          |
| `theme-dark`       | Declares 291 custom properties.                                          |
| `sizing-sm`        | Declares 432 custom properties.                                          |
| `sizing-md`        | Declares 432 custom properties.                                          |
| `sizing-lg`        | Declares 432 custom properties.                                          |

<!-- /generated -->

## Root variables

Core always emits the `:root` custom properties: breakpoints, fonts, color primitives, the responsive-sizing scale with its per-breakpoint `@media` overrides, and the color system, bound to `:root` and `[data-theme="<mode>"]`. Any element with a `data-theme` attribute therefore switches the color system for its subtree, which is what the components' `theme` prop renders.

`--breakpoint-current` and `--screen-current` hold the min width and the design width of the active breakpoint as plain numbers. The [`vw` utilities](https://docs.systhema.app/fr/styling/viewport-units.md) divide by `--screen-current`, and scripts can read it too. Switch the preview's viewport to watch it change:

```tsx preview iframe height=200 title="--screen-current"
import { useEffect, useState } from 'react'

export default function Demo() {
  const [screen, setScreen] = useState('')

  useEffect(() => {
    const read = () =>
      setScreen(getComputedStyle(document.documentElement).getPropertyValue('--screen-current'))
    read()
    window.addEventListener('resize', read)
    return () => window.removeEventListener('resize', read)
  }, [])

  return (
    <p className="flex items-baseline gap-3">
      <code className="text-small color-label">--screen-current</code>
      <span className="text-h3 color-heading">{screen}</span>
    </p>
  )
}
```

One more rule ships with the root variables: when a `.header` is on the page, `html` and `body` get `scroll-padding-top: var(--header-height, 64px)`, so anchor jumps and `scrollIntoView` land below a sticky header instead of under it. A project with a taller or shorter header overrides `--header-height`.

## Variable utilities

`theme-<mode>` re-declares one color-system mode on an element and `sizing-<breakpoint>` one breakpoint's sizes, whatever the page around them uses. The class form and the `data-theme` attribute give the same colors; use the class where you can't set an attribute, for example from a CMS class field.

```tsx preview iframe height=300 title="theme-* scopes"
export default function Demo() {
  return (
    <div className="grid w-full grid-cols-2 gap-4">
      <div className="theme-default flex flex-col gap-2 rounded-lg border border-(--color-foundations-surface-border) bg-layout-main p-6">
        <p className="text-label color-label">theme-default</p>
        <p className="text-h4 color-heading">Light panel</p>
        <p className="text-small color-body">Stays light on a dark page.</p>
      </div>
      <div className="theme-dark flex flex-col gap-2 rounded-lg border border-(--color-foundations-surface-border) bg-layout-main p-6">
        <p className="text-label color-label">theme-dark</p>
        <p className="text-h4 color-heading">Dark panel</p>
        <p className="text-small color-body">Stays dark on a light page.</p>
      </div>
    </div>
  )
}
```

`sizing-sm` keeps the phone sizes inside an element at every viewport, which suits a narrow sidebar or a card that should not grow its type on desktops. Switch the preview to desktop: the first heading grows, the second keeps its phone size.

```tsx preview iframe height=280 title="sizing-* scopes"
export default function Demo() {
  return (
    <div className="flex w-full flex-col gap-6">
      <div>
        <p className="text-label color-label">Page sizing</p>
        <p className="text-h2 color-heading">Quarterly report</p>
      </div>
      <div className="sizing-sm">
        <p className="text-label color-label">sizing-sm</p>
        <p className="text-h2 color-heading">Quarterly report</p>
      </div>
    </div>
  )
}
```

The `variables` family declares whole sets at once:

- `variables` declares every token variable on the element: fonts, color primitives, the color system with its `data-theme` modes, and the responsive sizes with their breakpoint media queries.
- `variables-themes` declares only the color system and its modes.
- `variables-sizing` declares only the responsive sizes and their media queries.

Use them when Systhema renders inside a page that doesn't carry its `:root` variables, for example a widget embedded in another site whose page doesn't load Systhema's CSS at its root. Put `variables` on the widget's root element and everything inside it resolves. Each class weighs over a thousand declarations, so use it on a few roots, not on repeated elements.

## Referencing a variable

Reference token variables by name in your own CSS, inside the `app` layer so they win over Systhema's sub-layers (see [Cascade layers](https://docs.systhema.app/fr/styling/cascade-layers.md)):

```css title="src/app/(site)/globals.css"
@layer app {
  .pull-quote {
    border-left: 4px solid var(--color-foundations-decorative);
    padding-left: var(--gap-md);
    color: var(--color-typography-heading);
  }
}
```

In markup, Tailwind's variable shorthand reads them too: `bg-(--color-foundations-surface-bg)`, `gap-(--gap-sm)`. The names follow the token paths in kebab case; the [color](https://docs.systhema.app/fr/reference/tokens/colors.md), [spacing](https://docs.systhema.app/fr/reference/tokens/spacing.md) and [typography](https://docs.systhema.app/fr/reference/tokens/typography.md) references list every one.

> [!WARNING]
> With variable obfuscation on, a production build renames these variables. Your own CSS is rewritten with them, but names in content authored after the build (CMS inline styles) need an `ignore` entry. See [Production CSS optimization](https://docs.systhema.app/fr/styling/optimization.md#obfuscation).

## Responsive and state variants

The scope classes take variants: `lg:sizing-md` keeps tablet sizes on desktops, `md:theme-dark` switches a block to dark from `md` up. The root variables need no variants; their breakpoint overrides are built in.

## Customizing

The variables come from your tokens: change a value in Figma (or in `customTokens` in `systhema.config`) and the variable changes with it. To change one variable for part of the page, override it in CSS on that element, since custom properties inherit:

```css
@layer app {
  .compact {
    --gap-md: 16px;
  }
}
```

The `:root` declarations and the scope classes are always emitted. `SysthemaConfig.blocks` declares `rootVariables` and `variablesUtilities` toggles, but core doesn't read them, so setting either to `false` changes nothing.

## Related

- [Design tokens](https://docs.systhema.app/fr/concepts/design-tokens.md)
- [Themes and backgrounds](https://docs.systhema.app/fr/concepts/themes.md)
- [Viewport-relative spacing](https://docs.systhema.app/fr/styling/viewport-units.md)
- [Variables utilities reference](https://docs.systhema.app/fr/reference/utilities/variables-utilities.md)
