公開日

既存の UI になじむ拡張:見た目とアクセシビリティの両立

Mastodon の投稿欄に足したボタン列は、本家のボタンのクラスを借りてテーマの色に追従させ、選択中だけ名前を出してほかはアイコンとツールチップにした。キーボードでも使えるよう radiogroup として組んだ話と、CSP でスタイルが効かなかった話。

はじめに

公開範囲と引用範囲をワンクリックで切り替えるユーザースクリプトは、Mastodon の投稿欄に自前のボタン列を差し込む。他人のページに相乗りする以上、浮いて見える UI は作りたくない。目標は、見た目を本家に寄せることと、マウスがなくても使えることの 2 つ。

本家のクラスを借りて色に追従する

ボタン列の外枠には、本家の公開範囲ボタンと同じ dropdown-button クラスを付けた。枠線と文字色は本家の CSS がそのまま当たるので、ライトテーマでもダークテーマでも色を自分で決めなくて済む。

中のボタンは color: inherit にし、背景の強調は currentColor を基準に塗る。

.tm-seg > button[aria-checked="true"] {
  background: rgba(127, 127, 127, .25);
  background: color-mix(in srgb, currentColor 22%, transparent);
}

color-mix が使えるブラウザでは文字色を薄めた背景になり、使えなければ灰色の半透明に落ちる。色の値をテーマごとに持たずに済むのは、本家のクラスを借りたおかげ。アイコンも本家と同じ Material Symbols を使い、fill="currentColor" で文字色に合わせている。

本家の元のボタンは消さずに display: none で隠した。本家のダイアログが必要になる場面は残るので、壊さずに退かせるだけにしている。

選択中だけ名前を出す

4 つの公開範囲と 3 つの引用範囲を全部文字で並べると、投稿欄の幅に流石に収まらない。選択中のボタンだけ短い名前(「公開」「ひかえめ」など)を出し、ほかはアイコンだけにした。アイコンだけのボタンには title を付け、マウスを乗せると「ひかえめな公開:検索・トレンド・公開タイムラインには表示しない」のような説明が出る。

名前の表示・非表示は、aria-checked の値を見る CSS だけで切り替えている。JavaScript で文字を出し入れしないので、状態と表示がずれない。

キーボードで使えるようにする

ボタン列は role="radiogroup"、各ボタンは role="radio" にした。見た目はボタンの列でも、意味としては 1 つを選ぶラジオボタンだろ、という判断。各ボタンの aria-label には title と同じ説明を入れ、読み上げでも何を選ぶのか分かるようにした。

キーボード操作は、選択中のボタンだけ tabIndex を 0 にし、ほかは -1 にする。Tab で列に入ったら ← → で隣へ移る。

const btns = [...group.querySelectorAll('button:not(:disabled)')];
const next = btns[(i + (e.key === 'ArrowRight' ? 1 : -1) + btns.length) % btns.length];
next.focus();
next.click();

無効なボタンは飛ばし、端まで行ったら反対側へ回る。移動と同時に click() するので、矢印を押すだけで公開範囲が切り替わる。フォーカスの位置は :focus-visible で currentColor の枠を出し、どこにいるか見えるようにした。

CSP でスタイルが効かない

一度つまずいたのは、追加した <style> が丸ごと無視されたこと。Mastodon は CSP でインラインのスタイルを禁じているんだよな。対処は二段構え。ページ内の nonce 付き要素から値を借りて <style> に付けるのと、CSP の対象外の Constructable Stylesheet(CSSStyleSheet を adoptedStyleSheets に足す)の両方で同じ CSS を当てる。どちらかが通れば表示される。

AI と壁打ちして決めたこと

role や aria-* の付け方、CSP の回避策の候補は AI(Claude Code)に出してもらった。ただ、アクセシビリティの属性は付けて終わりじゃないので、Tab と矢印キーで実際に操作できるかは自分で確かめる。本家のクラスを借りる方針は、色を自前の CSS で持つ案と比べて自分で決めた。本家が更新されたら壊れうるが、色のずれを自分で追いかけ続けるよりはいい。

次回は、ユーザースクリプトを軽くした話を書く。