Skip to content
Chapter 21Lesson 8

Quiz - The visual surface

Quiz progress

0 / 0

A card title sits in a flex row beside a price, wrapped in truncate so a long title clips to one line with an ellipsis. But long titles refuse to clip — instead they push the price off the edge of the card. What’s the missing piece?

Add min-w-0 to the title — a flex item defaults to min-width: auto, so it won’t shrink below its content and truncate can’t take effect.
Add whitespace-nowrap to the title — truncate doesn’t keep the text on one line by itself.
Replace truncate with line-clamp-1truncate doesn’t work on flex items.

You’re picking the modern text-wrap reflex for two surfaces: a three-line <h2> and a long body paragraph. Which assignment is right?

text-balance on the heading, text-pretty on the paragraph.
text-pretty on the heading, text-balance on the paragraph.
text-balance on both — it’s the general-purpose orphan fix.

You bump a brand token from oklch(0.6 0.18 255) to oklch(0.7 0.18 255) — only the L channel moved. What does the swatch do?

Brightens while staying exactly as blue and as saturated as before.
Brightens, but also drifts a little toward purple.
Brightens, and visibly washes out toward grey-blue.

You’re building a full-page dialog backdrop: a dark dimming layer with a crisp, fully-opaque “Saving…” message centered on top. Which utility belongs on the backdrop?

bg-black/50 — per-property alpha fades only the background color, so the text on top stays solid.
opacity-50 — it makes the backdrop half-transparent in one class.
bg-black plus opacity-50 — combine a solid color with a fade for a reliable dim.

You draw a keyboard focus indicator with a 2px border that only appears on :focus. Tabbing through a form, the whole layout twitches at every step. Why — and what should you have used?

A border occupies layout space, so adding it on focus grows the element 2px per side and reflows its neighbors; use outline (or ring-*), which paints on top of the layout and takes no space.
The border color can’t read a theme token, so the browser recomputes it each frame; use ring-*, which is themable.
:focus fires too often; switching to :focus-visible would stop the layout from shifting.

An option card keeps const [isChecked, setIsChecked] = useState(false), flipped by an onChange on the radio it wraps, read by one conditional class that adds a border when selected. What single change deletes all of that state?

Put has-[:checked]:border-primary on the card — it reads the :checked of the radio it contains, so the state, the handler, and the conditional all come out.
Use peer-checked:border-primary from the sibling before the radio.
Keep the onChange, but have it set a data-checked attribute and style with data-[checked]:.

Why is focus-visible:ring-2 the reflex on interactive elements rather than focus:ring-2?

:focus fires on mouse clicks too, so a focus: ring flashes on every click; :focus-visible uses the browser’s heuristic to show the ring for keyboard/programmatic focus and hide it on a plain click.
:focus-visible is the only one that works through a theme token, so focus: rings break in dark mode.
:focus doesn’t fire for keyboard users at all, so it leaves them with no indicator.

For motion that holds a smooth 60fps even on a low-end phone, which properties are the cheap, composite-only lane to animate? Select all that apply.

transform
opacity
height
background-color

A dialog’s content carries data-[state=closed]:animate-out fade-out-0, but when you flip your own isOpen boolean to false the dialog vanishes instantly — not one frame of the exit plays. What’s the 2026 fix?

Stop unmounting on the boolean — let the dialog primitive flip data-state to closed, hold the element in the DOM while the exit plays, and unmount only after it finishes.
Wrap the unmount in a setTimeout whose delay matches duration-200 so the element survives long enough.
Move the exit animation onto the overlay instead of the content.

In <nav className="flex flex-col md:flex-row">, what is md:flex-row doing below the 768px breakpoint?

Nothing — below md the rule isn’t in play, so the base flex-col is the only rule in effect; mobile-first prefixes only add from their width up.
Unsetting flex-col so the nav has no direction until the breakpoint activates a row.
Targeting tablets specifically, since md is the tablet device bucket.

A card’s “edit” button is styled opacity-0 hover:opacity-100 — invisible until you hover the card. It works on desktop. What happens on a phone, and why?

The button stays invisible and unreachable — Tailwind v4 wraps hover: in @media (hover: hover), so on a touch device the rule never fires and the button stays at opacity-0.
It works fine — Tailwind v4 falls back to firing hover: on tap for touch devices.
It flickers visible on first tap and then sticks — the classic sticky-hover bug.

Sort each layout change by whether it should respond to the screen (md:) or to the box the element sits in (@md:). Select every item that belongs to the container query group.

A <ProductCard> reused in both the narrow sidebar and the wide feed.
A stat tile whose inner layout changes when the dashboard grid drops to one column.
The top app navigation switching from a hamburger to a full bar.
The page splitting from one column to two on desktop.

You add @md:flex-row to a card’s inner layout to make it go side-by-side in wide slots, but it stays stacked at every width — no error in the console. What’s the most likely cause?

No ancestor declares container-type — without @container somewhere up the tree, @md: variants silently never match.
@md fires at 768px like the viewport md, and the card’s slot never gets that wide.
@md:flex-row must go on the same element as @container; you split them across two elements.

Quiz complete

Score by topic