# 拡大するとページを覆う、浮いた購入バー

Source: https://easeweb.dev/learn/floating-buttons
Topics: ダイアログとオーバーレイ > フローティングボタンが内容を覆わない (WCAG 2.4.11, 1.4.10)
The fix: バーを確保した余白より高くせず、ウィンドウの高さが低いときは邪魔にならないようにします。どの幅でもバーを1行に保ち、商品名を途中で省略して、確保した余白と常に一致させるか、それに加えて、400%の拡大表示や横向きのスマートフォンのようにウィンドウの高さが30em未満のときは、フッターの後のページの中に置きます。
Test with: zoom, keyboard
References: https://www.w3.org/WAI/WCAG22/Understanding/reflow.html https://www.w3.org/WAI/WCAG22/Techniques/css/C34 https://www.w3.org/WAI/WCAG22/Understanding/focus-not-obscured-minimum.html https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow

よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。

## 場面

衣料品店の商品ページが、幅390ピクセルのスマートフォン向けにデザインされています。写真、「リネンのオーバーシャツ」、$48、色、「カートに入れる」ボタン、短い説明、詳細、そしてサイズガイド、配送と返品、お問い合わせへのリンクが並びます。ページをスクロールすると、画面の下端のすぐ上に、角が丸く柔らかい影の付いた白いバーが浮いています。そこには商品名、価格、「カートに入れる」がもう一度入っていて、ボタンはいつでも親指の届く所にあります。開発者はデザインのとおりに作り、こうしたバーがフォーカスを隠すことがあると知っていたので、バーの5rem分を空けておきました。フォーカスがバーの上で止まるようにscroll-paddingを指定し、最後のリンクもバーの上までスクロールできるよう、ページの終わりに余白を足しています。スマートフォンでもノートパソコンでも、問題なく動きます。

ロービジョンの人が、幅1280 × 高さ1024ピクセルのウィンドウでページを400%に拡大します。ページの大きさは320 × 256 CSSピクセルになります。この幅では、商品名、価格、ボタンが1行に収まらず、「カートに入れる」が2行目に落ちます。バーの高さは6remになり、画面端からの距離を足すと、高さ16remのウィンドウの下7remを覆います。半分近くです。確保した5remでは2rem足りません。最後までスクロールしても、「お問い合わせ」はバーの下に残り、それ以上はスクロールできません。Tabで移動すると、誰にも見えないリンクにフォーカスがあります。

## 確認できる問題

バーのために確保した余白は、1行分の高さで描かれたデザインから測ったものです。しかし、バーの高さは与えられる幅によって変わります。バーが大きくなったことは、確保した余白に伝わりません。達成基準1.4.10は、幅320 CSSピクセルで、情報や機能を失わずに内容を表示できることを求めています。「お問い合わせ」はページにありますが、この幅ではどれだけスクロールしても表示されません。内容もリンクの機能も失われています。達成基準2.4.11も満たしません。このリンクにフォーカスがあるとき、バーで完全に隠れています。スクロールの余白はブラウザーに、フォーカスを下端から5rem上まで運ぶよう伝えていますが、大きくなったバーはその位置も覆っています。ページの最後ではそれ以上スクロールできないため、ブラウザーは指示どおりに動いても、リンクは隠れたままです。固定表示のフッターやヘッダーがフォーカスされた要素を完全に隠す、失敗例F110に当たります。

1.4.10の解説書は、隠れる部分以外についても固定表示の内容に注意を促しています。読むことを難しく、あるいは不可能にすることがあり、小さなサイズではそうした部分の固定をやめるか、利用者が非表示にできるようにすることを強く勧めています。この例でバーの上に残る読むための帯は9rem、テキスト6行分です。

## 利用者への影響

ページをCSSピクセルで小さなサイズで見る人は誰でも影響を受けます。デスクトップでの拡大表示や、スマートフォンで表示サイズを大きくした場合です。横向きのスマートフォンでもウィンドウの高さが低くなります。幅はバーを1行に保てるだけありますが、その上に読める部分はわずかしか残りません。

