Clinia UI

Using AI tools

How to set up Claude Code and Claude Design so the apps and sites they build follow Clinia's conventions.

DraftUnder review. It may change before sign-off.

AI agents can be leveraged to build with Clinia's conventions. This page shows what to give them, and how to set them up in Claude Code and Claude Design.

What your agent needs

SourceWhat it holdsUse it for
DESIGN.mdEvery page and convention from this site in one file, with the tokens and each component's rules. It's compiled from the content of this website, so it never drifts from it. It opens with the tokens as YAML, which tools that check code against DESIGN.md readContext on every task, and checking code against the tokens
/llms.txtThe index of every page on this site as MarkdownDetails on one topic
/llms-full.txtEvery page, docs and components, in fullAgents that want everything at once
/docs/<page>.md, /components/<name>.mdOne page as Markdown, like /docs/typography.mdReading a page without the site
The @clinia registryEvery component as source, already on Clinia's tokensBuilding the interface

Claude Code

1. Set up the project

Once the installation is completed from following either the App getting started or the Marketing getting started, the project has access to all the components and styles it needs to render accurately.

2. Add DESIGN.md

This file tells any agent working in your project what Clinia's brand guidelines are and how to leverage the installed components. Save it at the root of your repository:

bash
curl -o DESIGN.md https://ui.clinia.dev/DESIGN.md

Run the same command again when you update Clinia UI, so the agent reads the current rules. Keep DESIGN.md out of your formatter, such as in .prettierignore: a reformatted copy no longer matches the published file.

3. Describe the product in PRODUCT.md

DESIGN.md is the same in every repository: it says how Clinia looks. PRODUCT.md says what this product is, so the agent designs for the user who will actually use it. Save it at the root, next to DESIGN.md, and fill it in once:

md
# Product
 
## Users
 
Who uses it, and what they already know.
 
## What they do here
 
The main tasks, in the user's words, most frequent first.
 
## Where and when
 
Device, setting and pace: a clinician between patients, an analyst at a desk.
 
## Constraints
 
Languages (English, French), compliance, data the screens must never show, browsers to support.
 
## Evidence
 
What the agent may cite: real metrics, customers, approved claims. Anything else stays out of the interface.

The surface, App or Marketing, isn't in the file: the style in components.json already says it. Update PRODUCT.md when the product changes, not with each feature.

4. Point CLAUDE.md to it

Add this section to your CLAUDE.md to guide Claude to the design principles, and uncomment the line for your surface:

md
## Design system
 
<!-- Product app: This app uses Clinia UI (https://ui.clinia.dev) with shadcn's base-luma style. -->
<!-- Marketing site: This site uses Clinia UI (https://ui.clinia.dev) with shadcn's base-sera style. -->
 
- Read DESIGN.md before you build or change any interface, and PRODUCT.md for who uses it and why.
- For one topic in detail, read its page as Markdown. https://ui.clinia.dev/llms.txt lists them all.
- Add components from the Clinia registry (`pnpm dlx shadcn@latest add @clinia/<name>`), never from upstream shadcn.
- Style with semantic tokens (`bg-primary`, `text-muted-foreground`), never raw hex values.
- Write interface copy with the clinia-brand-voice skill.

The clinia-brand-voice skill is one of Clinia's enterprise skills, available to everyone by default.

5. Check every edit

DESIGN.md tells the agent the rules. The lint rules catch it when it breaks one. Install them with @clinia/eslint, then add this hook to .claude/settings.json. After each edit, it lints the file the agent changed and hands any error back, so the agent fixes it in the same turn:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "f=$(jq -r '.tool_input.file_path // empty'); case \"$f\" in *.js|*.jsx|*.ts|*.tsx|*.mjs) npx eslint --no-warn-ignored \"$f\" >&2 || exit 2 ;; esac"
          }
        ]
      }
    ]
  }
}

The hook needs jq (brew install jq). Commit the file, so everyone working in the repository gets the same checks.

6. Add the clinia-ui skill

The skill gives the agent four jobs to run on request: set up a repository, build or change a screen, audit one, and migrate an app. It reads the rules from DESIGN.md and this site, so it stays current with them. Install it in the repository:

bash
npx skills add clinia/ui --skill clinia-ui

The command needs access to the clinia/ui repository on GitHub. Without it, download the skill from this site:

