本文へスキップ

テーマ

このページは英語から翻訳されています。 英語のページを読む

ステータス: crates/zeyn/assets/zeyn.css に実装済みです。

標準の UI キットは、すべての色、フォント、サイズ、角丸を :root の CSS カスタムプロパティから読み取ります。組織はそのうちいくつかを上書きするだけで見た目を変更できます。zeyn.css を編集したり、セレクターを書いたりする必要はありません。

1. テーマの適用方法

サーバーはまず zeyn.css を配信し、アプリにトークン上書き用の追加スタイルシートがあれば、その後に配信します。上書きファイルには :root ブロックだけを書きます。

:root {
  --z-brand: #7a2448;
  --z-font: "Source Sans 3", "Segoe UI", system-ui, sans-serif;
  --z-radius: 8px;
  --z-density: 0.75;
}

上書きファイルのルールは次のとおりです。

  • アプリ自身のオリジンから配信する必要があります。Content Security Policy は style-src 'self' のため、インラインの <style> ブロックとサードパーティのスタイルシートはブロックされます。
  • フォントは、ユーザーのマシンにインストール済みであるか、アプリのオリジンから配信されている必要があります。キット自体は Web フォントを読み込みません。
  • zeyn.css の後に読み込みます。同じ詳細度のセレクターでは、後に書かれたものが優先されるためです。
  • 設定するのはセクション 2 の基本トークンです。セクション 3 の派生トークンは自動的に追従します。計算された値がブランドに合わない場合にだけ、派生トークンを上書きしてください。

ライト、ダーク、アプリテーマ

すべてのアプリのメニューには、言語ピッカーの隣にテーマピッカーがあります。System (prefers-color-scheme に従う)、Light、Dark、およびアプリが定義した任意のテーマを選べます。

App::new("Billing")
    .theme(Theme::new().brand("#7a2448").radius(6))
    .themes([
        Theme::light(),
        Theme::dark(),
        Theme::named("sand", [("--z-bg", "#f5f1ea"), ("--z-surface", "#fffdf8")]).label(t!("theme.sand")),
    ])
    .default_theme("system")
  • App::theme は組織の見た目で、すべてのテーマの下に適用されます。
  • App::themes はユーザーが選べるテーマを列挙します。指定しない場合、一覧は Light と Dark です。両方が一覧に含まれていれば System も提供されます。Theme::named(...) はライトテーマを土台に作られ、.dark_base() を付けるとダークテーマを土台にします。
  • App::default_theme は、まだ選択していないユーザーのテーマです。指定しなければ system です。
  • App::theme_picker(false) はピッカーを非表示にし、全員が既定のテーマになります。
  • テーマを 1 つだけにするとアプリが固定されます。.themes([Theme::named("acme", tokens)]) または .theme(Theme::dark()) を使います。

