DSNiemeyer
Language

Installation

Niemeyer ships as a private GitHub Packages npm package built around Tailwind 4. Six numbered steps below take you from a fresh app to a working <Button>.

1. Configure GitHub Packages access

Create a GitHub personal access token (classic) with read:packages and export it as GITHUB_TOKEN. Then add this .npmrc at the project root (Yarn Classic reads it directly; Yarn Berry users can set the equivalent npmScopes in .yarnrc.yml):

# .npmrc — at project root
@morada-ai:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

2. Install the package and peers

Tailwind 4 and @tabler/icons-react are peer dependencies — install them in your app so a single copy is hoisted next to the package.

# Install the package
yarn add @morada-ai/niemeyer

# Plus the peer dependencies (one copy in your app)
yarn add tailwindcss@^4
yarn add @tabler/icons-react

3. Configure PostCSS

Tailwind 4 ships its PostCSS plugin separately. Add it to your config (no autoprefixer needed):

// postcss.config.mjs
export default {
    plugins: {
        "@tailwindcss/postcss": {},
    },
};

4. Wire the stylesheet

Three explicit @imports in app/globals.css, in this order. You only need one @source for your own app code — the package's theme.css already self-sources its components, and Tailwind 4 merges @source directives across all imported stylesheets.

/* app/globals.css */
@import "tailwindcss";
@import "@morada-ai/niemeyer/styles";
@import "tw-animate-css";

/* Only your app code — the package self-sources its own components */
@source "./app/**/*.{ts,tsx}";

Order matters: tailwindcss must come first so the @theme blocks shipped inside the package are picked up.

5. Load fonts

Niemeyer expects two CSS variables: --font-heading (Outfit) and --font-body (Lato). Easiest path is next/font:

// app/layout.tsx
import { Outfit, Lato } from "next/font/google";

const outfit = Outfit({
    subsets: ["latin"],
    variable: "--font-heading",
});

const lato = Lato({
    subsets: ["latin"],
    weight: ["300", "400", "700", "900"],
    variable: "--font-body",
});

export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {
    return (
        <html className={`${outfit.variable} ${lato.variable}`}>
            <body>{children}</body>
        </html>
    );
}

6. Re-export `cn()` so consumer code can import it locally

The package ships the same cn pattern as shadcn (clsx + tailwind-merge). Both helpers are bundled, so a one-line re-export is enough:

// src/lib/utils.ts
export { cn } from "@morada-ai/niemeyer/utils";

Optional: enable the ESLint plugin

Catches design-system violations automatically (no hardcoded colors in className="…", no lucide-react imports, no native <button>/<input>):

// eslint.config.js
const niemeyer = require("@morada-ai/niemeyer/eslint");

module.exports = [niemeyer.configs.recommended];

Themes

Two color schemes (gray default, black high-contrast), each with light and dark mode. Toggle by setting one of these classes on the root element:

Root classResulting theme
(none) or .theme-grayGray — light (default)
.theme-gray.darkGray — dark
.theme-blackBlack — light (high contrast)
.theme-black.darkBlack — dark