Skip to content

モーダル利用時のaria-hiddenによるフォーカス警告の解決方法

問題の説明

Angularアプリケーションでモーダルダイアログ(ng-bootstrap)使用時に、次の警告が発生します:

Blocked aria-hidden on an element because its descendant retained focus. 
The focus must not be hidden from assistive technology users.
Avoid using aria-hidden on a focused element or its ancestor. 
Consider using the inert attribute instead.

根本原因
モーダル表示時に以下の状況が同時に発生しています:

  1. モーダルを開くボタンがフォーカスを保持したまま
  2. 祖先要素にaria-hidden="true"が追加される
  3. フォーカスされた要素が非表示領域内に残る

この組み合わせがアクセシビリティ上の衝突を引き起こし、スクリーンリーダー利用者がフォーカスを見失うリスクがあります。

推奨解決方法:フォーカスの明示的解除

サービスの拡張 (大規模プロジェクト向け)

NgbModal を拡張したカスタムサービスを作成し、モーダル表示時に自動でフォーカスを解除します:

typescript
// custom-modal.service.ts
import { Injectable } from '@angular/core';
import { NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';

@Injectable({ providedIn: 'root' })
export class CustomModalService extends NgbModal {
  override open(content: unknown, options?: NgbModalOptions): NgbModalRef {
    const activeElement = document.activeElement as HTMLElement;
    activeElement?.blur(); // アクティブ要素からフォーカスを解除
    return super.open(content, options); // 通常処理を継続
  }
}

app.module.tsでの登録

typescript
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { CustomModalService } from './custom-modal.service';

@NgModule({
  providers: [{ provide: NgbModal, useClass: CustomModalService }]
})
export class AppModule {}

コンポーネント内での直接処理 (小規模プロジェクト向け)

typescript
openModal() {
  const buttonElement = document.activeElement as HTMLElement;
  buttonElement?.blur(); // フォーカス解除
  this.modalService.open(ModalComponent, config);
}

動作の仕組み

  1. document.activeElement で現在フォーカス中の要素取得
  2. blur() で明示的にフォーカス解除
  3. モーダルを表示(解除後にaria-hiddenが適用される)

代替解決方法

ディレクティブを使用した汎用的な対応

クリック時に自動的にフォーカスを解除するディレクティブ:

typescript
// blur-on-click.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appBlurOnClick]',
  standalone: true
})
export class BlurOnClickDirective {
  constructor(private el: ElementRef) {}

  @HostListener('click')
  onClick(): void {
    this.el.nativeElement.blur(); // クリック直後にフォーカス解除
  }
}

テンプレートでの使用方法

html
<button 
  appBlurOnClick 
  (click)="openModal()">
  Open Modal
</button>

イベントリスナーを使ったグローバル対応

個別コンポーネント修正が難しい場合の方法:

typescript
document.addEventListener('hide.bs.modal', () => {
  if (document.activeElement) {
    (document.activeElement as HTMLElement).blur();
  }
});

非推奨な回避策

避けるべき方法

aria-hidden属性の単純削除

diff
- <span aria-hidden="true">&times;</span>
+ <span>&times;</span>

この方法は警告を抑制しますが、スクリーンリーダーが不要な要素を読み上げるため、アクセシビリティを低下させます

inert属性の適用:

html
<div inert>...</div>

理論的には理想的な解決策ですが、2025年7月現在ではブラウザ実装が不安定なため推奨できません。

ベストプラクティスまとめ

  1. モーダル開閉時のフォーカス管理を常に意識
  2. フォーカス解除はblur()で明示的に実行
  3. 大規模プロジェクトではカスタムサービスの採用
  4. アクセシビリティチェッカーでの定期検証
  5. inert属性はブラウャサポート安定後に移行

モーダル実装時は必ずフォーカスの初期位置をモーダル内に設定し、閉じた後に適切な要素へフォーカスを戻すことも重要です。