---
title: Themes
description: Built-in themes for color, fonts, and layout.
icon: palette
---

import { ComponentPreview } from '/snippets/component-preview.mdx'

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
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=" ">
<CodeGroup>
```python Python icon="python"
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"
{
  "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}]}]}]},"theme":{"light":"--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;","dark":"--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;","css":"@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\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=" ">
<CodeGroup>
```python Python icon="python"
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"
{
  "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
              }
            ]
          }
        ]
      }
    ]
  },
  "theme": {
    "light": "--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;",
    "dark": "--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;",
    "css": "@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\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"}]}]}]},"theme":{"light":"--card-padding-y: 0; --layout-gap: 0;","dark":"--card-padding-y: 0; --layout-gap: 0;","css":".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=" ">
<CodeGroup>
```python Python icon="python"
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"
{
  "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"}
            ]
          }
        ]
      }
    ]
  },
  "theme": {
    "light": "--card-padding-y: 0; --layout-gap: 0;",
    "dark": "--card-padding-y: 0; --layout-gap: 0;",
    "css": ".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"}]}]}]},"theme":{"light":"--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;","dark":"--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;","css":""}}} playground=" ">
<CodeGroup>
```python Python icon="python"
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"
{
  "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"}
            ]
          }
        ]
      }
    ]
  },
  "theme": {
    "light": "--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;",
    "dark": "--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;",
    "css": ""
  }
}
```
</CodeGroup>
</ComponentPreview>

```python
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
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
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
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(
    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.
