Skip to content
Chapter 27Lesson 6

Quiz - shadcn-ui and the accessibility baseline

Quiz progress

0 / 0

Your product’s primary button needs a brand-coloured variant="brand" you’ll reuse on dozens of screens, and your Select needs a custom anchor-positioning behaviour its current API can’t express at all. Where does each change belong?

The brand button is a new cva variant added to the existing button.tsx; the Select is the one that justifies a fork — edit select.tsx in components/ui/ directly and comment why.

Both are forks — once you’re editing shadcn output at all, you’ve left the upgrade path, so fork both files in components/ui/ and move on.

Both are className overrides at the call site — cn() puts your classes last, so you never need to touch the component files for either one.

A touch-primary size="icon" button on a mobile screen holds a 16px Lucide glyph and fails the WCAG 2.2 target-size floor. What’s the correct fix?

Grow the hit area with padding to at least 44×44 (min-h-11 min-w-11) and leave the 16px glyph as-is — visual size and tappable area are different things.

Scale the icon itself up to 44px so the whole target reaches the size floor.

Add aria-label to the button — the target-size rule is satisfied once a screen reader can announce the control.

You write {error && <div role="alert">{error}</div>}. Sighted users see the error appear, but screen-reader users hear nothing. Why, and what’s the fix?

The region only enters the DOM with its text already inside, so assistive tech never registered it to watch. Render <div role="alert">{error}</div> unconditionally and toggle only its contents.

role="alert" is polite by default and waits for the user to be idle; switch it to aria-live="assertive" so it announces immediately.

A role="alert" region must also carry aria-atomic="true" to announce; add that attribute and the existing conditional render will announce correctly.

A RouteFocus component focuses the new page’s <h1> on every navigation, because Next.js moves focus nowhere on a soft navigation. Which details are load-bearing for it to actually work? Select all that apply.

The heading carries tabindex="-1" so a non-interactive <h1> is a legal focus target without entering anyone’s Tab order.

The focus call uses { preventScroll: true } so moving focus doesn’t fight the framework’s scroll restoration.

The heading is given a positive tabindex so it sits first in the page’s focus order.

The component focuses a ref captured from the previous route, reused across navigations.

Why is modelling a data panel with three booleans (isLoading, error, data) considered the canonical bug, versus a single status discriminated union?

Three booleans can express eight combinations for four real states — the extra four (like loading and errored and full) are impossible states the booleans permit; the union makes only the four valid shapes writable.

Booleans render more slowly than a switch over a union, so the union is the performance fix.

The union is only a style preference — both express the same set of states, so the choice doesn’t change which states are reachable.

Quiz complete

Score by topic