Files
yoi/docs/principle/design-language.md

9.8 KiB
Raw Permalink Blame History

Workspace Web Design Language

この文書は、Workspace Webのrendered resultが満たす設計規則を定義する。

一つの規則は最も具体的な一箇所だけをauthorityとする。具体的な二案の一方を棄却できない文は規則として残さない。

1. Principles

複数の規則や要求が競合した場合だけ、この章の優先原理を使う。

Task before explanation

次の有効なactionへ到達する前の操作、scroll、常設説明が少ない案を選ぶ。ただし、説明を省くと対象、結果、permission、不可逆性を誤認する場合は説明をactionより前に置く。

Meaning before decoration

情報の関係、重要度、操作可能性、stateのいずれも変えない視覚差を追加しない。

Authority before convenience

Backend authorityが確定していないdata、state、permission、actionを、UIの都合で補完または推測する案を棄却する。

2. Semantics / Grammar

Information structure

  • pageと、page内で一つのtaskまたは判断を完結させる領域をscopeとする。
  • 各scopeで最も強い視覚要素はheading、現在判断が必要なattention、filled actionのいずれか一つだけにする。同じ強さのheading、attention、filled actionを複数置かない。
  • hierarchyをcolorだけで表現しない。DOMと画面上の配置順を一致させた上で、size、weight、contrastを使う。
  • scopeを識別するprimary titleは、一つのscopeにつき一度だけ表示する。
  • actionは、そのactionが影響するscope内に置く。
  • DTOにfieldが存在するという理由だけで表示せず、current taskの判断またはactionを変える値だけを常設する。

Disclosure

  • Raw dataやデバッグ情報など、通常は必要としない詳細な情報は折りたたみ領域に隠す。
  • 作成または編集のformを表示用screenへ常時同居させず、明示的なactionで開く折りたたみ領域または別pageに置く。

Interaction and state

  • actionにはbutton、navigationにはlink、選択には対応するform controlを使う。
  • interactive elementはhover、focus、active、disabledを視覚的に区別する。
  • staticなstatus、key、metadataをbuttonやpillの形にしない。
  • clickable rowは遷移先を一つだけ持ち、row全体で同じ遷移を実行する。
  • permissionがないactionをdisabled controlとして表示しない。

State and operation

  • stateの違いによってユーザーの判断または利用できるactionが変わる場合、その違いを一つの表示へ統合しない。
  • UI stateはそのstateを所有するauthorityから取得し、別domainやtransportのsignalから推測しない。
  • 非同期mutationでは楽観的更新か悲観的更新を選ぶ。楽観的更新は暫定stateを明示し、failure時のrollbackまたは再取得を定義できる場合だけ使う。それ以外は確定済みstateを維持し、operationのpending stateを別に表示する。
  • loading、failure、unavailableの表示は利用不能になった最小のscopeだけを置き換え、利用可能な親scopeとsibling scopeを残す。
  • destructive actionは対象と結果を明記し、復元できない場合だけconfirmationを要求する。

Language / Copy

  • title、navigation label、control labelで明らかな内容を説明文で繰り返さない。
  • 常設proseは、対象またはactionの選択を変えるwarning、permission、validation constraint、error recoveryに必要な内容だけにする。
  • button labelには具体的なactionを表す動詞を使い、SubmitとOKを使わない。
  • identifier、API名、type名を別の語へ言い換えない。

Responsive behavior

  • page全体にhorizontal scrollを発生させない。
  • wrappingまたはstack化によって情報の対応関係が失われる場合だけ、その領域内にbounded horizontal scrollを持たせる。それ以外のside-by-side layoutは一列にする。
  • primary actionを初期viewportから追い出さない。
  • contentをtruncateする場合、完全な値をcopyまたはdetailで取得できるようにする。

Accessibility

  • action、navigation、form controlを非semantic elementだけで実装しない。
  • すべてのcontrolにaccessible nameを与える。
  • keyboard focusを不可視にしない。
  • tab orderを画面上の順序と一致させる。
  • stateをcolorだけで区別しない。
  • horizontal scroll regionをkeyboard focus可能にし、accessible nameを与える。

3. Primitives / Tokens

Color / Theme

colorはCSS custom propertyをauthorityとし、通常のWorkspace colorはOKLCHで定義する。lightとdarkは同じsemantic token名を使う。

--bg
--bg-raised
--bg-subtle
--line
--line-strong
--text
--text-strong
--text-muted
--text-faint
--code
--accent
--accent-muted
--success
--warning
--danger
--interactive-hover
--interactive-selected
--shadow-overlay
  • --bg、--bg-raised、--bg-subtleへhueを持たせない。
  • accentはfocus、current navigation、current selectionだけに使い、通常本文や通常actionの背景へ使わない。
  • success、warning、dangerを、それぞれ対応するstate以外へ使わない。
  • componentへraw colorを追加せず、上記semantic tokenを使う。
  • ConsoleとterminalのANSI paletteを通常のstatusやformへ流用しない。

