デジタル庁デザインシステム:Dialogモーダルのアクセシビリティ対応実装

デジタル庁デザインシステム:Dialogモーダルのアクセシビリティ対応実装

【免费下载链接】design-system-example-components デジタル庁デザインシステムのサンプルコンポーネント 【免费下载链接】design-system-example-components 项目地址: https://gitcode.com/GitHub_Trending/de/design-system-example-components

あなたはWebアプリケーションでモーダルダイアログを実装する際、以下のような課題に直面したことはありませんか?

  • スクリーンリーダー(Screen Reader)ユーザーがダイアログの存在に気づかない
  • キーボード操作でダイアログ外にフォーカスが移動してしまう
  • ESCキーで閉じる機能が実装されていない
  • ダイアログの役割(role)やラベル付けが適切でない

これらの問題は、アクセシビリティ(Accessibility)対応が不十分なモーダル実装でよく見られる課題です。デジタル庁デザインシステムのDialogコンポーネントは、WCAG(Web Content Accessibility Guidelines)準拠を目指し、これらの課題を解決する実装パターンを提供しています。

目次

アクセシビリティ対応の重要性

Webアクセシビリティは、すべてのユーザーがWebコンテンツを利用できるようにするための設計原則です。特に行政サービスのデジタル化においては、アクセシビリティ対応は法的義務であり、社会的責任です。

mermaid

Dialogコンポーネントの基本実装

デジタル庁デザインシステムのDialogコンポーネントは、HTML5の<dialog>要素をベースに、Reactで実装されています。

import { type ComponentProps, forwardRef } from 'react';

export type DialogProps = ComponentProps<'dialog'>;

export const Dialog = forwardRef<HTMLDialogElement, DialogProps>((props, ref) => {
  const { children, className, ...rest } = props;

  return (
    <dialog
      className={`bg-transparent p-6 backdrop:bg-black/45 ${className ?? ''}`}
      onClick={(e) => {
        e.currentTarget.close();
      }}
      ref={ref}
      {...rest}
    >
      {children}
    </dialog>
  );
});

export type DialogBodyProps = ComponentProps<'div'>;

export const DialogBody = (props: DialogBodyProps) => {
  const { children, className, ...rest } = props;
  return (
    <div
      className={`flex flex-col items-center gap-4 rounded-12 border border-solid-gray-200 bg-white p-6 desktop:p-10 ${
        className ?? ''
      }`}
      onClick={(e) => {
        e.stopPropagation();
      }}
      {...rest}
    >
      {children}
    </div>
  );
};

キーボード操作の完全サポート

アクセシブルなダイアログは、マウスだけでなくキーボード操作も完全にサポートする必要があります。

必須のキーボード機能

キー動作重要性
ESCダイアログを閉じる必須
Tabダイアログ内の要素間を移動必須
Shift+Tab前の要素に移動必須
Enter主要アクションの実行推奨

フォーカストラップの実装

ダイアログ表示中は、フォーカスがダイアログ外に移動しないようにする必要があります。

