Tips & Tricks

Comment optimiser l'accessibilite avec Claude Code

Apprenez a optimiser l'accessibilite avec Claude Code. Inclut des exemples de code pratiques et un guide etape par etape.

Avantages de l’utilisation de Claude Code pour l’accessibilite

L’accessibilite web est importante, mais comprendre les criteres WCAG et l’utilisation correcte des attributs ARIA est complexe. Claude Code peut generer du code efficacement en suivant les meilleures pratiques d’accessibilite et auditer le code existant.

Audit d’accessibilite du code existant

> Passe en revue tous les composants dans src/components/ du point de vue de l'accessibilite.
> Verifie selon les criteres WCAG 2.1 AA et liste les problemes avec les solutions proposees.

Claude Code detecte des problemes tels que :

  • Images sans attribut alt
  • Elements de formulaire sans etiquette label associee
  • Ratio de contraste de couleur insuffisant
  • Elements interactifs non accessibles au clavier
  • Attributs ARIA utilises incorrectement

Generation de composants accessibles

Dialogue modal

> Cree un dialogue modal accessible.
> Implemente le piege a focus, la fermeture par touche Echap et le verrouillage du defilement de l'arriere-plan.
import { useEffect, useRef, useCallback } from 'react';

interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
  title: string;
  children: React.ReactNode;
}

function Modal({ isOpen, onClose, title, children }: ModalProps) {
  const modalRef = useRef<HTMLDivElement>(null);
  const previousFocus = useRef<HTMLElement | null>(null);

  // Piege a focus
  const trapFocus = useCallback((e: KeyboardEvent) => {
    if (!modalRef.current) return;

    const focusable = modalRef.current.querySelectorAll<HTMLElement>(
      'a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'
    );

    const first = focusable[0];
    const last = focusable[focusable.length - 1];

    if (e.key === 'Tab') {
      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    }

    if (e.key === 'Escape') onClose();
  }, [onClose]);

  useEffect(() => {
    if (isOpen) {
      previousFocus.current = document.activeElement as HTMLElement;
      document.body.style.overflow = 'hidden';
      document.addEventListener('keydown', trapFocus);

      // Mettre le focus sur le premier element focusable dans le modal
      setTimeout(() => {
        modalRef.current?.querySelector<HTMLElement>('[autofocus], button')?.focus();
      }, 0);
    }

    return () => {
      document.body.style.overflow = '';
      document.removeEventListener('keydown', trapFocus);
      previousFocus.current?.focus();
    };
  }, [isOpen, trapFocus]);

  if (!isOpen) return null;

  return (
    <div
      className="fixed inset-0 z-50 flex items-center justify-center"
      role="presentation"
    >
      <div
        className="fixed inset-0 bg-black/50"
        aria-hidden="true"
        onClick={onClose}
      />
      <div
        ref={modalRef}
        role="dialog"
        aria-modal="true"
        aria-labelledby="modal-title"
        className="relative z-10 mx-4 w-full max-w-lg rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800"
      >
        <h2 id="modal-title" className="text-xl font-bold mb-4">
          {title}
        </h2>
        {children}
        <button
          onClick={onClose}
          aria-label="Fermer"
          className="absolute right-4 top-4 rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-700"
        >
          <svg aria-hidden="true" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
            <path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" />
          </svg>
        </button>
      </div>
    </div>
  );
}
function DropdownMenu({ label, items }: { label: string; items: MenuItem[] }) {
  const [isOpen, setIsOpen] = useState(false);
  const [activeIndex, setActiveIndex] = useState(-1);
  const menuRef = useRef<HTMLUListElement>(null);

  const handleKeyDown = (e: React.KeyboardEvent) => {
    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        setActiveIndex(prev => Math.min(prev + 1, items.length - 1));
        break;
      case 'ArrowUp':
        e.preventDefault();
        setActiveIndex(prev => Math.max(prev - 1, 0));
        break;
      case 'Enter':
      case ' ':
        e.preventDefault();
        if (activeIndex >= 0) items[activeIndex].onClick();
        setIsOpen(false);
        break;
      case 'Escape':
        setIsOpen(false);
        break;
    }
  };

  return (
    <div className="relative" onKeyDown={handleKeyDown}>
      <button
        aria-haspopup="true"
        aria-expanded={isOpen}
        onClick={() => setIsOpen(!isOpen)}
      >
        {label}
      </button>
      {isOpen && (
        <ul ref={menuRef} role="menu" className="absolute mt-1 rounded-md border bg-white shadow-lg dark:bg-gray-800">
          {items.map((item, i) => (
            <li
              key={i}
              role="menuitem"
              tabIndex={-1}
              className={`cursor-pointer px-4 py-2 ${i === activeIndex ? 'bg-blue-100 dark:bg-blue-900' : ''}`}
              onClick={item.onClick}
            >
              {item.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Accessibilite des formulaires

> Reconstruis le formulaire de contact de maniere accessible.
> Fais en sorte que les erreurs de validation soient communiquees aux lecteurs d'ecran.
function ContactForm() {
  const [errors, setErrors] = useState<Record<string, string>>({});

  return (
    <form aria-label="Formulaire de contact" noValidate>
      <div className="mb-4">
        <label htmlFor="name" className="block font-medium mb-1">
          Nom <span aria-label="obligatoire">*</span>
        </label>
        <input
          id="name"
          type="text"
          required
          aria-required="true"
          aria-invalid={!!errors.name}
          aria-describedby={errors.name ? 'name-error' : undefined}
          className="w-full rounded border p-2"
        />
        {errors.name && (
          <p id="name-error" role="alert" className="mt-1 text-sm text-red-600">
            {errors.name}
          </p>
        )}
      </div>

      <div className="mb-4">
        <label htmlFor="email" className="block font-medium mb-1">
          Adresse e-mail <span aria-label="obligatoire">*</span>
        </label>
        <input
          id="email"
          type="email"
          required
          aria-required="true"
          aria-invalid={!!errors.email}
          aria-describedby="email-hint email-error"
          className="w-full rounded border p-2"
        />
        <p id="email-hint" className="mt-1 text-xs text-gray-500">
          ex. : [email protected]
        </p>
        {errors.email && (
          <p id="email-error" role="alert" className="mt-1 text-sm text-red-600">
            {errors.email}
          </p>
        )}
      </div>

      <button type="submit" className="rounded bg-blue-600 px-4 py-2 text-white">
        Envoyer
      </button>
    </form>
  );
}

Introduction des tests automatises

> Ajoute des tests d'accessibilite avec jest-axe.
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

describe('Modal accessibility', () => {
  it('should have no accessibility violations', async () => {
    const { container } = render(
      <Modal isOpen={true} onClose={() => {}} title="Test Modal">
        <p>Content</p>
      </Modal>
    );
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });
});

Summary

Avec Claude Code, vous pouvez generer efficacement des composants accessibles conformes aux criteres WCAG et auditer le code existant. Il est egalement efficace de configurer l’execution automatique des tests d’accessibilite avec la fonctionnalite de hooks. Pour plus de details, consultez le guide des hooks. Les astuces pour l’integrer dans votre flux de developpement quotidien sont presentees dans Conseils pour tripler votre productivite.

Pour plus de details sur Claude Code, consultez la documentation officielle d’Anthropic. Pour les details des directives WCAG, consultez W3C WCAG 2.1.

#Claude Code #accessibility #WCAG #a11y #React