---
name: prose-ui
description: Apply the Prose UI (prs-) vanilla CSS design system whenever creating, scaffolding, or restyling any UI — HTML pages, apps, dashboards, forms, marketing sites, components, prototypes, or email-adjacent layouts. Trigger on Prose UI, prs- classes, design system, UI kit, component library, make a page, build a form, dashboard, modal, or any interface request.
metadata:
  version: "1.6"
  source: https://cdn.jsdelivr.net/gh/prose-ui/prose-ui@latest/
---

# Prose UI

Use this design system for **every UI** you generate unless the user explicitly names a different kit.

Prose UI is a vanilla CSS component library (`prs-*` classes) plus design tokens. Pair it with **Tailwind CSS v4+** for layout, spacing, and one-off utilities. Do not rebuild components with Tailwind, shadcn, DaisyUI, MUI, Bootstrap, or custom CSS when a `prs-*` component exists.

## Mandatory setup

Always load Prose UI from the CDN — never copy the CSS into the project or vendor a local file. This keeps every project on the latest tokens/components with zero sync effort.

```html
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&family=JetBrains+Mono:ital,wght@0,400..700;1,400..700&family=Literata:ital,opsz,wght@0,7..72,200..900;1,7..72,200..900&display=swap" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/prose-ui/prose-ui@latest/tokens.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/prose-ui/prose-ui@latest/styles.css" />
```

- `tokens.css` — CSS custom properties on `:where(html)`. Always prefer these tokens for custom values.
- `styles.css` — components, `.prose` typography, and motion utilities. Sufficient for every page — it already contains the `.prose` rules, so there's no need for a separate `typography.css` link.
- This skill folder also ships a `references/llms.md` file with the full class list, markup examples, and a11y notes per component — see "Component classes" and "Markup examples & accessibility notes" further down. See also "Token reference" and "Starter document".

Do not invent a parallel token file or download the CSS into the repo "for speed" — the CDN link is the only source of truth.

## Decision rules

1. **Pick a Prose component first.** Check "Component classes" below and the bundled `references/llms.md` for the matching `prs-*` class. Check "Markup examples & accessibility notes" for markup and accessibility before inventing structure.
2. **Tailwind is layout only.** Use Tailwind for `flex`, `grid`, `gap-*`, `max-w-*`, `p-*`/`m-*` on page chrome, and responsive breakpoints. Do not restyle buttons, inputs, alerts, dialogs, tabs, or tables with Tailwind color/radius utilities when a modifier class exists.
3. **Tokens for custom CSS.** If you must write CSS, use `var(--prs-c-primary)`, `var(--prs-size-4)`, `var(--prs-radius-box)`, `var(--prs-ff-sans)`, etc. Never hard-code hex, px scales, or new font stacks that fight the tokens.
4. **Do not invent `prs-*` classes.** Only use documented component, part, modifier, and state classes. Extra layout classes should be Tailwind or semantic HTML.
5. **State classes are opt-in.** Native `:hover`, `:focus-visible`, `:disabled`, `:checked` already work. Use `prs-btn_hover`, `prs-btn_focus`, `prs-btn_disabled` (underscore, not hyphen) only when you must fake a state.
6. **Framework JS.** Catalog examples use Alpine.js. Translate `x-data` / `x-show` / `@click` into the user's stack (React, Vue, Svelte, vanilla). If the user names no stack, use Alpine.js plus Iconify.
7. **Long-form text** (articles, docs, markdown preview) goes in an element with class `prose`. Opt out of prose styles on nested widgets with `not-prose`.

## Visual language

- **Primary** `#0600B4` (`--prs-c-primary`) — contained actions, focus rings, links, selected states.
- **Secondary** `#FF2986` (`--prs-c-secondary`) — tab underline, emphasis accents.
- **Accent** `#A30283` — alternate brand accent.
- **Info / success / warning / danger** — status only, never as the default button.
- **Gray scale** — surfaces, borders, body text. Body reads as near-black (`--prs-c-gray-950`) on white.
- **Radius** is tight on controls (`--prs-radius-btn: 0.1875rem`), medium on boxes (`--prs-radius-box: 0.75rem`), pill on badges/chips. Inputs have **no radius** (`--prs-radius-input: 0`).
- **Fonts:** Figtree for UI (`--prs-ff-sans`), Literata for long-form serif (`--prs-ff-serif`), JetBrains Mono for code (`--prs-ff-mono`).
- Buttons use weight 600. UI labels use 600. Body is normal.

