Skip to content

Commit 2116678

Browse files
authored
docs: more accordion examples (#1268)
1 parent 3ffd82b commit 2116678

File tree

7 files changed

+273
-90
lines changed

7 files changed

+273
-90
lines changed

docs/content/components/accordion.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ description: Organizes content into collapsible sections, allowing users to focu
44
---
55

66
<script>
7-
import { APISection, ComponentPreviewV2, AccordionDemo, AccordionDemoTransitions, AccordionDemoCustom, Callout } from '$lib/components/index.js'
7+
import { APISection, ComponentPreviewV2, AccordionDemo, AccordionDemoTransitions, AccordionDemoCustom, AccordionDemoHorizontalCards, Callout, AccordionDemoCheckoutSteps } from '$lib/components/index.js'
8+
89
let { schemas } = $props()
910
</script>
1011

@@ -346,4 +347,32 @@ You can then use the `MyAccordionContent` component alongside the other `Accordi
346347
</Accordion.Root>
347348
```
348349

350+
## Examples
351+
352+
The following examples demonstrate different ways to use the Accordion component.
353+
354+
### Horizontal Cards
355+
356+
Use the Accordion component to create a horizontal card layout with collapsible sections.
357+
358+
<ComponentPreviewV2 name="accordion-demo-horizontal-cards" componentName="Accordion Horizontal Cards">
359+
360+
{#snippet preview()}
361+
<AccordionDemoHorizontalCards />
362+
{/snippet}
363+
364+
</ComponentPreviewV2>
365+
366+
### Checkout Steps
367+
368+
Use the Accordion component to create a multi-step checkout process.
369+
370+
<ComponentPreviewV2 name="accordion-demo-checkout-steps" componentName="Accordion Checkout Steps">
371+
372+
{#snippet preview()}
373+
<AccordionDemoCheckoutSteps />
374+
{/snippet}
375+
376+
</ComponentPreviewV2>
377+
349378
<APISection {schemas} />

docs/src/lib/components/demo-container.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
<div
2626
class={cn(
27-
"rounded-tl-card rounded-tr-card border-muted ring-transparent! relative border-2 bg-zinc-50 dark:bg-neutral-900/50",
27+
"rounded-tl-card rounded-tr-card border-muted ring-transparent! relative mt-6 border-2 bg-zinc-50 dark:bg-neutral-900/50",
2828
wrapperClass
2929
)}
3030
data-llm-ignore
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<script lang="ts">
2+
import { cn } from "$lib/utils/styles.js";
3+
import { Accordion, useId, Button } from "bits-ui";
4+
import CaretDown from "phosphor-svelte/lib/CaretDown";
5+
import { SvelteSet } from "svelte/reactivity";
6+
7+
let activeStep = $state("");
8+
let completedSteps = new SvelteSet<string>();
9+
</script>
10+
11+
{#snippet MyAccordionHeader({ value, title }: { value: string; title: string })}
12+
{@const isCompleted = completedSteps.has(value)}
13+
<Accordion.Header>
14+
<Accordion.Trigger
15+
class="flex w-full flex-1 select-none items-center justify-between gap-3 py-5 text-[15px] font-medium transition-all [&[data-state=open]>span>svg]:rotate-180"
16+
>
17+
<div
18+
class={cn(
19+
"border-foreground/30 flex size-6 shrink-0 items-center justify-center rounded-full border text-xs font-medium",
20+
isCompleted ? "text-foreground" : "text-muted-foreground"
21+
)}
22+
>
23+
{isCompleted ? "" : value}
24+
</div>
25+
<span class="w-full text-left">
26+
{title}
27+
</span>
28+
<span
29+
class="hover:bg-dark-10 inline-flex size-8 items-center justify-center rounded-[7px] bg-transparent"
30+
>
31+
<CaretDown class="size-[18px] transition-transform duration-200" />
32+
</span>
33+
</Accordion.Trigger>
34+
</Accordion.Header>
35+
{/snippet}
36+
37+
{#snippet InputField({
38+
label,
39+
placeholder,
40+
type = "text",
41+
}: {
42+
label: string;
43+
placeholder: string;
44+
type?: string;
45+
})}
46+
{@const id = useId()}
47+
<div class="flex flex-col gap-1">
48+
<label class="select-none text-sm font-medium" for={id}>{label}</label>
49+
<input
50+
{type}
51+
{id}
52+
name={label}
53+
{placeholder}
54+
class="rounded-card-sm border-border-input bg-background placeholder:text-foreground-alt/50 hover:border-dark-40 focus-override inline-flex h-10 w-full items-center border px-4 text-base sm:text-sm"
55+
/>
56+
</div>
57+
{/snippet}
58+
59+
<Accordion.Root bind:value={activeStep} class="w-full sm:max-w-[70%]" type="single">
60+
<Accordion.Item value="1" class="border-dark-10 group border-b px-1.5">
61+
{@render MyAccordionHeader({ title: "Shipping Information", value: "1" })}
62+
<Accordion.Content
63+
class="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm tracking-[-0.01em]"
64+
>
65+
<div class="flex flex-col gap-4 pb-6 pt-2">
66+
<div class="grid grid-cols-2 gap-4">
67+
{@render InputField({ label: "First Name", placeholder: "John" })}
68+
{@render InputField({ label: "Last Name", placeholder: "Doe" })}
69+
<div class="col-span-2">
70+
{@render InputField({
71+
label: "Address",
72+
placeholder: "1234 Elm Street",
73+
})}
74+
</div>
75+
{@render InputField({ label: "City", placeholder: "Tampa" })}
76+
{@render InputField({ label: "ZIP", placeholder: "123456" })}
77+
</div>
78+
<div class="pt-2">
79+
<Button.Root
80+
class="rounded-input bg-dark text-background shadow-mini hover:bg-dark/95 inline-flex h-10 select-none items-center justify-center whitespace-nowrap px-[21px] text-sm font-medium transition-all hover:cursor-pointer active:scale-[0.98]"
81+
onclick={() => {
82+
completedSteps.add("1");
83+
activeStep = "2";
84+
}}
85+
>
86+
Continue to Payment
87+
</Button.Root>
88+
</div>
89+
</div>
90+
</Accordion.Content>
91+
</Accordion.Item>
92+
<Accordion.Item value="2" class="border-dark-10 group border-b px-1.5">
93+
{@render MyAccordionHeader({ title: "Payment Method", value: "2" })}
94+
<Accordion.Content
95+
class="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm tracking-[-0.01em]"
96+
>
97+
<div class="flex flex-col gap-4 pb-6 pt-2">
98+
<div class="grid grid-cols-3 gap-4">
99+
<div class="col-span-3">
100+
{@render InputField({
101+
label: "Card Number",
102+
placeholder: "4242 4242 4242 4242",
103+
})}
104+
</div>
105+
{@render InputField({ label: "Exp. Month", placeholder: "MM" })}
106+
{@render InputField({ label: "Exp. Year", placeholder: "YY" })}
107+
{@render InputField({ label: "CVC", placeholder: "123" })}
108+
</div>
109+
<div class="pt-2">
110+
<Button.Root
111+
class="rounded-input bg-dark text-background shadow-mini hover:bg-dark/95 inline-flex h-10 select-none items-center justify-center whitespace-nowrap px-[21px] text-sm font-medium transition-all hover:cursor-pointer active:scale-[0.98]"
112+
onclick={() => {
113+
completedSteps.add("2");
114+
activeStep = "3";
115+
}}
116+
>
117+
Continue to Review
118+
</Button.Root>
119+
</div>
120+
</div>
121+
</Accordion.Content>
122+
</Accordion.Item>
123+
<Accordion.Item value="3" class="border-dark-10 group border-b px-1.5">
124+
{@render MyAccordionHeader({ title: "Payment Method", value: "3" })}
125+
<Accordion.Content
126+
class="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden pb-6 text-sm tracking-[-0.01em]"
127+
>
128+
<div class="flex flex-col gap-4 pt-2">
129+
<div class="rounded-lg border p-4">
130+
<h4 class="mb-2 font-medium">Order Summary</h4>
131+
<div class="flex flex-col gap-2">
132+
<div class="flex justify-between text-sm">
133+
<span class="text-muted-foreground">Product 1</span>
134+
<span>$29.99</span>
135+
</div>
136+
<div class="flex justify-between text-sm">
137+
<span class="text-muted-foreground">Product 2</span>
138+
<span>$49.99</span>
139+
</div>
140+
<div class="flex justify-between text-sm">
141+
<span class="text-muted-foreground">Shipping</span>
142+
<span>$4.99</span>
143+
</div>
144+
<div class="mt-2 flex justify-between border-t pt-2 font-medium">
145+
<span>Total</span>
146+
<span>$84.97</span>
147+
</div>
148+
</div>
149+
</div>
150+
<div class="pt-2">
151+
<Button.Root
152+
class="rounded-input bg-dark text-background shadow-mini hover:bg-dark/95 inline-flex h-10 select-none items-center justify-center whitespace-nowrap px-[21px] text-sm font-medium transition-all hover:cursor-pointer active:scale-[0.98]"
153+
onclick={() => {
154+
completedSteps.add("3");
155+
activeStep = "";
156+
}}
157+
>
158+
Place Order
159+
</Button.Root>
160+
</div>
161+
</div>
162+
</Accordion.Content>
163+
</Accordion.Item>
164+
</Accordion.Root>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script lang="ts">
2+
import { Accordion } from "bits-ui";
3+
4+
let value = $state("item-1");
5+
6+
const items = [
7+
{
8+
id: "item-1",
9+
title: "Mountain Range",
10+
image: "https://images.unsplash.com/photo-1586589058841-f1264894a260?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3",
11+
description: "Majestic mountain ranges with snow-capped peaks and lush valleys.",
12+
},
13+
{
14+
id: "item-2",
15+
title: "Ocean Views",
16+
image: "https://images.unsplash.com/photo-1650300874827-7d39bc9276ea?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3",
17+
description:
18+
"Serene ocean scenes with crashing waves, beautiful sunsets, and sandy beaches.",
19+
},
20+
{
21+
id: "item-3",
22+
title: "Forest Retreats",
23+
image: "https://images.unsplash.com/photo-1693297490324-37ee6301f6c8?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3",
24+
description:
25+
"Dense forests with towering trees, abundant wildlife, and peaceful streams.",
26+
},
27+
];
28+
</script>
29+
30+
<Accordion.Root
31+
type="single"
32+
orientation="horizontal"
33+
class="flex h-[400px] w-full flex-col gap-2 sm:flex-row"
34+
bind:value
35+
>
36+
{#each items as item (item.id)}
37+
<Accordion.Item
38+
value={item.id}
39+
class="ring-primary/70 relative cursor-pointer overflow-hidden rounded-lg transition-all duration-500 ease-in-out data-[state=closed]:w-[10%] data-[state=open]:w-[100%] [&:has(:focus-visible)]:ring-2"
40+
onclick={() => (value = item.id)}
41+
>
42+
<img src={item.image} alt={item.title} class="h-[400px] w-full object-cover" />
43+
<div
44+
class="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-black/80 via-black/40 to-transparent p-4"
45+
>
46+
<div
47+
class="transition-all duration-300 group-data-[state=closed]:translate-y-2 group-data-[state=open]:translate-y-0"
48+
>
49+
<Accordion.Header>
50+
<Accordion.Trigger
51+
class="focus-override text-left font-bold text-white transition-all duration-300 focus-visible:!outline-none data-[state=open]:mb-2 data-[state=closed]:text-sm data-[state=open]:text-xl data-[state=closed]:opacity-0 data-[state=open]:opacity-100"
52+
>
53+
{item.title}
54+
</Accordion.Trigger>
55+
</Accordion.Header>
56+
<Accordion.Content
57+
forceMount
58+
class="max-h-0 overflow-hidden text-white/90 transition-all duration-700 data-[state=open]:max-h-[100px] data-[state=closed]:opacity-0 data-[state=open]:opacity-100"
59+
>
60+
{item.description}
61+
</Accordion.Content>
62+
<div
63+
class="absolute bottom-0 left-0 h-1 w-full transition-all duration-300 group-data-[state=closed]:opacity-0 group-data-[state=open]:opacity-100"
64+
></div>
65+
</div>
66+
</div>
67+
</Accordion.Item>
68+
{/each}
69+
</Accordion.Root>

docs/src/lib/components/demos/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
export { default as AccordionDemo } from "./accordion-demo.svelte";
22
export { default as AccordionDemoCustom } from "./accordion-demo-custom.svelte";
33
export { default as AccordionDemoTransitions } from "./accordion-demo-transitions.svelte";
4+
export { default as AccordionDemoHorizontalCards } from "./accordion-demo-horizontal-cards.svelte";
5+
export { default as AccordionDemoCheckoutSteps } from "./accordion-demo-checkout-steps.svelte";
46
export { default as AlertDialogDemo } from "./alert-dialog-demo.svelte";
57
export { default as AspectRatioDemo } from "./aspect-ratio-demo.svelte";
68
export { default as AvatarDemo } from "./avatar-demo.svelte";

docs/src/lib/styles/app.css

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,13 +294,6 @@
294294
}
295295

296296
@layer components {
297-
*:not(body):not(.focus-override) {
298-
outline: none !important;
299-
&:focus-visible {
300-
@apply focus-visible:ring-foreground focus-visible:ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-1;
301-
}
302-
}
303-
304297
.link {
305298
@apply hover:text-foreground/80 focus-visible:ring-foreground focus-visible:ring-offset-background rounded-xs focus-visible:outline-hidden inline-flex items-center gap-1 font-medium underline underline-offset-4 focus-visible:ring-2 focus-visible:ring-offset-2;
306299
}

0 commit comments

Comments
 (0)