Underlith logo Underlith

CLI Integration

Underlith is the infrastructure for your own source of truth.
Two commands for new projects. Three for existing ones.

You'll need an npm account and your username or org name to publish your brand package.


New project

Full setup from scratch.

Install the CLI, create a project, copy the starter tokens, scaffold your brand package, publish.

$ underlith brand init $ npm publish

Step 1 — Install the CLI globally

Run this once. Makes the underlith command available in any terminal session.

npm install -g @mikaelcarrara/underlith

Step 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/underlith

Step 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-username

When 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 public

Step 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 public

Step 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.


Existing project

Migrate without rewrites.

Map your existing variables to Underlith tokens, extract your brand layer, publish. Components keep working exactly as before.

$ underlith init --shadcn $ underlith brand init $ npm publish

Step 1 — Install the CLI globally

Run this once. Makes the underlith command available in any terminal session.

npm install -g @mikaelcarrara/underlith

Step 2 — Install the package locally

npm install @mikaelcarrara/underlith

Step 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.css

Before 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-username

Step 6 — Review and publish

cd ./@your-npm-username/your-package-name
npm publish --access public

Step 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};
`;