前提

  • 本ページの記載は特記のない限り w0s.jp および blog.w0s.jp ドメインで公開しているコンテンツに関する情報である。その他のサブドメインは状況が異なる部分も多い。
  • 当サイトは Web 技術の実験場としての役割も兼ねており、一時的なものも含めて細かい改修はたびたび実施しているが、本ドキュメントは常に追従できるとは限らない。
  • 本ドキュメントは「サイト内でそれぞれの技術をどのように使用しているか」といった純粋な技術論に留まらず、筆者の Web 技術あるいは Web そのものに対する思想展開も行っている。

歴史

2001年2月

前身のサイトを開設。COOL ONLINE (web.archive.org) の無料会員枠を利用(容量20MB)。地域コミュニティを謳っており、登録の際にまず都市を選択、それによってサブドメインが振り分けられた(e.g. tokyo.cool.ne.jp)。

2001年6月

掲載写真の増大により容量面で厳しくなり、goo フリーホームページ (web.archive.org) に移転(容量50MB)。URL は http://users.goo.ne.jp/${userId} 形式だが、IP アドレスのドメインにリダイレクトされる(ブラウザのアドレスバーには IP アドレスの数字が表示される)というすごい仕様だった。

2002年3月

ネットワーク利用技術研究会(NURS) (www.nurs.or.jp) に移転。レンタルサーバーではなく研究・実験目的のサーバーであり、Telnet が制限なしに使えたので、root 権限が必要なこと以外は割となんでもできた。

2012年2月

NURS はドメインがサブドメインで分ける方式ではなく全ユーザー共通であり、Cookie の導入がセキュリティ上の理由[注1]で躊躇われることからアイネットディー (www.inetd.co.jp) に移転したうえで独自ドメインを取得。月額270円の格安サーバーにしては Cron の制限が緩いのがありがたかった。

2017年1月

常時 TLS の波に乗りさくらの VPS (vps.sakura.ad.jp) に移転。Let's Encrypt (letsencrypt.org) を利用して TLS 対応を行う。

サーバー構成

ドメイン 用途 サーバー実行環境 フレームワーク RDBMS ソース 備考
w0s.jp 個人サイト Node.js + PM2 Express, Astro GitHub 静的コンテンツ中心
blog.w0s.jp ブログ Hono SQLite GitHub
report.w0s.jp エラーレポート収集 Hono SQLite GitHub
labs.w0s.jp Web 技術の遊び場 Apache + PHP GitHub HTTP でもアクセス可
analytics.w0s.jp アクセス解析 MySQL Matomo Analytics を利用

URL

ウェブページの URL に拡張子が含まれるのは好ましくないため[文献1]、拡張子なしの URL でアクセスできるようにしている。対象は以下のとおりである。

  • HTML コンテンツ
  • フィード用の XML ファイル
  • OpenSearch (GitHub) 用の XML ファイル

w0s.jp では express.static (expressjs.com) にて extensions プロパティに拡張子情報を指定することで実現している。

一方でそれ以外の静的リソース(画像や PDF など)は拡張子のフォールバックを設定しておらず、常に拡張子付きの URL でアクセスする必要がある。対応を分けている理由のひとつに、静的リソースには /robots.txt のように拡張子を含めた特定のパスであること前提のものが混在している事実が挙げられる。

ただしもっとも重要な点は、拡張子にはリソース種別の情報を提示する役割があることだ。リソースをブラウザの画面に表示するだけでなく、手元のマシンにローカルファイルとして保存する場合なども考慮すると、URL の永続性の観点でのデメリットは承知のうえで、拡張子を提示するメリットがそれを上回るものと判断している。

  • 外部サイトからディープリンクが張られた場合
  • 書籍に URL が掲載された場合
  • ブラウザでリソースを保存(Ctrl + S)する場合
  • curl -O (curl.se) 等のコマンドで保存する場合

HTML

MIME タイプ

HTML コンテンツの MIME タイプは text/html としている。

