デジタル庁デザインシステム:アクセシビリティ対応フォームコンポーネントの実装手法...

デジタル庁デザインシステム:アクセシビリティ対応フォームコンポーネントの実装手法

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

はじめに

Webフォームはユーザーとの主要なインタラクションポイントであり、アクセシビリティ(Accessibility、利用しやすさ)の確保が最も重要な課題の一つです。デジタル庁デザインシステム(DADS)は、日本の行政サービス向けに設計されたデザインシステムで、特にアクセシビリティを最優先事項として位置付けています。

本記事では、DADSのReact版コンポーネントにおけるフォーム要素のアクセシビリティ実装手法を詳細に解説します。WCAG(Web Content Accessibility Guidelines、ウェブコンテンツアクセシビリティガイドライン)準拠の実装パターンから、具体的なコード実装まで、実践的なノウハウを提供します。

アクセシビリティ実装の基本原則

4つの核心原則

mermaid

HTMLネイティブ機能の活用戦略

DADSでは保守性の観点から、可能な限りHTMLネイティブの機能を使用することを重視しています。これにより、ブラウザのネイティブアクセシビリティ機能を最大限に活用できます。

主要フォームコンポーネントの実装手法

1. 入力フィールド(Input)コンポーネント

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

export type InputBlockSize = 'lg' | 'md' | 'sm';

export type InputProps = ComponentProps<'input'> & {
  isError?: boolean;
  blockSize?: InputBlockSize;
};

export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
  const { className, readOnly, isError, blockSize = 'lg', ...rest } = props;

  return (
    <input
      className={`
        max-w-full rounded-8 border bg-white px-4 py-3 border-solid-gray-600 text-oln-16N-100 text-solid-gray-800
        hover:[&:read-write]:border-black
        data-[size=sm]:h-10 data-[size=md]:h-12 data-[size=lg]:h-14
        aria-[invalid=true]:border-error-1 aria-[invalid=true]:[&:read-write]:hover:border-red-1000
        focus:outline focus:outline-4 focus:outline-black focus:outline-offset-[calc(2/16*1rem)] focus:ring-[calc(2/16*1rem)] focus:ring-yellow-300
        read-only:border-dashed
        aria-disabled:border-solid-gray-300 aria-disabled:!border-solid aria-disabled:bg-solid-gray-50 aria-disabled:text-solid-gray-420 aria-disabled:pointer-events-none aria-disabled:forced-colors:text-[GrayText] aria-disabled:forced-colors:border-[GrayText]
        ${className ?? ''}
      `}
      aria-invalid={isError || undefined}
      data-size={blockSize}
      readOnly={props['aria-disabled'] ? true : readOnly}
      ref={ref}
      {...rest}
    />
  );
});
実装のポイント
機能実装手法アクセシビリティ効果
エラー状態aria-invalid属性スクリーンリーダーにエラー状態を通知
フォーカス表示黄色アウトライン視覚的に明確なフォーカス表示
無効状態aria-disabled + CSSキーボード操作の適切な制御
読み取り専用readOnly属性状態の明確な伝達

2. チェックボックス(Checkbox)コンポーネント

