Skip to main content

Overview

The system is built with accessibility in mind, following WCAG guidelines to ensure usability for all users including those with disabilities.

Keyboard Navigation

Form Navigation

All forms support keyboard navigation:
<Input
  id="username"
  type="text"
  placeholder="Ingresa tu usuario"
  value={username}
  onChange={(e) => setUsername(e.target.value)}
  disabled={isSubmitting}
  autoComplete="username"
/>
From ~/workspace/source/src/pages/Login.tsx:93-101 Features:
  • Tab navigation through fields
  • Enter to submit forms
  • Autocomplete support
  • Disabled state management

Button Focus States

Buttons show focus indicators:
<Button type="submit" className="w-full" disabled={isSubmitting}>
  Iniciar Sesión
</Button>
Focus rings visible when navigating with keyboard.

Semantic HTML

Proper Form Labels

All inputs have associated labels:
<div className="space-y-2">
  <Label htmlFor="username">Usuario</Label>
  <Input
    id="username"
    type="text"
    // ...
  />
</div>
From ~/workspace/source/src/pages/Login.tsx:91-101 Labels properly associated via htmlFor and id attributes.

Table Structure

Semantic table markup:
<table className="w-full border-collapse">
  <thead>
    <tr className="border-b bg-muted/50">
      <th className="text-left p-3 font-semibold text-sm">
        Alumno
      </th>
      <th className="text-center p-3 font-semibold text-sm">
        Total
      </th>
    </tr>
  </thead>
  <tbody>
    {/* Table rows */}
  </tbody>
</table>
From ~/workspace/source/src/pages/Finanzas.tsx:812-826 Proper <thead>, <tbody>, <th> usage for screen readers.

ARIA Attributes

Dialog Accessibility

<Dialog open={isDialogOpen} onOpenChange={handleCloseDialog}>
  <DialogContent className="sm:max-w-lg">
    <DialogHeader>
      <DialogTitle className="font-display">
        {claseToEdit ? "Editar Clase" : "Nueva Clase"}
      </DialogTitle>
      <DialogDescription>
        {claseToEdit
          ? `Editando clase de ${getNombreCompletoParaClase(claseToEdit)}`
          : `Programar clase para el ${format(currentDate, "d 'de' MMMM 'de' yyyy", { locale: es })}`}
      </DialogDescription>
    </DialogHeader>
    {/* Dialog content */}
  </DialogContent>
</Dialog>
From ~/workspace/source/src/pages/Calendario.tsx:251-262 Proper dialog structure with title and description for screen readers.

Loading States

Accessible loading indicators:
<div className="min-h-screen flex items-center justify-center bg-background">
  <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
</div>
From ~/workspace/source/src/components/auth/ProtectedRoute.tsx:16-18 Could be enhanced with role="status" and aria-live="polite".

Color Contrast

High Contrast Text

Text colors meet WCAG AA standards:
  • Primary text: High contrast against background
  • Muted text: Sufficient contrast for readability
  • Links: Distinguishable from regular text

Status Colors

Status indicators use both color and text:
<StatusBadge status={c.disponible ? "success" : "error"}>
  {c.disponible ? "Disponible" : "No disp."}
</StatusBadge>
From ~/workspace/source/src/pages/Finanzas.tsx:1220-1222 Doesn’t rely on color alone - includes text labels.

Visual Feedback

Button States

Clear visual states for buttons:
<Button variant="outline" size="sm" disabled={isLoading}>
  {isLoading ? (
    <span className="flex items-center gap-2">
      <span className="animate-spin rounded-full h-4 w-4 border-b-2 border-primary-foreground"></span>
      Cargando...
    </span>
  ) : (
    <span className="flex items-center gap-2">
      <Download className="mr-2 h-4 w-4" />
      Exportar
    </span>
  )}
</Button>
States:
  • Default
  • Hover
  • Focus
  • Active
  • Disabled
  • Loading

Error States

Visible error feedback:
{error && (
  <div className="px-6">
    <Alert variant="destructive" className="mb-4">
      <AlertDescription>{error}</AlertDescription>
    </Alert>
  </div>
)}
From ~/workspace/source/src/pages/Login.tsx:82-88 Errors clearly indicated with color, icon, and text.

Password Visibility

Toggle for Better Accessibility

const [showPassword, setShowPassword] = useState(false);

<div className="relative">
  <Input
    id="password"
    type={showPassword ? "text" : "password"}
    // ...
  />
  <Button
    type="button"
    variant="ghost"
    size="sm"
    onClick={() => setShowPassword(!showPassword)}
  >
    {showPassword ? (
      <EyeOff className="h-4 w-4 text-muted-foreground" />
    ) : (
      <Eye className="h-4 w-4 text-muted-foreground" />
    )}
  </Button>
