A palette you can prove
Twelve steps per hue, generated in OKLCH. Text steps are solved for WCAG AA on every fill they can sit on — and verified across 1,152 generated scales in the test suite.
prfct is an open-code design system for React. A generated OKLCH palette with contrast solved at build time, 54 accessible components on Base UI, and tokens you own — for Next.js, Tailwind CSS and shadcn.
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/prfct.jsonSystematic by design
Every prfct component is composed from the same foundations. Change a foundation and the whole interface follows.
Twelve steps per hue, generated in OKLCH. Text steps are solved for WCAG AA on every fill they can sit on — and verified across 1,152 generated scales in the test suite.
Inter with optical sizing switches to its Display cut as text grows. Every OpenType feature survives self-hosting.
Physical springs simulated at build time and shipped as CSS linear() curves. Interruptible, and off with reduced motion.
// light.tokens.json — DTCG 2025.10
"brand": { "9": {
"$type": "color",
"$value": { "colorSpace": "oklch",
"components": [0.515, 0.24, 267],
"hex": "#2c4fee" } } }Defined once in TypeScript. Exported as CSS, Tailwind utilities and W3C design-token JSON for Figma and Tokens Studio.
Base UI primitives implement the ARIA patterns; prfct makes the accessible choice the default one.
Make it yours
Pick a brand color and prfct regenerates the whole scale for light and dark mode — re-solving every text step for contrast. This entire page is repainting as you choose.
Open code
Components are copied into your project with the shadcn CLI. Read them, change them, delete what you don’t need. Updates are a diff you review — never a breaking upgrade you’re forced into.
const buttonVariants = cva(base, {
variants: {
variant: {
default: "bevel bg-primary text-primary-foreground",
brand: "bevel bg-brand text-brand-foreground",
outline: "border border-border-strong bg-card",
ghost: "hover:bg-accent",
},
size: { xs, sm, default, lg, xl, icon },
},
})
// Loading keeps width and focus — no layout shift.
<Button loading={saving}>Save changes</Button>Install the theme, add your first component, and ship an interface that is precise from the first commit.