export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
  const { children, isError, onClick, size = 'sm', ...rest } = props;

  const handleDisabled = (e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
    e.preventDefault();
  };

  const checkbox = (
    <span
      className={`
        flex items-center justify-center shrink-0 rounded-[calc(1/8*100%)]
        data-[size=sm]:size-6 data-[size=md]:size-8 data-[size=lg]:size-11
        has-[input:hover:not(:focus):not([aria-disabled="true"])]:bg-solid-gray-420
      `}
      data-size={size}
    >
      <input
        className={`
          appearance-none size-3/4 rounded-[calc(2/18*100%)] border-solid-gray-600 bg-white bg-clip-padding
          hover:border-black
          focus:outline focus:outline-4 focus:outline-black focus:outline-offset-[calc(2/16*1rem)] focus:ring-[calc(2/16*1rem)] focus:ring-yellow-300
          checked:border-blue-900 checked:bg-blue-900 checked:hover:border-blue-1100 checked:hover:bg-blue-1100
          indeterminate:border-blue-900 indeterminate:bg-blue-900 indeterminate:hover:border-blue-1100 indeterminate:hover:bg-blue-1100
          before:hidden before:size-3.5 before:bg-white
          checked:before:block checked:before:[clip-path:path('M5.6,11.2L12.65,4.15L11.25,2.75L5.6,8.4L2.75,5.55L1.35,6.95L5.6,11.2Z')]
          indeterminate:before:block indeterminate:before:[clip-path:path('M3.25,7.75H10.75V6.25H3.25V7.75Z')]
          data-[size=sm]:border-[calc(2/16*1rem)]
          data-[size=md]:border-[calc(2/16*1rem)] data-[size=md]:before:origin-top-left data-[size=md]:before:scale-[calc(20/14)]
          data-[size=lg]:border-[calc(3/16*1rem)] data-[size=lg]:before:origin-top-left data-[size=lg]:before:scale-[calc(27/14)]
          data-[error]:border-error-1 data-[error]:hover:border-red-1000 data-[error]:checked:bg-error-1 data-[error]:checked:hover:bg-red-1000 data-[error]:indeterminate:bg-error-1 data-[error]:indeterminate:hover:bg-red-1000
          aria-disabled:!border-solid-gray-300 aria-disabled:!bg-solid-gray-50 aria-disabled:checked:!bg-solid-gray-300 aria-disabled:indeterminate:!bg-solid-gray-300 aria-disabled:before:border-solid-gray-50
          forced-colors:!border-[ButtonText] forced-colors:checked:!bg-[Highlight] forced-colors:checked:!border-[Highlight] forced-colors:indeterminate:!bg-[Highlight] forced-colors:indeterminate:!border-[Highlight] forced-colors:before:!bg-[HighlightText] forced-colors:aria-disabled:!border-[GrayText] forced-colors:aria-disabled:checked:!bg-[GrayText]
        `}
        onClick={props['aria-disabled'] ? handleDisabled : onClick}
        ref={ref}
        type='checkbox'
        data-size={size}
        data-error={isError || null}
        {...rest}
      />
    </span>
  );

  return children ? (
    <label
      className='flex w-fit items-start py-2 data-[size=sm]:gap-1 data-[size=md]:gap-2 data-[size=lg]:gap-2'
      data-size={size}
    >
      {checkbox}
      <span
        className='text-solid-gray-800 data-[size=sm]:pt-px data-[size=sm]:text-dns-16N-130 data-[size=md]:pt-1 data-[size=md]:text-dns-16N-130 data-[size=lg]:pt-2.5 data-[size=lg]:text-dns-17N-130'
        data-size={size}
      >
        {children}
      </span>
    </label>
  ) : (
    checkbox
  );
});
チェックボックスのアクセシビリティ機能

mermaid

3. セレクトボックス(Select)コンポーネント

export const Select = forwardRef<HTMLSelectElement, SelectProps>((props, ref) => {
  const { children, className, isError, blockSize = 'lg', onKeyDown, onMouseDown, ...rest } = props;

  const handleDisabledKeyDown = (e: React.KeyboardEvent<HTMLSelectElement>) => {
    if (e.code !== 'Tab') {
      e.preventDefault();
    }
  };

  const handleDisabledMouseDown = (e: React.MouseEvent<HTMLSelectElement, MouseEvent>) => {
    e.preventDefault();
  };

  return (
    <span className='relative'>
      <select
        className={`
          w-full appearance-none border border-solid-gray-600 rounded-8 bg-white pl-4 pr-10 py-[calc(11/16*1rem)] text-oln-16N-100 text-solid-gray-800
          hover:border-black
          data-[size=sm]:h-10 data-[size=md]:h-12 data-[size=lg]:h-14
          aria-[invalid=true]:border-error-1 aria-[invalid=true]:hover:border-red-1000
          focus:outline focus:outline-4 focus:outline-black focus:outline-offset-[calc(2/16*1rem)] focus:ring-[calc(2/16*1rem)] focus:ring-yellow-300
          aria-disabled:border-solid-gray-300 aria-disabled:bg-solid-gray-50 aria-disabled:text-solid-gray-420 aria-disabled:pointer-events-none aria-disabled:forced-colors:text-[GrayText] aria-disabled:forced-colors:border-[GrayText]
          ${className ?? ''}
        `}
        aria-invalid={isError || undefined}
        data-size={blockSize}
        onMouseDown={props['aria-disabled'] ? handleDisabledMouseDown : onMouseDown}
        onKeyDown={props['aria-disabled'] ? handleDisabledKeyDown : onKeyDown}
        ref={ref}
        {...rest}
      >
        {children}
      </select>
      <svg
        aria-hidden={true}
        className={`
          pointer-events-none absolute right-4 top-1/2 -translate-y-1/2
          ${props['aria-disabled'] ? 'text-solid-gray-420 forced-colors:text-[GrayText]' : 'text-solid-gray-900 forced-colors:text-[CanvasText]'}
        `}
        fill='none'
        height='16'
        viewBox='0 0 16 16'
        width='16'
      >
        <path
          d='M13.3344 4.40002L8.00104 9.73336L2.66771 4.40002L1.73438 5.33336L8.00104 11.6L14.2677 5.33336L13.3344 4.40002Z'
          fill='currentColor'
        />
      </svg>
    </span>
  );
});

