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

# Themes

> Built-in themes for color, fonts, and layout.

export const ComponentPreview = ({json, height, resizable, bare, hideJson, playground, children}) => {
  const hostRef = React.useRef(null);
  const handleRef = React.useRef(null);
  const cardRef = React.useRef(null);
  const containerRef = React.useRef(null);
  const [previewWidth, setPreviewWidth] = React.useState(null);
  const [isDragging, setIsDragging] = React.useState(false);
  const [playgroundHref, setPlaygroundHref] = React.useState(null);
  React.useEffect(function () {
    if (!playground) return;
    var isLocal = location.hostname === "localhost" || location.hostname === "127.0.0.1";
    var path = isLocal ? "/playground" : "/docs/playground";
    var url = new URL(path, window.location.href);
    url.hash = "code=" + playground;
    setPlaygroundHref(url.href);
  }, [playground]);
  var jsonStr = typeof json === "string" ? json : JSON.stringify(json, null, 2);
  React.useEffect(function () {
    if (cardRef.current) {
      var card = cardRef.current.closest(".group");
      if (card) card.classList.remove("group");
    }
    var host = hostRef.current;
    if (!host || !json) return;
    function mount() {
      if (!window.__prefab || !host) return;
      var dark = document.documentElement.classList.contains("dark");
      handleRef.current = window.__prefab.mountPreview(host, jsonStr, {
        dark: dark
      });
      if (bare && host.shadowRoot) {
        var m = host.shadowRoot.querySelector("[data-prefab-mount]");
        if (m) m.style.background = "transparent";
      }
    }
    if (window.__prefab) {
      mount();
    } else {
      if (!window.__prefabLoading) {
        if (!window.__prefabReady) {
          var s = document.createElement("script");
          s.src = "/renderer.js";
          document.head.appendChild(s);
        }
        window.__prefabLoading = new Promise(function (resolve) {
          function check() {
            if (window.__prefabReady) {
              window.__prefabReady.then(resolve);
            } else {
              setTimeout(check, 10);
            }
          }
          check();
        });
      }
      window.__prefabLoading.then(mount);
    }
    var observer = new MutationObserver(function () {
      var dark = document.documentElement.classList.contains("dark");
      if (handleRef.current) handleRef.current.setDark(dark);
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return function () {
      observer.disconnect();
      if (handleRef.current) {
        handleRef.current.unmount();
        handleRef.current = null;
      }
    };
  }, [json]);
  function startResize(e) {
    e.preventDefault();
    var startX = e.clientX;
    var startW = hostRef.current ? hostRef.current.offsetWidth : 0;
    var maxW = containerRef.current ? containerRef.current.offsetWidth - 10 : startW;
    setIsDragging(true);
    function onMove(ev) {
      var w = Math.max(260, Math.min(startW + (ev.clientX - startX), maxW));
      setPreviewWidth(w);
    }
    function onUp() {
      document.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseup", onUp);
      setIsDragging(false);
    }
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
  }
  if (bare) {
    return <div ref={hostRef} />;
  }
  if (resizable) {
    return <Card>
        <div ref={cardRef} />
        <div ref={containerRef} style={{
      display: "flex",
      position: "relative",
      overflow: "hidden"
    }}>
          <div ref={hostRef} style={{
      flex: previewWidth ? "none" : 1,
      width: previewWidth ? previewWidth + "px" : undefined,
      minWidth: 0
    }} />
          <div onMouseDown={startResize} style={{
      width: "14px",
      flexShrink: 0,
      cursor: "col-resize",
      background: isDragging ? "var(--border, #e5e7eb)" : "color-mix(in srgb, var(--border, #e5e7eb) 40%, transparent)",
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      transition: "background 0.15s",
      userSelect: "none"
    }}>
            <svg width="6" height="24" style={{
      opacity: 0.5
    }}>
              <circle cx="2" cy="4" r="1.2" fill="currentColor" />
              <circle cx="2" cy="9" r="1.2" fill="currentColor" />
              <circle cx="2" cy="14" r="1.2" fill="currentColor" />
              <circle cx="2" cy="19" r="1.2" fill="currentColor" />
              <circle cx="5" cy="4" r="1.2" fill="currentColor" />
              <circle cx="5" cy="9" r="1.2" fill="currentColor" />
              <circle cx="5" cy="14" r="1.2" fill="currentColor" />
              <circle cx="5" cy="19" r="1.2" fill="currentColor" />
            </svg>
          </div>
          <div style={{
      flex: previewWidth ? 1 : "0 0 24px",
      minWidth: "24px",
      background: "repeating-linear-gradient(-45deg, transparent, transparent 3px, var(--border, #e5e7eb) 3px, var(--border, #e5e7eb) 4px)",
      opacity: 0.4
    }} />
          {previewWidth && <div style={{
      position: "absolute",
      bottom: "8px",
      right: "8px",
      background: "var(--background, white)",
      border: "1px solid var(--border, #e5e7eb)",
      borderRadius: "4px",
      padding: "1px 6px",
      fontSize: "11px",
      fontFamily: "monospace",
      color: "var(--muted-foreground, #6b7280)",
      pointerEvents: "none"
    }}>
              {previewWidth}px
            </div>}
        </div>
        {playgroundHref && <div style={{
      display: "flex",
      justifyContent: "flex-end",
      padding: "4px 0 8px"
    }}>
            <a href={playgroundHref} target="_blank" rel="noopener noreferrer" style={{
      fontSize: "11px",
      opacity: 0.4,
      textDecoration: "none",
      textDecorationLine: "none",
      borderBottom: "none",
      boxShadow: "none",
      display: "inline-flex",
      alignItems: "center",
      gap: "3px",
      transition: "opacity 0.15s",
      fontWeight: 400
    }} onMouseOver={function (e) {
      e.currentTarget.style.opacity = "0.6";
    }} onMouseOut={function (e) {
      e.currentTarget.style.opacity = "0.4";
    }}>
              Edit in Playground
              <svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h6v6" /><path d="M10 14 21 3" /><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>
            </a>
          </div>}
        <div style={{
      marginBottom: "-2rem"
    }}>{children}</div>
      </Card>;
  }
  return <Card>
      <div ref={cardRef} />
      <div ref={hostRef} />
      {playgroundHref && <div style={{
    display: "flex",
    justifyContent: "flex-end",
    padding: "4px 0 8px"
  }}>
          <a href={playgroundHref} target="_blank" rel="noopener noreferrer" style={{
    fontSize: "11px",
    opacity: 0.4,
    textDecoration: "none",
    textDecorationLine: "none",
    borderBottom: "none",
    boxShadow: "none",
    display: "inline-flex",
    alignItems: "center",
    gap: "3px",
    transition: "opacity 0.15s",
    fontWeight: 400
  }} onMouseOver={function (e) {
    e.currentTarget.style.opacity = "0.6";
  }} onMouseOut={function (e) {
    e.currentTarget.style.opacity = "0.4";
  }}>
            Edit in Playground
            <svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h6v6" /><path d="M10 14 21 3" /><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>
          </a>
        </div>}
      <div style={{
    marginBottom: "-2rem"
  }}>{children}</div>
    </Card>;
};

Prefab renders with sensible defaults out of the box: system fonts, automatic gaps between cards, comfortable card padding, and gradient fills on progress bars. No theme is required for a polished look.

Themes let you go further: accent colors, custom fonts, dark chrome, and data-oriented styling. Pass a `theme` to `PrefabApp`:

```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
from prefab_ui.app import PrefabApp
from prefab_ui.themes import Presentation

app = PrefabApp(view=my_view, theme=Presentation(accent="blue"))
```

## Themes

<Tabs>
  <Tab title="Default">
    No theme needed. The base renderer ships with system fonts, automatic gaps between cards, comfortable card padding, and gradient fills. This is what every `PrefabApp` looks like with no `theme` argument.

    <ComponentPreview json={{"view":{"cssClass":"pf-app-root","type":"Div","children":[{"type":"Column","children":[{"cssClass":"grid-cols-3","type":"Grid","children":[{"type":"Card","children":[{"type":"CardContent","children":[{"type":"Metric","label":"Revenue","value":"$1.2M","delta":"+12%"}]}]},{"type":"Card","children":[{"type":"CardContent","children":[{"type":"Metric","label":"Users","value":"8,420","delta":"-4.3%"}]}]},{"type":"Card","children":[{"type":"CardContent","children":[{"type":"Metric","label":"Uptime","value":"99.9%","delta":"+0.1%"}]}]}]},{"type":"Card","children":[{"type":"CardHeader","children":[{"type":"CardTitle","content":"Pipeline"}]},{"type":"CardContent","children":[{"cssClass":"gap-2","type":"Column","children":[{"type":"Progress","value":92.0,"variant":"success","size":"default"},{"type":"Progress","value":67.0,"variant":"default","size":"default"},{"type":"Progress","value":34.0,"variant":"warning","size":"default"}]}]}]}]}]}}} playground="ZnJvbSBwcmVmYWJfdWkuYXBwIGltcG9ydCBQcmVmYWJBcHAKZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgQ2FyZCwgQ2FyZENvbnRlbnQsIE1ldHJpYywgR3JpZCwgQ29sdW1uLCBQcm9ncmVzcywgQ2FyZEhlYWRlciwgQ2FyZFRpdGxlCgp3aXRoIENvbHVtbigpIGFzIHZpZXc6CiAgICB3aXRoIEdyaWQoY29sdW1ucz0zKToKICAgICAgICB3aXRoIENhcmQoKToKICAgICAgICAgICAgd2l0aCBDYXJkQ29udGVudCgpOgogICAgICAgICAgICAgICAgTWV0cmljKGxhYmVsPSJSZXZlbnVlIiwgdmFsdWU9IiQxLjJNIiwgZGVsdGE9IisxMiUiKQogICAgICAgIHdpdGggQ2FyZCgpOgogICAgICAgICAgICB3aXRoIENhcmRDb250ZW50KCk6CiAgICAgICAgICAgICAgICBNZXRyaWMobGFiZWw9IlVzZXJzIiwgdmFsdWU9IjgsNDIwIiwgZGVsdGE9Ii00LjMlIikKICAgICAgICB3aXRoIENhcmQoKToKICAgICAgICAgICAgd2l0aCBDYXJkQ29udGVudCgpOgogICAgICAgICAgICAgICAgTWV0cmljKGxhYmVsPSJVcHRpbWUiLCB2YWx1ZT0iOTkuOSUiLCBkZWx0YT0iKzAuMSUiKQogICAgd2l0aCBDYXJkKCk6CiAgICAgICAgd2l0aCBDYXJkSGVhZGVyKCk6CiAgICAgICAgICAgIENhcmRUaXRsZSgiUGlwZWxpbmUiKQogICAgICAgIHdpdGggQ2FyZENvbnRlbnQoKToKICAgICAgICAgICAgd2l0aCBDb2x1bW4oZ2FwPTIpOgogICAgICAgICAgICAgICAgUHJvZ3Jlc3ModmFsdWU9OTIsIHZhcmlhbnQ9InN1Y2Nlc3MiLCBsYWJlbD0iQnVpbGRzIikKICAgICAgICAgICAgICAgIFByb2dyZXNzKHZhbHVlPTY3LCB2YXJpYW50PSJkZWZhdWx0IiwgbGFiZWw9IkRlcGxveSIpCiAgICAgICAgICAgICAgICBQcm9ncmVzcyh2YWx1ZT0zNCwgdmFyaWFudD0id2FybmluZyIsIGxhYmVsPSJUZXN0cyIpCgpQcmVmYWJBcHAodmlldz12aWV3KQo">
      <CodeGroup>
        ```python Python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
        from prefab_ui.app import PrefabApp
        from prefab_ui.components import Card, CardContent, Metric, Grid, Column, Progress, CardHeader, CardTitle

        with Column() as view:
            with Grid(columns=3):
                with Card():
                    with CardContent():
                        Metric(label="Revenue", value="$1.2M", delta="+12%")
                with Card():
                    with CardContent():
                        Metric(label="Users", value="8,420", delta="-4.3%")
                with Card():
                    with CardContent():
                        Metric(label="Uptime", value="99.9%", delta="+0.1%")
            with Card():
                with CardHeader():
                    CardTitle("Pipeline")
                with CardContent():
                    with Column(gap=2):
                        Progress(value=92, variant="success", label="Builds")
                        Progress(value=67, variant="default", label="Deploy")
                        Progress(value=34, variant="warning", label="Tests")

        PrefabApp(view=view)
        ```

        ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
        {
          "view": {
            "cssClass": "pf-app-root",
            "type": "Div",
            "children": [
              {
                "type": "Column",
                "children": [
                  {
                    "cssClass": "grid-cols-3",
                    "type": "Grid",
                    "children": [
                      {
                        "type": "Card",
                        "children": [
                          {
                            "type": "CardContent",
                            "children": [{"type": "Metric", "label": "Revenue", "value": "$1.2M", "delta": "+12%"}]
                          }
                        ]
                      },
                      {
                        "type": "Card",
                        "children": [
                          {
                            "type": "CardContent",
                            "children": [{"type": "Metric", "label": "Users", "value": "8,420", "delta": "-4.3%"}]
                          }
                        ]
                      },
                      {
                        "type": "Card",
                        "children": [
                          {
                            "type": "CardContent",
                            "children": [{"type": "Metric", "label": "Uptime", "value": "99.9%", "delta": "+0.1%"}]
                          }
                        ]
                      }
                    ]
                  },
                  {
                    "type": "Card",
                    "children": [
                      {
                        "type": "CardHeader",
                        "children": [{"type": "CardTitle", "content": "Pipeline"}]
                      },
                      {
                        "type": "CardContent",
                        "children": [
                          {
                            "cssClass": "gap-2",
                            "type": "Column",
                            "children": [
                              {"type": "Progress", "value": 92.0, "variant": "success", "size": "default"},
                              {"type": "Progress", "value": 67.0, "variant": "default", "size": "default"},
                              {"type": "Progress", "value": 34.0, "variant": "warning", "size": "default"}
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        }
        ```
      </CodeGroup>
    </ComponentPreview>
  </Tab>

  <Tab title="Presentation">
    Optimized for displaying one card at a time. Presentation uses a dark, blue-tinted slate chrome with Inter font, gradient fills, generous card padding, tinted badges, and taller table rows. The dark background makes charts and colored indicators stand out. Use it for slides, demos, and focused data displays.

    <ComponentPreview json={{"view":{"cssClass":"pf-app-root","type":"Div","children":[{"type":"Card","children":[{"type":"CardHeader","children":[{"content":"Week of Mar 9, 2026","type":"Muted"},{"cssClass":"font-bold","content":"Service Health vs Target","type":"H2"}]},{"type":"CardContent","children":[{"type":"DataTable","columns":[{"key":"name","header":"Metric","sortable":false},{"key":"current","header":"Current","sortable":false,"headerClass":"text-right","cellClass":"text-right"},{"key":"target","header":"Target","sortable":false,"headerClass":"text-right","cellClass":"text-right"},{"key":"gauge","header":"vs Target","sortable":false,"width":"180px"},{"key":"status","header":"","sortable":false}],"rows":[{"name":"API Requests","current":"2,531/s","target":"3,900/s","gauge":{"type":"Progress","value":65.0,"variant":"default","size":"default"},"status":{"type":"Badge","label":"65%","variant":"default"}},{"name":"Latency P99","current":"38ms","target":"52ms","gauge":{"type":"Progress","value":73.0,"variant":"success","size":"default"},"status":{"type":"Badge","label":"73%","variant":"success"}},{"name":"Error Rate","current":"0.4%","target":"0.5%","gauge":{"type":"Progress","value":80.0,"variant":"success","size":"default"},"status":{"type":"Badge","label":"80%","variant":"success"}},{"name":"Cache Hit","current":"91%","target":"95%","gauge":{"type":"Progress","value":96.0,"variant":"warning","size":"default"},"status":{"type":"Badge","label":"96%","variant":"warning"}}],"search":false,"paginated":false,"pageSize":10}]}]}]},"css":["@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\n:root {\n  --card-padding-y: 2.5rem; --layout-gap: 1.5rem; --background: #0f1117; --foreground: #e2e8f0; --card: #1a1d2e; --card-foreground: #f1f5f9; --popover: #1a1d2e; --popover-foreground: #f1f5f9; --secondary: #252840; --secondary-foreground: #e2e8f0; --muted: #252840; --muted-foreground: #94a3b8; --accent: #2a2d3e; --accent-foreground: #e2e8f0; --destructive: #f472b6; --success: #34d399; --warning: #f59e0b; --info: #818cf8; --border: #1e2235; --input: #2a2d3e; --primary: oklch(0.7 0.18 var(--accent-hue, 275)); --primary-foreground: oklch(0.205 0 0); --ring: oklch(0.7 0.18 var(--accent-hue, 275)); --chart-1: oklch(0.72 0.22 var(--accent-hue, 275)); --chart-2: #34d399; --chart-3: #f59e0b; --chart-4: #f472b6; --chart-5: #38bdf8;\n}\n.dark {\n  --card-padding-y: 2.5rem; --layout-gap: 1.5rem; --background: #0f1117; --foreground: #e2e8f0; --card: #1a1d2e; --card-foreground: #f1f5f9; --popover: #1a1d2e; --popover-foreground: #f1f5f9; --secondary: #252840; --secondary-foreground: #e2e8f0; --muted: #252840; --muted-foreground: #94a3b8; --accent: #2a2d3e; --accent-foreground: #e2e8f0; --destructive: #f472b6; --success: #34d399; --warning: #f59e0b; --info: #818cf8; --border: #1e2235; --input: #2a2d3e; --primary: oklch(0.7 0.18 var(--accent-hue, 275)); --primary-foreground: oklch(0.205 0 0); --ring: oklch(0.7 0.18 var(--accent-hue, 275)); --chart-1: oklch(0.72 0.22 var(--accent-hue, 275)); --chart-2: #34d399; --chart-3: #f59e0b; --chart-4: #f472b6; --chart-5: #38bdf8;\n}\n/* App root \u2014 centered with max-width for slide-like presentation */\n.pf-app-root {\n  padding: 2rem;\n  max-width: 64rem;\n  margin: 0 auto;\n}\n\n/* Font */\n.pf-card, .pf-table {\n  font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;\n}\n\n/* Card \u2014 generous horizontal padding for slide-like presentation */\n.pf-card-header,\n.pf-card-content {\n  padding-left: 2rem;\n  padding-right: 2rem;\n}\n\n/* Progress/Slider \u2014 dark navy tracks */\n.pf-progress,\n.pf-progress-track {\n  background: #252840;\n}\n.pf-slider-track {\n  background: #252840;\n}\n.pf-progress-target {\n  background: #f1f5f9;\n  opacity: 0.5;\n}\n\n/* Badge \u2014 tinted backgrounds, consistent border-radius */\n.pf-badge {\n  border-radius: 6px;\n  font-weight: 600;\n  font-size: 0.9rem;\n  padding: 0.2em 0.6em;\n}\n.pf-badge-variant-default {\n  color: oklch(0.78 0.15 var(--accent-hue, 275));\n  background: oklch(0.72 0.22 var(--accent-hue, 275) / 0.12);\n}\n.pf-badge-variant-warning {\n  color: #fcd34d;\n  background: rgba(245, 158, 11, 0.12);\n}\n.pf-badge-variant-destructive {\n  color: #f9a8d4;\n  background: rgba(244, 114, 182, 0.12);\n}\n\n/* Table cells \u2014 taller rows, tabular numerals, dimmer number color */\n.pf-table-cell {\n  padding: 0.85rem 0.75rem;\n  font-size: 0.9rem;\n  font-variant-numeric: tabular-nums;\n  color: #cbd5e1;\n}\n\n/* Table rows \u2014 subtle borders, accent hover */\n.pf-table-row {\n  border-color: #1e2235;\n}\n.pf-table-row:hover {\n  background: oklch(0.72 0.22 var(--accent-hue, 275) / 0.06);\n}\n"]}} playground="ZnJvbSBwcmVmYWJfdWkuYXBwIGltcG9ydCBQcmVmYWJBcHAKZnJvbSBwcmVmYWJfdWkudGhlbWVzIGltcG9ydCBQcmVzZW50YXRpb24KZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgQ2FyZCwgQ2FyZEhlYWRlciwgQ2FyZENvbnRlbnQsIE11dGVkLCBCYWRnZSwgRGF0YVRhYmxlLCBEYXRhVGFibGVDb2x1bW4sIFByb2dyZXNzLCBIMgoKcm93cyA9IFsKICAgIHsibmFtZSI6ICJBUEkgUmVxdWVzdHMiLCAgICJjdXJyZW50IjogIjIsNTMxL3MiLCAgInRhcmdldCI6ICIzLDkwMC9zIiwgICJnYXVnZSI6IFByb2dyZXNzKHZhbHVlPTY1KSwgICAgICAgICAgICAgICJzdGF0dXMiOiBCYWRnZSgiNjUlIil9LAogICAgeyJuYW1lIjogIkxhdGVuY3kgUDk5IiwgICAgImN1cnJlbnQiOiAiMzhtcyIsICAgICAgInRhcmdldCI6ICI1Mm1zIiwgICAgICJnYXVnZSI6IFByb2dyZXNzKHZhbHVlPTczLCB2YXJpYW50PSJzdWNjZXNzIiksICAic3RhdHVzIjogQmFkZ2UoIjczJSIsIHZhcmlhbnQ9InN1Y2Nlc3MiKX0sCiAgICB7Im5hbWUiOiAiRXJyb3IgUmF0ZSIsICAgICAiY3VycmVudCI6ICIwLjQlIiwgICAgICAidGFyZ2V0IjogIjAuNSUiLCAgICAgImdhdWdlIjogUHJvZ3Jlc3ModmFsdWU9ODAsIHZhcmlhbnQ9InN1Y2Nlc3MiKSwgICJzdGF0dXMiOiBCYWRnZSgiODAlIiwgdmFyaWFudD0ic3VjY2VzcyIpfSwKICAgIHsibmFtZSI6ICJDYWNoZSBIaXQiLCAgICAgICJjdXJyZW50IjogIjkxJSIsICAgICAgICJ0YXJnZXQiOiAiOTUlIiwgICAgICAiZ2F1Z2UiOiBQcm9ncmVzcyh2YWx1ZT05NiwgdmFyaWFudD0id2FybmluZyIpLCAic3RhdHVzIjogQmFkZ2UoIjk2JSIsIHZhcmlhbnQ9Indhcm5pbmciKX0sCl0KCndpdGggQ2FyZCgpIGFzIHZpZXc6CiAgICB3aXRoIENhcmRIZWFkZXIoKToKICAgICAgICBNdXRlZCgiV2VlayBvZiBNYXIgOSwgMjAyNiIpCiAgICAgICAgSDIoIlNlcnZpY2UgSGVhbHRoIHZzIFRhcmdldCIsIGJvbGQ9VHJ1ZSkKICAgIHdpdGggQ2FyZENvbnRlbnQoKToKICAgICAgICBEYXRhVGFibGUoCiAgICAgICAgICAgIGNvbHVtbnM9WwogICAgICAgICAgICAgICAgRGF0YVRhYmxlQ29sdW1uKGtleT0ibmFtZSIsIGhlYWRlcj0iTWV0cmljIiksCiAgICAgICAgICAgICAgICBEYXRhVGFibGVDb2x1bW4oa2V5PSJjdXJyZW50IiwgaGVhZGVyPSJDdXJyZW50IiwgYWxpZ249InJpZ2h0IiksCiAgICAgICAgICAgICAgICBEYXRhVGFibGVDb2x1bW4oa2V5PSJ0YXJnZXQiLCBoZWFkZXI9IlRhcmdldCIsIGFsaWduPSJyaWdodCIpLAogICAgICAgICAgICAgICAgRGF0YVRhYmxlQ29sdW1uKGtleT0iZ2F1Z2UiLCBoZWFkZXI9InZzIFRhcmdldCIsIHdpZHRoPSIxODBweCIpLAogICAgICAgICAgICAgICAgRGF0YVRhYmxlQ29sdW1uKGtleT0ic3RhdHVzIiwgaGVhZGVyPSIiKSwKICAgICAgICAgICAgXSwKICAgICAgICAgICAgcm93cz1yb3dzLAogICAgICAgICkKClByZWZhYkFwcCh2aWV3PXZpZXcsIHRoZW1lPVByZXNlbnRhdGlvbigpKQo">
      <CodeGroup>
        ```python Python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
        from prefab_ui.app import PrefabApp
        from prefab_ui.themes import Presentation
        from prefab_ui.components import Card, CardHeader, CardContent, Muted, Badge, DataTable, DataTableColumn, Progress, H2

        rows = [
            {"name": "API Requests",   "current": "2,531/s",  "target": "3,900/s",  "gauge": Progress(value=65),              "status": Badge("65%")},
            {"name": "Latency P99",    "current": "38ms",      "target": "52ms",     "gauge": Progress(value=73, variant="success"),  "status": Badge("73%", variant="success")},
            {"name": "Error Rate",     "current": "0.4%",      "target": "0.5%",     "gauge": Progress(value=80, variant="success"),  "status": Badge("80%", variant="success")},
            {"name": "Cache Hit",      "current": "91%",       "target": "95%",      "gauge": Progress(value=96, variant="warning"), "status": Badge("96%", variant="warning")},
        ]

        with Card() as view:
            with CardHeader():
                Muted("Week of Mar 9, 2026")
                H2("Service Health vs Target", bold=True)
            with CardContent():
                DataTable(
                    columns=[
                        DataTableColumn(key="name", header="Metric"),
                        DataTableColumn(key="current", header="Current", align="right"),
                        DataTableColumn(key="target", header="Target", align="right"),
                        DataTableColumn(key="gauge", header="vs Target", width="180px"),
                        DataTableColumn(key="status", header=""),
                    ],
                    rows=rows,
                )

        PrefabApp(view=view, theme=Presentation())
        ```

        ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
        {
          "view": {
            "cssClass": "pf-app-root",
            "type": "Div",
            "children": [
              {
                "type": "Card",
                "children": [
                  {
                    "type": "CardHeader",
                    "children": [
                      {"content": "Week of Mar 9, 2026", "type": "Muted"},
                      {"cssClass": "font-bold", "content": "Service Health vs Target", "type": "H2"}
                    ]
                  },
                  {
                    "type": "CardContent",
                    "children": [
                      {
                        "type": "DataTable",
                        "columns": [
                          {"key": "name", "header": "Metric", "sortable": false},
                          {
                            "key": "current",
                            "header": "Current",
                            "sortable": false,
                            "headerClass": "text-right",
                            "cellClass": "text-right"
                          },
                          {
                            "key": "target",
                            "header": "Target",
                            "sortable": false,
                            "headerClass": "text-right",
                            "cellClass": "text-right"
                          },
                          {"key": "gauge", "header": "vs Target", "sortable": false, "width": "180px"},
                          {"key": "status", "header": "", "sortable": false}
                        ],
                        "rows": [
                          {
                            "name": "API Requests",
                            "current": "2,531/s",
                            "target": "3,900/s",
                            "gauge": {"type": "Progress", "value": 65.0, "variant": "default", "size": "default"},
                            "status": {"type": "Badge", "label": "65%", "variant": "default"}
                          },
                          {
                            "name": "Latency P99",
                            "current": "38ms",
                            "target": "52ms",
                            "gauge": {"type": "Progress", "value": 73.0, "variant": "success", "size": "default"},
                            "status": {"type": "Badge", "label": "73%", "variant": "success"}
                          },
                          {
                            "name": "Error Rate",
                            "current": "0.4%",
                            "target": "0.5%",
                            "gauge": {"type": "Progress", "value": 80.0, "variant": "success", "size": "default"},
                            "status": {"type": "Badge", "label": "80%", "variant": "success"}
                          },
                          {
                            "name": "Cache Hit",
                            "current": "91%",
                            "target": "95%",
                            "gauge": {"type": "Progress", "value": 96.0, "variant": "warning", "size": "default"},
                            "status": {"type": "Badge", "label": "96%", "variant": "warning"}
                          }
                        ],
                        "search": false,
                        "paginated": false,
                        "pageSize": 10
                      }
                    ]
                  }
                ]
              }
            ]
          },
          "css": [
            "@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\n:root {\n  --card-padding-y: 2.5rem; --layout-gap: 1.5rem; --background: #0f1117; --foreground: #e2e8f0; --card: #1a1d2e; --card-foreground: #f1f5f9; --popover: #1a1d2e; --popover-foreground: #f1f5f9; --secondary: #252840; --secondary-foreground: #e2e8f0; --muted: #252840; --muted-foreground: #94a3b8; --accent: #2a2d3e; --accent-foreground: #e2e8f0; --destructive: #f472b6; --success: #34d399; --warning: #f59e0b; --info: #818cf8; --border: #1e2235; --input: #2a2d3e; --primary: oklch(0.7 0.18 var(--accent-hue, 275)); --primary-foreground: oklch(0.205 0 0); --ring: oklch(0.7 0.18 var(--accent-hue, 275)); --chart-1: oklch(0.72 0.22 var(--accent-hue, 275)); --chart-2: #34d399; --chart-3: #f59e0b; --chart-4: #f472b6; --chart-5: #38bdf8;\n}\n.dark {\n  --card-padding-y: 2.5rem; --layout-gap: 1.5rem; --background: #0f1117; --foreground: #e2e8f0; --card: #1a1d2e; --card-foreground: #f1f5f9; --popover: #1a1d2e; --popover-foreground: #f1f5f9; --secondary: #252840; --secondary-foreground: #e2e8f0; --muted: #252840; --muted-foreground: #94a3b8; --accent: #2a2d3e; --accent-foreground: #e2e8f0; --destructive: #f472b6; --success: #34d399; --warning: #f59e0b; --info: #818cf8; --border: #1e2235; --input: #2a2d3e; --primary: oklch(0.7 0.18 var(--accent-hue, 275)); --primary-foreground: oklch(0.205 0 0); --ring: oklch(0.7 0.18 var(--accent-hue, 275)); --chart-1: oklch(0.72 0.22 var(--accent-hue, 275)); --chart-2: #34d399; --chart-3: #f59e0b; --chart-4: #f472b6; --chart-5: #38bdf8;\n}\n/* App root \u2014 centered with max-width for slide-like presentation */\n.pf-app-root {\n  padding: 2rem;\n  max-width: 64rem;\n  margin: 0 auto;\n}\n\n/* Font */\n.pf-card, .pf-table {\n  font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;\n}\n\n/* Card \u2014 generous horizontal padding for slide-like presentation */\n.pf-card-header,\n.pf-card-content {\n  padding-left: 2rem;\n  padding-right: 2rem;\n}\n\n/* Progress/Slider \u2014 dark navy tracks */\n.pf-progress,\n.pf-progress-track {\n  background: #252840;\n}\n.pf-slider-track {\n  background: #252840;\n}\n.pf-progress-target {\n  background: #f1f5f9;\n  opacity: 0.5;\n}\n\n/* Badge \u2014 tinted backgrounds, consistent border-radius */\n.pf-badge {\n  border-radius: 6px;\n  font-weight: 600;\n  font-size: 0.9rem;\n  padding: 0.2em 0.6em;\n}\n.pf-badge-variant-default {\n  color: oklch(0.78 0.15 var(--accent-hue, 275));\n  background: oklch(0.72 0.22 var(--accent-hue, 275) / 0.12);\n}\n.pf-badge-variant-warning {\n  color: #fcd34d;\n  background: rgba(245, 158, 11, 0.12);\n}\n.pf-badge-variant-destructive {\n  color: #f9a8d4;\n  background: rgba(244, 114, 182, 0.12);\n}\n\n/* Table cells \u2014 taller rows, tabular numerals, dimmer number color */\n.pf-table-cell {\n  padding: 0.85rem 0.75rem;\n  font-size: 0.9rem;\n  font-variant-numeric: tabular-nums;\n  color: #cbd5e1;\n}\n\n/* Table rows \u2014 subtle borders, accent hover */\n.pf-table-row {\n  border-color: #1e2235;\n}\n.pf-table-row:hover {\n  background: oklch(0.72 0.22 var(--accent-hue, 275) / 0.06);\n}\n"
          ]
        }
        ```
      </CodeGroup>
    </ComponentPreview>
  </Tab>

  <Tab title="Minimal">
    Strips all renderer defaults. No card padding, no automatic gaps, no gradient fills. You control every detail.

    <ComponentPreview json={{"view":{"cssClass":"pf-app-root","type":"Div","children":[{"cssClass":"gap-4","type":"Column","children":[{"content":"System Status","type":"H2"},{"content":"Last checked 2 minutes ago","type":"Muted"},{"cssClass":"gap-2","type":"Column","children":[{"type":"Progress","value":92.0,"variant":"success","size":"default"},{"type":"Progress","value":67.0,"variant":"default","size":"default"},{"type":"Progress","value":34.0,"variant":"warning","size":"default"}]},{"cssClass":"gap-2","type":"Row","children":[{"type":"Badge","label":"3 healthy","variant":"success"},{"type":"Badge","label":"1 degraded","variant":"warning"}]}]}]},"css":[":root {\n  --card-padding-y: 0; --layout-gap: 0;\n}\n.dark {\n  --card-padding-y: 0; --layout-gap: 0;\n}\n.pf-app-root { padding: 0; }\n.pf-progress-variant-default,\n.pf-progress-variant-success,\n.pf-progress-variant-warning,\n.pf-progress-variant-destructive,\n.pf-progress-variant-info,\n.pf-slider-variant-default,\n.pf-slider-variant-success,\n.pf-slider-variant-warning,\n.pf-slider-variant-destructive,\n.pf-slider-variant-info { background-image: none; }\n.pf-ring-variant-default { stroke: var(--primary); }\n.pf-ring-variant-success { stroke: var(--success); }\n.pf-ring-variant-warning { stroke: var(--warning); }\n.pf-ring-variant-destructive { stroke: var(--destructive); }\n.pf-ring-variant-info { stroke: var(--info); }\n"]}} playground="ZnJvbSBwcmVmYWJfdWkuYXBwIGltcG9ydCBQcmVmYWJBcHAKZnJvbSBwcmVmYWJfdWkudGhlbWVzIGltcG9ydCBNaW5pbWFsCmZyb20gcHJlZmFiX3VpLmNvbXBvbmVudHMgaW1wb3J0IENvbHVtbiwgUm93LCBIMiwgTXV0ZWQsIFByb2dyZXNzLCBCYWRnZQoKd2l0aCBDb2x1bW4oZ2FwPTQpIGFzIHZpZXc6CiAgICBIMigiU3lzdGVtIFN0YXR1cyIpCiAgICBNdXRlZCgiTGFzdCBjaGVja2VkIDIgbWludXRlcyBhZ28iKQogICAgd2l0aCBDb2x1bW4oZ2FwPTIpOgogICAgICAgIFByb2dyZXNzKHZhbHVlPTkyLCB2YXJpYW50PSJzdWNjZXNzIiwgbGFiZWw9IkFQSSIpCiAgICAgICAgUHJvZ3Jlc3ModmFsdWU9NjcsIGxhYmVsPSJXb3JrZXJzIikKICAgICAgICBQcm9ncmVzcyh2YWx1ZT0zNCwgdmFyaWFudD0id2FybmluZyIsIGxhYmVsPSJRdWV1ZSIpCiAgICB3aXRoIFJvdyhnYXA9Mik6CiAgICAgICAgQmFkZ2UoIjMgaGVhbHRoeSIsIHZhcmlhbnQ9InN1Y2Nlc3MiKQogICAgICAgIEJhZGdlKCIxIGRlZ3JhZGVkIiwgdmFyaWFudD0id2FybmluZyIpCgpQcmVmYWJBcHAodmlldz12aWV3LCB0aGVtZT1NaW5pbWFsKCkpCg">
      <CodeGroup>
        ```python Python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
        from prefab_ui.app import PrefabApp
        from prefab_ui.themes import Minimal
        from prefab_ui.components import Column, Row, H2, Muted, Progress, Badge

        with Column(gap=4) as view:
            H2("System Status")
            Muted("Last checked 2 minutes ago")
            with Column(gap=2):
                Progress(value=92, variant="success", label="API")
                Progress(value=67, label="Workers")
                Progress(value=34, variant="warning", label="Queue")
            with Row(gap=2):
                Badge("3 healthy", variant="success")
                Badge("1 degraded", variant="warning")

        PrefabApp(view=view, theme=Minimal())
        ```

        ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
        {
          "view": {
            "cssClass": "pf-app-root",
            "type": "Div",
            "children": [
              {
                "cssClass": "gap-4",
                "type": "Column",
                "children": [
                  {"content": "System Status", "type": "H2"},
                  {"content": "Last checked 2 minutes ago", "type": "Muted"},
                  {
                    "cssClass": "gap-2",
                    "type": "Column",
                    "children": [
                      {"type": "Progress", "value": 92.0, "variant": "success", "size": "default"},
                      {"type": "Progress", "value": 67.0, "variant": "default", "size": "default"},
                      {"type": "Progress", "value": 34.0, "variant": "warning", "size": "default"}
                    ]
                  },
                  {
                    "cssClass": "gap-2",
                    "type": "Row",
                    "children": [
                      {"type": "Badge", "label": "3 healthy", "variant": "success"},
                      {"type": "Badge", "label": "1 degraded", "variant": "warning"}
                    ]
                  }
                ]
              }
            ]
          },
          "css": [
            ":root {\n  --card-padding-y: 0; --layout-gap: 0;\n}\n.dark {\n  --card-padding-y: 0; --layout-gap: 0;\n}\n.pf-app-root { padding: 0; }\n.pf-progress-variant-default,\n.pf-progress-variant-success,\n.pf-progress-variant-warning,\n.pf-progress-variant-destructive,\n.pf-progress-variant-info,\n.pf-slider-variant-default,\n.pf-slider-variant-success,\n.pf-slider-variant-warning,\n.pf-slider-variant-destructive,\n.pf-slider-variant-info { background-image: none; }\n.pf-ring-variant-default { stroke: var(--primary); }\n.pf-ring-variant-success { stroke: var(--success); }\n.pf-ring-variant-warning { stroke: var(--warning); }\n.pf-ring-variant-destructive { stroke: var(--destructive); }\n.pf-ring-variant-info { stroke: var(--info); }\n"
          ]
        }
        ```
      </CodeGroup>
    </ComponentPreview>
  </Tab>
</Tabs>

## Accent

All themes accept `accent`, which controls the primary color, focus rings, and chart colors. Pass a Tailwind color name, CSS color string, or OKLCH hue number:

<ComponentPreview json={{"view":{"cssClass":"pf-app-root","type":"Div","children":[{"cssClass":"gap-3","type":"Column","children":[{"content":"Welcome back","type":"Heading","level":1},{"content":"You have 3 new notifications.","type":"Text"},{"type":"Text","children":[{"content":"Run ","code":false,"type":"Span"},{"cssClass":"font-mono","content":"prefab serve app.py","code":true,"type":"Span"},{"content":" to preview locally.","code":false,"type":"Span"}]},{"cssClass":"gap-2","type":"Row","children":[{"type":"Button","label":"View all","variant":"default","size":"default","disabled":false},{"type":"Button","label":"Dismiss","variant":"outline","size":"default","disabled":false}]},{"cssClass":"gap-2","type":"Row","children":[{"type":"Badge","label":"On Track","variant":"success"},{"type":"Badge","label":"3 alerts","variant":"warning"}]}]}]},"css":[":root {\n  --primary: oklch(0.6 0.24 var(--accent-hue)); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.6 0.24 var(--accent-hue)); --chart-1: oklch(0.65 0.25 var(--accent-hue)); --chart-2: oklch(0.65 0.25 calc(var(--accent-hue) + 72)); --chart-3: oklch(0.65 0.25 calc(var(--accent-hue) + 144)); --chart-4: oklch(0.65 0.25 calc(var(--accent-hue) + 216)); --chart-5: oklch(0.65 0.25 calc(var(--accent-hue) + 288)); --primary: #0ea5e9; --ring: #0ea5e9;\n}\n.dark {\n  --primary: oklch(0.7 0.18 var(--accent-hue)); --primary-foreground: oklch(0.205 0 0); --ring: oklch(0.7 0.18 var(--accent-hue)); --chart-1: oklch(0.72 0.22 var(--accent-hue)); --chart-2: oklch(0.72 0.22 calc(var(--accent-hue) + 72)); --chart-3: oklch(0.72 0.22 calc(var(--accent-hue) + 144)); --chart-4: oklch(0.72 0.22 calc(var(--accent-hue) + 216)); --chart-5: oklch(0.72 0.22 calc(var(--accent-hue) + 288)); --primary: #0ea5e9; --ring: #0ea5e9;\n}\n"]}} playground="ZnJvbSBwcmVmYWJfdWkuYXBwIGltcG9ydCBQcmVmYWJBcHAKZnJvbSBwcmVmYWJfdWkudGhlbWVzIGltcG9ydCBCYXNpYwpmcm9tIHByZWZhYl91aS5jb21wb25lbnRzIGltcG9ydCBDb2x1bW4sIEhlYWRpbmcsIFRleHQsIFNwYW4sIEJ1dHRvbiwgQmFkZ2UsIFJvdwoKd2l0aCBDb2x1bW4oZ2FwPTMpIGFzIHZpZXc6CiAgICBIZWFkaW5nKCJXZWxjb21lIGJhY2siKQogICAgVGV4dCgiWW91IGhhdmUgMyBuZXcgbm90aWZpY2F0aW9ucy4iKQogICAgVGV4dCgiUnVuICIsIFNwYW4oInByZWZhYiBzZXJ2ZSBhcHAucHkiLCBjb2RlPVRydWUpLCAiIHRvIHByZXZpZXcgbG9jYWxseS4iKQogICAgd2l0aCBSb3coZ2FwPTIpOgogICAgICAgIEJ1dHRvbigiVmlldyBhbGwiLCB2YXJpYW50PSJkZWZhdWx0IikKICAgICAgICBCdXR0b24oIkRpc21pc3MiLCB2YXJpYW50PSJvdXRsaW5lIikKICAgIHdpdGggUm93KGdhcD0yKToKICAgICAgICBCYWRnZSgiT24gVHJhY2siLCB2YXJpYW50PSJzdWNjZXNzIikKICAgICAgICBCYWRnZSgiMyBhbGVydHMiLCB2YXJpYW50PSJ3YXJuaW5nIikKClByZWZhYkFwcCh2aWV3PXZpZXcsIHRoZW1lPUJhc2ljKGFjY2VudD0ic2t5IikpCg">
  <CodeGroup>
    ```python Python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.app import PrefabApp
    from prefab_ui.themes import Basic
    from prefab_ui.components import Column, Heading, Text, Span, Button, Badge, Row

    with Column(gap=3) as view:
        Heading("Welcome back")
        Text("You have 3 new notifications.")
        Text("Run ", Span("prefab serve app.py", code=True), " to preview locally.")
        with Row(gap=2):
            Button("View all", variant="default")
            Button("Dismiss", variant="outline")
        with Row(gap=2):
            Badge("On Track", variant="success")
            Badge("3 alerts", variant="warning")

    PrefabApp(view=view, theme=Basic(accent="sky"))
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "cssClass": "pf-app-root",
        "type": "Div",
        "children": [
          {
            "cssClass": "gap-3",
            "type": "Column",
            "children": [
              {"content": "Welcome back", "type": "Heading", "level": 1},
              {"content": "You have 3 new notifications.", "type": "Text"},
              {
                "type": "Text",
                "children": [
                  {"content": "Run ", "code": false, "type": "Span"},
                  {
                    "cssClass": "font-mono",
                    "content": "prefab serve app.py",
                    "code": true,
                    "type": "Span"
                  },
                  {"content": " to preview locally.", "code": false, "type": "Span"}
                ]
              },
              {
                "cssClass": "gap-2",
                "type": "Row",
                "children": [
                  {
                    "type": "Button",
                    "label": "View all",
                    "variant": "default",
                    "size": "default",
                    "disabled": false
                  },
                  {
                    "type": "Button",
                    "label": "Dismiss",
                    "variant": "outline",
                    "size": "default",
                    "disabled": false
                  }
                ]
              },
              {
                "cssClass": "gap-2",
                "type": "Row",
                "children": [
                  {"type": "Badge", "label": "On Track", "variant": "success"},
                  {"type": "Badge", "label": "3 alerts", "variant": "warning"}
                ]
              }
            ]
          }
        ]
      },
      "css": [
        ":root {\n  --primary: oklch(0.6 0.24 var(--accent-hue)); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.6 0.24 var(--accent-hue)); --chart-1: oklch(0.65 0.25 var(--accent-hue)); --chart-2: oklch(0.65 0.25 calc(var(--accent-hue) + 72)); --chart-3: oklch(0.65 0.25 calc(var(--accent-hue) + 144)); --chart-4: oklch(0.65 0.25 calc(var(--accent-hue) + 216)); --chart-5: oklch(0.65 0.25 calc(var(--accent-hue) + 288)); --primary: #0ea5e9; --ring: #0ea5e9;\n}\n.dark {\n  --primary: oklch(0.7 0.18 var(--accent-hue)); --primary-foreground: oklch(0.205 0 0); --ring: oklch(0.7 0.18 var(--accent-hue)); --chart-1: oklch(0.72 0.22 var(--accent-hue)); --chart-2: oklch(0.72 0.22 calc(var(--accent-hue) + 72)); --chart-3: oklch(0.72 0.22 calc(var(--accent-hue) + 144)); --chart-4: oklch(0.72 0.22 calc(var(--accent-hue) + 216)); --chart-5: oklch(0.72 0.22 calc(var(--accent-hue) + 288)); --primary: #0ea5e9; --ring: #0ea5e9;\n}\n"
      ]
    }
    ```
  </CodeGroup>
</ComponentPreview>

```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
Basic(accent="amber")          # Tailwind name (defaults to 500 shade)
Basic(accent="amber-600")      # specific shade
Basic(accent="#3b82f6")        # hex
Basic(accent=260)              # OKLCH hue (0-360)
```

With `accent=None` (the default), no color overrides are applied and components use the renderer's neutral zinc palette.

## Fonts

`font` sets the sans-serif typeface. `font_mono` sets the monospace font. Both auto-import from Google Fonts:

```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
Basic(font="Geist", font_mono="Geist Mono")
Presentation(font="IBM Plex Sans")
```

Presentation loads Inter by default, a typeface optimized for screen readability with tabular numerals that keep table columns aligned. The base renderer uses the platform's system font stack for zero-latency rendering with no font download.

## Color Scheme

By default, themes follow the OS preference for light or dark mode. Presentation's dark slate chrome renders dark regardless of system preference.

Override on any theme with `mode`:

```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
Basic(accent="blue", mode="dark")
Presentation(mode="light")
```

## Custom Themes

`Theme` gives full control. The `css` field accepts any CSS for component styling, fonts, and overrides:

```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
from prefab_ui.themes import Theme

Theme(font="Inter", css=".pf-table-cell { font-variant-numeric: tabular-nums; }")
```

For color overrides that differ between light and dark mode, use `light_css` and `dark_css`:

```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
Theme(
    light_css="--primary: oklch(0.60 0.24 260); --ring: oklch(0.60 0.24 260);",
    dark_css="--primary: oklch(0.70 0.18 260); --ring: oklch(0.70 0.18 260);",
)
```

### Variable Reference

| Variable                             | Controls                                         |
| ------------------------------------ | ------------------------------------------------ |
| `primary` / `primary-foreground`     | Primary buttons, active states                   |
| `secondary` / `secondary-foreground` | Secondary buttons                                |
| `accent` / `accent-foreground`       | Highlighted surfaces                             |
| `background` / `foreground`          | Page background and default text                 |
| `card` / `card-foreground`           | Card surfaces                                    |
| `muted` / `muted-foreground`         | Subdued text and surfaces                        |
| `destructive`                        | Delete and error states                          |
| `success` / `warning` / `info`       | Semantic status colors                           |
| `border`                             | Borders and dividers                             |
| `ring`                               | Focus rings                                      |
| `chart-1` through `chart-5`          | Chart color palette                              |
| `radius`                             | Border radius                                    |
| `card-padding-y`                     | Card vertical padding (default `1rem`)           |
| `layout-gap`                         | Gap between cards in containers (default `1rem`) |

Prefab's CSS variables follow [shadcn/ui naming conventions](https://ui.shadcn.com/docs/theming), so output from any shadcn theme generator can be pasted directly into `light_css` and `dark_css`.

## Themes in the Playground

Themes apply in the [playground](/playground) exactly as in production. The toolbar's theme picker overrides the code theme when active; selecting "Code" restores whatever your code defines.
