Every component follows your shadcn design system, all 8 styles, every base color and theme, your icon library, font and radius.
shadcn/ui projects are built from a preset: a style (Vega, Nova, Maia, Lyra, Mira, Luma, Sera or Rhea), a base color, a theme, an icon library, a font and a radius. UploadCN components follow all of it. Click Customize in the header to switch the whole site, or paste a preset code from ui.shadcn.com/create.
Styles
Styles change shape and density, not just color: Lyra is square and compact, Maia is round and roomy, Sera is editorial with uppercase headings. UploadCN works the same way shadcn/ui does:
- Parts are written with semantic classes,
cn-upload-dropzone,cn-upload-item,cn-upload-card… - Each style defines them, derived from the matching shadcn component: upload items from Item, dropzones from Empty, cards from Card, modals from Dialog, progress from Progress.
- The registry compiles them per style, so your files contain plain Tailwind classes, no runtime, no extra CSS.
Nova
rounded-lg text-sm gap-2.5 px-3 py-2.5Lyra
rounded-none text-xs gap-2.5 px-3 py-2.5Maia
rounded-2xl text-sm gap-3.5 px-4 py-3.5Sera
rounded-none uppercase tracking-wider headingsThe same upload item, compiled for four styles.
How the right style is installed
Register the namespace with a {style} URL (the default in
Installation):
{
"registries": {
"@uploadcn": "https://uploadcn.dev/r/{style}/{name}.json"
}
}The CLI replaces {style} with your project's style, like base-lyra or radix-maia,
and downloads that build. With a plain {name}.json URL you get Nova.
Icons
Components use IconPlaceholder, which names an icon in every library shadcn supports:
<IconPlaceholder
lucide="UploadIcon"
tabler="IconUpload"
hugeicons="Upload01Icon"
phosphor="UploadSimpleIcon"
remixicon="RiUploadLine"
/>shadcn add rewrites it to an import from your iconLibrary and installs that package,
the same mechanism shadcn/ui's own components use.
Colors, radius and fonts
Components only use theme tokens (primary, muted, border, destructive, chart-*…),
so base colors and themes just work. Radius comes from --radius (Lyra and Sera stay
square, like shadcn's own components there), and text inherits your font.
Components that need a dark surface, like
Upload Terminal, use a dark scope, so they show
your theme's dark palette rather than a fixed black.
Contrast in every theme
We scan the components with axe in every base color and theme, light and dark. All
pass WCAG AA except one property of two palettes: in light Olive and Mist,
shadcn's own --muted-foreground is 4.25:1 and 4.33:1 on white, just under 4.5:1,
for every component that uses it. If you need AA there, darken that one variable, e.g.
--muted-foreground: oklch(0.52 0.031 107.3).
Radix, Base UI and React Aria
shadcn ships on three primitive libraries whose components don't share an API, React
Aria's Button takes isDisabled and onPress, its Table is a collection, its
Dialog has different parts. UploadCN only relies on what's common:
- Buttons are native
<button>s styled with your ownbuttonVariants. - Dialogs use the native
<dialog>element; popovers the native Popover API. - Tabs and tables are plain, accessible HTML styled with your style's tokens.
- Inputs, badges, cards and fields come from your
components/ui.
Keeping it that way
Every registry item is checked with @shadcn/lint
(no-raw-colors, no-unknown-classes, no-arbitrary-values, require-static-classes),
compiled for all 8 styles, and installed into fresh Radix, Base UI and React Aria projects
by the test suite. You can run the same lint rules on your copy:
import { plugin as shadcn } from "@shadcn/lint"
export default [
{
files: ["components/**/*.tsx"],
plugins: { shadcn },
rules: {
"shadcn/no-raw-colors": "error",
"shadcn/no-unknown-classes": "error",
},
},
]