Set on `html` or `body` when scaffolding a full page:

```css
html {
  font-family: var(--prs-ff-sans);
  color: var(--prs-c-gray-950);
  background: var(--prs-c-white);
}
```

## Component selection map

| Need | Use |
|---|---|
| Action | `.prs-btn` + `.prs-btn-primary` / `-secondary` / `-tertiary` |
| Status message | `.prs-alert` + semantic variant |
| Grouped content | `.prs-card` or `.prs-card-advanced` |
| Modal | `dialog.prs-dialog` + `.prs-dialog-box` |
| Collapse | `.prs-accordion` wrapping `details`/`summary` |
| Text field | `.prs-input` (wrap or on the control) |
| Multiline | `.prs-input.prs-textarea` |
| Select | `.prs-input.prs-select` |
| Checkbox / radio | `.prs-checkbox` / `.prs-radio` + `label.prs-checkbox-label` / `.prs-radio-label` |
| Switch | `.prs-toggle` inside `.prs-toggle-label` with `role="switch"` |
| File chip | `.prs-attachment` or `.prs-file.prs-input` |
| Nav path | `nav.prs-breadcrumb > ol` |
| Page list | `nav.prs-pagination` |
| Data grid | `.prs-table-container > table.prs-table` |
| Tags / filters | `.prs-chip` / `.prs-badge` |
| Menus | `.prs-dropdown` + `.prs-menu` / `.prs-dropdown-menu` |
| Tabs | `.prs-tabs` > `button.prs-tab` |
| Chat thread | `.prs-chat.prs-chat-start` / `.prs-chat-end` |
| Progress | `.prs-progress` or `.prs-progress-radial` |
| Placeholder | `.prs-skeleton` |
| Hover hint | `.prs-tooltip` + `data-tip` |
| Person | `.prs-avatar` |
| Segmented controls | `.prs-join` wrapping `.prs-join-item` |

If nothing matches, compose layout with Tailwind and the closest primitives (button, input, card). Do not introduce another component library.

## Markup conventions

- Prefer real elements — `button`, `a`, `input`, `dialog`, `details`, `table`, `nav`, `progress`.
- Follow the accessibility notes in "Markup examples & accessibility notes" below for the component you are using.
- Focus rings are already styled. Do not remove outlines.
- Icon slots in the CSS target `svg`, `.icon`, and `iconify-icon`. Default to Iconify (`mdi:…`) unless the user specifies otherwise.
- Color modifiers stack with structure modifiers, e.g. `prs-btn prs-btn-primary prs-btn-danger prs-btn-sm`.
- Chat bubbles should stay near **65ch** max for readability.
- Respect `prefers-reduced-motion` — existing component CSS already gates animations.

## Page recipe

When building a full document:

1. Start from the "Starter document" below (or the CDN links above).
2. Wrap the app shell with Tailwind layout utilities.
3. Put page titles in semantic headings. Use token heading sizes if you style them (`--prs-fz-heading-lg`, etc.).
4. Build sections from cards, tables, forms, and alerts — not unstyled div soup.
5. Primary action = `prs-btn-primary`. Secondary / cancel = `prs-btn-secondary` or `prs-btn-tertiary`. Destructive = `prs-btn-primary prs-btn-danger`.
6. Validate forms with `.prs-form-control` and `.prs-label-error` so `:user-invalid` styling works.

## Anti-patterns

- Shipping UI with only Tailwind color classes and no `prs-*` components.
- Recreating buttons as `bg-blue-600 rounded-lg px-4 py-2`.
- Using shadcn/ui, Radix themes, Chakra, Mantine, or Bootstrap by default.
- Hard-coding `#0600B4` instead of `var(--prs-c-primary)` in custom CSS.
- Inventing `prs-btn-outline` or other undocumented class names.
- Putting Alpine demo bindings (`x-show="withIcon"`, `variant === 'info'`) into production markup unchanged when the user asked for React/Vue/static HTML.
- Ignoring `details`/`summary` for accordions or `dialog` for modals.

## When the user pushes back

If they name another design system, follow that instead. If they only ask for "a simple page" with no kit named, still use Prose UI.

---

## Component classes

