Skip to main content
Gremorie

Overview

The three token layers - primitive, semantic and chart - and how each is consumed.

Foundation

Applied

Consuming

Tokens are the chromatic and dimensional vocabulary of Gremorie. Instead of painting a border as #E5E5E5 or setting a padding to 16px, you use a word from the vocabulary, like border or p-4, that points to a value that can change with the theme, with dark mode, or simply because the brand evolved.

Gremorie organizes that vocabulary in three layers, each with a clear job.

The three-layer model

Data flows one way, from raw value to intent to data color:

┌──────────────────────┐
│ 1. PRIMITIVE         │  --color-clay-500, --radius-base, --spacing
│    what exists       │  fixed values, mode-invariant
└──────────┬───────────┘
           │ referenced via var(--color-*)
┌──────────▼───────────┐
│ 2. SEMANTIC          │  --primary, --background, --border
│    what it means     │  resolved per theme (data-theme) and mode (.dark)
└──────────┬───────────┘
           │ referenced via var(--primary) / var(--color-*)
┌──────────▼───────────┐
│ 3. CHART             │  five schemes keyed to the shape of the data
│    how data reads    │  sequential, categorical, divergent, status, comparison
└──────────────────────┘

Edit a primitive and every theme referencing it updates. Swap the theme and every component repaints. Components never skip a layer downward: they consume semantics, and semantics consume primitives.

1. Primitive, or what exists

Primitives are raw values with no opinion. A --color-blue-600 is just "that specific blue". A --radius-lg is just a length. They live in the native Tailwind v4 namespace (--color-*, --radius-*, --shadow-*, and so on), which means utilities like bg-blue-600 or rounded-lg show up automatically, with no bridge code.

You rarely touch primitives directly in components. Their job is to feed the other two layers.

2. Semantic, or what those values mean

Semantics translate primitives into intent. Instead of asking for "the gray-900", the component asks for "the foreground". When the theme changes, the foreground swaps primitives on its own and the component never finds out.

This is where Gremorie supports its bundled brand themes, six in total (Default, Claude, ChatGPT, Gemini, Mistral, Perplexity), each with light and dark modes. All component chrome talks to this layer.

3. Chart, or why dataviz is different

UI colors and chart colors follow different rules. A blue button exists in a context. Five blues in a heatmap need to be ordered by lightness, not by brand. So chart tokens live in their own layer, with five canonical schemes (Sequential, Categorical, Divergent, Status, Comparison), each for a specific shape of data.

What each section covers

LayerPagesWhat you get
PrimitiveColors, Typography, Spacing, Radius, Shadow, MotionEvery raw value with sample, token, utility class, and use case
SemanticColorsThe intent-to-primitive truth table per theme, light and dark
ChartSequential, Categorical, Divergent, Status, ComparisonWhen to use each scheme, its tokens, and the decision matrix

How to read this section

  • Styling a UI surface (button, card, input)? Go to Semantic and find the token with the right intent.
  • Building a chart? Go straight to Chart; the right scheme depends on the shape of the data, not on taste.
  • Need an exact value (a marketing color outside the system, a specific spacing)? Use Primitive.

Naming convention. Primitives use the native Tailwind prefix (--color-*, --radius-*, --shadow-*). Semantics are unprefixed (--primary, --foreground, --border) and live in :root or [data-theme="<id>"]. Chart tokens are bridged into Tailwind utilities via @theme inline.

How to consume

Three equivalent forms, depending on the context:

// 1. Tailwind utility class, preferred inside components
<div className="bg-primary text-primary-foreground rounded-lg p-4 shadow-md">
  A card painted with semantic tokens
</div>

// 2. CSS variable, for dynamic styles or libraries that take a color
//    string (Recharts, SVG, inline motion frames)
<rect fill="var(--color-chart-cat-1)" />

// 3. Gremorie ChartConfig, consumed by the chart tooltip / legend helpers
const config = {
  revenue: { label: 'Revenue', color: 'var(--color-chart-cat-1)' },
} satisfies ChartConfig;

The mental rule: if you are painting an element, use a Tailwind class. If you are passing a color as a string to an external API, use the CSS variable. ChartConfig is the special case the charting system expects.

The golden rule

Components never hardcode a value. Always a token. If you are about to type #FF6B6B or padding: 14px, stop. A token probably exists. If it does not, that is a system decision before it is an implementation decision.

The motivation is simple: swapping the theme, adjusting the brand, or improving accessibility should each be a diff in a single file. When hardcoded values leak into components, that "one diff" becomes "review 200 files".

On this page