コンテンツのデータをスクレイピングで活用するケースにおいては XML として処理できた方が利便性が高いため以前は application/xhtml+xml で配信していた。しかしブラウザアドオンや外部サービスの中には text/html しか対応していないものが存在し、また昨今は新しい Web 技術が XML 構文に対応しない例も多くなってきているため、現代では XML 構文の採用はデメリットの方が大きくなってしまった状況と判断する。ちなみに HTML Living Standard では2024年4月より XML 構文の使用は推奨されないとの記述が追加された[文献2]

インデント

インデントをタブにするかスペースにするかは永遠の議題であるが、当サイトではタブを採用している。

タブのメリットは表示幅をカスタマイズできることだ。エディターはもちろんだが、CSS には tab-size (drafts.csswg.org) プロパティがあるので、Web ページ内でのコード表示においてもユーザースタイルシートを使うことでカスタマイズが可能だ。これらのことはフォントサイズを極端に大きくしているユーザーにはとくに重要な点である[文献3]

最小化

当サイトでは HTML の最小化を行っていない。……とわざわざ宣言しなければならないほど、昨今のフロントエンドフレームワークでは出力される HTML を自動的に最小化するのが当然の風潮がある。しかし最小化はデメリットも多く、あくまで最終手段と考えている。

HTML はユーザーエージェントが解釈するだけでなく、ユーザーが直接ソースコードを見るものでもある。ユーザースタイルシートの設定に際して HTML 構造を知る必要があることはもちろんだが、Web 文化の研究、あるいは単なる個人的興味で閲覧するケースもあるだろう。人が見て理解しやすいコードを提供するのはサイト制作者の責務である。

また昨今のデスクトップ向けブラウザは開発者ツールでソースコードが自動整形されるが、それが使えない環境も多い。

  • 開発者ツールが封じられた環境(例:国立国会図書館の館内端末)
  • モバイルブラウザ(view-source: でソースコードを閲覧できるケースもあるが自動整形は行われない)

w0s.jp ではフロントエンドフレームワークに Astro (astro.build) を使用しているのだが、デフォルト設定では例に漏れず最小化されてしまうため compressHTML (docs.astro.build) を無効にしている。さらに事前レンダリングする静的ページでは Prettier (prettier.io) による整形処理をビルド工程に挟んでいる。

外部リンク

当サイトでは外部サイトのリンクに target=_blank の属性を設定していない。

世間では「外部サイトのリンクは別タブで開く」がスタンダードな慣習となっている。良い慣習ではないのだが、もはやこれを覆すのは不可能だろう。しかし明文化されたルールがあるわけではなく、実際にはどの性質のリンクを別タブで開くか統一されていない。

私の住む神奈川県横浜市のサイト (www.city.yokohama.lg.jp) は世の慣習に反して、外部サイトのリンクであっても同一タブで開かれる。その代わりアンカーテキストには「○○のページ(外部サイト)」のように括弧書きのテキストが付与されているため、この先は横浜市が管理するページではないことが誰の目にも分かる。外部サイトの区別というものは本来こうあるべきだ。

逆に同一サイトであっても性質が異なるページへのリンクは別タブで開かせる設定をしているケースもある。それに対してアイコン等で区別をしているサイトもあれば、そうしていない(リンクを辿ってみないと挙動が分からない)サイトもある。

もしも世の中のサイトが統一的なルールのもとに実装されているのなら(たとえ悪習だとしても)それに従うことに一考の余地があるのだが、現状そうではないのだからこの慣習に合わせる必要はない。そのため当サイトでは target 属性を設定せず、代わりに以下のマークアップをしている。

  • rel=external (WHATWG) のリンクタイプ属性を付ける
  • リンク先 URL のドメイン名(頻出するサイトはファビコン)をリンク直後に付与する

3xx リダイレクト画面

URL が変更になった場合は 301 Moved Permanently、フォームの POST 送信後には 303 See Other などいくつかのケースでは HTTP レスポンスでリダイレクトを設定している。

