Webパーツ辞典

通知一覧Notification center

ベルのボタンを押すと開く、届いた通知を新しい順に並べたパネル。未読の印と時刻が付き、「すべて既読にする」でまとめて読んだことにできる。

表示例

別名・英語表記

  • 通知センター
  • お知らせパネル
  • 通知パネル
  • お知らせ一覧
  • ベルのメニュー
  • 通知の履歴
  • Notification center

実装例

<div class="x-notification-center">
  <button class="x-notification-center__bell" type="button" aria-label="お知らせ(未読2件)"
          aria-expanded="true" aria-controls="x-notification-center-p">
    <i class="fa-solid fa-bell" aria-hidden="true"></i>
    <span class="x-notification-center__n" aria-hidden="true">2</span>
  </button>
  <!-- 見本なので開いた状態で流れの中に置いている。実際は section に popover 属性、ベルに popovertarget="x-notification-center-p" を付けて開閉し、
       aria-expanded は false から始めて toggle イベントで書き換える(hidden 属性では閉じない) -->
  <section class="x-notification-center__p" id="x-notification-center-p" aria-labelledby="x-notification-center-h">
    <div class="x-notification-center__head">
      <h2 class="x-notification-center__h" id="x-notification-center-h">お知らせ</h2>
      <button class="x-notification-center__all" type="button">すべて既読にする</button>
    </div>
    <ul role="list" class="x-notification-center__list">
      <li>
        <a class="x-notification-center__a x-notification-center__a--new" href="/notice/3/">
          <span class="x-notification-center__dot" aria-hidden="true"></span>
          <span class="x-notification-center__sr">未読:</span>
          <span class="x-notification-center__t">ご注文の商品を発送しました</span>
          <time class="x-notification-center__time" datetime="2026-09-25T10:20">10分前</time>
        </a>
      </li>
      <li>
        <a class="x-notification-center__a x-notification-center__a--new" href="/notice/2/">
          <span class="x-notification-center__dot" aria-hidden="true"></span>
          <span class="x-notification-center__sr">未読:</span>
          <span class="x-notification-center__t">コメントに返信がありました</span>
          <time class="x-notification-center__time" datetime="2026-09-25T08:05">2時間前</time>
        </a>
      </li>
      <li>
        <a class="x-notification-center__a" href="/notice/1/">
          <span class="x-notification-center__t">メンテナンスのお知らせ</span>
          <time class="x-notification-center__time" datetime="2026-09-23">9月23日</time>
        </a>
      </li>
    </ul>
  </section>
</div>

AIへの指示文

「通知一覧(Notification center)」を実装してください。

## これは何か
ベルのボタンを押すと開く、届いた通知を新しい順に並べたパネル。未読の印と時刻が付き、「すべて既読にする」でまとめて読んだことにできる。

## 使いどころ
- 会員向けのサービスで、発送・返信・お知らせなど複数の通知をあとから読み返せるようにしたいとき
- トーストのように消える通知を見逃した人に、履歴を残しておきたいとき
- 件数だけを示すバッジや通知ドットの先に、中身の一覧を置きたいとき

