Get started with norcel.
A 5-minute walkthrough. From zero to a working page using every primitive in the kit — with the same dark-first tokens, components, and patterns you see on the marketing site.
What is norcel?
A dark-only design system + component library. 34 components, every token exposed as a CSS custom property, every variant typed via CVA. Built so that the marketing site you're looking at is built from the same primitives you'll use.
34 components
Buttons, inputs, dialogs, tables, charts, command palette — every primitive you need.
Dark by default
Every token is dark-first. Light is opt-in. Polarity-flip when needed.
Zero runtime deps
Built on Radix + CVA. Tree-shakeable. ~12 KB gzipped baseline.
5-minute quickstart
The fastest path from an empty repo to a running page. Run one command, edit one file, ship.
Bootstrap a new project
Create a new Next.js 16 + Tailwind v4 app. norcel works with any React 19 framework, but the demo assumes Next.js.
npx create-next-app@latest my-app \
--typescript \
--tailwind \
--app \
--src-dir \
--import-alias "@/*" \
--use-npm
cd my-appRun the norcel init
Drops the cn() utility, the components.json config, the Tailwind v4 @theme block, and the design tokens into your project. Run this ONCE per project.
npx shadcn@latest add https://ui.norcel.dev/r/initsrc/lib/utils.ts, components.json, src/app/theme.css, and it appends the design tokens to your globals.css. Plus 3 npm deps: clsx, tailwind-merge, radix-ui.Add components
norcel ships as a set of copy-paste components (not a runtime dependency). The fastest way to grab them is the shadcn registry.
npx shadcn@latest add https://ui.norcel.dev/r/button
npx shadcn@latest add https://ui.norcel.dev/r/card
npx shadcn@latest add https://ui.norcel.dev/r/input
npx shadcn@latest add https://ui.norcel.dev/r/badge
npx shadcn@latest add https://ui.norcel.dev/r/dialogsrc/components/ui/. You can copy any one of them into your project without the registry. See the component catalog for the full list.Verify the install
Open src/components/ui/button.tsx in your editor. You should see the full norcel Button source — about 100 lines, importing from @radix-ui/react-slot, class-variance-authority, and @/lib/utils.
cat src/components/ui/button.tsx | head -10
# Output:
# "use client";
#
# import * as React from "react";
# import { Slot } from "@radix-ui/react-slot";
# import { cva, type VariantProps } from "class-variance-authority";
# import { cn } from "@/lib/utils";If you see the imports + the buttonVariants = cva(...) call, the install worked. If you see a 404 from the registry, check that you ran npm run dev first (or that you deployed with the prebuild hook — see Step 8).
Wrap your app in a dark-first shell
Set `color-scheme: dark` on `<html>` so form controls, scrollbars, and native inputs render in dark. Optional: add the theme provider if you plan to support light mode later.
import type { Metadata, Viewport } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "My App — Built with norcel",
};
export const viewport: Viewport = {
themeColor: "#0a0a0a",
colorScheme: "dark",
};
export default function RootLayout({ children }) {
return (
<html lang="en" className="dark" style={{ colorScheme: "dark" }}>
<body className="bg-canvas text-ink">{children}</body>
</html>
);
}Compose your first page
Mix marketing + primitives the way the home page does. Each section is a documented component you can copy-paste.
import { Button } from "@/components/ui/button";
import {
Card, CardHeader, CardTitle, CardDescription, CardContent,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
export default function Home() {
return (
<main className="mx-auto max-w-3xl p-12">
<Badge>v1.0 — live</Badge>
<h1 className="mt-6 text-display-xl text-ink">
Build and ship.
</h1>
<p className="mt-4 text-body-lg text-body">
Welcome to your new norcel app.
</p>
<div className="mt-8 flex gap-3">
<Button size="lg" shape="pill" variant="primary">
Get started
</Button>
<Button size="lg" shape="pill" variant="secondary">
View docs
</Button>
</div>
<Card className="mt-12">
<CardHeader>
<CardTitle>Hello, world.</CardTitle>
<CardDescription>
The same primitives, the same tokens, your project.
</CardDescription>
</CardHeader>
<CardContent>
You just rendered your first card.
</CardContent>
</Card>
</main>
);
}Run npm run dev and open http://localhost:3000. You should see a dark hero with a white-pill primary button and a card beneath it — built entirely from the kit.
Installation in detail
Three ways to bring norcel into your project. Pick the one that matches your workflow.
Via shadcn registry
Run `npx shadcn@latest add <url>` and the file lands in your project. Updates are pull-based.
npx shadcn@latest add https://ui.norcel.dev/r/buttonCopy a single file
Open the source on GitHub, copy `button.tsx` into `src/components/ui/`, and import it. Zero tooling.
cp button.tsx src/components/ui/button.tsxVendor the whole kit
Copy the entire `src/components/ui/`, `src/lib/`, and tokens into your repo. You now own it.
git clone https://github.com/norcel/ui.gitWhere things live
A typical norcel project. The shape is opinionated; the names are not.
my-app/
├── app/
│ ├── (auth)/ # auth routes (sign-in, sign-up, …)
│ ├── (marketing)/ # marketing routes (home, pricing, blog)
│ ├── dashboard/ # authenticated app routes
│ ├── layout.tsx # root layout (html, body, providers)
│ ├── page.tsx # home / landing
│ └── globals.css # tokens + base layer
│
├── src/
│ ├── components/
│ │ ├── ui/ # 60+ primitives (button, card, …)
│ │ └── marketing/ # hero, pricing, faq, footer, …
│ │
│ └── lib/
│ ├── utils.ts # cn() helper
│ └── theme.ts # design token registry (optional)
│
├── public/
├── package.json
└── tailwind.config.tsPrimitives
Every Button, Card, Input, etc. lives here. Each is a single file you can edit freely.
Marketing sections
Hero, Pricing, Testimonials, FAQ, Footer, LogoCloud. Pre-composed sections ready to drop into a page.
Tokens
Every color, spacing, radius, shadow is a CSS custom property. The single source of truth.
tsconfig + path aliases
Set up path aliases once. The kit uses `@/components/*` and `@/lib/*` everywhere.
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"strict": true,
"jsx": "preserve"
}
}If you scaffolded with create-next-app --import-alias "@/*", this is already configured. You can skip this step.
Customizing the tokens
The fastest way to re-skin norcel. Edit one CSS variable, the whole system updates.
1. Override a color
Drop a new value into globals.css and every component that uses the token updates.
:root {
/* Re-skin the whole system to indigo */
--color-accent: #818cf8;
--color-fs-primary: #ffffff; /* keeps the white pill */
--color-link: #818cf8;
}2. Override a radius
Switch from rounded to square by setting --radius-pill to 0px.
:root {
/* Switch to a square look */
--radius-pill: 0px;
--radius-md: 0px;
--radius-lg: 0px;
--radius-xl: 0px;
}Adding components to a page
The mental model: marketing sections + primitives. Stack them like Lego.
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Hero } from "@/components/marketing/hero";
import { Features } from "@/components/marketing/features";
import { Testimonials } from "@/components/marketing/testimonials";
import { Faq } from "@/components/marketing/faq";
import { Cta } from "@/components/marketing/cta";
import { Footer } from "@/components/marketing/footer";
export default function Home() {
return (
<>
<Hero
eyebrow="// v1.0 — Live"
title="Build and ship."
subtitle="Your new site, using the same primitives as the marketing site."
primaryCta={{ label: "Get started", href: "/signup" }}
secondaryCta={{ label: "View pricing", href: "/pricing" }}
/>
<Features
title="Everything you need."
features={[...]}
/>
<Testimonials testimonials={[...]} />
<Faq items={[...]} />
<Cta
title="Ready to ship?"
primaryCta={{ label: "Get started", href: "/signup" }}
/>
<Footer />
</>
);
}Dark mode by default
norcel is dark-only. Here's how to keep it that way even when browsers / users try to opt in to light.
<html
lang="en"
className="dark" // Tailwind's dark variant
style={{ colorScheme: "dark" }} // native form controls
>
<body className="bg-canvas text-ink">
{children}
</body>
</html>Native form controls
`color-scheme: dark` on `<html>` makes the browser paint date pickers, scrollbars, and input chrome in dark — matching your surface.
OS theme override
If the user has a light OS theme, your app still renders dark. norcel doesn't follow the OS — it asserts the dark canvas.
Ship it
norcel has no runtime server requirements. Deploy anywhere that runs Next.js, Astro, or a static host.
Vercel
Zero config. Push to Git, get a URL.
vercel --prodNetlify
Auto-detects Next.js. Edge functions ready.
netlify deploy --prodDocker
Standalone Next.js output. ~120 MB image.
docker build -t app . && docker run -p 3000:3000 appBuild something like this.
You have the tokens, the components, and the patterns. The home page, pricing, FAQ, testimonials, CTA — all of it is the kit, wired together. Now go wire your own.