# Migrating Greda v0.7.0 to the experimental theme contract

This guide covers the move from the immutable Greda v0.7.0 release to the
theme-family contract currently developed at the repository root. That contract
is identified as `0.8.0-experimental`; it is not a stable release.

Migrate only when a project needs multiple visual families or is helping test
the next contract. A production project that only needs Clay light and dark can
remain on v0.7.0.

## What changes

Greda v0.7.0 keeps structural and colour tokens together in `tokens.css`.
Light and dark are selected through `data-gr-theme`:

```html
<html data-gr-theme="dark">
```

The experimental contract separates two independent choices:

- `data-gr-theme` selects a family such as `clay` or `graphite`;
- `data-gr-mode` selects `light` or `dark`.

Colour and shadow values move into theme stylesheets. Typography, spacing,
shape, control sizes, layout, motion, component classes, markup, JavaScript and
accessibility behavior remain unchanged.

## Before migrating

1. Confirm the project is actually pinned to v0.7.0.
2. Record every place that reads or writes `data-gr-theme`.
3. Check whether the project overrides any `--gr-color-*` or `--gr-shadow-*`
   token.
4. Keep a rollback branch or commit that still loads the immutable stylesheet.

Do not edit files inside `v0.7.0/`. Versioned release folders are immutable.

## Step 1: change the stylesheet source

The stable installation uses one immutable entry point:

```html
<link rel="stylesheet" href="https://www.greda.design/v0.7.0/greda.css">
```

While the new contract is experimental, test it from a checked-out copy of the
repository:

```html
<link rel="stylesheet" href="/styles/greda.css">
```

`styles/greda.css` loads Clay automatically. To make Graphite available, load
its stylesheet after the entry point:

```html
<link rel="stylesheet" href="/styles/greda.css">
<link rel="stylesheet" href="/styles/themes/graphite.css">
```

Do not label this setup as Greda v0.8.0 in production. Its registry version is
`0.8.0-experimental` until an immutable release is cut.

## Step 2: separate family from mode

Use this mapping:

| v0.7.0 | Experimental contract |
| --- | --- |
| no attribute | no attribute, or `data-gr-theme="clay"` |
| `data-gr-theme="light"` | `data-gr-theme="clay" data-gr-mode="light"` |
| `data-gr-theme="dark"` | `data-gr-theme="clay" data-gr-mode="dark"` |

To opt into Graphite:

```html
<html data-gr-theme="graphite" data-gr-mode="dark">
```

The old `data-gr-theme="light|dark"` form remains supported as a compatibility
alias during the experimental period. Migrate anyway if code needs to select a
family: one attribute must not represent two different concepts.

## Step 3: update theme controls and persistence

Code that previously persisted one value now needs two independent values. A
minimal implementation looks like this:

```js
const root = document.documentElement;

function setGredaTheme({ family, mode }) {
  root.dataset.grTheme = family;
  root.dataset.grMode = mode;
  localStorage.setItem("greda-theme-family", family);
  localStorage.setItem("greda-theme-mode", mode);
}
```

Keep control labels explicit: “Clay” and “Graphite” select family; “Light” and
“Dark” select mode. Each selected option should expose its state with
`aria-pressed="true"` or the native selected state of the control being used.

If storage is unavailable, the selected values should still apply for the
current page. Persistence failure must not break the control.

## Step 4: review token overrides

Project CSS should continue consuming semantic tokens:

```css
.project-panel {
  color: var(--gr-color-text);
  background: var(--gr-color-surface);
  border-color: var(--gr-color-border);
}
```

If the project overrides theme tokens globally, move those overrides into an
explicit family selector. A custom family must provide every token listed in
`styles/themes/themes.json`, including both shadows, and must support light and
dark through `light-dark()` plus a light fallback.

Do not place typography, spacing, radii, control dimensions, layout or motion
inside a theme. Those remain system structure.

## Compatibility notes

- Component classes and component markup do not change.
- `base.css` and `components.css` remain compatible with the v0.7.0 contract.
- Clay preserves the warm neutral direction, but its light accent changes from
  `#c43b2f` to the darker `#b33127` to keep the expanded contrast matrix valid.
- Graphite is opt-in and has no effect unless its stylesheet is loaded and its
  family is selected.
- Browsers without `light-dark()` receive each family's light fallback.
- A project using only the immutable v0.7.0 stylesheet cannot select Graphite.

## Verification matrix

Before accepting the migration, test:

| Family | Light | Dark |
| --- | --- | --- |
| Clay | required | required |
| Graphite | required when loaded | required when loaded |

For each combination, verify:

- layout from 360px upward;
- text and control-boundary contrast;
- visible keyboard focus and logical focus order;
- hover, pressed, disabled, loading, empty and error states where present;
- dialogs, drawers, popovers, menus and other elevated surfaces;
- reduced-motion behavior;
- reload persistence without a flash into the wrong saved selection.

Inside the Greda repository, run:

```sh
node skills/greda/scripts/check-markup.mjs --experimental path/to/file.html
node scripts/validate.mjs
```

## Rollback

Rollback does not require changing component markup:

1. Restore the immutable v0.7.0 stylesheet URL.
2. Remove the Graphite stylesheet.
3. Replace the family/mode pair with `data-gr-theme="light|dark"`, or remove the
   attributes to follow the system preference.
4. Remove or ignore the separate family preference in storage.

The rollback is intentionally mechanical because the experimental contract does
not alter component APIs.