## 実装の要件
- button[aria-expanded][aria-controls](アイコン + 件数の丸)+ パネル(見出し + 「すべて既読にする」+ ul > li > a)
- 各項目は 未読の印 + 題名 + time[datetime]。未読の項目は背景と太字も変える
- 実際のページではパネルに popover 属性を付け、ベルの popovertarget で開く。閉じた状態を hidden 属性で持たない(popover と併せると、開いても表示されない)
- 外側を押す・Esc で閉じる動きと、Esc で閉じたときにフォーカスを開く前の要素(キーボードで開いたならベル)へ戻す動きはブラウザが持つ(Chrome・Edge は 114 から、Safari は 17 から、Firefox は 125 から。iOS の Safari が外側のタップで閉じるのは 18.3 から)
- ベルの click で先に this.focus() を呼ぶ。Safari と macOS の Firefox はマウスで押したボタンにフォーカスを移さず、そのままだとパネルの中から Esc で閉じたときにベルへ戻らない
- aria-expanded はパネルの toggle イベントの newState(open / closed)に合わせて true / false に書き換え、外側クリックや Esc で閉じたときも古い値を残さない
- 位置は見本の margin-top も含めて @supports (position-area: block-end) の中だけに書く。ベルの popovertarget で開いたパネルは、ベルが暗黙のアンカーになる(Chrome・Edge は 133 から、Safari は 26 から、Firefox は 147 から)
- @supports の中では margin: 6px 0 0; inset: auto; position-area: block-end span-inline-start; position-try-fallbacks: flip-block; を指定し、ベルの右端にそろえて下に出す
- position-area に対応しないブラウザ向けに、@supports not (position-area: block-end) の中でパネルに margin: auto; を書き、画面の中央に出す(popover の既定の margin: auto に頼ると、margin を 0 にするリセット CSS に消されて画面の端に張りつく)
- 幅は見本の width: 100% をやめ、パネル自身に width: min(290px, 100vw - 32px) で持たせる。popover の既定の padding(0.25em)は 0 に戻す(戻さないと枠の内側に白い縁が出る)
- 見本の overflow: hidden を overflow-y: auto に替え、高さを抑えて中でスクロールさせる。抑えないと、画面の下にはみ出した通知と「すべて見る」に届かない
- @supports の中では max-height: calc(100% - 6px); position-try-order: most-block-size; を足し、ベルの上下のうち広い側に収める(100% はベルから画面の端までの高さ、6px は隙間。position-try-order は Firefox は 148 から)
- @supports not の中では画面の中央に出るので、max-height: calc(100dvh - 32px) で抑える
- 開いただけで全部を既読にしない。どれを読んだかが分からなくなる。既読にするのは項目を開いたときと「すべて既読にする」を押したとき
- 件数を無制限に積まない。新しい順に20件ほどを出し、残りは「すべて見る」の一覧ページへ送る
- 時刻は「10分前」のような相対表記にするなら、time 要素の datetime に正確な日時を持たせる
- 狭い画面ではパネルが画面からはみ出す。幅を画面に合わせるか、通知の一覧ページへ移す

## アクセシビリティの要件
- ベルのボタンの名前に未読の件数を含める(「お知らせ(未読2件)」)。数字の丸は aria-hidden="true" にする
- ベルのボタンに aria-expanded と aria-controls(パネルの id)を付ける。Esc で閉じ、フォーカスをベルに戻す
- 未読を色だけで示さない。印の有無と太字に加え、読み上げ用に「未読:」の文字を添える
- 新しい通知が届いたことは、ベルの名前を更新したうえで role="status" の要素で控えめに伝える。パネルを勝手に開かない

## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。

```html
<div class="x-notification-center">
  <button class="x-notification-center__bell" type="button" aria-label="お知らせ(未読2件)"
          aria-expanded="true" aria-controls="x-notification-center-p">
    <i class="fa-solid fa-bell" aria-hidden="true"></i>
    <span class="x-notification-center__n" aria-hidden="true">2</span>
  </button>
  <!-- 見本なので開いた状態で流れの中に置いている。実際は section に popover 属性、ベルに popovertarget="x-notification-center-p" を付けて開閉し、
       aria-expanded は false から始めて toggle イベントで書き換える(hidden 属性では閉じない) -->
  <section class="x-notification-center__p" id="x-notification-center-p" aria-labelledby="x-notification-center-h">
    <div class="x-notification-center__head">
      <h2 class="x-notification-center__h" id="x-notification-center-h">お知らせ</h2>
      <button class="x-notification-center__all" type="button">すべて既読にする</button>
    </div>
    <ul role="list" class="x-notification-center__list">
      <li>
        <a class="x-notification-center__a x-notification-center__a--new" href="/notice/3/">
          <span class="x-notification-center__dot" aria-hidden="true"></span>
          <span class="x-notification-center__sr">未読:</span>
          <span class="x-notification-center__t">ご注文の商品を発送しました</span>
          <time class="x-notification-center__time" datetime="2026-09-25T10:20">10分前</time>
        </a>
      </li>
      <li>
        <a class="x-notification-center__a x-notification-center__a--new" href="/notice/2/">
          <span class="x-notification-center__dot" aria-hidden="true"></span>
          <span class="x-notification-center__sr">未読:</span>
          <span class="x-notification-center__t">コメントに返信がありました</span>
          <time class="x-notification-center__time" datetime="2026-09-25T08:05">2時間前</time>
        </a>
      </li>
      <li>
        <a class="x-notification-center__a" href="/notice/1/">
          <span class="x-notification-center__t">メンテナンスのお知らせ</span>
          <time class="x-notification-center__time" datetime="2026-09-23">9月23日</time>
        </a>
      </li>
    </ul>
  </section>
</div>
```

