# `Editor.*` styling contract

<!-- GENERATED — do not edit. Derived from the parts themselves by
     packages/canvas/src/components/editor/__tests__/stylingContract.test.tsx.
     Regenerate: pnpm -C packages/canvas gen:styling-contract -->

Every `Editor.*` part renders minimal styled DOM carrying a stable
`data-sc-part` attribute, accepts `className`/`style`, and is themed by the
`.sc-root` CSS custom properties below. Restyle by targeting
`[data-sc-part="…"]` selectors and overriding `.sc-root` tokens — you never
need this package's source. Attribute values shown are the stable enums;
`<angle-bracket>` values are runtime data. `sc-*` classes are additional
stable hooks; `sc:`-prefixed utility classes are internal and NOT contract.

## Part index

| `data-sc-part` | rendered by |
|---|---|
| `add` | Editor.Add |
| `buy` | Editor.Buy |
| `canvas` | Editor.Canvas |
| `controls` | Editor.Controls |
| `error` | Editor.Root |
| `error-retry` | Editor.Root |
| `history` | Editor.History |
| `layers` | Editor.Layers |
| `layers-item` | Editor.Layers |
| `loading-overlay` | Editor.LoadingOverlay |
| `menu` | Editor.Menu |
| `placements` | Editor.Placements |
| `placements-item` | Editor.Placements |
| `preview` | Editor.Preview |
| `preview-error` | Editor.Preview |
| `preview-image` | Editor.Preview |
| `preview-loading` | Editor.Preview |
| `root` | Editor.Root |
| `snowcone-editor` | SnowconeEditor |
| `snowcone-editor-header` | SnowconeEditor |
| `snowcone-editor-idle-dock` | SnowconeEditor |
| `snowcone-editor-layer-thumb` | SnowconeEditor |
| `snowcone-editor-menu` | SnowconeEditor |
| `snowcone-editor-menu-item` | SnowconeEditor |
| `snowcone-editor-menu-list` | SnowconeEditor |
| `snowcone-editor-pip` | SnowconeEditor |
| `snowcone-editor-stage` | SnowconeEditor |
| `snowcone-editor-swap-back` | SnowconeEditor |
| `snowcone-editor-swap-stage` | SnowconeEditor |
| `snowcone-editor-zone` | SnowconeEditor |
| `textfield` | Editor.TextField |

## The DOM each part renders

### `add` — Editor.Add

```html
<div data-sc-part="add">
  <div>
    <div>
      <button aria-label=<label>>
```

### `buy` — Editor.Buy — incomplete (seed value untouched)

```html
<button data-sc-buy-missing=<reason-codes> data-sc-buy-state="incomplete" data-sc-part="buy" disabled type="button">
```

### `buy` — Editor.Buy — ready

```html
<button data-sc-buy-state="ready" data-sc-part="buy" type="button">
```

### `canvas` — Editor.Canvas

```html
<div class="sc-canvas-frame" data-sc-part="canvas">
  <div class="sc-root">
    <div>
      <div>
        <div role="status">
        <canvas aria-label=<label> role="application">
```

### `controls` — Editor.Controls — placement="docked", element selected

```html
<div data-sc-part="controls" data-sc-placement="docked">
  <div>
    <div>
      <div>
        <div class="sc-toolbar" data-mode="embedded">
          …
    <div class="sc-secondary-toolbar-panel-wrapper">
      <div> ×20
```

### `controls` — Editor.Controls — built-in floating toolbar (no Controls mounted; body portal)

```html
<div class="sc-toolbar" data-mode="floating" data-sc-part="controls">
  <div class="sc-toolbar-slide-container">
    <div class="sc-toolbar-regular-content">
      <span>
        <span>
      <button aria-label=<label> aria-pressed="false">
        <svg>
          …
      <button aria-label=<label> aria-pressed="false">
        <span class="sc-normalized-icon-responsive">
      <div class="sc-toolbar-items">
        <button aria-label=<label> title=<title>>
          …
        <button aria-label=<label> aria-pressed="false">
          …
        <button aria-label=<label> class="sc-toolbar-btn">
          …
        <button aria-label=<label> aria-pressed="false">
          … ×5
        <button aria-expanded="false" aria-label=<label> class="sc-toolbar-btn" type="button">
        <button aria-label=<label> aria-pressed="false">
          … ×8
        <button aria-label=<label> aria-pressed="false" disabled>
          …
        <button aria-expanded="false" aria-label=<label> class="sc-toolbar-btn" type="button">
    <div class="sc-toolbar-collapsed-header">
```

