Build faster with
zero overhead.
Drop two files into any project and get a complete, production-ready component system. No npm, no bundler, no framework lock-in.
Installation
Start HereTwo tags. That's the whole install story.
<!DOCTYPE html>
<html>
<head>
<!-- Oak CSS — in <head> -->
<link rel="stylesheet" href="src/oak.css">
</head>
<body>
<button>Hello Oak</button>
<!-- Oak JS — before </body> -->
<script src="src/oak.js"></script>
</body>
</html><oak-tabs>, <oak-dropdown>, <oak-accordion>, <oak-sidebar>, and oak.toast(). Everything else is pure CSS.Forms
ComponentAll native controls styled automatically. Wrap in [data-field] for error states and helper text.
<div data-field>
<label for="email">Email</label>
<input id="email" type="email">
<span data-hint>Helper text.</span>
</div>
<!-- Error state -->
<div data-field="error">
<input aria-invalid="true">
<span class="field-error">Error msg.</span>
</div><label><input type="checkbox"> Accept</label>
<!-- Switch -->
<label>
<input type="checkbox" role="switch" checked>
Notifications
</label><div class="input-group">
<span class="input-addon">https://</span>
<input type="text">
</div>Cards
ComponentSurface containers with optional card-header, card-body, and card-footer slots.
Project Alpha
Q1 performance
Revenue up 24% from last month. 1,200 new users onboarded.
<div class="card">
<div class="card-header"><h3>Title</h3></div>
<div class="card-body">...</div>
<div class="card-footer">...</div>
</div>
<!-- Hover lift -->
<div class="card card-hover">...</div>Badges & Chips
ComponentStatus indicators and removable filter tags.
<span class="badge badge-success badge-dot">Active</span>
<span class="badge badge-danger">Error</span>
<span class="badge badge-outline">Outline</span><span class="chip chip-active">Active</span>
<span class="chip">
Label <button class="chip-remove">×</button>
</span>Alerts
ComponentInline feedback using role="alert" and data-variant.
<div role="alert" data-variant="success">
<span class="alert-icon">...svg...</span>
<div>
<div class="alert-title">Done!</div>
<div class="alert-desc">It worked.</div>
</div>
</div>
<!-- variants: info | success | warning | danger -->Tabs
Web Component<oak-tabs> handles ARIA, keyboard nav (←→), and panel visibility automatically.
Revenue up 24% vs. last month. Active users grew by 1,200 — the strongest month this quarter.
Deep-dive into funnel drop-offs, session duration, and feature engagement across cohorts.
Manage API keys, webhooks, team members, and billing preferences.
<oak-tabs>
<div role="tablist">
<button role="tab">Tab 1</button>
<button role="tab">Tab 2</button>
</div>
<div role="tabpanel">Content 1</div>
<div role="tabpanel">Content 2</div>
</oak-tabs>class="tabs-line" to tablistManage your profile, display name, and notification preferences.
View invoices, update payment methods, and manage subscription.
Change password, enable two-factor auth, view login history.
Connect third-party services and manage API keys.
<div role="tablist" class="tabs-line">...</div>Modals
ComponentNative <dialog> with CSS animations and backdrop blur. Wire triggers with commandfor — no JS needed.
<button commandfor="my-modal" command="show-modal">Open</button>
<dialog id="my-modal">
<div class="dialog-header"><h2>Title</h2></div>
<div class="dialog-body">...</div>
<div class="dialog-footer">
<button commandfor="my-modal" command="close" data-variant="outline">Cancel</button>
<button>Confirm</button>
</div>
</dialog>
<!-- sizes: dialog-sm | (default) | dialog-lg | dialog-full -->Dropdowns
Web Component<oak-dropdown> auto-positions the menu, flips upward near viewport edges, and handles full keyboard nav.
<oak-dropdown>
<button>Actions</button>
<div popover>
<div class="menu-label">Category</div>
<button role="menuitem">Edit</button>
<div role="separator"></div>
<button role="menuitem" data-variant="danger">Delete</button>
</div>
</oak-dropdown>Toasts
JS APIProgrammatic notifications via oak.toast(). Icons, 6 placements, pause-on-hover, and auto-dismiss included.
// variants: success | warning | danger | info
oak.toast('Saved!', 'Title', { variant: 'success' });
// placement + custom duration
oak.toast('Done!', undefined, {
variant: 'info',
placement: 'bottom-center', // top/bottom + left/center/right
duration: 6000 // 0 = no auto-dismiss
});
oak.toast.clear(); // dismiss allAccordion
Web ComponentWraps native <details>. Add exclusive to allow only one open at a time.
What is Oak UI?
How do I customize the theme?
:root. Change --primary to rebrand everything instantly.Is dark mode automatic?
light-dark() which follows the user's system preference with zero JavaScript.What browsers are supported?
<oak-accordion exclusive>
<details open>
<summary>Question</summary>
<div class="accordion-body">Answer.</div>
</details>
</oak-accordion>Tables
Component| Name | Role | Status | Joined | |
|---|---|---|---|---|
JD Jane Doe |
Designer | Active | Jan 2024 | |
AB Alex Brown |
Engineer | Away | Mar 2024 | |
SC Sam Chen |
Product | Offline | Jun 2024 |
<div class="table-wrap">
<table class="hoverable">...</table>
</div>
<!-- also: class="striped" -->Progress & Meter
Component<progress value="85" max="100"></progress>
<progress class="progress-success" value="99" max="100"></progress>
<progress class="progress-warning progress-lg" value="85" max="100"></progress>
<progress class="progress-danger progress-striped" value="6" max="100"></progress>Avatars
Component<div class="avatar">JD</div>
<div class="avatar avatar-lg avatar-success">✓</div>
<div class="avatar-group">
<div class="avatar">A</div>
<div class="avatar">+3</div>
</div>Misc
Component<button data-tooltip="Tooltip text">Hover</button>
<button data-tooltip="Below" data-tooltip-placement="bottom">Below</button><div class="skeleton" role="status" style="height:1rem;width:100%"></div>
<div class="spinner"></div>
<div class="spinner spinner-lg spinner-success"></div><nav aria-label="breadcrumb navigation">
<ol><li><a href="#">Home</a></li><li>Current</li></ol>
</nav>
<div class="divider">or</div>
<kbd>⌘</kbd> <kbd>K</kbd>Theming
CustomizationOverride CSS custom properties on :root to rebrand completely. No SASS, no config files.
:root {
/* Brand — cascades everywhere */
--primary: #7c3aed;
--primary-foreground: #ffffff;
--primary-muted: rgba(124, 58, 237, 0.1);
/* Typography */
--font-sans: 'Geist', system-ui, sans-serif;
/* Sharper corners */
--radius-md: 3px;
--radius-lg: 6px;
/* Force dark mode always */
color-scheme: dark;
}| Token | Purpose | Light default |
|---|---|---|
| --primary | Brand — buttons, links, focus rings | #2a5af5 |
| --background | Page background | #ffffff |
| --foreground | Body text | #111118 |
| --card | Card / panel surface | #ffffff |
| --border | Borders and dividers | #e2e2ec |
| --muted-foreground | Secondary text | #6b6b80 |
| --success / --warning / --danger | Semantic status colors | Green / Amber / Red |
| --font-sans | Body typeface | system-ui, sans-serif |
| --radius-md | Default border radius | 0.5rem |