WARDROBE / HYPER

HYPER.

Acid yellow. Brown borders. CAPS forever.
Stop building purple-gradient apps.

TOKENS.

Colors

--color-bg
acid yellow · #EFFF71
--color-fg
deep brown · #3A1E1E
--color-surface
pure white · #FFFFFF
--color-pink
pastel pink · #FFC1E3
--color-blue
pastel blue · #BCEFFF
--color-green
pastel green · #C3FF8B
--color-danger
red flag · #FF4B4B

Typography

DISPLAY · ANTON
EVERY STREET
IS A TRACK.
BODY · GEORGIA

Hyper is loud on purpose. The body copy is Georgia — old-school serif — so the display can scream while the reading stays grown-up. Don't replace it with Inter.

Radius & shadow

--radius-sm · 8px
--radius-md · 12px
--radius-lg · 16px
--radius-pill · 32px
--shadow-hard

COMPONENTS.

Button.

Pill-shaped, CAPS forever. Hard offset shadow when you press.

Button.tsx
import * as React from "react";
import { cn } from "./cn";

type ButtonProps = {
    variant?: "primary" | "secondary" | "destructive";
    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("hyp-btn", `hyp-btn--${variant}`, `hyp-btn--${size}`, className)}
            {...props}
        >
            {children}
        </button>
    );
}
Add this Wardrobe Hyper 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="hyper"]:
   --color-fg
   --color-bg
   --color-danger
   --font-display
   --radius-pill
   --shadow-hard
   --shadow-hard-sm
   --border
   --border-color
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- Button.tsx ---

import * as React from "react";
import { cn } from "./cn";

type ButtonProps = {
    variant?: "primary" | "secondary" | "destructive";
    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("hyp-btn", `hyp-btn--${variant}`, `hyp-btn--${size}`, className)}
            {...props}
        >
            {children}
        </button>
    );
}


--- cn.ts ---

export function cn(...classes: Array<string | false | null | undefined>): string {
    return classes.filter(Boolean).join(" ");
}