RFC 9110 の 15.4. Redirection 3xx (www.rfc-editor.org) では、ステータスコード 300, 301, 302, 307, 308 で Location ヘッダーフィールドが設定されている場合のユーザーエージェントの挙動としてthe user agent MAY use the Location field value for automatic redirectionと書かれている。MUSTSHOULD でなくあくまで MAY であり、実際にブラウザの環境によってはリダイレクトされずにレスポンスコンテンツ(ボディ)の内容が画面に表示されるケースが存在する[文献4]

これに対して Apache HTTP Server はリダイレクトしない環境が考慮されており、Location ヘッダーフィールドだけでなくレスポンスコンテンツにおいてもリダイレクト先のリンク付きの HTML を返すようになっている。curl にて当サイトのトップページの URL を https: でなくあえて http: で指定すると次のようになる。

user@hostname:~$ curl http://w0s.jp
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="https://w0s.jp/">here</a>.</p>
<hr>
<address>Apache/2.4.63 (Ubuntu) Server at w0s.jp Port 80</address>
</body></html>

しかし近年の Web フレームワークはリダイレクト先のリンクが含まれていなかったり、好ましくない HTML を返していたり、あるいはレスポンスコンテンツ自体が空だったりといった有様である。そのため当サイトでは Express, Hono ともデフォルトで用意されているリダイレクト機能は使わず、前者は res.send() (expressjs.com) メソッド、後者は c.html() (hono.dev) メソッドの第2〜3引数を活用する形で独自の HTML を返すようにカスタマイズしている。

4xx クライアントエラー画面

404 Not Found のエラー画面には JavaScript で以下の機能を組み込んでいる。

  • 同一ドメインのリファラーがあった場合(= サイト内から無効なリンクが張られている)は管理者へ通知する。検知プログラムは GitHub で公開 (GitHub) している。
  • 直近の有効な祖先ディレクトリへのリンクを提示する。(e.g. /foo/bar へのリクエストに対し、そのレスポンスコードが 404 で /foo/ が 200 の場合、/foo/ のリンクを提示)

Lint

Markuplint (markuplint.dev) を使用したチェックを実施している。

Markuplint にはプリセット (markuplint.dev) が用意されているが、当サイトでは推奨プリセットを採用せず、当サイト向けに改めて定義した設定ファイル (GitHub) を使用している。主な特徴(推奨プリセットとの差異)は以下のとおりである。

  • <meta charset="UTF-8"> の記述を強制しない
    • HTML 仕様において <meta> 要素による文字エンコーディング宣言 (WHATWG) は特定の条件下においてのみ必須となる。当サイトは Content-Type メタデータで明示的に指定しているので、<meta> 要素の記述は任意である。
    • 仮に Content-Type メタデータでの指定ができずに <meta> 要素の記述を行う場合は、charset 属性を認識しないブラウザの存在を考慮して昔ながらの <meta http-equiv="content-type"> の使用を検討すべきだ。
  • <dl> 要素の子要素に <div> 要素の配置を強制する[文献5]
  • <abbr> 要素での title 属性の使用を禁止する
    • 推奨プリセットでは逆に title 属性が必須となっている。
    • しかし title 属性が有用とされていたのは HTML 4 時代の考え方であり、現代の閲覧環境にはそぐわない[文献6]

スタイルシート(CSS)

ダークテーマ

いわゆるダークテーマを制作者スタイルシートで提供するサイトが多いが、本来は OS やブラウザが提供すべきものと考えており、当サイトにおいてテーマの切り替え機構を提供するつもりはない。

実際に2024年以降、複数のブラウザで強制的に Web ページの色(テーマ)をカスタマイズする機能が実装されている。本ページでは便宜上「強制ダークテーマ」と呼称する。

強制ダークテーマを実装したブラウザ
ブラウザ 公開時期 Windows iOS Android 設定項目名(Windows 版の場合) 紹介記事
Vivaldi 6.6+ 2024年2月 すべてのウェブサイトにダークテーマを強制適用 Vivaldi Blog
Edge 130+ 2024年10月 ページの色 Microsoft Edge Blog
Firefox 138+ 2025年8月 ウェブサイトのコントラスト monotonous.org

Web 制作者はダークテーマ切り替え機能を提供するよりも、現状まだ不安定でブラウザごとにクセのある強制ダークテーマの最新動向を把握するよう心掛けた方が有意義だろう。

