The item contract
tradecn items compose the consumer's @/components/ui/* exports so shadcn can change its styles or base without changes to tradecn's files. These rules define that boundary. scripts/validate-registry.ts and ESLint check the source; the browser matrix checks the installed components in radix-nova, base-mira, and base-vega.
Updating
Use --diff to review an update. Replace <item> with the item name and the example tag with the version you want:
pnpm dlx shadcn add "tradecn/ui/<item>#v0.2.0" --diff
pnpm dlx shadcn add table --diff
npx shadcn add "tradecn/ui/<item>#v0.2.0" --diff
npx shadcn add table --diff
yarn dlx shadcn add "tradecn/ui/<item>#v0.2.0" --diff
yarn dlx shadcn add table --diff
bunx --bun shadcn add "tradecn/ui/<item>#v0.2.0" --diff
bunx --bun shadcn add table --diff
The first reviews a tradecn item; the second reviews a shadcn built-in. tradecn bundles its own files and names shadcn built-ins as dependencies.
A bare --diff lists file changes but omits CSS changes, so a theme with no files shows no diff. Review stylesheet changes separately by running the command again with the tailwind.css path from your components.json, such as --diff src/index.css. This shows only the stylesheet diff. After reviewing both, remove --diff and its path to apply the update.
Files and imports
Item types follow their location.
registry:componentis not an item type.Location under registry/tradecn/Item type ui/*registry:uihooks/*registry:hooklib/*registry:libblocks/*registry:blockNo files registry:theme, for opt-in themes onlyBlock files may use
registry:block,registry:page,registry:component, orregistry:file. Theregistry:pageandregistry:filetypes require explicittargets.Files under
agents/are Markdown for a coding agent, bundled by the item that installs them asregistry:filewith an explicittarget. They are not source: the source rules below andjust tokensread.tsand.tsxfiles only.Bundle shared tradecn code in
files[], with a type per file. Never put another tradecn item inregistryDependencies: the GitHub install form does not pass#refto dependencies, so an item pinned tov0.1.0could pull siblings frommain. Shared files across items stay byte-identical and merge cleanly.registryDependenciesare bare shadcn built-in names. Each must be inregistry/tradecn/builtins.lock.json, with every imported symbol exported in all three locked styles.just lock-builtins-checkrefetches upstream; the nightly job runs that check.Use the supported import paths. Own-file imports must resolve to files in the item's
files[].Source Import form tradecn files @/registry/tradecn/{ui,hooks,lib}/xshadcn built-ins @/components/ui/xClass helper import { cn } from "cn"npm packages Declared in dependenciesandregistry/tradecn/deps.allow.jsonReact peers reactandreact-dom, without a dependency entryRelative imports,
@/lib/utils, and other registry aliases are forbidden. So are direct imports ofradix-ui,@radix-ui/*,@base-ui/*,@base-ui-components/*,cmdk, andreact-resizable-panels.Do not use
asChild,render, ornativeButtonin registry JSX. Style a shadcn trigger withclassNameor pass children. UseclassName="contents"when it must wrap a tradecn element.Declare dependencies as
name@^x.y.z, using the allowlist's exact range. Every declared dependency must be imported. Items do not usedevDependencies.
Tokens and CSS
Generate
cssVarswithjust tokens. It readsregistry/tradecn/tokens.jsonand includes the tokens an item's files use, their referenced tokens, and the accessible font pair when needed. For example, using--tradecn-font-numericalso installs--tradecn-font-sansbecause its value isvar(--tradecn-font-sans).Token Value Colors Literal oklch(...)orvar(--color-<shadcn token>); the CLI creates Tailwind color utilitiestradecn-font-*Font stacks; tradecn-font-weight-*holds weightstradecn-text-size-*px sizes tradecn-line-height-*Unitless line heights tradecn-numeric-variantlining-nums tabular-numsTypography tokens are written as-is into
:rootand.dark, without color aliases. Installing a UI item on its own preserves existing variables such as--up,--down, and--tradecn-font-sans. A theme enables variable overwrites for the whole install.Limit non-theme
cssto keyframes, third-party restyles, and the accessibility remap. Style elements, shadcn classes, and tradecn slots in source files.Allowed CSS Requirement @keyframes tradecn-*Prefix animation names with tradecn-@layer componentsSelectors start with .dockview-theme-tradecnor.dv-*; set the package's custom properties because its unlayered rules beat layered restylesAccessibility remap Generated by just tokensfor items that read a font tokenWorkspace's floating-container rule uses
!importantto replace Dockview's unlayered self-reference with inheritance. Set overlay z-index overrides on the theme root.The remap uses
:root[data-accessibility="hyperlegible"], [data-accessibility="hyperlegible"]to point--tradecn-font-sansand--tradecn-font-monoat the accessible pair. It works without a theme and stays unlayered so it can override the unlayered:roottokens on<html>.
Metadata and composition
- Require
title,description, andcategories. Also requiredocswhen an item hascss,cssVars, or a dependency beyondcn.meta.componentsmust name exports from the primary file. - Name the item after its primary file's basename. A
registry:uiorregistry:blockroot carriesdata-slot="tradecn-<name>". - Supply any provider a built-in needs in either base. An item using tooltips wraps itself in
<TooltipProvider>without configuration props, because the prop names differ by base. Radix requires the provider; Base UI does not. Export checks cannot catch this runtime difference, so components have smoke scenes in all three consumers. - Keep file basenames unique within an item, regardless of directory or extension. CLI 4.21.0 resolves own-file imports by basename, preferring
.tsxover.tsbefore matching the requested path. Pairinglib/sparkline.tswithui/sparkline.tsxcan make the installed component import itself, even though source and built JSON resolve correctly. The validator rejects this; the library is namedsparkline-geometry.ts.
Themes
A theme replaces the consumer's variables; its docs must say so first. Among tradecn item types, only
registry:themedoes this. It has no files and sets every tradecn token in bothcssVars.lightandcssVars.dark, so none retain values from the previous palette.Token values follow rule 7. A theme's
cssmust exactly matchTHEME_TYPOGRAPHY_CSSinscripts/lib/registry.ts: the numeric variant on:rootand.tradecn-num, [data-numeric]in@layer base, plus the accessibility remap. Other items cannot carry that base layer.just tokensleaves themes alone: deriving tokens from an empty file list would erase them. The validator rejects empty themes and missing tokens.
Typography
Any component rendering numeric data MUST set
font-variant-numeric: lining-nums tabular-numson the numeric node.Use
NUMERIC_CLASS,MONO_NUMERIC_CLASS, ornumericFontClass(convention)fromformat.tabular-numsalone replaces the whole property and drops lining figures; the validator rejects it withoutlining-numson the same source line.Read fonts through tokens, for example
font-(family-name:--tradecn-font-mono). Tailwind'sfont-mono,font-sans, andfont-serifbypass the consumer's--tradecn-font-*choices and are forbidden. Keep text at least 12 px, the floor named by--tradecn-text-size-grid-min. The validator rejects smaller arbitrary classes (text-[10px],text-[0.6rem]) and smallerfontSizevalues, in a style or as an attribute;text-xsis the smallest named size to use. SVG text takes its size in the viewBox's units, so a chart drawn at its own size labels atfontSize={12}or more.A compact style draws some built-ins under the floor. base-mira draws
Badge,Kbd,CommandShortcut,ContextMenuShortcut,DropdownMenuShortcut,Button size="xs", andNativeSelect size="sm"at 10 px, so settext-xsor larger on each one an item composes. A small select takes it as[&_select[data-size=sm]]:text-xs, because base-mira's rule sits on the select itself and outranks a plain[&_select]:text-xson the wrapper. A key inside anInputGroupAddontakes it on the addon as[&_kbd[data-slot=kbd]]:text-xs, because the addon's own rule for its keys outranks the key's class.scripts/typography.test.tsholds the registry, the demos, and the scenes to this.The browser matrix checks computed numeric styles in all three consumers, with and without each theme. It checks elements whose own text contains a digit and inputs containing digits under tradecn slots, plus every
[data-numeric]node. The site smoke checks every preview the same way.typography.mdexplains the choices.
Direction
Carry direction in more than hue. A value using
text-up,bg-down-soft,stroke-up, or another direction color also needs a sign fromformatSigned, adata-directionattribute, screen-reader text, or a visible label such as Buy. Red and green are not distinguishable to every reader, and their meanings reverse in some markets.Add each file that uses direction colors to the channel table in
scripts/color.test.ts, with evidence of its other channel. The test fails on an unlisted file. Every theme'sup/downpair and the items' default pair must also have a grayscale contrast ratio of at least 1.3:1 in both modes.
What a version means
The tag is the version. From v0.1.3 on, tradecn follows semver with no pre-1.0 exceptions.
| Release | Meaning |
|---|---|
Major (v1.0.0, v2.0.0) |
An installed file's API changes: a prop, export, hotkey, token name, or data-slot. Read ⚠ Breaking Changes in the release notes and use --diff before updating. |
| Minor | New items or backward-compatible props. Existing usage keeps working. |
| Patch | Files change without API changes: fixes, performance work, refactors, or cleanup. --diff shows the changes. |
A patch can contain only docs: tradecn.dev serves the docs from its release tag, so documentation changes need a release to appear. Such a tag has no installed-file diff. CI and test changes do not release. CONTRIBUTING.md explains how PR titles pick the bump.
This page is docs/contract.md at v2.0.0.