```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-notification-center,
.x-notification-center *:not([class*="fa-"]),
.x-notification-center *:not([class*="fa-"])::before,
.x-notification-center *:not([class*="fa-"])::after {
  box-sizing: border-box;
  /* 貼った先の既定の書体に引きずられないようにする。書体が変わると
     文字の幅と高さが変わり、枠のサイズまでずれる */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}

.x-notification-center {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  width: 290px;
  color: #1b2028;
}
.x-notification-center__bell {
  position: relative;
  width: 40px;
  height: 40px;
  border: 1px solid #7f8a99;
  border-radius: 50%;
  background: #fff;
  color: #1b2028;
  font-size: 16px;
  cursor: pointer;
}
.x-notification-center__n {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border: 2px solid #fff;
  border-radius: 9px;
  background: #b91c1c;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 14px;
}
.x-notification-center__p {
  width: 100%;
  margin-top: 6px;
  border: 1px solid #cfd4dc;
  border-radius: 10px;
  background: #fff;
  overflow: hidden;
}
.x-notification-center__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 8px 6px 12px;
  border-bottom: 1px solid #e3e6ea;
}
.x-notification-center__h {
  margin: 0;
  font-size: 12.5px;
  font-weight: 700;
}
.x-notification-center__all {
  min-height: 26px;
  padding: 2px 6px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: #1d4ed8;
  font: inherit;
  font-size: 11px;
  cursor: pointer;
}
.x-notification-center__list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.x-notification-center__list li + li {
  border-top: 1px solid #e3e6ea;
}
.x-notification-center__a {
  display: grid;
  position: relative;
  grid-template-columns: 8px 1fr;
  column-gap: 8px;
  padding: 8px 12px;
  color: #1b2028;
  text-decoration: none;
}
.x-notification-center__a--new {
  background: #eff4ff;
}
.x-notification-center__dot {
  grid-row: 1 / 3;
  width: 8px;
  height: 8px;
  margin-top: 5px;
  border-radius: 50%;
  background: #1d4ed8;
}
.x-notification-center__t,
.x-notification-center__time {
  grid-column: 2;
}
.x-notification-center__t {
  font-size: 12px;
}
.x-notification-center__a--new .x-notification-center__t {
  font-weight: 700;
}
.x-notification-center__time {
  font-size: 12px;
  color: #56606f;
}
.x-notification-center__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.x-notification-center__a:hover .x-notification-center__t {
  text-decoration: underline;
}
.x-notification-center__bell:focus-visible,
.x-notification-center__all:focus-visible,
.x-notification-center__a:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: -2px;
}
.x-notification-center__bell:focus-visible {
  outline-offset: 2px;
}
```

## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。

使いどころ

  • 会員向けのサービスで、発送・返信・お知らせなど複数の通知をあとから読み返せるようにしたいとき
  • トーストのように消える通知を見逃した人に、履歴を残しておきたいとき
  • 件数だけを示すバッジや通知ドットの先に、中身の一覧を置きたいとき

注意点

  • 開いただけで全部を既読にしない。どれを読んだかが分からなくなる。既読にするのは項目を開いたときと「すべて既読にする」を押したとき
  • 件数を無制限に積まない。新しい順に20件ほどを出し、残りは「すべて見る」の一覧ページへ送る
  • 時刻は「10分前」のような相対表記にするなら、time 要素の datetime に正確な日時を持たせる
  • 狭い画面ではパネルが画面からはみ出す。幅を画面に合わせるか、通知の一覧ページへ移す

アクセシビリティの要点

  • ベルのボタンの名前に未読の件数を含める(「お知らせ(未読2件)」)。数字の丸は aria-hidden="true" にする
  • ベルのボタンに aria-expanded と aria-controls(パネルの id)を付ける。Esc で閉じ、フォーカスをベルに戻す
  • 未読を色だけで示さない。印の有無と太字に加え、読み上げ用に「未読:」の文字を添える
  • 新しい通知が届いたことは、ベルの名前を更新したうえで role="status" の要素で控えめに伝える。パネルを勝手に開かない

作るときに使えるツール

関連するパーツ