見た目の決まり
画面の見た目の決まりです (手を入れる人向け)。言葉の決まりは writing.md、画面ごとの説明は screens.md。
合言葉: 塗らない。囲みすぎない。浮かせすぎない。でも、ちゃんと伝わる。
並べ方は writing.md の順です (大きな決まりは 1 章、直す予定のずれは最後の「今とのずれ」)。 決まりはどこでも同じにし、部品ごとの例外は作りません。
1. 考え方
Section titled “1. 考え方”大きな決まりは 5 つです。2 章から下は、どれもこのどれかから来ています。
- ふだんは平らに。 アイコン・字・余白・揃えだけで組み、要らない地の色・枠・影・飾りは付けない
- 強弱は塗りではなく、見た目の順位で。 選んでいる・オンを強い色で塗らない。色は役目を決めて使う
- 奥行きは層で。 Liquid Glass から層の考え方だけを借りる。ぼかし・屈折・ガラスの照り返しは使わない (CSS では重い)
- 1 つの意味に印は 1 つ。 同じことを 2 つの印で言わず、1 つの見た目に 2 つの意味を持たせない
- 動きは CSS で、軽く。 JS では動かさない。時間で動き続けるものも使ってよいが、見えている間だけ・合成だけで済む形で動かし、ソフトウェアで描いている端末では止める (7 章)
2. 3 つの層
Section titled “2. 3 つの層”元: 3 奥行きは層で
置くものは地・件の面・操作の層の 3 つの高さに分け、それより上は作らない。
| 層 | 置くもの | 面 |
|---|---|---|
| 地 | 1 行の並び (サイドバー・設定の項目)・映像 | 平ら。線も影も付けない (--dp-bg) |
| 件の面 | 1 件が 2〜3 行ある一覧の件 (録画・予約・ルール・検索の結果) | 透かさない面 (--dp-item)。明るいほうは白とごく薄い影、暗いほうは地より一段明るい。縁は無し |
| 操作の層 | ヘッダー・その下に貼り付く操作の 1 行・下のタブ・画面の下の主の操作・貼り付いた日付の見出し・番組表の局名と時の列と「いま」の時刻・メニュー・ダイアログ | 面 (--dp-float か --dp-pop) と 2 段の影 (--dp-shadow-pop)。隙間を詰めて並ぶものは近い影だけ (--dp-shadow-tight) |
- 浮いたものの上に、さらに浮いたものを重ねない。 中のボタンは平らで、主の操作は太字だけ
- 例: 件の面・メニュー・ダイアログ・下から出す一枚・浮いた 1 行の中のボタン
- 主の操作は色ではなく層で示す。 主のボタンを操作の層に乗せ、ほかの操作は字とアイコンだけで平ら
- 例外: 縁に光を回す操作 (8 章) は浮かせない。光が主の操作の印になる (1 つの意味に印は 1 つ)
- 隙間を詰めて並ぶものは、近い影だけにする (遠い影が隣に掛かって段々に見えないように)
- 例: ヘッダーとその下の操作の 1 行、番組表の局名
3. 浮かせ方
Section titled “3. 浮かせ方”元: 1 ふだんは平らに・3 奥行きは層で
浮かせるのは、動く中身の上に重なるものだけ。 裏に何も来ないうちは平らに置く。
- 操作の置き場は 2 つの形のどちらか。 スマホの形は上のヘッダーを出さず、下のタブ (とそのすぐ上の操作の 1 行) に寄せる。広い形は上のヘッダーと、その下に貼り付く操作の 1 行
- スマホの形にする条件は
#lib/phone-form.tsの 1 か所で決める (いまは 1000px 未満と、折り畳み端末のテーブルトップ。タブレットの縦・横にしたスマホ・狭めた PC の窓もスマホの形)。 CSS は<html data-phone>の印で書き分ける。ダイアログを下から出す・行を折り返すなど、幅だけで決まるものは幅で書く - 下のタブは 640px 未満が 5 つ (録画・番組表・ライブ・検索・その他)、640px からは切り抜き・ルール・設定も並べて 7 つ (「その他」は出さない。テーマと新しいバージョンのお知らせは設定の中)
- 映像の画面 (ライブ・観る画面・追っかけ) も同じ
- 例外: 横にしたスマホの映像の画面は、映像を画面の高さいっぱいに出すため下のタブも隠す
- スマホの形にする条件は
- 中身が裏に入ったときだけ、面と影を出す (巻いた量に合わせて出る。仕組みは「動き」)
- 例: ヘッダー (広い形)。面は
--dp-float(メニューの面を 92% 残す)、字は 4.5:1 以上、端から離した角丸の面 - 例: ヘッダーの下に貼り付く操作の 1 行。番組表の種別・日付の 1 行 (広い形。ほかの画面の絞り込みは、広い形では右のサイドバーに入る)。
形はヘッダーと同じ (
app.cssの--dp-bar-*)、ヘッダーとの間は--dp-bar-gap(0.25rem)。この 1 行がある間はヘッダーも近い影にそろえる - 例: 日付の見出し。貼り付いて行がその裏を流れるときだけ、小さなカプセル (
scroll-state(stuck: top)) - 例外: 下のタブ (スマホの形) はいつも浮いたまま (いつも中身が裏を流れる)。同じ操作の 1 行も、スマホの形ではどの画面も下のタブのすぐ上にいつも浮かせる (ホームの絞り込み・番組表の種別と日付・切り抜きの絞り込み・ルールの絞り込みと並べ替え・設定の節の操作・検索の「ルールとして保存」)。
形は
app.cssの.dock(下のタブから--dp-dock-gap上のカプセル)。検索の「ルールとして保存」は 1 行の面を敷かず、ボタンだけを浮かせる- 例: 番組表は、種別・日付の 1 行を下のタブのすぐ上に浮かせる (表は画面の下の端まで伸ばし、この 1 行と下のタブは表の上に浮く)
- 例: 設定は、節へ飛ぶ行を下のタブのすぐ上に、見えている節の操作 (見出しの行の押すもの・絞り込み。チャンネルのスキャン・ログの絞り込みと「Issue にする」) をそのすぐ上に浮かせる。節を移ると入れ替わり、操作の無い節では出さない。768px からは左の節の一覧を残すので節へ飛ぶ行は出さず、節の操作を下のタブのすぐ上に浮かせる (広い形では節の中)
- 例外: 対応していないブラウザと動きを減らす設定の端末では、いつも浮いた形 (平らだと裏の中身と重なって読めない)
- 例: ヘッダー (広い形)。面は
- 透かすのは、裏が動くものだけ。 ぼかさない
- 例: 映像の上のメニュー・音量のつまみ・実況に書く欄は、テーマにかかわらず暗い面で映像を透かす (地を 75% 残す。真っ白な映像の上でも控えた字で 5:1)
- 例: ホーム画面から開いた iPhone のステータスバーは、暗いテーマのときだけ透かす (地を 85% 残した面)
- 例外: 明るいテーマのステータスバーは地の色で塗ったまま (時刻の字がいつも白いため)。ブラウザのタブでは色 (
theme-color) を合わせるだけ - 例外: 透かしを減らす設定 (
prefers-reduced-transparency) では透かさず、映像の上の影を濃くする - ページの中で開くメニュー・検索の候補・ダイアログは、後ろが動かないので透かさない
- 中身を読んでいる間は引っ込む。 スマホの形の下のタブ (とその上の 1 行) は、下へ送ると隠れ、戻すと出る。
#で飛んだ巻き取り (設定の節へ飛ぶ) では隠さない (飛んだ先の操作を見せる)- 例: 番組表は窓ではなく表の箱の中で巻くので、表の箱の縦の巻き取りで引っ込む。横に送っても、開いたときの「いま」への位置合わせや時刻へ送るボタンで動いても隠さない。出し入れで表の箱の大きさは変わらない (中身が跳ねない)
- 例外: 広い形のヘッダーは引っ込めない (検索を見せたまま)
- 例外: テーブルトップの下のタブは引っ込めない。下の段は低く、右の列は中で巻くので、読み進めても下のタブの裏に隠れる中身が無い
画面ごとの組み方は次のとおりです (上の決まりの例)。
- 一覧の画面 (録画・切り抜き・ルール・設定・検索) はページごと巻き、広い形では中身がヘッダーの裏を流れる。頭に
--dp-header-space(ヘッダーの下端から--dp-bar-gap。下に貼り付く操作の 1 行・日付の見出し・サイドバーも同じ隙間) を空け、スクロールバーは窓の右端に出る。 サイドバーは地のまま、ヘッダーの下に貼り付けて動かさない (あふれたらサイドバーの中で巻く) - 観る画面は、広い形では箱をヘッダーの下から始める (
+layout.svelteの.shell.fill。裏まで伸ばすと箱のスクロールバーだけが透ける)。窓が巻けないので、ヘッダーはいつも平ら- 二段組 (映像と右の列) にするのは 1000px から (ヘッダーを出す幅と同じ線)。それより狭いと横長の窓でも、映像を端から端まで出し、右の列 (局の一覧・番組の中身) を映像の下に積んでページごと巻く。 横にしたスマホは映像を画面の高さいっぱいにし、右の列は映像の下で巻き取る (下のタブは隠す。映像の下端に重ねない)
- 折り畳み端末を半分に折って机に置いたとき (テーブルトップ。
#lib/phone-form.tsのTABLETOP) は、上の段を映像だけにし、映像の枠を段の幅か高さいっぱいに収める (縦横比は保つ。余りは黒)。右の列は下の段。折り目には何も置かない。 上下の段はどちらも低いので、操作はスマホの形に寄せる。上のヘッダーは出さず、下のタブを下の段の下端に浮かべる。 下の段は舞台の中で、黒い地にアンビエントの光の代わりの反射を敷き、右の列と下のタブをその上に重ねる (右の列は中で巻く。どちらも暗いテーマの色)。全画面はページごと (並べ方を崩さない)。当たらないブラウザ・端末では何も変わらない
- 番組表 (広い形) は表をヘッダーの裏まで伸ばし、操作の 1 行と局名の行をその下に貼り付ける。ヘッダーは窓ではなく表の巻いた量で浮く
(表の箱の時間の軸
--guideをtimeline-scopeで外枠まで見せる)。スマホの形では表を画面の上から下の端まで伸ばす- 表のスクロールバーは出さない (ヘッダーの裏に透ける)。つかむ・ホイール・キー・指で動く
- 局名は局ごとの角丸の面 (列と一緒に横へ動くので、1 本につながった面にしない)
- 時の列は操作の層。横に送ってマスがその裏に入ったときだけ面を出し (表の箱の横の軸
--guide-x)、影はマスの側 (右) にだけ落とす - 左上の角は地の色で平らに塗り、縦に巻いて裏に来る時の数字を隠すだけにする。面は時の列と同じく横に送ったときだけ出す (局名の行に詰めて並ぶので近い影)
- 局名の行より上 (ヘッダーと操作の 1 行の裏) へ巻かれた時の列は、角の上に伸ばした地の色で覆う。浮いた面の横の隙間から、時の数字や「いま」の時刻が覗かないように (操作の層の裏から、別の操作の層を見せない)
4. 印の意味
Section titled “4. 印の意味”元: 2 強弱は見た目の順位で・4 1 つの意味に印は 1 つ
印は意味ごとに 1 つの見た目に決め、どこでも同じ意味で使う。
| 見た目 | 意味 |
|---|---|
| 文字の幅いっぱいの下線 (横に並ぶもの) | いまいる場所・選んでいるもの (ヘッダーのナビ・種別・日付・チップ) |
| 左の縦線 (縦に並ぶもの) | いまいる場所・選んでいるもの (サイドバー・局・開いている行・メニューの選択肢)・自分のもの (実況の自分の書き込み) |
| 短い下線 | 機能がオン (プレイヤーの字幕・番組表の「サブ ch」など) |
| 主の色の太字 | 線と組んで、選んでいる・オンを補う |
| ごく薄い地 / 件の面を一段変える | 指を乗せた (地の上の行 / 件) |
| 外側の 2px の線 | キーボードで触っているところ (焦点) |
| ごく小さな縮み | 押した手応え |
| 状態の色 (赤・緑・黄) | 録画中・問題・成功・注意。バッジ・通知・進み具合のバー |
| 淡い地のカプセル (バッジ) | 状態だけ (録画中・有料・無効・競合) |
| 主の色の小さな点 | まだ見ていないもの (未視聴の録画・新しいバージョンが出ている設定の入口) |
- 下線は長さで意味を分ける。 幅いっぱいは「選んでいる」、短い線は「オン」
- 1 つの部品に線は 1 本。
- 押されているボタン (
aria-pressed="true") は、並び (role="group") の中なら選んでいるもの (幅いっぱいの下線・左の縦線)、並びの外ならオン (短い下線)。どちらか 1 本だけを、置いた場所で決める (app.cssの「入っている切り替え」) - 例: ホームの絞り込みのチップ・ライブの種別のタブは並びの中なので短い下線を引かない。番組表の「サブ ch」・映像の上の字幕・ライブの「ライブ」は並びの外なので短い下線
- 押されているボタン (
- 事実はバッジにしない。 番組の事実 (ジャンル・映像・音声) は、一覧の補足と同じく控えた字を「・」でつなぐ
- 例: 検索の「録る」「録らない (理由)」は行の補足に、観る画面の「端末」は時間帯・局の前に、引き継ぎの「取り込み / 下見」とスキャンの段階は控えた字で
- 例: 数はその行のほかの数と同じ形 (チャンネルの表のロゴの数は、チャンネル・局の数と同じ太字)
- 例: 押せる事実は控えた字に印を添える (ヘッダーの「v… が公開されています」は外へ出るリンクの印)
元: 2 強弱は見た目の順位で
色は役目ごとに決め、選んでいる印と状態で混ぜない。
| 色 | 使うところ |
|---|---|
主の色 (--pico-primary) |
線 (下線・左の縦線・オンの短い線)・焦点の線・リンク・見せる画面の地の淡い光 (8 章) |
淡い地の上で字にする主の色 (--dp-primary-text) |
選んでいるもの・オンの字とアイコン・土曜 |
| 本文の字 | 題名・本文 |
控えた字 (--dp-muted) |
説明・補足 (日時・局・状態)・押せない日 (カレンダー)・選べないチップ |
録画の赤 (--dp-rec)・緑 (--dp-ok)・黄 (--dp-warn) |
状態だけ。選んでいる印には使わない |
案内の青緑 (--dp-info) |
案内の通知 (ⓘ) と案内のバッジだけ。主の色の azure と並ぶと見分けが付かないので、緑寄りの cyan にする |
- 読める濃さを保つ。 字は 4.5:1 以上、線とアイコンは 3:1 以上。両方のテーマで、指を乗せた地・選んだ行の上でも確かめる (e2e
48-link-hover・45-buttons)- 例: 状態の色を字やアイコンにするときは
-textの側 (--dp-rec-textなど。淡い地の上で 4.5:1 になるよう濃くした段)
- 例: 状態の色を字やアイコンにするときは
- 例外: 番組表の日付の日曜は録画の赤の字 (
--dp-rec-text)。暦の慣わしに合わせ、土曜の主の色と対にする - 色だけに頼らない。 選んでいるものは線と太字、オンは線、状態は印 (点・✕・⚠) と言葉を添える
- ブラウザが描く部品もテーマに合わせる (
color-scheme)- 例: プルダウンの一覧・スクロールバー・日付の選び方が、暗いテーマで白く出ない
- 映像の上はテーマで変えない (舞台はいつも黒い)
- 例: 字
--dp-on-video・控えた字--dp-on-video-muted・オン--dp-on-video-on(空色)・戻せないものの 2 回目と「今」--dp-on-video-danger(赤)・ メニューの面--dp-video-menu・その影--dp-video-shadow・つまみの入れたぶん--dp-video-fill・覆い--dp-video-veil・再生位置のバーの CM の所--dp-video-cm(観たところは--dp-video-cm-played) - 例: テーブルトップの下の段 (3 章) は黒い地。右の列はテーマにかかわらず暗いテーマの色で描く (
data-theme="dark")。敷く反射の明るさはアンビエントの光と同じ上限で、上の控えた字も 4.5:1 を保つ
- 例: 字
元: 1〜4 の全部。部品ごとに、上の決まりをどう当てるかを書く
選んでいるもの (ナビ・サイドバー・タブ・チップ・カレンダー)
Section titled “選んでいるもの (ナビ・サイドバー・タブ・チップ・カレンダー)”塗らずに、主の色の線と太字で示す。
- 通常は地を塗らず、字は本文 (ナビも薄くしない)
- 選んでいるものは主の色の線 (横は下線、縦は左の縦線) と主の色の太字。行全体を塗らない
- 線の太さは
--dp-mark(2px)。選び直しても滑らない横の並び (チップ・番組表の日付) の下線はapp.cssの.pick-line - 例: チップは枠も地も付けず、入っているものは主の色の太字と下線。形と大きさはどの画面も
app.cssの.chip1 つ (切り替え・絞り込み・設定の節へ飛ぶ行) - 例: 下のタブはアイコンの下に字 (640px から 7 つ並べても同じ形)。選んでいるタブはほかの横の並びと同じく、字の幅いっぱいの下線と主の色の太字。同じ画面に印を 2 つ付けない (タブに出した画面は「その他」に入れない)
- 例: カレンダーの選んでいる日は下線と字。行のある日は本文の字、無く押せない日は控えた字、今日は赤い点 (「いま」と同じ赤)
- 例外: 番組表の日付の土日は、選んでいても土日の字の色のまま (線は主の色)
- 例外: 録画の注意の絞り込み (競合・失敗・録り逃し・CM の判定) は、選んでいても注意の色の字のまま (線は主の色)。状態の色を選んでいる印で消さない
- 線の太さは
- 指を乗せたら地をごく薄く (
--dp-hover-soft)。選んでいる印とは混ぜない
何行かある一覧
Section titled “何行かある一覧”1 件ずつ件の面に乗せ、線で区切らない。
- 件は
.item-listに並べ、件の間は--dp-list-gap(6px)。1 件の中は近づけ、件の間は離す- 例外: ホームは日付の見出しを貼り付けるため、日ごとのまとまり (
.t-block) に並べる。件の間は同じ--dp-list-gap
- 例外: ホームは日付の見出しを貼り付けるため、日ごとのまとまり (
- 題名がいちばん強く、説明・補足 (日時・局・状態) の順に控える。サムネイルと字の始まりを揃える
- 字の大きさは、1 段目 (題名) が本文 (
--dp-text-base)、2 段目からが控えた字 (.small。--dp-text-sm)。どの一覧も同じで、2 段目を.tinyにしない
- 字の大きさは、1 段目 (題名) が本文 (
- 右端のボタンはその件の中に置き、位置を件ごとに同じにする。ボタンは平ら
- 反応は件とボタンで分ける。件は面を一段変え (
--dp-item-hover)、ボタンは自分だけ (削除に乗せたときだけ赤い字)- 例: 選んでいる・開いている件は左の縦線と太字で、面は塗り替えない
- 例: 削除・取消・連携の解除のような戻せない操作は 2 回押しで確かめる (writing.md)
- 指の端末でも押せる範囲を保つ。行は
--dp-item-h(64px。指の端末では 72px)、行の中のボタンは高さ 40px 以上 - 設定の中の一覧も同じ (通知先・テレビのアプリ)。1 行で済む設定の項目だけが地に並ぶ
- 件の中の線は面の端に付ける
- 例: 進み具合のバー (録画中・エンコード・ダウンロード・端末への保存) は件の面の下端いっぱいに付け、面の角で切り落とす
- 「いま」は地に置き、線は引かない。赤い点と赤い太字の時刻、すぐ後ろに控えた字で次の録画までの一言。左は日付の字にそろえる
強弱は層と字の太さで付け、色では塗らない。 色と形は app.css の --dp-btn-* で決め、画面ごとに塗り直さない。
- 主のボタン (クラス無し・
.small・.xs・.saveなど) は、操作の層の面 (--dp-float) と影のカプセルで、字は本文の色の太字- 1 画面に 1 つか 2 つ。保存などは画面の下に置き、中身を送っても見えたままにする
- 例外: 浮いたものの中では浮かせず太字だけ (
--dp-btn-solidと--dp-btn-liftを消す。「3 つの層」)
- 控え (
.secondary・.outline) と字だけ (.ghost) のボタンは平ら。地も枠も影も無く、乗せたら薄い地 - 戻せないものの 1 回目 (
.outline.danger) は控えと同じで、乗せたときだけ赤い字。確定 (.danger) は主と同じ層で赤い字 (赤では塗らない) - 入っている切り替え (並びの外の
aria-pressed="true") は主の色の字と短い下線。塗らず、浮かせない - 形はカプセル (
--dp-radius-full)、アイコンだけなら円- 例外: 自前の見た目を持つもの (行・メニューの項目・タブ・チップ・リモコン) は名前のクラスを持たせ、浮かせない
線も凹みも付けず、平らな面だけで示す。
- 面 (
--dp-field) は下を透かす薄い色で、地・件の面・ヘッダーのどれの上でも一段濃い (暗いほうは明るい) 面になる - ラベルはいつも見せる (中の字だけの案内に頼らない)。打っている間は外側の 2px の主の色の線
- 例外: 面と地の差は 1.24:1 (明るい)・1.31:1 (暗い) で、境目の 3:1 (WCAG 1.4.11) には届かない。ラベルと焦点の線で補う
メニュー・ダイアログ・検索の候補
Section titled “メニュー・ダイアログ・検索の候補”どのメニューも同じ形にし、縁を付けず影だけで浮かせる。
- 形は箱の内側の余白 8px・項目の間 2px・項目の余白 8px × 12px。色だけ層で変わる (ページは
--dp-pop、映像の上は暗い面、ダイアログは--dp-surface)- 例: ページのメニュー・映像の上のメニュー (再生の設定・速さ)・プルダウンの一覧
- 中のまとまりも線で分けず、余白と、要るときは控えた字の見出しで分ける
- 選択肢の選んでいるものは左の縦線と太字、オン/オフの項目は右のスイッチ (
.s-switch。映像の上の再生の設定は白い溝のスイッチ)。オン/オフに左の縦線は使わない- 例: 番組表のジャンル・映像の上の速さ (選択肢の左の縦線)
- 例: ホームの「⋯」の表示の切り替え・番組表の狭い画面の「サブチャンネルも表示」
- ダイアログと番組の詳細のボタンは右下に並べ、主はいちばん右。640px より狭い画面 (下から出る一枚) でも全幅にせず、入りきらない分は上の段へ折り返す (主は下の段の右端)
- 例: 録画の詳細は「閉じる」と「再生」が下の段の両端、「端末に保存」「ルールを作る」「その他…」はその間か上の段
- ダイアログ (
showModal()で開いた<dialog>。番組の詳細も同じ) は、開いている間は後ろのページを巻かず、Tab で動く焦点もダイアログの外へ出さない (app.cssのhtml:has(dialog:modal))。スクロールバーの幅は空けたままにして、中身を横にずらさない- 例外: 番組の詳細 (
.detail-modal) は幅を空けない。画面いっぱいに置く箱が空けた幅の分だけ狭くなるため
- 例外: 番組の詳細 (
- 横に開く子メニューは使わない (指の端末には乗せる操作が無く、よく使う項目までひと手間増える)
- プルダウンの開いた一覧も、マウスの画面ではメニューと同じ姿 (
appearance: base-select)- 例外: 指の端末と、対応していないブラウザはブラウザ・OS の選び方のまま (画面の下から大きく出て、指で選びやすい)
種類は頭のアイコンと言葉で示し、地は状態の色で塗らない。
- アイコンは失敗 ✕・注意 ⚠・成功 ✓・案内 ⓘ。状態の色 (
-textの側) はアイコンにだけ使う。字は本文の色 - ページの中の通知は淡い面 (
--dp-subtle)、右下のトーストは操作の層の面 (--dp-pop) と影 - 左の線は引かない (左の縦線は「選んでいる」の印)
番組表のマス
Section titled “番組表のマス”マスは淡い面にし、状態は小さな印と言葉で示す。
- マスは淡い面 (
--dp-subtle。ジャンルのあるマスは--dp-surfaceにジャンルの色をごく淡く混ぜる)。枠も影も付けない - ジャンルは時刻の前の小さな点 (ジャンルのメニューの見本も同じ点)。左の太い線は使わない (左の縦線は「選んでいる」の印)
- 予約の状態は時刻の後ろの印と、説明の代わりの太字の言葉。印は説明の行が切れる短いマスでも見える
- 例: 予約済みは主の色の ✓、録画中は赤い ●、競合は赤い ⚠ (競合を予約済みと同じ青にしない)
- 塗るのはいま動いている録画中だけ (録画の赤をごく淡く)
- 放送が過ぎた所は、マスの上から地の色を重ねて沈める。表を横切る「いま」の線は引かず、時の列に「いま」の時刻のカプセルだけを置く (分だけ)
- 例: 終わった番組はマス全体、放送中はマスの上端からいまの位置まで浅く
- 境目はどの局もいまの時刻の位置にそろえる。マスは 5 分に合わせて置くので、覆いの高さはマスの上端と下端から出し、終わったかどうか (濃さ) は実際の終わりの時刻で決める
映像の上の操作
Section titled “映像の上の操作”ボタンは塗らず、オンは空色の短い線で示す。
- 明るい映像でも読めるよう、並び全体の裏に端から消えていく黒い影を敷く (
ControlBar)。アイコンと字にも小さな影- 影とボタンは別の段に置き、間に映像に重ねるものを挟む。重なりは 映像 → 影 → 実況のコメント → 字幕 → ボタン (下の操作バーも右端の列も同じ)。コメントが影に潜って暗く欠けて見えないように
- オフは白、オンは空色のアイコンと下の短い空色の線。線はほかの所と同じ「入っている切り替え」の短い下線で、色だけ字に合わせる (
--dp-on-mark)。オンは押した後も残し、いまの設定がいつでも分かるようにする- 例外: 戻せないものの 2 回目と、「今」へ張り付いている「ライブ」「最新」は赤い字と赤い短い線
- 指を乗せた・キーボードで触っているボタンだけ、ごく薄い地を出す
- 読み上げには
aria-pressed(切り替えと、「今」へ張り付いているか)、マウスにはtitleで名前を渡す。メニューを開くボタン (速さ・⚙) はaria-pressedではなくaria-expandedで開いているかを言う- 例: 速めているときの速さのボタンは数字だけを出し、オンの線は重ねない (数字が「速めている」の印。1 つの意味に印は 1 つ)
- 映像の上のつまみ (再生位置のバー・音量) は、入れたぶんをほかの所と同じ主の色を透かして (
--dp-video-fill。#0172adの 80%) 塗る。色はそろえ、広く塗っても映像が透けて重くならないようにする。白い字・アイコンはその上で、黒い舞台なら 7:1、白い映像の上でも 3.8:1 (小さな影で補う)。指で押せる高さは 44px 以上 (縦のつまみは幅)- 例: 再生位置は細い溝 (4px) に同じ色の丸 (12px)。丸は触っている間だけ出し、押せる所を上へ広げる
- 例: 音量は太いカプセル (
VolumeSlider)。カプセルの全体がつまみで、入れたぶんを下 (横なら左) から塗る。細い溝と丸は無い。空の所には面を敷き (⚙ の中では黒で沈める)、面は塗りの下には敷かない (透かした塗りの向こうが映像になるように)。塗りと空の所の差は黒い舞台の上で 2.5:1 ほどで、3:1 には届かない (目立たなさを選んだ。中のアイコンと値で補う)。中の端にスピーカーのアイコン (0%・半分より下・それより上で替える)、値は変えている間だけ反対の端に出して少し経つと消す。キーとホイールでは塗りが短く (--dp-duration) 追いつき、つまんでいる間はそのまま付いてくる - 例: マウスの端末では、音のボタンに乗せると真上に縦のカプセルが浮かぶ (箱には入れず、カプセルの地が映像の上のメニューと同じ面)。⚙ では行の幅の横のカプセルにし、中にアイコン・名前・値をほかの行と同じ並びで置く。つまみは画面に 1 つだけで、⚙ に置くのは音のボタンがつまみを出せないとき (指の端末と、音のボタンを ⚙ に畳んだ狭い枠) だけ
元: 5 動きは CSS で、軽く
動きは CSS だけで作り、時間で動き続けるものは重さの決まりを守る。 時間と曲線は app.css の名前から選び、画面ごとに数字を書かない。
| 時間 | 長さ | 使うところ |
|---|---|---|
--dp-duration |
120ms | 色・地・影の移り変わり、押して縮む |
--dp-duration-enter |
240ms | 開く・出てくる (メニュー・ダイアログ・番組の詳細・下から出す一枚・件・画像) |
--dp-duration-move |
300ms | 場所が動く (選んでいる印が滑る・並べ直した行が滑る・浮いた 1 行の出し入れ・スイッチ・縮んだものが戻る) |
--dp-duration-exit |
150ms | 閉じる・消える (待たせないよう短く) |
曲線は 3 つです。ふつうは --dp-ease (終わりをゆっくり)、開く・滑る・戻るは --dp-ease-spring (少しだけ行き過ぎて戻るばね)、閉じるは --dp-ease-exit (頭をゆっくり)。
- JS では動かさない。
requestAnimationFrame・setIntervalなどで値を毎フレーム書き換える動きと、JS のアニメーションのライブラリは使わない- JS で動かすと毎フレームがメインスレッドを通り、合成だけで済む CSS の transform・透明度より重い。メインスレッドが混むと動きも止まる。
測った値 (GPU を切った Chrome、止まって 5 秒、小さな箱 1 つ): CSS の transform 3%、JS の
requestAnimationFrameで毎フレーム 7% - Web Animations API (
element.animate()) は CSS と同じ仕組みで動くが、動きの定義を CSS (app.cssの名前) に集めるため使わない - JS がするのは、状態の印 (クラス・
data-*) を付け外しすることと、行き先を渡すことだけ。動きはそれを受けた CSS がする - 例: 読み込めた画像 (下の「件」)・ボタンで送る (下の
scroll-behavior)・入れ替わる色 (下の 2 枚の重ね) - 対象外: 画面の動きではなく中身を描く処理。映像の描画 (WebGL)・字幕の点滅 (放送が指定した点滅を
caption-draw.tsが描く)・データ放送のタイマー・番組表のマスの描き足しと大きさを測る処理
- JS で動かすと毎フレームがメインスレッドを通り、合成だけで済む CSS の transform・透明度より重い。メインスレッドが混むと動きも止まる。
測った値 (GPU を切った Chrome、止まって 5 秒、小さな箱 1 つ): CSS の transform 3%、JS の
- 開くものは、少し小さく少しずれた透明な姿からばねで出る (
@starting-style)。閉じるときは短く引っ込む- 例: メニュー・プルダウンの一覧・ダイアログ・番組の詳細
- 例外: 番組の詳細は閉じるとすぐ消える。開いた画面が
{#if}で外すので、引っ込む間の姿が残らない
- 選んでいる印は、前の場所から滑る。 並びの疑似要素 1 本を CSS のアンカーで選んでいる項目に付ける。開いた直後はその場に出る
- 例: タブの下線・ヘッダーのナビの下線・下のタブの印・サイドバーとライブの局の一覧の左の縦線 (縦に滑る)。実況に書く色・位置・大きさもタブと同じ並び
- 例外: アンカーに対応していないブラウザは項目の中に線を引く (滑らない)
- 項目がほかのアンカー名を持つときは、包む要素に名前を付ける (例: 640px 未満の下のタブの「その他」。
Menuがメニューを置くための名前をボタンに付ける)
- 並べ直した行は、前の場所から滑る。 JS は並べ直す前と後の行の位置を測り、差を行に渡して (
--slide-from) 前の場所にずらした印 (data-slide) を付け、すぐ外すだけ。元の場所へ滑らせるのは CSS の transition (--dp-duration-move・ばね)- 例: ライブの「実況」タブ (勢いの順。
live/+page.svelteのslide) - 使っている一覧は並べ直さない。 指・ポインタが乗っている間とキーボードのフォーカスがある間は待ち、離れて少したってから並べ直す。押そうとした行が別のものに替わらないように
- 例: ライブの「実況」タブ (勢いの順。
- 浮くものは、巻いた量に合わせて面と影が出る (
animation-timeline: scroll()。番組表では表の箱の縦の軸--guideと横の軸--guide-x)。巻けない短いページでは平らなまま- 例: ヘッダーとその下の操作の 1 行・番組表の局名と時の列
- 押すと主のボタンは少し縮み (97%)、離すとばねで戻る。 押した後の状態は色と線で示す
- ボタンで送るときは、巻く箱の
scroll-behavior: smoothで滑らせる。 JS は行き先を渡すだけ (behaviorを渡さない)。長さはブラウザに任せる- 例: 番組表の時刻へ送るボタン・横にしたスマホのライブで一覧から選んだとき (映像へ戻る)
- 開いたときの位置合わせと、マウスでつかんで動かすときは
behavior: "instant"で止める (動きを作るのではなく止めるだけ) - 動きを減らす設定では
scroll-behavior: autoに戻す (時間のトークンは効かない)
- 入れ替わる色は、2 枚を重ねて透明度の transition で入れ替える。 新しい色を裏の 1 枚に描いてから、表の印だけを付け替える。2 枚は足して重ね (
plus-lighter)、途中で透明度が抜けないようにする- 例: アンビエントの光 (
AmbientGlow.svelte)・テーブルトップの反射 (FoldReflection.svelte)。写す繰り返しと 2 枚の入れ替えは共通の部品 (glow-loop.svelte.ts)
- 例: アンビエントの光 (
- 件は透明度だけでふわっと出る。 場所は動かさず、読んでいる所をずらさない
- 例: 読み込めた画像も同じ (
#lib/motion.svelte.tsのfadeInは読み込めた合図で印を付けるだけ)
- 例: 読み込めた画像も同じ (
- 明暗を選び直したときは、画面全体を前の色から次の色へ透明度だけで移す (
--dp-duration-move)。JS は View Transitions を始める合図だけで (theme.svelte.tsのcrossfade。種類theme)、動きはapp.cssの「明暗の切り替え」- 地・件の面・ヘッダーはそれぞれ自分の色を持つので、1 つの要素に transition を付けると、ほかが先に替わって段違いに見える。写しを 1 枚撮って重ねれば、移る間に色を描き直さない
- 開いたとき (
app.htmlが描く前に当てる) と、端末の設定に従って替わるときは移さない - 例外: 映像を流している間はすぐ替える (移る間は写しが映像を止めて見せる)
- 例外: ソフトウェアで描いている端末 (
<html data-lite>) でもすぐ替える (画面いっぱいの 2 枚を重ねるのも CPU がやる)
- 時間で動き続けるものも使ってよい。ただし重さの決まりを守る。 動き続けると、止まって見ているだけで毎フレーム合成する
- 例: 録画中の点・動いているバーの光・読み込み中の骨組みの光・ページを移る途中の線
- 例: アンビエントの光とテーブルトップの反射 (映像の色を 1 秒に 5 回写して描き直す)。動きを減らす設定と
data-liteでは 1 枚写して止める - 見えている間だけ動かす。 画面の外では
content-visibility: autoで描くのをやめる (何もしないと、画面の外でも回って CPU を使う)- 大きさが中身で決まる箱には
contain-intrinsic-sizeを一緒に書く。書かないと画面の外の箱の高さが分からず、巻くと高さが跳ね、スクロールバーが伸び縮みする (大きさを決めてある箱は要らない) - 描くのをやめた箱の中の要素は、CSS のアンカーの基準になれない。選んでいる印をアンカーで付ける並びの中には使わない
- 行のように中にアンカーの基準がある箱には付けず、動くものだけを包む小さな箱 (点の入れ物・バーそのもの) に付ける
- 例: ホームの一覧の動いているものの点 (
.act-mark) と進み具合のバー (.row-bar)
- 例: ホームの一覧の動いているものの点 (
- 大きさが中身で決まる箱には
- ソフトウェアで描いている端末 (GPU が無い・ハードウェアアクセラレーションを切った) では止める (合成まで CPU がやる)。開いたときに 1 回だけ見分けて
<html data-lite>を付ける (#lib/lite.ts)- 見分けは小さな WebGL の箱を作って描き手の名前を読み、すぐ捨てる。名前が SwiftShader・llvmpipe・softpipe・Microsoft Basic Render Driver など (新しい名前が分かったら足す) か、WebGL を作れなければソフトウェア
- 例外: 描き手の名前を読めるのは Chrome・Edge だけ。Firefox は丸めた名前、Safari は「Apple GPU」しか出さないので、見分けられずに回り続ける (止まらない側に倒す)
- 回す面は小さく、合成だけで済む transform と透明度で動かす。 色・背景の位置・
@propertyの角度は毎フレーム描き直すので重い - コマ送りでよいもの (点滅など) は
steps()でコマを落とす - 巻いた量で動かす (
animation-timeline: scroll()・view()) のも軽くする手段の 1 つ。止まって見ている間は何も描き直さない- 例: 浮くものの面と影 (上の「浮くもの」)・8 章の見本・画面の紹介・数字・縁の光
- 動きを減らす設定では止める (下)
- 測った値 (GPU を切った Chrome、止まって 5 秒、
data-liteを付けない素のページに 1 つだけ): 小さい箱の透明度・transform 3%、同じ動きのsteps(8)1.6%、色・背景の位置 (描き直し) 5〜8%、 600×600 の回転 15%、画面の外 1.6%、画面の外 +content-visibility: auto0%。corp のトップの見本と縁の光は、時間で回すと 6〜19%、巻いた量に変えて 0% (danything/corp#20)。 今の corp は時間で回し、画面の外とdata-liteで止めている (danything/corp#22)
- 動きを減らす設定 (
prefers-reduced-motion) では、動きを止めて終わった姿を見せる (app.cssの最後)- 移り変わりは時間のトークンを 0 に、繰り返すもの (録画中の点など) は動きを付けない (動きを足すのは、動きを減らす設定でも
data-liteでもないときだけ) - 巻いた量の動き (8 章の見本など) も
animation: noneにし、組み上がった後の素の姿で見せる。時間のトークンは巻いた量の動きには効かないので、0 にしても止まらない - 例: 浮くものはいつも浮いた形 (3 章)。Pico がアニメーションをすぐ終わらせるので、外さなくてよい
- 移り変わりは時間のトークンを 0 に、繰り返すもの (録画中の点など) は動きを付けない (動きを足すのは、動きを減らす設定でも
8. 見せる画面
Section titled “8. 見せる画面”元: 1 ふだんは平らに
操作するより伝えるための画面 (ログイン・空の状態・初めての案内・紹介のページ) は、平らから一歩出てよい。 動きの決まり (7 章) はそのまま守り、作業する画面 (一覧・番組表・観る画面・設定) には持ち込まない。
- 作業する画面の中でも、まだ何も無くて次にすることを案内する所 (空の状態) は見せる画面にする。絞り込んで当てはまらなかっただけの所は、作業の結果なので平らのまま
- 空の状態の並べ方は、どの一覧も
app.cssの.empty-state(一覧の場所の真ん中に、太字の一言・控えた字の案内・ボタン)。見せる画面にするときだけ.glowを足す
- 空の状態の並べ方は、どの一覧も
- 下の例のうち denpa にまだ無いものは、corp (doany.io) のトップの作り (danything/corp#20) を見る
- 主の色の淡い光を地に敷いてよい。
radial-gradientの円を中身の裏に敷くだけにし、ぼかし (filter・backdrop-filter) は使わず、動かさない- 光は
app.cssの.glow(濃さ--dp-glow)。光の上でも字は 4.5:1 以上 (控えた字で明るい 5.5:1・暗い 6.2:1) - 例: 1 つだけ言う画面 (
.solo。ログアウト・テレビのペアリング・開けなかったとき) - 例: 空の状態 (ホームの「予約も録画もありません」・切り抜き・ルール・端末に保存した録画・チャンネル)
- 例: docs のサイトのトップの見出し (
site/src/pages/index.astro) - corp のトップの光は事業者サイトとして外していて、主の色に紫と青緑を混ぜ、頭の光は巻くと少し流れる。denpa は主の色 1 色で、動かさない
- 光は
- 何をするものかは、画像の代わりに小さな実物の見本で見せてよい。 見本は件が巻いて見えてくるのに合わせて組み上がる (件の
view-timeline) か、時間で回す (7 章の重さの決まりを守る)。 素の姿を組み上がった後にしておき、対応していないブラウザと動きを減らす設定では、はじめから出そろって見せる。見本は飾りなので読み上げない (aria-hidden)- 例: denpa にはまだ無い (corp の事業の件で、デプロイのログが 1 行ずつ出る・車検証の項目が埋まる)
- 例: 時間で回す場合。corp のトップは、止まって見ていても動いていると分かるよう見本を時間で回し、画面の外と
data-liteでは止めている (danything/corp#22)
- 画面の紹介は、説明の横に画面を貼り付けて重ねる。 次の説明に入ると次の画面が重なり、前の画面は少し沈む。
重ねるのは
position: sticky(画面を自分の行から最後の行までの升に置く)、沈むのは次の説明のview-timeline- 例: denpa にはまだ無い (corp のトップの自社サービス)
- 数字は巻くと数え上がってよい。
@propertyの<integer>をview()で動かし、counter()で出す。対応していないブラウザでは書いてある数字をそのまま出す- 例: denpa にはまだ無い
- いちばん大事な操作が 1 画面に 1 つだけなら、その縁に主の色の光を回してよい。 縁の裏に敷いた
conic-gradientのrotateだけを、巻いた量か時間で回す- 光を回す操作は浮かせない (主の操作の印を、層の代わりに光にする。1 つの意味に印は 1 つ)
- 例: denpa にはまだ無い (corp のトップの「お問い合わせ」は時間で回している)
- 時間で回すなら、ボタンの幅ちょうどの正方形の
rotateがいちばん軽い (GPU を切った Chrome で 5%。offset-pathで縁を走る粒は 9%、@propertyの角度は描き直しで 13%)
- 確かめ方: GPU を切った Chrome (
--disable-gpu --disable-gpu-compositing) で、巻いている間のフレームの間隔と、止まって 5 秒の CPU を測る。巻いている間はフレームを落とさない。 止まっている間は、巻いた量で動くものは 0%、時間で回すものは 7 章の測った値の軽さ (小さい箱の transform・透明度 1 つで 3% 前後) に収める (GPU を切るとdata-liteで止まるので、回したときの重さの測り方は danything/corp#22)
9. 作り方
Section titled “9. 作り方”元: 1〜5 の全部。CSS での書き方
値は app.css の名前から選び、画面ごとに新しい値を作らない。
-
地・線・字の色は
--dp-*から選ぶ -
字の大きさは
--dp-text-*の 6 段から選ぶ。本文は 16px のまま (番組表のマスなど rem で組んだ寸法が変わらない)名前 大きさ 使うところ --dp-text-2xs11px アイコン・列・バーに添える字。幅か高さの決まった所だけに使う。アイコンの下の名前 (下のタブ)・列や行に重ねる時刻 (番組表の時の列の「いま」・ホームのバーの先の「いま」)・カレンダーの曜日・番組表のマスの「コピー禁止」 --dp-text-xs12px 小さな補足 ( .tiny)・日付の見出し・狭い画面のホームの時刻・表と欄の見出し・バッジ・サイドバーの右の値・映像の上の短い通知--dp-text-sm14px 控えた字 ( .small)・一覧の 2 段目・チップ・サイドバーの行・カレンダーの日--dp-text-base16px 本文・表 --dp-text-lg18px 小見出し ( h2・h3)--dp-text-xl24px 画面の見出し ( h1) -
余白 (
padding・margin・gap) は--dp-space-*の 6 段 (4・8・12・16・24・32px) から選ぶ- 例外: 段の間の 2px (0.125rem) と 6px (0.375rem) は、詰めた行やアイコンの横のように、段に寄せると 2px ずれて目立つ所だけ数字で書く
- 例外: 下に浮く 1 行のぶん空ける下の余白や、映像の上の操作バーの影の高さのように、ほかの寸法から決まる値は段にせず数字で書く
-
状態は
colorと疑似要素 (::before・::after) の線で作る。線は押す範囲の内側に引き、高さを変えない -
影は要るところだけ、控えめな
box-shadowで -
重なりの段 (
z-index) は--dp-z-*から選ぶ。ページの段 (日付の見出し・貼り付く操作の 1 行・下に浮く 1 行・ヘッダー・トースト・ダイアログ) と、映像の舞台の中の段 (映像 → データ放送 → データ放送が小窓に寄せた映像 → 操作バーの影 → 実況のコメント → 字幕・幕 → ボタン) に分ける- 名前は部品をまたいで比べる段にだけ付ける。1 つの部品の中だけで前後を決めるもの (面の上の線・裏に敷く 1 枚、番組表の箱の中の局名と時の列) は、どの部品でも中で数字で書く (箱の外の段とは比べない)
-
何千と並ぶもの (番組表のマス) には、描く層を作る指定 (
position・isolation・opacity) を持たせない。ブラウザが画面の一点を調べるたびに層を全部たどり、番組表からほかの画面へ移り始めるのが遅れていた- 例: 過ぎた所の覆いはマスの背景の 1 枚目に重ね、控えた字は字の色を透かす
-
焦点は
:focus-visibleに外側の 2px の線。マウスで押したときは出さない- 例外: 件いっぱいを押せる行・まとめの見出しは内側に引く (
outline-offset: -2px)。バーのある件は角で切るので、外側の線が欠ける - 本文へ移動のリンクは、キーボードで Tab を押したときだけ左上に操作の層のカプセル (
--dp-float・--dp-shadow-pop) で出す。押すと本文 (main) に焦点が移る (+layout.svelte)
- 例外: 件いっぱいを押せる行・まとめの見出しは内側に引く (
-
日本語は文節で折り返す (
bodyのword-break: auto-phrase・line-break: strict)。段落 (p・li・dd) は最後の行に 1〜2 字だけ残さず (text-wrap: pretty)、見出しは行の長さをそろえる (balance)- 狭い升は字ごとに折る (
.char-wrapと表の升)。文節は切れないひとかたまりなので、狭い幅では行の右に大きく空き、行が増えて高さが変わる (行数で切る所は読める字が減る)。表の升は文節の長さで列が広がる - 例: 番組表のマス・行数で切る一覧の字 (狭い画面の録画の状態)。1 行で「…」と切る題名は折り返さないので関係ない
- 狭い升は字ごとに折る (
-
角は入れ子でそろえる (外の角 = 内の角 + 間の余白)
- 例: ボタンと下に浮く 1 行 (スマホの形) はカプセル、ヘッダーとその下の操作の 1 行は
--dp-bar-radius(中の欄の角 + 余白)、 行・入力欄は--dp-radius、件は--dp-item-radius、メニューの項目は箱の角 − 内側の余白
- 例: ボタンと下に浮く 1 行 (スマホの形) はカプセル、ヘッダーとその下の操作の 1 行は
この決まりに揃っていないところを書きます。直したらここから消します。
- いまは無い
Amazon のアソシエイトとして、denpa は適格販売により収入を得ています。