テーマの調整
変える手段は3つあります。上から順に試してください。下にいくほど強く、壊れやすくなります。
1. CSS 変数を上書きする
ブランド色を変えるならこれだけで済みます。 --novi-color-primary を使う全コンポーネントに一度に効きます。
/* app/globals.css */
@import '@novi-ui/core/base.css';
@import '@novi-ui/raster/raster.css';
:root {
--novi-color-primary: oklch(58% 0.18 265);
--novi-color-primary-fg: oklch(99% 0 0);
}
[data-novi-scheme='dark'] {
--novi-color-primary: oklch(72% 0.15 265);
}色は oklch() で書いてください。明度を数値で扱えるため、 ライトとダークで同じコントラスト比を保ったまま色相だけ差し替えられます。
2. slot にクラスを足す
1箇所だけ形を変えたいときは classNames を使います。 variant が付けたクラスを打ち消したいときも、拡張ではなくこちらが正解です。
import { Button, Card, CardBody, CardHeader } from '@novi-ui/raster'
// slot ごとにクラスを足す。variant のクラスを上書きしたいときもこれを使う。
export function WideSaveButton() {
return (
<Button
color="primary"
classNames={{
root: 'w-full justify-between',
label: 'tracking-wide',
}}
>
保存
</Button>
)
}
// どの slot があるかは各コンポーネントのページの slot 表にある。
export function TightCard() {
return (
<Card classNames={{ header: 'pb-1', body: 'pt-1' }}>
<CardHeader>売上</CardHeader>
<CardBody>¥1,240,000</CardBody>
</Card>
)
}3. styles を拡張する
同じ見た目を何度も使うなら、派生した styles を1つ作ります。tailwind-variants が必要です。
pnpm add tailwind-variantsimport { Button, buttonStyles } from '@novi-ui/raster'
import { tv } from 'tailwind-variants'
// slot ベースの定義では `base` は効かない。**必ず `slots` を使う。**
export const wideButtonStyles = tv({
extend: buttonStyles,
slots: {
root: 'w-full justify-between',
},
})
// 拡張した styles は自分で呼び、結果を classNames に渡す。
export function WideButton() {
const s = wideButtonStyles({ size: 'lg' })
return (
<Button size="lg" classNames={{ root: s.root() }}>
続ける
</Button>
)
}slot ベースの定義では base が効きません。 tv({ extend, base }) と書いても無視されるため、slots を使ってください。 また extend で足したクラスは base に加算されるだけなので、 variant のクラスには勝てません。勝たせたいときは 2 の classNames を使います。
上書きできる変数
Raster(@novi-ui/raster)が定義する値です。 この表は CSS を出力しているのと同じ定義から作られているため、実装とズレません。
角丸
3段の階調。小さな部品 sm / 操作類 md / 浮く面 lg が既定(ADR-R8)
| CSS 変数 | 値 |
|---|---|
| --novi-radius-none | 0px |
| --novi-radius-sm | 6px |
| --novi-radius-md | 8px |
| --novi-radius-lg | 12px |
| --novi-radius-full | 9999px |
影
浮いている層(メニュー・ダイアログ)だけが持つ。面は平らで、境界線と背景色の差で階層を作る
| CSS 変数 | 値 |
|---|---|
| --novi-shadow-none | none |
| --novi-shadow-sm | 0 1px 2px oklch(15% 0 0 / 0.06) |
| --novi-shadow-md | 0 4px 12px oklch(15% 0 0 / 0.1), 0 1px 3px oklch(15% 0 0 / 0.06) |
| --novi-shadow-lg | 0 16px 40px oklch(15% 0 0 / 0.16), 0 4px 12px oklch(15% 0 0 / 0.08) |
文字サイズ
比率 1.2(minor third)。単一比率で階層を作る
| CSS 変数 | 値 |
|---|---|
| --novi-text-xs | 12px |
| --novi-text-sm | 14px |
| --novi-text-base | 16px |
| --novi-text-lg | 20px |
| --novi-text-xl | 24px |
| --novi-text-2xl | 30px |
| --novi-text-3xl | 36px |
余白(内側)
面とコントロールの内側の余白。すべて 4px の倍数で、高さと同じ格子の上に乗せている
| CSS 変数 | 値 |
|---|---|
| --novi-pad-surface-x | 20px |
| --novi-pad-surface-y | 16px |
| --novi-pad-control-x-sm | 12px |
| --novi-pad-control-x-md | 16px |
| --novi-pad-control-x-lg | 20px |
余白(要素間)
要素と要素の距離。inline : stack : section = 1 : 2 : 3 の比が「どこまでが一塊か」を伝える
| CSS 変数 | 値 |
|---|---|
| --novi-gap-inline | 8px |
| --novi-gap-stack | 16px |
| --novi-gap-section | 24px |
字送り
文字同士の詰め。Raster が締めるのは見出しだけで、本文は可読性のため 0 に置く
| CSS 変数 | 値 |
|---|---|
| --novi-tracking-tight | -0.014em |
| --novi-tracking-normal | 0em |
行送り
行と行の距離。読む文章(body)と見る文字(heading)で必要な高さが違う
| CSS 変数 | 値 |
|---|---|
| --novi-leading-body | 1.6 |
| --novi-leading-heading | 1.2 |
書体
本文・等幅・見出しの書体と、数字の字形。Raster は書体で飾らず、見出しも sans を参照する
| CSS 変数 | 値 |
|---|---|
| --novi-font-sans | system-ui, sans-serif |
| --novi-font-mono | ui-monospace, monospace |
| --novi-font-heading | var(--novi-font-sans) |
| --novi-font-numeric | tabular-nums |
モーション
Raster では速度を1つに固定している。動きで飾らない
| CSS 変数 | 値 |
|---|---|
| --novi-duration-fast | 120ms |
| --novi-duration-base | 120ms |
| --novi-duration-slow | 120ms |
| --novi-ease-standard | cubic-bezier(0.2, 0, 0, 1) |
| --novi-ease-emphasized | cubic-bezier(0.2, 0, 0, 1) |
色相
選択中のカラー(data-novi-color)の hue。既定は Ink。利用者が派生色を作るときの参照点
| CSS 変数 | 値 |
|---|---|
| --novi-hue | 268 |
色
primary / secondary はカラーセット Print Inks の既定色(Ink × Brick)。data-novi-color で切替。中立色の chroma は 0 に固定している
bgsubtlefgmutedborderborder-strongoverlaydefaultdefault-fgprimaryprimary-fgsecondarysecondary-fgsuccesssuccess-fgwarningwarning-fgdangerdanger-fg
| CSS 変数 | ライト | ダーク |
|---|---|---|
| --novi-color-bg | oklch(99% 0 0) | oklch(16% 0 0) |
| --novi-color-subtle | oklch(96% 0 0) | oklch(21% 0 0) |
| --novi-color-fg | oklch(20% 0 0) | oklch(95% 0 0) |
| --novi-color-muted | oklch(48% 0 0) | oklch(72% 0 0) |
| --novi-color-border | oklch(92% 0 0) | oklch(27% 0 0) |
| --novi-color-border-strong | oklch(58% 0 0) | oklch(55% 0 0) |
| --novi-color-overlay | oklch(20% 0 0 / 0.45) | oklch(10% 0 0 / 0.6) |
| --novi-color-default | oklch(96% 0 0) | oklch(25% 0 0) |
| --novi-color-default-fg | oklch(20% 0 0) | oklch(95% 0 0) |
| --novi-color-primary | oklch(44% 0.09 268) | oklch(74% 0.09 268) |
| --novi-color-primary-fg | oklch(99% 0 0) | oklch(16% 0 0) |
| --novi-color-secondary | oklch(44% 0.09 35) | oklch(74% 0.09 35) |
| --novi-color-secondary-fg | oklch(99% 0 0) | oklch(16% 0 0) |
| --novi-color-success | oklch(47% 0.13 155) | oklch(77% 0.13 155) |
| --novi-color-success-fg | oklch(99% 0 0) | oklch(16% 0 0) |
| --novi-color-warning | oklch(47% 0.13 60) | oklch(81% 0.13 65) |
| --novi-color-warning-fg | oklch(99% 0 0) | oklch(16% 0 0) |
| --novi-color-danger | oklch(50% 0.19 27) | oklch(73% 0.16 27) |
| --novi-color-danger-fg | oklch(99% 0 0) | oklch(16% 0 0) |
テーマには解釈の自由があります
語彙は core が固定しますが、値の解釈はテーマが決めます。 たとえば Raster の radius は sm=6px / md=8px / lg=12px の控えめな3段で、 これより丸くする語彙はありません。影も浮いている層にしか付きません。 その範囲を超えた見た目が必要なときは、トークンの上書きではなくテーマを替えます。
解釈の差は値だけに留まりません。Tactile では size が Modal の最大高を意味し、 Modal 自体が下から出るシートになります。同じコードのまま DOM の組み立て方が変わります。
- Raster のデザイン言語 — ミニマル / スイス系。線で切る
- Tactile のデザイン言語 — タッチファースト。面を持ち上げる
- Flatlay のデザイン言語 — 帳票・文具。z 軸を持たない