bash
curl --create-dirs -o .claude/skills/clinia-ui/SKILL.md https://ui.clinia.dev/skills/clinia-ui/SKILL.md

Then ask in plain words, such as "audit the settings screen" or "set this repository up for Clinia UI".

Migrate an existing app

An agent can move an app that doesn't follow Clinia UI yet onto it, without changing what the app does. It audits the app, proposes a plan as a series of small pull requests (or one pull request with a commit per step), then makes one change at a time and proves each one: same tests, same screens, same keyboard behaviour. It stops for your approval after the audit, after the plan, and before opening any pull request.

Have these ready before you start:

  • The repository where changes are made. Not a mirror or an export of another repository: changes there are overwritten.
  • What it takes to run the app and reach every screen: credentials, environment variables, and seed data. A static site needs none of these. The agent asks for them in the audit, and stops rather than guessing.
  • Whether the repository is public. Clinia's brand fonts are licensed and can't be committed to a public repository.

Open Claude Code in the app's repository and give it this prompt. With the clinia-ui skill installed, "migrate this app to Clinia UI" runs the same prompt:

text
Migrate this app to Clinia UI, Clinia's design system, without changing what the app does.
 
## Sources
 
- https://ui.clinia.dev/DESIGN.md is the whole design system for agents. Every docs page also has a Markdown version: add `.md` to its URL. Read DESIGN.md first, then /docs/migration.md, /docs/installation.md, and the Breaking sections of /docs/changelog.md.
- Read this repo's AGENTS.md, CLAUDE.md, README, CONTRIBUTING, and convention docs before planning. Where they disagree with Clinia UI, list the disagreement; don't pick a side. That includes rules that keep a host such as `ui.clinia.dev` out of a public tree, which the setup files name. A DESIGN.md already in the repo is an older copy: it's replaced in the first PR, so never read it as a source or list its rules as disagreements.
- Before Phase 1, check what the install needs and ask for everything missing in one list: the seven font files (/docs/migration.md, step 4), and where this repo keeps its environment variables.
 
## The rule
 
Behaviour doesn't change. Same routes, data and API calls, states, validation, form values, keyboard and focus behaviour, screen-reader names, copy, analytics events, and test ids. Only how the app looks, and which components render it, change.
 
When the two conflict, take Clinia's visuals and keep the app's behaviour. Put every conflict in a decision table (what, current, Clinia, your proposal) and wait for my answer. Never resolve one silently. DESIGN.md conventions that change behaviour are conflicts too, such as an Alert Dialog for destructive actions, a row menu in place of a button on every row, or verb-first labels: put each in the decision table. So is a Clinia component that brings behaviour the app doesn't have, such as Sidebar collapsing to icons; adopting only its look is not a conflict.
 
Defects you find are the exception that proves the rule: accessibility defects, analytics events that never fire, broken links. Fixing one changes behaviour on purpose. List them, and fix each only as its own flagged change once I've approved it.
 
## Scope
 
The app's UI code only. Leave out email templates, server code, scripts, a docs site beside the app, and anything else that isn't the app's interface, and confirm that boundary with me in Phase 1. When the docs site is the app, migrate it: /docs/migration.md covers sites on Fumadocs. Don't upgrade, refactor, or tidy anything the migration doesn't need; the `@clinia/tailwindcss` version the theme requires is part of the migration. The migration includes the logo, the favicon and the app icons, and replacing hand-built patterns (in an app: navigation, list rows, loading and empty states, form fields, status pills; on a marketing site: the header, CTAs, logo rows, compliance badges, line accents and the product lockup) with the Clinia components that do the same job (/docs/migration.md, steps 10 and 11). That isn't tidying. Don't rewrite copy. Clinia's content guidelines don't apply to app copy.
 
If the repository is public or open source, don't commit Clinia's licensed fonts or brand files to it. Ask me how the public build should handle them.
 
## Phase 1: audit (read only)
 
First, confirm this repository is where changes are made. Check the README, CONTRIBUTING, commit authors, and any "generated" or "exported" headers. If it's a mirror or an export of another repository, stop and ask me for the source.
 
