diff --git a/apps/cards/apps/web/src/lib/components/CardFace.svelte b/apps/cards/apps/web/src/lib/components/CardFace.svelte index 9e14a4161..19d09cf1f 100644 --- a/apps/cards/apps/web/src/lib/components/CardFace.svelte +++ b/apps/cards/apps/web/src/lib/components/CardFace.svelte @@ -3,6 +3,19 @@ * CardFace — renders one learnable unit (a single subIndex of a card) * for any Phase-1 card type. Stateless: the parent owns `showBack`, * `typedAnswer`, and any timing. + * + * Card-feel design (Phase A polish): + * - Single surface that physically flips on Y axis when revealed. + * Both faces share a CSS-grid cell so the parent height is the + * max of front/back, no jumpy reflow on flip. + * - Tap anywhere on the surface reveals (only while `showBack` is + * false). The /learn page keeps the keyboard space/enter shortcut. + * - `prefers-reduced-motion: reduce` collapses the rotateY into an + * instant cross-fade — same affordance, no vestibular trigger. + * + * Type-in cards skip the flip: the input field doesn't make sense on + * a flippable face, so we keep the historical "input + answer below" + * layout for that single card type. */ import { renderCloze, renderMarkdown, type Card } from '@mana/cards-core'; @@ -13,9 +26,10 @@ showBack: boolean; typedAnswer?: string; onTypedAnswer?: (value: string) => void; + onReveal?: () => void; } - let { card, subIndex, showBack, typedAnswer = '', onTypedAnswer }: Props = $props(); + let { card, subIndex, showBack, typedAnswer = '', onTypedAnswer, onReveal }: Props = $props(); const view = $derived.by(() => { switch (card.type) { @@ -54,34 +68,127 @@ const matched = $derived( isTypeIn && typedAnswer.trim().toLowerCase() === view.expected.trim().toLowerCase() ); + + function tryReveal() { + if (!showBack && !isTypeIn) onReveal?.(); + } -
-
- {@html view.prompt} -
+{#if isTypeIn} + +
+
+ {@html view.prompt} +
- {#if isTypeIn} onTypedAnswer?.((e.currentTarget as HTMLInputElement).value)} disabled={showBack} /> - {/if} - {#if showBack} -
+ {@html view.answer} +
+ {/if} +
+{:else} +
+
+
+ {@html view.prompt} + {#if !showBack} +

+ Tippe auf die Karte oder drücke Leertaste +

+ {/if} +
+ +
+ {@html view.answer} +
+ +
+{/if} + + diff --git a/apps/cards/apps/web/src/routes/learn/[deckId]/+page.svelte b/apps/cards/apps/web/src/routes/learn/[deckId]/+page.svelte index 8c3dc299d..6588788b0 100644 --- a/apps/cards/apps/web/src/routes/learn/[deckId]/+page.svelte +++ b/apps/cards/apps/web/src/routes/learn/[deckId]/+page.svelte @@ -146,6 +146,7 @@ {showBack} {typedAnswer} onTypedAnswer={(v) => (typedAnswer = v)} + onReveal={reveal} /> {#if canSuggest} @@ -171,14 +172,14 @@ {/if} {/if} - {#if !showBack} + {#if !showBack && current.card.type === 'type-in'} - {:else} + {:else if showBack}