配色とくに背景色(背景画像を含む)は以前から注意が必要なものであった。たとえば <mark> 要素のデフォルトスタイルは黄色背景に黒文字 (WHATWG) であるが、一般に印刷時は背景色が無視されて前後の文字列と区別が付かなくなるため、背景色と同色の枠線を設定するといった工夫が求められる。また強制ダークテーマのような現代的な機能ができる遙か以前の1990年代からブラウザは配色設定を有しており、ダークモードという言葉が存在しない時代からユーザーは背景色を暗く設定することができたのである。

これらの事象を以前から意識していた場合、クセのある強制ダークテーマに対してもそれほど慌てる必要はないはずだ。とはいえ当サイトにおいても border-color: transparent を使用していた箇所を subgrid で置き換えるなど、いくつかの対応に迫られた[文献7]。強制ダークテーマは産声を上げたばかりの機能であるため、今しばらくは注視を続ける必要があるだろう。

リーダー表示

昨今のブラウザはリーダーモードを備えたものも多いが、制作者の意図どおりに表示されるとは限らないため、それとは別個に制作者スタイルシートにてリーダー表示を実装している。

ヘッダーやフッターを非表示にする簡易なスタイルシートを用意し、代替スタイルシート (WHATWG) で任意に適用可能な状態としている。PC 版 Firefox ではメニューバーの「表示」→「スタイルシート」から切り替え可能である。他ブラウザでも拡張機能が公開されている。

印刷用スタイル

