feat(css): add Base UI theme demo

This commit is contained in:
James Long 2026-07-22 14:14:58 +00:00
commit 1c3d5c399e
7 changed files with 820 additions and 34 deletions

View file

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en" data-color-scheme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#080808" />
<link rel="icon" href="data:," />
<title>OpenCode × Base UI</title>
</head>
<body data-oc-root>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View file

@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/css-base-ui",
"version": "1.18.4",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
"build": "vite build --base=./ && bun script/inline.ts",
"typecheck": "tsgo --noEmit"
},
"dependencies": {
"@base-ui/react": "1.6.0",
"@opencode-ai/css": "workspace:*",
"react": "19.2.7",
"react-dom": "19.2.7"
},
"devDependencies": {
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
"@typescript/native-preview": "catalog:",
"@vitejs/plugin-react": "6.0.3",
"vite": "8.1.4"
}
}

View file

@ -0,0 +1,20 @@
import { rm } from "node:fs/promises"
import path from "node:path"
const root = path.join(import.meta.dir, "..", "dist")
const html = await Bun.file(path.join(root, "index.html")).text()
const script = html.match(/<script type="module" crossorigin src="\.\/(assets\/[^"]+\.js)"><\/script>/)
const style = html.match(/<link rel="stylesheet" crossorigin href="\.\/(assets\/[^"]+\.css)">/)
if (!script || !style) throw new Error("Vite output did not contain the expected script and stylesheet")
const javascript = (await Bun.file(path.join(root, script[1])).text()).replaceAll("</script", "<\/script")
const stylesheet = (await Bun.file(path.join(root, style[1])).text()).replaceAll("</style", "<\/style")
await Bun.write(
path.join(root, "index.html"),
html
.replace(script[0], () => `<script type="module">${javascript}</script>`)
.replace(style[0], () => `<style>${stylesheet}</style>`),
)
await rm(path.join(root, "assets"), { recursive: true })

View file

