# Design tokens

The CSS custom properties Techno defines from theme settings, and how to use them in custom code.

`snippets/css-variables.liquid` turns theme settings into CSS custom properties on `:root`. Every color and size in the theme reads from these variables. Use them in your own CSS so custom code follows the active color palette and any color the merchant overrides.

## Settings tokens

### Color

Each color token is the merchant's setting when it's been changed, or the color palette's value otherwise.

| Token | Setting |
| --- | --- |
| `--tc-page` | "Page background" |
| `--tc-ink` | "Page text" |
| `--tc-panel` | "Panel background" |
| `--tc-surface` | "Button background" |
| `--tc-ink-strong` | "Headings and button text" |
| `--tc-ink-muted` | "Body text" |
| `--tc-ink-dim` | "Secondary text" |
| `--tc-line` | "Lines and borders" |
| `--tc-accent` | "Accent" |
| `--tc-danger` | "Alerts" |

### Type and layout

| Token | Setting | Value |
| --- | --- | --- |
| `--tc-font-family` | "Primary font" | The font family and its fallbacks |
| `--tc-font-style` | "Primary font" | The font's style |
| `--tc-font-weight` | "Primary font" | The font's regular weight |
| `--tc-font-weight-bold` | none | `700` |
| `--tc-font-size` | "Base text size" | 13px to 17px |
| `--tc-type-scale` | "Base text size" | Base size ÷ 14, from about 0.93 to 1.21 |
| `--tc-page-width` | "Page width" | `90rem`, `97.5rem` or `110rem` |
| `--tc-page-margin` | "Page margin" | 10px to 60px |
| `--tc-logo-width` | "Logo width" | 80px to 320px |
| `--tc-tap-min` | none | `44px` |

## Header tokens

Set only while Header › "Sticky header" is Always or On scroll up. With None, neither is set.

| Token | Set by | Value |
| --- | --- | --- |
| `--tc-header-height` | `<tc-sticky-header>`, on `:root` | The stuck header's measured height, rounded up to the pixel, including the space above it. Updates when the header's height changes. |
| `--tc-scroll-offset` | The header's stylesheet, on `:root` | `--tc-header-height` plus 8px, or 7rem plus the page's top spacing before the script has measured. Everything in the page column except the header uses it as `scroll-margin-block-start`, so anchors and focused elements land below the stuck header. |

To keep your own sticky element under a header that's set to Always, use `top: var(--tc-header-height, 0px)`.

## Docked bar tokens

| Token | Set by | Value |
| --- | --- | --- |
| `--tc-dock-bottom` | `:root`, in `snippets/css-variables.liquid` | The height of the bars docked to the bottom of the screen: `--tc-dock-buy` plus `--tc-dock-compare`, 0px when neither shows. The back-to-top button, its scroll margin and the space at the end of the page all grow by it, or by the screen's bottom safe area (`env(safe-area-inset-bottom)`) while that is larger, never by both. |
| `--tc-dock-buy` | The product page's sticky add-to-cart bar (`<tc-sticky-buy>`), on `:root`, while it shows | The bar's height, including the bottom safe area it pads itself by. Unset otherwise. |
| `--tc-dock-compare` | The compare bar (`<tc-compare-tray>`, see [Product comparison](../features/product-comparison.md)), on `:root`, while it shows | The bar's height, including the bottom safe area it pads itself by when no sticky add-to-cart bar is under it. Unset otherwise. The compare bar sits on top of the sticky add-to-cart bar, lifted by `--tc-dock-buy`, so the two never overlap. |
| `--tc-scroll-offset-end` | The back-to-top snippet's stylesheet, on `:root`, while "Show back-to-top button" is on; with it off, the Buy buttons block's stylesheet while the sticky add-to-cart bar shows, and the compare bar's while it shows | 44px plus 24px plus the larger of `--tc-dock-bottom` and the bottom safe area with the back-to-top button; `--tc-dock-bottom` plus 8px without it. Everything in the page column except the header uses it as `scroll-margin-block-end`, so focused elements stop above the button and the bar. The page column's end padding is at least this much too, so the footer's last line can be scrolled clear of them; without the back-to-top button, only while the sticky add-to-cart bar or the compare bar is docked (their `data-docked` attribute). |