### `error` — Editor.Root — contained-error card (E_CHUNK_LOAD_FAILED shown)

```html
<div data-sc-part="error" role="alert">
  <p> ×2
  <button data-sc-part="error-retry" type="button">
```

### `error-retry` — Editor.Root — the retry affordance (retryable errors only)

```html
<button data-sc-part="error-retry" type="button">
```

### `history` — Editor.History

```html
<div data-sc-part="history">
  <div class="sc-undo-redo">
    <button aria-label=<label> aria-pressed="false" disabled>
      <span class="sc-normalized-icon-responsive"> ×2
```

### `layers` — Editor.Layers — with a mounted Canvas (live document panel)

```html
<div data-sc-direction="vertical" data-sc-part="layers">
  <div class="sc-layers-panel">
    <div>
      <span>
    <div>
      <div>
        <div> ×2
        <div>
          … ×2
```

### `layers` — Editor.Layers — canvas-free (design layers from the store)

```html
<div data-sc-direction="vertical" data-sc-part="layers">
  <div>
    <div data-sc-layer-kind="custom" data-sc-part="layers-item">
      <span>
    <div data-sc-layer-kind="image" data-sc-part="layers-item">
      <span>
```

### `layers-item` — Editor.Layers — one canvas-free layer row

```html
<div data-sc-layer-kind="custom" data-sc-part="layers-item">
  <span>
```

### `loading-overlay` — Editor.LoadingOverlay — standalone render-in-flight rainbow for host-owned preview surfaces (fills its positioned parent)

```html
<div class="sc-loading-overlay-host" data-sc-part="loading-overlay">
  <div class="sc-prism-overlay sc-prism-fade-in" data-sc-part="preview-loading">
    <div class="sc-prism-band"> ×6
    <div class="sc-prism-particle"> ×10
```

### `menu` — Editor.Menu

```html
<div data-sc-part="menu">
  <div>
    <div>
      <button aria-label=<label>>
  <input type="file">
```

### `placements` — Editor.Placements — two-placement design

```html
<div data-sc-part="placements">
  <button aria-pressed="true" data-sc-active="true" data-sc-part="placements-item" type="button">
  <button aria-pressed="false" data-sc-active="false" data-sc-part="placements-item" type="button">
```

### `placements-item` — Editor.Placements — one tab (active state in data-sc-active)

```html
<button aria-pressed="true" data-sc-active="true" data-sc-part="placements-item" type="button">
```

### `preview` — Editor.Preview — awaiting the first mockup

```html
<div aria-busy="true" class="sc-preview-frame" data-sc-part="preview" data-sc-preview-state="rendering">
```

### `preview` — Editor.Preview — with a rendered mockup

```html
<div class="sc-preview-frame" data-sc-part="preview" data-sc-preview-state="ready">
  <img alt=<alt> data-sc-part="preview-image" src=<url>>
```

### `preview` — Editor.Preview — loadingOverlay frame (the part becomes the stage)

```html
<div class="sc-preview-frame" data-sc-part="preview" data-sc-preview-state="ready">
  <img alt=<alt> data-sc-part="preview-image" src=<url>>
```

### `preview-error` — Editor.Preview — render/grant failure before any mockup

```html
<div data-sc-error=<message> data-sc-part="preview-error" role="alert">
```

### `preview-image` — Editor.Preview — loadingOverlay image

```html
<img alt=<alt> data-sc-part="preview-image" src=<url>>
```

### `preview-loading` — Editor.Preview — loadingOverlay render-in-flight rainbow

