@aleph-alpha/ui-library 1.14.0 → 1.15.0

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.
@@ -0,0 +1,13 @@
1
+ # Components
2
+
3
+ Explore the design system components: view variants, try them in isolation, and copy code for your app.
4
+
5
+ ---
6
+
7
+ ## Live playground (Storybook)
8
+
9
+ The easiest way to browse and try components and see detailed documentation on each component is Storybook. You can switch props, see states and sizes, see provided examples, etc.
10
+
11
+ **[Open Storybook →](https://dev.aleph-alpha.de/ui-library)**
12
+
13
+ More soon.
@@ -0,0 +1,11 @@
1
+ # Contributing
2
+
3
+ ---
4
+
5
+ ## Contribution Guidelines (Workflows & Diagrams)
6
+
7
+ Coming soon.
8
+
9
+ ## Support (Mattermost, FAQ, Office Hours)
10
+
11
+ Coming soon.
@@ -0,0 +1,16 @@
1
+ # External Links
2
+
3
+ Quick access to Design System resources.
4
+
5
+ ---
6
+
7
+ ## Coming soon
8
+
9
+ This page will provide:
10
+
11
+ | Resource | Description | Link |
12
+ |----------|-------------|------|
13
+ | 📘 **Storybook (Live Code)** | Live code and component playground | [Storybook](https://dev.aleph-alpha.de/ui-library) |
14
+ | 🎨 **Figma (Design System)** | Design system files | [Figma](https://www.figma.com/design/SrZQ19QVN1sQkuAMnMC746/AA26--WIP-?node-id=65-520&m=dev) |
15
+ | 🐙 **GitHub Repository** | Source code and issues | [GitHub](https://github.com/Aleph-Alpha/frontend-hub/tree/main/packages/ui-library) |
16
+
@@ -0,0 +1,25 @@
1
+ # Foundations
2
+
3
+ *Links to Storybook.*
4
+
5
+ ---
6
+
7
+ ## Color & Dark Mode
8
+
9
+ Coming soon.
10
+
11
+ ## Spacing & Grid
12
+
13
+ Coming soon.
14
+
15
+ ## Typography
16
+
17
+ Coming soon.
18
+
19
+ ## Elevation & Surfaces
20
+
21
+ Coming soon.
22
+
23
+ ## Iconography
24
+
25
+ Coming soon.
@@ -0,0 +1,17 @@
1
+ # Getting Started for Designers
2
+
3
+ ---
4
+
5
+ ## Figma Access & Library Links
6
+
7
+ Figma components and design files:
8
+
9
+ - **[AA Design System Components (Figma)](https://www.figma.com/design/SrZQ19QVN1sQkuAMnMC746/AA26--WIP-?node-id=580-9181&t=2HPn1uKWpFKHnrMR-1)** — component library and design system.
10
+
11
+ ## Using Design Tokens in Figma
12
+
13
+ Coming soon.
14
+
15
+ ## Link to Contribution Model
16
+
17
+ Coming soon.
@@ -0,0 +1,5 @@
1
+ # Overview
2
+
3
+ Coming soon.
4
+
5
+
@@ -0,0 +1,230 @@
1
+ # Quick Start
2
+
3
+ Get the UI library integrated into your project in under 5 minutes.
4
+
5
+ **Designers:** For Figma, design tokens, and the contribution model, see [Getting Started for Designers](getting-started-designers.md).
6
+
7
+ !!! note "Prerequisites"
8
+ - **Node.js** (>=20.19.0)
9
+ - **pnpm** or **npm**
10
+ - A **Vite + Vue 3** project
11
+ - **MCP (optional):** An editor that supports MCP (Cursor, VS Code, or Claude Code) if you want AI-assisted component discovery
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ **1. Add the library and peer dependencies**
18
+
19
+ ```bash
20
+ pnpm add @aleph-alpha/ui-library
21
+ pnpm add -D unocss @unocss/preset-wind4 @unocss/reset unocss-preset-animations unocss-preset-shadcn
22
+ ```
23
+
24
+ <details>
25
+ <summary>Using npm?</summary>
26
+
27
+ ```bash
28
+ npm install @aleph-alpha/ui-library
29
+ npm install -D unocss @unocss/preset-wind4 @unocss/reset unocss-preset-animations unocss-preset-shadcn
30
+ ```
31
+ </details>
32
+
33
+ !!! warning "Use `@unocss/preset-wind4`, not `preset-wind3`"
34
+ The library requires **preset-wind4**. Using preset-wind3 will break hover states and colors because wind3 injects opacity variables (`/ var(--un-bg-opacity)`) that conflict with the library's oklch token values.
35
+
36
+ ---
37
+
38
+ ## Setup and configuration
39
+
40
+ ### 2. UnoCSS config
41
+
42
+ Create or update `uno.config.ts` in your project root:
43
+
44
+ ```typescript
45
+ // uno.config.ts
46
+ import { defineConfig } from 'unocss'
47
+ import {
48
+ getUiLibraryContentConfig,
49
+ presetUiLibraryThemeFromTokens,
50
+ presetUiLibraryUtils,
51
+ } from '@aleph-alpha/ui-library/config'
52
+ import presetWind from '@unocss/preset-wind4'
53
+ import presetAnimations from 'unocss-preset-animations'
54
+ import presetShadcn from 'unocss-preset-shadcn'
55
+
56
+ export default defineConfig({
57
+ ...getUiLibraryContentConfig(),
58
+ presets: [
59
+ presetWind(),
60
+ presetAnimations(),
61
+ presetShadcn({ color: 'neutral', darkSelector: 'html.dark' }),
62
+ // Must come AFTER presetShadcn — bundles token CSS vars, colors, and typography
63
+ presetUiLibraryThemeFromTokens(),
64
+ presetUiLibraryUtils(),
65
+ ],
66
+ })
67
+ ```
68
+
69
+ **What each part does:**
70
+
71
+ | Config | Purpose |
72
+ |--------|---------|
73
+ | `getUiLibraryContentConfig()` | Scans `node_modules/@aleph-alpha/ui-library` for class usage so UnoCSS generates the right utilities |
74
+ | `presetWind()` | Tailwind-compatible utility classes (must be **wind4**, not wind3) |
75
+ | `presetAnimations()` | CSS animation utilities for transitions |
76
+ | `presetShadcn()` | Base shadcn CSS variables and color utilities |
77
+ | `presetUiLibraryThemeFromTokens()` | Token preset — must come **after** `presetShadcn`. Bundles design token CSS variables (light/dark), token-based theme colors (`bg-background-surface-secondary`, etc.), semantic color overrides (`background`, `foreground`, `modal`, etc.), and typography classes (`heading-48-bold`, `body-14-normal`, `label-12-medium`, etc.) |
78
+ | `presetUiLibraryUtils()` | Utility rules required by library components — Tailwind v4 CSS variable syntax (`w-(--var)`, `h-(--var)`, etc.) not yet supported by preset-wind4 |
79
+
80
+ ### 3. Vite config
81
+
82
+ Ensure your `vite.config.ts` includes both the Vue plugin and the UnoCSS Vite plugin:
83
+
84
+ ```typescript
85
+ // vite.config.ts
86
+ import { defineConfig } from 'vite'
87
+ import vue from '@vitejs/plugin-vue'
88
+ import UnoCSS from 'unocss/vite'
89
+
90
+ export default defineConfig({
91
+ plugins: [vue(), UnoCSS()],
92
+ })
93
+ ```
94
+
95
+ ### 4. Global styles and app entry
96
+
97
+ In your main entry (e.g. `main.ts`), import the UnoCSS reset and the generated CSS. These imports must run before your app mounts so that component styles apply everywhere.
98
+
99
+ ```typescript
100
+ // main.ts
101
+ import '@unocss/reset/tailwind.css'
102
+ import 'virtual:uno.css'
103
+
104
+ import { createApp } from 'vue'
105
+ import App from './App.vue'
106
+
107
+ createApp(App).mount('#app')
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Test it
113
+
114
+ Use a component to confirm everything works:
115
+
116
+ ```vue
117
+ <script setup lang="ts">
118
+ import { UiButton, UiBadge } from '@aleph-alpha/ui-library'
119
+ </script>
120
+
121
+ <template>
122
+ <div class="min-h-screen bg-background text-foreground p-8">
123
+ <UiButton>Click me</UiButton>
124
+ <UiBadge variant="secondary">New</UiBadge>
125
+ </div>
126
+ </template>
127
+ ```
128
+
129
+ Run your dev server (e.g. `pnpm dev`) and open the app. The button and badge should render with the correct styles and colors matching [Storybook](https://dev.aleph-alpha.de/ui-library).
130
+
131
+ **If colors look wrong or hover states don't work:**
132
+
133
+ - Verify you're using `@unocss/preset-wind4` (not wind3) — check your `package.json`
134
+ - Verify `presetUiLibraryThemeFromTokens()` comes **after** `presetShadcn()` in the presets array
135
+ - Clear the Vite cache: `rm -rf node_modules/.vite && pnpm dev`
136
+
137
+ ---
138
+
139
+ ## Dark mode
140
+
141
+ The library supports dark mode via the `html.dark` class. Toggle it on the document element:
142
+
143
+ ```typescript
144
+ // Toggle dark mode
145
+ document.documentElement.classList.toggle('dark')
146
+ ```
147
+
148
+ Both `presetShadcn` and `presetUiLibraryThemeFromTokens` default to `darkSelector: 'html.dark'`, so CSS variables switch correctly out of the box.
149
+
150
+ ---
151
+
152
+ ## Config exports reference
153
+
154
+ All config functions are exported from `@aleph-alpha/ui-library/config`:
155
+
156
+ | Export | Description |
157
+ |--------|-------------|
158
+ | `getUiLibraryContentConfig()` | UnoCSS content/pipeline config to scan library classes |
159
+ | `presetUiLibraryThemeFromTokens(options?)` | **Token preset.** Bundles token CSS variables, theme colors, semantic overrides, and typography classes. Options: `darkSelector` (default: `'html.dark'`), `includeAllTokens` (default: `true`), `extend`, `theme` |
160
+ | `presetUiLibraryUtils()` | Utility rules required by library components (`w-(--var)`, `h-(--var)`, etc.) |
161
+ | `presetUiLibraryTheme(theme, options?)` | Low-level preset with manually provided theme variables (for custom themes) |
162
+ | `getUiLibraryThemeFromTokens()` | Returns the raw theme object derived from `tokens.json` |
163
+ | `getUiLibraryTokenVars()` | Returns all token-based CSS variables for light and dark modes |
164
+ | `getUiLibraryTokenColors()` | Returns UnoCSS theme colors config for all token-based colors (included automatically by `presetUiLibraryThemeFromTokens`) |
165
+ | `getUiLibraryTypographyShortcuts()` | Returns typography class definitions as `[className, cssProperties]` tuples (included automatically by `presetUiLibraryThemeFromTokens`) |
166
+ | `defineThemeConfig(config)` | Type-safe helper for external theme config files |
167
+
168
+ ---
169
+
170
+ ## MCP Integration
171
+
172
+ Enable AI assistants (Cursor, Claude Code, etc.) to discover and use UI library components.
173
+
174
+ !!! warning "Run from your project directory"
175
+ Run the installer from the **project that uses the UI library** (your app root). It detects the root from the current working directory and writes `.mcp.json` or `.cursor/mcp.json` there. Running it from the wrong folder will use the wrong root or fail.
176
+
177
+ **1. Run the installer**
178
+
179
+ ```bash
180
+ npx @aleph-alpha/lib-mcp mcp-install
181
+ ```
182
+
183
+ **2. For Cursor**: add the Cursor-specific config:
184
+
185
+ ```bash
186
+ npx @aleph-alpha/lib-mcp mcp-install --cursor
187
+ ```
188
+
189
+ **3. Restart your editor** so it picks up the MCP server.
190
+
191
+ **Available tools:**
192
+
193
+ | Tool | Description |
194
+ |------|-------------|
195
+ | `list_components` | Browse all components, optionally filter by name/category/description |
196
+ | `get_component_docs` | Get full documentation for a component including props, examples, and usage |
197
+ | `get_setup_guide` | Get this setup guide (so AI assistants can configure projects correctly) |
198
+
199
+ ### Or add the server manually
200
+
201
+ If you prefer not to run the installer, add the MCP server yourself.
202
+
203
+ 1. **Where to put the config** (see your editor docs):
204
+
205
+ | Editor | Config location / docs |
206
+ |--------|------------------------|
207
+ | **Cursor** | [Using mcp.json](https://cursor.com/docs/context/mcp#using-mcpjson) — use `.cursor/mcp.json` in your project or `~/.cursor/mcp.json` globally |
208
+ | **VS Code** | [Add and manage MCP servers](https://code.visualstudio.com/docs/copilot/customization/mcp-servers#_configure-the-mcpjson-file) — use `.vscode/mcp.json` (workspace) or user config |
209
+ | **Claude Code** | [Add a local stdio server](https://code.claude.com/docs/en/mcp#option-3-add-a-local-stdio-server) — use `.mcp.json` in your project (project scope) |
210
+
211
+ 2. **Add this server** to your `mcpServers`:
212
+
213
+ ```json
214
+ {
215
+ "mcpServers": {
216
+ "ui-library": {
217
+ "command": "npx",
218
+ "args": ["-y", "@aleph-alpha/lib-mcp", "serve"]
219
+ }
220
+ }
221
+ }
222
+ ```
223
+
224
+ 3. **Restart your editor** so it picks up the MCP server.
225
+
226
+ ---
227
+
228
+ ## Contribute
229
+
230
+ To contribute to the UI library (guidelines, support, and how to get involved), see [Contributing](contributing.md).
@@ -0,0 +1,15 @@
1
+ # Standards & Guidelines
2
+
3
+ ---
4
+
5
+ ## Design Principles
6
+
7
+ Coming soon.
8
+
9
+ ## Dos and Don'ts (Visual Gallery)
10
+
11
+ Coming soon.
12
+
13
+ ## ♿ Accessibility (A11y) Standards
14
+
15
+ Coming soon.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aleph-alpha/ui-library",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "dist/system/lib.js",
@@ -22,7 +22,8 @@
22
22
  "config.js",
23
23
  "config.d.ts",
24
24
  "tailwind.preset.js",
25
- "tokens.json"
25
+ "tokens.json",
26
+ "docs/public-docs"
26
27
  ],
27
28
  "description": "Next-gen UI Library for Vue.js projects.",
28
29
  "author": "Aleph Alpha GmbH",
@@ -31,7 +31,7 @@ const meta: Meta<typeof UiCalendar> = {
31
31
  weekStartsOn: {
32
32
  control: 'select',
33
33
  options: [0, 1, 2, 3, 4, 5, 6],
34
- description: 'The day of the week to start on. 0 = Sunday, 1 = Monday, etc.',
34
+ description: 'The day of the week to start on. 0 is the locale default first day.',
35
35
  },
36
36
  numberOfMonths: {
37
37
  control: { type: 'number', min: 1, max: 3 },
@@ -392,7 +392,6 @@ const date = ref<DateValue>()
392
392
  <UiCalendar
393
393
  v-model="date"
394
394
  locale="de"
395
- :week-starts-on="1"
396
395
  weekday-format="short"
397
396
  class="rounded-md border shadow-sm"
398
397
  />
@@ -405,7 +404,6 @@ const date = ref<DateValue>()
405
404
  export const GermanLocale: Story = {
406
405
  args: {
407
406
  locale: 'de',
408
- weekStartsOn: 1,
409
407
  weekdayFormat: 'short',
410
408
  },
411
409
  render: (args) => ({
@@ -419,7 +417,6 @@ export const GermanLocale: Story = {
419
417
  v-model="date"
420
418
  :default-placeholder="placeholder"
421
419
  :locale="args.locale"
422
- :week-starts-on="args.weekStartsOn"
423
420
  :weekday-format="args.weekdayFormat"
424
421
  :disabled="args.disabled"
425
422
  class="rounded-md border shadow-sm"
@@ -105,7 +105,7 @@ export interface UiCalendarProps {
105
105
 
106
106
  /**
107
107
  * The day of the week to start on.
108
- * @default 0 (Sunday)
108
+ * @default 0
109
109
  */
110
110
  weekStartsOn?: UiCalendarWeekStartsOn;
111
111
 
@@ -30,7 +30,7 @@ const meta: Meta<typeof UiRangeCalendar> = {
30
30
  weekStartsOn: {
31
31
  control: 'select',
32
32
  options: [0, 1, 2, 3, 4, 5, 6],
33
- description: 'The day of the week to start on. 0 = Sunday, 1 = Monday, etc.',
33
+ description: 'The day of the week to start on. 0 is the locale default first day.',
34
34
  },
35
35
  numberOfMonths: {
36
36
  control: { type: 'number', min: 1, max: 3 },
@@ -337,7 +337,7 @@ const dateRange = ref<DateRange>()
337
337
  </script>
338
338
 
339
339
  <template>
340
- <UiRangeCalendar v-model="dateRange" locale="de" :week-starts-on="1" weekday-format="short" class="rounded-md border shadow-sm" />
340
+ <UiRangeCalendar v-model="dateRange" locale="de" weekday-format="short" class="rounded-md border shadow-sm" />
341
341
  </template>`;
342
342
 
343
343
  /**
@@ -347,7 +347,6 @@ const dateRange = ref<DateRange>()
347
347
  export const GermanLocale: Story = {
348
348
  args: {
349
349
  locale: 'de',
350
- weekStartsOn: 1,
351
350
  weekdayFormat: 'short',
352
351
  },
353
352
  render: (args) => ({
@@ -361,7 +360,6 @@ export const GermanLocale: Story = {
361
360
  v-model="dateRange"
362
361
  :default-placeholder="placeholder"
363
362
  :locale="args.locale"
364
- :week-starts-on="args.weekStartsOn"
365
363
  :weekday-format="args.weekdayFormat"
366
364
  :disabled="args.disabled"
367
365
  class="rounded-md border shadow-sm"
@@ -90,7 +90,7 @@ export interface UiRangeCalendarProps {
90
90
 
91
91
  /**
92
92
  * The day of the week to start on.
93
- * @default 0 (Sunday)
93
+ * @default 0
94
94
  */
95
95
  weekStartsOn?: UiRangeCalendarWeekStartsOn;
96
96
 
@@ -1,4 +1,4 @@
1
- import { ref, computed } from 'vue';
1
+ import { ref, computed, defineComponent } from 'vue';
2
2
  import type { Meta, StoryObj } from '@storybook/vue3-vite';
3
3
  import {
4
4
  UiSidebar,
@@ -25,6 +25,7 @@ import {
25
25
  UiSidebarRail,
26
26
  UiSidebarSeparator,
27
27
  UiSidebarTrigger,
28
+ useSidebar,
28
29
  } from '.';
29
30
  import { CollapsibleRoot, CollapsibleContent, CollapsibleTrigger } from 'reka-ui';
30
31
  import {
@@ -1008,3 +1009,180 @@ export const WithSkeleton: Story = {
1008
1009
  },
1009
1010
  },
1010
1011
  };
1012
+
1013
+ const programmaticControlTemplateSource = `<script setup lang="ts">
1014
+ import {
1015
+ UiSidebar,
1016
+ UiSidebarContent,
1017
+ UiSidebarGroup,
1018
+ UiSidebarGroupContent,
1019
+ UiSidebarGroupLabel,
1020
+ UiSidebarHeaderTrigger,
1021
+ UiSidebarInset,
1022
+ UiSidebarMenu,
1023
+ UiSidebarMenuButton,
1024
+ UiSidebarMenuItem,
1025
+ UiSidebarProvider,
1026
+ UiSidebarRail,
1027
+ useSidebar,
1028
+ } from '@aleph-alpha/ui-library'
1029
+ import { Bot, Database, BarChart3, MessageSquare, Command } from 'lucide-vue-next'
1030
+
1031
+ const items = [
1032
+ { title: 'Playground', icon: Bot },
1033
+ { title: 'Models', icon: Database },
1034
+ { title: 'Evaluations', icon: BarChart3 },
1035
+ { title: 'Conversations', icon: MessageSquare },
1036
+ ]
1037
+
1038
+ // useSidebar() must be called inside <UiSidebarProvider>.
1039
+ // Use a child component or <script setup> in a component nested within the provider.
1040
+ </script>
1041
+
1042
+ <template>
1043
+ <UiSidebarProvider>
1044
+ <!-- Wrap content in a child component that calls useSidebar() -->
1045
+ <SidebarLayout :items="items" />
1046
+ </UiSidebarProvider>
1047
+ </template>
1048
+
1049
+ <!-- Child component (e.g. SidebarLayout.vue) -->
1050
+ <script setup lang="ts">
1051
+ import { useSidebar } from '@aleph-alpha/ui-library'
1052
+
1053
+ const { state, open, setOpen, toggleSidebar } = useSidebar()
1054
+ </script>
1055
+
1056
+ <template>
1057
+ <UiSidebar><!-- ... --></UiSidebar>
1058
+ <UiSidebarInset>
1059
+ <div class="p-6 space-y-4">
1060
+ <p>Sidebar is <strong>{{ state }}</strong></p>
1061
+ <button @click="toggleSidebar()">Toggle</button>
1062
+ <button @click="setOpen(true)">Open</button>
1063
+ <button @click="setOpen(false)">Close</button>
1064
+ </div>
1065
+ </UiSidebarInset>
1066
+ </template>`;
1067
+
1068
+ /**
1069
+ * Programmatic sidebar control via the `useSidebar()` composable. The composable must be called
1070
+ * inside a component rendered within `<UiSidebarProvider>`. It returns reactive state (`state`, `open`)
1071
+ * and methods (`toggleSidebar`, `setOpen`) to control the sidebar from anywhere in the provider tree.
1072
+ *
1073
+ * This is the recommended pattern for controlling the sidebar from outside — e.g. opening it
1074
+ * from a toolbar button, closing it after navigation, or syncing state with a store.
1075
+ */
1076
+ export const ProgrammaticControl: Story = {
1077
+ render: (args) => ({
1078
+ components: {
1079
+ UiSidebar,
1080
+ UiSidebarContent,
1081
+ UiSidebarGroup,
1082
+ UiSidebarGroupContent,
1083
+ UiSidebarGroupLabel,
1084
+ UiSidebarHeaderTrigger,
1085
+ UiSidebarInset,
1086
+ UiSidebarMenu,
1087
+ UiSidebarMenuButton,
1088
+ UiSidebarMenuItem,
1089
+ UiSidebarProvider,
1090
+ UiSidebarRail,
1091
+ Bot,
1092
+ Database,
1093
+ BarChart3,
1094
+ MessageSquare,
1095
+ Command,
1096
+ // Inline child component that uses useSidebar() — in real apps this would be a separate .vue file
1097
+ SidebarContent: defineComponent({
1098
+ setup() {
1099
+ const { state, toggleSidebar, setOpen } = useSidebar();
1100
+ return { state, toggleSidebar, setOpen };
1101
+ },
1102
+ template: `<slot :state="state" :toggleSidebar="toggleSidebar" :setOpen="setOpen" />`,
1103
+ }),
1104
+ },
1105
+ setup() {
1106
+ const items = [
1107
+ { title: 'Playground', icon: Bot },
1108
+ { title: 'Models', icon: Database },
1109
+ { title: 'Evaluations', icon: BarChart3 },
1110
+ { title: 'Conversations', icon: MessageSquare },
1111
+ ];
1112
+ return { args, items };
1113
+ },
1114
+ template: `
1115
+ <UiSidebarProvider>
1116
+ <UiSidebar :side="args.side" :variant="args.variant" :collapsible="args.collapsible">
1117
+ <UiSidebarHeaderTrigger>
1118
+ <template #logo>
1119
+ <div class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
1120
+ <Command class="size-4" />
1121
+ </div>
1122
+ </template>
1123
+ <span class="truncate font-semibold">Acme AI</span>
1124
+ <span class="truncate text-xs">Enterprise</span>
1125
+ </UiSidebarHeaderTrigger>
1126
+ <UiSidebarContent>
1127
+ <UiSidebarGroup>
1128
+ <UiSidebarGroupLabel>Platform</UiSidebarGroupLabel>
1129
+ <UiSidebarGroupContent>
1130
+ <UiSidebarMenu>
1131
+ <UiSidebarMenuItem v-for="item in items" :key="item.title">
1132
+ <UiSidebarMenuButton :tooltip="item.title">
1133
+ <component :is="item.icon" />
1134
+ <span>{{ item.title }}</span>
1135
+ </UiSidebarMenuButton>
1136
+ </UiSidebarMenuItem>
1137
+ </UiSidebarMenu>
1138
+ </UiSidebarGroupContent>
1139
+ </UiSidebarGroup>
1140
+ </UiSidebarContent>
1141
+ <UiSidebarRail />
1142
+ </UiSidebar>
1143
+ <UiSidebarInset>
1144
+ <SidebarContent v-slot="{ state, toggleSidebar, setOpen }">
1145
+ <div class="p-6">
1146
+ <div class="mb-6">
1147
+ <h2 class="text-lg font-semibold mb-1">Programmatic Control</h2>
1148
+ <p class="text-sm text-muted-foreground">
1149
+ Use <code class="rounded bg-muted px-1.5 py-0.5 text-xs">useSidebar()</code> inside the provider tree to control the sidebar from any component.
1150
+ </p>
1151
+ </div>
1152
+ <div class="flex items-center gap-3 mb-6">
1153
+ <span class="text-sm">State: <strong>{{ state }}</strong></span>
1154
+ </div>
1155
+ <div class="flex flex-wrap gap-2">
1156
+ <button
1157
+ class="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 border border-input bg-background hover:bg-accent hover:text-accent-foreground"
1158
+ @click="toggleSidebar()"
1159
+ >
1160
+ Toggle sidebar
1161
+ </button>
1162
+ <button
1163
+ class="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 border border-input bg-background hover:bg-accent hover:text-accent-foreground"
1164
+ @click="setOpen(true)"
1165
+ >
1166
+ Open
1167
+ </button>
1168
+ <button
1169
+ class="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 border border-input bg-background hover:bg-accent hover:text-accent-foreground"
1170
+ @click="setOpen(false)"
1171
+ >
1172
+ Close
1173
+ </button>
1174
+ </div>
1175
+ </div>
1176
+ </SidebarContent>
1177
+ </UiSidebarInset>
1178
+ </UiSidebarProvider>
1179
+ `,
1180
+ }),
1181
+ parameters: {
1182
+ docs: {
1183
+ source: {
1184
+ code: programmaticControlTemplateSource,
1185
+ },
1186
+ },
1187
+ },
1188
+ };
@@ -7,12 +7,10 @@
7
7
  });
8
8
 
9
9
  const props = defineProps<UiSidebarProviderProps>();
10
-
11
- const open = defineModel<boolean | undefined>('open');
12
10
  </script>
13
11
 
14
12
  <template>
15
- <ShadcnSidebarProvider v-model:open="open" :default-open="props.defaultOpen">
13
+ <ShadcnSidebarProvider :default-open="props.defaultOpen">
16
14
  <slot />
17
15
  </ShadcnSidebarProvider>
18
16
  </template>
@@ -12,20 +12,17 @@ export type UiSidebarMenuButtonSize = 'default' | 'sm' | 'lg';
12
12
 
13
13
  export type UiSidebarMenuSubButtonSize = 'sm' | 'md';
14
14
 
15
+ /**
16
+ * Wraps the sidebar and manages its open/closed state.
17
+ * Omit `default-open` to use cookie-based persistence (falls back to expanded).
18
+ */
15
19
  export interface UiSidebarProviderProps {
16
20
  /**
17
- * The default open state when uncontrolled.
18
- * If not provided, the core provider uses cookie-based persistence
21
+ * The initial open state of the sidebar.
22
+ * If not provided, the inner provider uses cookie-based persistence
19
23
  * (falls back to expanded if no cookie exists).
20
24
  */
21
25
  defaultOpen?: boolean;
22
-
23
- /**
24
- * The controlled open state of the sidebar.
25
- * - Omit for uncontrolled mode (component manages state internally)
26
- * - Use `v-model:open` for controlled mode
27
- */
28
- open?: boolean;
29
26
  }
30
27
 
31
28
  /**