@lemon.ui/react
v0.3.1
Published
Lemon UI — an installable, retheme-able React design system (Tailwind v4 tokens + components).
Downloads
462
Readme
@lemon.ui/react
Lemon UI — an installable, retheme-able React design system. Tailwind v4 design tokens (three tiers: primitive → semantic → component) plus a full set of accessible components built on Radix.
Status: 0.x — unstable. Every export may change until 1.0. Only what is re-exported from the package entry is public API; anything else is an implementation detail. Breaking changes to the public API are recorded in CHANGELOG.md.
Install
npm install @lemon.ui/react react react-domreact and react-dom are peer dependencies (^18.3 || ^19) — the library
never bundles its own copy, so you never get the "invalid hook call" that two
Reacts cause.
Styling — pick ONE of two modes
A utility-class library is unstyled by default in a consuming app, because your CSS build doesn't know the package exists and purges every class the components use. Choose the entry that matches your app:
1. Batteries-included (no Tailwind in your app)
One import, fully compiled — Tailwind + tokens + every utility the components need:
import "@lemon.ui/react/styles.css";Use this when your app has no Tailwind build of its own.
2. Theme-only (your app runs its own Tailwind v4)
Keeps a single Tailwind instance. In your CSS entry:
@import "tailwindcss";
@import "@lemon.ui/react/theme.css";
/* REQUIRED — let your Tailwind see the classes Lemon's components use, or it
purges them and the components render unstyled. */
@source "../node_modules/@lemon.ui/react/dist";The
@sourceline is the #1 cause of "I installed it and everything is unstyled." Don't skip it.
Dark mode
Semantic tokens flip on the .dark class. Toggle it on <html> (or any
ancestor):
document.documentElement.classList.toggle("dark", isDark);Everything themes off the semantic layer (--primary, --border,
--muted-foreground, …); components never reference raw primitives, so dark mode
and retheming work by overriding semantic tokens alone.
Fonts
Fonts ship with the package. Add one line at your app entry, above the styles import:
import "@lemon.ui/react/fonts.css";That self-hosts the two families the type scale is designed around — Inter
Tight (sans/heading) and Geist Mono (mono) — and sets --font-sans /
--font-geist-mono. Both are variable fonts, so one file per family covers every
weight. No Google Fonts request; works offline.
Don't skip this because it looks fine locally. If Inter Tight happens to be installed on your machine, the CSS
font-familyname resolves against your system copy and everything looks right — for you. On any machine without it, text silently falls back to system-ui and the typography is wrong. Shipping the@font-facerules is what makes it deterministic.
Bringing your own fonts
Skip fonts.css and define the variables yourself; Lemon reads them and always
falls back gracefully:
--font-sans: var(--font-sans, "Inter Tight", ui-sans-serif, system-ui, sans-serif);
--font-mono: var(--font-geist-mono, ui-monospace, SFMono-Regular, Menlo, monospace);Next.js apps should use next/font instead of fonts.css — it self-hosts,
preloads, and avoids a layout shift the plain CSS import can't:
import { Inter_Tight, Geist_Mono } from "next/font/google";
const sans = Inter_Tight({ variable: "--font-sans", subsets: ["latin"] });
const mono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"] });
// <html className={`${sans.variable} ${mono.variable}`}> …If you set nothing at all, the inner fallbacks keep text rendering (an undefined
bare var() would otherwise invalidate the whole declaration).
Minimal example
import "@lemon.ui/react/styles.css";
import { Button, Card, CardContent } from "@lemon.ui/react";
export default function App() {
return (
<Card>
<CardContent className="p-6">
<Button>Get started</Button>
</CardContent>
</Card>
);
}Retheming
Override the semantic tokens (and, if you like, the primitive ramps) after importing Lemon's styles:
:root {
--primary: oklch(0.72 0.19 145);
--radius: 0.5rem;
}What's exported
Everything importable from @lemon.ui/react is listed in src/index.ts — components,
their variant helpers (e.g. buttonVariants), and the cn class-merge utility.
If it isn't exported there, treat it as private.