```html
<div class="sc-prism-overlay sc-prism-fade-in" data-sc-part="preview-loading">
  <div class="sc-prism-band"> ×6
  <div class="sc-prism-particle"> ×10
```

### `root` — Editor.Root

```html
<div class="sc-root" data-sc-part="root">
  <div class="sc-preview-frame" data-sc-part="preview" data-sc-preview-state="ready">
    <img alt=<alt> data-sc-part="preview-image" src=<url>>
  <input aria-label=<label> data-sc-layer=<layer-name> data-sc-part="textfield" placeholder=<placeholder> type="text">
  <button data-sc-buy-missing=<reason-codes> data-sc-buy-state="incomplete" data-sc-part="buy" disabled type="button">
```

### `snowcone-editor` — SnowconeEditor

```html
<div data-sc-part="snowcone-editor">
  <div data-sc-part="snowcone-editor-header">
    <span>
    <div data-sc-part="history">
      <div class="sc-undo-redo">
        <button aria-label=<label> aria-pressed="false" disabled>
          … ×2
    <div>
      <button aria-expanded="false" aria-label=<label> data-sc-part="snowcone-editor-menu" type="button">
    <button data-sc-buy-state="ready" data-sc-part="buy" type="button">
  <div data-sc-part="snowcone-editor-stage">
    <div class="sc-canvas-frame" data-sc-part="canvas">
      <div class="sc-root">
        <div>
          …
    <button aria-label=<label> data-sc-part="snowcone-editor-pip" type="button">
      <div aria-busy="true" class="sc-preview-frame" data-sc-part="preview" data-sc-preview-state="rendering">
        <span aria-busy="true">
  <div data-sc-part="snowcone-editor-zone" data-sc-zone-state="idle">
    <div>
      <div data-sc-part="controls" data-sc-placement="docked">
        <div>
          …
    <div data-sc-part="snowcone-editor-idle-dock">
      <div data-sc-part="add">
        <div>
          …
      <div>
      <button aria-label=<label> data-sc-part="snowcone-editor-layer-thumb" type="button">
        <span> ×2
```

### `snowcone-editor-header` — SnowconeEditor — header

```html
<div data-sc-part="snowcone-editor-header">
  <span>
  <div data-sc-part="history">
    <div class="sc-undo-redo">
      <button aria-label=<label> aria-pressed="false" disabled>
        <span class="sc-normalized-icon-responsive"> ×2
  <div>
    <button aria-expanded="false" aria-label=<label> data-sc-part="snowcone-editor-menu" type="button">
  <button data-sc-buy-state="ready" data-sc-part="buy" type="button">
```

### `snowcone-editor-idle-dock` — SnowconeEditor — idle dock

```html
<div data-sc-part="snowcone-editor-idle-dock">
  <div data-sc-part="add">
    <div>
      <div>
        <button aria-label=<label>>
  <div>
  <button aria-label=<label> data-sc-part="snowcone-editor-layer-thumb" type="button">
    <span> ×2
```

### `snowcone-editor-layer-thumb` — SnowconeEditor — one layer thumbnail

```html
<button aria-label=<label> data-sc-part="snowcone-editor-layer-thumb" type="button">
  <span>
```

### `snowcone-editor-menu` — SnowconeEditor — the ⋯ menu button

```html
<button aria-expanded="false" aria-label=<label> data-sc-part="snowcone-editor-menu" type="button">
```

### `snowcone-editor-menu-item` — SnowconeEditor — one ⋯ menu item

```html
<button data-sc-part="snowcone-editor-menu-item" role="menuitem" type="button">
```

### `snowcone-editor-menu-list` — SnowconeEditor — open ⋯ menu

```html
<div data-sc-part="snowcone-editor-menu-list" role="menu">
  <button data-sc-part="snowcone-editor-menu-item" role="menuitem" type="button"> ×2
```

### `snowcone-editor-pip` — SnowconeEditor — preview PiP

```html
<button aria-label=<label> data-sc-part="snowcone-editor-pip" type="button">
  <div aria-busy="true" class="sc-preview-frame" data-sc-part="preview" data-sc-preview-state="rendering">
    <span aria-busy="true">
```