// フォーカストラップの実装例
useEffect(() => {
  const handleTabKey = (e: KeyboardEvent) => {
    if (e.key === 'Tab') {
      const focusableElements = dialogRef.current?.querySelectorAll(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      
      if (focusableElements && focusableElements.length > 0) {
        const firstElement = focusableElements[0] as HTMLElement;
        const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;

        if (e.shiftKey && document.activeElement === firstElement) {
          lastElement.focus();
          e.preventDefault();
        } else if (!e.shiftKey && document.activeElement === lastElement) {
          firstElement.focus();
          e.preventDefault();
        }
      }
    }
  };

  document.addEventListener('keydown', handleTabKey);
  return () => document.removeEventListener('keydown', handleTabKey);
}, []);

スクリーンリーダー対応の詳細

スクリーンリーダーユーザーにとって、ダイアログの存在と内容を正しく認識できることが重要です。

ARIA属性の適切な使用

<Dialog
  aria-labelledby="dialog-heading"
  aria-describedby="dialog-description"
  role="dialog"
  ref={dialogRef}
>
  <DialogBody>
    <h2 id="dialog-heading">ダイアログタイトル</h2>
    <p id="dialog-description">ダイアログの説明文が入ります。</p>
    {/* 操作ボタンなど */}
  </DialogBody>
</Dialog>

アラートダイアログの特別対応

緊急性の高い通知にはrole="alertdialog"を使用します。

<Dialog 
  aria-labelledby="alert-heading" 
  ref={dialogRef} 
  role="alertdialog"
>
  <DialogBody>
    <h2 id="alert-heading">緊急通知</h2>
    <p>重要なメッセージ内容が入ります。</p>
    <Button onClick={() => dialogRef.current?.close()}>
      了解
    </Button>
  </DialogBody>
</Dialog>

フォーカス管理のベストプラクティス

適切なフォーカス管理は、アクセシビリティの核心です。

フォーカス移動の戦略

mermaid

実装コード例

const DialogExample = () => {
  const dialogRef = useRef<HTMLDialogElement>(null);
  const firstFocusableRef = useRef<HTMLButtonElement>(null);

  const openDialog = () => {
    dialogRef.current?.showModal();
    // ダイアログ表示後に最初の要素にフォーカス
    setTimeout(() => firstFocusableRef.current?.focus(), 100);
  };

  const closeDialog = () => {
    dialogRef.current?.close();
  };

  return (
    <div>
      <Button onClick={openDialog}>ダイアログを開く</Button>
      
      <Dialog ref={dialogRef} onClose={closeDialog}>
        <DialogBody>
          <h2 id="dialog-title">タイトル</h2>
          <p>コンテンツ</p>
          <Button ref={firstFocusableRef} onClick={closeDialog}>
            閉じる
          </Button>
        </DialogBody>
      </Dialog>
    </div>
  );
};

実装パターンとコード例

基本パターン

import { useRef } from 'react';
import { Button } from './Button';
import { Dialog, DialogBody } from './Dialog';

const BasicDialog = () => {
  const dialogRef = useRef<HTMLDialogElement>(null);

  return (
    <div>
      <Button onClick={() => dialogRef.current?.showModal()} size='lg' variant='solid-fill'>
        ダイアログ表示
      </Button>

      <Dialog
        aria-labelledby='dialog-heading'
        className='max-w-[560px]'
        ref={dialogRef}
      >
        <DialogBody>
          <h2 className='text-std-24B-150' id='dialog-heading'>
            ダイアログタイトル
          </h2>
          <p className='text-std-16N-170'>
            ダイアログの補助テキストが入ります。
          </p>
          <div className='mt-6 flex gap-4'>
            <Button onClick={() => dialogRef.current?.close()} size='lg'>
              確定
            </Button>
            <Button onClick={() => dialogRef.current?.close()} variant='outline' size='lg'>
              キャンセル
            </Button>
          </div>
        </DialogBody>
      </Dialog>
    </div>
  );
};

水平配置アクションパターン

const HorizontalActionsDialog = () => {
  const dialogRef = useRef<HTMLDialogElement>(null);

  return (
    <Dialog
      aria-labelledby='horizontal-dialog-heading'
      className='max-w-[640px]'
      ref={dialogRef}
    >
      <DialogBody>
        <h2 id='horizontal-dialog-heading'>タイトル</h2>
        <p>詳細な説明文</p>
        <div className='flex flex-col gap-4 desktop:flex-row-reverse'>
          <Button onClick={() => dialogRef.current?.close()} size='lg'>
            主要アクション
          </Button>
          <Button onClick={() => dialogRef.current?.close()} variant='outline' size='lg'>
            次要アクション
          </Button>
          <Button onClick={() => dialogRef.current?.close()} variant='text' size='lg'>
            キャンセル
          </Button>
        </div>
      </DialogBody>
    </Dialog>
  );
};

テストと検証方法

アクセシビリティ対応を確認するためのテスト方法を紹介します。

自動テストツール

ツール用途特徴
Axe自動アクセシビリティテストCI/CDパイプライン統合可能
Lighthouse総合的な品質評価Chrome DevTools統合
WAVE視覚的なエラー表示ブラウザ拡張機能

手動テストチェックリスト

  1. キーボード操作テスト

    • Tabキーで全ての操作可能要素にアクセスできる
    • Shift+Tabで逆方向移動が可能
    • ESCキーでダイアログを閉じられる
  2. スクリーンリーダーテスト

    • ダイアログ表示時に適切なアナウンスがある
    • タイトルと説明文が正しく読み上げられる
    • 操作要素のラベルが適切
  3. フォーカス管理テスト

    • ダイアログ表示時にフォーカスが適切な要素に移動
    • ダイアログ閉鎖時に元の要素にフォーカスが戻る
    • フォーカスがダイアログ外に漏れない
  4. 視覚的テスト

    • 十分なコントラスト比(4.5:1以上)
    • フォーカスインジケータが明確に表示
    • レスポンシブデザイン対応

Storybookでのテスト

デジタル庁デザインシステムはStorybookを採用しており、各コンポーネントのアクセシビリティを視覚的に確認できます。

npm run storybook

Storybookのアクセシビリティアドオンを使用することで、ARIA属性の状態やコントラスト比などをリアルタイムで確認できます。

まとめ

デジタル庁デザインシステムのDialogコンポーネントは、WCAGガイドラインに準拠したアクセシブルなモーダル実装を提供します。重要なポイントは:

  • 適切なARIA属性でスクリーンリーダー対応
  • 完全なキーボード操作サポート
  • 堅牢なフォーカス管理によるユーザビリティ確保
  • 視覚的明確性のためのデザイントークン活用

これらの実装パターンを活用することで、すべてのユーザーが利用しやすい行政デジタルサービスを構築できます。アクセシビリティは後付けではなく、設計段階から組み込むべき重要な要素です。

デジタル庁デザインシステムのコンポーネントは、継続的に改善されていくオープンソースプロジェクトです。実際の実装では、最新のバージョンとドキュメントを確認し、プロジェクトの要件に合わせて適切にカスタマイズしてください。

【免费下载链接】design-system-example-components デジタル庁デザインシステムのサンプルコンポーネント 【免费下载链接】design-system-example-components 项目地址: https://gitcode.com/GitHub_Trending/de/design-system-example-components

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值