> ## 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

> Workspace-wide design tokens — colors, typography, spacing — that every block resolves at render time.

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 Def = ({term, type, defaultValue, required, children}) => <>
    <dt className="gv-def-list__term">
      <span className="gv-def-list__name">{term}</span>
      {type && <span className="gv-def-list__type">{type}</span>}
      {required && <span className="gv-def-list__required">required</span>}
      {defaultValue !== undefined && <span className="gv-def-list__default">
          default <code>{defaultValue}</code>
        </span>}
    </dt>
    <dd className="gv-def-list__description">{children}</dd>
  </>;

export const DefList = ({children}) => <dl className="gv-def-list">{children}</dl>;

export const BrandCard = ({eyebrow, title, href, icon, badge, cta, featured = false, horizontal = false, children}) => {
  const classes = ["gv-card", featured && "gv-card--featured", horizontal && "gv-card--horizontal"].filter(Boolean).join(" ");
  const inner = <>
      {icon && <span className="gv-card__icon" aria-hidden="true">
          <Icon icon={icon} />
        </span>}
      <div className="gv-card__main">
        {(eyebrow || badge) && <div className="gv-card__meta">
            {eyebrow && <Eyebrow label={eyebrow} tone="muted" />}
            {badge && <span className="gv-card__badge">{badge}</span>}
          </div>}
        <h3 className="gv-card__title">{title}</h3>
        {children && <div className="gv-card__body">{children}</div>}
        {cta && <span className="gv-card__cta">
            {cta}
            <svg className="gv-card__cta-arrow" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <line x1="7" y1="17" x2="17" y2="7" />
              <polyline points="7 7 17 7 17 17" />
            </svg>
          </span>}
      </div>
    </>;
  return href ? <a className={classes} href={href}>
      {inner}
    </a> : <div className={classes}>{inner}</div>;
};

export const FeatureTable = ({columns, rows, featuredRow, caption, variant}) => <div className={`gv-feature-table ${variant ? `gv-feature-table--${variant}` : ""}`}>
    <table>
      {caption && <caption className="gv-feature-table__caption">{caption}</caption>}
      <thead>
        <tr>
          {columns.map(col => <th key={col.key} scope="col">
              <span className="gv-feature-table__header">{col.label}</span>
            </th>)}
        </tr>
      </thead>
      <tbody>
        {rows.map((row, i) => {
  const rowClass = [i === featuredRow ? "gv-feature-table__row--featured" : "", row.chosen ? "gv-feature-table__row--chosen" : ""].filter(Boolean).join(" ");
  return <tr key={i} className={rowClass}>
              {columns.map(col => {
    const v = row[col.key];
    if (variant === "tradeoff" && Array.isArray(v)) {
      const pill = col.key === "pros" ? "gv-feature-table__pill--pro" : col.key === "cons" ? "gv-feature-table__pill--con" : "";
      return <td key={col.key}>
                      {v.map((item, j) => <span key={j} className={`gv-feature-table__pill ${pill}`}>{item}</span>)}
                    </td>;
    }
    return <td key={col.key}>{v}</td>;
  })}
            </tr>;
})}
      </tbody>
    </table>
  </div>;

export const ArrowItem = ({children}) => <li className="gv-arrow-item">
    <span className="gv-arrow-item__arrow" aria-hidden="true">{"↳︎"}</span>
    <span className="gv-arrow-item__content">{children}</span>
  </li>;

export const ArrowList = ({children}) => <ul className="gv-arrow-list">{children}</ul>;

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="THEME" />

The theme is a workspace-scoped document of design tokens — brand color, font scale, border radius, motion timing — that every block reads at render time. There is one theme per workspace. By the end of this page you will understand the token categories, how blocks resolve them, and how to patch and publish a theme without touching individual pages.

## Why one theme per workspace

Every visual property a block exposes — its background color, its corner radius, its title font size — resolves through theme tokens rather than hard-coded values. Change a token and every page in the workspace picks up the new value on the next render. You never edit individual page documents to rebrand the site.

