Theming
@navree/theme: navree.com's dark theme as Tailwind CSS 4 tokens, shadcn/ui and shadcn-vue colors, self-hosted fonts and the tokens as data.
On this page
@navree/theme is the look of navree.com for your app: near-black ink surfaces with a faint sea-green cast, off-white text in four steps, one green accent, amber for warnings, and three self-hosted faces. It is dark only. shadcn/ui and shadcn-vue components pick it up without changes, so Next.js and Nuxt apps look the same.
Setting up
@import "tailwindcss";
@import "@navree/theme/theme.css";
@import "@navree/theme/fonts.css";Put class="dark" on <html>: the theme declares @custom-variant dark (&:where(.dark, .dark *)), so the dark: classes shadcn components use always apply. Add the framework package to Tailwind's sources, so the classes its data table uses are generated:
@source "../../node_modules/@navree/next/dist";@source "../../../node_modules/@navree/nuxt/dist";| File | What it is |
|---|---|
@navree/theme/theme.css | The Tailwind CSS 4 preset: @theme tokens, the shadcn semantic colors, the radius scale, base styles and utilities. |
@navree/theme/fonts.css | @font-face rules for the three faces from the @fontsource packages, and the --font-mona, --font-geist and --font-monaspace variables. |
@navree/theme/tokens.css | The color tokens and font stacks as plain custom properties, for pages without Tailwind. |
@navree/theme/registry/navree.json | A shadcn registry theme, for the shadcn CLI. |
@navree/theme | The tokens as data (colors, semanticColors, fonts, radius, themeColor) and contrastRatio. |
The tokens
| Token | Value | Use |
|---|---|---|
ink-0 | #040605 | Page background |
ink-1 | #0a0e0c | Cards, panels, the sidebar |
ink-2 | #111714 | Hover surfaces, inputs |
ink-3 | #16201b | The current item, a selected option |
line | #1c2420 | Every 1px border (the default border color) |
line-2 | #2a3530 | Stronger borders: buttons, inputs |
fg | #e9efeb | Headings, primary text |
fg-1 | #cfd8d3 | Strong secondary text, table cells |
fg-2 | #a9b5ae | Body copy, descriptions |
fg-3 | #8a968f | Captions, metadata, column headings |
accent | #3dff8f | The primary action, focus rings, positive data |
accent-ink | #03130a | Text on the accent |
amber | #ffbf5e | Warnings |
danger | #ff6b6b | Errors (shadcn's destructive) |
Every text step meets WCAG AA on every ink step; the faintest pair, fg-3 on ink-3, is 5.4:1. The package's tests check it, and that the tokens match navree.com's.
The theme also has the display type scale (text-display-2xl to text-display-sm, text-title for an app's page title, text-figure for key figures, text-eyebrow), green glows (shadow-glow-sm, shadow-glow, shadow-glow-lg), shadow-float for popovers and dialogs, the easings ease-out-quint and ease-spring, and animate-shimmer for loading placeholders.
shadcn's semantic colors
| shadcn name | Token |
|---|---|
background | ink-0 |
foreground | fg |
card, popover | ink-1 |
secondary, muted | ink-2 |
muted-foreground | fg-2 |
primary, ring | accent |
primary-foreground | accent-ink |
destructive | danger |
warning, success | amber, accent |
border, input | line, line-2 |
sidebar, sidebar-accent | ink-1, ink-2 |
chart-1 to chart-5 | slate to pale mint, then accent |
accent stays Navree green: it is not a grey hover color. After pnpm dlx shadcn@latest add <component>, replace hover:bg-accent and focus:bg-accent with hover:bg-ink-2 and bg-ink-3.
Corners
App UI has two radii: 4px for controls (buttons, inputs, menu items, badges, tabs, avatars) and 6px for surfaces (cards, popovers, dialogs, sheets). The theme pins Tailwind's scale to them, so whatever the shadcn CLI generates lands inside the rule: rounded-sm, rounded-md and rounded-lg are 4px, rounded-xl and up are 6px. Write rounded-md on a control and rounded-xl on a surface; rounded-full is for status dots only.
Utilities
| Utility | What it does |
|---|---|
surface | A raised surface: ink-1, a 1px line border, 6px corners. |
th-label | Column headings: 12px, medium, fg-3. |
eyebrow | The small label above a heading: 13px, medium, fg-3. |
figures | Tabular, lining figures for amounts, counts and dates. |
scroll-thin | A thin scrollbar for sidebars, boards and lists. |
font-display-wide | The display face at its widest, for one oversized word at most. |
Fonts
- Mona Sans (SIL OFL), the display face: page titles and big figures, set semi-expanded (
font-display,wdth112).h1uses it automatically. - Geist (SIL OFL), the UI and body sans (
font-sans). - Monaspace Neon (SIL OFL), for code, ids and keys (
font-mono).
fonts.css loads them from the @fontsource packages the theme depends on, every subset with its unicode-range, so a browser downloads only what a page uses and the app makes no requests to a font service. It works in Nuxt and any Vite or PostCSS pipeline.
In Next.js, next/font/local preloads the files and removes the layout shift. Load the same files there instead of fonts.css, and put the variables on <html>:
import localFont from "next/font/local";
export const monaSans = localFont({
src: [{ path: "../../node_modules/@fontsource-variable/mona-sans/files/mona-sans-latin-standard-normal.woff2", weight: "200 900" }],
variable: "--font-mona",
declarations: [{ prop: "font-stretch", value: "75% 125%" }],
});
export const geist = localFont({
src: [{ path: "../../node_modules/@fontsource-variable/geist/files/geist-latin-wght-normal.woff2", weight: "100 900" }],
variable: "--font-geist",
});
export const monaspace = localFont({
src: [{ path: "../../node_modules/@fontsource/monaspace-neon/files/monaspace-neon-latin-400-normal.woff2", weight: "400" }],
variable: "--font-monaspace",
});
export const fontVariables = [monaSans.variable, geist.variable, monaspace.variable].join(" ");<html lang="en" className={`dark ${fontVariables}`}>Add @fontsource-variable/mona-sans, @fontsource-variable/geist and @fontsource/monaspace-neon to the app's own dependencies for these paths. For scripts the three faces don't cover, see fonts for other scripts.
shadcn/ui and shadcn-vue
theme.css already maps every shadcn variable, so components added with the shadcn CLI or the shadcn-vue CLI use the theme as they are. For a project that keeps its colors in shadcn's own CSS variables instead, the package also ships the theme as a registry item in shadcn's registry schema (registry:theme), which the shadcn CLI installs from a path or a URL:
pnpm dlx shadcn@latest add ./node_modules/@navree/theme/registry/navree.jsonIt has the same colors in its light and dark variables, because the theme is dark only. A shadcn-vue project can copy the same values from it into its CSS variables, or import theme.css.
The tokens in code
Canvas charts, <meta name="theme-color">, emails and PDFs can't read CSS variables. The package exports the tokens:
import { colors, contrastRatio, semanticColors, themeColor } from "@navree/theme";
export const viewport = { themeColor }; // "#040605"
const series = [semanticColors["chart-1"], semanticColors["chart-5"]];
contrastRatio(colors["fg-3"], colors["ink-3"]); // 5.4…The rules
The green is for three things: the primary action, focus rings, and positive data (a won amount, a completed task, a success toast). Navigation, selection and hover use the ink steps: hover:bg-ink-2, the current item bg-ink-3. No pills, no glows on app UI except shadow-float under popovers and dialogs, and no second green element competing with the primary action. apps/website/DESIGN.md (section 9, App UI) has the full rules for shells, density and type.