Dialog と Popover #10
Intro
ここまで解説した仕様を踏まえ、いくつかの代表的なユースケースの実装について考えていく。
あくまで仕様の組み合わせ方についての解説であり、実装そのものの推奨ではない。
また、ここで紹介する仕様はまだ変更の可能性があり、かつ実装も揃っていないものがある点に注意
Tooltip
今回は、Menu の実装を考えてみる。GitHub でいうとこの部分だ。
           
          
元となるボタンによって表示され、このボタンからの相対位置で調整されるため、Anchor Positioning を活用することになる。非常によくある実装パターンだ。
HTML の仕様にも、類似の実装が Example として掲載されている。
- 6.12 The popover attribute
APG としては、Menu Button パターンにあたるだろう。
- Menu Button Pattern | APG | WAI | W3C
GitHub のこの実装はまだ Popover ではないため、現状を参考にどのように Popover で実装できるかを考えてみる。
         よくあるメニューとして、以下のような HTML をベースに考える。
           開くためのボタンが Invoker となり、Popover としてメニューのアイテムが開く実装が考えられるだろう。
          
           ここでも Popover はセマンティクスに影響を与えないため、単に  最初のコントローラには明示的に  APG などにも書かれているように、従来このような実装を行う際は、 しかし、Popover が Invoker Relationship を持っている場合、 これが基本の構造となる。
         今回はとりあえずパッと表示されて、終わったら消えればよいということで、アニメーションは割愛する。
           問題は表示位置だ。
           基本的には、開く側のボタンに対して相対的に表示するため、ボタンをアンカーとして指定する。
           あとは、 あとは、通常通り必要なスタイルを当ててやれば良い。
         Popover の開閉に関しては、特に JS なしで挙動が実現できているため、特に必要はない。
           あとは、普通にメニューの機能そのものを実装すれば良いだろう。
        HTML
          <button popovertarget=menu>Actions</button>
<ul id=menu popover>
  <li><button>Edit</button></li>
  <li><button>Hide</button></li>
  <li><button>Delete</button></li>
</ul><ul> が開いただけになる。この場合、外側は role=menu とし、各アクションが role=menuitem とすることで、開いているものがメニューであるということを宣言できるだろう。
          <button popovertarget=menu>Actions</button>
<ul id=menu role=menu popover>
  <li role=menuitem><button>Edit</button></li>
  <li role=menuitem><button>Hide</button></li>
  <li role=menuitem><button>Delete</button></li>
</ul>autofocus を付与し、各操作実行後には Popover を閉じるために popovertargetaction=close も担わせることができる。
          <button popovertarget=menu>Actions</button>
<ul id=menu role=menu popover>
  <li><button role=menuitem popovertarget=menu popovertargetaction=close autofocus>Edit</button></li>
  <li><button role=menuitem popovertarget=menu popovertargetaction=close>Hide</button></li>
  <li><button role=menuitem popovertarget=menu popovertargetaction=close>Delete</button></li>
</ul><button popovertarget> をクリックしたことで <ul role=menu> が開いたと言う事実を UA に伝えるために、aria-haspopup=menu を付与し、メニューが開いている間は aria-expanded=true にするといった実装が行われていた。
          <button popovertarget> が <ul role=menu> を開いたことを UA は認識しているため、このような実装は Popover API を用いる限りは不要となる。これも Popover がネイティブの API になったことのメリットの 1 つだ。
          
            
aria-haspopup · Issue #9153 · whatwg/html
              
            CSS
          button[popovertarget] {
  anchor-name: --menu;
}
ul[popover] {
  position-anchor: --menu;
}[popovertarget] に合わせて位置を指定し、必要に応じて translate で調整することで実装ができるだろう。
          [popover] {
  top: anchor(end);
  left: anchor(center);
  translate: 2% 4%;
}position-area を用いて指定する場合は、以下のようにも指定可能だ。
          [popover] {
  position-area: bottom span-right;
}JS
          
Nesting
メニューの項目が、さらにサブメニューを開く実装もある。
この場合、ネストした項目をさらに Popover として開くことで実装できる。
<ul>
  <li>
    <button popovertarget=menu>Actions</button>
    <ul id=menu role=menu popover>
      <li role=menuitem><button popovertarget=menu popovertargetaction=close autofocus>Save</button></li>
      <li role=menuitem>
        <button popovertarget=submenu popovertargetaction=show>Edit</button>
        <ul id=submenu role=menu popover>
          <li role=menuitem><button popovertarget=menu popovertargetaction=close autofocus>Cut</button></li>
          <li role=menuitem><button popovertarget=menu popovertargetaction=close>Copy</button></li>
          <li role=menuitem><button popovertarget=menu popovertargetaction=close>Paste</button></li>
        </ul>
      </li>
      <li role=menuitem><button popovertarget=menu popovertargetaction=close>Close</button></li>
    </ul>
  </li>
</ul>CSS も、Anchor をサブメニューに対して付与していけば良い。
button[popovertarget=menu] {
  anchor-name: --menu;
}
ul#menu {
  position-anchor: --menu;
  position-area: bottom span-right;
}
[popovertarget=submenu] {
  anchor-name: --submenu;
}
ul#submenu {
  position-anchor: --submenu;
  position-area: right span-bottom;
}また、これが横に広がることで画面に収まらない可能性を考慮して、fallback を指定する。
ul#submenu {
  position-anchor: --submenu;
  position-area: right span-bottom;
  translate: 14px;
  position-try-fallbacks: --bottom;
}
@position-try --bottom {
  position-area: bottom span-right;
}これによって右に余白がなければ、下に表示をフォールバックすることが可能だ。
DEMO
動作するデモを以下に用意した。
- Menu Popover DEMO