キーボードナビゲーションの実装

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

操作実装方法アクセシビリティ効果
Tabキー移動ネイティブtabindex論理的なフォーカス順序
フォーカス表示黄色アウトライン + リング視認性の高いフォーカス表示
無効要素のスキップaria-disabled + イベント制御不要な操作の防止
キーボード操作適切なイベントハンドリング全ての操作のキーボード対応

フォーカスリングの実装例

focus:outline focus:outline-4 focus:outline-black 
focus:outline-offset-[calc(2/16*1rem)] 
focus:ring-[calc(2/16*1rem)] focus:ring-yellow-300

この実装により、以下の利点が得られます:

  • 4pxの黒いアウトラインで明確な視覚的フィードバック
  • 2pxのオフセットで要素から離れた表示
  • 2pxの黄色リングで強調表示
  • 高コントラストによる視認性の向上

エラー処理とユーザー支援

エラー状態の伝達方法

mermaid

支援テキストの関連付け

// エラーテキストとの関連付け例
<Input 
  aria-describedby="error-message-id"
  aria-invalid={true}
/>
<ErrorText id="error-message-id">
  入力内容に誤りがあります
</ErrorText>

高コントラストモード対応

forced-colorsメディアクエリの活用

DADSコンポーネントでは、Windowsの高コントラストモードなどに対応するために、forced-colorsメディアクエリを積極的に活用しています。

aria-disabled:forced-colors:text-[GrayText] 
aria-disabled:forced-colors:border-[GrayText]
forced-colors:!border-[ButtonText] 
forced-colors:checked:!bg-[Highlight]
対応カラーパターン
状態標準モード高コントラストモード
無効テキストtext-solid-gray-420text-[GrayText]
無効ボーダーborder-solid-gray-300border-[GrayText]
チェック状態bg-blue-900bg-[Highlight]
ボタンテキストtext-blue-900text-[ButtonText]

実装チェックリスト

必須アクセシビリティ項目

  •  適切なラベル付け: aria-label, aria-labelledbyの適切な使用
  •  エラー状態の伝達: aria-invalidaria-describedbyの連携
  •  キーボード操作: 全ての機能のキーボードアクセス可能
  •  フォーカス表示: 明確で視認性の高いフォーカスインジケーター
  •  無効状態の処理: aria-disabledの適切な使用とイベント制御
  •  高コントラスト対応: forced-colorsメディアクエリの実装
  •  スクリーンリーダー対応: 適切なARIA属性とセマンティックHTML

推奨アクセシビリティ項目

  •  操作可能領域の拡大: タッチターゲットサイズの適切な確保
  •  状態変化の通知: aria-liveによる動的コンテンツの通知
  •  複数言語対応: 言語属性の適切な設定
  •  パフォーマンス最適化: 操作遅延の最小化

まとめ

デジタル庁デザインシステムのフォームコンポーネントは、WCAGガイドラインに完全準拠したアクセシビリティ実装を提供しています。HTMLネイティブ機能を最大限に活用しつつ、必要な箇所ではARIA属性を適切に使用することで、全てのユーザーが利用しやすいフォーム体験を実現しています。

特に重要なのは、単なる技術的な準拠ではなく、実際のユーザー体験を考慮した実装であることです。キーボード操作の快適さ、スクリーンリーダーでの情報伝達の明確さ、視覚的なフィードバックの適切さなど、多角的な観点からアクセシビリティを確保しています。

これらの実装手法は、行政サービスだけでなく、あらゆるWebアプリケーションで応用可能なベストプラクティスです。アクセシビリティを考慮した設計は、結果的に全てのユーザーにとって使いやすいインターフェースを生み出します。

【免费下载链接】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、付费专栏及课程。

余额充值