ディスプレイ表示の眩しさを低減するため、ページの背景色は白色ではなく、若干黄味がかった色を設定している。印刷時にはそのような配慮は不要なので Media Queries の @media print を使い完全な白(#ffffff)に設定している。

クラス名

class 属性値の命名規則にはさまざまな考え方があるが、当サイトではユーティリティーファーストを採用していない。

HTML 4 で構造とプレゼンテーションの分離 (W3C) が掲げられて以来、プレゼンテーションをスタイルシートに分離して HTML は文書構造に専念する手法が普及した。HTML 5 以降は仕様書の class 属性 (WHATWG) の部分でauthors are encouraged to use values that describe the nature of the content, rather than values that describe the desired presentation of the contentとの言及がある。

ユーティリティーファーストはこれと真逆の方法論であるが、その最大の問題点はユーザースタイルシートでのカスタマイズに支障があることだ。これはアクセシビリティの問題なので、他のメリットで打ち消されることがあってはならない。他メディアと比べて Web はユーザーが見た目や挙動を自由にカスタマイズできることが大きな特徴であり、それを軽視することは Web が Web らしく発展する方向性を狭めてしまうと危惧している。

ただし実際の Web 制作では HTML から一切のプレゼンテーションを廃するのは難しく、構造が視覚的なレイアウトに影響されることは避けられない。そのような状況下でクラス名を抽象化するのは無理があり不自然な実装方法との批判も見られる[文献8]。それ自体はもっともな意見であるし、上記で引用した HTML 仕様書の記述はモダンなフロントエンドフレームワークが登場する前の2008年に公開された (W3C) ものであるからして、2026年現在においてはある程度懐疑的に受け取る必要があるだろう。

このような現実的な問題もあり、私はユーティリティーファーストそのものを否定するつもりはない。それを採用する際には、たとえば警告文ならば class="error text-red-500" のようにセマンティックな名前とプレゼンテーション値を併記すれば良いのだ。HTML 仕様書は「クラス名にプレゼンテーション値を含めてはいけない」とまでは言っていないのだから。

もっともこれらを考慮すると、巷で言われているユーティリティーファーストの利点はだいぶ薄れてしまう。当サイトでそれを採用していないのは単純に実装コスト面で判断した結果に過ぎない。

ビルド

CSS ファイルのビルドには PostCSS (postcss.org) を使用している。

複数のプラグインを使用しているが、それらはブラウザが対応していない機能の先行使用、あるいはパフォーマンス向上等が目的であり、標準化の見込みのないプラグインは使っていない。いざとなればビルド前のコードをそのままブラウザに読み込ませても問題ないような作り方をしている。

最小化

当サイトでは CSS の最小化を行っていない。理由は HTML のそれと同じである。

それでも以前は従量制課金接続を考慮して最小化を行っていた時期もあるのだが、2022年に Internet Explorer 11 のサポートが終了し、すべてのメジャーブラウザが Brotli による高圧縮なファイル配信に対応したことから、もはや転送量の削減を最小化テクニックに頼る時代ではなくなったと判断して取り止めた[文献9]

コメント

最小化にも関連する話だが、HTML と同様に CSS もユーザーが直接閲覧するものであるため、ビルド工程でコメントを一律に削除することはせず、以下の2種に分類して処理している。

  • 開発者向けのコメントはビルド工程で除去する
  • それ以外のコメントは残す

前者の代表的なものは Stylelint 用の設定コメント (stylelint.io) である。除去には CSSNANO の関連パッケージのひとつである postcss-discard-comments (GitHub) を使用している。

一方、それ以外の多くのコメントは開発者だけでなくユーザーにもその情報を伝えることが重要だろう。当サイトが使用しているリセットスタイルの一例を挙げる。

a:not(:any-link) {
	color: unset;
}

本来このようなリセットは必要ない。そのためユーザースタイルシートで当サイトのアンカーリンク色をカスタマイズしようとして制作者スタイルシートを閲覧したユーザーは、コメントがないとその意図を理解できずに混乱してしまうだろう。カスタマイズに支障が出るのはアクセシビリティの問題であるため、実際には以下のようにコメントを残すようにしている。

a:not(:any-link) {
	color: unset; /* for Dark Reader, iOS Firefox with Website Dark Mode <https://github.com/darkreader/darkreader/issues/9836> */
}

Lint

Stylelint (stylelint.io) を使用したチェックを実施している。stylelint-config-standard (GitHub) で定義されている一般的な慣習とは別に、当サイトでは独自のルール (GitHub) を多数設定している。

「ID セレクターの禁止」「物理プロパティではなく論理プロパティを使用」「宣言ブロック内のプロパティ順序に特定のルールを適用」といったあたりは他のサイトでも導入している例が見られるが、当サイトならではのルールを以下に挙げる。

  • 次兄弟結合子を除き、複数のユニバーサルセレクターは使用しない(* > * などを禁止)
  • 大文字と小文字を区別する属性セレクターでは i 識別子を明記する
  • backgroundfont などの複雑なショートハンドは使用しない
  • ビューポートパーセント単位にはデフォルトの単位(v*)ではなく、大きいビューポート(lv*)、小さいビューポート(sv*)、あるいは動的ビューポート(dv*)の単位を使用する
  • text-indent プロパティにマイナス値は使用しない(hanging 値を活用する)
  • ルート要素では colorbackground-color プロパティをセットで使用する[文献10]

スクリプト(JavaScript)

JavaScript 無効環境および類似ケースに対する考え方

当サイトでは JavaScript は補助的な使用とし、スクリプトの全部ないし一部が動かない場合でもコンテンツの閲覧に支障がないようにしている。すなわち Client Side Rendering 方式のようにユーザーに JavaScript の動作を強要する作り方はしていない。

Web ページが提供する JavaScript 機能はユーザー環境によっては動作しないことがある。俗に「JavaScript 無効環境」という表現があるが、JavaScript 設定が有効であっても個々の JavaScript 機能がすべて動作するとは限らない。

まず JavaScript がまったく動作しない環境の具体例を以下に挙げる。

  • ブラウザのリーダーモード
  • ブラウザ設定で JavaScript を無効にした場合
  • フィード全文配信

ブラウザ設定における JavaScript 無効化はいずれも URL による除外が可能なので、あらゆるサイトで一律に無効にするような非現実的な設定ではなく、地図サービスやブラウザゲームなど JavaScript を利用したユーザー体験を受けたいサービスでは有効にするといった運用が可能である。すなわち制作側の視点でいえば、もし JavaScript の動作が必須な作りにする場合は「JavaScript を有効にしてください」といった一文を書くのではなく、黙っていてもユーザーがそう設定してくれるようなコンテンツ提供を心掛けた方がよいだろう。

次に JavaScript が有効な環境でありながら機能が動作しない(失敗する)ケースを挙げる。

  • 新しい機能を古いブラウザで実行した場合
  • 例外が発生した場合

前者は説明不要だろうが、後者を考慮していないサイトは多い。

これがもっとも頻発するのは Web Storage API だろう。sessionStoragelocalStorage には規定の容量(一般には 5MiB)を超えた値をセットすることはできず、その場合には例外がスローされる。またブラウザのプライバシー設定で Cookie の受け入れを拒否している場合も例外がスローされる。

const storageKey = 'foo';

/* 5MiB を超えるデータをセット */
sessionStorage.clear();
sessionStorage.setItem(storageKey, 'x'.repeat(5242880 - storageKey.length + 1)); // `QuotaExceededError` がスローされる

doSomething(); // ここには到達しない

言語によってはコンパイラが try...catch で囲うことを強制するものもあるが、JavaScript の場合は TypeScript を含めてそのような機能は存在しないため、Lint でチェックしない限りはエンジニアが意識しておく必要がある[注2]。そしてサーバーサイドプログラムにはない視点として「ユーザー環境によって挙動が変わりうる」ことも忘れてはならない。

sessionStorage.setItem('foo', '1'); // ブラウザ設定で Cookie をブロックしている場合は `SecurityError` がスローされる

doSomething(); // 環境によってはここには到達しない

エラー検知

作成したスクリプト機能は普段使いのブラウザでの軽い動作確認はしているが、きちんとしたテストは行っていない。というよりサーバーサイドプログラムとは異なり、ブラウザには様々な設定項目があり、また Bot 等を含めたあらゆる環境が存在するためそれらを網羅したテストの実施は現実的に不可能である。もとより前述のとおり JavaScript が動かなくてもコンテンツの閲覧には支障がないように作っているため、テストの実施は割り切り、せめて発生してしまったエラーは把握できるよう error イベントを検知し通知する機能を組み込んでいる。この検知プログラムは GitHub で公開 (GitHub) しているが、簡略化したコード例を下記に示す。

window.addEventListener('error', (ev) => {
	fetch(ENDPOINT, {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({
			documentURL: location.toString(),
			message: ev.message,
			jsURL: ev.filename,
			lineNumber: ev.lineno,
			columnNumber: ev.colno,
		}),
	});
});

これにより事前確認が難しい以下のようなケースで発生したエラーも検知することができる。

  • ひじょうに古いブラウザ
  • ブラウザの設定やアドオンに起因するもの
  • 検索エンジンのロボットなどブラウザ以外の環境

アクセシビリティ

アクセシビリティで重要視していること

Web アクセシビリティでもっとも重要な点は「ユーザーの閲覧環境を問わないこと」だと考えている。決して「ユーザーの利便性を高めること」が目的なのではない。紙の本や TV など情報を伝える媒体にはそれぞれに特性があるが、こと閲覧環境の豊富さにおいて Web は他の追従を許さない。Web が Web であり続けるためにもこのことは最優先で守っていかなければならない。

そのため当サイトはどのような環境でもコンテンツの閲覧に支障が起こらないようにすることを心掛けている。ここでいう環境とは OS やブラウザの種類はもちろんであるが、それらの設定をも含む。ブラウザの設定で Cookie の受け入れを拒否するとまったく閲覧できなくなるサイトが多い現状は残念に思うが、当サイトでは特定の設定に依存した設計はしていない。「目が見えるはずだ」「色を区別できるはずだ」「Cookie を受け入れるはずだ」「JavaScript が実行されるはずだ」といったあらゆる前提を捨てている。フロントエンドフレームワークの選定に際しても機能面より先にまずこの点を最重要基準としている。

とはいえ難しいことをしているわけではない。コンテンツのデータは(JSON ではなく)HTML で提供すること、自分がよく理解していない技術は使わないこと。細かいことを挙げればキリはないが、大前提といえるのはこの2点のみだ。

さて、Web アクセシビリティではスクリーンリーダーをはじめとした支援技術の話題が事欠かない。しかし当サイトでは支援技術を特段に意識することはなく、読み上げの最適化も行っていない。本ページではここの章を含み見出しの横にはセルフリンクを設けているが、そのマークアップは単に <a href="#foo">§</a> としている。記号文字を読まないスクリーンリーダーの存在は承知しているが、だからといって aria-label 属性の追加といった対応は Web アクセシビリティとしてはナンセンスである。

Visually hidden テクニック

当サイトではいわゆる Visually hidden のテクニックは使っていない。

このテクニックは視覚的には見えないように支援技術にのみ追加の情報を提供する目的で使われるものだが、利用方法によっては問題が発生する。

  • 画面を見ながら音声でも聞くといったように、ディスプレイ表示と支援技術とを併用した閲覧方法では「見えない文章が読み上げられる」こととなり混乱をきたす。
  • スタイルシートが適用されない環境で意図が破綻する。

ブラウザにリーダーモードが搭載され、近年はさらに強制ダークテーマも普及し始めている。CSS の特徴のひとつに制作者スタイルシートよりもユーザー設定の方が優先される点が挙げられるが、今後ますますその傾向は強まるだろう。そのような流れの中で Visually hidden は閲覧環境に前提を求めすぎていると感じる。特定の前提のもとでは使い勝手が向上するケースもあるのだが、その前提から離れるほどさまざまな問題が起こりうる、そのようなものを「アクセシビリティのテクニック」として使う風潮には違和感を覚える。

余談だが、それでもこのテクニックを使う場合は CSS のルールセットにコメントでその意図を説明しておくべきだろう。Visually hidden は関連性のないプロパティを組み合わせた、ルールセット全体で初めて意味を持つテクニックである。制作者スタイルシートを書くエンジニア、ユーザースタイルシートを書く利用者のいずれにも、必ずしもその意味が周知されているとは限らない。現実にこのテクニックを知らない同僚が display: none に書き換えてしまった事例があるようだ[文献11]。そのような事故を防ぐ意味合いもあるし、なにより CSS はユーザーが見て理解するものでもあるのだから、Visually hidden に限らず本質的でないテクニックを使わざるを得ない際はコメントを併記しておくことがアクセシビリティ上必要である。

ファビコン

ファビコンは SVG 形式で提供している。

Internet Explorer 5 の実装 (web.archive.org) を発端とする歴史的な経緯により、/favicon.ico のファイルパスにすることでブラウザが自動的に読み取るようになっているが、あくまで URL が /favicon.ico であれば良く、ファイルの実体が ICO 形式である必要はない。そのため SVG 形式のファビコンファイルを /favicon.ico として配置し、MIME タイプを image/svg+xml で返すように設定することで <link rel="icon"> の記述を省略できる[文献12]。ただし以下のデメリットがある。

  • Android 版 Firefox は SVG ファビコンに対応しておらず、そればかりか The Open Graph protocol (ogp.me) が設定されているページでは og:image をファビコン代わりに表示してしまう。(2026年5月、Android Firefox 150 にて確認)
  • Safari が SVG ファビコンに対応したのは2025年のバージョン 26 であり、未対応の環境も残っている。
  • 検索サービスでの SVG ファビコン表示はいまだ安定していないように思える。DuckDuckGo (duckduckgo.com) が SVG ファビコンに対応したのは比較的最近(2025年~2026年頃)であるし、別の著名サービスでも不具合によるものか一時期表示されないことがあった。

CSP

  • Fetch ディレクティブ(*-src)は Content-Security-Policy-Report-Only で設定。ユーザースタイルシートやユーザースクリプト、アドオン等によるカスタマイズを妨げないため、あくまで実態調査目的として Report-Only にしている。
  • Trusted Types 関係も現状は Content-Security-Policy-Report-Only で設定している。
  • それ以外のディレクティブは Content-Security-Policy で設定している。
  • Reporting のエンドポイントは report.w0s.jp 内に作成しており、ソースコードは GitHub で公開 (GitHub) している[文献13]

注釈

参考文献