SYSTEM
Anti-vibe. Pro-data. Quantified-self for the post-Headspace generation. Mono labels everywhere, sharp 2px corners, no purple.
TOKENS_
Surface & accents
base · #F4F4F4
card surface · #FFFFFF
ink · #0D0D0D
analysis panel · #1A1A1A
mint, primary · #6CEFA0
streak · #6CDDEF
milestone · #B06CEF
achievement · #EF9B6C
flag · #FF6B6B
Typography
10:42:01.04 SYSTEM // CYCLE_INIT
[ID: 0x4492] / READ_30_MINS
Radius & shadow
COMPONENTS_
Button.
Sharp 2px corners. JBM CAPS, 1px tracking. Mint variant for commit-style success actions.
import * as React from "react";
import { cn } from "./cn";
type ButtonProps = {
variant?: "primary" | "secondary" | "destructive" | "mint";
size?: "sm" | "md" | "lg";
children: React.ReactNode;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
export function Button({
variant = "primary",
size = "md",
className,
children,
...props
}: ButtonProps) {
return (
<button
className={cn("sys-btn", `sys-btn--${variant}`, `sys-btn--${size}`, className)}
{...props}
>
{children}
</button>
);
}
Add this Wardrobe System Button to my project.
1. Create components/ui/wardrobe/Button.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-fg
--color-card
--color-accent
--color-fail
--font-mono
--radius-md
--button-letter-spacing
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Button.tsx ---
import * as React from "react";
import { cn } from "./cn";
type ButtonProps = {
variant?: "primary" | "secondary" | "destructive" | "mint";
size?: "sm" | "md" | "lg";
children: React.ReactNode;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
export function Button({
variant = "primary",
size = "md",
className,
children,
...props
}: ButtonProps) {
return (
<button
className={cn("sys-btn", `sys-btn--${variant}`, `sys-btn--${size}`, className)}
{...props}
>
{children}
</button>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Input.
No box. Bottom-border 1px only. Mono micro-label above. Focus pulls border to fg.
import * as React from "react";
import { cn } from "./cn";
type InputProps = {
size?: "sm" | "md" | "lg";
variant?: "default" | "error";
label?: string;
hint?: string;
display?: boolean;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
export function Input({
size = "md",
variant = "default",
label,
hint,
display,
className,
id,
...props
}: InputProps) {
const inputId = id ?? React.useId();
return (
<label className="sys-field" htmlFor={inputId}>
{label && <span className="sys-field__label">{label}</span>}
<input
id={inputId}
className={cn(
"sys-input",
`sys-input--${size}`,
variant === "error" && "sys-input--error",
display && "sys-input--display",
className,
)}
aria-invalid={variant === "error" || undefined}
{...props}
/>
{hint && (
<span
className={cn(
"sys-field__hint",
variant === "error" && "sys-field__hint--error",
)}
>
{hint}
</span>
)}
</label>
);
}
Add this Wardrobe System Input to my project.
1. Create components/ui/wardrobe/Input.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-fg
--color-card
--color-fg-muted
--font-mono
--font-display
--label-letter-spacing
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Input.tsx ---
import * as React from "react";
import { cn } from "./cn";
type InputProps = {
size?: "sm" | "md" | "lg";
variant?: "default" | "error";
label?: string;
hint?: string;
display?: boolean;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
export function Input({
size = "md",
variant = "default",
label,
hint,
display,
className,
id,
...props
}: InputProps) {
const inputId = id ?? React.useId();
return (
<label className="sys-field" htmlFor={inputId}>
{label && <span className="sys-field__label">{label}</span>}
<input
id={inputId}
className={cn(
"sys-input",
`sys-input--${size}`,
variant === "error" && "sys-input--error",
display && "sys-input--display",
className,
)}
aria-invalid={variant === "error" || undefined}
{...props}
/>
{hint && (
<span
className={cn(
"sys-field__hint",
variant === "error" && "sys-field__hint--error",
)}
>
{hint}
</span>
)}
</label>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Textarea.
Same energy as Input — taller. Mono everywhere, no rounded edges.
import * as React from "react";
import { cn } from "./cn";
type TextareaProps = {
size?: "sm" | "md" | "lg";
variant?: "default" | "error";
label?: string;
hint?: string;
} & Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "size">;
export function Textarea({
size = "md",
variant = "default",
label,
hint,
className,
id,
...props
}: TextareaProps) {
const inputId = id ?? React.useId();
return (
<label className="sys-field" htmlFor={inputId}>
{label && <span className="sys-field__label">{label}</span>}
<textarea
id={inputId}
className={cn(
"sys-textarea",
`sys-input--${size}`,
variant === "error" && "sys-textarea--error",
className,
)}
aria-invalid={variant === "error" || undefined}
{...props}
/>
{hint && (
<span
className={cn(
"sys-field__hint",
variant === "error" && "sys-field__hint--error",
)}
>
{hint}
</span>
)}
</label>
);
}
Add this Wardrobe System Textarea to my project.
1. Create components/ui/wardrobe/Textarea.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-fg
--color-card
--font-mono
--label-letter-spacing
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Textarea.tsx ---
import * as React from "react";
import { cn } from "./cn";
type TextareaProps = {
size?: "sm" | "md" | "lg";
variant?: "default" | "error";
label?: string;
hint?: string;
} & Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "size">;
export function Textarea({
size = "md",
variant = "default",
label,
hint,
className,
id,
...props
}: TextareaProps) {
const inputId = id ?? React.useId();
return (
<label className="sys-field" htmlFor={inputId}>
{label && <span className="sys-field__label">{label}</span>}
<textarea
id={inputId}
className={cn(
"sys-textarea",
`sys-input--${size}`,
variant === "error" && "sys-textarea--error",
className,
)}
aria-invalid={variant === "error" || undefined}
{...props}
/>
{hint && (
<span
className={cn(
"sys-field__hint",
variant === "error" && "sys-field__hint--error",
)}
>
{hint}
</span>
)}
</label>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Select.
Mono CAPS for the value. Soft shadow on focus. Custom chevron in fg.
import * as React from "react";
import { cn } from "./cn";
type SelectProps = {
options: Array<{ value: string; label: string }>;
value: string;
onValueChange: (value: string) => void;
placeholder?: string;
label?: string;
} & Omit<
React.SelectHTMLAttributes<HTMLSelectElement>,
"value" | "onChange" | "size"
>;
export function Select({
options,
value,
onValueChange,
placeholder,
label,
className,
id,
...props
}: SelectProps) {
const inputId = id ?? React.useId();
return (
<label className="sys-field" htmlFor={inputId}>
{label && <span className="sys-field__label">{label}</span>}
<select
id={inputId}
value={value}
onChange={(e) => onValueChange(e.target.value)}
className={cn("sys-select", className)}
{...props}
>
{placeholder && (
<option value="" disabled>
{placeholder}
</option>
)}
{options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</label>
);
}
Add this Wardrobe System Select to my project.
1. Create components/ui/wardrobe/Select.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-fg
--color-card
--font-mono
--radius-md
--button-letter-spacing
--shadow-soft
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Select.tsx ---
import * as React from "react";
import { cn } from "./cn";
type SelectProps = {
options: Array<{ value: string; label: string }>;
value: string;
onValueChange: (value: string) => void;
placeholder?: string;
label?: string;
} & Omit<
React.SelectHTMLAttributes<HTMLSelectElement>,
"value" | "onChange" | "size"
>;
export function Select({
options,
value,
onValueChange,
placeholder,
label,
className,
id,
...props
}: SelectProps) {
const inputId = id ?? React.useId();
return (
<label className="sys-field" htmlFor={inputId}>
{label && <span className="sys-field__label">{label}</span>}
<select
id={inputId}
value={value}
onChange={(e) => onValueChange(e.target.value)}
className={cn("sys-select", className)}
{...props}
>
{placeholder && (
<option value="" disabled>
{placeholder}
</option>
)}
{options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</label>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Card.
White surface, soft shadow, near-invisible border. Optional code-texture in the corner. Dark variant for analysis panels.
Status: OPERATIONAL
import * as React from "react";
import { cn } from "./cn";
type CardProps = {
variant?: "default" | "dark" | "accent";
size?: "default" | "lg";
codeTexture?: string;
children: React.ReactNode;
} & React.HTMLAttributes<HTMLDivElement>;
export function Card({
variant = "default",
size = "default",
codeTexture,
className,
children,
...props
}: CardProps) {
return (
<div
className={cn(
"sys-card",
variant !== "default" && `sys-card--${variant}`,
size !== "default" && `sys-card--${size}`,
className,
)}
{...props}
>
{codeTexture && (
<span className="sys-card__code-texture">{codeTexture}</span>
)}
{children}
</div>
);
}
Add this Wardrobe System Card to my project.
1. Create components/ui/wardrobe/Card.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-card
--color-dark-bg
--color-dark-fg
--color-accent
--radius-md
--shadow-soft
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Card.tsx ---
import * as React from "react";
import { cn } from "./cn";
type CardProps = {
variant?: "default" | "dark" | "accent";
size?: "default" | "lg";
codeTexture?: string;
children: React.ReactNode;
} & React.HTMLAttributes<HTMLDivElement>;
export function Card({
variant = "default",
size = "default",
codeTexture,
className,
children,
...props
}: CardProps) {
return (
<div
className={cn(
"sys-card",
variant !== "default" && `sys-card--${variant}`,
size !== "default" && `sys-card--${size}`,
className,
)}
{...props}
>
{codeTexture && (
<span className="sys-card__code-texture">{codeTexture}</span>
)}
{children}
</div>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Badge.
Tiny 9px JBM rectangle. Filled = mint bg, fg text — for live/passing state.
import * as React from "react";
import { cn } from "./cn";
type BadgeProps = {
variant?: "default" | "filled";
children: React.ReactNode;
className?: string;
};
export function Badge({ variant = "default", children, className }: BadgeProps) {
return (
<span
className={cn(
"sys-badge",
variant === "filled" && "sys-badge--filled",
className,
)}
>
{children}
</span>
);
}
Add this Wardrobe System Badge to my project.
1. Create components/ui/wardrobe/Badge.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-card
--color-fg
--color-fg-muted
--color-accent
--font-mono
--radius-sm
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Badge.tsx ---
import * as React from "react";
import { cn } from "./cn";
type BadgeProps = {
variant?: "default" | "filled";
children: React.ReactNode;
className?: string;
};
export function Badge({ variant = "default", children, className }: BadgeProps) {
return (
<span
className={cn(
"sys-badge",
variant === "filled" && "sys-badge--filled",
className,
)}
>
{children}
</span>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Dialog.
White card, sharp corners, soft shadow. Title in Space Grotesk weight 400. Backdrop rgba(13,13,13,0.4).
import * as React from "react";
type DialogProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
title: string;
children: React.ReactNode;
};
export function Dialog({ open, onOpenChange, title, children }: DialogProps) {
const dialogRef = React.useRef<HTMLDivElement>(null);
const previousFocus = React.useRef<HTMLElement | null>(null);
React.useEffect(() => {
if (!open) return;
previousFocus.current = document.activeElement as HTMLElement;
const focusable = dialogRef.current?.querySelectorAll<HTMLElement>(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
);
focusable?.[0]?.focus();
function onKey(e: KeyboardEvent) {
if (e.key === "Escape") {
e.preventDefault();
onOpenChange(false);
return;
}
if (e.key !== "Tab" || !focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
document.addEventListener("keydown", onKey);
const prevOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.removeEventListener("keydown", onKey);
document.body.style.overflow = prevOverflow;
previousFocus.current?.focus();
};
}, [open, onOpenChange]);
if (!open) return null;
return (
<div
className="sys-dialog-backdrop"
onClick={(e) => {
if (e.target === e.currentTarget) onOpenChange(false);
}}
>
<div
ref={dialogRef}
className="sys-dialog"
role="dialog"
aria-modal="true"
aria-labelledby="sys-dialog-title"
>
<button
type="button"
className="sys-dialog__close"
onClick={() => onOpenChange(false)}
aria-label="Close"
>
×
</button>
<h2 id="sys-dialog-title" className="sys-dialog__title">
{title}
</h2>
{children}
</div>
</div>
);
}
Add this Wardrobe System Dialog to my project.
1. Create components/ui/wardrobe/Dialog.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-card
--color-fg
--font-display
--radius-md
--shadow-soft
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Dialog.tsx ---
import * as React from "react";
type DialogProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
title: string;
children: React.ReactNode;
};
export function Dialog({ open, onOpenChange, title, children }: DialogProps) {
const dialogRef = React.useRef<HTMLDivElement>(null);
const previousFocus = React.useRef<HTMLElement | null>(null);
React.useEffect(() => {
if (!open) return;
previousFocus.current = document.activeElement as HTMLElement;
const focusable = dialogRef.current?.querySelectorAll<HTMLElement>(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
);
focusable?.[0]?.focus();
function onKey(e: KeyboardEvent) {
if (e.key === "Escape") {
e.preventDefault();
onOpenChange(false);
return;
}
if (e.key !== "Tab" || !focusable || focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
document.addEventListener("keydown", onKey);
const prevOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.removeEventListener("keydown", onKey);
document.body.style.overflow = prevOverflow;
previousFocus.current?.focus();
};
}, [open, onOpenChange]);
if (!open) return null;
return (
<div
className="sys-dialog-backdrop"
onClick={(e) => {
if (e.target === e.currentTarget) onOpenChange(false);
}}
>
<div
ref={dialogRef}
className="sys-dialog"
role="dialog"
aria-modal="true"
aria-labelledby="sys-dialog-title"
>
<button
type="button"
className="sys-dialog__close"
onClick={() => onOpenChange(false)}
aria-label="Close"
>
×
</button>
<h2 id="sys-dialog-title" className="sys-dialog__title">
{title}
</h2>
{children}
</div>
</div>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Tabs.
Segmented nav-items. Active tab fills sort, white text, 0 radius. Arrow keys move you.
import * as React from "react";
import { cn } from "./cn";
type TabsProps = {
tabs: Array<{ id: string; label: string }>;
activeId: string;
onTabChange: (id: string) => void;
className?: string;
};
export function Tabs({ tabs, activeId, onTabChange, className }: TabsProps) {
return (
<div role="tablist" className={cn("sys-tabs", className)}>
{tabs.map((tab) => (
<button
key={tab.id}
role="tab"
aria-selected={tab.id === activeId}
type="button"
className={cn(
"sys-tabs__btn",
tab.id === activeId && "sys-tabs__btn--active",
)}
onClick={() => onTabChange(tab.id)}
onKeyDown={(e) => {
if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
e.preventDefault();
const idx = tabs.findIndex((t) => t.id === activeId);
const next =
e.key === "ArrowRight"
? (idx + 1) % tabs.length
: (idx - 1 + tabs.length) % tabs.length;
onTabChange(tabs[next].id);
}}
>
{tab.label}
</button>
))}
</div>
);
}
Add this Wardrobe System Tabs to my project.
1. Create components/ui/wardrobe/Tabs.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-card
--color-fg
--color-divider
--font-mono
--button-letter-spacing
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Tabs.tsx ---
import * as React from "react";
import { cn } from "./cn";
type TabsProps = {
tabs: Array<{ id: string; label: string }>;
activeId: string;
onTabChange: (id: string) => void;
className?: string;
};
export function Tabs({ tabs, activeId, onTabChange, className }: TabsProps) {
return (
<div role="tablist" className={cn("sys-tabs", className)}>
{tabs.map((tab) => (
<button
key={tab.id}
role="tab"
aria-selected={tab.id === activeId}
type="button"
className={cn(
"sys-tabs__btn",
tab.id === activeId && "sys-tabs__btn--active",
)}
onClick={() => onTabChange(tab.id)}
onKeyDown={(e) => {
if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
e.preventDefault();
const idx = tabs.findIndex((t) => t.id === activeId);
const next =
e.key === "ArrowRight"
? (idx + 1) % tabs.length
: (idx - 1 + tabs.length) % tabs.length;
onTabChange(tabs[next].id);
}}
>
{tab.label}
</button>
))}
</div>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Switch.
Sharp 2px square checkbox + mint check. Optional `terminal` prop renders [X]/[ ] LABEL in JBM.
import * as React from "react";
import { cn } from "./cn";
type SwitchProps = {
checked: boolean;
onCheckedChange: (checked: boolean) => void;
label?: string;
id?: string;
terminal?: boolean;
};
export function Switch({
checked,
onCheckedChange,
label,
id,
terminal,
}: SwitchProps) {
const inputId = id ?? React.useId();
return (
<label
className={cn("sys-switch", terminal && "sys-switch--terminal")}
htmlFor={inputId}
>
<input
id={inputId}
type="checkbox"
role="switch"
checked={checked}
onChange={(e) => onCheckedChange(e.target.checked)}
className="sys-switch__input"
/>
{terminal ? (
<>
<span className="sys-switch__bracket">
{checked ? "[X]" : "[ ]"}
</span>
{label && <span>{label}</span>}
</>
) : (
<>
<span className="sys-switch__box">
<svg
className="sys-switch__check"
viewBox="0 0 12 12"
fill="none"
stroke="currentColor"
strokeWidth="2"
aria-hidden="true"
>
<polyline points="2 6 5 9 10 3" />
</svg>
</span>
{label && <span>{label}</span>}
</>
)}
</label>
);
}
Add this Wardrobe System Switch to my project.
1. Create components/ui/wardrobe/Switch.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-fg
--color-card
--color-accent
--font-mono
--radius-md
--button-letter-spacing
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Switch.tsx ---
import * as React from "react";
import { cn } from "./cn";
type SwitchProps = {
checked: boolean;
onCheckedChange: (checked: boolean) => void;
label?: string;
id?: string;
terminal?: boolean;
};
export function Switch({
checked,
onCheckedChange,
label,
id,
terminal,
}: SwitchProps) {
const inputId = id ?? React.useId();
return (
<label
className={cn("sys-switch", terminal && "sys-switch--terminal")}
htmlFor={inputId}
>
<input
id={inputId}
type="checkbox"
role="switch"
checked={checked}
onChange={(e) => onCheckedChange(e.target.checked)}
className="sys-switch__input"
/>
{terminal ? (
<>
<span className="sys-switch__bracket">
{checked ? "[X]" : "[ ]"}
</span>
{label && <span>{label}</span>}
</>
) : (
<>
<span className="sys-switch__box">
<svg
className="sys-switch__check"
viewBox="0 0 12 12"
fill="none"
stroke="currentColor"
strokeWidth="2"
aria-hidden="true"
>
<polyline points="2 6 5 9 10 3" />
</svg>
</span>
{label && <span>{label}</span>}
</>
)}
</label>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
Toast.
White card with 3px mint accent border-left. JBM timestamp top-right. Slide-in from the right.
import * as React from "react";
import { cn } from "./cn";
export type ToastVariant = "default" | "success" | "error";
export type ToastProps = {
title: string;
description?: string;
variant?: ToastVariant;
};
type ToastEntry = ToastProps & { id: number; time: string };
type ToastContextValue = {
show: (toast: ToastProps) => void;
};
const ToastContext = React.createContext<ToastContextValue | null>(null);
export function useToast() {
const ctx = React.useContext(ToastContext);
if (!ctx) throw new Error("useToast must be used within <ToastProvider>");
return ctx;
}
function timestamp(): string {
const d = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const ms = String(Math.floor(d.getMilliseconds() / 10)).padStart(2, "0");
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${ms}`;
}
export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, setToasts] = React.useState<ToastEntry[]>([]);
const idRef = React.useRef(0);
const show = React.useCallback((toast: ToastProps) => {
const id = ++idRef.current;
setToasts((prev) => [...prev, { ...toast, id, time: timestamp() }]);
window.setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, 4000);
}, []);
return (
<ToastContext.Provider value={{ show }}>
{children}
<div
className="sys-toast-region"
role="region"
aria-label="Notifications"
aria-live="polite"
>
{toasts.map((t) => (
<Toast key={t.id} {...t} />
))}
</div>
</ToastContext.Provider>
);
}
type ToastViewProps = ToastProps & { time?: string };
export function Toast({
title,
description,
variant = "default",
time,
}: ToastViewProps) {
return (
<div
className={cn(
"sys-toast",
variant === "success" && "sys-toast--success",
variant === "error" && "sys-toast--error",
variant === "default" && "sys-toast--default",
)}
role="status"
>
<span className="sys-toast__title">{title}</span>
{time && <span className="sys-toast__time">{time}</span>}
{description && (
<span className="sys-toast__desc">{description}</span>
)}
</div>
);
}
Add this Wardrobe System Toast to my project.
1. Create components/ui/wardrobe/Toast.tsx with the code below.
2. Make sure components/ui/wardrobe/cn.ts exists (utility shown at the bottom).
3. Add these CSS variables to your globals.css under [data-system="system"]:
--color-card
--color-fg
--color-accent
--color-fail
--font-mono
--radius-md
--shadow-soft
4. Add the font import to globals.css:
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
5. Wrap any section using this component with <div data-system="system">.
--- Toast.tsx ---
import * as React from "react";
import { cn } from "./cn";
export type ToastVariant = "default" | "success" | "error";
export type ToastProps = {
title: string;
description?: string;
variant?: ToastVariant;
};
type ToastEntry = ToastProps & { id: number; time: string };
type ToastContextValue = {
show: (toast: ToastProps) => void;
};
const ToastContext = React.createContext<ToastContextValue | null>(null);
export function useToast() {
const ctx = React.useContext(ToastContext);
if (!ctx) throw new Error("useToast must be used within <ToastProvider>");
return ctx;
}
function timestamp(): string {
const d = new Date();
const pad = (n: number) => String(n).padStart(2, "0");
const ms = String(Math.floor(d.getMilliseconds() / 10)).padStart(2, "0");
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${ms}`;
}
export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, setToasts] = React.useState<ToastEntry[]>([]);
const idRef = React.useRef(0);
const show = React.useCallback((toast: ToastProps) => {
const id = ++idRef.current;
setToasts((prev) => [...prev, { ...toast, id, time: timestamp() }]);
window.setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, 4000);
}, []);
return (
<ToastContext.Provider value={{ show }}>
{children}
<div
className="sys-toast-region"
role="region"
aria-label="Notifications"
aria-live="polite"
>
{toasts.map((t) => (
<Toast key={t.id} {...t} />
))}
</div>
</ToastContext.Provider>
);
}
type ToastViewProps = ToastProps & { time?: string };
export function Toast({
title,
description,
variant = "default",
time,
}: ToastViewProps) {
return (
<div
className={cn(
"sys-toast",
variant === "success" && "sys-toast--success",
variant === "error" && "sys-toast--error",
variant === "default" && "sys-toast--default",
)}
role="status"
>
<span className="sys-toast__title">{title}</span>
{time && <span className="sys-toast__time">{time}</span>}
{description && (
<span className="sys-toast__desc">{description}</span>
)}
</div>
);
}
--- cn.ts ---
export function cn(...classes: Array<string | false | null | undefined>): string {
return classes.filter(Boolean).join(" ");
}
/* =========================================================
Wardrobe — SYSTEM tokens
Scoped via :where() for low specificity.
Activate by adding data-system="system" to a body or wrapper.
========================================================= */
:where([data-system="system"]) {
/* Surface */
--color-bg: #F4F4F4;
--color-card: #FFFFFF;
--color-fg: #0D0D0D;
--color-fg-muted: #888888;
--color-fg-faint: #BBBBBB;
--color-fg-ghost: #EEEEEE;
--color-divider: #F0F0F0;
/* Accents */
--color-accent: #6CEFA0;
--color-accent-blue: #6CDDEF;
--color-accent-purple: #B06CEF;
--color-accent-orange: #EF9B6C;
--color-success: #6CEFA0;
--color-fail: #FF6B6B;
/* Dark inversions (analysis panels in light dashboards) */
--color-dark-bg: #1A1A1A;
--color-dark-fg: #DDDDDD;
--color-dark-muted: #888888;
/* Borders */
--border: 1px solid rgba(0, 0, 0, 0.05);
--border-strong: 1px solid #EEEEEE;
--border-color: rgba(0, 0, 0, 0.05);
/* Radius — sharp! 2px MAX. Never pill. */
--radius-sm: 1px;
--radius-md: 2px;
--radius-lg: 2px;
--radius-full: 9999px;
/* Typography */
--font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Type scale */
--text-micro: 10px;
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
--text-md: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 24px;
--text-3xl: 32px;
--text-4xl: 42px;
--text-5xl: 48px;
--text-6xl: 64px;
--display-letter-spacing: -2px;
--display-line-height: 1;
--label-letter-spacing: 1.5px;
--button-letter-spacing: 1px;
/* Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* Shadow — soft only. Borders are nearly invisible. */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.04);
--shadow-flat: 0 1px 2px rgba(0, 0, 0, 0.03);
/* Motion */
--duration-fast: 100ms;
--duration-base: 200ms;
--easing: cubic-bezier(0.4, 0, 0.2, 1);
}
[data-system="system"] {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap");
INSTALL_
One paste, one prompt, your project is wearing System. No npm install, no config files, no purple gradients.
BIG GREEN INSTALL BUTTON →MCP COMING SOON_
Wardrobe MCP server is in the works. Soon you'll just say "install system" to your AI and it'll be done. Sign up for the drop.
SEE IT LIVE_
Sequence — a fictional habit-tracking system using System end-to-end.