Workshop Kit · widget reference

Shipped

tab-flip

A WAI-ARIA tabs widget for inline rhetorical comparisons. The Method uses it for "good copy vs bad copy" side-by-sides — three response styles to a bad review, four versions of a one-promise sentence. Viewer-only: the operator reads, picks, learns. No state writes to MuntinContext.

Live demo

Tabs render in the order specified; the defaultIndex determines which one opens first. Click a tab, use Left/Right arrow keys to preview labels, press Enter or Space to switch the panel. Manual-activation focus mode means a screen-reader user can walk the labels without committing to a panel change.

Three response styles · defaultIndex: 2 (the "good" tab opens first)

No MuntinContext writes — tab-flip is a viewer-only widget. The operator's choice doesn't persist; reading-and-comparing IS the learning.

Contract

The widget reads its tabs from an inline JSON config block. The lesson author authors the tab labels + panel HTML; both fields take optional -es variants for the Spanish copy. No data attributes beyond data-widget.

Attribute / configTypePurpose
data-widgetRequired literal Always "tab-flip".
<script class="tab-flip-config" type="application/json">Required JSON object { tabs: [...], defaultIndex: 0 }. Each tab is { id, label, label-es, body, body-es }. id is the stable identifier (used for the panel's aria-controls); label renders on the tab trigger; body is the HTML rendered inside the panel.
tabs[*].body HTML string Rendered as-is into the panel — lesson authors control the safety of this content (these are lesson assets, not operator input). Keep it small; prefer single-paragraph examples with optional <blockquote> for the contrast subject and <p class="meta"> for the rhetorical takeaway.
defaultIndex integer Zero-based tab index that opens first. Use the "good example" position when the lesson is teaching the right answer; use the "bad example" position when the lesson is teaching the failure mode first.

What it writes: nothing. tab-flip is viewer-only; contextKeys is the empty array.

Markup

<section class="course-widget" data-widget="tab-flip"> <script type="application/json" class="tab-flip-config"> { "tabs": [ {"id":"bad", "label":"Vague", "label-es":"Vago", "body":"<p>Great food, great vibes</p>", "body-es":"<p>Buena comida, buen ambiente</p>"}, {"id":"good", "label":"Specific", "label-es":"Específico", "body":"<p>The Tuesday-night taqueria your block tells other blocks about.</p>", "body-es":"<p>La taquería de los martes que tu cuadra recomienda a otras cuadras.</p>"} ], "defaultIndex": 1 } </script> </section>

Accessibility

Full WAI-ARIA Tabs Pattern implementation:

Where it ships

Source

/tools/_shared/workshop/tab-flip.js — ~180 LOC. Exports { tag, contextKeys, mount, serialize } per the Workshop Kit widget contract. Parses the inline JSON config, renders the tab strip + panels, wires the WAI-ARIA Tabs Pattern keyboard model (arrows for focus, Enter/Space to activate, Home/End for edges).

contextKeys is the empty array — tab-flip never writes to MuntinContext. The operator's tab choice is ephemeral (resets on reload). That's deliberate: the learning IS the comparison, not the choice.

← Back to the Workshop Kit