Getting started
Installation
Major is one package, @major/ui: the components and the tokens they are built on, versioned together. Works with Next.js (App Router and Server Components), Vite and any Tailwind v4 setup.
1. Install the package
It is published to the company's private npm registry under the @major scope.
.npmrc
@major:registry=https://npm.major.example/
//npm.major.example/:_authToken=${MAJOR_NPM_TOKEN}yarn add @major/ui lucide-react
# or: pnpm add @major/ui lucide-react- Keep the token in
MAJOR_NPM_TOKEN, in your shell and in CI; never commit it. - Needs React 19 and Tailwind v4.
lucide-reactis for your own icons: components take icons as elements.
2. Load the styles
One import: the tokens (light and dark), the Tailwind theme, and a @source that lets your Tailwind generate the classes the components use.
app/globals.css
@import "tailwindcss";
@import "@major/ui/tailwind.css";The theme resets Tailwind's palette and text sizes: only Major values exist (bg-surface, text-ink, text-body, rounded-xl…), in the components and in your own markup.
3. Load the fonts
DM Sans and JetBrains Mono, from Google Fonts. In Next.js let next/font self-host them and point the tokens at them.
app/layout.tsx
import { DM_Sans, JetBrains_Mono } from "next/font/google";
import type { PropsWithChildren } from "react";
import { Toaster } from "@major/ui/sonner";
import { TooltipProvider } from "@major/ui/tooltip";
const sans = DM_Sans({ subsets: ["latin"], variable: "--font-dm-sans" });
const mono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-jetbrains-mono" });
export default function RootLayout({ children }: PropsWithChildren) {
return (
<html lang="en" className={`${sans.variable} ${mono.variable}`}>
<body>
<TooltipProvider>
{children}
<Toaster />
</TooltipProvider>
</body>
</html>
);
}app/globals.css
:root {
--font-sans: var(--font-dm-sans), "DM Sans", ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-jetbrains-mono), "JetBrains Mono", ui-monospace, monospace;
}4. Use a component
Every component has its own entry point; each page starts its API section with the import.
import { Button } from "@major/ui/button";
import { Plus } from "lucide-react";
export function AgentsHeader() {
return (
<Button>
<Plus />
Create agent
</Button>
);
}- Components are client components (
"use client"is in each file), so you can render them from Server Components.cn()and the tokens are not: use them on the server too. - Components are composed from parts, as in shadcn/ui:
Dialog+DialogContent+DialogTitle,Field+FieldLabel+Input. Icons go inchildrenlike any other content. - Primitives (Button, Dialog, Select…) carry no product meaning; composed components (PageHeader, DataTable, Trace…) are built from them for one recurring Major need. The sidebar lists them separately.
- Compose with
cn()from@major/ui/lib/cnto merge your classes; it knows Major's theme (it won't droptext-inkwhen you addtext-ui). - "Go to definition" opens the component's TypeScript source: the package ships declaration maps and sourcemaps with the sources inside.
When a component doesn't fit
A component that doesn't fit is either a bug in the system or a use it wasn't made for. Both are fixed in the system.
- First compose:
className,asChildand the exported variants (buttonVariants) cover most cases without changing the component. - If it is a bug, open an issue on the design system. To ship before the fix is released, patch the package with
yarn patch @major/ui(orpnpm patch) and link the issue in the patch commit. The patch stays visible in review and fails loudly when an upgrade changes the same lines. - Never copy a component into a product to change it.
Adding a stock shadcn/ui component
The theme defines shadcn's variable names (background, primary, border, ring…) mapped onto Major tokens, so components from the shadcn registry pick up Major's look. If a product needs one long term, propose it for @major/ui.
pnpm dlx shadcn@latest add hover-cardNote: in Major, accent is the call-to-action green and muted is a text colour. Replace shadcn's bg-accent (hover) with bg-hover and bg-muted with bg-secondary.