@codecademy/gamut

68.6.068.6.1-alpha.edab62.0
agent-tools/rules/accessibility.mdc
+agent-tools/rules/accessibility.mdcNew file
+69
Index: package/agent-tools/rules/accessibility.mdc
===================================================================
--- package/agent-tools/rules/accessibility.mdc
+++ package/agent-tools/rules/accessibility.mdc
@@ -0,0 +1,69 @@
+---
+description: Apply these guardrails when editing Gamut UI in TS/JS/TSX/JSX. Mirrors the General rules in the `gamut-accessibility` skill; see `skills/gamut-accessibility/SKILL.md` in this plugin for full component patterns and checklists. Loaded automatically for matched files.
+alwaysApply: true
+globs: ["*.tsx", "*.ts", "*.jsx", "*.js"]
+---
+
+# Gamut Accessibility Rules
+
+## Prefer HTML over ARIA
+
+Unnecessary ARIA can cause harm. If a native HTML element or attribute with the semantics and behavior you need already exists, use it. Reach for ARIA only when native HTML is genuinely insufficient for the pattern.
+
+## A Role is a Promise
+
+ARIA roles modify the accessibility tree and _imply_ behavior. Always ensure that the implied keyboard behavior, focusability, and interactivity exists when a role is used.
+
+## ARIA can both cloak and enhance
+
+ARIA can augment native semantics (`aria-pressed` on a `<button>`) or override them entirely (`role="menuitem"` on an `<a>`). Both capabilities are powerful and dangerous. Override only when native HTML genuinely doesn't fit the pattern; when augmenting, don't contradict the native semantics.
+
+
+## Align accessible names with visible copy
+
+Prefer wiring names through visible text and native `<label>` / control text / `alt` over using `aria-label`. Point `aria-labelledby` at the visible heading or label that should define the name if it's not possible to name elements from their content. Use bare `aria-label` when there is no suitable visible label.
+
+## Treat missing visible labels as a design smell
+
+When there is no visible text for a nameable element, consider this a sign that the content design could be improved, but not a requirement that it is changed. This is not an accessibility violation.
+
+```html
+<!-- smell: this list has no conceptual name, so we have to create one using ARIA -->
+<ul aria-label="List heading">
+  <li>...</li>
+</ul>
+
+<!-- better: the list's name is visible and can be used for its accessible name -->
+<h2 id="list-name">List heading</h2>
+<ul aria-labelledby="list-name">
+  <li>...</li>
+</ul>
+```
+
+## Use Gamut components — don't reimplement what they already solve
+
+- `<Button>` not `<div onClick>` or `<span role="button">`
+- `<Tabs>` / `<Tab>` / `<TabList>` / `<TabPanel>` — arrow key, Home, End navigation is automatic
+- `<Dialog>` / `<Modal>` — always provide an accessible name; **prefer `aria-labelledby`** to a visible title when one exists, otherwise `aria-label`. Focus lock and Escape are handled by the components.
+
+## Every interactive control needs an accessible name
+
+- **`<IconButton>`** — provide `tip` (becomes the accessible name for icon-only)
+- **`<InfoTip>`** — provide `ariaLabel` or `ariaLabelledby`; there is no automatic fallback
+- Icon SVGs next to visible text — add `aria-hidden="true"` to decorative icons
+
+## Form label association
+
+Match `htmlFor` on `<FormGroupLabel>` with the `id` on the input. `<FormGroup>` auto-wires `aria-live` for `error` and `description` — do not add redundant live regions manually.
+
+## Screen-reader-only text
+
+Use `<Text screenreader>` for visually hidden but announced content. `<HiddenText>` is deprecated.
+
+## Color and contrast
+
+Do not hardcode hex values for adaptive UI. Gamut semantic color tokens through `ColorMode` are built for WCAG AA; see the `gamut-color-mode` skill for token usage. For non-text contrast, focus order, and ARIA beyond color, see `gamut-accessibility`.
+
+## Focus visibility
+
+Never suppress focus indicators with `outline: none` or `outline: 0` without a visible replacement. Gamut's focus styles are intentional and meet WCAG 2.4.7.