Duncan Faulkner @duncanfaulkner.bsky.social · 21hThe rule → ngbracket.com/tools/a11y-...ngbracket.comngbr/modal-focus-not-contained — @ngbracket/a11y-devtools docsA modal dialog that doesn't keep keyboard focus inside it. What the finding means and how to fix it. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 21haria-modal="true" is a promise, not a behaviour. It hides the page from screen readers but doesn't stop Tab walking out of the dialog. Fix: native <dialog> + showModal(), inert on the background, or a real focus trap. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 08/10/2026Keyboard & focus docs → ngbracket.com/tools/a11y-...ngbracket.comKeyboard & Focus Mode — @ngbracket/a11y-devtools docsCheck keyboard operability — the part axe can't test — with tab-order visualisation, keyboard findings, an accessibility-tree preview, and keyboard-trap detection. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 08/10/2026axe checks markup; the worst keyboard bugs are behaviour. Our Angular devtool draws the tab order on the page, shows the focused control's computed role and name as you Tab, and runs 4 keyboard checks of its own. Heuristics, labelled as such. 110
Duncan Faulkner @duncanfaulkner.bsky.social · 08/10/2026The rule, with its exact thresholds → ngbracket.com/tools/a11y-...ngbracket.comngbr/tab-order-mismatch — @ngbracket/a11y-devtools docsTab moves against the visual reading order. What the finding means, how it's detected, and how to fix it. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 08/10/2026Press Tab. If focus jumps back up the page, your tab order disagrees with your layout. Usual causes: a positive tabindex, or CSS order / row-reverse / grid moving things visually but not in the DOM. Fix the DOM order, not the tabindex. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 07/10/2026How attribution works → ngbracket.com/tools/a11y-...ngbracket.comHow attribution works — @ngbracket/a11y-devtools docsHow a DOM element flagged by axe is traced back to the Angular component that rendered it. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 07/10/2026Most a11y scanners tell you where a problem is. Ours says whose it is. Instead of .card__head > h3 you get "NgbrCard — heading-order". On our own apps that was the Card hard-coding an h3, breaking every screen that used it. Fix it once, not per page. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 07/10/2026The rule, and when it's not a problem → ngbracket.com/tools/a11y-...ngbracket.comngbr/click-without-key — @ngbracket/a11y-devtools docsA control you can Tab to but may not be able to activate with the keyboard. What the finding means and how to fix it. 010
Duncan Faulkner @duncanfaulkner.bsky.social · 07/10/2026Tab reaches it. Enter does nothing. A (click) on a non-native element with tabindex="0": browsers only turn Enter/Space into click on real buttons and links. Fix: make it a <button>, or handle keydown for Enter and Space. 110
Duncan Faulkner @duncanfaulkner.bsky.social · 06/10/2026Live demo → a11y-demo.ngbracket.com/a11y-demo 000
Duncan Faulkner @duncanfaulkner.bsky.social · 06/10/2026You can now try our Angular a11y devtool in the browser, no install. A live admin app with a broken page and the overlay on: each finding labelled with the component that rendered it, tab-order badges as you press Tab, our own keyboard checks. Open source, dev-only. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 06/10/2026The rule, with what triggers it and how to check it yourself → ngbracket.com/tools/a11y-...ngbracket.comngbr/unreachable-control — @ngbracket/a11y-devtools docsA control that the keyboard can't reach. What the finding means, who it affects, how to fix and check it. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 06/10/2026A div with role="button" sounds like a button to a screen reader, and Tab skips straight past it. Most common keyboard bug in Angular templates: (click) on a div/span, or a role with no tabindex. Mouse users never notice. Fix: a real <button>. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 05/10/20263 packs free to start → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 05/10/202619+ accessible Angular packs, built in public. What worked: • a11y as a release gate, not a phase • one system, not a bag of widgets • evidence over adjectives (axe + 38 aria-tree snapshots + real NVDA in CI + public conformance report) What I'd redo: never make a claim you can't back. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 05/10/2026Recipe → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 05/10/2026Toasts are the component that looks accessible and says nothing. Right way: a live region already in the DOM (polite; assertive only for errors), render the text INTO it, don't auto-dismiss critical ones (WCAG 2.2.1), give a close button, don't trap focus. "Saved ✓" nobody hears isn't feedback. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 02/10/2026More WCAG wins → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 02/10/2026WCAG 2.2 rule drag-and-drop UIs miss: Dragging Movements (2.5.7, AA). Any drag function needs a single-pointer alternative that isn't a drag — up/down buttons, a "move to…" menu, tap-then-tap. Kanban, sortable lists, sliders. Drag is an enhancement, never the only way. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 01/10/2026Ours, criterion by criterion → ngbracket.com/accessibili...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 01/10/2026Buying an Angular component library you'll defend in an audit? Ask for, in writing: • a published VPAT/ACR (not "on request") • evidence beyond axe (~a third of WCAG) • keyboard as a release gate • contrast in *both* themes • honest caveats • a named WCAG version + level 100
Duncan Faulkner @duncanfaulkner.bsky.social · 01/10/2026More patterns → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 01/10/2026Still writing ngOnDestroy + a Subject to unsubscribe? Modern Angular deleted that. obs$.pipe(takeUntilDestroyed()).subscribe() — auto-unsubscribes on destroy. Outside an injection context, pass inject(DestroyRef). DestroyRef.onDestroy(fn) covers imperative cleanup too. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 30/09/2026One attribute, floor enforced → docs.ngbracket.com/components/... 000
Duncan Faulkner @duncanfaulkner.bsky.social · 30/09/2026"Can we make it more compact?" is the most common way teams accidentally fail a11y — tighten padding and icon buttons/row actions drop below WCAG 2.5.8's 24×24px target size. NgBracket's density scale tightens spacing but holds a hard ≥24px floor. Dense layout, no motor-a11y regression. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 30/09/2026Recipe → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 010
Duncan Faulkner @duncanfaulkner.bsky.social · 30/09/2026A real ARIA menu is stricter than a `<ul>` of links: role=menu/menuitem, ONE tab stop, ↑↓ move (roving), Enter/Space activate, Esc closes + returns focus, trigger has aria-haspopup/expanded, typeahead. A nav bar of links is NOT a menu — that's `<nav>` + a list. 110
Reposted by Duncan FaulknerAngular @angular.dev · 29/09/2026Want your UI to be beautiful with accessibility built-in? ✨ Discover how Google Antigravity simplifies creating complex styles with Angular Aria tabs. What’s your biggest accessibility challenge right now? Drop it below👇 Read more: g.dev/ai/customizi...g.devCustomizing Angular Aria Tabs Quickly with Google Antigravity CLIThe Angular team has called Angular Aria a set of customizable, fully accessible UI patterns. This is a strong claim, but in practice, how... 122
Duncan Faulkner @duncanfaulkner.bsky.social · 30/09/2026Announcements that coincide with a focus move. When one action moves focus and posts a live message ("Email added"), NVDA speaks one or the other. Delaying the message made it never spoken. What worked: put it in what focus reads (the new row's name). Caught by running real NVDA in CI. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 29/09/2026Turn your sound on and try it → docs.ngbracket.com/components/... 000
Duncan Faulkner @duncanfaulkner.bsky.social · 29/09/2026Charts are where accessibility usually dies — most dataviz libs give a screen-reader user a data table at best. NgBracket Charts: arrow-key point to point, each value announced, and turn on sonification to hear every point as a tone (pitch tracks the value). Hear the *shape* of your data. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 29/09/2026More WCAG wins → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 29/09/2026The a11y bug in almost every app: status messages nobody hears (WCAG 4.1.3, AA). "Saved", "3 results", a form error — if it appears without moving focus it needs a live region: polite for most, assertive for errors, present BEFORE you write to it. Toasts, validation, filter counts all count. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 28/09/2026Our other two-thirds → ngbracket.com/accessibili...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 28/09/2026Axe catches ~a third of WCAG. What a green run happily ships: a modal that never moves focus into it, "tabs" arrow keys can't traverse, drag-only reorder with no keyboard path, name ≠ visible label. Roles present, behaviour absent. "We pass axe" is a third of the story. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 28/09/2026More patterns → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 28/09/2026`computed()` is the most under-used signal primitive. total = computed(() => items().reduce((n,i) => n+i.price, 0)) Lazy, memoised, glitch-free. If a value is *derived*, it's a computed — not an effect that writes a signal. Deriving → computed; reacting (DOM/log/sync) → effect. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 25/09/2026Recipe → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 25/09/2026The modal is where focus management dies. A div overlay with a close button isn't a dialog. Real one: role=dialog + aria-modal, labelled by its title, focus moves IN on open, trapped (Tab cycles inside), Esc closes, focus RETURNS to the trigger (most-skipped). Bonus: background inert. 101
Duncan Faulkner @duncanfaulkner.bsky.social · 24/09/2026We check 304 theme × state combos in CI, both themes → ngbracket.com/accessibili...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 24/09/2026Ship a dark theme and you've doubled your accessibility surface. Contrast has to hold in *every* theme — a palette that's spotless in light can fail in dark (secondary text, disabled states, focus rings, chart series). One axe run on light mode says nothing about dark. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 24/09/2026More WCAG wins → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 010
Duncan Faulkner @duncanfaulkner.bsky.social · 24/09/2026WCAG 2.2 rule sticky headers break: Focus Not Obscured (2.4.11, AA). A focused element must not be *fully* hidden by other content — sticky nav, cookie bar, chat widget. Fix: scroll-margin-top on focusable targets so scroll-into-view clears the sticky bar. Tab through your app and watch. 110
Duncan Faulkner @duncanfaulkner.bsky.social · 23/09/2026a11y as the default → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 23/09/2026Accessibility is cheap as a default, expensive as a retrofit. A component library is a multiplier: one inaccessible modal costs every screen that imported it, not one fix. Build it in at the component layer and every screen inherits it — that's the whole ROI argument. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 23/09/2026More patterns → ngbracket.com/?utm_source...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 23/09/2026Angular inputs are signals now, and `model()` is the two-way you faked with @Input+@Output. value = model('') // [(value)] disabled = input(false) label = input.required<string>() input() is a read-only signal that drops into computed()/effect(); model() adds .set() + auto valueChange. 100
Duncan Faulkner @duncanfaulkner.bsky.social · 22/09/2026How we ship auth → ngbracket.com/accessibili...ngbracket.comNgBracket — Accessible Angular component packsAccessible-by-default Angular component packs — WCAG AA and AXE-passing from the start. Buy a pack or subscribe. 000
Duncan Faulkner @duncanfaulkner.bsky.social · 22/09/2026Your login might be a WCAG 2.2 fail — Accessible Authentication (3.3.8, AA): don't *require* a cognitive test to log in. Let people paste into password + OTP fields (it breaks managers), don't make a puzzle CAPTCHA the only path, support passkeys. "Hardening" the form is how most fail it. 100