---
title: Theming
description: Four override layers, from global tokens down to a single component.
category: Foundations
status: beta
since: 0.4.0
tags: [theme, tokens, dark-mode, contrast, css-variables]
syncStatus: reviewed
verified: false
---

## The model

No component hard-codes a color; every visual reads a CSS custom property, so re-theming is a stylesheet change. The four layers run widest first. Pick the **narrowest** one that solves the problem.

| Layer | Where it lives | Scope |
|-------|----------------|-------|
| Global tokens | `:root` in `base.css` | Everything |
| Theme selectors | `[data-theme='dark']`, `[data-tx-contrast='high']` | A whole mode |
| BUI tokens | `--tx-bui-*` | The AI suite |
| Component hooks | `--tx-<component>-*` | One component, or one subtree |

The token inventory is in [Design Foundations](./foundations.en.mdc).

## Global tokens

Redefine tokens after importing `base.css`. Components resolve them at paint time, so an override applies to the whole subtree below it.

:::TuffCodeBlock{lang="css"}
---
code: |
  @import '@talex-touch/tuffex/base.css';

  :root {
    --tx-color-primary: #7c5cff;
    --tx-border-radius-base: 8px;
    --tx-transition-duration: 0.24s;
  }
---
:::

`--tx-color-primary-soft` and `--tx-coloring-border-color` are `color-mix()` expressions on `--tx-color-primary`, so they follow it. The primary ramp (`--tx-color-primary-light-*`, `--tx-color-primary-dark-2`) is fixed hex, and the default focus ring reads from it; override the ramp too when rebranding.

## Dark mode

Dark is a selector, not a media query: set `data-theme="dark"` or the `dark` class on any ancestor.

:::TuffCodeBlock{lang="html"}
---
code: |
  <html data-theme="dark">
  <!-- or -->
  <html class="dark">
---
:::

- The two are equivalent, so Tuffex drops into a Tailwind app (`.dark`) or a `data-theme` app without an adapter.
- Set either on a subtree to get a dark island on a light page.

## High contrast

A fourth palette that raises text and border contrast in light and dark.

| Trigger | Effect |
|---------|--------|
| `html[data-tx-contrast='high']` or `html.contrast` | On, explicitly |
| `@media (prefers-contrast: more)` | On, following the OS |
| `html[data-tx-contrast='normal']` | Opts **out** of the media query |

With `data-theme="dark"` and either high-contrast trigger, the dark high-contrast palette applies; the two themes don't stack.

## AI suite tokens

`--tx-bui-*` came over with the Beautiful UI components (surface, inset, field, ink, and accent / green / orange / red with matching tints) and has its own dark block under the same `[data-theme='dark']` / `.dark` selectors. Override it when AI surfaces shouldn't inherit your brand hue:

:::TuffCodeBlock{lang="css"}
---
code: |
  :root {
    --tx-bui-accent: #7c5cff;
    --tx-bui-accent-tint: #f0ecff;
  }
---
:::

## Component hooks

`--tx-<component>-*` restyles one component without touching the global palette. Names are predictable (`--tx-collapse-header-bg`, `--tx-avatar-ring-color`, `--tx-progress-height`), and each has a fallback. They inherit, so setting them on a wrapper themes that subtree only.

:::TuffCodeBlock{lang="vue"}
---
code: |
  <template>
    <section class="settings-panel">
      <TxCollapse v-model="open">
        <TxCollapseItem title="Appearance" name="appearance">…</TxCollapseItem>
      </TxCollapse>
    </section>
  </template>

  <style scoped>
  .settings-panel {
    --tx-collapse-radius: 6px;
    --tx-collapse-header-bg: transparent;
    --tx-collapse-border: rgba(148, 163, 184, 0.24);
  }
  </style>
---
:::

Each component page lists the hooks it reads. Use them instead of descendant selectors into component internals: custom properties are the supported surface, class names are not.

## Caveats

- When a component exposes a fill hook, set the token, not `background-color`: some fills are `background-image` gradients, and `background-color` paints behind them.
- Import `base.css` exactly once. With two copies, the one that loads last wins, which is rarely the one you edited.

## Technologies

- Tokens and theme selectors: `packages/tuffex/packages/components/style/variables.scss`.
- AI suite tokens: `packages/tuffex/packages/components/style/bui-tokens.scss`.
- Runtime entry: `@talex-touch/tuffex/base.css`.
