You'll need an npm account and your username or org name to publish your brand package.
Full setup from scratch.
Install the CLI, create a project, copy the starter tokens, scaffold your brand package, publish.
Step 1 — Install the CLI globally
Run this once. Makes the underlith command available in any terminal session.
npm install -g @mikaelcarrara/underlithStep 2 — Create a project and install the package
The local install gives you access to the starter tokens file.
mkdir my-project
cd my-project
npm init -y
npm install @mikaelcarrara/underlithStep 3 — Copy the starter tokens file
This is the file Underlith reads to scaffold your brand package. Edit brand token values before the next step.
cp node_modules/@mikaelcarrara/underlith/src/underlith.tokens.css .Step 4 — Scaffold your brand package
Use your npm username as --org. Underlith reads your tokens file, separates brand tokens from framework primitives, and generates a ready-to-publish package. Follow the prompts to name your package.
underlith brand init --org your-npm-usernameWhen complete, the command shows the exact output path — use that in the next step.
Next steps:
1. cd ./@your-npm-username/your-package-name
2. Review your-npm-username.brand.css
3. npm publish --access publicStep 5 — Review and publish
Use the exact path the command printed — don't guess it.
cd ./@your-npm-username/your-package-name
npm publish --access publicStep 6 — Consume in any project
From this point, every product your org builds installs the package and inherits your brand decisions automatically.
npm install @your-npm-username/your-package-name@import "@your-npm-username/your-package-name/your-npm-username.brand.css";
color: var(--ul-color-brand-primary);
gap: var(--ul-space-4);
border-radius: var(--ul-radius-md);Change a token value, publish a new version, and every product updates on the next npm update.
Migrate without rewrites.
Map your existing variables to Underlith tokens, extract your brand layer, publish. Components keep working exactly as before.
Step 1 — Install the CLI globally
Run this once. Makes the underlith command available in any terminal session.
npm install -g @mikaelcarrara/underlithStep 2 — Install the package locally
npm install @mikaelcarrara/underlithStep 3 — Map existing variables
Point the script at your globals.css. It maps every CSS variable to an Underlith token without touching a single component.
underlith init --shadcn --globals ./styles/globals.cssBefore and after — zero component changes:
/* Before */
--primary: oklch(0.205 0 0);
/* After */
--primary: var(--ul-primary);Step 4 — Copy the tokens file if not present
cp node_modules/@mikaelcarrara/underlith/src/underlith.tokens.css .Step 5 — Scaffold your brand package
Use your npm username as --org. The command prints the exact output path — use that for cd.
underlith brand init --org your-npm-usernameStep 6 — Review and publish
cd ./@your-npm-username/your-package-name
npm publish --access publicStep 7 — Consume in any project
Every product your org builds installs the package and inherits your brand decisions automatically.
npm install @your-npm-username/your-package-name@import "@your-npm-username/your-package-name/your-npm-username.brand.css";
color: var(--ul-color-brand-primary);
gap: var(--ul-space-4);
border-radius: var(--ul-radius-md);Consumption examples
Underlith defines what. Consumers decide how.
Plain CSS
.card {
padding: var(--ul-space-4);
background: var(--ul-color-surface);
color: var(--ul-color-text-primary);
border-radius: var(--ul-radius-md);
}Tailwind v4
Map tokens into Tailwind's theme layer using @theme. Utilities will reference var(--token) directly.
/* app.css */
@import "tailwindcss";
@theme {
--color-surface: var(--ul-color-surface);
--color-brand: var(--ul-color-brand-primary);
--color-brand-hover: var(--ul-color-brand-hover);
--color-foreground: var(--ul-color-text-primary);
--color-inverse: var(--ul-color-text-inverse);
--spacing-2: var(--ul-space-2);
--spacing-4: var(--ul-space-4);
--radius-sm: var(--ul-radius-sm);
--radius-md: var(--ul-radius-md);
--shadow-sm: var(--ul-shadow-sm);
}<div class="rounded-md p-4 bg-surface text-foreground shadow-sm">
<h3 class="text-lg font-semibold mb-2">Card</h3>
<p>Powered by tokens via Tailwind v4.</p>
<button class="mt-2 px-4 py-2 rounded-sm bg-brand text-inverse hover:bg-brand-hover">
Action
</button>
</div>Sass / Less
Preprocessors do not transform tokens. They only organize consumption.
.card {
padding: var(--ul-space-4);
background: var(--ul-color-surface);
border-radius: var(--ul-radius-md);
}CSS-in-JS
// styled-components / emotion
const Card = styled.div`
padding: var(--ul-space-4);
background: var(--ul-color-surface);
border-radius: var(--ul-radius-md);
`;// vanilla-extract
export const card = style({
padding: 'var(--ul-space-4)',
background: 'var(--ul-color-surface)',
borderRadius: 'var(--ul-radius-md)',
});Motion
.button {
transition: background var(--ul-duration-fast) var(--ul-ease-inout);
}
.skeleton {
animation: var(--ul-motion-skeleton);
}// CSS-in-JS
const Button = styled.button`
transition: background var(--ul-duration-fast) var(--ul-ease-inout);
`;
const Skeleton = styled.div`
animation: var(--ul-motion-skeleton);
`;AI
Generate a Card component using only:
- ul-space-4
- ul-color-surface
- ul-radius-md
Do not introduce hardcoded values.
Output plain CSS..card {
padding: var(--ul-space-4);
background: var(--ul-color-surface);
border-radius: var(--ul-radius-md);
}JSON / JavaScript
For JavaScript-based tools, linters, or CSS-in-JS environments that need raw values, import the generated JSON directly.
import tokens from '../tokens.json';
const Card = styled.div`
background: ${tokens.color.surface.value};
padding: ${tokens.space[4].value};
`;