トークンはアプリの起動時に検査されます。App::check (したがって router と serve も) は、未知のトークン名、宣言から抜け出せてしまう値 (;、波括弧、\、<、>、url()、不正なテーマ名、未知の既定テーマ、そしてテーマによって WCAG 2.2 AA を下回るセクション 5 の色の組をすべて報告します。コントラストを検査できるのは #rgb と #rrggbb の値だけで、それ以外の色の記法は信頼されます。

すべてのテーマは 1 つのスタイルシート /_zeyn/theme.css として配信されます。このファイルは起動時に一度だけ構築され、キットのスタイルシートの後に読み込まれます。選択は zeyn_theme クッキーに保存され、ビューストア (App::datagrid_views) を持つアプリでサインイン済みのユーザーについては、そのストアにも保存されるため、新しいデバイスにも引き継がれます。サーバーは <html> 上にテーマを出力するので、最初の描画から正しい見た目になり、インラインスクリプトは不要です。

マークアップ結果
<html>System: prefers-color-scheme に従う
<html data-theme="light">常にライト
<html data-theme="dark">常にダーク
<html data-theme="light" data-zeyn-theme="sand">アプリテーマ sand (ライトテーマ上)

ピッカーでテーマを変更すると、これらの属性と color-scheme メタが変わるだけで、その後 POST /_zeyn/theme が呼ばれます。再読み込みは行われません。

手書きの上書きファイルでダークテーマに独自の値を与えるには、キットが使うセレクターを繰り返し書きます。

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --z-bg: #0e1012; }
}
:root[data-theme="dark"] { --z-bg: #0e1012; }

密度

--z-density は余白の単位を拡大縮小します。1 が標準、0.75 がコンパクトです。<html data-density="compact"> を指定すると、スタイルシートなしで 0.75 になります。コントロールとグリッドの行は 24 px より小さくならないため、コンパクトモードでも WCAG 2.2 のターゲットサイズを満たします。

仮想グリッドの行の高さはサーバーが固定します (Paging::virtual_rows(px)、既定は 32 px)。ランタイムがそこからスクロール位置を計算するためです。密度を変えてもこの値は変わりません。

2. 基本トークン

組織が設定することを想定しているトークンです。

トークン用途ライトダーク
--z-brand唯一のブランドカラー。プライマリボタン、アプリ名の帯、現在のメニュー項目、フォーカス中の行、フォーカスリング#0f6674同じ値。--z-accent を参照
--z-on-brand塗りつぶしの --z-brand 面の上の文字#ffffff#ffffff
--z-fontインターフェースのフォントスタック"Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, "Noto Sans", sans-serif同じ
--z-font-monoコード、API の例ui-monospace, "Cascadia Mono", "SF Mono", "Segoe UI Mono", Menlo, Consolas, monospace同じ
--z-font-size本文0.875rem (14 px)同じ
--z-font-size-smグリッドのセル、ラベル、フィルター0.8125rem (13 px)同じ
--z-font-size-xs列ヘッダー、メニューグループのタイトル、ページ情報0.75rem (12 px)同じ
--z-radius基本の角丸。0 から 12 px。すべてのコンポーネントがこれを基準に拡大縮小する4px同じ
--z-density余白の倍率。1 が標準、0.75 がコンパクト1同じ
--z-bgパネルの背後のページ背景#f4f5f6#121416
--z-surfaceパネル、グリッド、ウィンドウ、入力欄#ffffff#1a1d20
--z-surface-2グリッドヘッダー、ゼブラ行、ウィンドウのフッター、読み取り専用の入力欄#f7f8f9#202326
--z-hover行、メニュー項目、ボタンのホバー背景#eceef1#2a2e33
--z-border区切り線とパネルの輪郭#dcdfe3#33383e
--z-border-strong入力欄とボタンの輪郭、ヘッダーの罫線。--z-surface に対して 3:1 に達する必要がある#7d848d#7a818a
--z-text本文と値#1a1d21#e4e6e9
--z-text-mutedラベル、ヘッダー、補助的な文字。--z-surface-2 に対して 4.5:1 に達する必要がある#5b626b#a2a9b1
--z-dangerエラー、必須マーク、危険な行#b3261e#f28b82
--z-warning警告の行#8a5300#e6b45a
--z-success成功の行#1e6b3a#6fcf8f
--z-info情報トースト#2b5d8a#8ab8e6

3. 派生トークン

基本トークンから color-mix() と相対カラー構文で計算されます。基本トークンが変わると、自動的に更新されます。

トークン用途ライトダーク
--z-accent面の上で文字や細い線として使うブランドカラー。リンク、現在のメニュー項目、フォーカス中の行のバーvar(--z-brand)oklch(from var(--z-brand) 0.8 c h)。ブランドの色相を固定の明るさで使う。相対カラー構文のないブラウザーでは color-mix(in oklab, var(--z-brand) 45%, #fff) になる
--z-primaryプライマリボタンの塗りvar(--z-brand)var(--z-accent)
--z-on-primaryプライマリボタンの文字var(--z-on-brand)#121416
--z-primary-hoverプライマリボタンのホバーブランド 86% に黒を混ぜた色アクセント 84% に白を混ぜた色
--z-primary-activeプライマリボタンの押下ブランド 74% に黒を混ぜた色アクセント 70% に白を混ぜた色
--z-brand-subtle現在のメニュー項目とフォーカス中のグリッド行の背後の淡い色--z-surface の上に --z-tint の強さで重ねたアクセント同じ式
--z-focusフォーカスリングvar(--z-accent)var(--z-accent)
--z-danger-subtle, --z-warning-subtle, --z-success-subtle, --z-info-subtle行とメッセージの背景--z-surface の上に --z-tint の強さで重ねたステータス色同じ式
--z-tint淡い色の強さ9%20%
--z-space余白の単位calc(4px * var(--z-density))同じ
--z-controlボタンと入力欄の高さmax(24px, 8 * --z-space) (32 px)同じ
--z-rowページ分割および無限スクロールのグリッドの行の高さmax(24px, 8 * --z-space) (32 px)同じ
--z-gap画面セクション間の間隔3 * --z-space (12 px)同じ
--z-radius-sm, --z-radius-lg小さな部品 (フィルター、コード)、ウィンドウ--z-radius の 0.75x と 1.5x同じ
--z-shadow-2ウィンドウとトーストのみ。グリッドの行には影を付けない柔らかい 2 層の影より濃い影
--z-backdropモーダルウィンドウの背後rgb(20 24 28 / 40%)rgb(0 0 0 / 60%)
--z-dir論理プロパティのないオフセット(開始側または終了側を示す内側の影、スライドの変形)に使う読み方向の符号: calc(2px * var(--z-dir))1、dir="rtl" では -1同じ

サーバーが出力するマークアップが要素ごとに設定するトークンもあります。--z-width (ウィンドウの幅)、--z-flex (列の割合)、--z-row-height (仮想グリッドの行の高さ)、そしてコンポーネントライブラリ用の --z-stack-gap、--z-split、--z-progress、--z-columns、--z-level、--z-pop-x / --z-pop-y (ポップアップの位置) です。

チャートの系列色は components.css の --z-chart-1 から --z-chart-6 で、ダークテーマではより明るい値になります。それぞれ --z-surface に対して 3:1 に達しています。他の基本トークンと同じように上書きできます。

4. ブランドカラーの選び方

  • ライトテーマでは、--z-brand の上に白い文字が載ります (ボタン、アプリ名の帯)。--z-on-brand に対して 4.5:1 以上になる色を選んでください。中程度から濃いブランドカラーの多くは条件を満たします。黄色のように明るいブランドカラーの場合は、--z-on-brand も暗い色に設定してください。
  • ダークテーマでは、キットは文字、リング、プライマリボタンのためにブランドカラーを明るくし、アプリ名の帯には塗りつぶしのブランドカラーをそのまま使います。そのため、2 色目を用意しなくても、別のブランドカラーがどちらのテーマでも意図した見た目になります。
  • ステータス色はブランドカラーと区別できるようにしてください。赤いブランドカラーが赤いエラー行の隣にあると、エラーを見つけにくくなります。その場合は --z-danger を別の赤にずらすか、--z-brand-subtle を上書きしてグリッドからブランドカラーを外してください。
  • 色の確認には、セクション 5 の表を目標値として使ってください。基準を下回る組は WCAG 2.2 AA を満たしません。

5. 検証済みのコントラスト

実行中のサンプルアプリを Chromium で測定しました。すべてのトークンを (color-mix() と相対カラーを含めて) 描画後の sRGB 値に解決し、その上で WCAG 2.x のコントラスト比を計算しています。しきい値は、文字が 4.5:1、UI コンポーネントの輪郭とフォーカスインジケーターが 3:1 です。

組ライトダーク必要値
--z-text on --z-bg15.4914.774.5
--z-text on --z-surface16.9113.544.5
--z-text on --z-surface-215.9112.634.5
--z-text on --z-hover14.5510.934.5
--z-text on --z-brand-subtle (フォーカス中の行)14.799.274.5
--z-text on --z-danger-subtle14.739.764.5
--z-text on --z-warning-subtle14.829.314.5
--z-text on --z-success-subtle14.769.334.5
--z-text-muted on --z-bg5.657.784.5
--z-text-muted on --z-surface6.177.134.5
--z-text-muted on --z-surface-2 (ヘッダー)5.806.654.5
--z-text-muted on --z-hover5.315.764.5
--z-text-muted on --z-danger-subtle (淡色の行)5.375.144.5
--z-text-muted on --z-warning-subtle5.414.904.5
--z-accent on --z-surface (リンク)6.629.334.5
--z-accent on --z-bg6.0610.184.5
--z-accent on --z-brand-subtle (現在のメニュー項目)5.796.394.5
--z-on-primary on --z-primary6.6210.184.5
--z-on-primary on --z-primary-hover8.8211.264.5
--z-on-primary on --z-primary-active11.1212.264.5
--z-on-brand on --z-brand (アプリ名の帯)6.626.624.5
--z-danger on --z-surface (フィールドのエラー)6.547.094.5
--z-danger on --z-danger-subtle5.695.114.5
--z-warning on --z-surface6.338.904.5
--z-warning on --z-warning-subtle5.556.124.5
--z-success on --z-surface6.528.874.5
--z-info on --z-surface6.928.134.5
--z-focus on --z-surface6.629.333
--z-focus on --z-bg6.0610.183
--z-focus on --z-brand-subtle5.796.393
--z-border-strong on --z-surface (入力欄の輪郭)3.784.303
--z-border-strong on --z-surface-23.554.013

--z-border は装飾用 (すでに文字を持つ項目同士の区切り線) であり、3:1 は求められません。

6. その他のアクセシビリティ上の挙動

  • すべての操作可能な要素には、--z-focus の色で 2 px の :focus-visible リングが表示されます。グリッドの行ではリングを行の内側に描画するため、スクロールコンテナーで切り取られることはありません。
  • ボタン、入力欄、ソートヘッダー、フィルターボックス、ページャーボタン、ウィンドウの閉じるボタンは、どの密度でも 24 x 24 px 以上です。
  • prefers-reduced-motion: reduce の場合、トーストの登場アニメーションとすべてのトランジションが無効になります。
  • forced-colors モードでは、アイコンは CanvasText を使い、プライマリボタンとフォーカス中の行は Highlight を使います。背景の淡い色に頼っている状態 (現在のメニュー項目、行のステータス) には、代わりにアウトラインまたは枠線が付きます。