;
+
+const withCaption = (content: unknown, label: string) => html`
+
+ ${content}
+ ${label}
+
+`;
+
+const permutationContent = () => html`
+ ${TABS_DIRECTIONS.map((direction) =>
+ row(
+ TAB_DENSITIES.map((density) =>
+ withCaption(
+ renderTabs({
+ direction,
+ density,
+ accessibleLabel: `${direction} ${density} example`,
+ }),
+ DENSITY_LABELS[density]
+ )
+ ),
+ direction
+ )
+ )}
+ ${row(
+ [
+ renderTabs({
+ selected: 'selected',
+ accessibleLabel: 'Individual tab states',
+ tabs: html`
+ ${tab({ id: 'default', label: 'Default' })}
+ ${tab({ id: 'selected', label: 'Selected' })}
+ ${tab({ id: 'disabled', label: 'Disabled', disabled: true })}
+ `,
+ panels: html`
+ ${panel('default', 'Default tab content.')}
+ ${panel('selected', 'Selected tab content.')}
+ ${panel('disabled', 'Disabled tab content.')}
+ `,
+ }),
+ ],
+ 'Tab states'
+ )}
+ ${row(
+ [renderTabs({ disabled: true, accessibleLabel: 'Disabled container' })],
+ 'Disabled container'
+ )}
+ ${row(
+ [
+ renderTabs({
+ accessibleLabel: 'Text-only anatomy',
+ }),
+ renderTabs({
+ accessibleLabel: 'Icon and text anatomy',
+ tabs: html`
+ ${tab({ id: '1', label: 'Dashboard', icon: '☰' })}
+ ${tab({ id: '2', label: 'Reports', icon: '📊' })}
+ ${tab({ id: '3', label: 'Settings', icon: '⚙' })}
+ `,
+ panels: html`
+ ${panel('1', 'Dashboard content.')} ${panel('2', 'Reports content.')}
+ ${panel('3', 'Settings content.')}
+ `,
+ }),
+ renderTabs({
+ accessibleLabel: 'Icon-only anatomy',
+ tabs: html`
+ ${tab({ id: '1', label: 'Dashboard', icon: '☰', iconOnly: true })}
+ ${tab({ id: '2', label: 'Reports', icon: '📊', iconOnly: true })}
+ ${tab({ id: '3', label: 'Settings', icon: '⚙', iconOnly: true })}
+ `,
+ panels: html`
+ ${panel('1', 'Dashboard content.')} ${panel('2', 'Reports content.')}
+ ${panel('3', 'Settings content.')}
+ `,
+ }),
+ ],
+ 'Anatomy'
+ )}
+ ${row(
+ [
+ renderTabs({
+ // Baseline "Selected" tab isolates the underline from the forced
+ // pseudo-states below, so it doesn't read as part of Hover/Active.
+ selected: '0',
+ accessibleLabel: 'Forced pseudo-states',
+ tabs: html`
+ ${tab({ id: '0', label: 'Selected' })}
+ ${tab({ id: '1', label: 'Hover', forceState: 'hover' })}
+ ${tab({
+ id: '2',
+ label: 'Focus-visible',
+ forceState: 'focus-visible',
+ })}
+ ${tab({ id: '3', label: 'Active', forceState: 'active' })}
+ `,
+ panels: html`
+ ${panel('0', 'Selected content.')} ${panel('1', 'Hover content.')}
+ ${panel('2', 'Focus content.')} ${panel('3', 'Active content.')}
+ `,
+ }),
+ ],
+ 'Forced states'
+ )}
+ ${row(
+ [
+ renderTabs({
+ accessibleLabel: '承認ワークフロー',
+ tabs: html`
+ ${tab({ id: '1', label: '概要' })} ${tab({ id: '2', label: '仕様' })}
+ ${tab({ id: '3', label: 'ガイドライン' })}
+ `,
+ panels: html`
+ ${panel('1', '概要コンテンツ。')} ${panel('2', '仕様コンテンツ。')}
+ ${panel('3', 'ガイドラインコンテンツ。')}
+ `,
+ }),
+ renderTabs({
+ accessibleLabel: '승인 워크플로',
+ tabs: html`
+ ${tab({ id: '1', label: '개요' })} ${tab({ id: '2', label: '사양' })}
+ ${tab({ id: '3', label: '가이드라인' })}
+ `,
+ panels: html`
+ ${panel('1', '개요 콘텐츠.')} ${panel('2', '사양 콘텐츠.')}
+ ${panel('3', '가이드라인 콘텐츠.')}
+ `,
+ }),
+ renderTabs({
+ accessibleLabel: '审批工作流',
+ tabs: html`
+ ${tab({ id: '1', label: '概览' })} ${tab({ id: '2', label: '规格' })}
+ ${tab({ id: '3', label: '准则' })}
+ `,
+ panels: html`
+ ${panel('1', '概览内容。')} ${panel('2', '规格内容。')}
+ ${panel('3', '准则内容。')}
+ `,
+ }),
+ ],
+ 'CJK language'
+ )}
+`;
+
+const forceTabStates = forcePseudoStates('swc-tab[data-force-state]');
+
+// VRT stories
+
+// Direction (horizontal, vertical) x density (regular, compact), individual
+// tab states (default/selected/disabled within one group), a fully disabled
+// container, anatomy (text-only, icon+text, icon-only), forced
+// hover/focus-visible/active on individual tabs (see the `play` function
+// below - tab.css styles these on `:host()` directly, so no internal
+// selector is needed), and CJK tab labels. Rendered once in light/ltr and
+// once in dark/rtl below (that combination covers both axes; RTL also
+// exercises the selection indicator's mirrored `transform-origin` for
+// horizontal direction), all still in a single story so it costs one
+// snapshot.
+export const Permutations: Story = {
+ render: () => html`
+ ${theme(permutationContent(), 'light', 'ltr')}
+ ${theme(permutationContent(), 'dark', 'rtl')}
+ `,
+ parameters: vrtParameters,
+ play: forceTabStates,
+};
+
+// `forced-colors` is a real browser media feature Chromatic can emulate
+// directly, unlike :hover/:active/:focus-visible above. Forced-colors mode
+// replaces the whole page's palette, so it needs its own story/snapshot
+// rather than folding into Permutations. Tab, Tabs' selection indicator, and
+// TabPanel all define their own forced-colors overrides, so this covers all
+// three parts of the three-element architecture.
+export const ForcedColors: Story = {
+ render: () => theme(permutationContent(), 'light', 'ltr'),
+ parameters: forcedColorsVrtParameters,
+ play: forceTabStates,
+};