Drop the full Chaoz-MADS design system into any web app in under 30 seconds. 217 components · 12 themes · 30+ motion primitives · 10 layout primitives. Single source of truth — every framework adapter (React/Vue/Svelte/vanilla) consumes the same cx-* classes, so output is byte-identical visually.
CDN: design.chaozcode.com/latest/ · Manifest: /manifest.json · Integrity: /integrity.json · npm: @chaozcode/mads
Cx* components — all visual styling lives in cx-* classes inside chaozcode.css. (You CAN use Tailwind around them in your own JSX.)prefers-reduced-motion. Motion is on by default; consumers opt in via setReducedMotion(true) → sets [data-reduce-motion] on <html>. iOS Low Power Mode firing this OS query was killing animation for users who never asked for it.width / height / top / left / margin / padding). Use transform: translate3d() / scale() / scaleX(). Compositor-only → 60fps even on mid-tier mobile.| Path | Use it when | Build step | Tree-shakeable | SSR |
|---|---|---|---|---|
| 1 CSS-only | Plain HTML, Astro, Hugo, Jekyll, MPA | None | n/a | ✅ |
| 2 ESM importmap | React in modern browsers, no bundler | None | — | — |
| 3 UMD script | Legacy browsers, CMS embeds, WordPress | None | — | — |
| 4 npm | Next.js / Vite / Remix / Astro+React | Yes | ✅ | ✅ |
Drop the stylesheet in. Every cx-* class works in any HTML / Vue / Svelte / Angular / Astro template — no JS required.
<!DOCTYPE html>
<html data-cx data-theme="midnight">
<head>
<link rel="stylesheet" href="https://design.chaozcode.com/latest/mads.css">
</head>
<body>
<header class="cx-row" data-justify="between" style="padding:1rem">
<strong class="cx-text-gradient cx-text-gradient-purple">NimbusOps</strong>
<button class="cx-btn cx-btn-primary cx-btn-sm">Get started</button>
</header>
<main class="cx-stack" data-gap="md" style="padding:1rem">
<div class="cx-card cx-card-frosted" style="padding:1rem">
<h2 class="cx-fluid-h2">Search</h2>
<input class="cx-input" placeholder="Try cx-* classes…" />
</div>
<div class="cx-grid" data-min="220">
<div class="cx-stat-card"><div class="cx-stat-card-label">Revenue</div><div class="cx-stat-card-value cx-text-gradient cx-text-gradient-purple">$48,290</div></div>
<div class="cx-stat-card"><div class="cx-stat-card-label">Active</div><div class="cx-stat-card-value cx-text-gradient cx-text-gradient-cyan">2,847</div></div>
<div class="cx-stat-card"><div class="cx-stat-card-label">API</div><div class="cx-stat-card-value">14.2M</div></div>
</div>
</main>
</body>
</html>
For theme cycling, sticky-toolbar etc., add bootstrap.js:
<script src="https://design.chaozcode.com/latest/bootstrap.js"></script>
<!-- exposes window.MADS.setTheme('cyber'), .setDirection('rtl'), .setDensity('compact'), .setReducedMotion(true) -->
Modern browsers (Chrome 89+, Safari 16.4+, Firefox 108+) support <script type="importmap">. Map react and @chaozcode/mads to CDN URLs and import like a normal npm package.
<!DOCTYPE html>
<html data-cx>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<link rel="stylesheet" href="https://design.chaozcode.com/latest/mads.css">
<script src="https://design.chaozcode.com/latest/bootstrap.js"></script>
</head>
<body>
<div id="root">Loading…</div>
<script type="module">
import { createRoot } from 'react-dom/client'
import React, { createElement as h } from 'react'
import {
CxButton, CxCard, CxCardHeader, CxCardContent,
CxInput, CxStack, CxRow, CxGrid, CxSidebar,
useTheme, useReducedMotion, setTheme,
} from '@chaozcode/mads'
function App() {
const [theme] = useTheme()
return h(CxStack, { gap: 'md', style: { padding: '1.25rem' } },
h(CxCard, { variant: 'frosted' },
h(CxCardHeader, null, h('h2', null, 'Hello from Chaoz-MADS')),
h(CxCardContent, null,
h(CxRow, { min: 200 },
h(CxButton, { variant: 'primary', onClick: () => setTheme('cyber') }, 'Cyber'),
h(CxButton, { variant: 'ghost', onClick: () => setTheme('midnight') }, 'Midnight'),
),
h(CxInput, { placeholder: 'Current theme: ' + theme }),
),
),
)
}
createRoot(document.getElementById('root')).render(h(App))
</script>
</body>
</html>
The bootstrap.js tag above auto-injects this importmap:
{
"imports": {
"react": "https://esm.sh/react@18",
"react-dom": "https://esm.sh/react-dom@18?external=react,react-dom",
"react-dom/client": "https://esm.sh/react-dom@18/client?external=react,react-dom",
"react/jsx-runtime": "https://esm.sh/react@18/jsx-runtime",
"framer-motion": "https://esm.sh/framer-motion@12?external=react,react-dom",
"lucide-react": "https://esm.sh/lucide-react@0.469?external=react,react-dom",
"@chaozcode/mads": "https://design.chaozcode.com/latest/design-system.mjs",
"@chaozcode/mads/components": "https://design.chaozcode.com/latest/components.mjs",
"@chaozcode/mads/patterns": "https://design.chaozcode.com/latest/patterns.mjs"
}
}
?external=react,react-dom suffix on framer-motion and lucide-react is mandatory. Without it, esm.sh bundles its own React copy → multiple React instances → useContext null.Legacy browsers, WordPress, embeds. Loads React + framer-motion globals, then exposes window.MADS.
<link rel="stylesheet" href="https://design.chaozcode.com/latest/mads.css">
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://unpkg.com/framer-motion@12/dist/framer-motion.umd.js"></script>
<script src="https://design.chaozcode.com/latest/mads.umd.js"></script>
<div id="root"></div>
<script>
const { CxButton, CxCard, CxRow, useTheme } = window.MADS;
const e = React.createElement;
ReactDOM.createRoot(document.getElementById('root')).render(
e(CxCard, { variant: 'frosted' },
e(CxRow, null,
e(CxButton, { variant: 'primary' }, 'Hello'),
e(CxButton, { variant: 'ghost' }, 'World'),
),
),
);
</script>
npm install @chaozcode/mads react react-dom framer-motion lucide-react
# or
pnpm add @chaozcode/mads react react-dom framer-motion lucide-react
// my-app.tsx
import '@chaozcode/mads/styles' // mads.css
import {
// Layout primitives
CxContainer, CxStack, CxRow, CxGrid, CxSidebar,
CxCluster, CxCenter, CxCover, CxFrame, CxSplit,
// Primitives
CxButton, CxCard, CxCardHeader, CxCardContent, CxCardFooter,
CxInput, CxTextarea, CxInputGroup,
// Theme API
THEMES, setTheme, useTheme, useReducedMotion,
useDirection, useDensity,
// A11y helpers
getContrastRatio, contrastVerdict, validateThemeContrast,
// Tokens (CSS-var fallbacks, perfect for theming)
MADS_TOKENS, VARIANTS, SIZES, BREAKPOINTS,
// Widgets
SettingsPanel, ThemeCustomizer,
} from '@chaozcode/mads'
import {
Kanban, Timeline, ChatBubble, Accordion,
CommandPalette, Sparkline, ProgressBar, DataGrid,
} from '@chaozcode/mads/components' // all 217 components
import {
CxHero, CxLoginCard, CxPricingTable,
CxAppShell, CxDashboardLayout,
} from '@chaozcode/mads/patterns' // production layouts
// app/layout.tsx
import '@chaozcode/mads/styles'
import type { ReactNode } from 'react'
export default function Layout({ children }: { children: ReactNode }) {
return (
<html lang="en" data-cx data-theme="midnight">
<body>{children}</body>
</html>
)
}
// app/page.tsx
'use client'
import { CxCard, CxButton, useTheme } from '@chaozcode/mads'
export default function Home() {
const [theme, setTheme] = useTheme()
return <CxCard variant="frosted">Theme: {theme}</CxCard>
}
Server Components: import non-hook primitives directly. Mark with 'use client' only where hooks (useTheme, useReducedMotion) appear.
// main.tsx
import '@chaozcode/mads/styles'
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(<App/>)
// vite.config.ts — optional, only if you also use Tailwind v4
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwind from '@tailwindcss/vite'
export default defineConfig({ plugins: [react(), tailwind()] })
---
// src/pages/index.astro
import '@chaozcode/mads/styles'
---
<html data-cx data-theme="midnight">
<body>
<div class="cx-card cx-card-frosted" style="padding:1rem">
<h1 class="cx-fluid-h1">Astro + MADS</h1>
<!-- For React components, use a client island: -->
<CxButton client:visible variant="primary">Click</CxButton>
</div>
</body>
</html>
<script setup>
import '@chaozcode/mads/styles'
const theme = ref('midnight')
</script>
<template>
<html :data-theme="theme">
<button class="cx-btn cx-btn-primary">Click</button>
<div class="cx-card cx-card-frosted">...</div>
</html>
</template>
Theme API works imperatively: document.documentElement.setAttribute('data-theme', 'cyber') or use window.MADS.setTheme().
<!-- App.svelte -->
<script>
import '@chaozcode/mads/styles'
</script>
<svelte:head><html data-cx data-theme="midnight" /></svelte:head>
<div class="cx-card cx-card-frosted">
<button class="cx-btn cx-btn-primary">Hello</button>
</div>
// app/root.tsx
import madsCSS from '@chaozcode/mads/styles?url'
export const links = () => [{ rel: 'stylesheet', href: madsCSS }]
export default function App() {
return (
<html data-cx data-theme="midnight"><body><Outlet/></body></html>
)
}
12 themes baked into mads.css. Switch globally via data-theme on <html>, or per-subtree by setting it on any wrapper.
| Theme | Vibe | Best for |
|---|---|---|
midnight (default) | Deep navy / purple | Dashboards, dev tools |
cyber | Neon cyan / magenta | Gaming, AI, sci-fi |
terra | Warm earth tones | Editorial, content |
aurora | Teal → violet gradient | Marketing, hero |
mono | High-contrast grayscale | Accessibility-first |
candy | Soft pastels | Consumer, kids |
light | Off-white | Print-style docs |
dark | Deep black | OLED, reading |
matrix · solar · nord · dracula | Classic dev themes | IDE-inspired UIs |
// Auto-exposed by bootstrap.js (window.MADS)
MADS.setTheme('cyber') // any of 12 ids
MADS.setReducedMotion(true) // opt-in, default OFF
MADS.setDirection('rtl') // 'ltr' | 'rtl'
MADS.setDensity('compact') // 'compact' | 'cozy' | 'comfortable'
// npm import (same API)
import { setTheme, setReducedMotion, setDirection, setDensity } from '@chaozcode/mads'
import { useTheme, useReducedMotion, useDirection, useDensity, useThemeOverrides } from '@chaozcode/mads'
const [theme, setTheme] = useTheme() // 'midnight' | 'cyber' | …
const [reduce, setReduce] = useReducedMotion() // boolean, opt-in
const [dir, setDir] = useDirection() // 'ltr' | 'rtl'
const [density, setDensity] = useDensity() // 'compact' | 'cozy' | 'comfortable'
// Custom token overrides
import { setThemeOverrides, clearThemeOverrides, exportThemeCss, uploadThemeCss } from '@chaozcode/mads'
setThemeOverrides({ accent: '#ff6b35', radius: '0.25rem' })
const css = exportThemeCss() // serialized override CSS
await uploadThemeCss(css, { endpoint: '/api/theme' })
import { getContrastRatio, contrastVerdict, validateThemeContrast } from '@chaozcode/mads'
getContrastRatio('#ffffff', '#050817') // 19.74
contrastVerdict(19.74) // 'AAA'
validateThemeContrast('cyber') // { pass: true, failures: [] }
Container-query-based — every primitive adapts to its container width, not the viewport. Lets a sidebar widget and a full-width panel share the same component and still render correctly.
| Component | Class | Props | Snippet |
|---|---|---|---|
CxContainer | cx-container | — | Wraps a region in container-query context. |
CxStack | cx-stack | gap: xs|sm|md|lg|xl | Vertical flow. |
CxRow | cx-row | min: 120..320, align, justify, gap | Auto-stacks below min. |
CxGrid | cx-grid | min: 140..320 or cols: 2|3|4|12 | Auto-fit columns. |
CxSidebar | cx-sidebar | width: 200|240|280|320, side | Sidebar + main; wraps when narrow. |
CxCluster | cx-cluster | gap, justify | Inline items that wrap (chips, tags). |
CxCenter | cx-center | max: sm|md|lg|xl, fill | Centers content both axes. |
CxCover | cx-cover | minHeight | Hero 3-zone (top/center/bottom). |
CxFrame | cx-frame | ratio: 16:9 (default) | Aspect-ratio container. |
CxSplit | cx-split | gap | 50/50 → stacks below 640px container. |
<h1 class="cx-fluid-h1">...</h1> /* clamp(1.35rem, 3vw, 1.85rem) */
<h2 class="cx-fluid-h2">...</h2> /* clamp(1.1rem, 2.4vw, 1.4rem) */
<p class="cx-fluid-body">...</p> /* clamp(0.78rem, 1.4vw, 0.875rem) */
<label class="cx-fluid-label">...</label> /* uppercase 0.6rem-0.68rem */
All motion is compositor-only (transform / opacity / filter). Animate-on-mount with utility classes; opt-in physics on hover/active.
| Class | Effect | Best for |
|---|---|---|
cx-animate-fade-up | Opacity + 12px slide | Cards on mount |
cx-animate-fade-in | Opacity only | Modal content |
cx-animate-scale-in | 0.94 → 1 scale + opacity | Popovers |
cx-animate-bounce-in | 0.85 → 1.02 → 1 spring | Toasts, achievements |
cx-animate-spring | Premium reveal with blur | Hero text |
cx-animate-slide-left/right | 32px horizontal slide | Drawers |
cx-animate-zoom-in | 0.85 + 8px blur clear | Lightbox |
cx-animate-reveal | Up + scale + blur clear | Section transitions |
| Class | Effect |
|---|---|
cx-animate-overshoot | Springs past target then settles |
cx-animate-settle | Critically-damped descent (no oscillation) |
cx-animate-elastic | Decaying oscillation back to origin |
cx-animate-pop | 0.6 → 1.08 → 1 punch |
cx-animate-drop-in | Falls from above with bounce-back |
cx-animate-flip-x/y | 3D card flip on entry |
cx-animate-blur-in | 10px blur clears |
cx-animate-rubber-band | X/Y squash-stretch attention-getter |
cx-animate-jelly | Soft squash on action confirmation |
<button class="cx-btn cx-btn-primary cx-hover-pop">Pop</button>
<div class="cx-card cx-card-frosted cx-hover-tilt-3d">3D tilt</div>
<div class="cx-card cx-hover-shimmer-y">Shimmer vertical</div>
<div class="cx-card cx-hover-jelly">Jelly squash</div>
<div class="cx-card cx-hover-rubber">Rubber band</div>
<button class="cx-btn cx-hover-glow">Glow halo</button>
<div class="cx-loop-breathe">Subtle scale 1.015 every 4s</div>
<div class="cx-loop-pulse-soft cx-pause-on-hover">Gentle pulse, pauses on hover</div>
<svg class="cx-loop-spin-slow">...</svg> /* 12s linear */
<div class="cx-loop-float">Hovering element</div>
<div data-stagger>
<div class="cx-card">Card 1</div> <!-- delays: 0ms -->
<div class="cx-card">Card 2</div> <!-- delays: 45ms -->
<div class="cx-card">Card 3</div> <!-- delays: 90ms -->
</div>
<div data-stagger="fast">...</div> <!-- 28ms step -->
<div data-stagger="slow">...</div> <!-- 70ms step -->
<div class="cx-animate-fade-up cx-dur-slow cx-delay-200">...</div>
/* dur: fast | base | slow | slower | slowest */
/* delay: 0 | 50 | 100 | 200 | 300 | 500 */
--cx-ease cubic-bezier(0.4, 0, 0.2, 1) /* material standard */
--cx-ease-out cubic-bezier(0.22, 1, 0.36, 1) /* decelerate */
--cx-ease-in-out cubic-bezier(0.65, 0, 0.35, 1) /* smooth */
--cx-ease-spring cubic-bezier(0.34, 1.56, 0.64, 1) /* overshoot */
--cx-ease-spring-soft cubic-bezier(0.16, 1.18, 0.5, 1) /* gentle bounce */
--cx-ease-bounce cubic-bezier(0.68, -0.6, 0.32, 1.6) /* big bounce */
--cx-ease-overshoot cubic-bezier(0.18, 1.32, 0.48, 0.97) /* expressive overshoot */
--cx-ease-snap cubic-bezier(0.05, 0.7, 0.1, 1) /* premium arrival */
--cx-ease-anticipate cubic-bezier(0.68,-0.55, 0.27, 1.55) /* pulls back first */
--cx-ease-drag cubic-bezier(0.2, 0, 0, 1) /* release fling */
Every visual decision is a CSS custom property. Override on :root or any subtree. JS mirrors live in MADS_TOKENS from @chaozcode/mads.
| Group | Examples |
|---|---|
| Color | --cx-accent, --cx-bg-base, --cx-text-primary, --cx-border |
| Status | --cx-color-success, --cx-color-danger, --cx-color-warning, --cx-color-info |
| Spacing | --cx-space-0..12 (4px base) |
| Radius | --cx-radius-sm..3xl, --cx-radius-full |
| Shadow | --cx-shadow-sm..xl, --cx-glow-sm..lg |
| Z-index | --cx-z-base..tooltip (0 → 3000) |
| Motion | --cx-duration-instant..slower, --cx-ease-* |
| I want… | Use |
|---|---|
| Primary call-to-action button | <CxButton variant="primary"> |
| Destructive action | <CxButton variant="danger"> |
| Secondary chrome control | <CxButton variant="ghost" size="sm"> |
| Container with subtle border | <CxCard variant="default"> |
| Glassmorphic surface | <CxCard variant="frosted"> or <CxCard variant="glass"> |
| Stat tile / KPI | cx-stat-card with cx-stat-card-label + cx-stat-card-value |
| Form input | <CxInput type=… variant="default|error"> |
| 4-column dashboard | <CxRow min={200}> with 4 children |
| Card gallery, fluid count | <CxGrid min={220}> |
| Sidebar + main | <CxSidebar width={240}> (first child = sidebar, last = main) |
| Tag/chip row | <CxCluster> with <span className="cx-badge"> |
| Hero pattern | <CxCover> |
| 16:9 video container | <CxFrame ratio="16:9"> |
| Status pill | <span className="cx-badge"> + status color |
| Code block | <pre className="cx-code"> |
| Modal / drawer | Modal / Drawer from @chaozcode/mads/components |
| Toast notifications | CxToastStack + useToast |
| Loading skeleton | <div className="cx-skeleton" /> or Skeleton component |
| Tabs | Tabs component (role="tabpanel" auto-animates) |
| Command palette (⌘K) | CommandPalette |
| Data grid | DataGrid |
| Avatar with status | <Avatar status="online" /> |
| Breadcrumbs | <Breadcrumbs items={[…]} /> |
foundation · buttons · badges · forms · cards · data · dataviz · feedback · navigation · overlays · advanced · fx · beauty · revolution · creative · devops · infrastructure · proforms · utilities · auth · calendar · chat · ecommerce · files · settings · social · motion · terminal
Browse each at design.chaozcode.com/components.
Cause: two React instances. Happens when framer-motion or lucide-react on esm.sh bundles its own React.
Fix: add ?external=react,react-dom on every package that has React as a peer dep. The shipped bootstrap.js already does this.
Cause: mads.css not loaded or loaded after a CSS reset that resets cx-*.
Fix: load mads.css AFTER any reset/normalize. Or import via npm import '@chaozcode/mads/styles'.
Fix: load React UMD BEFORE mads.umd.js.
Cause: a downstream CSS used @media (prefers-reduced-motion: reduce) which iOS Low Power Mode fires.
Fix: don't use that query. Reduce-motion is opt-in via setReducedMotion(true) → [data-reduce-motion].
Cause: @media block declared before the .sidebar base rule, so source-order made base position: sticky win over the media query's position: fixed. The sidebar stayed in-flow despite transform: translateX(-100%).
Fix: place @media (max-width: ...) blocks AFTER all base rules in your stylesheet.
Cause: missing tailwindcss() Vite plugin in lib-mode vite.config.ts. mads.css ships without utilities → DataGrid/Checkbox/Toggle break in consumers.
Fix: plugins: [react(), tailwindcss(), dts({...})] in BOTH showcase and lib configs.
Cause: SSR renders default theme, client renders saved theme. Mismatch flash.
Fix: inject the theme as an HTML attribute on the server (e.g., from a cookie). The MADS bootstrap reads data-theme on <html> without flicker.
| Entry | Path | Min+gz | What's in |
|---|---|---|---|
| CSS-only | /latest/mads.css | ~58 KB | All visuals, themes, motion, layout |
| Design system ESM | /latest/design-system.mjs | ~12 KB | CxButton, CxCard, CxInput, layout, theme |
| Components ESM | /latest/components.mjs | ~81 KB | All 217 React components |
| Patterns ESM | /latest/patterns.mjs | ~14 KB | Hero, login, pricing, dashboard layouts |
| UMD bundle | /latest/mads.umd.js | ~95 KB | All-in-one, exposes window.MADS |
Paste this preamble into your system prompt for high-fidelity output:
You're building UI with @chaozcode/mads. Hard rules:
- Use cx-* classes (never Tailwind inside Cx* components).
- Layout: CxRow (auto-stacks below `min`), CxGrid (auto-fit columns), CxSidebar (sidebar+main wraps).
- Surfaces: CxCard variant=frosted|glass|stat. Containers: cx-stat-card, cx-feature-card.
- Motion is on by default. Use cx-animate-fade-up | cx-animate-pop | cx-hover-tilt-3d.
- Skip prefers-reduced-motion media queries — use [data-reduce-motion] (opt-in).
- Animate transform/opacity only — never width/height/padding.
- For status: cx-badge + cx-color-success|danger|warning|info.
- Density target: text-[10px] labels, 0.5rem gaps, tight padding (compact-professional).
Same as Claude. Add function calling for theme switches via window.MADS.setTheme(themeId) if you wire tools.
Save this as .cursorrules or .github/copilot-instructions.md:
# Chaoz-MADS rules
- Import from '@chaozcode/mads' for primitives, '@chaozcode/mads/components' for everything else
- Wrap pages in CxStack/CxRow/CxGrid; never use raw CSS Grid/Flex when a primitive exists
- Hover/active uses cx-hover-* — never inline JS-driven CSS
- Forms: CxInput, CxTextarea, CxInputGroup — never raw <input> with Tailwind
- Always set data-theme on <html> (default "midnight")
- Skip prefers-reduced-motion @media — use [data-reduce-motion] opt-in flag
These models tend to invent class names. Pin them with the JSON-LD manifest at the top of this page or this single sentence:
Only emit class names from this list: cx-btn, cx-btn-{primary|secondary|ghost|outline|danger|success},
cx-card, cx-card-{frosted|glass|glassUltra|hover|stat}, cx-input, cx-textarea, cx-badge, cx-row,
cx-grid, cx-sidebar, cx-stack, cx-cluster, cx-frame, cx-split, cx-stat-card, cx-stat-card-{label,value},
cx-text-{primary|secondary|muted}, cx-text-gradient, cx-text-gradient-{purple|cyan|gold|green},
cx-fluid-{h1|h2|h3|body|small|micro|label}, cx-animate-{fade-up|fade-in|scale-in|bounce-in|pop|overshoot|settle|elastic|rubber-band|jelly},
cx-hover-{pop|tilt-3d|jelly|rubber|glow|shimmer-y|lift}, cx-loop-{breathe|pulse-soft|float|spin-slow}.
/latest/mads.css ← always-current (rolling)
/v1.6.0/mads.css ← pinned (v1.6.0)
/v1.5.0/mads.css ← pinned (older)
Same versioning applies to .mjs / .umd.js / manifest.json
<link rel="stylesheet"
href="https://design.chaozcode.com/v1.6.0/mads.css"
integrity="sha384-…"
crossorigin="anonymous">
Hashes are auto-published at /integrity.json.
Click → live preview of the integration modes: