W3C(w3.org)公式からWebサイト用UIパーツ実装例が提供されていると聞いたので見てみた

SEO

スポンサーリンク

Webサイトを作ったことがあったり、インターネットについて勉強したりしたことがある方なら、誰もが「W3C」を一度は耳にしたことがあると思います。

正式名称は「World Wide Web Consortium」で、WWW(World Wide Web)で用いられる技術の標準化をしている非営利団体です。

そんなW3CがUIパーツ実装例をまとめたらしいということで、GoogleもW3Cに追随するようなウワサがありますので、SEO的にも重要だと思い少し見てみました。

UIパーツ一覧

初めに断っておくと、本記事の内容は英語が読める方であれば、本家(Patterns | APG | WAI | W3C)をお読みいただいた方が早いです。
以下、日本語でバーッと確認したい方向けのまとめです。

アコーディオン(表示/非表示機能付きセクション)

アコーディオンは、コンテンツのセクションを表すタイトル、コンテンツスニペット、またはサムネイルを含む、インタラクティブな見出しを縦に積み重ねたものです。

アラート

アラートは、ユーザーのタスクを中断することなく、ユーザーの注意を引くような方法で、簡潔で重要なメッセージを表示する要素です。

アラート・ダイアログとメッセージ・ダイアログ

アラートダイアログは、ユーザーのワークフローを中断して重要なメッセージを伝え、応答を取得するモーダルダイアログです。

パンくずリスト

パンくずリストは、現在のページの親ページへのリンクを階層的に並べたものです。

ボタン

ボタンは、フォームの送信、ダイアログのオープン、アクションのキャンセル、削除操作など、ユーザーがアクションやイベントをトリガーできるようにするウィジェットです。

カルーセル

カルーセル(スライドショーまたは画像ローテーター)は、1つまたは複数のスライドのサブセットを順次表示することで、スライドと呼ばれるアイテムのセットを提示します。

チェックボックス

WAI-ARIA は、2 種類のチェックボックス ウィジェットをサポートしています。1 つは、チェック済みと未チェックの2つの選択肢を切り替えるデュアル・ステート・チェックボックス、もう1つは、部分的にチェック済みと呼ばれる 3 番目の状態を追加できるトライ・ステート・チェックボックスです。

コンボボックス

コンボボックスは、関連付けられたポップアップを持つ入力ウィジェットです。

ダイアログ(モーダル)

ダイアログは、プライマリウィンドウまたは別のダイアログウィンドウにオーバーレイ表示されるウィンドウです。

ディスクロージャー (表示/非表示)

ディスクロージャーは、コンテンツを折りたたむ (非表示) または展開する (表示) できるようにするウィジェットです。

フィード

フィードは、ユーザがスクロールすると自動的にコンテンツの新しいセクションをロードするページのセクションです。

グリッド

グリッド・ウィジェット(インタラクティブな表形式データとレイアウトコンテナ)は、ユーザーが方向ナビゲーションキー (矢印キー、ホーム、エンドなど) を使用して、そこに含まれる情報またはインタラクティブ要素をナビゲートできるようにするコンテナです。

ランドマーク

ランドマークは、ページの主要なセクションを識別する8つの役割のセットです。

リンク

リンクウィジェットは、リソースへのインタラクティブな参照を提供します。

リストボックス

リストボックスウィジェットは、オプションのリストを表示し、ユーザーがオプションを 1 つ以上選択できるようにします。

メニューとメニューバー

メニューは、一連のアクションや関数など、選択肢のリストをユーザーに提供するウィジェットです。

メニューボタン

メニュー ボタンは、メニューとメニューバーのパターンで説明されているように、メニューを開くボタンです。

メーター

メーターは、定義された範囲内で変化する数値のグラフィック表示です。

ラジオグループ

ラジオ グループは、ラジオ ボタンと呼ばれるチェック可能なボタンのセットであり、一度にチェックできるボタンは1つだけです。

スライダー

スライダーは、ユーザーが指定した範囲内から値を選択する入力です。

スライダー(マルチサム)

マルチサム・スライダーはスライダー・パターンを実装しますが、多くの場合、1 つのレール上に 2 つ以上のサムが含まれます。

スピンボタン

スピンボタンは、その値を離散値のセットまたは範囲に制限する入力ウィジェットです。

スイッチ

スイッチは、ユーザーがオンまたはオフの2つの値のいずれかを選択できる入力ウィジェットです。

テーブル

HTML テーブル要素と同様に、WAI-ARIA テーブルは、それぞれが 1 つ以上のセルを含む 1 つ以上の行を含む静的な表形式構造です。これはインタラクティブ・ウィジェットではありません。

タブ

タブは、タブパネルと呼ばれるコンテンツのレイヤー化されたセクションのセットで、一度に 1 つのコンテンツパネルが表示されます。

ツールバー

ツール バーは、ボタン、メニューボタン、チェック ボックスなどの一連のコントロールをグループ化するためのコンテナーです。

ツールチップ

ツールチップは、要素がキーボードフォーカスを受け取るか、マウスが要素の上にマウスを置いたときに、要素に関連する情報を表示するポップアップです。

ツリービュー

ツリー・ビュー・ウィジェットは、階層リストを表示します。

ツリーグリッド

ツリーグリッドウィジェットは、編集可能またはインタラクティブな表形式の情報からなる階層的なデータグリッドを表示します。

ウィンドウスプリッター

ウィンドウ・スプリッターは、ウィンドウの 2 つのセクション (ウィンドウ) 間の移動可能な区切り記号であり、ユーザーはウィンドウの相対的なサイズを変更できます。

実装例

アコーディオンを例に見ていきます。

実装イメージ

HTML

<div id="accordionGroup" class="accordion">
  <h3>
    <button type="button" aria-expanded="true" class="accordion-trigger" aria-controls="sect1" id="accordion1id">
      <span class="accordion-title">
        Personal Information
        <span class="accordion-icon"></span>
      </span>
    </button>
  </h3>
  <div id="sect1" role="region" aria-labelledby="accordion1id" class="accordion-panel">
    <div>
      
      <fieldset>
        <p>
          <label for="cufc1">Name<span aria-hidden="true">*</span>:</label>
          <input type="text" value="" name="Name" id="cufc1" class="required" aria-required="true">
        </p>
        <p>
          <label for="cufc2">Email<span aria-hidden="true">*</span>:</label>
          <input type="text" value="" name="Email" id="cufc2" aria-required="true">
        </p>
        <p>
          <label for="cufc3">Phone:</label>
          <input type="text" value="" name="Phone" id="cufc3">
        </p>
        <p>
          <label for="cufc4">Extension:</label>
          <input type="text" value="" name="Ext" id="cufc4">
        </p>
        <p>
          <label for="cufc5">Country:</label>
          <input type="text" value="" name="Country" id="cufc5">
        </p>
        <p>
          <label for="cufc6">City/Province:</label>
          <input type="text" value="" name="City_Province" id="cufc6">
        </p>
      </fieldset>
    </div>
  </div>
  <h3>
    <button type="button" aria-expanded="false" class="accordion-trigger" aria-controls="sect2" id="accordion2id">
      <span class="accordion-title">
        Billing Address
        <span class="accordion-icon"></span>
      </span>
    </button>
  </h3>
  <div id="sect2" role="region" aria-labelledby="accordion2id" class="accordion-panel" hidden="">
    <div>
      <fieldset>
        <p>
          <label for="b-add1">Address 1:</label>
          <input type="text" name="b-add1" id="b-add1">
        </p>
        <p>
          <label for="b-add2">Address 2:</label>
          <input type="text" name="b-add2" id="b-add2">
        </p>
        <p>
          <label for="b-city">City:</label>
          <input type="text" name="b-city" id="b-city">
        </p>
        <p>
          <label for="b-state">State:</label>
          <input type="text" name="b-state" id="b-state">
        </p>
        <p>
          <label for="b-zip">Zip Code:</label>
          <input type="text" name="b-zip" id="b-zip">
        </p>
      </fieldset>
    </div>
  </div>
  <h3>
    <button type="button" aria-expanded="false" class="accordion-trigger" aria-controls="sect3" id="accordion3id">
      <span class="accordion-title">
        Shipping Address
        <span class="accordion-icon"></span>
      </span>
    </button>
  </h3>
  <div id="sect3" role="region" aria-labelledby="accordion3id" class="accordion-panel" hidden="">
    <div>
      <fieldset>
        <p>
          <label for="m-add1">Address 1:</label>
          <input type="text" name="m-add1" id="m-add1">
        </p>
        <p>
          <label for="m-add2">Address 2:</label>
          <input type="text" name="m-add2" id="m-add2">
        </p>
        <p>
          <label for="m-city">City:</label>
          <input type="text" name="m-city" id="m-city">
        </p>
        <p>
          <label for="m-state">State:</label>
          <input type="text" name="m-state" id="m-state">
        </p>
        <p>
          <label for="m-zip">Zip Code:</label>
          <input type="text" name="m-zip" id="m-zip">
        </p>
      </fieldset>
    </div>
  </div>
