language-learning-app/frontend/src/routes/app/adventures/[id]/NextSteps.svelte
2026-05-06 22:51:55 +01:00

173 lines
4 KiB
Svelte

<script lang="ts">
type Props = {
options: { label: string; id: string }[];
onSelect: (optionId: string) => void;
};
const { options, onSelect }: Props = $props();
let isSubmitting = $state(false);
const handleOptionSelect = async (optionId: string) => {
if (isSubmitting) {
return;
}
isSubmitting = true;
try {
await onSelect(optionId);
} finally {
isSubmitting = false;
}
};
</script>
<section class="next-steps" aria-label="Choose what happens next">
<header class="next-steps__header">
<p class="next-steps__kicker">Choose your path</p>
<h2 class="next-steps__title">What happens next?</h2>
</header>
<ol class="next-steps__list">
{#each options as option, index (option.id)}
<li class="next-steps__item">
<button
class="next-steps__button"
onclick={() => handleOptionSelect(option.id)}
disabled={isSubmitting}
>
<span class="next-steps__index" aria-hidden="true"
>{String(index + 1).padStart(2, '0')}</span
>
<span class="next-steps__label">{option.label}</span>
<span class="next-steps__meta" aria-hidden="true">Choose</span>
</button>
</li>
{/each}
</ol>
</section>
<style>
.next-steps {
--next-steps-surface: var(--color-surface-container-low);
--next-steps-surface-hover: var(--color-surface-container-lowest);
max-width: 72ch;
margin: var(--space-6) auto 0;
padding: clamp(1rem, 0.9rem + 0.9vw, 1.8rem);
border-radius: var(--radius-xl);
background-color: var(--next-steps-surface);
}
.next-steps__header {
margin-bottom: var(--space-4);
}
.next-steps__kicker {
margin: 0;
font-family: var(--font-label);
font-size: var(--text-label-md);
letter-spacing: 0.08em;
text-transform: uppercase;
color: color-mix(in srgb, var(--color-on-surface) 70%, transparent);
}
.next-steps__title {
margin: var(--space-1) 0 0;
font-family: var(--font-display);
font-size: clamp(1.35rem, 1.2rem + 0.9vw, 2rem);
line-height: 1.15;
color: var(--color-on-surface);
}
.next-steps__list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.next-steps__item {
margin: 0;
}
.next-steps__button {
width: 100%;
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: var(--space-3);
padding: clamp(0.9rem, 0.8rem + 0.5vw, 1.2rem);
border: none;
border-radius: var(--radius-lg);
background: var(--color-surface-container);
text-align: left;
cursor: pointer;
transition:
transform var(--duration-fast) var(--ease-standard),
background-color var(--duration-fast) var(--ease-standard);
}
.next-steps__index {
font-family: var(--font-label);
font-size: var(--text-label-md);
font-weight: var(--weight-semibold);
letter-spacing: 0.08em;
color: color-mix(in srgb, var(--color-on-surface) 72%, transparent);
}
.next-steps__label {
font-family: var(--font-body);
font-size: clamp(1rem, 0.95rem + 0.3vw, 1.18rem);
line-height: 1.35;
color: var(--color-on-surface);
text-wrap: pretty;
}
.next-steps__meta {
font-family: var(--font-label);
font-size: var(--text-label-md);
font-weight: var(--weight-medium);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
line-height: 1;
color: color-mix(in srgb, var(--color-primary) 72%, var(--color-on-surface));
}
.next-steps__button:hover:enabled {
background: var(--next-steps-surface-hover);
transform: translateY(-1px);
}
.next-steps__button:hover:enabled .next-steps__meta,
.next-steps__button:focus-visible .next-steps__meta {
color: var(--color-primary);
}
.next-steps__button:focus-visible {
outline: 2px solid color-mix(in srgb, var(--colour-outline-variant) 20%, transparent);
outline-offset: 2px;
}
.next-steps__button:disabled {
opacity: 0.7;
cursor: progress;
}
@media (max-width: 42rem) {
.next-steps {
margin-top: var(--space-4);
padding: var(--space-3);
}
.next-steps__button {
grid-template-columns: auto 1fr;
}
.next-steps__meta {
display: none;
}
}
</style>