This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / welcome / Steps.svelte
2.5 kB 79 lines
1<script module lang="ts"> 2 import { tv, type VariantProps } from "tailwind-variants"; 3 4 // the numbered rail from Figma node 191:6514 — same shape as the appview's 5 // onboarding/fragments/steps, restyled onto the design-system tokens. the three 6 // states differ only in the marker fill and the label colour, so they are one 7 // variant across two slots rather than three hand-written class strings. 8 export const steps = tv({ 9 slots: { 10 root: "flex w-full items-center gap-2", 11 item: "flex shrink-0 items-center gap-2", 12 marker: 13 "flex size-5 shrink-0 items-center justify-center rounded-full typography-paragraph-small", 14 // labels are desktop-only, but sr-only rather than hidden so the rail still 15 // reads as a named list of steps on a narrow screen 16 label: "sr-only typography-paragraph-small whitespace-nowrap sm:not-sr-only", 17 connector: "h-px min-w-4 flex-1 bg-border-default" 18 }, 19 variants: { 20 state: { 21 done: { 22 marker: "bg-background-inset text-foreground-default", 23 label: "text-foreground-subtle" 24 }, 25 current: { 26 marker: "bg-background-muted text-foreground-default", 27 label: "text-foreground-default" 28 }, 29 upcoming: { 30 marker: "bg-background-inset text-foreground-subtle", 31 label: "text-foreground-subtle" 32 } 33 } 34 }, 35 defaultVariants: { 36 state: "upcoming" 37 } 38 }); 39 40 export type StepsVariants = VariantProps<typeof steps>; 41 export type StepState = NonNullable<StepsVariants["state"]>; 42</script> 43 44<script lang="ts"> 45 import Check from "$icon/check"; 46 47 interface Props { 48 items: readonly { key: string; label: string }[]; 49 current: number; 50 } 51 52 let { items, current }: Props = $props(); 53 54 // root and connector take no variant, so they resolve once 55 const shell = steps(); 56 57 const stateOf = (index: number): StepState => 58 index < current ? "done" : index === current ? "current" : "upcoming"; 59</script> 60 61<ol class={shell.root()}> 62 {#each items as item, index (item.key)} 63 {#if index > 0} 64 <li aria-hidden="true" class={shell.connector()}></li> 65 {/if} 66 {@const state = stateOf(index)} 67 {@const slot = steps({ state })} 68 <li class={slot.item()} aria-current={state === "current" ? "step" : undefined}> 69 <span class={slot.marker()} aria-label={state === "done" ? "done" : undefined}> 70 {#if state === "done"} 71 <Check class="size-3.5" aria-hidden="true" /> 72 {:else} 73 {index + 1} 74 {/if} 75 </span> 76 <span class={slot.label()}>{item.label}</span> 77 </li> 78 {/each} 79</ol>