kurobeni design
Design system, v1.0.0
Tokens, CSS, React components and a plain-JS runtime for kurobeni.jp, in English and Japanese.
Install
React or plain HTML
Package
"@kurobeni/design": "git+https://git.konpeki.co.uk/jacob/kurobeni.git#v1.0.0"Root layout
Import the CSS once and wrap the page in Kurobeni. It applies a stored theme before paint and sets any of accent, density, round, fontSans, fontDisplay and fontMono you pass. With no stored theme the CSS follows the OS.
import '@kurobeni/design/css';
import { Kurobeni, ToastProvider } from '@kurobeni/design/react';
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<Kurobeni accent="#c2163b">
<ToastProvider>{children}</ToastProvider>
</Kurobeni>
</body>
</html>
);
}Without React
<link rel="stylesheet" href="https://cdn.kurobeni.jp/design/kurobeni.css" />
<script src="https://cdn.kurobeni.jp/design/head.js"></script>
<script src="https://cdn.kurobeni.jp/design/kurobeni.js" defer></script>The markup uses the same class names as the React components. kurobeni.js adds the theme switch, dropdown, tabs, pressed groups, menus, scrollable tables and code, dialogs with typed confirmation, and toasts. Pinned builds are under https://cdn.kurobeni.jp/design/v1.0.0/ and never change.
Fonts
Not shipped. Load Atkinson Hyperlegible Next and Mono, and BIZ UDPGothic for Japanese pages, from Google Fonts or self-host them. Without them Japanese falls back to the platform's gothic.
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Mono:wght@400;500;700&family=Atkinson+Hyperlegible+Next:ital,wght@0,400..800;1,400&family=BIZ+UDPGothic:wght@400;700&display=swap" />Rules
A tone is three colours
A ground, a surface and an ink. Every other colour is mixed from those: secondary text, borders, hover and pressed fills, message backgrounds. A hue (accent, status, chart series) is mixed with the ink before it is used as text.
data-tone="sunk", "deep" or "accent" on any element sets its tone and re-mixes everything inside. A custom tone is three custom properties.
One knob per decision
--kb-accent, --kb-density, --kb-round and the three font faces are each one custom property, on :root or any scope. Density multiplies the space steps; round multiplies the radii, which are 3, 4 and 6px by default.
Palette
Kurobeni (黒紅, #23191e) is a traditional red-black, made by dyeing black over beni. It is the ink, so every grey is tinted by it, and the deep tone under the bar. Beni (紅) is the only accent: primary actions, links, focus, checked controls and selected rows, the rule under the bar, the mark before a title, and the seal. Data uses the ink; only charts comparing several series use the eight series hues.
Structure
A dark bar with a beni rule, a short beni bar before each section and screen title, tabs underlined in beni, and tables with a bold header band. The current location (bar link, side link, page number) is shown inverted.
Target sizes
Controls are 32, 36 and 44px with a mouse, 40, 44 and 48px on touch screens. Body text is 16px; nothing is smaller than 12px.
English and Japanese
A page is in one language. lang="ja" turns on Japanese typesetting: more leading, slight tracking, proportional kana in headings and controls, no line starting with 。or ), short text broken between phrases, and sesame dots (傍点) for emphasis instead of italics. LangLink links to the same page in the other language, labelled in that language.
Theme
The CSS follows the OS through light-dark(). A chosen theme is a data-theme attribute; any element can carry one to switch its subtree. With no stored choice, nothing is set.
State is never colour alone
A status is a word with a small coloured mark. A switch shows its state by the knob position, and the knob carries a check.
Type
Atkinson Hyperlegible Next for text and titles, Atkinson Hyperlegible Mono for code, BIZ UDPGothic for Japanese, all chosen for legibility. Sizes run from xs (12px) to 4xl; the interface is set at sm (14px) and reading text at md (16px).
Cascade layers
Everything is in @layer kb.*, so a page's unlayered CSS wins without !important.
Checks
Every commit
colors.json resolves 38 colour roles in 4 tones, in both themes and again under prefers-contrast: more: 608 values. pnpm test holds text at 4.5:1 and field edges, focus rings and chart marks at 3:1 in all of them.
The suite also runs Prettier and stylelint, checks the docs' class names and the React markup against the CSS, and runs Playwright (axe, focus, keyboard, target size, reflow, theme and behaviour) on the harness, the templates and this site.