</div>

CSS

.accordion {
  margin: 0;
  padding: 0;
  border: 2px solid hsl(0deg 0% 52%);
  border-radius: 7px;
  width: 20em;
}

.accordion h3 {
  margin: 0;
  padding: 0;
}

.accordion:focus-within {
  border-color: hsl(216deg 94% 43%);
}

.accordion:focus-within h3 {
  background-color: hsl(0deg 0% 97%);
}

.accordion > * + * {
  border-top: 1px solid hsl(0deg 0% 52%);
}

.accordion-trigger {
  background: none;
  color: hsl(0deg 0% 13%);
  display: block;
  font-size: 1rem;
  font-weight: normal;
  margin: 0;
  padding: 1em 1.5em;
  position: relative;
  text-align: left;
  width: 100%;
  outline: none;
}

.accordion-trigger:focus,
.accordion-trigger:hover {
  background: hsl(216deg 94% 94%);
}

.accordion-trigger:focus {
  outline: 4px solid transparent;
}

.accordion > *:first-child .accordion-trigger,
.accordion > *:first-child {
  border-radius: 5px 5px 0 0;
}

.accordion > *:last-child .accordion-trigger,
.accordion > *:last-child {
  border-radius: 0 0 5px 5px;
}

button {
  border-style: none;
}

.accordion button::-moz-focus-inner {
  border: 0;
}

.accordion-title {
  display: block;
  pointer-events: none;
  border: transparent 2px solid;
  border-radius: 5px;
  padding: 0.25em;
  outline: none;
}

.accordion-trigger:focus .accordion-title {
  border-color: hsl(216deg 94% 43%);
}

.accordion-icon {
  border: solid currentcolor;
  border-width: 0 2px 2px 0;
  height: 0.5rem;
  pointer-events: none;
  position: absolute;
  right: 2em;
  top: 50%;
  transform: translateY(-60%) rotate(45deg);
  width: 0.5rem;
}

.accordion-trigger:focus .accordion-icon,
.accordion-trigger:hover .accordion-icon {
  border-color: hsl(216deg 94% 43%);
}

.accordion-trigger[aria-expanded="true"] .accordion-icon {
  transform: translateY(-50%) rotate(-135deg);
}

.accordion-panel {
  margin: 0;
  padding: 1em 1.5em;
}

/* For Edge bug https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4806035/ */
.accordion-panel[hidden] {
  display: none;
}

fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

input {
  border: 1px solid hsl(0deg 0% 42%);
  border-radius: 0.3em;
  display: block;
  font-size: inherit;
  padding: 0.3em 0.5em;
}

JavaScript

/*
 *   This content is licensed according to the W3C Software License at
 *   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
 *
 *   Simple accordion pattern example
 */

'use strict';

class Accordion {
  constructor(domNode) {
    this.rootEl = domNode;
    this.buttonEl = this.rootEl.querySelector('button[aria-expanded]');

    const controlsId = this.buttonEl.getAttribute('aria-controls');
    this.contentEl = document.getElementById(controlsId);

    this.open = this.buttonEl.getAttribute('aria-expanded') === 'true';

    // add event listeners
    this.buttonEl.addEventListener('click', this.onButtonClick.bind(this));
  }

  onButtonClick() {
    this.toggle(!this.open);
  }

  toggle(open) {
    // don't do anything if the open state doesn't change
    if (open === this.open) {
      return;
    }

    // update the internal state
    this.open = open;

    // handle DOM updates
    this.buttonEl.setAttribute('aria-expanded', `${open}`);
    if (open) {
      this.contentEl.removeAttribute('hidden');
    } else {
      this.contentEl.setAttribute('hidden', '');
    }
  }

  // Add public open and close methods for convenience
  open() {
    this.toggle(true);
  }

  close() {
    this.toggle(false);
  }
}

// init accordions
const accordions = document.querySelectorAll('.accordion h3');
accordions.forEach((accordionEl) => {
  new Accordion(accordionEl);
});

まとめ

ザックリと見ていきましたが、公式にはキーボード操作や、他のパターンの具体的な解説なども載っているので是非確認してみてください。

【Reference】

https://www.w3.org/WAI/ARIA/apg/patterns

SEO

Posted by このめ