agentleFS
gabuldevgabuldev/flutter-skills/skills/flutter-accessibility/SKILL.md

flutter-accessibility

Skill12 starsChanged 55 days agoMITView on GitHub

Use when implementing or auditing accessibility on a Flutter screen — the Semantics widget, screen-reader labels (TalkBack/VoiceOver), colour contrast, minimum tap-target size, system font scaling, or semantics in a web build. Also triggers on "make this accessible", "a11y", or a request for an accessibility review.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

17 rules. About 0 are common, 8 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If no mode specified, default to implement

    line 24
  2. Distinctive:

    Follow WCAG 2.1 minimum contrast ratios:

    line 73
  3. Distinctive:

    Use the Accessibility Scanner on Android (Settings > Accessibility)

    line 83
  4. Distinctive:

    Run Lighthouse audit in Chrome DevTools for the web build

    line 84
  5. Very specific:

    Use online contrast checkers (WebAIM Contrast Checker) with hex values from ds_colors.dart

    line 85
  6. Very specific:

    When fixing, update colors in design_system/ tokens so the fix propagates everywhere.

    line 93
  7. Distinctive:

    All interactive elements must be at least 48x48 logical pixels:

    line 99
  8. Very specific:

    Ensure the HTML <title> tag is descriptive (e.g., "Product Catalog — Acme")

    line 216
  9. Very specific:

    Add lang="pt-BR" to the <html> tag in index.html

    line 217
  10. Very specific:

    Use minHeight / minWidth instead of fixed height / width for containers holding text

    line 242
  11. Distinctive:

    Test with system font scale at 2.0x (Settings > Display > Font size)

    line 243
  12. Very specific:

    Ensure text does not overflow or get clipped — use Flexible, Expanded, or overflow: TextOverflow.ellipsis with maxLines

    line 244
  13. Distinctive:

    Only limit scale factor for truly constrained UI (e.g., small price badges):

    line 249
  14. Very specific:

    Ensure all modals trap focus (Material showDialog and showModalBottomSheet do this automatically).

    line 302
  15. Distinctive:

    Every meaningful image needs descriptive text:

    line 308
  16. Very specific:

    Always use labelText on InputDecoration, not just hintText (hints disappear on focus)

    line 362
  17. Distinctive:

    After form submission with errors, move focus to the first field with an error

    line 364

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

You are implementing or auditing accessibility (a11y) for a Flutter app.

Context

Accessibility pressure differs by surface, and it changes what you prioritise:

  • Customer-facing web — the highest bar. Screen-reader semantics are off by default on Flutter web (see section 5), the audience is unknown, and in many jurisdictions this is a legal requirement, not a nicety.
  • Internal/admin web — keyboard navigation and contrast matter most; staff use these all day.
  • Mobile — platform screen readers (TalkBack/VoiceOver) mostly work if you give widgets labels and keep tap targets large enough.

Choose based on the skill argument, if one was given, or on the screen/task at hand:

  • audit — Review existing code for a11y violations and produce a report with fixes
  • implement — Add accessibility annotations and fixes to existing widgets
  • web-setup — Enable Flutter web semantics and configure for screen readers
  • If no mode specified, default to implement

1. Semantic Annotations

Wrap custom widgets with Semantics to expose meaning to assistive technology:

// Label interactive elements
Semantics(
  label: 'Adicionar ao pedido',
  button: true,
  child: CustomAddButton(...),
)

// Group related elements into a single announcement
MergeSemantics(
  child: Row(
    children: [
      Icon(Icons.star),
      Text('4.5'),
    ],
  ),
)

// Hide decorative elements from screen readers
ExcludeSemantics(
  child: DecorativeBackground(),
)

When to use each

WidgetUse case
Semantics(label:)Custom widgets that lack built-in semantics (raw GestureDetector, Container with onTap)
Semantics(button: true)Custom buttons not using Material ElevatedButton/TextButton
Semantics(header: true)Section headers (category names in a catalog)
Semantics(image: true, label:)Product/content images
MergeSemanticsIcon + text pairs, price + currency pairs
ExcludeSemanticsPurely decorative images, background gradients, dividers

Material widgets (ElevatedButton, TextField, Checkbox) already have built-in semantics. Do NOT double-wrap them unless overriding the default label.