/* =========================================================
   Wardrobe — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Input.

White surface, brown border, focus shadows hard. Optional numeric badge.

Input.tsx
import * as React from "react";
import { cn } from "./cn";

type InputProps = {
    size?: "sm" | "md" | "lg";
    variant?: "default" | "error";
    label?: string;
    hint?: string;
    numericBadge?: string;
    display?: boolean;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;

export function Input({
    size = "md",
    variant = "default",
    label,
    hint,
    numericBadge,
    display,
    className,
    id,
    ...props
}: InputProps) {
    const inputId = id ?? React.useId();
    return (
        <label className="hyp-field" htmlFor={inputId}>
            {label && <span className="hyp-field__label">{label}</span>}
            {numericBadge && <span className="hyp-badge-circle">{numericBadge}</span>}
            <input
                id={inputId}
                className={cn(
                    "hyp-input",
                    `hyp-input--${size}`,
                    variant === "error" && "hyp-input--error",
                    display && "hyp-input--display",
                    className,
                )}
                aria-invalid={variant === "error" || undefined}
                {...props}
            />
            {hint && (
                <span
                    className={cn(
                        "hyp-field__hint",
                        variant === "error" && "hyp-field__hint--error",
                    )}
                >
                    {hint}
                </span>
            )}
        </label>
    );
}
Add this Wardrobe Hyper 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="hyper"]:
   --color-surface
   --color-fg
   --font-display
   --font-body
   --radius-md
   --shadow-hard-sm
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- Input.tsx ---

import * as React from "react";
import { cn } from "./cn";

type InputProps = {
    size?: "sm" | "md" | "lg";
    variant?: "default" | "error";
    label?: string;
    hint?: string;
    numericBadge?: string;
    display?: boolean;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;

export function Input({
    size = "md",
    variant = "default",
    label,
    hint,
    numericBadge,
    display,
    className,
    id,
    ...props
}: InputProps) {
    const inputId = id ?? React.useId();
    return (
        <label className="hyp-field" htmlFor={inputId}>
            {label && <span className="hyp-field__label">{label}</span>}
            {numericBadge && <span className="hyp-badge-circle">{numericBadge}</span>}
            <input
                id={inputId}
                className={cn(
                    "hyp-input",
                    `hyp-input--${size}`,
                    variant === "error" && "hyp-input--error",
                    display && "hyp-input--display",
                    className,
                )}
                aria-invalid={variant === "error" || undefined}
                {...props}
            />
            {hint && (
                <span
                    className={cn(
                        "hyp-field__hint",
                        variant === "error" && "hyp-field__hint--error",
                    )}
                >
                    {hint}
                </span>
            )}
        </label>
    );
}


--- cn.ts ---

export function cn(...classes: Array<string | false | null | undefined>): string {
    return classes.filter(Boolean).join(" ");
}

/* =========================================================
   Wardrobe — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Textarea.

Same energy as Input, just taller. Body font for what you write.

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="hyp-field" htmlFor={inputId}>
            {label && <span className="hyp-field__label">{label}</span>}
            <textarea
                id={inputId}
                className={cn(
                    "hyp-textarea",
                    `hyp-input--${size}`,
                    variant === "error" && "hyp-textarea--error",
                    className,
                )}
                aria-invalid={variant === "error" || undefined}
                {...props}
            />
            {hint && (
                <span
                    className={cn(
                        "hyp-field__hint",
                        variant === "error" && "hyp-field__hint--error",
                    )}
                >
                    {hint}
                </span>
            )}
        </label>
    );
}
Add this Wardrobe Hyper 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="hyper"]:
   --color-surface
   --color-fg
   --font-body
   --radius-md
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- 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="hyp-field" htmlFor={inputId}>
            {label && <span className="hyp-field__label">{label}</span>}
            <textarea
                id={inputId}
                className={cn(
                    "hyp-textarea",
                    `hyp-input--${size}`,
                    variant === "error" && "hyp-textarea--error",
                    className,
                )}
                aria-invalid={variant === "error" || undefined}
                {...props}
            />
            {hint && (
                <span
                    className={cn(
                        "hyp-field__hint",
                        variant === "error" && "hyp-field__hint--error",
                    )}
                >
                    {hint}
                </span>
            )}
        </label>
    );
}


--- cn.ts ---

export function cn(...classes: Array<string | false | null | undefined>): string {
    return classes.filter(Boolean).join(" ");
}

/* =========================================================
   Wardrobe — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Select.

Caps display value, custom chevron, border-color matches everything.

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="hyp-field" htmlFor={inputId}>
            {label && <span className="hyp-field__label">{label}</span>}
            <select
                id={inputId}
                value={value}
                onChange={(e) => onValueChange(e.target.value)}
                className={cn("hyp-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 Hyper 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="hyper"]:
   --color-surface
   --color-fg
   --font-display
   --radius-md
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- 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="hyp-field" htmlFor={inputId}>
            {label && <span className="hyp-field__label">{label}</span>}
            <select
                id={inputId}
                value={value}
                onChange={(e) => onValueChange(e.target.value)}
                className={cn("hyp-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 — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Card.

Pastel pink, blue, green, or white. Numeric badge lapping the corner.

01
Members
234
02
Events year
47
03
Total km
1,920
Card.tsx
import * as React from "react";
import { cn } from "./cn";

type CardProps = {
    variant?: "default" | "pink" | "blue" | "green";
    numericBadge?: string;
    children: React.ReactNode;
} & React.HTMLAttributes<HTMLDivElement>;

export function Card({
    variant = "default",
    numericBadge,
    className,
    children,
    ...props
}: CardProps) {
    return (
        <div
            className={cn(
                "hyp-card",
                variant !== "default" && `hyp-card--${variant}`,
                className,
            )}
            {...props}
        >
            {numericBadge && <span className="hyp-badge-circle">{numericBadge}</span>}
            {children}
        </div>
    );
}
Add this Wardrobe Hyper 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="hyper"]:
   --color-surface
   --color-pink
   --color-blue
   --color-green
   --radius-md
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- Card.tsx ---

import * as React from "react";
import { cn } from "./cn";

type CardProps = {
    variant?: "default" | "pink" | "blue" | "green";
    numericBadge?: string;
    children: React.ReactNode;
} & React.HTMLAttributes<HTMLDivElement>;

export function Card({
    variant = "default",
    numericBadge,
    className,
    children,
    ...props
}: CardProps) {
    return (
        <div
            className={cn(
                "hyp-card",
                variant !== "default" && `hyp-card--${variant}`,
                className,
            )}
            {...props}
        >
            {numericBadge && <span className="hyp-badge-circle">{numericBadge}</span>}
            {children}
        </div>
    );
}


--- cn.ts ---

export function cn(...classes: Array<string | false | null | undefined>): string {
    return classes.filter(Boolean).join(" ");
}

/* =========================================================
   Wardrobe — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Badge.

Inline pill. Outline by default, brown-filled for live status.

NEWLIVEv0.1HYPER
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(
                "hyp-badge",
                variant === "filled" && "hyp-badge--filled",
                className,
            )}
        >
            {children}
        </span>
    );
}
Add this Wardrobe Hyper 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="hyper"]:
   --color-surface
   --color-fg
   --color-bg
   --radius-pill
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- 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(
                "hyp-badge",
                variant === "filled" && "hyp-badge--filled",
                className,
            )}
        >
            {children}
        </span>
    );
}


--- cn.ts ---

export function cn(...classes: Array<string | false | null | undefined>): string {
    return classes.filter(Boolean).join(" ");
}

/* =========================================================
   Wardrobe — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Dialog.

Centered, hard shadow, brown veil. Escape to close. Focus trapped inside.

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="hyp-dialog-backdrop"
            onClick={(e) => {
                if (e.target === e.currentTarget) onOpenChange(false);
            }}
        >
            <div
                ref={dialogRef}
                className="hyp-dialog"
                role="dialog"
                aria-modal="true"
                aria-labelledby="hyp-dialog-title"
            >
                <button
                    type="button"
                    className="hyp-dialog__close"
                    onClick={() => onOpenChange(false)}
                    aria-label="Close"
                >
                    ×
                </button>
                <h2 id="hyp-dialog-title" className="hyp-dialog__title">
                    {title}.
                </h2>
                {children}
            </div>
        </div>
    );
}
Add this Wardrobe Hyper 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="hyper"]:
   --color-surface
   --color-fg
   --font-display
   --radius-md
   --shadow-hard
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- 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="hyp-dialog-backdrop"
            onClick={(e) => {
                if (e.target === e.currentTarget) onOpenChange(false);
            }}
        >
            <div
                ref={dialogRef}
                className="hyp-dialog"
                role="dialog"
                aria-modal="true"
                aria-labelledby="hyp-dialog-title"
            >
                <button
                    type="button"
                    className="hyp-dialog__close"
                    onClick={() => onOpenChange(false)}
                    aria-label="Close"
                >
                    ×
                </button>
                <h2 id="hyp-dialog-title" className="hyp-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 — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Tabs.

Segmented control. Active tab fills brown. Arrow keys move you.

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("hyp-tabs", className)}>
            {tabs.map((tab) => (
                <button
                    key={tab.id}
                    role="tab"
                    aria-selected={tab.id === activeId}
                    type="button"
                    className={cn(
                        "hyp-tabs__btn",
                        tab.id === activeId && "hyp-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 Hyper 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="hyper"]:
   --color-surface
   --color-fg
   --color-bg
   --font-display
   --radius-md
   --radius-sm
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- 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("hyp-tabs", className)}>
            {tabs.map((tab) => (
                <button
                    key={tab.id}
                    role="tab"
                    aria-selected={tab.id === activeId}
                    type="button"
                    className={cn(
                        "hyp-tabs__btn",
                        tab.id === activeId && "hyp-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 — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Switch.

Pill track, brown thumb. Flips to brown-filled with acid thumb.

Switch.tsx
import * as React from "react";

type SwitchProps = {
    checked: boolean;
    onCheckedChange: (checked: boolean) => void;
    label?: string;
    id?: string;
};

export function Switch({ checked, onCheckedChange, label, id }: SwitchProps) {
    const inputId = id ?? React.useId();
    return (
        <label className="hyp-switch" htmlFor={inputId}>
            <input
                id={inputId}
                type="checkbox"
                role="switch"
                checked={checked}
                onChange={(e) => onCheckedChange(e.target.checked)}
                className="hyp-switch__input"
            />
            <span className="hyp-switch__track">
                <span className="hyp-switch__thumb" />
            </span>
            {label && <span>{label}</span>}
        </label>
    );
}
Add this Wardrobe Hyper 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="hyper"]:
   --color-fg
   --color-bg
   --radius-pill
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- Switch.tsx ---

import * as React from "react";

type SwitchProps = {
    checked: boolean;
    onCheckedChange: (checked: boolean) => void;
    label?: string;
    id?: string;
};

export function Switch({ checked, onCheckedChange, label, id }: SwitchProps) {
    const inputId = id ?? React.useId();
    return (
        <label className="hyp-switch" htmlFor={inputId}>
            <input
                id={inputId}
                type="checkbox"
                role="switch"
                checked={checked}
                onChange={(e) => onCheckedChange(e.target.checked)}
                className="hyp-switch__input"
            />
            <span className="hyp-switch__track">
                <span className="hyp-switch__thumb" />
            </span>
            {label && <span>{label}</span>}
        </label>
    );
}


--- cn.ts ---

export function cn(...classes: Array<string | false | null | undefined>): string {
    return classes.filter(Boolean).join(" ");
}

/* =========================================================
   Wardrobe — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

Toast.

Slides in from below. Auto-dismisses after 4s. Success swaps to green.

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 };

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;
}

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 }]);
        window.setTimeout(() => {
            setToasts((prev) => prev.filter((t) => t.id !== id));
        }, 4000);
    }, []);

    return (
        <ToastContext.Provider value={{ show }}>
            {children}
            <div
                className="hyp-toast-region"
                role="region"
                aria-label="Notifications"
                aria-live="polite"
            >
                {toasts.map((t) => (
                    <Toast key={t.id} {...t} />
                ))}
            </div>
        </ToastContext.Provider>
    );
}

export function Toast({ title, description, variant = "default" }: ToastProps) {
    return (
        <div
            className={cn(
                "hyp-toast",
                variant === "success" && "hyp-toast--success",
                variant === "error" && "hyp-toast--error",
            )}
            role="status"
        >
            <span>{title}</span>
            {description && <span className="hyp-toast__desc">{description}</span>}
        </div>
    );
}
Add this Wardrobe Hyper 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="hyper"]:
   --color-fg
   --color-bg
   --color-success
   --color-danger
   --font-display
   --radius-pill
   --shadow-hard-sm
   --border
4. Add the font import to globals.css:
   @import url("https://fonts.googleapis.com/css2?family=Anton&display=swap");
5. Wrap any section using this component with <div data-system="hyper">.

--- 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 };

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;
}

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 }]);
        window.setTimeout(() => {
            setToasts((prev) => prev.filter((t) => t.id !== id));
        }, 4000);
    }, []);

    return (
        <ToastContext.Provider value={{ show }}>
            {children}
            <div
                className="hyp-toast-region"
                role="region"
                aria-label="Notifications"
                aria-live="polite"
            >
                {toasts.map((t) => (
                    <Toast key={t.id} {...t} />
                ))}
            </div>
        </ToastContext.Provider>
    );
}

export function Toast({ title, description, variant = "default" }: ToastProps) {
    return (
        <div
            className={cn(
                "hyp-toast",
                variant === "success" && "hyp-toast--success",
                variant === "error" && "hyp-toast--error",
            )}
            role="status"
        >
            <span>{title}</span>
            {description && <span className="hyp-toast__desc">{description}</span>}
        </div>
    );
}


--- cn.ts ---

export function cn(...classes: Array<string | false | null | undefined>): string {
    return classes.filter(Boolean).join(" ");
}

/* =========================================================
   Wardrobe — HYPER tokens
   Scoped via :where() for low specificity.
   Activate by adding data-system="hyper" to a body or wrapper.
   ========================================================= */

