|
| 1 | +--- |
| 2 | +name: accessible-web-components |
| 3 | +description: 'Build web UIs with self-contained accessible web components that encapsulate semantic HTML, ARIA, keyboard operability, and WCAG compliance — closing the gaps that HTML5 leaves open. Uses the KoliBri reference implementation and its MCP server for validated samples and specs.' |
| 4 | +--- |
| 5 | + |
| 6 | +# Accessible Web Components |
| 7 | + |
| 8 | +HTML5 provides semantic elements, but leaves critical accessibility gaps: buttons without enforced labels, inputs without associated error handling, navigation without keyboard conventions, modals without focus management. These gaps produce the same accessibility failures across every project. |
| 9 | + |
| 10 | +Accessible web components close these gaps by encapsulating correct semantics, ARIA, keyboard behavior, and assistive technology support inside reusable custom elements. Developers get accessible UI by default — without needing to be accessibility experts. |
| 11 | + |
| 12 | +**KoliBri** (Public UI) is the reference implementation of this concept: a library of self-contained accessible HTML web components, built on Web Component standards, framework-agnostic, and designed as what an "accessible HTML standard" should look like. |
| 13 | + |
| 14 | +## Why This Matters: A Sustainable Contribution to the Web |
| 15 | + |
| 16 | +Accessible web components are not a workaround — they are the natural evolution of the HTML standard. Four properties make this approach sustainable: |
| 17 | + |
| 18 | +**1. Standards-based longevity** |
| 19 | +Web Components are part of the web platform itself. They require no additional framework, no build-time transpilation, and no runtime abstraction. Standards change slowly; components built on them outlast framework generations. |
| 20 | + |
| 21 | +**2. Framework independence** |
| 22 | +The same accessible component works in React, Angular, Vue, Svelte, Solid, and plain HTML. A form input that enforces labels and associates errors does so regardless of the host framework. Accessibility is not re-implemented per stack — it is packaged once and reused everywhere. |
| 23 | + |
| 24 | +**3. Shadow DOM encapsulation** |
| 25 | +The Shadow DOM isolates component markup and styles from the host page. External CSS cannot accidentally override focus indicators or contrast. The accessibility contract the component makes is structurally protected. |
| 26 | + |
| 27 | +**4. Accessibility as architecture, not afterthought** |
| 28 | +The component API enforces accessibility at design time: `_label` is required, `_error` is built in, keyboard behavior follows WAI patterns by default. Developers cannot accidentally ship an unlabelled input or a modal without focus management — the component prevents it. |
| 29 | + |
| 30 | +This is how HTML5 gaps become a solved problem across an entire ecosystem: not by documentation or guidelines, but by making the accessible path the only path. |
| 31 | + |
| 32 | +## When to Use This Skill |
| 33 | + |
| 34 | +- Building web UIs where accessibility is a requirement (public sector, enterprise, inclusive design) |
| 35 | +- Replacing hand-written HTML/ARIA patterns with self-contained accessible components |
| 36 | +- Integrating accessible components into any framework (React, Angular, Vue, Vanilla JS) |
| 37 | +- Reviewing code for accessibility gaps that accessible components could close |
| 38 | +- Understanding what "accessibility by default" means at the component level |
| 39 | + |
| 40 | +## The Core Idea: Accessibility as Component Contract |
| 41 | + |
| 42 | +Traditional HTML leaves accessibility as the developer's responsibility. Accessible web components invert this: |
| 43 | + |
| 44 | +| Traditional HTML | Accessible Web Component | |
| 45 | +|-----------------|-------------------------| |
| 46 | +| `<button>` — label is optional | `<kol-button _label="...">` — label is required | |
| 47 | +| `<input>` — error must be wired manually via `aria-describedby` | `<kol-input-text _label="..." _error="...">` — error is built in | |
| 48 | +| `<div class="modal">` — focus trap is DIY | `<kol-modal>` — focus trap, ESC close, focus return are built in | |
| 49 | +| `<nav>` — keyboard model is undefined | `<kol-nav>` — Tab/Arrow/Enter behavior follows WAI patterns | |
| 50 | +| No skip links by default | `<kol-skip-nav>` — skip navigation built in | |
| 51 | + |
| 52 | +The component enforces the contract. The developer provides content. Accessibility is not optional. |
| 53 | + |
| 54 | +## MCP-First Workflow |
| 55 | + |
| 56 | +Use the KoliBri MCP server to retrieve validated component APIs, samples, and integration scenarios. Never guess component properties — look them up. |
| 57 | + |
| 58 | +### Search |
| 59 | + |
| 60 | +Use `#tool:mcp_kolibri-mcp_search` to find entries: |
| 61 | +- `kind: spec` — component API (properties, events, slots) — **check first** |
| 62 | +- `kind: sample` — verified usage examples |
| 63 | +- `kind: scenario` — complex integration patterns (e.g. form validation with error summary) |
| 64 | +- `kind: doc` — supplementary documentation |
| 65 | + |
| 66 | +### Fetch |
| 67 | + |
| 68 | +Use `#tool:mcp_kolibri-mcp_fetch` with the `id` from search results (e.g. `spec/button`, `sample/form/basic`, `scenario/scenarios/sample-form-with-validation`). |
| 69 | + |
| 70 | +### Transparency |
| 71 | + |
| 72 | +- Cite MCP IDs for every verified fact |
| 73 | +- Mark inferences as your own recommendation |
| 74 | +- If no MCP entry exists, state the gap and fall back to HTML5/WCAG standards |
| 75 | + |
| 76 | +## Accessible Component Patterns |
| 77 | + |
| 78 | +### Registration |
| 79 | + |
| 80 | +Accessible web components must be registered before use. The bootstrap configures which theme (visual design) is applied: |
| 81 | + |
| 82 | +```ts |
| 83 | +import { register } from '@public-ui/components'; |
| 84 | +import { defineCustomElements } from '@public-ui/components/dist/loader'; |
| 85 | +import { DEFAULT } from '@public-ui/themes'; |
| 86 | + |
| 87 | +register(DEFAULT, defineCustomElements); |
| 88 | +``` |
| 89 | + |
| 90 | +### Accessible Forms |
| 91 | + |
| 92 | +Forms are where most accessibility failures occur. Accessible form components enforce: |
| 93 | +- Every input has a programmatically associated label (required `_label` prop) |
| 94 | +- Error messages are linked to their input automatically |
| 95 | +- Error summaries are keyboard-navigable with links that focus the offending field |
| 96 | + |
| 97 | +Pattern: Wrap inputs in `<KolForm>`, collect errors, show them in `<KolAlert _type="error">` with `<KolLink>` elements that focus fields on click. Reference `scenario/scenarios/sample-form-with-validation` for the complete pattern. |
| 98 | + |
| 99 | +### Keyboard Conventions |
| 100 | + |
| 101 | +Accessible components follow established WAI keyboard patterns: |
| 102 | +- **Buttons**: Space/Enter to activate |
| 103 | +- **Tabs**: Arrow keys to switch, Tab to enter content |
| 104 | +- **Modals**: Focus trapped, ESC to close, focus returns to trigger |
| 105 | +- **Navigation**: Arrow keys for items, Enter to activate, expandable sub-menus |
| 106 | +- **Skip Navigation**: Visible on Tab, jumps to landmarks |
| 107 | + |
| 108 | +### The Expert Slot Escape Hatch |
| 109 | + |
| 110 | +When the component API cannot express required semantics, the Expert Slot allows injecting custom HTML. This explicitly transfers accessibility responsibility back to the developer. Only recommend this when: |
| 111 | +1. Standard properties cannot express the intent |
| 112 | +2. You document the accessibility risk and required manual verification |
| 113 | + |
| 114 | +## Anti-patterns |
| 115 | + |
| 116 | +Avoid these common KoliBri mistakes: |
| 117 | + |
| 118 | +- ❌ Add `aria-label` or `aria-labelledby` manually to a KoliBri component — labels are enforced by `_label`; redundant ARIA creates conflicts |
| 119 | +- ❌ Use native `<button>`, `<input>`, `<select>` when a `Kol*` component exists — bypasses the accessibility contract entirely |
| 120 | +- ❌ Use the Expert Slot without documenting the accessibility risk and required manual verification |
| 121 | +- ❌ Invent or guess component properties — always verify via MCP `spec/` entries |
| 122 | +- ❌ Render `KolIcon` without confirming icon font assets are bundled — missing assets fail silently |
| 123 | +- ❌ Use `!important` in theme overrides — it silently breaks the base accessibility layer |
| 124 | +- ❌ Call `register()` after rendering `<kol-*>` elements — registration must complete before any component renders |
| 125 | + |
| 126 | +## Accessibility Baseline |
| 127 | + |
| 128 | +General WCAG 2.2 AA rules (semantics, keyboard, contrast, reflow, labels, forms, forced colors) are defined in `instructions/a11y.instructions.md` and apply automatically. Do not duplicate those rules here — they are the baseline for every recommendation. |
| 129 | + |
| 130 | +This skill adds KoliBri-specific accessibility knowledge on top of that baseline: |
| 131 | + |
| 132 | +- Accessible components handle ARIA internally — do not add redundant ARIA attributes |
| 133 | +- KoliBri provides `color-contrast-analysis=true` meta tag for built-in contrast checking |
| 134 | +- KoliBri base layer sets `hyphens: auto` and `word-break: break-word` for reflow by default |
| 135 | + |
| 136 | +## Theming: Separating Accessibility from Design |
| 137 | + |
| 138 | +Accessible components separate structure (accessibility contract) from design (visual appearance). This is a core architectural principle: |
| 139 | + |
| 140 | +- **Structure layer**: semantic HTML, ARIA, keyboard behavior — owned by the component |
| 141 | +- **Design layer**: colors, spacing, typography — owned by the theme |
| 142 | +- Themes are interchangeable NPM packages |
| 143 | +- Custom themes must be independently tested for contrast and focus visibility |
| 144 | +- Use well-prefixed CSS custom properties (`--kolibri-*`) for external tokens |
| 145 | +- Never use `!important` — it overrides the base accessibility layer |
| 146 | + |
| 147 | +## Component Reference |
| 148 | + |
| 149 | +| Component | Accessibility Gap It Closes | Key Props | |
| 150 | +|-----------|-----------------------------|-----------| |
| 151 | +| `KolButton` | Enforces label on buttons | `_label`, `_variant`, `_type`, `_on` | |
| 152 | +| `KolLink` / `KolLinkButton` | Semantic link vs. button distinction | `_label`, `_href`, `_on` | |
| 153 | +| `KolInputText` | Label + error association | `_label`, `_required`, `_error` | |
| 154 | +| `KolInputEmail` | Type-specific validation + label | `_label`, `_required` | |
| 155 | +| `KolInputPassword` | Accessible password field | `_label`, `_required` | |
| 156 | +| `KolInputCheckbox` | Label association for checkboxes | `_label`, `_checked` | |
| 157 | +| `KolInputRadio` | Grouped radio with keyboard model | `_label`, `_options` | |
| 158 | +| `KolSelect` | Accessible select with label | `_label`, `_options` | |
| 159 | +| `KolSingleSelect` | Single value selection | `_label`, `_options` | |
| 160 | +| `KolTextarea` | Multi-line with label | `_label`, `_rows` | |
| 161 | +| `KolForm` | Form wrapper with error summary support | `_on`, `_errorList` | |
| 162 | +| `KolAlert` | Accessible notifications | `_type`, `_label`, `_variant`, `_alert` | |
| 163 | +| `KolModal` | Focus trap, ESC close, focus return | `_label`, `_activeElement` | |
| 164 | +| `KolNav` | Keyboard-operable navigation | `_label`, `_links` | |
| 165 | +| `KolTabs` | WAI tab pattern (arrow keys) | `_label`, `_tabs`, `_selected` | |
| 166 | +| `KolSkipNav` | Bypass repeated content | `_label`, `_links` | |
| 167 | +| `KolTable` | Accessible data table with headers | `_label`, `_headers`, `_data` | |
| 168 | +| `KolHeading` | Enforced heading level | `_label`, `_level` | |
| 169 | +| `KolIcon` | Icon with required font assets | `_icons` | |
| 170 | + |
| 171 | +> **Icon assets**: `KolIcon` requires bundled icon fonts (e.g. KolIcons). Missing assets silently break rendering. |
| 172 | +
|
| 173 | +> **Combobox**: `KolCombobox` may be in preview — check maturity. Use `KolSelect` or `KolSingleSelect` as stable alternatives. |
| 174 | +
|
| 175 | +> **Tooltip**: `KolTooltip` is for internal library use. For help text, use inline hints or `aria-describedby`. |
0 commit comments