# Website UI Theming Concept

This document defines the *conceptual* theming architecture for the Website UI layer. It is written to make the UI maintainable, themeable, and **decoupled from Tailwind** (Tailwind may exist as an implementation detail, never as the source of truth).

For the existing visual direction and palette intent, see [`docs/THEME.md`](docs/THEME.md).

---

## Problem

The current UI style surface mixes:

- semantic intent (primary, surface, outline)
- concrete implementation (Tailwind class strings)
- and multiple naming systems (Tailwind tokens, ad-hoc names, CSS variables)

This makes it hard to:

- change colors consistently,
- introduce additional themes,
- reason about component contracts,
- and enforce architectural boundaries in [`apps/website/ui`](apps/website/ui).

---

## Goals

1. **Single source of truth** for theme tokens (colors, typography, radii, shadows).
2. **Semantic tokens** (intent-based), not implementation tokens (utility-class-based).
3. **Runtime theme selection** via CSS variables (SSR-correct by default).
4. UI components remain **pure** and follow [`docs/architecture/website/COMPONENT_ARCHITECTURE.md`](docs/architecture/website/COMPONENT_ARCHITECTURE.md).
5. Tailwind is optional and must remain a **consumer/adapter**, not the authoring surface.

Non-goal for this iteration: shipping a user-facing theme switcher. We implement a single default theme but design the architecture to add more later.

---

## Core idea: Theme contract + CSS variables boundary

### 1) Theme contract (TypeScript)

We define a typed theme contract, e.g. [`Theme`](apps/website/ui/theme/Theme.ts:1), that expresses the semantic palette and style tokens.

Principle:

- UI components accept **semantic props** (variant, intent, emphasis).
- UI components **do not accept** Tailwind token strings like `bg-panel-gray`.

Example shape (conceptual):

- [`Theme`](apps/website/ui/theme/Theme.ts:1)
  - `color`
    - `bg.base`
    - `bg.surface`
    - `bg.surfaceMuted`
    - `border.default`
    - `text.high`
    - `text.med`
    - `text.low`
    - `intent.primary`
    - `intent.warning`
    - `intent.success`
    - `intent.critical`
  - `radius`
    - `sm` `md` `lg` `xl`
  - `shadow`
    - `sm` `md` `lg` `focus`
  - `font`
    - `body` `mono` `heading`

The *exact* token taxonomy can evolve, but the rule is stable: tokens represent meaning, not implementation.

### 2) CSS variables as the runtime boundary

The theme is applied by setting CSS custom properties. UI components reference variables, not Tailwind classes.

The canonical variables are defined in a dedicated stylesheet, e.g. [`docs/architecture/website/UI_THEMING.md`](docs/architecture/website/UI_THEMING.md) describes this as: `--ui-color-bg-base`, `--ui-color-text-high`, etc.

Implementation typically lives in [`apps/website/ui/theme/theme.css`](apps/website/ui/theme/theme.css) and is imported by [`apps/website/app/globals.css`](apps/website/app/globals.css:1).

The existing variables in [`apps/website/app/globals.css`](apps/website/app/globals.css:1) are already a strong start. The key change is to treat them as the *UI contract* and stop treating Tailwind names as the contract.

---

## Where theme lives (layering)

This respects the UI purity rules in [`docs/architecture/website/COMPONENT_ARCHITECTURE.md`](docs/architecture/website/COMPONENT_ARCHITECTURE.md).

### Authoritative token source

- `CSS variables` are the runtime truth.
- `TypeScript` theme contract is the compile-time truth.

The theme is a public contract in the UI layer:

- [`apps/website/ui/theme/Theme.ts`](apps/website/ui/theme/Theme.ts:1)
- [`apps/website/ui/theme/themes/default.ts`](apps/website/ui/theme/themes/default.ts)
- [`apps/website/ui/theme/theme.css`](apps/website/ui/theme/theme.css)

### Applying the theme (SSR correctness)

SSR correctness means: the initial HTML response already knows which theme is active.