</div>
From ~/workspace/source/src/pages/Login.tsx:23,105-128 Allows users to verify password entry, helpful for:
  • Users with motor difficulties
  • Users with dyslexia
  • All users on mobile devices

Form Validation

Inline Validation Messages

Clear validation feedback:
if (!username.trim() || !password.trim()) {
  toast({
    title: "Error",
    description: "Por favor completa todos los campos",
    variant: "destructive",
  });
  return;
}
From ~/workspace/source/src/pages/Login.tsx:37-44

Password Strength Indicator

Visual and textual strength feedback:
<div className="space-y-2">
  <div className="flex justify-between text-xs">
    <span className="text-muted-foreground">
      Seguridad de la contraseña
    </span>
    <span className="font-medium">
      {passwordStrength < 40
        ? "Débil"
        : passwordStrength < 80
          ? "Regular"
          : "Fuerte"}
    </span>
  </div>
  <Progress value={passwordStrength} className="h-2" />
</div>
From ~/workspace/source/src/pages/Register.tsx:217-230 Combines visual progress bar with text description.

Touch Targets

Minimum Size

Buttons meet minimum touch target size:
<Button type="submit" className="w-full">
  Iniciar Sesión
</Button>
Full-width buttons easy to tap on mobile (44px+ height).

Spacing

Adequate spacing between interactive elements:
<div className="space-y-4">
  {/* Form fields with spacing */}
</div>
Prevents accidental taps on wrong elements.

Focus Management

Dialog Focus Trap

Dialogs trap focus when open:
<Dialog open={isDialogOpen} onOpenChange={handleCloseDialog}>
  <DialogContent>
    {/* Focus trapped within dialog */}
  </DialogContent>
</Dialog>
Users can’t accidentally tab out of modal dialogs.

Return Focus

Focus returns to trigger element when dialog closes.

Screen Reader Support

Descriptive Text

Descriptions for screen readers:
<DialogDescription>
  {claseToEdit
    ? `Editando clase de ${getNombreCompletoParaClase(claseToEdit)}`
    : `Programar clase para el ${format(currentDate, "d 'de' MMMM 'de' yyyy", { locale: es })}`}
</DialogDescription>
From ~/workspace/source/src/pages/Calendario.tsx:257-260

Icon Alternatives

Icons paired with text:
<Button>
  <Download className="mr-2 h-4 w-4" />
  Exportar
</Button>
Text provides context when icons aren’t visible.

Language

HTML Lang Attribute

Properly set language for screen readers:
<html lang="es">
Spanish language set for proper pronunciation.

Progressive Enhancement

Works Without JavaScript

Core functionality available even if JS fails:
  • Forms submit via HTTP POST
  • Links navigate without JS
  • Content readable without CSS

Graceful Degradation

Features degrade gracefully:
  • Charts show data tables as fallback
  • Complex interactions have simple alternatives
  • Error states handled properly

Best Practices Implemented

  1. Semantic HTML - Proper use of headings, lists, tables
  2. Keyboard Navigation - All features accessible via keyboard
  3. Focus Indicators - Visible focus states
  4. Color Contrast - WCAG AA compliant
  5. Text Alternatives - Icons paired with text
  6. Form Labels - All inputs properly labeled
  7. Error Messages - Clear, actionable error feedback
  8. Touch Targets - Adequate size for touch input
  9. Responsive Design - Works on all device sizes
  10. Loading States - Clear feedback during operations

Areas for Improvement

  1. ARIA Live Regions - Add for dynamic content updates
  2. Skip Links - Add skip to main content link
  3. Landmark Regions - More explicit landmark roles
  4. Alt Text - Ensure all images have descriptive alt text
  5. Heading Hierarchy - Verify proper h1-h6 usage
  6. Screen Reader Testing - Test with NVDA/JAWS
  7. High Contrast Mode - Support Windows high contrast
  8. Reduced Motion - Respect prefers-reduced-motion
  9. Focus Visible - Use :focus-visible for better UX
  10. ARIA Descriptions - More descriptive ARIA labels

Testing Accessibility

Automated Tools

  1. Lighthouse - Chrome DevTools accessibility audit
  2. axe DevTools - Browser extension for accessibility testing
  3. WAVE - Web accessibility evaluation tool

Manual Testing

  1. Keyboard Only - Navigate entire app with keyboard
  2. Screen Reader - Test with NVDA (Windows) or VoiceOver (Mac)
  3. Zoom - Test at 200% zoom level
  4. Color Blindness - Test with color blindness simulators
  5. Contrast Checker - Verify all text meets WCAG standards

WCAG Compliance

Target compliance level: WCAG 2.1 Level AA Key criteria:
  • Text contrast ≥ 4.5:1 (normal text)
  • Text contrast ≥ 3:1 (large text)
  • Touch targets ≥ 44x44px
  • Keyboard accessible
  • Focus visible
  • Error identification
  • Labels or instructions

Build docs developers (and LLMs) love