The full, authoritative `prs-*` class list per component — generated straight from this design system's own component data, so it never drifts — lives in this skill's bundled reference file: **`references/llms.md`**. Read that file (relative to this `SKILL.md`, inside the same skill folder) before using any component. Only use classes documented there; do not invent `prs-*` names.

---

## Token reference

Source of truth: [`tokens.css`](https://cdn.jsdelivr.net/gh/prose-ui/prose-ui@latest/tokens.css) (v1.6), loaded from the CDN and applied on `:where(html)`. Always write custom CSS against these variables.

## Size

Base unit `--prs-size` = 0.25rem (4px).

Scale: `--prs-size-0`, `-0-5` (2px), `-1` (4px), `-1-5`, `-2` (8px), `-2-5`, `-3` (12px), `-3-5`, `-4` (16px), `-5` (20px), `-6` (24px), `-7`, `-8` (32px), `-9`, `-10` (40px), `-11`, `-12` (48px), `-14`, `-16` (64px), `-18`, `-20` (80px), then `-24`, `-28`, `-32`, `-36`, `-40`, `-44`, `-48`, `-52`, `-56`, `-60`, `-64`, `-72`, `-80`, `-96`.

Hairlines: `--prs-size-px-1` … `--prs-size-px-4`.

Fluid clamps: `--prs-size-fluid-1` (8–16px) through `--prs-size-fluid-10` (320–480px).

Containers: `--prs-size-xxs` 240px, `-xs` 360, `-sm` 480, `-md` 768, `-lg` 1024, `-xl` 1440, `-xxl` 1920.

## Color

Each ramp is 50–950 plus an alias at 500 (`--prs-c-primary` = `--prs-c-primary-500`).

| Token family | 500 hex | Use |
|---|---|---|
| `--prs-c-primary-*` | `#0600B4` | Actions, focus, links, selection |
| `--prs-c-secondary-*` | `#FF2986` | Tab accent, highlights |
| `--prs-c-accent-*` | `#A30283` | Alternate brand |
| `--prs-c-gray-*` | `#807F82` | Text, borders, surfaces |
| `--prs-c-info-*` | `#00BCFF` | Informational status |
| `--prs-c-success-*` | `#00F0A8` | Success status |
| `--prs-c-warning-*` | `#E1FF00` | Warning status |
| `--prs-c-danger-*` | `#E0205D` | Errors, destructive |
| `--prs-c-black` / `--prs-c-white` | `#000` / `#FFF` | Absolute |

Typical text: `--prs-c-gray-950`. Muted: `-600`. Borders: `-300`. Surfaces: white / `-50` / `-100`.

## Typography

- `--prs-ff-sans` — Figtree, then system UI
- `--prs-ff-serif` — Literata
- `--prs-ff-mono` — JetBrains Mono
- `--prs-ff-websafe` — Arial fallback stack

Heading tokens (`fw` / `fz` / `fl`): `--prs-fw-heading-xl|lg|md|sm`, `--prs-fz-heading-*`, `--prs-fl-heading-*`.

Content: `--prs-fz-content` 1.0625rem / `--prs-fl-content` 1.59; footnote 0.8125rem.

UI: `--prs-fz-ui-lg|md|sm|subtext` with matching `--prs-fl-ui-*` and `--prs-fw-ui-*`.

Buttons: `--prs-fw-button-lg|md|sm` (all 600), `--prs-fz-button-*`, `--prs-fl-button-*`.

Base: `--prs-fz-base: 16`, `--prs-fl-base: 1.5`.

`.prose` (in styles.css) restyles markdown-like HTML. Variables `--prs-type-body`, `--prs-type-headings`, `--prs-type-links`, etc. Opt out with `not-prose`.

## Shape and elevation

- `--prs-border-btn: 1px`
- `--prs-radius-alert: 0.25rem`
- `--prs-radius-badge: 1.9rem`
- `--prs-radius-box: 0.75rem`
- `--prs-radius-btn: 0.1875rem`
- `--prs-radius-input: 0` (square inputs on purpose)
- Shadows: `--prs-shadow-sm|md|lg|xl|2xl`

## Motion

Component transitions use:

- `--prs-transition-property` (long list of animatable props)
- `--prs-transition-timing` → `var(--prs-ease-in-out)` (prefer `--prs-ease-in-out` if you set this yourself)
- `--prs-transition-duration` → `var(--prs-duration-180ms)` (prefer `--prs-duration-180ms` or `-260ms`)

Named animations: `--prs-animation-fade-in`, `-fade-in-bloom`, `-fade-out`, `-fade-out-bloom`, `-scale-up`, `-scale-down`, `-slide-out-up|down|right|left`, `-slide-in-up|down|right|left`, `-shake-x|y|z`, `-spin`, `-ping`, `-blink`, `-float`, `-bounce`, `-pulse`.

Apply via utility classes `prs-animation-fade-in`, `prs-duration-300ms`, `prs-ease-out`, etc.

Durations: `0ms`, `80ms`, `100ms`, `120ms`, `180ms`, `260ms`, `300ms`, `320ms`, `420ms`, `500ms`, `1s`, `3s`, `5s`, `10s`, `15s`.

Eases: `base`, `in`, `out`, `in-out`, `elastic-*`, `step`, `spring`, `bounce`, plus circ/cubic/expo/quad/quart/quint/sine variants.

## Component-specific tokens

- `--prs-input-size`
- `--prs-cal-width: 15.875rem`, `--prs-cal-radius`, `--prs-cal-day-radius`, `--prs-cal-day-selected`
- `--prs-dialog-backdrop: 0 0 0 / 0.5` (used as `rgb(var(--prs-dialog-backdrop))`)

## How to consume

```css
.my-panel {
  padding: var(--prs-size-6);
  border: 1px solid var(--prs-c-gray-300);
  border-radius: var(--prs-radius-box);
  background: var(--prs-c-white);
  color: var(--prs-c-gray-900);
  font-family: var(--prs-ff-sans);
  box-shadow: var(--prs-shadow-md);
}
```

In Tailwind v4 you may also write `text-(--prs-c-primary)` or `bg-(--prs-c-gray-50)` when a utility is more convenient than a custom rule.

---

## Markup examples & accessibility notes

Full per-component markup examples (Alpine.js), classes, and accessibility notes live in this skill's bundled `references/llms.md` file — read it directly from disk (it ships alongside this `SKILL.md` in the same skill folder). Do not fetch it from a URL; use the local copy.

---

## Starter document

Full starter HTML document (CDN links, Tailwind theme bridge, Alpine + Iconify, one alert and one card as a live example):

```html
<!DOCTYPE html>
<html lang="en" x-data>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Prose UI</title>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&family=JetBrains+Mono:ital,wght@0,400..700;1,400..700&family=Literata:ital,opsz,wght@0,7..72,200..900;1,7..72,200..900&display=swap" />
    
    <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
    
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/prose-ui/prose-ui@latest/tokens.css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/prose-ui/prose-ui@latest/styles.css" />

    <style type="text/tailwindcss">
      @theme {
        /* fonts */
        --font-sans: var(--prs-ff-sans);
        --font-serif: var(--prs-ff-serif);
        --font-mono: var(--prs-ff-mono);
      }
    </style>
  </head>
  <body class="min-h-dvh">

    <header class="mx-auto p-6 max-w-3xl flex flex-col gap-2">
      <p class="text-sm font-semibold text-(--prs-c-primary)">Prose UI</p>
      <h1 class="text-3xl font-semibold">Page title</h1>
      <p class="text-(--prs-c-gray-600)">Replace this boilerplate with the requested interface.</p>
    </header>

    <main class="mx-auto px-6 max-w-3xl flex flex-col gap-6">

      <div class="prs-alert prs-alert-info" role="status">
        <div class="icon" aria-hidden="true">
          <iconify-icon icon="mdi:information-outline"></iconify-icon>
        </div>
        <div class="prs-alert-content">Status message slot.</div>
      </div>

      <article class="prs-card prs-card-advanced prs-card-bordered">
        <header>
          <h2>Card title</h2>
        </header>
        <div class="prs-card-body">
          <p>Card body. Use Prose components inside. Use Tailwind only for layout.</p>
          <div class="flex flex-wrap gap-2">
            <button type="button" class="prs-btn prs-btn-primary">Primary</button>
            <button type="button" class="prs-btn prs-btn-secondary">Secondary</button>
            <button type="button" class="prs-btn prs-btn-tertiary">Tertiary</button>
          </div>
        </div>
      </article>

    </main>

    <script type="module">
      // icons
      import 'https://esm.run/iconify-icon';

      // alpine
      import Alpine from 'https://esm.run/alpinejs';
      window.Alpine = Alpine;
      Alpine.start();
    </script>

  </body>
</html>
```
