Novi UI

テーマの調整

変える手段は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-variants
import { 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)

Raster の角丸トークン
CSS 変数値
--novi-radius-none0px
--novi-radius-sm6px
--novi-radius-md8px
--novi-radius-lg12px
--novi-radius-full9999px

影

浮いている層(メニュー・ダイアログ)だけが持つ。面は平らで、境界線と背景色の差で階層を作る

Raster の影トークン
CSS 変数値
--novi-shadow-nonenone
--novi-shadow-sm0 1px 2px oklch(15% 0 0 / 0.06)
--novi-shadow-md0 4px 12px oklch(15% 0 0 / 0.1), 0 1px 3px oklch(15% 0 0 / 0.06)
--novi-shadow-lg0 16px 40px oklch(15% 0 0 / 0.16), 0 4px 12px oklch(15% 0 0 / 0.08)

文字サイズ

比率 1.2(minor third)。単一比率で階層を作る

Raster の文字サイズトークン
CSS 変数値
--novi-text-xs12px
--novi-text-sm14px
--novi-text-base16px
--novi-text-lg20px
--novi-text-xl24px
--novi-text-2xl30px
--novi-text-3xl36px

余白(内側)

面とコントロールの内側の余白。すべて 4px の倍数で、高さと同じ格子の上に乗せている

Raster の余白(内側)トークン
CSS 変数値
--novi-pad-surface-x20px
--novi-pad-surface-y16px
--novi-pad-control-x-sm12px
--novi-pad-control-x-md16px
--novi-pad-control-x-lg20px

余白(要素間)

要素と要素の距離。inline : stack : section = 1 : 2 : 3 の比が「どこまでが一塊か」を伝える

Raster の余白(要素間)トークン
CSS 変数値
--novi-gap-inline8px
--novi-gap-stack16px
--novi-gap-section24px

字送り

文字同士の詰め。Raster が締めるのは見出しだけで、本文は可読性のため 0 に置く

Raster の字送りトークン
CSS 変数値
--novi-tracking-tight-0.014em
--novi-tracking-normal0em

行送り

行と行の距離。読む文章(body)と見る文字(heading)で必要な高さが違う

Raster の行送りトークン
CSS 変数値
--novi-leading-body1.6
--novi-leading-heading1.2

書体

本文・等幅・見出しの書体と、数字の字形。Raster は書体で飾らず、見出しも sans を参照する

Raster の書体トークン
CSS 変数値
--novi-font-sanssystem-ui, sans-serif
--novi-font-monoui-monospace, monospace
--novi-font-headingvar(--novi-font-sans)
--novi-font-numerictabular-nums

モーション

Raster では速度を1つに固定している。動きで飾らない

Raster のモーショントークン
CSS 変数値
--novi-duration-fast120ms
--novi-duration-base120ms
--novi-duration-slow120ms
--novi-ease-standardcubic-bezier(0.2, 0, 0, 1)
--novi-ease-emphasizedcubic-bezier(0.2, 0, 0, 1)

色相

選択中のカラー(data-novi-color)の hue。既定は Ink。利用者が派生色を作るときの参照点

Raster の色相トークン
CSS 変数値
--novi-hue268

色

primary / secondary はカラーセット Print Inks の既定色(Ink × Brick)。data-novi-color で切替。中立色の chroma は 0 に固定している

  • bg
  • subtle
  • fg
  • muted
  • border
  • border-strong
  • overlay
  • default
  • default-fg
  • primary
  • primary-fg
  • secondary
  • secondary-fg
  • success
  • success-fg
  • warning
  • warning-fg
  • danger
  • danger-fg
Raster の色トークン
CSS 変数ライトダーク
--novi-color-bgoklch(99% 0 0)oklch(16% 0 0)
--novi-color-subtleoklch(96% 0 0)oklch(21% 0 0)
--novi-color-fgoklch(20% 0 0)oklch(95% 0 0)
--novi-color-mutedoklch(48% 0 0)oklch(72% 0 0)
--novi-color-borderoklch(92% 0 0)oklch(27% 0 0)
--novi-color-border-strongoklch(58% 0 0)oklch(55% 0 0)
--novi-color-overlayoklch(20% 0 0 / 0.45)oklch(10% 0 0 / 0.6)
--novi-color-defaultoklch(96% 0 0)oklch(25% 0 0)
--novi-color-default-fgoklch(20% 0 0)oklch(95% 0 0)
--novi-color-primaryoklch(44% 0.09 268)oklch(74% 0.09 268)
--novi-color-primary-fgoklch(99% 0 0)oklch(16% 0 0)
--novi-color-secondaryoklch(44% 0.09 35)oklch(74% 0.09 35)
--novi-color-secondary-fgoklch(99% 0 0)oklch(16% 0 0)
--novi-color-successoklch(47% 0.13 155)oklch(77% 0.13 155)
--novi-color-success-fgoklch(99% 0 0)oklch(16% 0 0)
--novi-color-warningoklch(47% 0.13 60)oklch(81% 0.13 65)
--novi-color-warning-fgoklch(99% 0 0)oklch(16% 0 0)
--novi-color-dangeroklch(50% 0.19 27)oklch(73% 0.16 27)
--novi-color-danger-fgoklch(99% 0 0)oklch(16% 0 0)

テーマには解釈の自由があります

語彙は core が固定しますが、値の解釈はテーマが決めます。 たとえば Raster の radius は sm=6px / md=8px / lg=12px の控えめな3段で、 これより丸くする語彙はありません。影も浮いている層にしか付きません。 その範囲を超えた見た目が必要なときは、トークンの上書きではなくテーマを替えます。

解釈の差は値だけに留まりません。Tactile では size が Modal の最大高を意味し、 Modal 自体が下から出るシートになります。同じコードのまま DOM の組み立て方が変わります。