Skip to content

Commit 49f10e3

Browse files
authored
feat(sheet): improve customizability with size prop, actions slot, and layout fixes (#302)
* feat(sheet): improve customizability with size prop, actions slot, and layout fixes - Add `size` prop to Sheet.Content for width variations (sm/md/lg/xl/full) - Add `actions` prop to Sheet.Header for action buttons beside the title - Increase Sheet.Title font size to text-lg for better hierarchy - Move close button from absolute positioning into Header normal flow * fix(sheet): align actions with close button and fix example padding - Wrap actions and close button in a shared items-center container - Fix example body padding from '1rem 0' to '0 1rem' to match Header/Footer * Format files * feat(sheet)!: redesign Sheet.Header layout with close button on left and action prop * test(sheet): add full size variant test and document in changeset/JSDoc * fix(core): refine sheet header layout * Update example * feat(sheet): add directional close icons and hover style - Replace XIcon with directional chevrons (ChevronsRight/Left/Up/Down) based on sheet side - Add hover background style (bg-accent) to close button matching sidebar trigger style - Remove opacity-based hover in favor of color-based transition
1 parent 98270c5 commit 49f10e3

6 files changed

Lines changed: 464 additions & 65 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"@tailor-platform/app-shell": minor
3+
---
4+
5+
Improve Sheet component customizability:
6+
7+
- Add `size` prop to `Sheet.Content` for width variations (`sm`, `md`, `lg`, `xl`, `full`)
8+
- Add `action` prop to `Sheet.Header` for placing action buttons to the right of the title
9+
- Increase `Sheet.Title` font size to `text-lg` for better visibility
10+
- Move close button to the left side of the header, with title inline next to it
11+
- Header now has a bottom border, Footer has a top border for visual separation
12+
13+
```tsx
14+
<Sheet.Root side="right">
15+
<Sheet.Trigger render={<Button />}>Open</Sheet.Trigger>
16+
<Sheet.Content size="lg">
17+
<Sheet.Header action={<Button size="sm">Save</Button>}>
18+
<Sheet.Title>Edit Record</Sheet.Title>
19+
</Sheet.Header>
20+
</Sheet.Content>
21+
</Sheet.Root>
22+
```

docs/components/sheet.md

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,56 @@ import { Sheet } from "@tailor-platform/app-shell";
5959

6060
### Sheet.Content Props
6161

62-
Accepts `className` and all standard HTML `<div>` props.
62+
| Prop | Type | Default | Description |
63+
| ----------- | ---------------------------------------- | ------- | --------------------------------------------------------------- |
64+
| `size` | `"sm" \| "md" \| "lg" \| "xl" \| "full"` | `"sm"` | Max-width of the sheet panel (applies to left/right sides only) |
65+
| `className` | `string` | - | Additional CSS classes |
66+
67+
Also accepts all standard HTML `<div>` props.
68+
69+
### Sheet.Header Props
70+
71+
| Prop | Type | Default | Description |
72+
| -------- | ----------------- | ------- | ------------------------------------------------------- |
73+
| `action` | `React.ReactNode` | - | Action buttons displayed to the right of the title area |
74+
75+
The header renders a close button on the left, title in the center, and action buttons on the right.
6376

6477
### Sheet.Trigger / Sheet.Close Props
6578

6679
Accept a `render` prop for custom element rendering (Base UI render pattern), plus standard button props.
6780

81+
## Sizes
82+
83+
Control the width of the sheet panel using the `size` prop on `Sheet.Content`. This applies to `left` and `right` side sheets.
84+
85+
```tsx
86+
<Sheet.Root side="right">
87+
<Sheet.Trigger render={<Button />}>Open Small</Sheet.Trigger>
88+
<Sheet.Content size="sm">…</Sheet.Content> {/* 384px (24rem) — default */}
89+
</Sheet.Root>
90+
91+
<Sheet.Root side="right">
92+
<Sheet.Trigger render={<Button />}>Open Medium</Sheet.Trigger>
93+
<Sheet.Content size="md">…</Sheet.Content> {/* 512px (32rem) */}
94+
</Sheet.Root>
95+
96+
<Sheet.Root side="right">
97+
<Sheet.Trigger render={<Button />}>Open Large</Sheet.Trigger>
98+
<Sheet.Content size="lg">…</Sheet.Content> {/* 720px (45rem) */}
99+
</Sheet.Root>
100+
101+
<Sheet.Root side="right">
102+
<Sheet.Trigger render={<Button />}>Open XL</Sheet.Trigger>
103+
<Sheet.Content size="xl">…</Sheet.Content> {/* 960px (60rem) */}
104+
</Sheet.Root>
105+
106+
<Sheet.Root side="right">
107+
<Sheet.Trigger render={<Button />}>Open Full</Sheet.Trigger>
108+
<Sheet.Content size="full">…</Sheet.Content> {/* 100% width */}
109+
</Sheet.Root>
110+
```
111+
68112
## Sides
69113

70114
```tsx
@@ -96,6 +140,24 @@ const [open, setOpen] = useState(false);
96140

97141
## Examples
98142

143+
### Header with Actions
144+
145+
Place action buttons (e.g., save, edit) to the right of the title using the `action` prop on `Sheet.Header`.
146+
147+
```tsx
148+
<Sheet.Root side="right">
149+
<Sheet.Trigger render={<Button />}>Edit Record</Sheet.Trigger>
150+
<Sheet.Content size="lg">
151+
<Sheet.Header action={<Button size="sm">Save</Button>}>
152+
<Sheet.Title>Edit Customer</Sheet.Title>
153+
</Sheet.Header>
154+
<div className="astw:p-4">
155+
<Input placeholder="Customer name" />
156+
</div>
157+
</Sheet.Content>
158+
</Sheet.Root>
159+
```
160+
99161
### Filter Panel
100162

101163
```tsx
@@ -151,7 +213,7 @@ function FilterPanel() {
151213
- Sheet title is announced by screen readers via `Sheet.Title`
152214
- Focus is trapped inside the sheet while open
153215
- Pressing `Escape` closes the sheet
154-
- The close button is always present inside `Sheet.Content`
216+
- The close button is rendered inside `Sheet.Header` in the normal document flow
155217

156218
## Related Components
157219

examples/nextjs-app/src/modules/pages/primitives-demo.tsx

Lines changed: 154 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,13 @@ export const primitiveComponentsDemoResource = defineResource({
9595
<Card.Root>
9696
<Card.Header title="Badge" />
9797
<Card.Content>
98-
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
98+
<div
99+
style={{
100+
display: "flex",
101+
flexDirection: "column",
102+
gap: "1rem",
103+
}}
104+
>
99105
<div>
100106
<div style={labelStyle}>Solid</div>
101107
<div style={rowStyle}>
@@ -187,50 +193,153 @@ export const primitiveComponentsDemoResource = defineResource({
187193
<Card.Root>
188194
<Card.Header title="Sheet" />
189195
<Card.Content>
190-
<div style={rowStyle}>
191-
<Sheet.Root side="right">
192-
<Sheet.Trigger render={<Button variant="outline" />}>
193-
Open Sheet (Right)
194-
</Sheet.Trigger>
195-
<Sheet.Content>
196-
<Sheet.Header>
197-
<Sheet.Title>Sheet Title</Sheet.Title>
198-
<Sheet.Description>This sheet slides in from the right.</Sheet.Description>
199-
</Sheet.Header>
200-
<div style={{ padding: "1rem 0" }}>Sheet content goes here.</div>
201-
<Sheet.Footer>
202-
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
203-
</Sheet.Footer>
204-
</Sheet.Content>
205-
</Sheet.Root>
206-
<Sheet.Root side="left">
207-
<Sheet.Trigger render={<Button variant="outline" />}>
208-
Open Sheet (Left)
209-
</Sheet.Trigger>
210-
<Sheet.Content>
211-
<Sheet.Header>
212-
<Sheet.Title>Left Sheet</Sheet.Title>
213-
<Sheet.Description>This sheet slides in from the left.</Sheet.Description>
214-
</Sheet.Header>
215-
<Sheet.Footer>
216-
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
217-
</Sheet.Footer>
218-
</Sheet.Content>
219-
</Sheet.Root>
220-
<Sheet.Root side="bottom">
221-
<Sheet.Trigger render={<Button variant="outline" />}>
222-
Open Sheet (Bottom)
223-
</Sheet.Trigger>
224-
<Sheet.Content>
225-
<Sheet.Header>
226-
<Sheet.Title>Bottom Sheet</Sheet.Title>
227-
<Sheet.Description>This sheet slides in from the bottom.</Sheet.Description>
228-
</Sheet.Header>
229-
<Sheet.Footer>
230-
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
231-
</Sheet.Footer>
232-
</Sheet.Content>
233-
</Sheet.Root>
196+
<div style={{ display: "flex", gap: "2rem", flexWrap: "wrap" }}>
197+
<div>
198+
<div style={labelStyle}>Side</div>
199+
<div style={rowStyle}>
200+
<Sheet.Root side="right">
201+
<Sheet.Trigger render={<Button variant="outline" />}>Right</Sheet.Trigger>
202+
<Sheet.Content>
203+
<Sheet.Header>
204+
<Sheet.Title>Sheet Title</Sheet.Title>
205+
<Sheet.Description>
206+
This sheet slides in from the right.
207+
</Sheet.Description>
208+
</Sheet.Header>
209+
<div style={{ padding: "1rem" }}>Sheet content goes here.</div>
210+
<Sheet.Footer>
211+
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
212+
</Sheet.Footer>
213+
</Sheet.Content>
214+
</Sheet.Root>
215+
<Sheet.Root side="left">
216+
<Sheet.Trigger render={<Button variant="outline" />}>Left</Sheet.Trigger>
217+
<Sheet.Content>
218+
<Sheet.Header>
219+
<Sheet.Title>Left Sheet</Sheet.Title>
220+
<Sheet.Description>This sheet slides in from the left.</Sheet.Description>
221+
</Sheet.Header>
222+
<Sheet.Footer>
223+
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
224+
</Sheet.Footer>
225+
</Sheet.Content>
226+
</Sheet.Root>
227+
<Sheet.Root side="bottom">
228+
<Sheet.Trigger render={<Button variant="outline" />}>Bottom</Sheet.Trigger>
229+
<Sheet.Content>
230+
<Sheet.Header>
231+
<Sheet.Title>Bottom Sheet</Sheet.Title>
232+
<Sheet.Description>
233+
This sheet slides in from the bottom.
234+
</Sheet.Description>
235+
</Sheet.Header>
236+
<Sheet.Footer>
237+
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
238+
</Sheet.Footer>
239+
</Sheet.Content>
240+
</Sheet.Root>
241+
</div>
242+
</div>
243+
<div>
244+
<div style={labelStyle}>Size</div>
245+
<div style={rowStyle}>
246+
<Sheet.Root side="right">
247+
<Sheet.Trigger render={<Button variant="outline" />}>
248+
sm (default)
249+
</Sheet.Trigger>
250+
<Sheet.Content size="sm">
251+
<Sheet.Header>
252+
<Sheet.Title>Small Sheet</Sheet.Title>
253+
<Sheet.Description>384px (24rem)</Sheet.Description>
254+
</Sheet.Header>
255+
<div style={{ padding: "1rem" }}>Sheet content goes here.</div>
256+
<Sheet.Footer>
257+
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
258+
</Sheet.Footer>
259+
</Sheet.Content>
260+
</Sheet.Root>
261+
<Sheet.Root side="right">
262+
<Sheet.Trigger render={<Button variant="outline" />}>md</Sheet.Trigger>
263+
<Sheet.Content size="md">
264+
<Sheet.Header>
265+
<Sheet.Title>Medium Sheet</Sheet.Title>
266+
<Sheet.Description>512px (32rem)</Sheet.Description>
267+
</Sheet.Header>
268+
<div style={{ padding: "1rem" }}>Sheet content goes here.</div>
269+
<Sheet.Footer>
270+
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
271+
</Sheet.Footer>
272+
</Sheet.Content>
273+
</Sheet.Root>
274+
<Sheet.Root side="right">
275+
<Sheet.Trigger render={<Button variant="outline" />}>lg</Sheet.Trigger>
276+
<Sheet.Content size="lg">
277+
<Sheet.Header>
278+
<Sheet.Title>Large Sheet</Sheet.Title>
279+
<Sheet.Description>720px (45rem)</Sheet.Description>
280+
</Sheet.Header>
281+
<div style={{ padding: "1rem" }}>Sheet content goes here.</div>
282+
<Sheet.Footer>
283+
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
284+
</Sheet.Footer>
285+
</Sheet.Content>
286+
</Sheet.Root>
287+
<Sheet.Root side="right">
288+
<Sheet.Trigger render={<Button variant="outline" />}>xl</Sheet.Trigger>
289+
<Sheet.Content size="xl">
290+
<Sheet.Header>
291+
<Sheet.Title>Extra Large Sheet</Sheet.Title>
292+
<Sheet.Description>960px (60rem)</Sheet.Description>
293+
</Sheet.Header>
294+
<div style={{ padding: "1rem" }}>Sheet content goes here.</div>
295+
<Sheet.Footer>
296+
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
297+
</Sheet.Footer>
298+
</Sheet.Content>
299+
</Sheet.Root>
300+
<Sheet.Root side="right">
301+
<Sheet.Trigger render={<Button variant="outline" />}>full</Sheet.Trigger>
302+
<Sheet.Content size="full">
303+
<Sheet.Header>
304+
<Sheet.Title>Full Screen Sheet</Sheet.Title>
305+
<Sheet.Description>Full-width overlay</Sheet.Description>
306+
</Sheet.Header>
307+
<div style={{ padding: "1rem" }}>Sheet content goes here.</div>
308+
<Sheet.Footer>
309+
<Sheet.Close render={<Button variant="outline" />}>Close</Sheet.Close>
310+
</Sheet.Footer>
311+
</Sheet.Content>
312+
</Sheet.Root>
313+
</div>
314+
</div>
315+
<div>
316+
<div style={labelStyle}>Actions</div>
317+
<div style={rowStyle}>
318+
<Sheet.Root side="right">
319+
<Sheet.Trigger render={<Button variant="outline" />}>
320+
Multiple actions
321+
</Sheet.Trigger>
322+
<Sheet.Content size="md">
323+
<Sheet.Header
324+
action={
325+
<>
326+
<Button size="sm" variant="outline">
327+
Reset
328+
</Button>
329+
<Button size="sm">Save</Button>
330+
</>
331+
}
332+
>
333+
<Sheet.Title>Edit Record</Sheet.Title>
334+
<Sheet.Description>
335+
Modify the details of the selected record.
336+
</Sheet.Description>
337+
</Sheet.Header>
338+
<div style={{ padding: "1rem" }}>Form content goes here.</div>
339+
</Sheet.Content>
340+
</Sheet.Root>
341+
</div>
342+
</div>
234343
</div>
235344
</Card.Content>
236345
</Card.Root>

0 commit comments

Comments
 (0)