@thorprovider/create-storefront 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -0
- package/bin/install.js +116 -0
- package/commands/sf-add-view.md +21 -0
- package/commands/sf-init.md +16 -0
- package/commands/sf-theme.md +15 -0
- package/commands/sf-view.md +20 -0
- package/package.json +40 -0
- package/recipes/archetype.schema.json +39 -0
- package/recipes/archetypes.json +148 -0
- package/recipes/recipe.schema.json +59 -0
- package/recipes/recipes.json +90 -0
- package/recipes/sections.json +46 -0
- package/recipes/validate.mjs +190 -0
- package/skills/building-storefronts/SKILL.md +178 -0
- package/skills/building-storefronts/references/frontend-integration.md +229 -0
- package/skills/json-render-core/SKILL.md +291 -0
- package/skills/json-render-next/SKILL.md +194 -0
- package/skills/json-render-react/SKILL.md +298 -0
- package/skills/json-render-remotion/SKILL.md +111 -0
- package/skills/json-render-shadcn/SKILL.md +159 -0
- package/skills/json-render-solid/SKILL.md +204 -0
- package/skills/nextjs-shadcn/SKILL.md +303 -0
- package/skills/nextjs-shadcn/references/architecture.md +499 -0
- package/skills/nextjs-shadcn/references/project-setup.md +127 -0
- package/skills/nextjs-shadcn/references/shadcn-platform.md +258 -0
- package/skills/nextjs-shadcn/references/sidebar.md +274 -0
- package/skills/nextjs-shadcn/references/styling.md +555 -0
- package/skills/sf-scaffold/SKILL.md +118 -0
- package/skills/sf-theme-gen/SKILL.md +44 -0
- package/skills/sf-view-gen/SKILL.md +94 -0
- package/skills/shadcn-component-discovery/SKILL.md +273 -0
- package/skills/shadcn-component-discovery/references/registries.md +226 -0
- package/skills/shadcn-theming/SKILL.md +104 -0
- package/skills/shadcn-theming/references/templates/theme-setup.md +109 -0
- package/skills/shadcn-theming/references/theming-guide.md +90 -0
- package/skills/storefront-best-practices/SKILL.md +421 -0
- package/skills/storefront-best-practices/reference/components/breadcrumbs.md +123 -0
- package/skills/storefront-best-practices/reference/components/cart-popup.md +189 -0
- package/skills/storefront-best-practices/reference/components/country-selector.md +298 -0
- package/skills/storefront-best-practices/reference/components/footer.md +112 -0
- package/skills/storefront-best-practices/reference/components/hero.md +241 -0
- package/skills/storefront-best-practices/reference/components/megamenu.md +239 -0
- package/skills/storefront-best-practices/reference/components/navbar.md +397 -0
- package/skills/storefront-best-practices/reference/components/popups.md +221 -0
- package/skills/storefront-best-practices/reference/components/product-card.md +125 -0
- package/skills/storefront-best-practices/reference/components/product-reviews.md +217 -0
- package/skills/storefront-best-practices/reference/components/product-slider.md +174 -0
- package/skills/storefront-best-practices/reference/components/search.md +101 -0
- package/skills/storefront-best-practices/reference/connecting-to-backend.md +391 -0
- package/skills/storefront-best-practices/reference/design.md +388 -0
- package/skills/storefront-best-practices/reference/features/promotions.md +307 -0
- package/skills/storefront-best-practices/reference/features/wishlist.md +230 -0
- package/skills/storefront-best-practices/reference/layouts/account.md +380 -0
- package/skills/storefront-best-practices/reference/layouts/cart.md +316 -0
- package/skills/storefront-best-practices/reference/layouts/checkout.md +486 -0
- package/skills/storefront-best-practices/reference/layouts/home-page.md +264 -0
- package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +231 -0
- package/skills/storefront-best-practices/reference/layouts/product-details.md +527 -0
- package/skills/storefront-best-practices/reference/layouts/product-listing.md +520 -0
- package/skills/storefront-best-practices/reference/layouts/static-pages.md +356 -0
- package/skills/storefront-best-practices/reference/medusa.md +307 -0
- package/skills/storefront-best-practices/reference/mobile-responsiveness.md +183 -0
- package/skills/storefront-best-practices/reference/seo.md +195 -0
- package/templates/app/app/[[...slug]]/page.tsx +17 -0
- package/templates/app/app/[[...slug]]/renderer.tsx +10 -0
- package/templates/app/app/globals.css +101 -0
- package/templates/app/app/layout.tsx +35 -0
- package/templates/app/lib/__STOREFRONT__/catalog.ts +132 -0
- package/templates/app/lib/__STOREFRONT__/handlers.ts +33 -0
- package/templates/app/lib/__STOREFRONT__/registry.tsx +134 -0
- package/templates/app/lib/__STOREFRONT__/runtime.ts +25 -0
- package/templates/app/lib/__STOREFRONT__/spec/home.ts +62 -0
- package/templates/app/lib/__STOREFRONT__/spec/index.ts +59 -0
- package/templates/app/lib/__STOREFRONT__/spec/types.ts +14 -0
- package/templates/app/lib/__STOREFRONT__/state.ts +35 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# shadcn Platform
|
|
2
|
+
|
|
3
|
+
The parts of shadcn that are not individual components: which primitive base to
|
|
4
|
+
build on, the CLI verbs worth knowing, and the CSS-level systems (typeset,
|
|
5
|
+
shimmer, scroll-fade).
|
|
6
|
+
|
|
7
|
+
## Choosing a base (do this first)
|
|
8
|
+
|
|
9
|
+
shadcn components ship on three primitive libraries: Base UI, Radix, and React
|
|
10
|
+
Aria. **Base UI is the default.** The choice is per-project and set at `init` —
|
|
11
|
+
components installed later inherit it.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bunx --bun shadcn@latest init --template next --base base # Base UI (default)
|
|
15
|
+
bunx --bun shadcn@latest init --template next --base radix # Radix (legacy projects)
|
|
16
|
+
bunx --bun shadcn@latest init --template next --base aria # React Aria
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| Base | Pick it when |
|
|
20
|
+
|------|-------------|
|
|
21
|
+
| `base` | New projects. Default since July 2026, most actively developed, gets new components first. |
|
|
22
|
+
| `radix` | Existing codebase already on Radix, or a dependency expects Radix primitives. |
|
|
23
|
+
| `aria` | Accessibility/interaction requirements beyond the defaults — React Aria's behavior hooks. |
|
|
24
|
+
|
|
25
|
+
**Why this matters for generated code:** the same component name has different
|
|
26
|
+
props and sub-components per base. Docs are base-scoped too —
|
|
27
|
+
`ui.shadcn.com/docs/components/base/sidebar` vs `.../radix/sidebar`. Never write
|
|
28
|
+
component code from memory without knowing the project's base.
|
|
29
|
+
|
|
30
|
+
The difference that bites most often: **Base UI composes through a `render`
|
|
31
|
+
prop, Radix through `asChild`.**
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
// Base UI (default) — element goes in render, children stay as children
|
|
35
|
+
<SidebarMenuButton render={<Link href="/inbox" />}>
|
|
36
|
+
<Inbox />
|
|
37
|
+
<span>Inbox</span>
|
|
38
|
+
</SidebarMenuButton>
|
|
39
|
+
|
|
40
|
+
// Radix — element wraps the children
|
|
41
|
+
<SidebarMenuButton asChild>
|
|
42
|
+
<Link href="/inbox">
|
|
43
|
+
<Inbox />
|
|
44
|
+
<span>Inbox</span>
|
|
45
|
+
</Link>
|
|
46
|
+
</SidebarMenuButton>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Read the base from `components.json` (or `shadcn info --json`) before editing an
|
|
50
|
+
existing project.
|
|
51
|
+
|
|
52
|
+
`migrate radix` does **not** switch a project from Radix to Base UI — it rewrites
|
|
53
|
+
`@radix-ui/react-*` imports to the single `radix-ui` package. Radix → Base UI is
|
|
54
|
+
a component-at-a-time migration driven by the official shadcn skill:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bunx --bun skills add shadcn/ui
|
|
58
|
+
# then ask the agent: "migrate accordion to base-ui"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Both libraries stay installed while you work, each component lands in its own
|
|
62
|
+
commit, and every run writes a report to `.migration/<component>.md`.
|
|
63
|
+
|
|
64
|
+
## CLI verbs beyond `add`
|
|
65
|
+
|
|
66
|
+
`add` and `init` are the familiar ones. These four are what make the CLI useful
|
|
67
|
+
to an agent:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
shadcn info --json # project config: framework, base, tailwind, aliases, installed components
|
|
71
|
+
shadcn docs button # API reference for a component, resolved to THIS project's base
|
|
72
|
+
shadcn docs button --json # machine-readable, for piping into context
|
|
73
|
+
shadcn view button card # inspect registry item source before installing
|
|
74
|
+
shadcn search @shadcn -q chart # search a registry namespace
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Prefer `shadcn docs <component>` over recalling props from memory or fetching
|
|
78
|
+
`llms.txt`** — it resolves against the project's actual base and version. Use
|
|
79
|
+
`shadcn view` before `add` when you are unsure what a registry item pulls in.
|
|
80
|
+
|
|
81
|
+
Other verbs: `add <component> --diff` (upstream changes to an installed
|
|
82
|
+
component — the standalone `diff` command is deprecated), `apply <preset>`
|
|
83
|
+
(apply a preset to an existing project; `--only theme,font` for just those
|
|
84
|
+
parts), `preset decode|resolve|url|open` (inspect a preset code), `build`
|
|
85
|
+
(generate registry JSON), `migrate` (`icons`, `rtl`, `radix`), `eject` (inline
|
|
86
|
+
`shadcn/tailwind.css` and drop the `shadcn` dependency).
|
|
87
|
+
|
|
88
|
+
### `migrate icons`
|
|
89
|
+
|
|
90
|
+
Swapping icon libraries across a whole project is a single command:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
bunx --bun shadcn@latest migrate icons --from lucide --to phosphor
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## The official shadcn skill
|
|
97
|
+
|
|
98
|
+
shadcn publishes its own agent skill, which injects live project config
|
|
99
|
+
(`shadcn info --json`) plus the full CLI and registry reference:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
bunx --bun skills add shadcn/ui
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
It activates when the project has a `components.json`. It covers CLI mechanics
|
|
106
|
+
and registry authoring — **this skill covers project conventions, architecture,
|
|
107
|
+
and Next.js integration instead.** Install both; don't duplicate CLI reference
|
|
108
|
+
material here.
|
|
109
|
+
|
|
110
|
+
There is also an MCP server for registry search and install from within the
|
|
111
|
+
editor — wire it into Claude Code with
|
|
112
|
+
`bunx --bun shadcn@latest mcp init --client claude`.
|
|
113
|
+
|
|
114
|
+
## Typeset — styling rendered markdown
|
|
115
|
+
|
|
116
|
+
One CSS file you own that styles headings, paragraphs, lists, tables and code
|
|
117
|
+
inside a wrapper class. Replaces hand-written `prose`-style overrides and
|
|
118
|
+
per-context markdown CSS.
|
|
119
|
+
|
|
120
|
+
**Use it for:** chat message bodies, docs pages, blog posts, LLM-streamed
|
|
121
|
+
markdown — anywhere you render HTML you did not author.
|
|
122
|
+
|
|
123
|
+
Typeset is **not** part of `init` and has no `add` command. Generate the file in
|
|
124
|
+
the builder at [ui.shadcn.com/typeset](https://ui.shadcn.com/typeset), drop it
|
|
125
|
+
next to your main CSS, and import it after Tailwind:
|
|
126
|
+
|
|
127
|
+
```css
|
|
128
|
+
/* app/globals.css */
|
|
129
|
+
@import "tailwindcss";
|
|
130
|
+
@import "./typeset.css";
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Three variables drive the whole rhythm; everything else derives from them:
|
|
134
|
+
|
|
135
|
+
```css
|
|
136
|
+
.typeset {
|
|
137
|
+
--typeset-font-body: inherit;
|
|
138
|
+
--typeset-font-heading: var(--font-heading);
|
|
139
|
+
--typeset-font-mono: var(--font-mono);
|
|
140
|
+
--typeset-size: 1em; /* base text size */
|
|
141
|
+
--typeset-leading: 1.75; /* line-height */
|
|
142
|
+
--typeset-flow: 1.25em; /* space between blocks */
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Define one preset per context and apply both classes:
|
|
147
|
+
|
|
148
|
+
```css
|
|
149
|
+
.typeset-docs {
|
|
150
|
+
--typeset-size: 15px;
|
|
151
|
+
--typeset-leading: 1.75;
|
|
152
|
+
--typeset-flow: 1.25em;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.typeset-chat {
|
|
156
|
+
--typeset-leading: 1.6;
|
|
157
|
+
--typeset-flow: 1em; /* tighter — chat bubbles are short */
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
<div className="typeset typeset-chat">
|
|
163
|
+
<Response>{message}</Response>
|
|
164
|
+
</div>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
It is container-aware (sizes to its container, not just the viewport) and
|
|
168
|
+
**streaming-stable**: appending a new block does not restyle blocks already
|
|
169
|
+
rendered above it. That property is the reason to prefer it over ad-hoc CSS in
|
|
170
|
+
streaming chat UIs.
|
|
171
|
+
|
|
172
|
+
## Shimmer — loading and processing text
|
|
173
|
+
|
|
174
|
+
CSS-only animated sweep across text. No component, no JS.
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
<p className="shimmer text-muted-foreground">Generating response…</p>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
| Class | Effect |
|
|
181
|
+
|-------|--------|
|
|
182
|
+
| `shimmer-color-<color>` | Highlight color, e.g. `shimmer-color-blue-500/60` |
|
|
183
|
+
| `shimmer-duration-<ms>` | Sweep speed (default 2000) |
|
|
184
|
+
| `shimmer-spread-<n>` | Width of the highlight band |
|
|
185
|
+
| `shimmer-angle-<deg>` | Tilt (default 20) |
|
|
186
|
+
| `shimmer-once` | Single sweep instead of looping |
|
|
187
|
+
| `shimmer-reverse` | Reverse direction |
|
|
188
|
+
|
|
189
|
+
Adapts to the element's text color, brightens in dark mode, respects
|
|
190
|
+
`prefers-reduced-motion` and RTL automatically.
|
|
191
|
+
|
|
192
|
+
**Use shimmer for indeterminate text states** ("Thinking…", "Searching…") and
|
|
193
|
+
`Skeleton` for layout placeholders with known shape. Don't stack both.
|
|
194
|
+
|
|
195
|
+
## Scroll-fade — soft scroll container edges
|
|
196
|
+
|
|
197
|
+
Masks the content itself at the edges of a scroll container rather than
|
|
198
|
+
overlaying a gradient, so it works on any background.
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
<div className="scroll-fade overflow-y-auto">{/* content */}</div>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
| Class | Effect |
|
|
205
|
+
|-------|--------|
|
|
206
|
+
| `scroll-fade` / `scroll-fade-y` | Vertical |
|
|
207
|
+
| `scroll-fade-x` | Horizontal |
|
|
208
|
+
| `scroll-fade-t` / `-b` / `-l` / `-r` | Single edge |
|
|
209
|
+
| `scroll-fade-s` / `-e` | Start/end edge (RTL-aware) |
|
|
210
|
+
| `scroll-fade-<n>` | Fade depth on the spacing scale |
|
|
211
|
+
| `scroll-fade-none` | Disable |
|
|
212
|
+
|
|
213
|
+
Scroll-aware: the top edge stays crisp until you scroll away from it.
|
|
214
|
+
|
|
215
|
+
Both `shimmer` and `scroll-fade` come from `shadcn/tailwind.css`, which `init`
|
|
216
|
+
wires up. In a project that skipped it:
|
|
217
|
+
|
|
218
|
+
```css
|
|
219
|
+
@import "tailwindcss";
|
|
220
|
+
@import "shadcn/tailwind.css";
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## RTL support
|
|
224
|
+
|
|
225
|
+
Sidebar and the CSS utilities are RTL-aware. Opt in at init with `--rtl`,
|
|
226
|
+
retrofit an existing project with `migrate rtl`, and pass `dir` on the `Sidebar`
|
|
227
|
+
component. Prefer logical utilities (`ms-`/`me-`, `scroll-fade-s/-e`)
|
|
228
|
+
over physical ones (`ml-`/`mr-`) in any project that might need it.
|
|
229
|
+
|
|
230
|
+
## Package imports (alias alternative)
|
|
231
|
+
|
|
232
|
+
Projects may use Node package imports instead of `tsconfig.json` paths:
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
// package.json
|
|
236
|
+
{ "imports": { "#components/*": "./src/components/*.tsx", "#lib/*": "./src/lib/*.ts" } }
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { Button } from "#components/ui/button";
|
|
241
|
+
import { cn } from "#lib/utils";
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Requires `moduleResolution: "bundler"` and `resolvePackageJsonImports: true`, and
|
|
245
|
+
the matching `aliases` block in `components.json`. The CLI rewrites imports to
|
|
246
|
+
whichever alias style is configured.
|
|
247
|
+
|
|
248
|
+
**Default to `@/` for new projects.** In an existing project, read
|
|
249
|
+
`components.json` and follow what is already there — never mix both styles.
|
|
250
|
+
|
|
251
|
+
## Registries
|
|
252
|
+
|
|
253
|
+
Beyond `@shadcn`, the CLI resolves namespaced registries (`@acme/button`),
|
|
254
|
+
including private and GitHub-hosted ones. If you author a registry with more
|
|
255
|
+
than a few hundred items, implement
|
|
256
|
+
[dynamic search](https://ui.shadcn.com/docs/registry/dynamic-search) so
|
|
257
|
+
`shadcn search` filters server-side instead of downloading the whole catalog.
|
|
258
|
+
Not needed for consuming registries.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# Sidebar
|
|
2
|
+
|
|
3
|
+
shadcn/ui sidebar with nested layouts for dashboard applications.
|
|
4
|
+
|
|
5
|
+
## Start from a block, not from scratch
|
|
6
|
+
|
|
7
|
+
Before hand-writing an `AppSidebar`, check whether a block already matches the
|
|
8
|
+
shape you need. A block installs a complete, working sidebar you then edit —
|
|
9
|
+
faster and less error-prone than assembling sub-components by hand.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bunx --bun shadcn@latest add sidebar-07
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Block | Shape |
|
|
16
|
+
|-------|-------|
|
|
17
|
+
| `sidebar-01` | Navigation grouped by section (simplest) |
|
|
18
|
+
| `sidebar-02` | Collapsible sections |
|
|
19
|
+
| `sidebar-03` | Submenus |
|
|
20
|
+
| `sidebar-04` | Floating sidebar with submenus |
|
|
21
|
+
| `sidebar-05` | Collapsible submenus |
|
|
22
|
+
| `sidebar-06` | Submenus as dropdowns |
|
|
23
|
+
| `sidebar-07` | Collapses to icons — the common dashboard default |
|
|
24
|
+
| `sidebar-08` | Inset sidebar with secondary navigation |
|
|
25
|
+
| `sidebar-09` | Collapsible nested sidebars |
|
|
26
|
+
| `sidebar-10` | Sidebar in a popover |
|
|
27
|
+
| `sidebar-11` | Collapsible file tree |
|
|
28
|
+
| `sidebar-12` | Sidebar with a calendar |
|
|
29
|
+
| `sidebar-13` | Sidebar in a dialog |
|
|
30
|
+
| `sidebar-14` | Sidebar on the right |
|
|
31
|
+
| `sidebar-15` | Left and right sidebar |
|
|
32
|
+
| `sidebar-16` | Sticky site header |
|
|
33
|
+
|
|
34
|
+
Preview them at [ui.shadcn.com/blocks/sidebar](https://ui.shadcn.com/blocks/sidebar).
|
|
35
|
+
Use `shadcn view sidebar-07` to read the source before installing.
|
|
36
|
+
|
|
37
|
+
The patterns below are for when no block fits, or when adapting one.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bunx --bun shadcn@latest add sidebar
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The sidebar API differs between Base UI, Radix and React Aria. Check the
|
|
46
|
+
project's base (`shadcn info --json`) and read
|
|
47
|
+
`shadcn docs sidebar` rather than assuming — the examples below target Base UI,
|
|
48
|
+
the current default. The difference that shows up in every example: Base UI
|
|
49
|
+
composes through a `render` prop (`<SidebarMenuButton render={<Link href="/" />}>`
|
|
50
|
+
with the icon and label as children), where Radix wraps the children in `asChild`.
|
|
51
|
+
|
|
52
|
+
## Layout Pattern
|
|
53
|
+
|
|
54
|
+
Use nested layouts with SidebarProvider for persistent sidebar state:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
app/
|
|
58
|
+
├── (dashboard)/ # Route group for sidebar pages
|
|
59
|
+
│ ├── layout.tsx # SidebarProvider + AppSidebar
|
|
60
|
+
│ ├── page.tsx # Dashboard home
|
|
61
|
+
│ ├── settings/
|
|
62
|
+
│ │ └── page.tsx
|
|
63
|
+
│ └── components/ # Route-specific components
|
|
64
|
+
├── (public)/ # Public routes (no sidebar)
|
|
65
|
+
│ └── login/
|
|
66
|
+
└── layout.tsx # Root layout
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Dashboard Layout
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// app/(dashboard)/layout.tsx
|
|
73
|
+
import { AppSidebar } from "@/components/layout/app-sidebar"
|
|
74
|
+
import {
|
|
75
|
+
SidebarInset,
|
|
76
|
+
SidebarProvider,
|
|
77
|
+
} from "@/components/ui/sidebar"
|
|
78
|
+
|
|
79
|
+
export default function DashboardLayout({
|
|
80
|
+
children,
|
|
81
|
+
}: {
|
|
82
|
+
children: React.ReactNode
|
|
83
|
+
}) {
|
|
84
|
+
return (
|
|
85
|
+
<SidebarProvider>
|
|
86
|
+
<AppSidebar />
|
|
87
|
+
<SidebarInset>{children}</SidebarInset>
|
|
88
|
+
</SidebarProvider>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Page Component
|
|
94
|
+
|
|
95
|
+
Keep pages clean - content only, no layout chrome:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// app/(dashboard)/page.tsx
|
|
99
|
+
import { DocumentWorkspace } from "@/components/workspace/document-workspace"
|
|
100
|
+
import { Suspense } from "react"
|
|
101
|
+
|
|
102
|
+
export default function DashboardPage() {
|
|
103
|
+
return (
|
|
104
|
+
<Suspense fallback={<DashboardSkeleton />}>
|
|
105
|
+
<DocumentWorkspace />
|
|
106
|
+
</Suspense>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## AppSidebar Component
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
// components/layout/app-sidebar.tsx
|
|
115
|
+
import Link from "next/link"
|
|
116
|
+
import {
|
|
117
|
+
Sidebar,
|
|
118
|
+
SidebarContent,
|
|
119
|
+
SidebarFooter,
|
|
120
|
+
SidebarGroup,
|
|
121
|
+
SidebarGroupContent,
|
|
122
|
+
SidebarGroupLabel,
|
|
123
|
+
SidebarHeader,
|
|
124
|
+
SidebarMenu,
|
|
125
|
+
SidebarMenuButton,
|
|
126
|
+
SidebarMenuItem,
|
|
127
|
+
SidebarRail,
|
|
128
|
+
SidebarSeparator,
|
|
129
|
+
} from "@/components/ui/sidebar"
|
|
130
|
+
import { NAV_GROUPS, FOOTER_NAV_ITEMS } from "./nav"
|
|
131
|
+
|
|
132
|
+
export function AppSidebar() {
|
|
133
|
+
return (
|
|
134
|
+
<Sidebar variant="inset" collapsible="icon">
|
|
135
|
+
<SidebarHeader>
|
|
136
|
+
<SidebarMenu>
|
|
137
|
+
<SidebarMenuItem>
|
|
138
|
+
<SidebarMenuButton
|
|
139
|
+
size="lg"
|
|
140
|
+
render={<Link href="/" className="flex items-center gap-3" />}
|
|
141
|
+
>
|
|
142
|
+
<Logo className="size-8" />
|
|
143
|
+
<span className="text-base font-semibold">App Name</span>
|
|
144
|
+
</SidebarMenuButton>
|
|
145
|
+
</SidebarMenuItem>
|
|
146
|
+
</SidebarMenu>
|
|
147
|
+
</SidebarHeader>
|
|
148
|
+
|
|
149
|
+
<SidebarContent>
|
|
150
|
+
{NAV_GROUPS.map((group, index) => (
|
|
151
|
+
<div key={group.title}>
|
|
152
|
+
<SidebarGroup>
|
|
153
|
+
<SidebarGroupLabel>{group.title}</SidebarGroupLabel>
|
|
154
|
+
<SidebarGroupContent>
|
|
155
|
+
<SidebarMenu>
|
|
156
|
+
{group.items.map((item) => (
|
|
157
|
+
<SidebarMenuItem key={item.title}>
|
|
158
|
+
<SidebarMenuButton render={<Link href={item.href} />}>
|
|
159
|
+
<item.icon />
|
|
160
|
+
<span>{item.title}</span>
|
|
161
|
+
</SidebarMenuButton>
|
|
162
|
+
</SidebarMenuItem>
|
|
163
|
+
))}
|
|
164
|
+
</SidebarMenu>
|
|
165
|
+
</SidebarGroupContent>
|
|
166
|
+
</SidebarGroup>
|
|
167
|
+
{index < NAV_GROUPS.length - 1 && <SidebarSeparator />}
|
|
168
|
+
</div>
|
|
169
|
+
))}
|
|
170
|
+
</SidebarContent>
|
|
171
|
+
|
|
172
|
+
<SidebarFooter>
|
|
173
|
+
<SidebarSeparator />
|
|
174
|
+
<SidebarMenu>
|
|
175
|
+
{FOOTER_NAV_ITEMS.map((item) => (
|
|
176
|
+
<SidebarMenuItem key={item.title}>
|
|
177
|
+
<SidebarMenuButton render={<Link href={item.href} />}>
|
|
178
|
+
<item.icon />
|
|
179
|
+
<span>{item.title}</span>
|
|
180
|
+
</SidebarMenuButton>
|
|
181
|
+
</SidebarMenuItem>
|
|
182
|
+
))}
|
|
183
|
+
</SidebarMenu>
|
|
184
|
+
</SidebarFooter>
|
|
185
|
+
|
|
186
|
+
<SidebarRail />
|
|
187
|
+
</Sidebar>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Navigation Config
|
|
193
|
+
|
|
194
|
+
Separate navigation data from component:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
// components/layout/nav.ts
|
|
198
|
+
import { Home, Settings, Users, HelpCircle } from "lucide-react"
|
|
199
|
+
import type { LucideIcon } from "lucide-react"
|
|
200
|
+
|
|
201
|
+
interface NavItem {
|
|
202
|
+
title: string
|
|
203
|
+
href: string
|
|
204
|
+
icon: LucideIcon
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
interface NavGroup {
|
|
208
|
+
title: string
|
|
209
|
+
items: NavItem[]
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export const NAV_GROUPS: NavGroup[] = [
|
|
213
|
+
{
|
|
214
|
+
title: "Main",
|
|
215
|
+
items: [
|
|
216
|
+
{ title: "Dashboard", href: "/", icon: Home },
|
|
217
|
+
{ title: "Users", href: "/users", icon: Users },
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
export const FOOTER_NAV_ITEMS: NavItem[] = [
|
|
223
|
+
{ title: "Settings", href: "/settings", icon: Settings },
|
|
224
|
+
{ title: "Help", href: "/help", icon: HelpCircle },
|
|
225
|
+
]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Sidebar Variants
|
|
229
|
+
|
|
230
|
+
| Variant | Description |
|
|
231
|
+
|---------|-------------|
|
|
232
|
+
| `sidebar` | Standard sidebar (default) |
|
|
233
|
+
| `inset` | Sidebar with padding, content area has rounded corners |
|
|
234
|
+
| `floating` | Sidebar floats over content |
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
<Sidebar variant="inset" collapsible="icon">
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Collapsible Options
|
|
241
|
+
|
|
242
|
+
| Option | Behavior |
|
|
243
|
+
|--------|----------|
|
|
244
|
+
| `icon` | Collapses to icon-only rail |
|
|
245
|
+
| `offcanvas` | Slides completely off-screen |
|
|
246
|
+
| `none` | Not collapsible |
|
|
247
|
+
|
|
248
|
+
## RTL
|
|
249
|
+
|
|
250
|
+
`Sidebar` takes a `dir` prop and positions via `data-side` attributes, so
|
|
251
|
+
right-to-left layouts work without JS conditionals — `SidebarTrigger` flips its
|
|
252
|
+
icon automatically. Opt in project-wide at `init` with `--rtl`, or retrofit with
|
|
253
|
+
`shadcn migrate rtl`. In any project that might need RTL, use logical utilities
|
|
254
|
+
(`ms-`/`me-`, `ps-`/`pe-`) instead of `ml-`/`mr-` in sidebar markup.
|
|
255
|
+
|
|
256
|
+
## Persisting open state
|
|
257
|
+
|
|
258
|
+
`SidebarProvider` takes `defaultOpen` (uncontrolled) or `open` / `onOpenChange`
|
|
259
|
+
(controlled); width comes from the `--sidebar-width` CSS variable. To keep the
|
|
260
|
+
sidebar's state across reloads without a hydration mismatch, read a cookie in the
|
|
261
|
+
layout Server Component and pass it to `defaultOpen` — don't read it client-side
|
|
262
|
+
in an effect.
|
|
263
|
+
|
|
264
|
+
For custom triggers, `useSidebar()` exposes `toggleSidebar`, `isMobile`, and the
|
|
265
|
+
mobile-specific `openMobile` / `setOpenMobile`.
|
|
266
|
+
|
|
267
|
+
## File Structure
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
components/
|
|
271
|
+
└── layout/
|
|
272
|
+
├── app-sidebar.tsx # Sidebar component
|
|
273
|
+
└── nav.ts # Navigation config
|
|
274
|
+
```
|