## 自動チェックとAIの限界

自動チェックはページをデスクトップのサイズで読み込み、そこではバーは1行に収まり、確保した余白とも一致します。axeにはこれを調べるルールがなく、隠れたフォーカスのチェックも、一つのウィンドウサイズでTab移動するだけです。この問題が見えるのは、狭いウィンドウで、ページを最後までスクロールしたときです。EaseWebの診断では、320 × 256 CSSピクセルでページの最後までスクロールし、固定表示の要素の下に残るテキストや操作要素を報告するようにしました。デザインレビューは、バーを描いた幅390ピクセルのスマートフォンの枠で見ることが多く、320で見ることはまれです。AIアシスタントが書くバーには、レスポンシブにするために `flex-wrap: wrap` が付き、確保する余白はデザインの高さを写したものになることがあります。

## 修正前：折り返すバーと、1行分の余白

```css
:root {
  /* デザインどおりのバー：高さ4rem、下端から1rem。 */
  --bar-space: 5rem;
}
html {
  scroll-padding-bottom: var(--bar-space);
}
body {
  padding-bottom: var(--bar-space);
}
.buy-bar {
  position: fixed;
  inset-inline: 1rem;
  bottom: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.75rem 1rem;
  border-radius: 1rem;
  background: #fff;
}
.buy-bar-name {
  flex-grow: 1;
}
.buy-bar-add {
  margin-inline-start: auto;
  white-space: nowrap;
}
```

```html
<div class="buy-bar">
  <p class="buy-bar-name">リネンのオーバーシャツ</p>
  <p class="buy-bar-price">$48</p>
  <button type="button" class="buy-bar-add">カートに入れる</button>
</div>
```

バーはフッターの後、ページの最後にあり、ウィンドウの下部に固定されています。幅320ピクセルではボタンが2行目に折り返し、バーは6remになりますが、scroll-paddingとページの終わりの余白は5remのままです。ページの最後の2remは、どこまでスクロールしてもバーの下にあります。

## 修正後：1行に収まるバー

```css
:root {
  /* デザインどおりのバー：高さ4rem、下端から1rem。 */
  --bar-space: 5rem;
}
html {
  scroll-padding-bottom: var(--bar-space);
}
body {
  padding-bottom: var(--bar-space);
}
.buy-bar {
  position: fixed;
  inset-inline: 1rem;
  bottom: 1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.75rem 1rem;
  border-radius: 1rem;
  background: #fff;
}
.buy-bar-name {
  flex-grow: 1;
  /* 折り返さずに商品名を省略します。ページの見出しにも同じ名前があります。 */
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.buy-bar-add {
  margin-inline-start: auto;
  white-space: nowrap;
}
```

```html
<div class="buy-bar">
  <p class="buy-bar-name">リネンのオーバーシャツ</p>
  <p class="buy-bar-price">$48</p>
  <button type="button" class="buy-bar-add">カートに入れる</button>
</div>
```

`flex-wrap: wrap` をなくすと、バーはどの幅でも1行のままになり、場所が足りない分は商品名が譲ります。幅320ピクセルでは、最初の数文字だけを残して省略されます。バーはデザインどおりの4remを保つため、確保した余白は常に一致し、「お問い合わせ」も含め、ページのどの行もバーの外へスクロールできます。商品名はページの見出しにすべて表示されているので、失われる情報はありません。スマートフォンとデスクトップでの見た目も変わりません。ウィンドウに占める割合も変わりません。400%では、高さ16remのウィンドウの5rem、3分の1近くをバーが覆い続け、利用者は残りの部分で読むことになります。バーのほかの文字やボタンのラベルも、幅320ピクセルで価格と並んで1行に収まる長さにしてください。

## 修正後：高さがあるときだけ固定