### `snowcone-editor-stage` — SnowconeEditor — stage

```html
<div data-sc-part="snowcone-editor-stage">
  <div class="sc-canvas-frame" data-sc-part="canvas">
    <div class="sc-root">
      <div>
        <div>
          …
  <button aria-label=<label> data-sc-part="snowcone-editor-pip" type="button">
    <div aria-busy="true" class="sc-preview-frame" data-sc-part="preview" data-sc-preview-state="rendering">
      <span aria-busy="true">
```

### `snowcone-editor-swap-back` — SnowconeEditor — the "artwork" chip back to the canvas

```html
<button aria-label=<label> data-sc-part="snowcone-editor-swap-back" type="button">
```

### `snowcone-editor-swap-stage` — SnowconeEditor — mockup swapped onto the stage

```html
<div data-sc-part="snowcone-editor-swap-stage">
  <div aria-busy="true" class="sc-preview-frame" data-sc-part="preview" data-sc-preview-state="rendering">
    <div aria-busy="true">
  <button aria-label=<label> data-sc-part="snowcone-editor-swap-back" type="button">
```

### `snowcone-editor-zone` — SnowconeEditor — morphing control zone (idle)

```html
<div data-sc-part="snowcone-editor-zone" data-sc-zone-state="idle">
  <div>
    <div data-sc-part="controls" data-sc-placement="docked">
      <div>
        <div>
        <div>
          …
        <div class="sc-secondary-toolbar-panel-wrapper">
          … ×20
  <div data-sc-part="snowcone-editor-idle-dock">
    <div data-sc-part="add">
      <div>
        <div>
          …
    <div>
    <button aria-label=<label> data-sc-part="snowcone-editor-layer-thumb" type="button">
      <span> ×2
```

### `textfield` — Editor.TextField

```html
<input aria-label=<label> data-sc-layer=<layer-name> data-sc-part="textfield" placeholder=<placeholder> type="text">
```

## `.sc-root` theming tokens (the host theming API)

The override surface is the UNPREFIXED primitives below (`--foreground`,
`--surface`, `--accent`, …), declared on `.sc-root` — the element
`Editor.Root` itself renders as the composition's OUTERMOST wrapper.
Working recipes:

- **Dark host:** put the `dark` class on `<html>` (or any ancestor of the
  Root). The package ships a complete `.dark .sc-root` theme — the `dark`
  column below. Blank = inherits the light value (brand/status colors,
  fonts, radius, spacing are mode-independent). `<html>`-level classes
  also reach body-portaled package UI.
- **Custom theme:** scope overrides as `.my-brand .sc-root { --foreground: …; }`
  with `class="my-brand"` on an element ABOVE `<Editor.Root/>` (page shell
  or `<html>`). A class on DOM you render INSIDE the Root cannot work —
  the Root's own `.sc-root` wraps your markup, so `.my-brand .sc-root`
  matches nothing there.
- **Portal caveat:** package UI portaled to `<body>` (the floating
  toolbar) carries its own `.sc-root` outside any page-shell wrapper —
  `<html>`-level classes reach it; a page-shell wrapper does not.

NOT the API: `--color-*` (Tailwind `@theme` utility-generation defaults)
and `--sc-color-*` (emitted plumbing that `.sc-root` re-points at the
primitives). Overriding those does nothing. Earlier versions of this
document listed the `@theme` defaults here in error.