@ -0,0 +1,260 @@
import { Accordion } from "@base-ui/react/accordion"
import { Button } from "@base-ui/react/button"
import { Checkbox } from "@base-ui/react/checkbox"
import { Dialog } from "@base-ui/react/dialog"
import { Field } from "@base-ui/react/field"
import { Input } from "@base-ui/react/input"
import { Menu } from "@base-ui/react/menu"
import { Popover } from "@base-ui/react/popover"
import { Select } from "@base-ui/react/select"
import { Slider } from "@base-ui/react/slider"
import { Switch } from "@base-ui/react/switch"
import { Tabs } from "@base-ui/react/tabs"
import { Tooltip } from "@base-ui/react/tooltip"
import { createRoot } from "react-dom/client"
import "@opencode-ai/css"
import "./theme.css"
const environments = [
{ label: "Production", value: "production" },
{ label: "Staging", value: "staging" },
{ label: "Development", value: "development" },
]
function App() {
return (
<main data-component="page">
<header data-slot="page-header">
<div data-slot="page-heading">
<p data-slot="page-eyebrow">Theme experiment / Base UI 1.6</p>
<h1 data-slot="page-title">Control room</h1>
<p data-slot="page-description">
OpenCodes unchanged colors, type, radii, and elevation applied to accessible Base UI primitives.
</p>
</div>
<div data-slot="page-actions">
<Tooltip.Provider>
<Tooltip.Root>
<Tooltip.Trigger data-component="button" data-variant="ghost" data-size="normal">
K
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Positioner sideOffset={8}>
<Tooltip.Popup data-ui="tooltip">Open command menu</Tooltip.Popup>
</Tooltip.Positioner>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
<Button data-component="button" data-variant="contrast" data-size="normal">
New deploy
</Button>
</div>
</header>
<div data-slot="page-content">
<section data-component="surface">
<header data-slot="surface-header">
<div className="section-heading">
<div>
<h2 data-slot="surface-title">Release settings</h2>
<p data-slot="surface-description">Field, Input, Select, Checkbox, Switch, and Slider</p>
</div>
<span data-component="badge" data-variant="accent">
Healthy
</span>
</div>
</header>
<div data-slot="surface-content" className="form-grid">
<Field.Root data-component="field">
<Field.Label data-slot="field-label">Release name</Field.Label>
<Input data-component="input" defaultValue="opencode-1.18.4" />
<Field.Description data-slot="field-description">Visible in the deployment timeline.</Field.Description>
</Field.Root>
<div data-component="field">
<Select.Root items={environments} defaultValue="production">
<Select.Label data-slot="field-label">Environment</Select.Label>
<Select.Trigger data-component="select" className="select-trigger">
<Select.Value />
<Select.Icon aria-hidden></Select.Icon>
</Select.Trigger>
<Select.Portal>
<Select.Positioner className="positioner" sideOffset={6} alignItemWithTrigger={false}>
<Select.Popup data-ui="popup">
{environments.map((environment) => (
<Select.Item key={environment.value} value={environment.value} data-ui="option">
<Select.ItemIndicator></Select.ItemIndicator>
<Select.ItemText>{environment.label}</Select.ItemText>
</Select.Item>
))}
</Select.Popup>
</Select.Positioner>
</Select.Portal>
</Select.Root>
<p data-slot="field-description">Controls where this build becomes available.</p>
</div>
<label data-ui="check-row">
<Checkbox.Root defaultChecked data-ui="checkbox">
<Checkbox.Indicator></Checkbox.Indicator>
</Checkbox.Root>
<span>
<strong>Run smoke tests</strong>
<small>Verify auth, sessions, and tool execution.</small>
</span>
</label>
<label data-ui="check-row">
<Switch.Root defaultChecked data-ui="switch">
<Switch.Thumb />
</Switch.Root>
<span>
<strong>Automatic rollback</strong>
<small>Restore the last healthy release on failure.</small>
</span>
</label>
<div data-component="field" className="slider-field">
<div className="field-inline">
<span data-slot="field-label">Traffic allocation</span>
<span data-component="badge">25%</span>
</div>
<Slider.Root defaultValue={25} data-ui="slider">
<Slider.Control className="slider-control">
<Slider.Track className="slider-track">
<Slider.Indicator className="slider-indicator" />
<Slider.Thumb className="slider-thumb" aria-label="Traffic allocation" />
</Slider.Track>
</Slider.Control>
</Slider.Root>
</div>
</div>
</section>
<div className="two-column">
<section data-component="surface">
<header data-slot="surface-header">
<h2 data-slot="surface-title">Workspace</h2>
<p data-slot="surface-description">Tabs and Accordion</p>
</header>
<div data-slot="surface-content">
<Tabs.Root defaultValue="activity" data-ui="tabs">
<Tabs.List>
{[
["activity", "Activity"],
["members", "Members"],
["settings", "Settings"],
].map(([value, label]) => (
<Tabs.Tab key={value} value={value}>
{label}
</Tabs.Tab>
))}
<Tabs.Indicator />
</Tabs.List>
<Tabs.Panel value="activity">
12 deployments completed this week. Median duration: 42 seconds.
</Tabs.Panel>
<Tabs.Panel value="members">8 operators have access to this workspace.</Tabs.Panel>
<Tabs.Panel value="settings">Production deploys require a healthy smoke check.</Tabs.Panel>
</Tabs.Root>
<Accordion.Root defaultValue={["checks"]} data-ui="accordion">
{[
[
"checks",
"What runs before activation?",
"Package checks, a production build, and browser interaction tests.",
],
[
"rollback",
"When does rollback happen?",
"When health checks fail during the five-minute observation window.",
],
["access", "Who can deploy?", "Workspace owners and operators with release permissions."],
].map(([value, title, content]) => (
<Accordion.Item key={value} value={value}>
<Accordion.Header>
<Accordion.Trigger>
{title}
<span aria-hidden>+</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel>
<div>{content}</div>
</Accordion.Panel>
</Accordion.Item>
))}
</Accordion.Root>
</div>
</section>
<section data-component="surface">
<header data-slot="surface-header">
<h2 data-slot="surface-title">Overlays</h2>
<p data-slot="surface-description">Popover, Menu, Dialog, and Tooltip</p>
</header>
<div data-slot="surface-content" className="overlay-demo">
<Popover.Root>
<Popover.Trigger data-component="button" data-variant="neutral" data-size="normal">
View status
</Popover.Trigger>
<Popover.Portal>
<Popover.Positioner className="positioner" sideOffset={8}>
<Popover.Popup data-ui="popup" className="popover-popup">
<Popover.Title>All systems normal</Popover.Title>
<Popover.Description>API and workers are healthy in every region.</Popover.Description>
<Popover.Close aria-label="Close">×</Popover.Close>
</Popover.Popup>
</Popover.Positioner>
</Popover.Portal>
</Popover.Root>
<Menu.Root>
<Menu.Trigger data-component="button" data-variant="outline" data-size="normal">
Actions
</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner className="positioner" sideOffset={6}>
<Menu.Popup data-ui="popup">
<Menu.Item data-ui="option">
View logs <span>L</span>
</Menu.Item>
<Menu.Item data-ui="option">Duplicate release</Menu.Item>
<Menu.Separator data-component="divider" />
<Menu.Item data-ui="option" className="danger-item">
Cancel deploy
</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
<Dialog.Root>
<Dialog.Trigger data-component="button" data-variant="danger" data-size="normal">
Rollback
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Backdrop data-ui="backdrop" />
<Dialog.Popup data-ui="dialog">
<Dialog.Title>Rollback production?</Dialog.Title>
<Dialog.Description>This restores v1.18.3 and stops the current rollout.</Dialog.Description>
<div className="dialog-actions">
<Dialog.Close data-component="button" data-variant="ghost" data-size="normal">
Cancel
</Dialog.Close>
<Dialog.Close data-component="button" data-variant="danger" data-size="normal">
Rollback
</Dialog.Close>
</div>
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
</div>
</section>
</div>
</div>
</main>
)
}
createRoot(document.getElementById("root")!).render(<App />)