:where([data-system="hyper"]) {
    /* Surface */
    --color-bg: #EFFF71;
    --color-fg: #3A1E1E;
    --color-surface: #FFFFFF;

    /* Pastels */
    --color-pink: #FFC1E3;
    --color-blue: #BCEFFF;
    --color-green: #C3FF8B;

    /* Status */
    --color-success: #C3FF8B;
    --color-warning: #FFC1E3;
    --color-danger: #FF4B4B;
    --color-info: #BCEFFF;

    /* Border */
    --border-width: 1px;
    --border-color: #3A1E1E;
    --border: var(--border-width) solid var(--border-color);

    /* Radius */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 32px;
    --radius-full: 9999px;

    /* Typography */
    --font-display: "Anton", "Impact", "Arial Narrow", sans-serif;
    --font-body: "Georgia", serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Type scale */
    --text-xs: 10px;
    --text-sm: 12px;
    --text-base: 14px;
    --text-md: 16px;
    --text-lg: 18px;
    --text-xl: 20px;
    --text-2xl: 24px;
    --text-3xl: 32px;
    --text-4xl: 42px;
    --text-5xl: 56px;
    --text-6xl: 80px;
    --text-7xl: 120px;

    --display-line-height: 0.85;
    --display-letter-spacing: -0.02em;

    /* 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;
    --space-16: 64px;

    /* Shadows */
    --shadow-hard: 4px 4px 0px var(--color-fg);
    --shadow-hard-sm: 2px 2px 0px var(--color-fg);
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.1);

    /* Motion */
    --duration-fast: 100ms;
    --duration-base: 200ms;
    --duration-slow: 400ms;
    --easing: cubic-bezier(0.4, 0, 0.2, 1);
}

[data-system="hyper"] {
    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=Anton&display=swap");

INSTALL.

One paste, one prompt, your project is wearing Hyper. 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 hyper" to your AI and it'll be done. Sign up below for the drop.

SEE IT LIVE.

Marathon Club — a fictional running club using Hyper end-to-end.