```css
:root {
  /* デザインどおりのバー：高さ4rem、下端から1rem。 */
  --bar-space: 5rem;
}
html {
  scroll-padding-bottom: var(--bar-space);
}
body {
  padding-bottom: var(--bar-space);
}
.buy-bar {
  position: fixed;
  inset-inline: 1rem;
  bottom: 1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.75rem 1rem;
  border-radius: 1rem;
  background: #fff;
}
.buy-bar-name {
  flex-grow: 1;
  /* 折り返さずに商品名を省略します。ページの見出しにも同じ名前があります。 */
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.buy-bar-add {
  margin-inline-start: auto;
  white-space: nowrap;
}
/* 400%の拡大表示や横向きのスマートフォンなど、高さの低いウィンドウでは、バーをページの中に置きます。 */
@media (max-height: 30em) {
  :root {
    --bar-space: 0;
  }
  .buy-bar {
    position: static;
    margin: 1rem;
  }
}
```

```html
<div class="buy-bar">
  <p class="buy-bar-name">リネンのオーバーシャツ</p>
  <p class="buy-bar-price">$48</p>
  <button type="button" class="buy-bar-add">カートに入れる</button>
</div>
```

前の修正と同じくバーは1行に収まるため、浮いているときはいつでも、確保した余白と一致します。ウィンドウの高さが30em（標準の文字サイズで480ピクセル）未満になると、バーは浮くのをやめ、フッターの後のページの最後に置かれて、ほかの内容と一緒にスクロールします。確保した余白も0になります。幅1280 × 高さ1024のウィンドウで400%に拡大すると、ページの高さは256ピクセルになるため、ウィンドウ全体が再びページのものになります。縦向きのスマートフォンとノートパソコンでは、バーはデザインのとおりに浮きます。これは、メディアクエリーで固定表示を解除する達成方法C34です。ページ本来の「カートに入れる」ボタンは、これまでどおり上のほうにあります。

## どの方法を選ぶか

どちらの方法でも、どのサイズでもページの最後がバーに隠れないため、1.4.10と2.4.11を満たします。バーを1行に保つ方法は、CSSの1行の削除と4行の追加、バーの商品名の省略で済む最小の変更ですが、400%ではバーがウィンドウの3分の1近くを覆い続けます。そのため「それだけでは不十分」としています。高さがあるときだけ固定する方法をおすすめします。同じ変更にメディアクエリーを一つ足すだけで、高さの低いウィンドウでは画面全体を読むために使えるようになり、スマートフォンとデスクトップでの見た目はデザインのまま保てます。

デザイナーにとっては、デザインファイルに描く画面が二つ増えます。幅320ピクセルのバーを商品名を省略した形で描き、開発者がちょうどその分を確保できるよう、横にバーの高さを書きます。400%の拡大表示で残る320 × 256ピクセルのウィンドウを、バーがフッターの後に置かれた形で描きます。バーには短いものだけを1行に並べます。価格とボタンなら幅320ピクセルに収まりますが、写真、サイズの選択、商品名までは収まりません。

## バーが外部サービスのものである場合

ショップのプラットフォームのアプリや外部のスクリプトが追加するバーは、スタイルを変えられないことがよくあります。小さな画面向けの設定が提供元にあるかを確認し、マークアップが安定していれば自分のスタイルシートで位置を上書きし、更新のたびに確認し直してください。

## 実装の判断

ウィンドウの下部に浮くものはすべて数えてください。確保する余白はそのすべてを覆う必要があり、どれも400%で確認する対象です。

- バーの上のチャットボタン。[フォーカス中のボタンを一部隠すチャットボタン](/ja/learn/focus-not-covered)のとおり、その隅も確保が必要です。
- 「ページ上部へ」ボタン。たいていスクロールした後にだけ現れます。
- Cookieバナー。初回の訪問ではバーに重なって独自の高さを足し、[フォーカスを隠し、最後にしか届かないCookieバナー](/ja/learn/cookie-banners)のとおり、ページの最後に置かれます。
- 固定ヘッダー。[フォーカス中のリンクを隠す固定ヘッダーとバナー](/ja/learn/focus-not-obscured)のとおり、同じ低いウィンドウの上部を占めます。

