Up and running in a minute.
Clay is a CSS file and an optional ~15kb (gzipped) runtime. There's no build step, no framework lock-in and no dependencies. These docs cover installing it, picking a platform, theming it and taking your tokens native.
Installation
Use whichever fits your stack. All options ship the same files.
CDN (fastest)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/clay-ds@1/dist/clay.min.css"> <script src="https://cdn.jsdelivr.net/npm/clay-ds@1/dist/clay.min.js" defer></script>
<link rel="stylesheet" href="https://unpkg.com/clay-ds@1/dist/clay.min.css"> <!-- or straight from GitHub releases --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/litu173/clay-design-system@v1.0.0/dist/clay.min.css">
Package managers
npm install clay-ds # or pnpm add clay-ds yarn add clay-ds bun add clay-ds
import 'clay-ds/dist/clay.css';
import Clay from 'clay-ds';
Clay.init({ os: 'auto', mode: 'auto' });
Download
Grab clay.css, clay.js and tokens.json and self-host them.
Quick start
Here's a complete page. Copy it into an .html file and open it.
<!doctype html>
<html lang="en" data-clay-os="auto" data-clay-mode="auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/clay-ds@1/dist/clay.min.css">
<script src="https://cdn.jsdelivr.net/npm/clay-ds@1/dist/clay.min.js" data-clay-auto data-os="auto" data-mode="auto" defer></script>
</head>
<body class="clay">
<main class="clay-container clay-stack" style="padding-block:48px">
<h1 class="clay-h1">Hello, Clay</h1>
<div class="clay-card"><div class="clay-card-body">
<div class="clay-field">
<label for="email">Email</label>
<input id="email" class="clay-input" placeholder="you@example.com">
</div>
<label class="clay-choice"><input type="checkbox" role="switch" class="clay-switch" checked> Subscribe</label>
<button class="clay-btn" data-clay-toast="Welcome aboard!" data-clay-toast-type="success">Sign up</button>
</div></div>
</main>
</body>
</html>
Choosing a platform
Set data-clay-os to web, ios, android, macos, windows, linux or auto (detect from the visitor's device). Set data-clay-mode to light, dark or auto. Keep both attributes on the same element. You can nest them to show several platforms on one page.
<section data-clay-os="ios" data-clay-mode="dark" class="clay"> …iOS preview… </section> <section data-clay-os="windows" data-clay-mode="light" class="clay"> …Windows preview… </section>
Platform specs
The defaults each preset ships with, all drawn from the platform's official guidelines.
| Token | Web | iOS | Android | macOS | Windows | Linux |
|---|---|---|---|---|---|---|
| Guideline | Clay | HIG | Material 3 | HIG | Fluent 2 | Adwaita |
| Accent | #6d5efc | #007aff | #6750a4 | #007aff | #0f6cbd | #3584e4 |
| Typeface | Inter | SF Pro | Roboto | SF Pro | Segoe UI Variable | Cantarell |
| Body size | 16px | 17pt | 16sp | 13pt | 14px | 15px |
| Control height | 40 | 44 | 40 | 28 | 32 | 34 |
| Button radius | 12 | 12 | full | 6 | 4 | 6 |
| Card radius | 17 | 14 | 12 | 10 | 8 | 12 |
| Dialog radius | 24 | 14 | 28 | 12 | 8 | 14 |
| Switch | 44×26 | 51×31 | 52×32 | 38×22 | 40×20 | 48×26 |
| Input style | Outlined | Filled | Outlined | Bezel | Underline accent | Filled |
| Press feedback | Scale .97 | Scale .98 | Ripple | Darken | Darken | Darken |
data-clay-os="auto". The same build then looks like iOS on an iPhone, Material on a Pixel and Fluent on Windows.CSS variables
Every decision is a --clay-* custom property. Override them globally, per section or per component. Colour ramps, soft tints and hover states are derived automatically.
:root {
--clay-primary: #e11d48; /* brand colour, ramps derive from it */
--clay-on-primary: #ffffff; /* text on primary */
--clay-radius: 16px; /* base radius, sm/lg/xl derive from it */
--clay-font: "Sora", system-ui, sans-serif;
--clay-font-scale: 1.05; /* multiply the entire type ramp */
--clay-density: 0.9; /* compact controls and spacing */
--clay-shadow-strength: 1.4; /* deeper shadows */
}
/* Scoped: a marketing band with its own personality */
.promo { --clay-primary: #059669; --clay-btn-radius: 999px; }
| Group | Key tokens |
|---|---|
| Colour | --clay-primary --clay-on-primary --clay-secondary --clay-success|warning|danger|info --clay-bg --clay-surface[-2|-3] --clay-border[-strong] --clay-text[-2|-3] --clay-primary-50…900 |
| Shape | --clay-radius[-xs|-sm|-lg|-xl|-full] --clay-btn-radius --clay-input-radius --clay-card-radius --clay-dialog-radius |
| Type | --clay-font --clay-font-display --clay-font-mono --clay-font-scale --clay-fs-display…caption --clay-fw-* |
| Space | --clay-space --clay-density --clay-control-h-base |
| Depth | --clay-shadow-1…4 --clay-shadow-strength --clay-glass |
| Motion | --clay-dur --clay-dur-slow --clay-ease --clay-ease-spring --clay-press-scale --clay-ripple |
| Components | --clay-switch-w|h|thumb --clay-range-* --clay-seg-* --clay-tab-ind-* --clay-input-focus --clay-list-sep-inset … |
JavaScript API
The runtime is optional, and every component works as plain HTML and CSS. The runtime adds live theming, OS detection, toasts, dialog animation, menus, tabs, the calendar and token export.
// Boot: reads data-clay-* attributes and applies defaults
Clay.init({ os: 'auto', mode: 'auto', theme: 'ocean' });
// Theme live, null falls back to the platform preset
Clay.setTheme({ primary: '#7c3aed', radius: 18, font: 'Plus Jakarta Sans', density: 0.9 });
Clay.setOS('android'); // switch preset (resets colour/shape/type overrides)
Clay.setMode('dark');
Clay.onChange(theme => localStorage.setItem('theme', JSON.stringify(theme)));
// Feedback
Clay.toast({ title: 'Saved', message: 'All changes synced', type: 'success', action: { label: 'Undo', onClick: undo } });
Clay.dialog.open('#confirm');
// Tokens
const t = Clay.tokens(); // resolved values for the current theme
const swift = Clay.export('swiftui');
| Method | Description |
|---|---|
init(options) | Boot Clay on options.target (default <html>). Options: os, mode, theme, primary, secondary, radius, font, fontScale, density, space, shadow, motion. |
setTheme(patch) | Merge and apply theme options. Pass theme: 'rose' to load a brand theme. Returns the new theme. |
setOS(os, keep?) | Switch platform preset. Pass keep = true to keep colour, shape and type overrides. |
setMode(mode) | 'light' | 'dark' | 'auto'. Auto follows the system setting live. |
getTheme() / reset() | Read the current options, or return to the platform defaults. |
onChange(fn) | Subscribe to theme changes. Returns an unsubscribe function. |
detectOS() | Best guess of the visitor's platform. |
tokens(el?) | Resolved tokens (hex colours, px sizes) for any element. |
export(format) | css, scss, json, figma, tailwind, swiftui, compose, flutter, xaml, gtk |
toast(opts) | { title, message, type, action, duration, position: 'corner'|'top'|'bottom' } |
dialog.open / close | Animated open and close for any <dialog class="clay-dialog">. |
icon(name) | Inline SVG string for a built-in icon. |
contrast(hex) / contrastRatio(a, b) | Pick readable text colour and check WCAG ratios. |
upgrade(scope?) | Hydrate icons, sliders and calendars in content added later (e.g. after a SPA route change). |
Declarative attributes
| Attribute | Effect |
|---|---|
data-clay-open="#id" | Opens a dialog, sheet or drawer. |
data-clay-close | Closes the nearest dialog (its value becomes returnValue). |
data-clay-menu | Toggles the parent .clay-dropdown. |
data-clay-toast="Title" | Shows a toast. Add -message, -type and -action variants. |
data-clay-tooltip="Text" | CSS tooltip on hover or focus. |
data-clay-icon="name" | Put it on an <i> to render an icon. |
data-clay-calendar | Renders an interactive month view. |
Token export
This is your current theme, compiled live for every platform. Change anything in the Foundations panel on the components page and the code below updates.
Frameworks
Clay is plain classes, so every framework just works. Here are the idiomatic setups.
import 'clay-ds/dist/clay.css';
import Clay from 'clay-ds';
import { useEffect } from 'react';
export default function RootLayout({ children }) {
useEffect(() => { Clay.init({ os: 'auto', mode: 'auto' }); }, []);
return (
<html lang="en" data-clay-os="web" data-clay-mode="light">
<body className="clay">{children}</body>
</html>
);
}
// Anywhere:
export const Button = ({ variant, ...p }) =>
<button className={`clay-btn ${variant ? 'clay-btn--' + variant : ''}`} {...p} />;
import 'clay-ds/dist/clay.css';
import Clay from 'clay-ds';
createApp(App).mount('#app');
Clay.init({ os: 'auto' });
// <button class="clay-btn clay-btn--tonal" @click="save">Save</button>
<script>
import 'clay-ds/dist/clay.css';
import Clay from 'clay-ds';
import { onMount } from 'svelte';
onMount(() => Clay.init({ os: 'auto', mode: 'auto' }));
</script>
<slot />
"styles": ["node_modules/clay-ds/dist/clay.css", "src/styles.css"], "scripts": ["node_modules/clay-ds/dist/clay.js"]
// Use Clay tokens as Tailwind utilities: bg-primary, rounded-lg, text-foreground-muted…
module.exports = {
presets: [require('clay-ds/tailwind')],
content: ['./src/**/*.{html,js,jsx,ts,tsx,vue,svelte}'],
};
Native apps
Design in the browser and ship natively. Export your theme from the Foundations panel, drop the file into your project and reference the tokens.
| Platform | Export | Use it |
|---|---|---|
| iOS / iPadOS / macOS | ClayTheme.swift | Text("Hi").foregroundStyle(Clay.Colors.primary) |
| Android | ClayTheme.kt | Wrap your app in ClayTheme { … } (Material 3) |
| Flutter (all OS) | clay_theme.dart | MaterialApp(theme: clayTheme) |
| Windows | ClayTheme.xaml | Merge into App.xaml resources (WinUI 3 / WPF) |
| Linux | gtk.css | Overrides libadwaita named colours (GTK 4) |
| Figma | clay.tokens.json | Import with Tokens Studio or Variables (DTCG format) |
| React Native / Expo | clay-tokens.json | StyleSheet.create({ btn: { backgroundColor: t.color.primary } }) |
Accessibility
Clay targets WCAG 2.2 AA. Here's what's built in and what you're responsible for.
| Built in | Your part |
|---|---|
Native elements (button, dialog, details, inputs) with correct semantics | Give icon-only buttons an aria-label |
| Visible focus rings on every interactive element | Pair every input with a <label> |
| Keyboard support for tabs, menus, dialogs and OTP fields | Use aria-invalid and aria-describedby for errors |
| Auto contrast for text on primary, plus a contrast checker in the Foundations panel | Check custom brand colours reach 4.5:1 |
prefers-reduced-motion and prefers-color-scheme support | Keep touch targets ≥ 44px (density ≥ 1 on mobile) |
Browser support
Clay works in all evergreen browsers: Chrome, Edge and Opera 111+, Safari 16.2+ (iOS 16.2+) and Firefox 113+. It relies on color-mix(), <dialog>, :focus-visible and CSS custom properties. In Android WebView and WKWebView it follows the system engine.
Changelog
- 1.0.0 · October 2026First release: six platform presets, 180+ components, per-platform catalogs, live Foundations panel, 10 export formats, 80+ icons.
- Next · plannedvisionOS and watchOS presets, KDE Breeze, RTL showcase, web components wrapper, Figma library.