Skip to content

Commit 3ffd82b

Browse files
authored
docs: improvements (#1267)
1 parent ce4283f commit 3ffd82b

File tree

8 files changed

+891
-233
lines changed

8 files changed

+891
-233
lines changed

docs/content/child-snippet.md

Lines changed: 162 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,82 @@ title: Child Snippet
33
description: Learn how to use the `child` snippet to render your own elements.
44
---
55

6-
## Usage
6+
The `child` snippet is a powerful feature that gives you complete control over the rendered elements in Bits UI components, allowing for customization while maintaining accessibility and functionality.
77

8-
Many Bits UI components have a default HTML element that wraps their `children`. For example, `Accordion.Trigger` typically renders as:
8+
## When to Use It
9+
10+
You should use the `child` snippet when you need:
11+
12+
- Svelte-specific features like transitions, animations, actions, or scoped styles
13+
- Integration with custom components in your application
14+
- Precise control over the DOM structure
15+
- Advanced composition of components
16+
17+
## Basic Usage
18+
19+
Many Bits UI components have default HTML elements that wrap their content. For example, `Accordion.Trigger` renders a `<button>` element by default:
920

1021
```svelte
11-
<button>
22+
<button {...props}>
1223
{@render children()}
1324
</button>
1425
```
1526

16-
While you can set standard button attributes, you might need more control for:
17-
18-
- Applying Svelte transitions or actions
19-
- Using custom components
20-
- Scoped CSS
27+
When you need to customize this element, the `child` snippet lets you take control:
2128

22-
This is where the `child` snippet comes in.
29+
```svelte
30+
<script lang="ts">
31+
import MyCustomButton from "$lib/components";
32+
import { Accordion } from "bits-ui";
33+
</script>
2334
24-
Components supporting render delegation accept an optional child prop, which is a Svelte snippet. When used, the component passes its attributes to this snippet, allowing you to apply them to any element.
35+
<Accordion.Trigger>
36+
{#snippet child({ props })}
37+
<MyCustomButton {...props}>Toggle Item</MyCustomButton>
38+
{/snippet}
39+
</Accordion.Trigger>
2540
26-
Let's take a look at an example using the `Accordion.Trigger` component:
41+
<!-- or -->
2742
28-
```svelte
2943
<Accordion.Trigger>
3044
{#snippet child({ props })}
31-
<div {...props}>Open accordion item</div>
45+
<button {...props} class="scoped-button">Toggle Item</button>
3246
{/snippet}
3347
</Accordion.Trigger>
34-
```
3548
36-
The `props` object includes event handlers, ARIA attributes, and any other attributes passed to `Accordion.Trigger`. Note that when using `child`, other children outside this snippet are ignored.
49+
<style>
50+
.scoped-button {
51+
background-color: #3182ce;
52+
color: #fff;
53+
}
54+
</style>
55+
```
3756

38-
## Custom IDs & Attributes
57+
In this example:
3958

40-
To use custom IDs, event handlers, or other attributes with a custom element, you must pass them to the component first. This is crucial because:
59+
- The `props` parameter contains all necessary attributes and event handlers
60+
- The `{...props}` spread applies these to your custom element/component
61+
- You can add scoped styles, transitions, actions, etc. directly to the element
4162

42-
- Many Bits UI internals rely on specific IDs
43-
- Props are merged using a [`mergeProps`](/docs/utilities/merge-props) function to handle cancelling internal handlers, etc.
63+
## How It Works
4464

45-
Correct usage:
65+
When you use the `child` snippet:
4666

47-
```svelte
48-
<Accordion.Trigger id="my-custom-id" onclick={() => console.log("clicked")}>
49-
<!-- your custom ID and event handler is now inside the `props` object -->
50-
{#snippet child({ props })}
51-
<div {...props}>Open accordion item</div>
52-
{/snippet}
53-
</Accordion.Trigger>
54-
```
67+
1. The component passes all internal props and your custom props passed to the component via the `props` snippet parameter
68+
2. You decide which element receives these props
69+
3. The component's internal logic continues to work correctly
5570

56-
In this example, `my-custom-id`, the click event handler, and my-custom-class are properly merged into the `props` object, ensuring they work alongside Bits UI's internal logic.
71+
### Behind the Scenes
5772

58-
Behind the scenes, components using the child prop typically implement logic similar to this:
73+
Components that support the `child` snippet typically implement logic similar to:
5974

6075
```svelte
6176
<script lang="ts">
62-
// other imports/props/logic omitted for brevity
77+
// Bits UI component internal logic
6378
let { child, children, ...restProps } = $props();
6479
const trigger = makeTrigger();
6580
81+
// Merge internal props with user props
6682
const mergedProps = $derived(mergeProps(restProps, trigger.props));
6783
</script>
6884
@@ -75,19 +91,64 @@ Behind the scenes, components using the child prop typically implement logic sim
7591
{/if}
7692
```
7793

78-
## Floating Content Components
94+
## Working with Props
95+
96+
### Custom IDs & Attributes
97+
98+
To use custom IDs, event handlers, or other attributes, pass them to the component first:
99+
100+
```svelte
101+
<Accordion.Trigger
102+
id="my-custom-id"
103+
data-testid="accordion-trigger"
104+
onclick={() => console.log("clicked")}
105+
>
106+
{#snippet child({ props })}
107+
<button {...props}>Open accordion item</button>
108+
{/snippet}
109+
</Accordion.Trigger>
110+
```
111+
112+
The `props` object will now include:
79113

80-
Floating content components (tooltips, popovers, dropdowns, etc.) require special handling when used with the `child` snippet due to their positioning requirements with Floating UI.
114+
- Your custom ID (`id="my-custom-id"`)
115+
- Your data attribute (`data-testid="accordion-trigger"`)
116+
- Your click event handler, properly merged with internal handlers
117+
- All required ARIA attributes and internal event handlers
81118

82-
### Implementation Details
119+
## Combining with Svelte Features
83120

84-
When implementing floating content, you must:
121+
You can apply Svelte-specific features to your custom elements, such as transitions, actions, and scoped styles:
85122

86-
- Include a wrapper element within the `child` snippet
87-
- Spread the `wrapperProps` prop to this wrapper element
88-
- Place your floating content inside this wrapper
123+
```svelte
124+
<Accordion.Trigger>
125+
{#snippet child({ props })}
126+
<div {...props} use:myCustomAction class="my-custom-trigger">
127+
<!-- ... -->
128+
</div>
129+
{/snippet}
130+
</Accordion.Trigger>
131+
132+
<style>
133+
.my-custom-trigger {
134+
background-color: #3182ce;
135+
color: #fff;
136+
}
137+
</style>
138+
```
89139

90-
```svelte {4,8} /wrapperProps/
140+
## Floating Components
141+
142+
Floating content components (tooltips, popovers, dropdowns, etc.) require special handling due to their positioning requirements.
143+
144+
### Required Structure
145+
146+
For floating components, you must use a two-level structure:
147+
148+
1. An **outer wrapper element** with `{...wrapperProps}`
149+
2. An **inner content element** with `{...props}`
150+
151+
```svelte
91152
<Popover.Content>
92153
{#snippet child({ wrapperProps, props, open })}
93154
{#if open}
@@ -101,13 +162,14 @@ When implementing floating content, you must:
101162
</Popover.Content>
102163
```
103164

104-
### Important Considerations
165+
### Important Rules for Floating Content
105166

106-
- The wrapper element must remain unstyled as its positioning is managed internally by Floating UI
107-
- The `wrapperProps` contain computed positioning data essential for proper floating behavior
108-
- Modifying the wrapper element's styles or structure may break positioning calculations
167+
- The wrapper element with `{...wrapperProps}` must remain **unstyled**
168+
- Positioning is handled by the wrapper element; styling goes on the inner content element
169+
- The `open` parameter lets you conditionally render the content, triggering Svelte transitions
170+
- Always maintain this two-level structure to ensure proper positioning and behavior
109171

110-
### Affected Components
172+
### Components Requiring Wrapper Elements
111173

112174
The following components require a wrapper element:
113175

@@ -120,3 +182,59 @@ The following components require a wrapper element:
120182
- `Popover.Content`
121183
- `Select.Content`
122184
- `Tooltip.Content`
185+
186+
## Examples
187+
188+
### Basic Custom Element
189+
190+
```svelte
191+
<Collapsible.Trigger>
192+
{#snippet child({ props })}
193+
<button {...props}>
194+
<Icon name="star" />
195+
<span>Favorite</span>
196+
</button>
197+
{/snippet}
198+
</Collapsible.Trigger>
199+
```
200+
201+
### With Svelte Transitions
202+
203+
```svelte
204+
<Dialog.Content>
205+
{#snippet child({ props, open })}
206+
{#if open}
207+
<div {...props} transition:scale={{ start: 0.95 }}>
208+
Dialog content with a scale transition
209+
</div>
210+
{/if}
211+
{/snippet}
212+
</Dialog.Content>
213+
```
214+
215+
### Floating Element Example
216+
217+
```svelte
218+
<Tooltip.Content>
219+
{#snippet child({ wrapperProps, props, open })}
220+
{#if open}
221+
<div {...wrapperProps}>
222+
<div {...props} transition:fade>Custom tooltip content</div>
223+
</div>
224+
{/if}
225+
{/snippet}
226+
</Tooltip.Content>
227+
```
228+
229+
## Common Pitfalls
230+
231+
- **Missing props spread**: Always include `{...props}` on your custom element
232+
- **Styling the wrapper**: Never style the wrapper element in floating components
233+
- **Direct children**: When using child, other children outside the snippet are ignored
234+
- **Missing structure**: For floating elements, forgetting the two-level structure will break positioning
235+
236+
## Related Resources
237+
238+
- [mergeProps](/docs/utilities/merge-props) Utility
239+
- [Styling Guide](/docs/styling)
240+
- [Transitions Guide](/docs/transitions)

docs/content/components/accordion.md

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,48 @@ description: Organizes content into collapsible sections, allowing users to focu
1818

1919
## Overview
2020

21-
The Accordion component is a versatile UI element designed to manage large amounts of content by organizing it into collapsible sections. It's ideal for FAQs, settings panels, or any interface where users need to focus on specific information without being overwhelmed by visual clutter.
21+
The Accordion component is a versatile UI element designed to organize content into collapsible sections, helping users focus on specific information without being overwhelmed by visual clutter.
2222

23-
## Key Features
24-
25-
- **Single or Multiple Mode**: Toggle between allowing one open section or multiple sections at once.
26-
- **Accessible by Default**: Built-in ARIA attributes and keyboard navigation support.
27-
- **Smooth Transitions**: Leverage CSS variables or Svelte transitions for animated expansions.
28-
- **Flexible State**: Use uncontrolled defaults or take full control with bound values.
29-
30-
## Structure
31-
32-
The Accordion is a compound component made up of several sub-components:
33-
34-
- **`Root`**: Wraps all items and manages state.
35-
- **`Item`**: Represents a single collapsible section.
36-
- **`Header`**: Displays the title or label.
37-
- **`Trigger`**: The clickable element that toggles the content.
38-
- **`Content`**: The collapsible body of each item.
39-
40-
Here's a basic example:
23+
## Quick Start
4124

4225
```svelte
4326
<script lang="ts">
4427
import { Accordion } from "bits-ui";
4528
</script>
4629
47-
<Accordion.Root>
30+
<Accordion.Root type="single">
4831
<Accordion.Item value="item-1">
4932
<Accordion.Header>
50-
<Accordion.Trigger>Section 1</Accordion.Trigger>
33+
<Accordion.Trigger>Item 1 Title</Accordion.Trigger>
34+
</Accordion.Header>
35+
<Accordion.Content>This is the collapsible content for this section.</Accordion.Content>
36+
</Accordion.Item>
37+
<Accordion.Item value="item-2">
38+
<Accordion.Header>
39+
<Accordion.Trigger>Item 2 Title</Accordion.Trigger>
5140
</Accordion.Header>
52-
<Accordion.Content>Content for section 1 goes here.</Accordion.Content>
41+
<Accordion.Content>This is the collapsible content for this section.</Accordion.Content>
5342
</Accordion.Item>
5443
</Accordion.Root>
5544
```
5645

46+
## Key Features
47+
48+
- **Single or Multiple Mode**: Toggle between allowing one open section or multiple sections at once.
49+
- **Accessible by Default**: Built-in ARIA attributes and keyboard navigation support.
50+
- **Smooth Transitions**: Leverage CSS variables or Svelte transitions for animated open/close effects.
51+
- **Flexible State**: Use uncontrolled defaults or take full control with bound values.
52+
53+
## Structure
54+
55+
The Accordion is a compound component made up of several parts:
56+
57+
- `Accordion.Root`: Container that manages overall state
58+
- `Accordion.Item`: Individual collapsible section
59+
- `Accordion.Header`: Contains the visible heading
60+
- `Accordion.Trigger`: The clickable element that toggles content visibility
61+
- `Accordion.Content`: The collapsible body content
62+
5763
## Reusable Components
5864

5965
To streamline usage in larger applications, create custom wrapper components for repeated patterns. Below is an example of a reusable `MyAccordionItem` and `MyAccordion`.

0 commit comments

Comments
 (0)