商品名を全部表示する必要がある場合は、折り返しを残し、代わりにバーを計測します。`ResizeObserver` で表示中のバーの高さに画面端からの距離を足して `--bar-space` に設定すれば、確保する余白もバーと一緒に大きくなります。ページの最後は隠れなくなりますが、低いウィンドウではバーがさらに大きな部分を占めるので、メディアクエリーと組み合わせてください。`body` の最後の子要素にしたバーに `position: sticky` と `bottom: 1rem` を指定する方法でも、ページの最後は隠れません。stickyのバーはページの中に自分の場所を持ち、最後にはそこに収まるからです。ただし、ページの途中のフォーカスには、バーの高さに合わせた `scroll-padding-bottom` が引き続き必要です。

高さの低いウィンドウでは、ページ本来のボタンを繰り返すだけのバーを `display: none` で隠しても、移動するのと同じように機能します。ただし、バーの中のものがすべてページにもある場合に限ります。解説書が挙げるもう一つの方法は、バーをしまうボタンです。「非表示」のような名前を付け、バーのTab順序の最初のほうに置き、訪問の間はバーをしまったままにします。どちらの方法でも、浮いている間はバーを1行に保つ必要があります。

ブレークポイントは `em` で指定しています。メディアクエリーの `em` はブラウザーの標準の文字サイズを指すため、ページのCSSではなくその設定に従います。ブラウザーの拡大表示はCSSピクセルでのウィンドウを小さくするので、このブレークポイントに届きます。バーとページの読める部分の両方が収まる高さを選んでください。30emなら、高さ1024ピクセルのウィンドウを200%に拡大してもバーは浮いたままです。

ページに重なって開くチャットのパネルも浮いた要素の一つで、キーボードで閉じられ、メッセージを読み上げる必要もあります。[メッセージを読み上げないチャットウィジェット](/ja/learn/chat-widgets)を参照してください。また、バーをマークアップのどこに書くかで、キーボードとスクリーンリーダーの利用者がいつ届くかが決まり、バーが開くパネルはその次に来る必要があります。[フォーカス順の最後に回る、フローティングボタンとパネル](/ja/learn/floating-order)を参照してください。

## 修正の確認

1. 幅1280 × 高さ1024ピクセルのウィンドウで、ブラウザーを400%に拡大する（または開発者ツールでビューポートを320 × 256 CSSピクセルにする）。バーが1行か、ページの中にあることを確認する。
2. 各ページのテンプレートを最後までスクロールし、最後のリンクを含むどの行も、バーとほかの浮いたボタンの外へスクロールできることを確認する。
3. ページ上部から最後の操作要素までTabキーで進み、Shift + Tabキーで戻る。フォーカス中の要素が完全に隠れることがないことを確認する。
4. 縦向きと横向きのスマートフォン、幅320 × 高さ568ピクセルのウィンドウ、ブラウザーの文字サイズを200%にした状態で繰り返す。
5. チャットボタン、「ページ上部へ」ボタン、回答前のCookieバナー、キャンペーンのバーなど、浮いた要素をすべて表示した状態で繰り返す。

OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。バー、その文字、ページのレイアウトを変更した際は、再度確認します。

## 制限

このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。30emのブレークポイントとバーの大きさは、この例のものです。実際のバーは、最も長い商品名、価格、翻訳で確認してください。幅320ピクセルでページが横にスクロールする問題は別のもので、[狭い画面で横スクロールが出るページ](/ja/learn/reflow)で扱っています。400%で基準を満たしても、その大きさでバーが使いやすいとは限りません。バーがページの中に移ったとき、ページ本来の「カートに入れる」ボタンが見つけやすいかを確認してください。コード内のクラス名とトークン名は、実際のものに置き換えてください。

---

このガイドをコーディングエージェントで使う: MCP https://easeweb.dev/mcp（設定は https://easeweb.dev/ja/services/developers）