2. Color Contrast

Follow WCAG 2.1 minimum contrast ratios:

ElementMinimum ratio
Normal text (< 18sp)4.5:1
Large text (>= 18sp or >= 14sp bold)3.0:1
UI components & graphical objects3.0:1

Verification

  • Use the Accessibility Scanner on Android (Settings > Accessibility)
  • Run Lighthouse audit in Chrome DevTools for the web build
  • Use online contrast checkers (WebAIM Contrast Checker) with hex values from ds_colors.dart

Violations that show up again and again

  • Light gray text on white backgrounds for secondary info (descriptions, prices in light theme)
  • Disabled button text that falls below 3.0:1
  • Placeholder text in search fields

When fixing, update colors in design_system/ tokens so the fix propagates everywhere.


3. Tap Targets

All interactive elements must be at least 48x48 logical pixels:

// BAD - icon button too small
GestureDetector(
  onTap: onAdd,
  child: Icon(Icons.add, size: 24),
)

// GOOD - ensure minimum hit area
IconButton(
  onPressed: onAdd,
  icon: Icon(Icons.add, size: 24),
  // IconButton already enforces 48x48 by default
)

// GOOD - for custom widgets, use constraints
GestureDetector(
  onTap: onAdd,
  child: ConstrainedBox(
    constraints: const BoxConstraints(minWidth: 48, minHeight: 48),
    child: Center(child: Icon(Icons.add, size: 24)),
  ),
)

Spacing between targets

Adjacent tap targets must have enough spacing to avoid accidental taps. Minimum 8px gap between interactive elements.


4. Screen Reader Support

Labels for images (product photos, avatars, thumbnails)

// Product image in a catalog
Semantics(
  image: true,
  label: 'Foto do produto: ${product.name}',
  child: CachedNetworkImage(
    imageUrl: product.imageUrl,
    // ...
  ),
)

// Decorative images (backgrounds, patterns)
ExcludeSemantics(
  child: Image.asset('assets/background_pattern.png'),
)

Labels for icon buttons

IconButton(
  icon: Icon(Icons.shopping_cart),
  tooltip: 'Ver carrinho', // tooltip doubles as semantic label
  onPressed: () => openCart(),
)

Price announcements

// Announce price clearly
MergeSemantics(
  child: Row(
    children: [
      Text('R\$'),
      Text('29,90'),
    ],
  ),
)
// Screen reader announces: "R$ 29,90"

5. Web Accessibility (any web build)

Flutter web disables semantics by default for performance. For customer-facing apps this is unacceptable.

Enable semantics in web builds

In the app's web/index.html, add before the Flutter loader script:

<script>
  // Enable accessibility for Flutter web
  window.flutterConfiguration = {
    canvasKitBaseUrl: '/canvaskit/',
  };
</script>

In the app's main.dart:

import 'package:flutter/semantics.dart';

void main() {
  // Force enable semantics for web accessibility
  WidgetsFlutterBinding.ensureInitialized();
  SemanticsBinding.instance.ensureSemantics();

  runApp(const MyApp());
}

Alternative: enable via URL parameter

For testing, append ?semantics=true to the web URL to activate the semantics tree without code changes.

Web-specific considerations

  • Ensure the HTML <title> tag is descriptive (e.g., "Product Catalog — Acme")
  • Add lang="pt-BR" to the <html> tag in index.html
  • Flutter web renders to canvas, so ALL accessibility depends on the Semantics tree being correct

6. Font Scaling

Support dynamic text sizing for users who increase system font size:

// BAD - breaks with large fonts
SizedBox(
  height: 48,
  child: Text('Categoria', style: TextStyle(fontSize: 16)),
)

// GOOD - adapt to text scale
ConstrainedBox(
  constraints: const BoxConstraints(minHeight: 48),
  child: Text('Categoria', style: TextStyle(fontSize: 16)),
)

Guidelines

  • Use minHeight / minWidth instead of fixed height / width for containers holding text
  • Test with system font scale at 2.0x (Settings > Display > Font size)
  • Ensure text does not overflow or get clipped — use Flexible, Expanded, or overflow: TextOverflow.ellipsis with maxLines
  • Product names and descriptions must remain readable at large scales

Limiting scale when necessary

Only limit scale factor for truly constrained UI (e.g., small price badges):