For a single default theme, the simplest SSR-correct approach is:

- set `data-theme=default` on the `<html>` element in [`RootLayout`](apps/website/app/layout.tsx:45).

Later, if theme becomes user-selectable, the server can choose the theme per request using cookies/headers in [`RootLayout`](apps/website/app/layout.tsx:45), still using the same CSS-variable mechanism.

---

## Tailwind’s role (strictly non-authoritative)

Tailwind may remain in the project for page/layout scaffolding or legacy areas, but it must be treated as:

- a *consumer* of theme variables (e.g. Tailwind config maps colors to `var(--ui-...)`), or
- an *adapter* for non-UI-layer code.

In other words:

- UI components inside [`apps/website/ui`](apps/website/ui) never rely on Tailwind class name semantics.
- If Tailwind is used, it only references CSS variables (so switching the theme changes the look without changing class names).

This prevents “utility token drift” where a Tailwind rename becomes a breaking theme change.

---

## Component authoring rules

### Components are theme-driven, not class-driven

Inside [`apps/website/ui`](apps/website/ui):

1. Use CSS Modules for component styling.
2. Use semantic variants and intent props.
3. Use CSS variables for actual values.

Example conceptually:

- [`Button`](apps/website/ui/Button.tsx)
  - `variant: primary | secondary | ghost`
  - `intent: default | danger`
  - CSS module uses variables like `background: var(--ui-color-intent-primary)`.

### Primitives

The primitives in [`apps/website/ui/primitives`](apps/website/ui/primitives) exist to build consistent semantics. They should not encode a “theme” directly; they expose minimal layout/styling mechanics.

Examples in code today:

- [`Box`](apps/website/ui/primitives/Box.tsx:1)
- [`Stack`](apps/website/ui/primitives/Stack.tsx:1)
- [`Surface`](apps/website/ui/primitives/Surface.tsx:1)

Conceptually, primitives should either:

- map typed props to standard CSS properties (style object), or
- map typed props to CSS variables that are already in the theme contract.

What primitives should **not** do: generate Tailwind class strings as their main behavior.

---

## Token naming: semantic + stable

Recommended CSS variable naming:

- `--ui-color-bg-base`
- `--ui-color-bg-surface`
- `--ui-color-border-default`
- `--ui-color-text-high`
- `--ui-color-intent-primary`
- `--ui-radius-md`
- `--ui-shadow-md`
- `--ui-font-body`

Rules:

1. Prefix with `--ui-` to avoid collisions.
2. Use semantic names (surface, text, intent), not “brand” names.
3. Avoid numeric-only palettes in component code (no `gray-600` in UI internals).

---

## Adding themes (extensibility model)

To add a new theme:

1. Keep the token contract stable (same variables).
2. Provide a new theme override block in [`apps/website/ui/theme/theme.css`](apps/website/ui/theme/theme.css), e.g.:
   - `:root[data-theme=default] { ... }`
   - `:root[data-theme=brand-x] { ... }`
3. Optionally provide a matching TypeScript definition in [`apps/website/ui/theme/themes`](apps/website/ui/theme/themes) for tooling and validation.

The important property: **components do not change** when a new theme is added.

---

## Architecture view

```mermaid
flowchart TD
  A[Semantic theme contract] --> B[CSS variables]
  B --> C[UI components]
  B --> D[Tailwind adapter]
  C --> E[App components]
```

Interpretation:

- Theme contract defines the tokens we promise.
- CSS variables implement those tokens at runtime.
- UI components consume tokens directly.
- Tailwind, if present, only consumes variables.

---

## Consequences (why this improves maintainability)

1. **Token changes are localized** to the theme definition (CSS vars) rather than spread across UI components.
2. UI components become **portable**: they do not depend on any particular styling framework.
3. It becomes possible to add enforcement (lint rules, conventions) around “no Tailwind tokens inside UI”.
4. Visual identity evolves without turning into a codebase-wide search-and-replace.

