skills/accessibility/SKILL.md
Production-grade Flutter accessibility mastery - Semantics API, screen readers (VoiceOver/TalkBack), WCAG 2.1 AA/AAA compliance, inclusive design patterns, automated a11y testing with comprehensive code examples
npx skillsauth add pluginagentmarketplace/custom-plugin-flutter custom-plugin-flutter-skill-accessibilityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
class AccessibleProductCard extends StatelessWidget {
final Product product;
final VoidCallback onTap;
final VoidCallback onAddToCart;
const AccessibleProductCard({
required this.product,
required this.onTap,
required this.onAddToCart,
});
@override
Widget build(BuildContext context) {
return Semantics(
label: '${product.name}, ${product.formattedPrice}',
hint: 'Double tap to view details',
button: true,
enabled: true,
child: Card(
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image with semantic description
Semantics(
image: true,
label: 'Product image: ${product.imageDescription}',
excludeSemantics: true,
child: Image.network(product.imageUrl, height: 120),
),
const SizedBox(height: 12),
Text(product.name),
Text(product.formattedPrice),
const SizedBox(height: 12),
// 48dp minimum touch target
ElevatedButton.icon(
onPressed: onAddToCart,
icon: const Icon(Icons.add_shopping_cart),
label: const Text('Add to Cart'),
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 48),
),
),
],
),
),
),
),
);
}
}
Semantics(
// Identity
label: 'Submit button', // What this element IS
value: '5 items', // Current value
hint: 'Double tap to submit', // How to interact
// Traits
button: true,
link: false,
header: false,
image: false,
textField: false,
slider: false,
// State
enabled: true,
checked: true, // Checkbox state
selected: false,
toggled: false,
focused: false,
hidden: false,
obscured: false, // Password field
// Actions
onTap: () {},
onLongPress: () {},
onIncrease: () {},
onDecrease: () {},
onDismiss: () {},
// Grouping
container: false,
explicitChildNodes: false,
excludeSemantics: false,
child: MyWidget(),
)
// MergeSemantics - Combine into single node
MergeSemantics(
child: Row(
children: [
Icon(Icons.star, semanticLabel: null),
Text('4.5 stars'),
],
),
)
// Screen reader: "4.5 stars"
// ExcludeSemantics - Remove decorative elements
ExcludeSemantics(
child: DecorativeBackground(),
)
// BlockSemantics - Block underlying content
BlockSemantics(
child: ModalOverlay(),
)
// Custom sort order
Semantics(
sortKey: OrdinalSortKey(1.0),
child: FirstItem(),
)
// Announce changes dynamically
void announceChange(String message) {
SemanticsService.announce(message, TextDirection.ltr);
}
// Live region for dynamic content
Semantics(
liveRegion: true,
child: CountdownTimer(),
)
// Custom actions
Semantics(
customSemanticsActions: {
CustomSemanticsAction(label: 'Mark as favorite'): markFavorite,
CustomSemanticsAction(label: 'Share'): share,
},
child: ItemCard(),
)
class ContrastChecker {
// WCAG AA: 4.5:1 normal, 3:1 large text
// WCAG AAA: 7:1 normal, 4.5:1 large text
static double calculateContrast(Color fg, Color bg) {
final fgL = fg.computeLuminance();
final bgL = bg.computeLuminance();
return (max(fgL, bgL) + 0.05) / (min(fgL, bgL) + 0.05);
}
static bool meetsAA(Color fg, Color bg, {bool largeText = false}) {
return calculateContrast(fg, bg) >= (largeText ? 3.0 : 4.5);
}
}
// Respect system text scale
final textScaler = MediaQuery.textScalerOf(context);
// Check reduced motion preference
final reduceMotion = MediaQuery.disableAnimationsOf(context);
if (reduceMotion) {
return StaticWidget();
}
return AnimatedWidget(duration: Duration(milliseconds: 300));
// Minimum 48x48 dp touch targets
ElevatedButton(
onPressed: () {},
child: Text('Tap'),
style: ElevatedButton.styleFrom(
minimumSize: Size(48, 48),
),
)
// Focus management
Focus(
focusNode: _focusNode,
onKeyEvent: (node, event) {
if (event.logicalKey == LogicalKeyboardKey.enter) {
handleAction();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: MyWidget(),
)
// Custom focus order
FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
children: [
FocusTraversalOrder(order: NumericFocusOrder(1), child: Field1()),
FocusTraversalOrder(order: NumericFocusOrder(2), child: Field2()),
],
),
)
testWidgets('meets accessibility guidelines', (tester) async {
final semanticsHandle = tester.ensureSemantics();
addTearDown(semanticsHandle.dispose);
await tester.pumpWidget(MyApp());
// Check semantic labels
expect(find.bySemanticsLabel('Submit button'), findsOneWidget);
// Check touch target size
final button = tester.getSize(find.byType(ElevatedButton));
expect(button.width, greaterThanOrEqualTo(48));
expect(button.height, greaterThanOrEqualTo(48));
// Verify semantics
expect(tester.getSemantics(find.byType(MyButton)), matchesSemantics(
label: 'Submit',
isButton: true,
isEnabled: true,
hasTapAction: true,
));
});
Issue: Screen reader skips element
1. Check ExcludeSemantics wrapping
2. Verify semantic label exists
3. Check MergeSemantics parent
4. Run: flutter run --show-semantics-debug
Issue: Wrong reading order
1. Add OrdinalSortKey for custom order
2. Use FocusTraversalGroup
3. Check visual layout matches logical order
Issue: Contrast too low
1. Use ContrastChecker utility
2. Test light and dark themes
3. Check disabled state colors
[ ] All interactive elements have labels
[ ] Reading order is logical
[ ] Color contrast >= 4.5:1
[ ] Text scales to 200%
[ ] Touch targets >= 48dp
[ ] Keyboard navigation works
[ ] Focus indicators visible
Build inclusive Flutter apps that everyone can use.
tools
1600+ lines of testing mastery - unit tests, widget tests, integration tests, E2E, coverage, mocking with production-ready code examples.
tools
2300+ lines of state management mastery - all patterns (Provider, Riverpod, BLoC, GetX), dependency injection, persistence, testing with production-ready code examples.
tools
Production-grade Flutter plugin development mastery - Platform channels, federated architecture, MethodChannel/EventChannel, iOS Swift/Android Kotlin integration, pub.dev publishing with comprehensive code examples
tools
1600+ lines of performance optimization mastery - profiling, rendering, memory, network, battery, APK size with production-ready code examples.