Then report what the plan depends on, with counts and file paths (put long lists in an appendix):
- Where the app starts: Tailwind version, the `@clinia/tailwindcss` version if any, the shadcn style and primitives (Radix or Base UI), component and icon libraries, fonts and weights (every font file outside the seven in /docs/migration.md, step 4, is deleted), and the themes a user can actually reach (a `dark:` class nothing switches on doesn't count). If the app is live, request each font URL its CSS declares: a font that returns 404 renders in a fallback nobody notices.
- If the primitives are Radix: every Radix-specific API you'll have to convert (`asChild`, data attributes, portals, controlled open state), with counts. Clinia's components are built on Base UI, and this conversion is where behaviour is most at risk. If they're already Base UI, report the drift from Clinia's style instead: local variants, sizes, and overrides.
- Every UI component and pattern, mapped to its `@clinia` registry item (https://ui.clinia.dev/r/registry.json) as an equivalent, a replacement that behaves differently (put the differences in the decision table), or none.
- Every pattern the app builds by hand, per screen: navigation, list rows, loading and empty states, form fields, status pills, `div` or `span` elements with `role="button"`, and logo images; on a marketing site, the header, hero and section CTAs, logo rows, compliance blocks, closing CTA bands, line accents and the lockup. Name the Clinia component that replaces each (/docs/migration.md, step 11, has a table for each surface). A hand-built pattern with a Clinia equivalent is not "none".
- The brand assets: every logo use, the favicon, and the app and manifest icons. On a marketing site, the compliance badges and their captions too, compared with the files and approved lines on /docs/marketing/compliance.md.
- Values that aren't Clinia tokens (raw colours, arbitrary sizes, radii, shadows, or font weights), with the token each becomes. A Clinia swatch used without its token counts too: `#cb6a3d` is Peach-4, a chart colour, and passing a palette check doesn't make it an accent. Count live and dead selectors apart: only selectors and classes the code renders are migration work, and dead ones go in cleanup. Search the code for them: the `@clinia/eslint` rules (/docs/migration.md, step 8) aren't installed yet, and once they are, they catch arbitrary text sizes, radii, hex colours in classes, font weights, and `--clinia-*` in JavaScript and TypeScript, but not arbitrary `leading` or `tracking`, `rgba()` in classes, or anything in CSS files.
- Product app (`base-luma`) or marketing site (`base-sera`). A help or support site uses `base-sera` with App conventions. With no `components.json`, propose one and put it in the decision table.
- How behaviour is protected today: unit, DOM, and end-to-end tests, test ids, and visual checks.
- Analytics: the tool, every event the app sends, and where each fires. With Vercel Analytics, an event tracked in an effect on page load is dropped unless the page starts Vercel's queue first (/docs/marketing/seo.md): list an event that never fires as a defect.
- What it takes to run the app and reach every screen: credentials, environment variables, services, and seed data. List what you need from me now.
- Accessibility defects: run axe (`@axe-core/playwright`) on every screen you can reach, and add any you see on the way. Triage axe's `incomplete` results by hand too: a contrast failure can show up only there. If the app's Content-Security-Policy blocks axe, open the page with `bypassCSP: true`. A cookie banner built on vanilla-cookieconsent hides itself from automated browsers: launch Chromium with `--disable-blink-features=AutomationControlled`, or axe and the screenshots never see it. List defects in a third-party library (a docs framework, a playground) on their own, as upstream issues.
 
## Phase 2: plan
 
A sequence of small PRs, each leaving the app working. If I want one PR, plan one commit per step instead, each leaving the app working: lint, typecheck, build, and tests pass on every commit. As a starting point, not a template: DESIGN.md and PRODUCT.md first if the repo lacks them (/docs/ai-tools.md, Claude Code steps 2 and 3), then registry, theme and the `icons` item (`sonner`, `dialog` and other components install `icons` as a dependency), then the style switch with the simplest components (`components.json` style is global, so it can't be split per component), then one component family per PR, then the icon swap in the app's own code, then the brand assets, then one PR per hand-built pattern or screen to align it with App or Marketing conventions, by surface, then cleanup (add the `@clinia/eslint` rules, remove unused libraries, and update the repo's own docs and agent files that describe the UI stack). For each PR: what changes, what could break, how you'll prove it didn't. Ask me which branch the PRs target, `main` or an integration branch, and how far `main` may move while they're open.
 
Components Clinia doesn't have in its registry stay as they are, restyled with Clinia tokens, and go on a list for https://ui.clinia.dev/docs/request-a-component.
 
Stop and wait for my OK on the audit, the decision table, and the plan.
 
## Phase 3: one PR at a time
 
In one-PR mode, read each PR below as a commit: the baseline is the previous commit, and every commit gets the same proof.
 
1. Capture the baseline, from the head of the previous PR: the test results, and screenshots of every screen the PR touches, at 1440, 1024, 800, and 390 pixels wide, in every theme a user can reach. A Playwright script for those screenshots is in scope; keep it in the repo so later PRs reuse it, and keep the images out of git. Make it deterministic: if the app is static, build once and serve it; otherwise the same seed data, seeded so a second run doesn't fail; dates, times and live values replaced with fixed placeholders once the page has settled; external services blanked; animations waited out. Give it its own port and database, keep it out of the default test run, and keep one screenshot folder per PR head. Run it twice and confirm the images match before you rely on it; in ARIA snapshots, mask names that change from run to run, such as a progress value. Capture the cookie banner too, with the launch flag from the axe step in Phase 1. If the app is live, compare the first baseline with it once: when they match, later PRs compare with the previous PR's head only. List any state you can't capture reliably.
2. Make the change. Install with `CI=1 pnpm dlx shadcn@latest add @clinia/<name> --yes --overwrite`, or with the repo's own package manager's runner (`CI=1 npx shadcn@latest add …` with npm): without `CI=1` and `--overwrite`, the CLI waits for an answer. Then check that the lockfile is still the repo's own, with no `pnpm-lock.yaml` appearing beside `package-lock.json`. Adding `@clinia/theme` replaces `globals.css`, so put back anything else the app had there.
3. Prove behaviour didn't change: lint, typecheck, and build pass, the existing tests pass, tests are added where a changed component had none, a Clinia component that replaces a custom one gets a test that passes on both versions, `cn()` keeps the type scale's sizes (/docs/migration.md, step 6), the screenshots are compared with the baseline and read, not only diffed, against the screen's App or Marketing conventions page, a script checks at each width that no text is clipped or overflows its box (an element whose `scrollWidth` or `scrollHeight` exceeds its client size), because a screenshot diff misses a label that wraps or a chip that clips, the affected flows are walked with the keyboard, and the same analytics events fire with the same payloads. Check that last one on every link moved under a `render` prop: a wrapper that spreads its props after its own `onClick` loses that handler. With Vercel Analytics, read `window.vaq` locally, where the script returns 404 and events only queue, or intercept and abort the beacons on a deployment with a desktop Chrome user agent: the live script ignores HeadlessChrome. When the app has no tests, the screenshots and the keyboard walk are the proof: say so in the PR.
4. Prepare the branch and a PR description with the before and after screenshots and what you checked. Push and open the PR only when I say so.
 
If you can't install a package, authenticate, seed data, or reach a screen, stop and tell me what you need. Never invent a screenshot or a test result.

The steps it follows are on Migration, including what changes when an app comes from Radix.

Claude Design

Clinia's design system in Claude Design is built from Clinia UI's components and DESIGN.md. Start your project from it, and say in your prompt which surface you're designing for: a product app (Luma) or a marketing site (Sera).

For Figma files, FigJam boards and artifacts made outside Claude Design and outside a repository, the clinia-design-system skill applies the same rules. It's one of Clinia's enterprise skills, available to everyone by default; to add it yourself, download it and upload it under Skills in Claude Desktop's settings. It reads DESIGN.md live and hands decks, documents and code to the skill made for them.

Scenes, Animations and Illustrations

The skills that make illustrated scenes, composing-scenes and authoring-rive-scenes, ship in this repository. Open it in Claude Code to make a scene, then copy the approved result into your site. See Scenes and Rive scenes.

Do and Don't

Do

  • Refresh DESIGN.md with the curl command above when there is an update to Clinia UI guidelines.
  • Get anything an agent makes reviewed and approved before you publish it. Scenes, animations and illustrations need the marketing lead's approval.
  • Say which surface (App or Marketing) you're building for.

Don't

  • Edit DESIGN.md directly. It's compiled from this site: to change a rule, request a change.
  • Let an agent edit a component's colours or states to make a design fit. Request a component change instead.
  • Publish a scene, an animation or an illustration before it's approved.
  • Use real patient data in prompts or mock-ups. Use a fictional persona, such as one picked from our synthetic patient list.