Forms inside cards
A card is not required to be read-only content. If it holds a real form field, two pieces of engine behaviour, both already covered in Accessibility, are what make that work without any extra configuration on your part.
Typing does not move the stack
Section titled “Typing does not move the stack”Riffle checks the keydown’s own target before mapping arrow keys, Home or End to a stack move
(see Accessibility). An <input>,
<textarea>, <select>, or a contenteditable element is left alone entirely: the keydown reaches
the field itself, so the caret moves and characters get typed exactly as they would outside a stack.
There is nothing to opt into.
A background card’s field is unreachable, not just visually behind
Section titled “A background card’s field is unreachable, not just visually behind”Every card except the active one is inert (see Accessibility).
An <input> inside a background card cannot receive focus: not by Tab, not by a screen reader’s
virtual cursor, not by a direct .focus() call from your own code. Advance the stack in the live demo
above, then Tab through the page: only the front card’s field is ever reachable, and the note you
typed on a card you have since navigated away from stays exactly where you left it, simply out of
reach until that card is active again.
Where focus goes when the active card changes
Section titled “Where focus goes when the active card changes”If a field inside the active card is focused and the stack moves to a new card, focus follows, exactly as Accessibility describes for any card’s content: it lands on the new active card’s own root element, never on a field inside it, and never the specific field you happened to be in before.
If you want focus to land inside the new card’s first field instead, put it there yourself, in your own change handler: the engine updates the new card’s accessibility state, including its own focus-follow onto that card’s root, before it fires its own change event, so your own redirect runs after the engine’s and is what sticks.
function onChange(event: RiffleEventMap['change']): void { activeIndex.value = event.index // Only redirect focus into the new field when focus was already // somewhere inside the stack: that is exactly the condition the engine's // own focus-following uses to decide whether to move focus onto the new // active card's root at all. Skipping this check would steal focus into // a card field even when nothing on the page had focus in the stack, for // example while a visitor was reading unrelated page text and something // else called goTo(). const wasFocusInStack = document.activeElement?.closest('[aria-roledescription="carousel"]') != null if (!wasFocusInStack) return // The engine updates the new active card's accessibility state, including // its own focus-follow onto that card's root, before it fires this // handler, so this call is the last one to move focus, not the first, and // it is the one that sticks. noteFieldRefs[event.index]?.focus()}Each card’s own field
Section titled “Each card’s own field”<template #card="{ card, index }"> <div class="card-face" :style="{ width: `${cardWidth}px`, height: `${cardHeight}px` }"> <strong>{{ card.title }}</strong> <label :for="`${idBase}-note-${index}`" class="note-label">Your note</label> <input :id="`${idBase}-note-${index}`" :ref="(el) => setNoteField(index, el as HTMLInputElement | null)" type="text" class="note-input" :placeholder="`What did you think of ${card.title}?`" /> </div></template>A few things worth calling out:
- Each input’s
idis built from a per-instance prefix plus the card’s index (Vue has nouseIdequivalent), so<label for>genuinely targets the right field even though every card’s template is the same.
- The label is real markup, not a placeholder standing in for one. A
placeholderdisappears the moment you type and most screen readers do not treat it as a substitute for a label; every version of this recipe uses both, a real<label>for the accessible name and aplaceholderfor the example prompt text. - Nothing here is Riffle-specific. The stack does not know or care that its cards contain form
fields; every behaviour on this page falls out of the roving-tabindex and
inertmechanics that apply to any element inside a card, form field or not.
You have reached the end of the recipes. Examples is the full gallery if you want to see every shipped example in one place.