Anything you fix to the bottom of the screen, such as a chat button, should sit at `bottom: calc(16px + max(env(safe-area-inset-bottom, 0px), var(--tc-dock-bottom, 0px)))`, as the back-to-top button does, so the sticky add-to-cart bar and the compare bar never cover it and it stays clear of the phone's home indicator or gesture bar. Take the larger of the two rather than adding them: a docked bar's height already includes the safe area. If your own code docks a bar to the bottom of the screen, place it at `bottom: calc(var(--tc-dock-buy, 0px) + var(--tc-dock-compare, 0px))`, on top of the theme's bars, and set `--tc-dock-bottom` on `document.documentElement` to `calc(YOUR-HEIGHT + var(--tc-dock-buy, 0px) + var(--tc-dock-compare, 0px))` while it shows, removing it when it hides, so the back-to-top button moves above all of them. YOUR-HEIGHT is the bar's full height, including any safe-area padding you give it.

## Derived tokens

Derived tokens are mixed from the color tokens with `color-mix()`, so they follow every palette.

### Hairlines

Decorative dividers and rules, from faintest to strongest. Don't use these for the border of anything a shopper interacts with.

| Token | Mix |
| --- | --- |
| `--tc-hairline-faint` | 9% line |
| `--tc-hairline-row` | 12% line |
| `--tc-hairline-soft` | 16% line |
| `--tc-hairline` | 22% line |
| `--tc-hairline-panel` | 28% line |
| `--tc-hairline-rule` | 30% line |
| `--tc-hairline-strong` | 35% line |
| `--tc-hairline-danger` | 35% danger |

### Borders

| Token | Mix | Use for |
| --- | --- | --- |
| `--tc-border-control` | 65% line | Inputs, buttons, checkboxes |
| `--tc-border-control-accent` | 72% accent | Accent-colored controls |
| `--tc-border-control-danger` | 70% danger | Controls in an error state |
| `--tc-border-panel` | 65% line | Panel edges |
| `--tc-border-panel-strong` | 80% line | Emphasized panels |

> **Warning:** `--tc-border-control` and `--tc-border-control-accent` are the WCAG 1.4.11 non-text contrast floor, 3:1, measured over `--tc-page`, `--tc-panel` and `--tc-surface` in all four color palettes. Use them, not a hairline, for any control border, and don't use a lighter mix.

### Fills and effects

| Token | Mix | Use for |
| --- | --- | --- |
| `--tc-fill-hover` | 20% line over page | Hover background |
| `--tc-fill-hover-strong` | 24% line over page | Hover on emphasized controls |
| `--tc-fill-hover-accent` | 17% accent over page | Hover on accent controls |
| `--tc-ink-dim-on-fill` | 25% strong ink into dim ink | Secondary text on a hover fill |
| `--tc-accent-on-fill` | 26% strong ink into accent | Accent text on a hover fill |
| `--tc-glow` | Line, per palette | Hover bloom. `transparent` in Blueprint and Monolith. |
| `--tc-scrim` | Page (dark palettes) or strong ink (light palettes) | The backdrop behind open drawers |
| `--tc-filter-mono` | A CSS `filter`, not a color: `brightness(0) invert(0.8)` on the dark palettes, `brightness(0) invert(0.2)` on the light ones | Monochrome logos in the Logo list: every opaque pixel becomes one grey that suits the palette |
| `--tc-scanlines` | Per palette | The scanline overlay, or `none` |
| `--tc-gridlines` | 11% line over page | The grid overlay, or `none` |

## Using tokens

```css
.my-spec-callout {
  background: var(--tc-panel);
  border: 1px solid var(--tc-border-panel);
  color: var(--tc-ink-muted);
  font-size: calc(13px * var(--tc-type-scale));
}

.my-spec-callout:hover {
  background: var(--tc-fill-hover);
  box-shadow: 0 0 20px var(--tc-glow);
}
```

* Scale every font size by `--tc-type-scale` so "Base text size" applies to your code.
* Keep text in any field you add at 16px or more on touch screens, or iOS Safari zooms the page when a shopper taps it: for example `font-size: max(16px, calc(13px * var(--tc-type-scale)))` inside `@media (pointer: coarse)`. Fields with the theme's `.tc-input`, `.tc-select`, `.tc-textarea` or `.tc-qty__input` class already do.
* Don't hard-code colors. A hex value won't change with the color palette or the merchant's overrides.
* When you put text on a color other than the one it was designed for, measure contrast in all four color palettes.

## See also

* [Colors & palettes](../core-concepts/theme-styles-and-color.md): the merchant view of these tokens
* [Custom code](custom-code.md): where to put CSS
