UI Kit
A standalone, framework-agnostic toolkit built with CSS custom properties and vanilla JS. No build tools required.
The kit ships as flat files you copy into your project. No npm, no build step.
<link rel="stylesheet" href="ui-kit/ui-kit.css">
<link rel="stylesheet" href="ui-kit/ui-kit-core.css"> <link rel="stylesheet" href="ui-kit/components/buttons.css"> <link rel="stylesheet" href="ui-kit/components/cards.css"> <!-- only import what you need -->
Minimal boilerplate to get a page running with the full kit, theme toggle, and FOWT prevention.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="ui-kit/theme-bootstrap.js"></script> <link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:wght@400;700&display=swap" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet"> <link rel="stylesheet" href="ui-kit/ui-kit.css"> </head> <body> <!-- your content --> <button id="theme-toggle" class="ui-theme-toggle"></button> <script src="ui-kit/icons.js"></script> <script src="ui-kit/ui-kit.js"></script> <script> UIKit.initThemeToggle(document.getElementById("theme-toggle")); </script> </body> </html>
All colours come from CSS custom properties in tokens.css.
Dark is the default; add data-theme="light" to <html> for light mode.
Use the theme toggle FAB (bottom-right) to switch live.
/* Use tokens as CSS custom properties */ .my-panel { background: var(--surface); border: 1px solid var(--border); color: var(--text); } .my-panel:focus { border-color: var(--border-focus); box-shadow: 0 0 0 3px var(--focus-ring); }
Base .btn with variant modifiers. All share consistent sizing, font, and disabled states.
<button class="btn btn-primary">Primary</button> <button class="btn btn-secondary">Secondary</button> <button class="btn btn-danger">Danger</button> <button class="btn btn-icon">...</button>
<button class="btn btn-primary" disabled>Disabled</button> <button class="btn btn-primary btn-loading" disabled>Training...</button>
<button class="btn btn-primary"> UIKit.ICONS.play + " Train" </button>
Surface-coloured panels with border. Use .card-header for a title + action row. Stack cards with automatic collapsed borders.
Model Configuration
Configure training parameters and hyperparameters for the selected model.
<div class="card"> <div class="card-header"> <h2>Model Configuration</h2> <button class="btn btn-secondary">Reset</button> </div> <p>Content here...</p> </div>
Input
Output
Logs
Three form patterns: .form-row (label+input grid), .control-row (select+buttons), and .feature-row (inline labelled inputs).
Configuration
<div class="card-form"> <div class="form-row"> <label>Model</label> <select>...</select> </div> <div class="form-row"> <label>Epochs</label> <input type="number" value="100"> </div> </div>
<div class="control-row"> <select>...</select> <button class="btn btn-primary">Load</button> <button class="btn btn-icon">...</button> </div>
<div class="feature-row"> <div class="feature-label"> Sepal Length <input class="feature-input" type="number" value="5.1"> </div> <!-- more inputs... --> <button class="btn btn-primary predict-row-btn">Predict</button> </div>
Two variants: .ui-table (simple) and .ui-table-sticky (sticky headers for scrollable data). Wrap in .ui-table-wrap for bordered container.
| Model | Accuracy | F1 Score | Status |
|---|---|---|---|
| Random Forest | 96.7% | 0.965 | Ready |
| SVM (RBF) | 94.2% | 0.940 | Ready |
| Logistic Regression | 78.3% | 0.771 | Needs tuning |
| Decision Tree | 65.1% | 0.632 | Overfitting |
<div class="ui-table-wrap"> <table class="ui-table"> <thead><tr> <th>Model</th> <th>Accuracy</th> </tr></thead> <tbody><tr> <td>Random Forest</td> <td><span class="acc-high">96.7%</span></td> </tr></tbody> </table> </div>
| Model | Accuracy | Status |
|---|---|---|
| No models trained yet | ||
Scrollable row-based lists with name, tag badge, and action buttons.
Saved Models
<div class="ui-list"> <div class="ui-list-row"> <span class="ui-list-name">rf_iris_v3.pkl</span> <span class="ui-list-tag">96.7%</span> <button class="btn btn-icon">...</button> <button class="btn btn-danger">...</button> </div> </div>
Minimal track + fill bar. Set width via inline style or JS. Add .hidden to .progress-area when inactive.
<div class="progress-area"> <div class="progress-bar-wrap"> <div class="progress-bar" style="width:65%" role="progressbar" aria-valuenow="65"></div> </div> <div class="status-text">Training epoch 65 / 100</div> </div> <!-- Update via JS: --> bar.style.width = pct + "%";
Monospace scrollable log area with colour-coded levels. Use UIKit.createLogger() to append entries via JS.
<div class="log-terminal" role="log" aria-live="polite" id="log"></div> <script> var log = UIKit.createLogger(document.getElementById("log")); log("Dataset loaded", "ok"); // green log("Low importance", "warn"); // amber log("Validation error", "err"); // red log("Processing..."); // default </script>
Absolutely-positioned menus toggled via JS. Supports .active state on items and click-outside-to-close.
<div style="position:relative"> <button class="btn btn-secondary" id="trigger">Select dataset</button> <div class="ui-dropdown hidden" id="menu"> <button class="ui-dropdown-item active">iris.csv</button> <button class="ui-dropdown-item">wine.csv</button> </div> </div> <script> UIKit.initDropdown( document.getElementById("trigger"), document.getElementById("menu") ); </script>
Collapsible side panels with a vertical handle + chevron. Click the handle to toggle.
<div class="drawer drawer-left" id="drawer"> <div class="side-panel side-panel-left"> <div class="side-panel-header"> <div class="side-panel-title">Datasets</div> </div> <!-- panel content --> </div> <button class="side-handle side-handle-left" id="handle"> UIKit.ICONS.chevron <span>Datasets</span> </button> </div> <script> UIKit.initDrawer( document.getElementById("drawer"), document.getElementById("handle") ); </script>
Overlay + centred dialog. Use .hidden on the overlay to toggle visibility. Combine with UIKit.onEscape() for keyboard dismissal.
<div class="modal-overlay" id="modal"> <div class="modal"> <div class="modal-header"> <span class="modal-title">Confirm Delete</span> <button class="btn btn-icon">...</button> </div> <p>Are you sure?</p> <div class="modal-footer"> <button class="btn btn-secondary">Cancel</button> <button class="btn btn-danger">Delete</button> </div> </div> </div> <script> // Toggle with .hidden class document.getElementById("modal").classList.toggle("hidden"); // Close on Escape UIKit.onEscape(function() { ... }); </script>
Black canvas with border, crosshair cursor, and an overlay clear button. Designed for digit/gesture drawing inputs.
<div class="canvas-area"> <div class="canvas-wrap"> <canvas class="ui-canvas" width="200" height="120" id="canvas"></canvas> <button class="ui-canvas-clear">Clear</button> </div> <div class="canvas-hint">Draw a digit</div> </div>
40+ icons via Font Awesome 6 Free. Available as UIKit.ICONS.name strings or via UIKit.icon("name"). Add .icon-spin for animation.
// Insert an icon via innerHTML el.innerHTML = UIKit.ICONS.play; // Or use the helper (same result) el.innerHTML = UIKit.icon("play"); // Combine with text btn.innerHTML = UIKit.ICONS.play + " Train Model"; // Spinning icon (e.g. loading state) el.innerHTML = UIKit.ICONS.spinner; // has .icon-spin built-in
Horizontal tab bar. Add .active to the selected tab.
<div class="ui-tabs"> <button class="ui-tab active">Overview</button> <button class="ui-tab">Metrics</button> <button class="ui-tab">Settings</button> </div>
Inline label badges for tags, counts, and status indicators.
<span class="ui-badge">Default</span> <span class="ui-badge ui-badge-accent">Accent</span> <span class="ui-badge ui-badge-success">Success</span> <span class="ui-badge ui-badge-warn">Warning</span> <span class="ui-badge ui-badge-danger">Danger</span>
Dot + label for showing connection, health, or process state.
<span class="ui-status ui-status-ok"> <span class="ui-status-dot"></span>Connected </span>
Auto-dismissing notification toasts. Use UIKit.toast(msg, opts) from JS.
UIKit.toast("Saved!", { type: "success" }); UIKit.toast("Warning", { type: "warn", duration: 5000 }); UIKit.toast("Error!", { type: "error" });
CSS-only toggle switch using a hidden checkbox + label.
<label class="ui-toggle"> <input type="checkbox" checked> <span class="ui-toggle-track"> <span class="ui-toggle-knob"></span> </span> </label>
Native <details>/<summary> with styled chevron. No JS required.
What frameworks does the kit support?
It's framework-agnostic — works with vanilla HTML, React, Vue, Svelte, or any templating system.
Do I need a build step?
No. Copy the files into your project and link the CSS. That's it.
Is it accessible?
Yes — Lighthouse 100 accessibility score with WCAG 2.1 AA contrast compliance.
<details class="ui-collapsible"> <summary>Question here</summary> <p>Answer here</p> </details>
Placeholder for empty lists, tables, or search results.
<div class="ui-empty"> <div class="ui-empty-title">No results found</div> <div class="ui-empty-desc">Try adjusting your search.</div> </div>
Responsive two-column card grid with border-collapse and tag pills.
<div class="ui-grid"> <div class="ui-grid-card"> <div class="ui-grid-title">Title</div> <div class="ui-grid-desc">Description</div> <div class="ui-grid-tags"> <span class="ui-grid-tag">Tag</span> </div> </div> </div>
Pure CSS tooltips via the data-tooltip attribute. No JS required.
<button data-tooltip="Save your work">Hover me</button>
Scroll-triggered reveal. Add .ui-fade-in and call UIKit.initFadeIn(). Respects prefers-reduced-motion.
<div class="ui-fade-in">Fades in on scroll</div> <script> UIKit.initFadeIn(); // observes all .ui-fade-in elements </script>
Styled drag handles for split-pane layouts. Pair with UIKit.initResize().
<div class="ui-resize-handle ui-resize-v"></div> <!-- vertical --> <div class="ui-resize-handle ui-resize-h"></div> <!-- horizontal -->
Accessibility skip-to-content link, hidden until focused via Tab key.
<a href="#main" class="ui-skip-link">Skip to content</a>
Compact button variants for toolbars and dense UIs.
<button class="btn btn-primary">Default</button> <button class="btn btn-primary btn-sm">Small</button> <button class="btn btn-primary btn-xs">Extra Small</button>
Circular or square profile images and initial placeholders.
<div class="ui-avatar ui-avatar-round">AP</div> <div class="ui-avatar ui-avatar-lg"> <img src="photo.jpg" alt="User"> </div>
Chronological entry list for experience sections, changelogs, and history displays.
- Led migration to microservices architecture
- Built real-time data pipeline
<div class="ui-timeline"> <div class="ui-timeline-entry"> <div class="ui-timeline-header"> <span class="ui-timeline-title">Role</span> <span class="ui-timeline-date">2023</span> </div> </div> </div>
Border-based loading spinner. Standalone or inline with a text label.
<div class="ui-spinner"></div> <span class="ui-spinner-inline"> <span class="ui-spinner ui-spinner-sm"></span> Loading… </span>
Segmented control for mode switching. Buttons share collapsed borders.
<div class="ui-btn-group"> <button class="btn btn-secondary active">Grid</button> <button class="btn btn-secondary">List</button> </div>
Status messages and notification banners.
<div class="ui-alert ui-alert-success">Saved!</div>
Keyboard shortcut and key-press display.
<span class="ui-kbd">Ctrl</span> + <span class="ui-kbd">S</span>
Inline code and code blocks with monospace font and terminal-style background.
Use npm install to add dependencies.
const UIKit = window.UIKit;
UIKit.toast("Hello!", { type: "success" });
<span class="ui-code">inline code</span> <pre class="ui-code-block">code block</pre>
Horizontal and vertical separators, with optional centered label.
<hr class="ui-divider"> <div class="ui-divider-label">Section</div>
Form section groups with optional legend title.
<fieldset class="ui-fieldset"> <legend>Settings</legend> <!-- form content --> </fieldset>
Horizontal action bar with groups, separators, spacers, and labels.
<div class="ui-toolbar ui-toolbar-gap"> <div class="ui-toolbar-group">...</div> <div class="ui-toolbar-separator"></div> <span class="ui-toolbar-spacer"></span> <span class="ui-toolbar-label">Draft</span> </div>
Numbered step indicator for wizards and multi-step flows.
<div class="ui-stepper"> <div class="ui-step completed">Upload</div> <div class="ui-step-connector completed"></div> <div class="ui-step active">Configure</div> <div class="ui-step-connector"></div> <div class="ui-step">Deploy</div> </div>
Key-value metric display for dashboards and summaries. Border-collapse row layout.
<div class="ui-stat-row"> <div class="ui-stat"> <span class="ui-stat-label">Users</span> <span class="ui-stat-value">1,284</span> <span class="ui-stat-change ui-stat-change-up">+12%</span> </div> </div>
Visual grip indicator for sortable and draggable items. Includes ghost/chosen states for SortableJS.
<div class="ui-drag-handle"></div> <!-- SortableJS classes: .ui-sortable-ghost, .ui-sortable-chosen -->
Fixed-ratio containers for video, images, and embeds.
<div class="ui-ratio ui-ratio-16x9"> <video>...</video> </div> <!-- Also: ui-ratio-4x3, ui-ratio-1x1, ui-ratio-21x9 -->
Navigation path indicator with separator.
<nav class="ui-breadcrumb"> <a>Home</a><span class="ui-breadcrumb-sep"></span> <span class="ui-breadcrumb-current">Page</span> </nav>
Page navigation with collapsed borders and active state.
<nav class="ui-pagination"> <a class="ui-page">«</a> <a class="ui-page active">2</a> <a class="ui-page">3</a> </nav>
Custom scrollbar styling. Add ui-scrollbar to any scrollable container.
<div class="ui-scrollbar" style="overflow:auto; height:200px;"> <!-- scrollable content --> </div> <!-- Also: ui-scrollbar-thin for 4px width -->
Loading placeholder animation. Respects prefers-reduced-motion.
<div class="ui-skeleton ui-skeleton-heading"></div> <div class="ui-skeleton ui-skeleton-text"></div> <div class="ui-skeleton ui-skeleton-text"></div> <!-- Also: ui-skeleton-avatar, ui-skeleton-block -->
Message display container for chat and conversation UIs.
<div class="ui-chat"> <div class="ui-chat-messages"> <div class="ui-chat-msg">Hello</div> <div class="ui-chat-msg ui-chat-msg-self">Hi!</div> </div> <div class="ui-chat-input"> <input placeholder="Type..."> <button>Send</button> </div> </div>
Corner-positioned overlay for video previews and mini views.
<div style="position:relative;"> <div class="ui-pip ui-pip-br ui-pip-sm"> <video>...</video> </div> </div> <!-- Positions: ui-pip-tr, ui-pip-tl, ui-pip-br, ui-pip-bl --> <!-- Sizes: ui-pip-sm, ui-pip-md, ui-pip-lg -->
Full-viewport app shell with topbar, sidebar, main area, and optional split columns with drag-to-resize.
<div class="app"> <div class="app-topbar">...</div> <div class="app-body"> <div class="app-sidebar">...</div> <div class="app-main">...</div> </div> </div>
<div class="split-layout" id="split"> <div class="left-col" id="left"> <!-- cards, forms --> </div> <div class="resize-handle resize-h" id="handle"></div> <div class="right-col"> <!-- table, chart --> </div> </div> <script> UIKit.initResize( document.getElementById("handle"), document.getElementById("left"), document.getElementById("split"), { min: 200, "default": 300, key: "split-w" } ); </script>
.hidden — display: none !important.sr-only — Visually hidden, accessible to screen readers.resize-dragging — Applied to <body> during drag-resize (disables text selection)
conf-high: Ready conf-low: Needs tuning
pred-label: Setosa
<span class="acc-high">96.7%</span> <!-- green --> <span class="acc-med">78.3%</span> <!-- amber --> <span class="acc-low">65.1%</span> <!-- red --> <span class="conf-high">Ready</span> <!-- green --> <span class="conf-low">...</span> <!-- muted --> <span class="pred-label">Setosa</span> <!-- bold accent -->
Load icons.js first, then ui-kit.js. Everything lives on window.UIKit.
Nothing auto-initialises — call only what you need.
| Method | Purpose | Returns |
|---|---|---|
| initThemeToggle(el, opts?) | Dark/light toggle with localStorage | { setTheme(t), destroy() } |
| initDrawer(drawerEl, handleEl) | Collapsible side drawer | { open(), close(), toggle(), destroy() } |
| initDropdown(triggerEl, menuEl) | Toggle menu + click-outside | { open(), close(), destroy() } |
| onEscape(callback) | Register Escape-key handler | unsubscribe() |
| initResize(handle, target, container, opts?) | Drag-to-resize split layout | — |
| createLogger(terminalEl, max?) | Log terminal appender | addLog(msg, level?) |
| toast(msg, opts?) | Show auto-dismissing toast | HTMLElement |
| dismissToast(el) | Manually dismiss a toast | — |
| initFadeIn(scope?, opts?) | Observe .ui-fade-in elements | — |
| ICONS | Object of Font Awesome icon markup | { play, pause, stop, ... } |
| icon(name, opts?) | Get icon HTML by name | string |
// Must be the FIRST <script> in <head>, before any CSS <script src="ui-kit/theme-bootstrap.js"></script> // It reads from localStorage and sets data-theme before first paint. // Also syncs across tabs via the "storage" event. // Exposes: window.__setTheme(t), window.__getTheme()