View file

@ -0,0 +1,386 @@
:root {
--oc-font-family-sans: Inter, ui-sans-serif, system-ui, sans-serif;
}
body {
position: relative;
}
#root {
isolation: isolate;
}
button,
input {
font: inherit;
}
.section-heading,
.field-inline {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.form-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px;
}
.select-trigger {
display: flex;
align-items: center;
justify-content: space-between;
text-align: left;
}
.positioner {
z-index: 20;
outline: none;
}
[data-ui="popup"],
[data-ui="tooltip"] {
box-sizing: border-box;
min-width: var(--anchor-width, 180px);
padding: 6px;
border: 1px solid var(--oc-border-strong);
border-radius: 8px;
background: var(--oc-bg-layer-01);
color: var(--oc-text-base);
box-shadow: var(--oc-elevation-raised);
font: 13px/1.4 var(--oc-font-family-sans);
outline: none;
transform-origin: var(--transform-origin);
transition:
opacity 100ms ease-out,
transform 100ms ease-out;
}
[data-ui="popup"][data-starting-style],
[data-ui="popup"][data-ending-style],
[data-ui="tooltip"][data-starting-style],
[data-ui="tooltip"][data-ending-style] {
opacity: 0;
transform: scale(0.98);
}
[data-ui="tooltip"] {
min-width: 0;
padding: 5px 8px;
border-radius: 6px;
color: var(--oc-text-muted);
font-size: 11px;
}
[data-ui="option"] {
display: grid;
grid-template-columns: 16px 1fr auto;
align-items: center;
gap: 8px;
min-height: 32px;
padding: 0 8px;
border-radius: 4px;
outline: none;
cursor: default;
user-select: none;
}
[data-ui="option"][data-highlighted] {
background: var(--oc-overlay-hover);
}
.danger-item {
color: var(--oc-state-fg-danger);
}
[data-ui="check-row"] {
display: flex;
align-items: flex-start;
gap: 10px;
color: var(--oc-text-base);
}
[data-ui="check-row"] span {
display: grid;
gap: 3px;
}
[data-ui="check-row"] strong {
font-weight: 530;
}
[data-ui="check-row"] small {
color: var(--oc-text-muted);
}
[data-ui="checkbox"] {
display: grid;
width: 18px;
height: 18px;
flex: none;
place-items: center;
border: 1px solid var(--oc-border-strong);
border-radius: 4px;
background: var(--oc-bg-base);
color: transparent;
outline: none;
}
[data-ui="checkbox"][data-checked] {
border-color: var(--oc-bg-accent);
background: var(--oc-bg-accent);
color: var(--oc-text-contrast);
}
[data-ui="checkbox"]:focus-visible,
[data-ui="switch"]:focus-visible,
.slider-thumb:focus-within {
outline: 2px solid var(--oc-border-focus);
outline-offset: 2px;
}
[data-ui="switch"] {
width: 30px;
height: 18px;
flex: none;
padding: 2px;
border: 0;
border-radius: 999px;
background: var(--oc-bg-layer-03);
outline: none;
}
[data-ui="switch"] > span {
display: block;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--oc-text-contrast);
box-shadow: 0 1px 2px var(--oc-alpha-dark-30);
transition: transform 120ms ease-out;
}
[data-ui="switch"][data-checked] {
background: var(--oc-bg-accent);
}
[data-ui="switch"][data-checked] > span {
transform: translateX(12px);
}
.slider-field {
grid-column: 1 / -1;
}
.slider-control {
display: flex;
align-items: center;
height: 18px;
}
.slider-track {
position: relative;
width: 100%;
height: 4px;
border-radius: 999px;
background: var(--oc-bg-layer-03);
}
.slider-indicator {
position: absolute;
height: 100%;
border-radius: inherit;
background: var(--oc-bg-accent);
}
.slider-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--oc-text-contrast);
box-shadow: var(--oc-elevation-button-neutral);
outline: none;
}
.two-column {
display: grid;
grid-template-columns: minmax(0, 1.3fr) minmax(320px, 0.7fr);
gap: 16px;
}
[data-ui="tabs"] > [role="tablist"] {
position: relative;
display: flex;
gap: 4px;
border-bottom: 1px solid var(--oc-border-muted);
}
[data-ui="tabs"] [role="tab"] {
height: 32px;
padding: 0 10px;
border: 0;
background: transparent;
color: var(--oc-text-muted);
outline: none;
}
[data-ui="tabs"] [role="tab"]:hover,
[data-ui="tabs"] [role="tab"][data-active] {
color: var(--oc-text-base);
}
[data-ui="tabs"] [role="tab"]:focus-visible {
outline: 2px solid var(--oc-border-focus);
outline-offset: -2px;
}
[data-ui="tabs"] [role="tablist"] > span {
position: absolute;
bottom: -1px;
left: 0;
width: var(--active-tab-width);
height: 2px;
translate: var(--active-tab-left);
background: var(--oc-bg-accent);
transition:
translate 140ms ease-out,
width 140ms ease-out;
}
[data-ui="tabs"] [role="tabpanel"] {
min-height: 72px;
padding: 16px 10px;
color: var(--oc-text-muted);
outline: none;
}
[data-ui="accordion"] {
border-top: 1px solid var(--oc-border-muted);
}
[data-ui="accordion"] h3 {
margin: 0;
border-bottom: 1px solid var(--oc-border-muted);
}
[data-ui="accordion"] h3 button {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
padding: 12px 10px;
border: 0;
background: transparent;
color: var(--oc-text-base);
text-align: left;
outline: none;
}
[data-ui="accordion"] h3 button:hover {
background: var(--oc-overlay-hover);
}
[data-ui="accordion"] h3 button[data-panel-open] span {
transform: rotate(45deg);
}
[data-ui="accordion"] [role="region"] {
height: var(--accordion-panel-height);
overflow: hidden;
color: var(--oc-text-muted);
transition: height 150ms ease-out;
}
[data-ui="accordion"] [role="region"][data-starting-style],
[data-ui="accordion"] [role="region"][data-ending-style] {
height: 0;
}
[data-ui="accordion"] [role="region"] > div {
padding: 12px 10px 16px;
}
.overlay-demo {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.popover-popup {
position: relative;
max-width: 260px;
padding: 16px;
}
.popover-popup [role="heading"] {
font-weight: 530;
}
.popover-popup p {
margin: 6px 20px 0 0;
color: var(--oc-text-muted);
}
.popover-popup > button {
position: absolute;
top: 8px;
right: 8px;
border: 0;
background: none;
color: var(--oc-text-muted);
}
[data-ui="backdrop"] {
position: fixed;
inset: 0;
z-index: 30;
background: var(--oc-alpha-dark-60);
backdrop-filter: blur(2px);
}
[data-ui="dialog"] {
position: fixed;
top: 50%;
left: 50%;
z-index: 31;
width: min(calc(100% - 32px), 420px);
padding: 24px;
border: 1px solid var(--oc-border-strong);
border-radius: 12px;
background: var(--oc-bg-layer-01);
color: var(--oc-text-base);
box-shadow: var(--oc-elevation-raised);
transform: translate(-50%, -50%);
outline: none;
}
[data-ui="dialog"] h2 {
margin: 0;
font-size: 18px;
font-weight: 530;
}
[data-ui="dialog"] p {
margin: 8px 0 24px;
color: var(--oc-text-muted);
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
@media (max-width: 760px) {
.form-grid,
.two-column {
grid-template-columns: 1fr;
}
.slider-field {
grid-column: auto;
}
}

View file

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "Bundler",
"jsx": "react-jsx",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"types": ["vite/client"]
},
"include": ["src"]
}