Use when the user asks to set up, install, or migrate to the Unify Theme for reablocks — a production-ready theme pack built on a three-level design-token system (primitives → semantic → Tailwind utilities) synced with the Unify Figma library. Covers the single-file `themeUnify.ts` TypeScript download, the Reablocks Figma Plugin export for CSS layers, file layout (root.css / dark.css / light.css / tw.css / common.css), per-component detail tokens (e.g. `--buttons-details-height-core-icon-lg`)...
Scanned 5/27/2026
Install via CLI
openskills install reaviz/skills---
name: unify-theme
description: Use when the user asks to set up, install, or migrate to the Unify Theme for reablocks — a production-ready theme pack built on a three-level design-token system (primitives → semantic → Tailwind utilities) synced with the Unify Figma library. Covers the single-file `themeUnify.ts` TypeScript download, the Reablocks Figma Plugin export for CSS layers, file layout (root.css / dark.css / light.css / tw.css / common.css), per-component detail tokens (e.g. `--buttons-details-height-core-icon-lg`), re-branding via primitive scale, customization without TypeScript changes, and Tailwind v4+ wiring.
---
# Unify Theme
The **Unify Theme** is a complete, production-ready theme pack for reablocks built on a **three-level design-token system** that exposes tokens at every level and for every component — not just `primary` / `secondary` / `accent`, but per-component detail tokens like `--buttons-details-height-core-icon-lg`, `--inputs-details-corner-radius-primary`, and `--tabs-details-stroke-width-underline-lg`.
This means you can re-skin Unify at any granularity:
- Change the brand color → every component updates.
- Change the semantic `--background-brand-base` → every brand surface updates.
- Change just `--buttons-details-corner-radius-base` → only button radii change.
## When to use Unify
Use Unify when **any** of these apply:
- The user mentions Figma, the Unify Figma library, or the Reablocks Figma Plugin.
- The user wants design tokens synced end-to-end from Figma to production.
- The user wants to re-skin reablocks at fine granularity (per-component dimensions, radii, padding) without writing Tailwind overrides or per-component TypeScript.
- The user references `themeUnify.ts`, `root.css` / `dark.css` / `light.css` / `tw.css`, or per-component detail tokens like `--buttons-details-*`, `--inputs-details-*`, `--tabs-details-*`.
- The user is on Tailwind v4+ and wants a complete token system rather than ad-hoc class overrides.
Do **not** use Unify when:
- The user just wants minor color/variant tweaks → use `extendTheme(theme, …)` against the default theme instead (see [main reablocks skill](../SKILL.md)).
- The user is not on Tailwind v4+ or reablocks v10+ → fall back to the default theme.
- The user explicitly asks for the default theme.
## Three-level token architecture
Unify is structured as three layers that cascade. A change in Figma travels down through all three and reaches every reablocks component without any code change.
```
FIGMA VARIABLES
│ export via Figma plugin
▼
┌──────────────────────────────────────────────────────────────────────┐
│ LEVEL 1 · PRIMITIVES root.css │
│ • Color scales (50–1000 + alpha) │
│ • Spacing scale │
│ • Sizing scale │
│ • Corner-radius scale │
│ • Per-component detail tokens │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ LEVEL 2 · SEMANTIC TOKENS dark.css / light.css │
│ Role-named aliases — one file per Figma mode. │
│ background-*, content-text-*, content-assets-*, stroke-*, │
│ effects-*, gradient-* │
└──────────────────────────────────────────────────────────────────────┘
│ registered in
▼
┌──────────────────────────────────────────────────────────────────────┐
│ LEVEL 3 · TAILWIND UTILITIES tw.css │
│ @theme inline { … } — exposes every token as a Tailwind utility │
└──────────────────────────────────────────────────────────────────────┘
│ consumed by
▼
reablocks components (no code changes)
```
The **per-component detail tier** in Level 1 is what makes Unify uniquely customizable — you can tune individual component dimensions (button heights, input padding, tab underline thickness) without writing Tailwind overrides, because each component theme already references those variables through arbitrary-value classes like `h-(--buttons-details-height-core-icon-lg)`.
## Exporting tokens from Figma
The official [**Reablocks Figma Plugin**](https://www.figma.com/community/plugin/1285928654186754176/reablocks-figma-plugin) keeps designs and code in sync without hand-copying tokens.
### Running the plugin
1. Open the Figma design file.
2. Run **Plugins → Reablocks Figma Plugin → Select Mode Variant (Light/Dark) → Generate**.
3. Drop the generated tokens into the styles directory (`root.css`, `dark.css`, `light.css`, `tw.css`).
### Download the pre-built TypeScript theme
For the TypeScript half of Unify, grab the single-file bundle — every component theme plus the composed `ReablocksTheme` in one file:
📄 **[Download themeUnify.ts](/assets/themeUnify.ts)**
Drop it anywhere in `src/` (e.g. `src/themeUnify.ts` or `src/shared/utils/Theme/themeUnify.ts`) and import the composed `theme`:
```tsx
import { theme } from './themeUnify';
```
Then jump to [Wire it up at the app root](#wire-it-up-at-the-app-root). The CSS token layers (`root.css` / `dark.css` / `light.css` / `tw.css`) still come from the Figma plugin export — `themeUnify.ts` references those CSS variables and Tailwind utilities and expects them to be present in the stylesheet.
> Prefer per-component files for easier diffing and overrides? `themeUnify.ts` also exports each component theme individually (`buttonTheme`, `inputTheme`, …), so you can split it back into the [Optional: split per component](#optional-split-per-component) layout below by hand, or use it as-is.
## Prerequisites
```bash
npm install reablocks
npm install -D tailwindcss @tailwindcss/postcss postcss postcss-nested postcss-preset-env
```
Unify is built for **Tailwind CSS v4+** and **reablocks v10+** with React 18+.
## File layout
The theme is split into two parts: **CSS token files** (generated by the Figma plugin) and a **TypeScript component-theme module** (the downloadable `themeUnify.ts`).
### Recommended: single-file TypeScript theme
The simplest layout — one TS file alongside the CSS layers from the plugin:
```
src/
├─ assets/
│ └─ styles/
│ ├─ index.css # entry — imports the others
│ ├─ common.css # base body styles & global resets
│ ├─ root.css # Layer 1 — primitive tokens (incl. per-component detail)
│ ├─ dark.css # Layer 2 — semantic tokens (default / dark mode)
│ ├─ light.css # Layer 2 — semantic tokens overridden for light mode
│ └─ tw.css # Layer 3 — Tailwind @theme registration
└─ themeUnify.ts # every component theme + composed ReablocksTheme
```
### Optional: split per component
If you'd rather maintain each component theme in its own file (easier to diff and review individual changes), split `themeUnify.ts` along its `// region` markers:
```
src/
├─ assets/styles/ # same as above
└─ shared/
└─ utils/
└─ Theme/
├─ index.ts
├─ theme.ts # composes ReablocksTheme
└─ components/ # one file per component theme
├─ ButtonTheme.ts
├─ InputTheme.ts
├─ SelectTheme.ts
└─ …
```
Both layouts are functionally identical. The only hard requirement is the CSS import order, before the app renders.
## Step 1 — Install the CSS token layers
### `index.css` — entry point
```css
@import "./common.css";
@import "./light.css";
@import "./dark.css";
@import "./root.css";
@import "./tw.css";
```
**Import order matters.** `light.css` and `dark.css` must come before `root.css` is fully registered as Tailwind's source of truth, and `tw.css` must come **last** so it can reference all the variables.
### `common.css` — global base styles
```css
body {
color-scheme: dark;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
@apply font-sans text-base;
line-height: 1.3;
}
body,
#root {
margin: 0;
display: flex;
min-height: 100vh;
width: 100vw;
@apply bg-background-neutral-canvas-base text-content-text-on-color-light-dark;
}
```
### `root.css` — Layer 1: primitives + per-component detail
The **raw palette plus per-component detail tokens**. This is the file you regenerate from Figma on every design-system bump:
- **Color scales** — every hue has a 50–1000 scale plus an alpha (`-a-`) variant.
- **Spacing** — `--spacing-padding-{4xs..8xl}` and `--spacing-space-between-{3xs..4xl}`.
- **Sizing** — `--sizing-asset-{3xs..4xl}`, `--sizing-size-tokens-{4xs..6xl}`, `--sizing-dividers-details-*`.
- **Corner radius** — `--corner-radius-{sharp, sm, primary, lg, pill}`.
- **Per-component detail tokens** — what makes Unify deeply customizable: `--buttons-details-height-core-icon-lg`, `--inputs-details-corner-radius-primary`, `--tabs-details-stroke-width-underline-lg`, `--calendar-details-vertical-padding-inside`, `--navigation-details-corner-radius-row-item`, `--notifications-details-asset-size-base`, `--selectors-details-width-toggle-lg`, `--table-details-padding-row-cell-lg`, …
```css
:root,
:host {
/* Colors */
--color-blue-hyperstream-500: #3476ff;
--color-blue-hyperstream-600: #105eff;
/* …more brand scales… */
/* Spacing */
--spacing-padding-base: 16px;
/* Corner radius */
--corner-radius-primary: 6px;
/* Per-component detail — the Unify differentiator */
--buttons-details-height-core-icon-lg: var(--sizing-size-tokens-base);
--inputs-details-corner-radius-primary: var(--corner-radius-primary);
--tabs-details-stroke-width-underline-lg: 2px;
/* …more per-component detail tokens */
}
```
### `dark.css` — Layer 2: semantic tokens (default)
Maps role-named aliases onto primitives:
| Family | Examples | Purpose |
| --- | --- | --- |
| `background-*` | `background-brand-base`, `background-neutral-canvas-base`, `background-neutral-raised-1..6` | Surfaces, raised cards, canvas |
| `content-text-*` | `content-text-neutral-base`, `content-text-brand-1`, `content-text-semantic-warning-2` | Text colors |
| `content-assets-*` | `content-assets-brand-base`, `content-assets-semantic-error-1` | Icon / asset fills |
| `stroke-*` | `stroke-neutral-3`, `stroke-brand-base`, `stroke-focused-highlight` | Borders / outlines |
| `effects-*` | `effects-shadows-base-md`, `effects-focused-base` | Shadows and focus rings |
| `gradient-*` | `gradient-brand-50`, `gradient-neutral-700` | Multi-stop gradient stops |
```css
:root,
:host {
--reablocks-theme: dark;
--background-neutral-canvas-base: var(--color-neutrals-darth-abyss-a-1000);
--background-brand-base: var(--color-blue-hyperstream-a-1000);
--background-semantic-error-base: var(--color-red-crimson-wrath-a-1000);
--content-text-neutral-base: var(--color-neutrals-kyber-crystal-a-1000);
--content-text-brand-base: var(--color-blue-hyperstream-a-1000);
--stroke-neutral-3: var(--color-neutrals-kyber-crystal-a-300);
--stroke-focused-highlight: var(--color-blue-hyperstream-a-500);
/* …accents 1-4, info, success, warning, gradient stops, shadows… */
}
```
### `light.css` — Layer 2: light-mode overrides
Same shape as `dark.css` but scoped to a selector that activates light mode. Unify recognizes **six selector aliases** so any theme-switch library works: `.theme-light`, `.light`, `[data-theme='light']` (and their `&`-prefixed forms).
```css
:root,
:host {
.theme-light,
&.theme-light,
.light,
&.light,
[data-theme='light'],
&[data-theme='light'] {
--reablocks-theme: light;
--background-neutral-canvas-base: var(--color-neutrals-kyber-crystal-a-1000);
--content-text-neutral-base: var(--color-neutrals-darth-abyss-a-1000);
/* …same tokens flipped for light surfaces… */
}
}
```
Because both modes target the same semantic names, every component automatically re-skins when the theme attribute changes — **no component code to update**.
### `tw.css` — Layer 3: Tailwind registration
This is the bridge: it tells Tailwind v4 to expose every CSS variable as a utility class.
1. `@import 'tailwindcss';` — pull Tailwind v4 in.
2. `@source "node_modules/reablocks";` — keeps reablocks's compiled classes alive through Tailwind's purge.
3. `@custom-variant dark/light/disabled-within` — registers variant selectors matching the class/attribute on the root element.
4. `@theme inline { … }` — maps every CSS variable to a `--color-*`, `--radius-*`, `--text-*`, `--font-*` token Tailwind recognizes.
```css
@import 'tailwindcss';
@source "node_modules/reablocks";
@source inline("line-clamp-{1..10}");
@custom-variant disabled-within (&:has(input:is(:disabled), textarea:is(:disabled), button:is(:disabled)));
@theme inline {
--font-sans: 'Inter', sans-serif;
--font-mono: 'Share Tech Mono', monospace;
--radius-primary: var(--corner-radius-primary);
--radius-lg: var(--corner-radius-lg);
--radius-pill: var(--corner-radius-pill);
/* Reset default Tailwind palettes — Unify owns the names */
--color-red-*: initial;
--color-blue-*: initial;
--color-green-*: initial;
/* Re-register every primitive + semantic token as a utility */
--color-background-brand-base: var(--background-brand-base);
--color-content-text-neutral-1: var(--content-text-neutral-1);
--color-stroke-semantic-error-3: var(--stroke-semantic-error-3);
/* …all tokens follow the same pattern */
}
```
The full file is generated alongside `root.css` by the Figma plugin and produces utilities like:
- `bg-background-brand-base` / `bg-background-neutral-raised-2`
- `text-content-text-neutral-1` / `text-content-text-semantic-error-base`
- `border-stroke-brand-3` / `border-stroke-neutral-5`
- `fill-content-assets-brand-base`
- `rounded-(--corner-radius-primary)` (arbitrary-value form)
- `h-(--buttons-details-height-core-icon-lg)` (per-component detail)
### `postcss.config.ts`
```ts
export default {
plugins: {
'@tailwindcss/postcss': {}
}
};
```
## Step 2 — Add the component-theme module
The TypeScript half maps reablocks's `ReablocksTheme` slots onto Tailwind utilities that reference the CSS tokens. Each component theme references both Tailwind utilities (semantic colors) **and** the per-component detail variables (dimensions, radii, gaps), so component dimensions are tunable from CSS alone.
For example, the button theme references the detail tier directly:
```ts
// excerpt from themeUnify.ts (or ButtonTheme.ts in the split layout)
sizes: {
small: 'h-(--buttons-details-height-core-icon-sm) text-xs px-(--buttons-details-horizontal-padding-sm)',
medium: 'h-(--buttons-details-height-core-icon-md) text-sm px-(--buttons-details-horizontal-padding-md)',
large: 'h-(--buttons-details-height-core-icon-lg) text-base px-(--buttons-details-horizontal-padding-lg)'
}
```
Change `--buttons-details-height-core-icon-md` in `root.css` and every medium button across the app re-flows — no component override, no Tailwind class change.
### Single-file shape (`themeUnify.ts`)
```ts
import { type ReablocksTheme } from 'reablocks';
// region: ArrowTheme
export const arrowTheme = { /* … */ };
// region: ButtonTheme
export const buttonTheme = { /* … */ };
// region: InputTheme
export const inputTheme = { /* … */ };
// …more component themes
export const theme: ReablocksTheme = {
components: {
arrow: arrowTheme,
button: buttonTheme,
input: inputTheme,
// …more slots
}
};
```
### Split-layout root composition (`shared/utils/Theme/theme.ts`)
```ts
import { type ReablocksTheme } from 'reablocks';
import { arrowTheme } from './components/ArrowTheme';
import { avatarTheme } from './components/AvatarTheme';
import { buttonTheme } from './components/ButtonTheme';
import { inputTheme } from './components/InputTheme';
import { selectTheme } from './components/SelectTheme';
import { tabsTheme } from './components/TabsTheme';
// …more imports
export const theme: ReablocksTheme = {
components: {
avatar: avatarTheme,
arrow: arrowTheme,
button: buttonTheme,
input: inputTheme,
select: selectTheme,
tabs: tabsTheme,
// …more slots
}
};
```
### Components included
| Category | Components |
| --- | --- |
| **Inputs** | `input`, `textarea`, `select`, `dateInput`, `checkbox`, `radio`, `toggle`, `range`, `field` |
| **Actions** | `button`, `chip`, `kbd`, `pager`, `sort` |
| **Overlays** | `dialog`, `drawer`, `tooltip`, `popover`, `menu`, `contextMenu`, `commandPalette`, `notification`, `backdrop` |
| **Layout** | `card`, `divider`, `collapse`, `tabs`, `stepper`, `breadcrumbs`, `callout` |
| **Data** | `tree`, `jsonTree`, `list`, `calendar`, `calendarRange`, `redact`, `ellipsis`, `dateFormat` |
| **Display** | `avatar`, `avatarGroup`, `badge`, `arrow`, `skeleton`, `dotsLoader`, `typography` |
| **Navigation** | `navigation` |
## Wire it up at the app root
```tsx
// src/index.tsx
import { ThemeProvider } from 'reablocks';
import { theme } from './themeUnify'; // or 'shared/utils/Theme' for the split layout
import './assets/styles/index.css'; // pulls in all CSS layers
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
```
## Unify-specific customization
For generic single-component overrides via `extendTheme`, see the main [reablocks skill](../SKILL.md). The patterns below are unique to Unify because they leverage its token tiers.
### Re-brand by replacing the primitive scale
Open `root.css` and replace the `--color-blue-hyperstream-*` scale with the new brand hex codes. Every semantic alias (`--background-brand-base`, `--content-text-brand-1`, …) and every component that uses them updates automatically.
```css
/* root.css */
--color-blue-hyperstream-500: #ff6b35;
--color-blue-hyperstream-600: #e55a2b;
--color-blue-hyperstream-a-1000: #ff6b35;
```
**Better path:** do this in Figma and re-export via the plugin.
### Tune a single component without code
Per-component detail tokens let you reshape components from CSS alone.
Make every medium button shorter:
```css
/* root.css */
--buttons-details-height-core-icon-md: 28px; /* was 32px */
```
Soften input corners across the app:
```css
--inputs-details-corner-radius-primary: 10px; /* was 6px */
```
Thicker tab underline:
```css
--tabs-details-stroke-width-underline-lg: 4px; /* was 2px */
```
### Adjust the spacing or sizing scale
Spacing, sizing, and radius scales are exposed as CSS variables in `root.css`. Change a single value and every component that references it (via Tailwind's `p-(--spacing-padding-base)` or `h-(--sizing-size-tokens-sm)` arbitrary-value syntax) re-flows:
```css
--spacing-padding-base: 20px; /* was 16px */
--corner-radius-primary: 8px; /* was 6px */
--sizing-size-tokens-base: 44px; /* was 40px */
```
### Add a new semantic role
Need a `tertiary` brand or a fifth accent? Add the semantic alias in `dark.css` (and mirror it in `light.css`), then register it in `tw.css` under `@theme inline`:
```css
/* dark.css */
--background-tertiary-base: var(--color-purple-fuchsia-a-1000);
/* tw.css */
@theme inline {
--color-background-tertiary-base: var(--background-tertiary-base);
}
```
Now `bg-background-tertiary-base` is a valid utility in components.
## Troubleshooting
**Tailwind utilities like `bg-background-brand-base` are unknown.**
The `@theme inline { … }` block in `tw.css` must register the variable as a `--color-*` (or `--radius-*`, `--text-*`, etc.) for Tailwind to emit a utility. If you added a new semantic token, also alias it in `tw.css`.
**Default Tailwind palettes leak in.**
Unify deliberately resets `--color-blue-*`, `--color-red-*`, etc. to `initial` so its own scale owns those names. For a one-off stock-Tailwind color, use an arbitrary value (`bg-[#3b82f6]`) or remove the relevant `initial` line in `tw.css`.
**reablocks components render unstyled in production.**
Tailwind's purger is dropping reablocks classes. Verify the `@source` directive in `tw.css` points at `node_modules/reablocks` — the path is relative to the CSS file, so monorepos may need a different number of `..` segments.
**Per-component detail tokens don't take effect.**
Verify the override is declared **after** `root.css` in the CSS import chain. If you put bespoke tokens in `overrides.css`, import it last:
```css
@import "./root.css";
@import "./overrides.css"; /* must come after root.css */
@import "./tw.css";
```
## Recommended cadence
- **Designers** keep Figma variables as the single source of truth.
- **Engineers** re-run the plugin on design-system bumps and commit the regenerated CSS — component themes reference Tailwind utilities, so there is almost never any TypeScript to update.
- **Don't hand-edit** the generated files; put bespoke tokens in a separate `overrides.css` imported after `root.css`.
## Reference
- Token export: [Reablocks Figma Plugin](https://www.figma.com/community/plugin/1285928654186754176/reablocks-figma-plugin)
- TypeScript theme download: `themeUnify.ts` (single-file, all component themes + composed `ReablocksTheme`)
- Main reablocks skill (default theme, `extendTheme`, hooks): [../SKILL.md](../SKILL.md)
No comments yet. Be the first to comment!