| group | token | default | dark |
|---|---|---|---|
| Primitive colors | `--white` | `oklch(100% 0 0)` |  |
| Primitive colors | `--black` | `oklch(0% 0 0)` |  |
| Primitive colors | `--snow` | `oklch(0.9911 0 0)` |  |
| Primitive colors | `--eclipse` | `oklch(0.2103 0.0059 285.89)` |  |
| Primary / Accent — HeroUI default blue | `--accent` | `oklch(0.6204 0.195 253.83)` |  |
| Primary / Accent — HeroUI default blue | `--accent-foreground` | `var(--snow)` |  |
| Primary / Accent — HeroUI default blue | `--primary` | `var(--accent)` |  |
| Primary / Accent — HeroUI default blue | `--primary-foreground` | `var(--accent-foreground)` |  |
| Secondary | `--secondary` | `oklch(0.45 0.0 0)` |  |
| Secondary | `--secondary-foreground` | `var(--eclipse)` |  |
| Default — Neutral elements | `--default` | `oklch(94% 0.001 286.375)` | `oklch(27.4% 0.006 286.033)` |
| Default — Neutral elements | `--default-foreground` | `var(--eclipse)` | `var(--snow)` |
| Default — Neutral elements | `--color-default-on-surface` | `oklch(90% 0.006 265)` | `oklch(30% 0.006 265)` |
| Status colors | `--success` | `oklch(0.7329 0.1935 150.81)` |  |
| Status colors | `--success-foreground` | `var(--eclipse)` |  |
| Status colors | `--warning` | `oklch(0.7819 0.1585 72.33)` |  |
| Status colors | `--warning-foreground` | `var(--eclipse)` |  |
| Status colors | `--danger` | `oklch(0.6532 0.2328 25.74)` |  |
| Status colors | `--danger-foreground` | `var(--snow)` |  |
| Layout colors | `--background` | `oklch(0.9607 0 0)` | `oklch(0.07 0.002 270)` |
| Layout colors | `--foreground` | `var(--eclipse)` | `var(--snow)` |
| Layout colors | `--muted` | `oklch(0.5517 0.0138 285.94)` | `oklch(70.5% 0.015 286.067)` |
| Layout colors | `--muted-foreground` | `oklch(from var(--foreground) l c h / 0.6)` | `oklch(from var(--foreground) l c h / 0.6)` |
| Layout colors | `--divider` | `oklch(92% 0.004 286.32)` | `oklch(32% 0.006 286.033)` |
| Layout colors | `--focus` | `var(--accent)` |  |
| Surface | `--surface` | `var(--white)` | `oklch(0.2103 0.0059 285.89)` |
| Surface | `--surface-foreground` | `var(--foreground)` | `var(--foreground)` |
| Surface | `--surface-shadow` | `0 2px 4px 0 rgba(0, 0, 0, 0.04), 0 1px 2px 0 rgba(0, 0, 0, 0.06), 0 0 1px 0 rgba(0, 0, 0, 0.06)` | `0 0 0 0 transparent inset` |
| Overlay | `--overlay` | `var(--white)` | `oklch(0.22 0.0059 285.89)` |
| Overlay | `--overlay-foreground` | `var(--foreground)` | `var(--foreground)` |
| Overlay | `--overlay-shadow` | `0 4px 16px 0 rgba(24, 24, 27, 0.08), 0 8px 24px 0 rgba(24, 24, 27, 0.09)` | `0 0 0 0 transparent inset` |
| Fields/Inputs | `--field` | `var(--white)` | `var(--default)` |
| Fields/Inputs | `--field-foreground` | `var(--eclipse)` | `var(--foreground)` |
| Fields/Inputs | `--field-placeholder` | `var(--muted)` |  |
| Fields/Inputs | `--field-border` | `transparent` | `transparent` |
| Fields/Inputs | `--color-field-on-surface` | `oklch(96% 0.006 265)` | `oklch(25% 0.006 265)` |
| Shadows | `--shadow-color-sm` | `rgba(0, 0, 0, 0.06)` | `rgba(0, 0, 0, 0.2)` |
| Shadows | `--shadow-color-md` | `rgba(0, 0, 0, 0.08)` | `rgba(0, 0, 0, 0.25)` |
| Shadows | `--shadow-color-lg` | `rgba(0, 0, 0, 0.1)` | `rgba(0, 0, 0, 0.3)` |
| Shadows | `--shadow-color-tooltip` | `rgba(0, 0, 0, 0.1)` | `rgba(0, 0, 0, 0.3)` |
| Shadows | `--shadow-color-hover` | `rgba(0, 0, 0, 0.08)` | `rgba(0, 0, 0, 0.2)` |
| Fonts | `--font-heading` | `ui-sans-serif, system-ui, sans-serif` |  |
| Fonts | `--font-body` | `ui-sans-serif, system-ui, sans-serif` |  |
| Fonts | `--font-label` | `var(--font-body)` |  |
| Fonts | `--font-button` | `var(--font-body)` |  |
| Fonts | `--font-caption` | `var(--font-body)` |  |
| Fonts | `--font-display` | `var(--font-heading)` |  |
| Fonts | `--font-accent` | `var(--font-heading)` |  |
| Font weights | `--font-weight-body` | `400` |  |
| Font weights | `--font-weight-heading` | `600` |  |
| Font weights | `--font-weight-button` | `500` |  |
| Font weights | `--font-weight-label` | `500` |  |
| Font weights | `--font-weight-caption` | `400` |  |
| Font weights | `--font-weight-display` | `700` |  |
| Semantic radius roles → map to primitives | `--radius-semantic-button` | `var(--radius-md, 4px)` |  |
| Semantic radius roles → map to primitives | `--radius-semantic-input` | `var(--radius-md, 4px)` |  |
| Semantic radius roles → map to primitives | `--radius-semantic-card` | `var(--radius-lg, 6px)` |  |
| Semantic radius roles → map to primitives | `--radius-semantic-modal` | `var(--radius-xl, 8px)` |  |
| Semantic radius roles → map to primitives | `--radius-semantic-badge` | `var(--radius-full, 9999px)` |  |
| Semantic radius roles → map to primitives | `--radius-semantic-avatar` | `var(--radius-full, 9999px)` |  |
| Semantic radius roles → map to primitives | `--radius-semantic-tooltip` | `var(--radius-lg, 6px)` |  |
| Semantic radius roles → map to primitives | `--radius-semantic-image` | `var(--radius-md, 4px)` |  |
| Radius primitives (fallbacks for tokens.css) | `--radius-sm` | `2px` |  |
| Radius primitives (fallbacks for tokens.css) | `--radius-md` | `4px` |  |
| Radius primitives (fallbacks for tokens.css) | `--radius-lg` | `6px` |  |
| Radius primitives (fallbacks for tokens.css) | `--radius-xl` | `8px` |  |
| Radius primitives (fallbacks for tokens.css) | `--radius-2xl` | `12px` |  |
| Radius primitives (fallbacks for tokens.css) | `--radius-3xl` | `20px` |  |
| Radius primitives (fallbacks for tokens.css) | `--radius-full` | `9999px` |  |
| Spacing (fallbacks for tokens.css) | `--space-xs` | `4px` |  |
| Spacing (fallbacks for tokens.css) | `--space-sm` | `6px` |  |
| Spacing (fallbacks for tokens.css) | `--space-md` | `8px` |  |
| Spacing (fallbacks for tokens.css) | `--space-lg` | `10px` |  |
| Spacing (fallbacks for tokens.css) | `--space-xl` | `12px` |  |
| Spacing (fallbacks for tokens.css) | `--space-2xl` | `16px` |  |
| Spacing (fallbacks for tokens.css) | `--space-3xl` | `20px` |  |
| Spacing (fallbacks for tokens.css) | `--space-4xl` | `24px` |  |
| Transitions | `--transition-fast` | `0.1s ease` |  |
| Transitions | `--transition-base` | `0.15s ease` |  |
| Opacity | `--opacity-disabled` | `0.3` |  |
| Opacity | `--opacity-muted` | `0.5` |  |
| Canvas background | `--color-canvas-bg` | `#f5f5f5` | `#080809` |
| Legacy tokens.css shorthand aliases used by embed components | `--text-primary` | `var(--foreground)` | `var(--foreground)` |
| Legacy tokens.css shorthand aliases used by embed components | `--text-secondary` | `var(--muted-foreground)` | `var(--muted-foreground)` |
| Legacy tokens.css shorthand aliases used by embed components | `--bg-primary` | `var(--surface)` | `var(--surface)` |
| Legacy tokens.css shorthand aliases used by embed components | `--bg-secondary` | `var(--background)` | `var(--background)` |
