> ## Documentation Index
> Fetch the complete documentation index at: https://amd-gaia.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# GAIA design language

> The graphite, ivory, and copper palette the GAIA Agent UI and TUI share, the AMD-brand palette the website carries, and the contrast floors each surface must hold

GAIA looks like one product in a desktop window and a terminal. This page pins
the colour roles both implement, the values behind them, and the contrast each
surface is tested against. The website shares their roles, floors and status
hues but wears AMD's brand palette instead of the copper; see
[The website](#the-website). This page is the source of truth for
[`website/src/design/tokens.css`](https://github.com/amd/gaia/blob/main/website/src/design/tokens.css),
the Agent UI's CSS custom properties, and
[`tui/internal/ui/theme/theme.go`](https://github.com/amd/gaia/blob/main/tui/internal/ui/theme/theme.go).

Implements milestone 0 of the UI redesign plan proposed in
[amd/gaia#3803](https://github.com/amd/gaia/pull/3803).

## Roles, not numbers

A surface asks for a **role** — canvas, primary text, accent — and never for a
hex literal. That is what keeps a value tuned against one background from going
unreadable on another, and it is why every surface routes colour through tokens
rather than inline hexes.

| Role | Dark | Light | Used for |
| - | - | - | - |
| Canvas | Graphite `#17161C` | Porcelain `#F5F2EC` | The page or terminal behind everything |
| Raised surface | Warm slate `#222128` | Pale stone `#EAE5DC` | Cards, the composer, the quiet status band |
| Primary text | Ivory `#F0EDE7` | Graphite `#242129` | Answers, labels, values |
| Secondary text | Ash `#AAA5B0` | Slate `#645D6A` | Hints, descriptions, key names |
| Accent / focus | Copper `#EBA474` | Burnished copper `#9A4930` | The `>` prompt, focus ring, selection, the one primary action |
| Accent fill | Burnished copper `#9A4930` | Burnished copper `#9A4930` | A filled primary button; carries white text |

Most of the screen is neutral. Colour marks the prompt, focus, selection, the one
primary action — and, in rendered markdown, headings and inline code. Nothing
else; ordinary prose stays uncoloured.

Headings and code spans are structure, not emphasis. A reader scans for them
rather than reading them, there are few per screen, and the accent is what makes
them findable at a glance. Emphasis inside a sentence is the opposite: accent on
every bold word turns running prose into the speckle the palette reserves for the
prompt and the one primary action. That is why the TUI's markdown renderer paints
`strong` in primary text and leaves the accent to the two structural marks
(`tui/internal/ui/components/markdown_style.go`).

### The terminal has no canvas, and its status band is not the raised surface

The first two rows are the Agent UI's grounds (its light theme substitutes white;
see [Surface mappings](#surface-mappings)). A terminal's background belongs
to the user's emulator, so the TUI sets none — it paints foregrounds onto
whatever the reader already chose.

The status band is the one place the TUI fills a background, and it uses its own
pair (`#C2B8A8` light, `#615D69` dark) rather than the raised surface above. The
raised-surface values would vanish there: pale stone measures **1.25:1** on a
white terminal and warm slate **1.32:1** on a black one, both under the 1.5:1
chrome floor a band has to clear to be seen at all. Those two values are cards on
a canvas GAIA controls; the band sits on a background it does not. What carries
across is the cast — warm stone on light, violet graphite on dark — not the
literal. See `SurfaceBG` in `tui/internal/ui/theme/theme.go`.

### Why copper is two different values, and one of them is not the obvious one

The dark copper `#EBA474` is the identity colour. The light value is **not** a
simple darkening of it, because the light accent has to survive a constraint the
dark one does not.

macOS Terminal is 256-colour only, so every value is rounded to the nearest of
256 fixed indices before it reaches the screen. Among the colours dark enough to
hold 4.5:1 on *every* light terminal background the TUI measures — pure white
through Solarized Light `#FDF6E3` — that cube offers exactly one saturated warm
entry: `#875F00`, and `#875F00` is already where Warning and Selected land. A
light copper that rounds there stops being a distinct colour and silently becomes
the warning colour.

The qualifier is load-bearing. Against pure white alone the cube offers two:
`#AF5F00` reads 4.71:1 there and 4.51:1 on One Half Light, then drops to 4.37:1
on Solarized Light. Judging the palette against white only is how a value that
fails on a real terminal passes review.

`#9A4930` is the most saturated light copper that rounds to `#875F5F` instead: a
warm near-neutral. On a 256-colour light terminal GAIA's accent therefore reads
as a dark warm grey rather than copper. That is the documented, intended
degradation — the palette's rule is that a colour may fade toward neutral, but
may never land on a *different* saturated meaning. Truecolor terminals and the
Agent UI render `#9A4930` as copper.

Using the same `#9A4930` in the Agent UI as in the terminal is deliberate: the
surfaces previously carried three near-but-unequal golds, and one shared literal
is what makes "the same assistant" true rather than approximate.

## The website

amd-gaia.ai is where people decide whether to install GAIA, not where they work
with it, so it carries AMD's brand rather than the assistant's working palette.
It is **dark-only**: black ground, white type, AMD red as the single accent, and
AMD orange for release candidates and nothing else.

| Role | Value | Used for |
| - | - | - |
| Canvas | Black `#000000` | The page, and the starfield behind it |
| Brand red | AMD red `#ED1C24` | Display-size type, the wordmark mark, section rules, the starfield's red streaks |
| Accent | Lifted red `#FF4B51` | Links, small marks, the focus ring, the code panel's `$` |
| Accent fill | Deep red `#D3121A` | The one filled primary action |
| Release candidate | AMD orange `#F26522` | RC badges and the RC line under the download |

AMD red `#ED1C24` measures **4.01:1** on the raised surfaces, below the 4.5:1 text
floor, so it is held to the 3:1 large-text floor and used only at display size.
Text-size red is lifted to `#FF4B51` instead. The filled button is
deepened to `#D3121A`, which carries white at **5.42:1**; the brand red itself
carries white at only 4.38:1.

Red is the brand here, not a status. That does not break "a hue that carries a
status meaning is not available for decoration": the site shows no status
outside a code panel, and inside one, danger keeps its own `#F2787C`, a
distinctly pinker red than the brand.

What still matches the other two surfaces: the roles and their floors, and the
status hues inside a code panel (`cross-surface.test.ts` holds green, amber and
danger equal to the Agent UI's). What does not: the accent, the grounds, and the
typography — Antonio, a tall condensed face, for headings, buttons and figures,
and Titillium Web for body text (it stands in for Klavika, AMD's licensed face).
The AMD logo is never drawn, imitated or used as a pattern; "AMD" appears as text.

**Motion is allowed on the website, within limits.** The site has one ambient
layer: a canvas of stars streaking past, which arrives at speed, settles to a
cruise, and speeds up as the visitor scrolls. On top of it go entrance and
scroll reveals, a headline that resolves out of noise once on load, a red
glow on the primary action and on the hero's red phrase, and a breathing dot on the release-candidate line.
Nothing else moves. Every one of these stops under `prefers-reduced-motion`,
which gets a single static frame, and the footer carries a control that pauses
the background and the dot for good. Below the hero the field dims, so body text
never sits on a full-strength streak. The "no glow" and
"no simulated typing" rules below still govern the Agent UI and the TUI.

## Semantic colours keep their conventional hues

The brand motif moves off green; **status does not**. Success stays green,
warning amber, danger red, information blue. A terminal that paints a failed
check in copper to stay on-brand is a worse terminal. The plan's "no green motif"
governs the accent, mascot, and brand marks — not whether a passing check looks
like a passing check.

Status must never depend on colour alone. Every state carries a word or an icon
as well, so the palette degrading to neutral on a limited terminal costs polish,
never meaning.

The reservation runs the other way too: **a hue that carries a status meaning is
not available for decoration.** Green, amber, red, and blue are spoken for, which
leaves no palette to give each tool or file type its own colour — so those stay
neutral and are told apart by their icon. The alternative is a green badge beside
a red failure icon in the same row.

One recognised exception: **a link stays blue.** That convention is older than
this palette and readers arrive already knowing it, so the blue is carrying
meaning rather than decorating — which is why the TUI's markdown links resolve to
the information role rather than to neutral text.

## Contrast floors

Each surface enforces these in its own test suite; the floors are the same
everywhere so a role cannot be legible in the browser and marginal in the terminal.

| Kind of colour | Floor | Rationale |
| - | - | - |
| Text | 4.5:1 | WCAG 2.1 AA body text |
| Recessive text | 3.0:1 | WCAG AA large text; always duplicates something on the same row |
| Chrome (rules, bar tracks, mascot) | 1.5:1 | Carries no information; only has to stay visible |
| Text on a filled button | 4.5:1 | The pair covers the background, so it is judged against itself |

Measured minimums for the roles above, against their own canvas and raised
surface: **6.62:1 dark, 4.97:1 light**. Accent-as-fill carries white at 6.23:1.

The TUI additionally re-checks every floor against ten real terminal
backgrounds and against the ANSI-256 value that actually lands, because a
colour that clears its floor in truecolor can drop below it once rounded. See
`tui/internal/ui/theme/contrast_test.go`.

## Surface mappings

| Surface | Token home | Theme switch | Test command |
| - | - | - | - |
| Website | `website/src/design/tokens.css` (`--g-*`, RGB triplets for Tailwind alpha) | None: dark-only | `npm test` and `npm run build` in `website/` |
| Agent UI / Electron | `src/gaia/apps/webui/src/styles/index.css` | `html[data-theme]`, `localStorage` key `gaia-chat-theme` | `npm test` in `src/gaia/apps/webui/` |
| TUI | `tui/internal/ui/theme/theme.go` (`lipgloss.AdaptiveColor`) | terminal background query, `GAIA_TUI_THEME` override | `go test ./internal/ui/...` in `tui/` |

**The Agent UI's light theme is neutral, not porcelain.** Its light canvas is
white (`#FFFFFF`) with faintly cool greys for raised surfaces, rules and hover,
because the warm stone read as brown in a full-window app. The accent, the accent
fill and every status hue are the shared values above, and the dark theme is the
shared graphite. `contrast.test.ts` holds the Agent UI's values to the same
floors.

## Rules for adding a colour

1. **Add a role, not a literal.** A new hex in a component file is a colour
   nothing can police, and every surface has a test that catches it —
   `website/src/design/literals.test.ts`,
   `src/gaia/apps/webui/src/styles/__tests__/contrast.test.ts`, and
   `tui/internal/ui/theme/literals_test.go`. Each sweeps its own tree and keeps
   a short allowlist where every entry says why a role cannot carry it.
2. **Declare what it owes the reader.** The TUI will not compile past its tests
   without a contrast floor and a hue family for the new token.
3. **Check the degraded value, not just the one you typed.** Truecolor contrast
   is not evidence; the TUI test measures what a 256-colour terminal shows.
4. **Never let two roles share a hue family.** That is how "selected" and
   "warning" become the same thing on a user's machine but not on yours.

## Typography and spacing

Shared starting points, not pixel-matched measurements:

* Body text 15–16px, readable proportional type for prose on the website.
* System monospace for composer input, the `>` prompt, labels, and activity lines.
  The terminal follows the user's own font — never force size, truecolor, or
  background.
* Roughly 760px readable content width; a 240–260px collapsible sidebar.
* Restrained rounding, subtle borders, generous spacing. No glow, no
  gradient-filled panels.
* No simulated typing. A blinking cursor means output is still streaming — it is
  a status indicator, never decoration, and never runs when nothing is being
  written.
* Light, dark, and system appearance in the Agent UI (the website is dark-only);
  motion that honours `prefers-reduced-motion`; visible keyboard focus at every
  interactive element.

Two gaps stand between those rules and what ships today. "No glow" is enforced
by a test in the app (`styles/__tests__/contrast.test.ts`), and "no
gradient-filled panels" by a test on both web surfaces (that file, and
`design/literals.test.ts` on the site); the rest are held by review. Where each
gap is scheduled in the [redesign plan](https://amd-gaia.ai/docs/plans/gaia-ui-redesign.md):

* `outline: none` appears in most component stylesheets. Most sit on a `:focus`
  rule that draws a border or ring in its place, but the pairing is unverified
  and nothing fails when it is missing. Milestone 3 covers keyboard behaviour.
* "A hue that carries a status meaning is not available for decoration" is
  enforced for red only. Extending it to amber, green, blue, cyan and purple
  surfaces 84 rules that wear a status hue for decoration — a switch whose on
  state is the information blue, memory categories each given their own colour,
  links tinted green — so closing it is a repaint across 18 stylesheets, not a
  test change. Milestone 3 for the task surfaces, Milestone 4 for Settings and
  the document and memory panels.

The [mobile design system](https://github.com/amd/gaia/blob/main/docs/spec/mobile-design-system.md)
remains the responsive baseline, including its 768px boundary and 44px touch targets.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.