/// Accessibility Wrapper /// /// Enforces WCAG 3.0+ accessibility on all wrapped widgets. /// AI agents: wrap generated UI components with these helpers /// to guarantee accessibility compliance. library; import 'package:flutter/material.dart'; /// Wraps any widget to enforce minimum touch target size /// Per WCAG 3.0+ and platform guidelines: 44x44 logical pixels minimum class AccessibleTouchTarget extends StatelessWidget { final Widget child; final String semanticLabel; static const double minSize = 44.0; const AccessibleTouchTarget({ super.key, required this.child, required this.semanticLabel, }); @override Widget build(BuildContext context) { return Semantics( label: semanticLabel, child: ConstrainedBox( constraints: const BoxConstraints( minWidth: minSize, minHeight: minSize, ), child: child, ), ); } } /// Focus-aware wrapper that provides visible focus indicators /// Per WCAG 3.0+: 3px outline with 3:1 contrast ratio class AccessibleFocusWrapper extends StatelessWidget { final Widget child; final Color focusColor; final double focusWidth; const AccessibleFocusWrapper({ super.key, required this.child, this.focusColor = Colors.blue, this.focusWidth = 3.0, }); @override Widget build(BuildContext context) { return Focus( child: Builder( builder: (context) { final hasFocus = Focus.of(context).hasFocus; return Container( decoration: hasFocus ? BoxDecoration( border: Border.all( color: focusColor, width: focusWidth, ), borderRadius: BorderRadius.circular(4), ) : null, child: child, ); }, ), ); } } /// Screen reader announcement helper /// Use this when state changes need to be announced class AccessibleAnnouncement { /// Announce a state change to screen readers static void announce(BuildContext context, String message) { SemanticsService.announce(message, TextDirection.ltr); } /// Announce a polite update (doesn't interrupt current speech) static void announcePolite(BuildContext context, String message) { SemanticsService.announce(message, TextDirection.ltr); } } /// Contrast checker utility /// Validates that text/background combinations meet WCAG AAA (7:1) class ContrastChecker { static const double wcagAAA = 7.0; static const double wcagAA = 4.5; /// Calculate contrast ratio between two colors static double contrastRatio(Color foreground, Color background) { final fgLuminance = foreground.computeLuminance(); final bgLuminance = background.computeLuminance(); final lighter = fgLuminance > bgLuminance ? fgLuminance : bgLuminance; final darker = fgLuminance > bgLuminance ? bgLuminance : fgLuminance; return (lighter + 0.05) / (darker + 0.05); } /// Check if a color pair meets WCAG AAA static bool meetsAAA(Color foreground, Color background) { return contrastRatio(foreground, background) >= wcagAAA; } /// Check if a color pair meets WCAG AA static bool meetsAA(Color foreground, Color background) { return contrastRatio(foreground, background) >= wcagAA; } }