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

# Theme

> Read and patch the workspace theme — color tokens, typography, and spacing that apply across every page.

export const ErrorTable = ({errors}) => <div className="gv-error-table" role="table" aria-label="Errors and fixes">
    <div className="gv-error-table__head" role="row">
      <span className="gv-error-table__col-head" role="columnheader">Error</span>
      <span className="gv-error-table__col-head" role="columnheader">Cause</span>
      <span className="gv-error-table__col-head" role="columnheader">Fix</span>
    </div>
    {errors.map((err, i) => <div className="gv-error-table__row" role="row" key={i}>
        <code className="gv-error-table__code" role="cell">{err.code}</code>
        <div className="gv-error-table__cause" role="cell">{err.cause}</div>
        <div className="gv-error-table__fix" role="cell">{err.fix}</div>
      </div>)}
  </div>;

export const APIBadge = ({method = "GET", path, scope}) => {
  const m = method.toUpperCase();
  return <span className="gv-api-badge">
      <span className={`gv-api-badge__method gv-api-badge__method--${m.toLowerCase()}`}>
        {m}
      </span>
      <code className="gv-api-badge__path">{path}</code>
      {scope && <span className="gv-api-badge__scope">
          <span className="gv-api-badge__scope-label">scope</span>
          <code>{scope}</code>
        </span>}
    </span>;
};

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="RESOURCE" />

The theme is a single workspace-level document holding color tokens, typography settings, and spacing values. Every block reads from it at render time, so changing one token re-skins every block instance in every page — no per-page patching required. The Theme resource lets you fetch the current draft, patch it with JSON Patch, and publish it live. By the end of this page you will know the endpoints, the draft vs live model, and how token changes propagate.

## A theme in shape

```json theme={null}
{
  "colors": {
    "brand_primary":   "#6C2BD9",
    "brand_secondary": "#A78BFA",
    "surface":         "#0F0F10",
    "on_surface":      "#FAFAFA"
  },
  "typography": {
    "font_family_heading": "Inter",
    "font_family_body":    "Inter",
    "scale":               "default"
  },
  "spacing":    { "section_gap": "lg" },
  "updated_at": "2026-05-09T11:00:00Z"
}
```

Block props that reference a color name resolve through the `colors` map. Typography and spacing apply globally.

## Endpoints

<APIBadge method="GET" path="/v1/workspaces/{slug}/theme" scope="theme:read" />

Fetch the current draft theme document.

<APIBadge method="PATCH" path="/v1/workspaces/{slug}/theme" scope="theme:write" />

Apply RFC 6902 JSON Patch operations to the draft.

<APIBadge method="POST" path="/v1/workspaces/{slug}/theme" scope="theme:write" />

Publish the current draft theme to live.

## Patching tokens

The patch protocol is identical to [patching a page](/api-reference/resources/pages) — an RFC 6902 array of `{ op, path, value }` objects. Paths target JSON Pointer locations inside the theme document.

```bash theme={null}
curl -X PATCH https://api.gavai.app/v1/workspaces/acme/theme \
  -H "Authorization: Bearer gak_live_xxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '[
    { "op": "replace", "path": "/colors/brand_primary",          "value": "#7C3AED" },
    { "op": "replace", "path": "/typography/font_family_heading", "value": "Geist"   }
  ]'
```

A single token change re-skins every block instance that references it. Preview in a staging workspace before patching production.

## Draft vs live

| State | What it is                                  | How it changes                                                                  |
| ----- | ------------------------------------------- | ------------------------------------------------------------------------------- |
| Draft | The working theme returned by `GET /theme`. | Every `PATCH` produces a new draft version.                                     |
| Live  | The theme visitors render against.          | Updated only by `POST /theme`. Visitors see the change on their next page load. |

## Common errors

<ErrorTable
  errors={[
{ code: "400 invalid_patch", cause: "Operation failed — path not found, invalid value, `test` op mismatched.", fix: "Fetch the current draft, recompute paths and values, and resend." },
{ code: "403 insufficient_scope", cause: "Token lacks `theme:read` or `theme:write`.", fix: "Mint a token with the scope you need." },
{ code: "404 not_found", cause: "Workspace not found, or not visible to this token.", fix: "Verify the slug and that the token can see the workspace." },
{ code: "409 conflict", cause: "Concurrent edit — another writer beat you to the draft.", fix: "Fetch the latest draft and reapply your patch on top of it." },
]}
/>

## Related

<Card title="Theme in PageBlocks" icon="palette" href="/pageblocks/theme">
  How color tokens, typography, and spacing flow from the theme into individual block props.
</Card>