```mermaid theme={null}
graph LR
    Theme[Workspace theme<br/>colors.primary = #6d4aff]
    P1[Page: landing]
    P2[Page: pricing]
    P3[Page: dashboard]
    B1[Hero.cta_background]
    B2[Cta.button_background]
    B3[NavShell.accent]
    Theme --> B1
    Theme --> B2
    Theme --> B3
    P1 --- B1
    P2 --- B2
    P3 --- B3
```

When `colors.primary` changes, the Hero on the landing page, the Cta on the pricing page, and the NavShell accent on the dashboard all update — no page edits required.

## Token categories

A theme document organizes tokens into broad categories.

<DefList>
  <Def term="colors" type="category">Brand primaries, surface colors, text colors, border colors, and semantic states such as error and success.</Def>
  <Def term="typography" type="category">Font families, size scale, line heights, font weights.</Def>
  <Def term="spacing" type="category">A scale of values used by blocks for padding, gap, and margin.</Def>
  <Def term="radii" type="category">Border radius values ranging from sharp to fully rounded.</Def>
  <Def term="motion" type="category">Transition duration and easing values for interactive elements.</Def>
</DefList>

To see the live token set for your workspace, fetch the theme document — it contains every token name and current value.

## Reading the theme

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

```bash get-theme.sh theme={null}
curl https://api.gavai.app/v1/workspaces/acme/theme \
  -H "Authorization: Bearer gak_test_..."
```

The response is the full theme object. Full endpoint details: [API reference → Theme](/api-reference/resources/theme).

## Editing the theme

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

Apply changes with the same RFC 6902 JSON Patch model used for pages. Patch one token or many in a single call.

```bash patch-theme.sh theme={null}
curl -X PATCH https://api.gavai.app/v1/workspaces/acme/theme \
  -H "Authorization: Bearer gak_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      { "op": "replace", "path": "/colors/primary", "value": "#6d4aff" }
    ]
  }'
```

The patch writes to a theme draft — live pages keep rendering the previously published values until you publish the draft. See [Editing pages with JSON Patch](/pageblocks/json-patch) for operation types, path syntax, and `test` guards. The semantics are identical.

## Publishing the theme

<APIBadge method="POST" path="/v1/workspaces/:slug/theme" scope="theme:publish" />

```bash publish-theme.sh theme={null}
curl -X POST https://api.gavai.app/v1/workspaces/acme/theme \
  -H "Authorization: Bearer gak_test_..."
```

After publish, every page in the workspace resolves the updated values on the next render.

<Warning>
  Publishing the theme re-skins every page in the workspace immediately. Preview your changes in the Builder before publishing to a live workspace.
</Warning>

## How blocks consume tokens

A block declares which token each of its visual properties should resolve to, and the runtime substitutes the current value at render time. A Hero's background pulls from a surface color token. A Cta button's color pulls from `colors.primary`. A FeaturesGrid card's corner radius pulls from a radius token. The block document does not name a hex code or a pixel value — only the token.

The benefit shows up at rebrand time. Switching a workspace from violet to teal is a single `replace` on `/colors/primary`. The eight presentational blocks and the four data-bound blocks all pick it up — no page patches, no migrations.

## Per-instance overrides

For one-off adjustments on a single block instance, every block accepts the `class_overrides` common prop. Overrides layer on top of the resolved theme values and affect only the instance they're set on.

<FeatureTable
  columns={[
{ key: "use", label: "Use when" },
{ key: "reach_for", label: "Reach for" }
]}
  rows={[
{ use: "You want to rebrand every page in the workspace.", reach_for: "Patch the theme document." },
{ use: "One Hero on one page needs a different background.", reach_for: "Set `class_overrides` on that Hero instance." },
{ use: "Two workspaces need different visual identities.", reach_for: "Each workspace has its own theme — patch each independently." }
]}
/>

See [common props on the PageBlocks overview](/pageblocks/overview#common-props).

## Where to go next

<Columns cols={2}>
  <BrandCard eyebrow="PAGEBLOCKS" title="PageBlocks overview" href="/pageblocks/overview">
    The block catalogue, page-document summary, and common props.
  </BrandCard>

  <BrandCard eyebrow="EDITING" title="JSON Patch" href="/pageblocks/json-patch">
    The patch model is shared with pages — operations, paths, and `test` guards.
  </BrandCard>
</Columns>
