9.8 KiB
9.8 KiB
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を一つだけ使う。
- 汎用的な
Cardprimitiveを設けない。 - 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へ書かない。