MediaQuery.withClampedTextScaling(
  minScaleFactor: 1.0,
  maxScaleFactor: 1.5,
  child: PriceBadge(...),
)

7. Focus Management (Web)

Keyboard navigation is essential for web accessibility:

// Ensure custom interactive widgets can receive focus
Focus(
  child: GestureDetector(
    onTap: onTap,
    child: MenuItemCard(...),
  ),
)

// Set logical focus traversal order
FocusTraversalGroup(
  policy: OrderedTraversalPolicy(),
  child: Column(
    children: [
      FocusTraversalOrder(
        order: NumericFocusOrder(1),
        child: SearchBar(),
      ),
      FocusTraversalOrder(
        order: NumericFocusOrder(2),
        child: CategoryFilter(),
      ),
      FocusTraversalOrder(
        order: NumericFocusOrder(3),
        child: ProductGrid(),
      ),
    ],
  ),
)

Keyboard shortcuts for the web build

  • Tab — move between interactive elements
  • Enter/Space — activate buttons
  • Escape — close modals/bottom sheets

Ensure all modals trap focus (Material showDialog and showModalBottomSheet do this automatically).


8. Accessible Images

Every meaningful image needs descriptive text:

// Product image
Semantics(
  image: true,
  label: product.name, // "Pizza Margherita"
  child: ProductImage(url: product.imageUrl),
)

// Category icon
Semantics(
  image: true,
  label: 'Categoria: ${category.name}',
  child: CategoryIcon(icon: category.icon),
)

// Restaurant logo
Semantics(
  image: true,
  label: 'Logo do restaurante ${restaurant.name}',
  child: RestaurantLogo(url: restaurant.logoUrl),
)

// Decorative divider — exclude
ExcludeSemantics(
  child: Image.asset('assets/divider_ornament.png'),
)

9. Accessible Forms

Forms (search, checkout, sign-up) must announce labels and errors:

TextFormField(
  decoration: InputDecoration(
    labelText: 'Nome completo', // announced by screen reader
    hintText: 'Digite seu nome',
    errorText: hasError ? 'Nome e obrigatorio' : null, // announced on error
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Nome e obrigatorio'; // announced when validation fails
    }
    return null;
  },
)

Guidelines

  • Always use labelText on InputDecoration, not just hintText (hints disappear on focus)
  • Error messages must be descriptive: say what is wrong, not just "campo invalido"
  • After form submission with errors, move focus to the first field with an error
  • Group related fields with MergeSemantics or Semantics(label:) when context is needed

10. Audit Workflow

When mode is audit

  1. Scan widgets for missing Semantics on:
    • Images (Image, CachedNetworkImage, SvgPicture)
    • Custom buttons (GestureDetector, InkWell without Semantics)
    • Icon-only buttons without tooltip
    • Decorative elements that should use ExcludeSemantics
  2. Check tap target sizes — find interactive widgets with constrained sizes below 48x48
  3. Review color usage — cross-reference text colors against background colors in the design system tokens
  4. Check web semantics — verify SemanticsBinding.instance.ensureSemantics() is called in every web build
  5. Check font scaling — find fixed height/width on containers holding text
  6. Produce report in this format:
## Accessibility Audit: [app_name]

### Critical (must fix)
- [ ] Issue description — file:line

### Important (should fix)
- [ ] Issue description — file:line

### Minor (nice to have)
- [ ] Issue description — file:line

Manual testing checklist

PlatformToolHow to test
AndroidTalkBackSettings > Accessibility > TalkBack. Navigate the app with swipe gestures.
iOSVoiceOverSettings > Accessibility > VoiceOver. Swipe to navigate.
macOSVoiceOverCmd+F5. Use arrow keys to navigate web apps in Safari/Chrome.
WebLighthouseChrome DevTools > Lighthouse > Accessibility. Target score >= 90.
Webaxe DevToolsChrome extension. Finds WCAG violations in rendered page.
WebTab navigationPress Tab through the entire checkout/browse flow without using a mouse.

Lighthouse audit for the web build

# Run Lighthouse CI from command line
npx lighthouse http://localhost:PORT --only-categories=accessibility --output=json --output-path=./a11y-report.json

Target: Lighthouse accessibility score >= 90 for the web build.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.