Skip to content

Commit 987d738

Browse files
authored
feat(Accordion/Collapsible): hiddenUntilFound prop (#1782)
1 parent 529d897 commit 987d738

23 files changed

+720
-29
lines changed

.changeset/funny-boats-roll.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"bits-ui": minor
3+
---
4+
5+
feat(Collapsible): add `hiddenUntilFound` prop to expand collapsible when the content matches a browser search

.changeset/yellow-eagles-itch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"bits-ui": minor
3+
---
4+
5+
feat(Accordion): `hiddenUntilFound` to expand on browser search match

docs/content/components/accordion.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,25 @@ Disable specific items with the `disabled` prop:
270270
</Accordion.Root>
271271
```
272272

273+
### Hidden Until Found
274+
275+
The `hiddenUntilFound` prop enables browser search functionality within collapsed accordion content. When enabled, collapsed content is marked with `hidden="until-found"`, allowing browsers to automatically expand accordion items when users search for text within them.
276+
277+
```svelte {4}
278+
<Accordion.Root type="single">
279+
<Accordion.Item value="item-1">
280+
<Accordion.Header>
281+
<Accordion.Trigger>Search Demo</Accordion.Trigger>
282+
</Accordion.Header>
283+
<Accordion.Content hiddenUntilFound>
284+
This content can be found by browser search (Ctrl+F/CMD+F) even when the
285+
accordion is closed. The accordion will automatically open when the
286+
browser finds matching text.
287+
</Accordion.Content>
288+
</Accordion.Item>
289+
</Accordion.Root>
290+
```
291+
273292
### Svelte Transitions
274293

275294
The Accordion component can be enhanced with Svelte's built-in transition effects or other animation libraries.

docs/content/components/collapsible.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Conceals or reveals content sections, enhancing space utilization a
44
---
55

66
<script>
7-
import { APISection, ComponentPreview, CollapsibleDemo, CollapsibleDemoTransitions, Callout } from '$lib/components/index.js'
7+
import { APISection, ComponentPreview, CollapsibleDemo, CollapsibleDemoTransitions, CollapsibleHiddenUntilFoundDemo, Callout } from '$lib/components/index.js'
88
let { schemas } = $props()
99
</script>
1010

@@ -26,6 +26,7 @@ The Collapsible component enables you to create expandable and collapsible conte
2626
- **Transition Support**: CSS variables and data attributes for smooth transitions between states.
2727
- **Flexible State Management**: Supports controlled and uncontrolled state, take control if needed.
2828
- **Compound Component Structure**: Provides a set of sub-components that work together to create a fully-featured collapsible.
29+
- **Hidden Until Found**: Support for the `hidden="until-found"` attribute for browser search integration.
2930

3031
## Architecture
3132

@@ -218,4 +219,37 @@ You can then use the `MyCollapsibleContent` component alongside the other `Colla
218219
</Collapsible.Root>
219220
```
220221

222+
## Hidden Until Found
223+
224+
The `hiddenUntilFound` prop enables integration with the browser's find-in-page functionality. When enabled, the collapsible content is marked with `hidden="until-found"`, which allows browsers to automatically expand collapsed content when users search for text within it.
225+
226+
<ComponentPreview variant="collapsed" name="collapsible-hidden-until-found-demo" componentName="Collapsible">
227+
228+
{#snippet preview()}
229+
<CollapsibleHiddenUntilFoundDemo />
230+
{/snippet}
231+
232+
</ComponentPreview>
233+
234+
### Basic Usage
235+
236+
```svelte
237+
<script lang="ts">
238+
import { Collapsible } from "bits-ui";
239+
</script>
240+
241+
<Collapsible.Root>
242+
<Collapsible.Trigger>Show More Details</Collapsible.Trigger>
243+
<Collapsible.Content hiddenUntilFound={true}>
244+
<p>
245+
This content will be automatically revealed when users search for text
246+
within it using Ctrl+F (Cmd+F on Mac).
247+
</p>
248+
<p>
249+
For example, try searching for "automatically revealed" on this page.
250+
</p>
251+
</Collapsible.Content>
252+
</Collapsible.Root>
253+
```
254+
221255
<APISection {schemas} />

docs/src/lib/components/demos/collapsible-demo.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
</Collapsible.Trigger>
1515
</div>
1616

17-
<Collapsible.Content class="space-y-2 font-mono text-[15px] tracking-[0.01em]">
17+
<Collapsible.Content
18+
hiddenUntilFound
19+
class="data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up space-y-2 overflow-hidden font-mono text-[15px] tracking-[0.01em]"
20+
>
1821
<div class="rounded-9px bg-muted inline-flex h-12 w-full items-center px-[18px] py-3">
1922
@huntabyte/bits-ui
2023
</div>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<script lang="ts">
2+
import { Collapsible } from "bits-ui";
3+
import CaretUpDown from "phosphor-svelte/lib/CaretUpDown";
4+
</script>
5+
6+
<div class="flex w-full max-w-md flex-col gap-4">
7+
<div
8+
class="border-border bg-background-alt absolute left-4 top-4 flex flex-col gap-2 rounded-xl border p-4"
9+
>
10+
<div class="text-foreground-alt flex flex-col gap-2 text-sm">
11+
<span>Try searching for "searchable content" on this page</span>
12+
<code class="bg-muted w-fit rounded px-1 py-0.5 text-xs">(Ctrl+F / Cmd+F)</code>
13+
</div>
14+
</div>
15+
16+
<Collapsible.Root class="flex flex-col gap-3">
17+
<div class="flex items-center justify-between gap-4">
18+
<h4 class="text-[15px] font-medium">FAQ: How does search work?</h4>
19+
<Collapsible.Trigger
20+
class="rounded-9px border-border-input bg-background-alt text-foreground shadow-btn hover:bg-muted inline-flex h-10 w-10 items-center justify-center border transition-all active:scale-[0.98]"
21+
aria-label="Toggle FAQ answer"
22+
>
23+
<CaretUpDown class="size-4" weight="bold" />
24+
</Collapsible.Trigger>
25+
</div>
26+
27+
<Collapsible.Content
28+
hiddenUntilFound
29+
class="data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up overflow-hidden"
30+
>
31+
<div
32+
class="bg-background border-border rounded-lg border p-4 text-[14px] leading-relaxed"
33+
>
34+
<p class="mb-3">
35+
This collapsible contains <strong>searchable content</strong> that demonstrates
36+
the
37+
<code class="bg-muted rounded px-1 py-0.5 text-xs">hiddenUntilFound</code> feature.
38+
When you search for text within this collapsed section, the browser will automatically
39+
expand it to show the matching results.
40+
</p>
41+
</div>
42+
</Collapsible.Content>
43+
</Collapsible.Root>
44+
</div>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export { default as CheckboxDemoCustom } from "./checkbox-demo-custom.svelte";
1616
export { default as CheckboxDemoGroup } from "./checkbox-demo-group.svelte";
1717
export { default as CollapsibleDemo } from "./collapsible-demo.svelte";
1818
export { default as CollapsibleDemoTransitions } from "./collapsible-demo-transitions.svelte";
19+
export { default as CollapsibleHiddenUntilFoundDemo } from "./collapsible-hidden-until-found-demo.svelte";
1920
export { default as ComboboxDemo } from "./combobox-demo.svelte";
2021
export { default as ComboboxDemoAutoScrollDelay } from "./combobox-demo-auto-scroll-delay.svelte";
2122
export { default as ComboboxDemoTransition } from "./combobox-demo-transition.svelte";

docs/src/lib/content/api-reference/accordion.api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ const content = defineComponentApiSchema<AccordionContentPropsWithoutHTML>({
131131
description: "The accordion item content, which is displayed when the item is open.",
132132
props: {
133133
forceMount: forceMountProp,
134+
hiddenUntilFound: defineBooleanProp({
135+
description:
136+
"Whether the content should use `hidden='until-found'` when closed. This allows browsers to search within collapsed content and automatically expand the accordion item when matches are found.",
137+
default: false,
138+
}),
134139
...withChildProps({
135140
elType: "HTMLDivElement",
136141
child: {

docs/src/lib/content/api-reference/collapsible.api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ export const content = defineComponentApiSchema<CollapsibleContentPropsWithoutHT
8484
description: "The content displayed when the collapsible is open.",
8585
props: {
8686
forceMount: forceMountProp,
87+
hiddenUntilFound: defineBooleanProp({
88+
default: false,
89+
description:
90+
'When true, the content will be marked with `hidden="until-found"` when collapsed, allowing browsers to find and automatically expand the content during page searches.',
91+
}),
8792
...withChildProps({
8893
elType: "HTMLDivElement",
8994
child: {

docs/src/lib/styles/app.css

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@
132132

133133
--animate-accordion-down: accordion-down 0.2s ease-out;
134134
--animate-accordion-up: accordion-up 0.2s ease-out;
135+
--animate-collapsible-down: collapsible-down 0.2s ease-out;
136+
--animate-collapsible-up: collapsible-up 0.2s ease-out;
135137
--animate-caret-blink: caret-blink 1s ease-out infinite;
136138
--animate-scale-in: scale-in 0.2s ease;
137139
--animate-scale-out: scale-out 0.15s ease;
@@ -167,6 +169,27 @@
167169
}
168170
}
169171

172+
@keyframes collapsible-down {
173+
from {
174+
height: 0;
175+
}
176+
177+
to {
178+
height: var(--bits-collapsible-content-height);
179+
}
180+
}
181+
182+
@keyframes collapsible-up {
183+
from {
184+
height: var(--bits-collapsible-content-height);
185+
}
186+
187+
to {
188+
height: 0;
189+
}
190+
}
191+
192+
170193
@keyframes caret-blink {
171194

172195
0%,

0 commit comments

Comments
 (0)