Tactile
タッチファースト。指で触る前提で寸法を決め、面を持ち上げて階層を作ります。 下から出るシート、大きなタップ領域、押した手応え。 画面あたりの情報量は Raster より意図的に少なくしてあります。
Raster とどう違うか
公開 API は完全に同一です。 import 元を差し替えるだけで、見た目だけでなく DOM の組み立て方ごと切り替わります。 違うのは寸法と面の作り方、そして次の構造です。
| 部位 | Raster | Tactile |
|---|---|---|
| Modal | 中央のダイアログ・閉じるは右上の ✕ | 下から出るシート・閉じるはフッターのフルワイド |
| Select / Menu | トリガーの隣に出る | 画面下端のシート |
| Tabs | 下線1本 | セグメンテッドコントロール |
| Toast | 右下 | 上端(下端はシートとキーボードが占有する) |
| 高さ | 32 / 40 / 48px | 40 / 48 / 56px |
| 本文 | 16px | 17px |
デスクトップで情報密度が要るなら Raster を選んでください。 寸法はテーマの美学に属するもので、どちらが正しいという話ではありません。
すべて数値で決まっています
「タッチファースト」を感覚で運用すると必ずブレます。目分量で近い値を書かず、 この値を使ってください。
- controlHeights
- sm: 40 / md: 48 / lg: 56
- radii
- none: 0px / sm: 8px / md: 14px / lg: 20px / full: 9999px
- text
- xs: 13px / sm: 15px / base: 17px / lg: 21px / xl: 26px / 2xl: 32px / 3xl: 40px
- motion
- duration-fast: 120ms / duration-base: 200ms / duration-slow: 260ms / ease-standard: cubic-bezier(0.2, 0, 0, 1) / ease-emphasized: cubic-bezier(0.05, 0.7, 0.1, 1)
指が届くことを実測しています
対話要素の実効タップ領域は 44×44px 以上です(WCAG 2.2 AAA / Apple HIG)。Checkbox の箱のように小さく見せたい部品は、 擬似要素で当たり判定だけを広げてあります — 視覚寸法とタップ寸法は別物です。
クラスが付いているかではなく、実際のブラウザで計算済みスタイルを測って検査しています。 擬似要素で広げた領域は要素の矩形に現れないため、それ以外の方法では確かめられません。
入力の文字は 16px を下回りません
iOS Safari は 16px 未満の入力欄にフォーカスするとページごと拡大し、以後戻りません。size="sm" でも入力の文字は小さくせず、視覚的な軽さは高さと余白で作ります。
| CSS 変数 | 値 |
|---|---|
| --novi-text-xs | 13px |
| --novi-text-sm | 15px |
| --novi-text-base | 17px |
| --novi-text-lg | 21px |
| --novi-text-xl | 26px |
| --novi-text-2xl | 32px |
| --novi-text-3xl | 40px |
影は面にも使います
階層は持ち上がりで作ります (Raster は線で切ります)。境界線は補助的にしか使いません。lg だけ影が上向きなのは、 浮く面が画面の下端から来るためです。
| CSS 変数 | 値 |
|---|---|
| --novi-shadow-none | 0 0 #0000 |
| --novi-shadow-sm | 0 1px 3px oklch(20% 0.01 255 / 0.10), 0 1px 2px oklch(20% 0.01 255 / 0.06) |
| --novi-shadow-md | 0 6px 16px oklch(20% 0.01 255 / 0.14), 0 2px 4px oklch(20% 0.01 255 / 0.08) |
| --novi-shadow-lg | 0 -8px 32px oklch(20% 0.01 255 / 0.18), 0 -2px 8px oklch(20% 0.01 255 / 0.08) |
中立色まで選んだ色に染まります
Tactile は染まる生地です。data-novi-color で染料を選ぶと、primary だけでなく背景・境界線・文字までその色相を帯びます。 Raster(染まらない紙・中立色の彩度は 0)との最も分かりやすい違いがここに出ます。
色は --novi-color-* の CSS 変数経由でのみ指定する。data-novi-color で選ばれた染料に中立色まで追従するため、リテラル値を書くとテーマの外に取り残される
bgsubtlesurfacebordermutedfgsize は幅ではなく最大高です
全幅のシートに幅の段階は存在しないので、Modal の size はシートの最大高として解釈します (sm 40dvh / md 60dvh / lg 80dvh / full 100dvh)。 Select と Menu ではトリガーの寸法と行の密度になります。
語彙は core が共通で固定し、解釈はテーマの自由という設計です。 同じコードがテーマを跨いでそのまま動くのはこのためで、不具合ではありません。
書いてはいけないクラス
テーマを触る場合の規律です。CI が機械的に検査しているため、違反するとビルドが落ちます。 この一覧は検査スクリプトと同じ定義から作られています。
トークンを経由しない角丸(rounded-sm / rounded-full / rounded-t-xl など)角丸はトークン経由。Tactile は sm=8 / md=14 / lg=20px で、none 以外は 8px 以上トークン外の影(shadow-md / shadow-[0_1px…] など)影はトークン経由。値は α ≤ 0.24 に縛られているborder-2 以上境界線は補助。使う場合も 1px のみ。面の分割は影と背景色差で行う押下状態以外の scale-*装飾目的の scale は使わない。押下フィードバック(data-[pressed]:scale-*)のみ許可rotate-* / animate-spin回転は Spinner と Accordion のシェブロンのみ例外(ADR-T4)リテラルの色値(#fff / rgb() / hsl() / oklch())色は必ず --novi-color-* を経由する。リテラル値を書かない8px 以上の生の余白ユーティリティ(p/px/py/pt/pb/pl/pr/gap-2 以上)余白はトークン経由(--novi-pad-* / --novi-gap-*)。余白がテーマの所有物でないと、各コンポーネントが自分の判断で数値を書き、3モデルが同じ密度に潰れる。8px 未満の微小インセット(アイコンの位置合わせ)だけ生値を許すduration-*(--novi-duration-* の任意値を除く)モーションの時間はトークン経由にする
例外
tactile-tokens.tsはliteral-color, duration, radius, shadow-literalのみ許可 — トークンの値そのものを定義する唯一の場所。ここだけは「使用」ではなく「定義」color-set.tsはliteral-colorのみ許可 — カラーセット Textile Dyes の値を定義する場所(specs/06-tones-and-colors)。tactile-tokens.ts と同じ「定義」側spinner.styles.tsはrotateのみ許可 — ローディング表現の代替(点滅・バー往復)は視認性か情報量で劣る(ADR-R2 と同じ判断)accordion.styles.tsはrotateのみ許可 — シェブロンの回転は開閉方向そのものを示す。+/− への置き換えは向きの情報を失う(ADR-T4)
押すと沈みます
対話要素は押下中に 0.97 倍に縮み、solid では影が消えます。持ち上がっていた面が沈むという一連の動きで手応えを返します。scale を使うのはここだけで、装飾目的では使いません。prefers-reduced-motion が有効なときは沈み込みもシートのスライドも起きません。
| CSS 変数 | 値 |
|---|---|
| --novi-duration-fast | 120ms |
| --novi-duration-base | 200ms |
| --novi-duration-slow | 260ms |
| --novi-ease-standard | cubic-bezier(0.2, 0, 0, 1) |
| --novi-ease-emphasized | cubic-bezier(0.05, 0.7, 0.1, 1) |
値を変えたいとき
ここにある値はすべて CSS 変数です。上書きの方法は テーマの調整にあります。