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.

raw
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.

semantic
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:

JSX · Tailwind utilities (recommended)
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
CSS · var() reference
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
JSX · inline style
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.

identity
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

define + theme + consume
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:

root override
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
scoped override
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.

density-scale-y in product CSS
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.