Corredor

ウェブ、プログラミングの勉強メモ。

チェックボックスを利用した、CSS だけでできる言語切替ページの作り方

チェックボックスのチェック状態に応じて、ページの言語切替を CSS だけで行ってみた。

拙作のライブラリ in-browser-sass のデモページで実際に組み込んでいる。

HTML 構造

今回は JavaScript を使わず CSS だけで別要素に影響を与えたいので、HTML 構造が若干制限される。今回のデモでは以下のような HTML にした。

<div class="wrapper">
  <input type="checkbox" id="language">
  <label for="language"></label>
  <div class="contents">
    <p lang="en">English contents.</p>
    <p lang="ja">日本語のコンテンツ。</p>
  </div>
</div>

ポイントは以下のとおり。

  • div.wrapper は必須ではない。body 直下にチェックボックスを置くのが嫌だったので書いたまで。
  • input[type="checkbox"]::before::after 疑似要素で文字列を追加できない。どうやら「テキストコンテンツを持つ要素じゃないよね」という判断みたい。
  • チェックボックスは非表示にするので、代わりに label 要素を置いた。文言をチェック状態に応じて変えるので、中身は空にし、後で ::after 疑似要素に文言を入れる。
  • 一番大事なのは、隣接セレクタ (+) が使えるように、チェックボックスと div.contents を同じ階層に置いておかないといけない、ということ。ついついチェックボックスだけ p 要素とかで囲みたくなるけど、そうすると親要素に遡って隣接セレクタを指定しないといけなる。まだ :has() セレクタは各ブラウザに実装されていないため、それだと実装しきれないのだ。というワケで、input[type="checkbox"] と、影響を与えたい div.contents は同じ階層に配置する。
  • 英語と日本語のコンテンツは、div.contents 要素内に、それぞれ lang 属性を付与して書いておく。ココは自力で両言語のテキストを用意しておくこと。w

チェックボックスとラベルの実装

まずはチェックボックスを非表示にし、ラベルをボタン風に実装する。

今回のサンプルは「チェックされていない初期状態が日本語表示」「チェックされた状態が英語表示」になるようにするので、それを考慮した文言にする。

/* コレがチェックボックス。label 要素の for 属性で操作できるので、チェックボックス自体は非表示にする */
#language {
  display: none;
}

/* チェックボックスに隣接するラベルの ::after 疑似要素でスタイリング */
#language + label::after {
  display: inline-block;
  content: "English Version";  /* 未チェック時に表示するテキスト */
  border: 1px solid #ccc;      /* あとはボタン風にスタイリングするだけ */
  border-radius: 4px;
  padding: 2px 4px;
  background: #eee;
  cursor: pointer;
}

/* ホバー時の見た目変更 */
#language + label:hover::after {
  border-color: #999;
  background: #ccc;
}

/* チェック時のラベルを変更 */
#language:checked + label::after {
  content: "日本語版にする";
}

コレで、チェックボックスを見せずにボタン風な要素が配置できた。

表示コンテンツの切替

あとは隣接セレクタ (+) と :checked 疑似クラスで、隣接する div.contents 配下の要素を表示切替すれば良い。

/* チェックボックスの直後のラベル要素、の直後にラッパー要素があるので以下のようになる */
#language:not(:checked) + label + .contents *[lang="en"],   /* 未チェック時は英語コンテンツを非表示 */
#language:checked       + label + .contents *[lang="ja"] {  /* チェック時は日本語コンテンツを非表示 */
  display: none;
}

詳細度を同レベルにするため、#language:not(:checked) と書く必要がある。


コレでできあがり。

あとは :has() セレクタが使えるようになればもう少し CSS だけでできる幅が広がるんだけどなぁ〜。