FOUNDATIONS
Tokens
Damo UI uses a three-layer token architecture. Raw scales are private; semantic pairs are the public surface; identity tokens are component-specific overrides. Every token is a CSS variable so theme, palette, and density switch live without rebuilds.
1. Raw palette (private)
Numeric scales like --plum-*, --gold-*, --paper-*. These power the semantic layer but are not exposed as Tailwind utilities — using them directly couples your code to brand specifics.
1:root {
2 --plum-500: #4a2a4f;
3 --gold-500: #d6a64f;
4 --paper-100: #f8f3e6;
5 /* …private scales, never used directly in product code */
6}
2. Semantic tokens (public)
Paired bg/fg pairs: --background + --foreground, --primary + --primary-foreground, etc. This is the layer product code should consume.
1:root {
2 --background: var(--paper-100);
3 --foreground: var(--plum-900);
4 --primary: var(--gold-500);
5 --primary-foreground: var(--plum-900);
6 /* …public bg+fg pairs */
7}
Consuming semantic tokens
Three equivalent ways. Pick the one that fits where you are:
1// In JSX: use the lib's Tailwind utilities
2<div className="bg-card text-card-foreground border-2 border-memphis p-4">
3 <h3 className="text-foreground font-display text-xl">Title</h3>
4 <p className="text-muted-foreground">Body copy goes here.</p>
5 <button className="bg-primary text-primary-foreground px-3 py-1.5">
6 Save
7 </button>
8</div>
9
1/* In a stylesheet: read the variable directly */
2.callout {
3 background: var(--card);
4 color: var(--card-foreground);
5 border: 2px solid var(--memphis-border-color);
6 box-shadow: var(--shadow-memphis);
7 padding: 1rem;
8 border-radius: var(--radius-md);
9}
10
1// In JSX inline style: same vars, no Tailwind dependency
2<div
3 style={{
4 background: 'var(--primary)',
5 color: 'var(--primary-foreground)',
6 padding: '0.75rem 1.25rem',
7 boxShadow: 'var(--shadow-memphis)',
8 }}
9>
10 Inline-styled chip
11</div>
12
3. Identity tokens
Component-specific overrides. They reference the semantic layer but allow narrow adjustments — --nav-on-dark-*, --badge-*, --chart-*. Add your own when a component needs a value the semantic layer doesn't express.
1:root {
2 --nav-on-dark-bg: var(--plum-900);
3 --badge-success: var(--success);
4 --chart-1: var(--primary);
5 /* …component-specific overrides */
6}
Adding a custom identity token
1/* 1. Declare a new identity token at :root */
2:root {
3 --pricing-card-accent: var(--gold-500);
4 --pricing-card-accent-foreground: var(--plum-900);
5}
6
7/* 2. Override per theme/palette as needed */
8:root[data-theme='dark'] {
9 --pricing-card-accent: var(--gold-300);
10 --pricing-card-accent-foreground: #0a0a0a;
11}
12
13/* 3. Consume it in the component */
14.pricing-card {
15 background: var(--pricing-card-accent);
16 color: var(--pricing-card-accent-foreground);
17}
18
Overriding tokens
Any token is just a CSS variable. Override at :root to change the whole document, or scope to a wrapper class for an island:
1/* Override at the root — affects the whole document */
2:root {
3 --primary: hsl(280 70% 55%);
4 --primary-foreground: #ffffff;
5 --radius-md: 12px;
6 --shadow-memphis: 4px 4px 0 #000;
7}
8
1/* Override scoped to a subtree */
2.brand-island {
3 --primary: hsl(150 60% 45%);
4 --memphis-shadow-color: hsl(150 60% 25%);
5}
6
7/* Now any <Button variant="primary" /> inside .brand-island
8 uses the green primary, while the rest of the page is unchanged. */
9
Density & the multiplier pattern
The density attribute flips a single multiplier: --density-scale-y. Bake it into your component spacing and density switching becomes free.
1/* Tailwind v4 multiplies every spacing utility by the lib's --spacing
2 token, which itself is calc(0.25rem * var(--density-scale-y)). Use any
3 spacing utility (p-4, gap-3, mt-2, …) and density flips component
4 spacing without re-rendering React. */
5.my-card {
6 padding-block: calc(1rem * var(--density-scale-y));
7 padding-inline: 1rem;
8}
9
10/* compact → padding-block = 16px * 0.75 = 12px
11 normal → padding-block = 16px * 1.00 = 16px
12 comfortable → padding-block = 16px * 1.25 = 20px */
13
Switching modes
Three switchers live on <html>: data-theme, data-palette, data-density. They are orthogonal — any combination works. See Theming for the full wiring.
Want to author a custom theme? The Theme Generator edits all three layers visually and exports CSS, Tailwind, or JSON.