Typography

font sizeは、その領域でユーザーが行う読み方によって選ぶ。

  • defaultは14px / 20pxとし、個々のtextを読むこと自体がtaskの中心になる領域に使う。
  • 同じ構造の反復を一覧として走査する領域と、位置、順序、形、選択状態を中心に識別するUI chromeには12px / 16pxを使う。textを読まなければactionの意味や結果を判断できない場合は14px / 20pxを使う。
  • 24px / 32pxはページまたはdocument内で唯一かつ最大のheadingだけに使う。
  • 収めるため、または情報を弱く見せるためにfont sizeを下げない。12px未満と13pxを使わない。
  • 機械的な値にはmonospace familyを使う。
--font-sans
--font-mono
--font-size-title /* 24px */
--line-height-title /* 32px */
--font-size-body /* 14px */
--line-height-body /* 20px */
--font-size-compact /* 12px */
--line-height-compact /* 16px */

Spacing / Shape

--space-1 /* 4px */
--space-2 /* 8px */
--space-3 /* 12px */
--space-4 /* 16px */
--space-5 /* 24px */
--space-6 /* 32px */
--radius-soft
  • 一つの境界にspacing、line、surface backgroundを重ねず、primary cueを一つだけ使う。
  • 汎用的なCard primitiveを設けない。
  • nested surfaceでparentと同じ境界表現を繰り返さない。
  • shadowは通常flowから浮くmenu、popover、Tooltipだけに使う。
  • radiusはinteractive controlだけに使う。

Motion

  • motion完了をactionの受付、state change、content理解の条件にしない。
  • 繰り返しまたは連続animationを、prefers-reduced-motion: reduceで停止する。

4. Components / Patterns

Border / Structural edge

  • Bevelは1pxの単色borderを描くvisual wrapperであり、照明、raised/inset、ridge/grooveを表現しない。
  • Bevel自体へbackgroundやsurface toneを与えず、必要なbackgroundは内側のsemantic childが持つtext/content areaへ適用する。
  • 隣接するsurfaceでは、実際に境界となる辺だけを有効にする。
  • standaloneな閉じた領域は全辺をBevelで囲う。Desktop shellではHeaderのbottom edgeとSidebarのright edgeだけを有効にし、viewport外周のtop/left edgeを描かない。
  • BevelLineは開いた領域内の1px separatorだけに使う。同じlayerではheading、本文、Lineの端を揃え、Lineだけに端方向のpaddingやmarginを追加しない。
  • nested layerでは親layoutがそのlayer全体へinline方向の余白を与え、content、row、Lineを一緒に移動する。Lineだけを短くしない。
  • control自身のborder、focus ring、status marker、table gridをBevelまたはBevelLineで置き換えない。

Sidebar

  • Desktop幅はclamp(220px, 20vw, 280px)とする。
  • navigation contentだけをscrollさせる。
  • Desktopのfold controlはSidebar下部、Mobileのfold controlはHeaderに置く。
  • MobileでSidebarを表示するときはHeader下の残りviewport全体を使い、main contentと同時表示しない。
  • navigationとscope control以外を置かない。
  • navigation linkはSidebar幅全体を使うflat rowとする。
  • active routeは一つの視覚cueとaria-current="page"で示す。
  • iconだけでnavigation labelを置き換えない。
  • fold controlは一つだけ置き、current stateに対応するaccessible nameを使う。

Tooltip / Contextual help

  • operationやtable全体を説明する常設proseを置く前に、button labelとcolumn headingだけで識別できるようにする。
  • labelだけでは表現できない補助説明だけを、対象へbindしたTooltipに入れる。
  • pointer hoverとkeyboard focusの両方で開く。
  • triggerとTooltipをaria-describedbyで関連付ける。
  • hoverはdelay後、focusは直ちに表示する。
  • Escapeで閉じ、triggerからfocusを移動しない。
  • 複数paragraph、form、link、buttonを入れない。
  • error、permission、validation constraint、不可逆操作の結果、current stateをTooltipへ入れない。
  • touch環境ではTooltipだけを説明への到達手段にしない。

Generic component patterns

  • Form field: label、control、constraintまたはhelp、field errorの順に配置する。
  • Key-value: 一つのresourceの属性にはdlを使う。

Design-labではcomponent名と実際のsampleだけを表示する。規則とrationaleをUIへ書かない。