@stacksjs/components 0.2.91 → 0.2.92

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,240 @@
1
+ /**
2
+ * Sidebar theme registry
3
+ *
4
+ * Every visual decision the sidebar makes lives here, expressed as utility
5
+ * classes, so a theme can be read top-to-bottom like a spec sheet.
6
+ *
7
+ * The flagship theme is `macos` — a faithful recreation of the sidebar in
8
+ * the latest macOS (Tahoe, macOS 26/27 "Liquid Glass"). Its metrics were
9
+ * measured from native apps (Mail, Music, Notes) at @2x:
10
+ *
11
+ * row pitch 32px = 30px row + 2px gap · highlight radius 9px
12
+ * label 13px · count 13px secondary gray · section header 11px semibold
13
+ * icon 17px in a fixed 22px slot · disclosure gutter 18px · child indent 16px
14
+ *
15
+ * Legacy themes (`workspace`, `desktop`, `solid`, `transparent`, `vibrancy`)
16
+ * are preserved verbatim from the previous variant maps. `tahoe` now aliases
17
+ * `macos` — it always meant "look like macOS", and now it actually does.
18
+ */
19
+
20
+ /** macOS system colors (light appearance) for tinting sidebar icons. */
21
+ export const macosColors = {
22
+ blue: '#0088ff',
23
+ red: '#ff383c',
24
+ pink: '#ff2d55',
25
+ orange: '#ff8d28',
26
+ yellow: '#ffcc00',
27
+ green: '#34c759',
28
+ teal: '#00c3d0',
29
+ cyan: '#00c0e8',
30
+ indigo: '#6155f5',
31
+ purple: '#cb30e0',
32
+ brown: '#ac7f5e',
33
+ gray: '#8e8e93',
34
+ } as const
35
+
36
+ export type MacosColor = keyof typeof macosColors
37
+
38
+ /** Class groups a sidebar theme provides. All values are utility classes. */
39
+ export interface SidebarTheme {
40
+ /** The <aside> pane itself: material, text color, borders. */
41
+ pane: string
42
+ /** Extra overlay layers rendered inside the pane (visual only). */
43
+ layers: {
44
+ /** Liquid Glass edge highlight — a bright rim that subtly shimmers. */
45
+ shimmerRim?: boolean
46
+ /** Soft tint gradient blended over the material. */
47
+ tint?: string
48
+ }
49
+ /** Scrollable content area between header and footer. */
50
+ scrollArea: string
51
+ /** Section header row (e.g. "Favorites", "iCloud"). */
52
+ sectionHeader: string
53
+ /** Chevron inside the section header (revealed on hover). */
54
+ sectionChevron: string
55
+ /** Wrapper around a section's items. */
56
+ sectionGroup: string
57
+ /** One navigation row, layout + typography. State styles live below. */
58
+ item: {
59
+ base: string
60
+ hover: string
61
+ active: string
62
+ pressed: string
63
+ disabled: string
64
+ /** Fixed-width leading slot the disclosure chevron sits in. */
65
+ disclosure: string
66
+ /** Chevron glyph itself (rotates 90° when expanded). */
67
+ chevron: string
68
+ /** Fixed-width slot that centers the 17px icon. */
69
+ iconSlot: string
70
+ icon: string
71
+ /** Thumbnail images (album art, avatars) rendered instead of an icon. */
72
+ image: string
73
+ label: string
74
+ /** Right-aligned unread/item count — plain gray text on macOS. */
75
+ count: string
76
+ /** Indentation added per nesting depth. */
77
+ indentPerLevel: number
78
+ }
79
+ }
80
+
81
+ const macos: SidebarTheme = {
82
+ pane: [
83
+ // Frosted sidebar material. In a Craft window with `webSidebarMaterial`
84
+ // the native vibrancy shows through; in a browser the backdrop blur
85
+ // approximates it over whatever sits behind the pane.
86
+ 'bg-white/55 dark:bg-[#1e1e23]/55',
87
+ 'backdrop-blur-[60px] backdrop-saturate-[1.8]',
88
+ 'text-black dark:text-white',
89
+ 'select-none',
90
+ ].join(' '),
91
+ layers: {
92
+ shimmerRim: true,
93
+ },
94
+ scrollArea: 'flex-1 overflow-y-auto overflow-x-hidden px-[10px] pb-[10px]',
95
+ sectionHeader: [
96
+ 'group/section flex w-full items-center',
97
+ 'pl-[8px] pr-[6px] pt-[16px] pb-[5px]',
98
+ 'text-[11px] font-semibold leading-[13px]',
99
+ 'text-[#3c3c43]/60 dark:text-[#ebebf5]/60',
100
+ ].join(' '),
101
+ sectionChevron: [
102
+ 'i-f7-chevron-down h-[11px] w-[11px] ml-auto',
103
+ 'text-[#3c3c43]/45 dark:text-[#ebebf5]/45',
104
+ 'opacity-0 group-hover/section:opacity-100 transition-opacity duration-150',
105
+ 'transition-transform duration-200',
106
+ ].join(' '),
107
+ sectionGroup: 'flex flex-col gap-[2px]',
108
+ item: {
109
+ base: [
110
+ 'flex w-full items-center',
111
+ 'h-[30px] rounded-[9px] pl-[4px] pr-[8px]',
112
+ 'text-[13px] leading-[16px] font-normal',
113
+ 'text-black dark:text-white',
114
+ 'transition-colors duration-150 ease-out',
115
+ 'cursor-default',
116
+ ].join(' '),
117
+ hover: 'hover:bg-black/4 dark:hover:bg-white/6',
118
+ active: 'bg-black/8 dark:bg-white/14',
119
+ pressed: 'active:bg-black/10 dark:active:bg-white/18',
120
+ disabled: 'opacity-40 pointer-events-none',
121
+ disclosure: 'flex h-[16px] w-[18px] shrink-0 items-center justify-center',
122
+ chevron: [
123
+ 'i-f7-chevron-right h-[11px] w-[11px]',
124
+ 'text-[#3c3c43]/55 dark:text-[#ebebf5]/55',
125
+ 'transition-transform duration-200 ease-out',
126
+ ].join(' '),
127
+ iconSlot: 'flex h-[20px] w-[22px] shrink-0 items-center justify-center mr-[7px]',
128
+ icon: 'h-[17px] w-[17px]',
129
+ image: 'h-[20px] w-[20px] rounded-[4px] object-cover shadow-sm',
130
+ label: 'flex-1 truncate text-left',
131
+ count: [
132
+ 'ml-[8px] shrink-0 tabular-nums',
133
+ 'text-[13px] leading-[16px]',
134
+ 'text-[#3c3c43]/60 dark:text-[#ebebf5]/60',
135
+ ].join(' '),
136
+ indentPerLevel: 16,
137
+ },
138
+ }
139
+
140
+ /**
141
+ * Legacy themes carried over from the old per-component variant maps so
142
+ * existing apps keep rendering identically. New work should use `macos`.
143
+ */
144
+ const workspace: SidebarTheme = {
145
+ pane: 'bg-[#f4f4f3] dark:bg-neutral-950 text-zinc-950 dark:text-white select-none',
146
+ layers: {},
147
+ scrollArea: 'flex-1 overflow-y-auto overflow-x-hidden px-5 py-5',
148
+ sectionHeader: 'group/section flex w-full items-center gap-2.5 px-4 pb-2 pt-5 text-[13px] font-semibold uppercase tracking-[0.12em] text-zinc-400 dark:text-zinc-500',
149
+ sectionChevron: 'i-f7-chevron-down h-3 w-3 ml-auto opacity-0 group-hover/section:opacity-100 transition-opacity duration-150 transition-transform duration-200',
150
+ sectionGroup: 'flex flex-col space-y-1',
151
+ item: {
152
+ base: 'flex w-full items-center gap-3 rounded-[16px] px-3.5 py-2 text-[14px] leading-tight transition-colors duration-150',
153
+ hover: 'hover:bg-[#ececea] hover:text-zinc-950 dark:hover:bg-white/10',
154
+ active: 'bg-[#e7e7e5] text-zinc-950 dark:bg-white/12 dark:text-white',
155
+ pressed: 'active:bg-[#e2e2e0] dark:active:bg-white/15',
156
+ disabled: 'opacity-50 pointer-events-none',
157
+ disclosure: 'flex h-4 w-4 shrink-0 items-center justify-center',
158
+ chevron: 'i-f7-chevron-right h-3 w-3 opacity-50 transition-transform duration-200',
159
+ iconSlot: 'flex h-5 w-5 shrink-0 items-center justify-center',
160
+ icon: 'h-[18px] w-[18px] text-zinc-500 dark:text-zinc-500',
161
+ image: 'h-5 w-5 rounded object-cover',
162
+ label: 'flex-1 truncate text-left text-zinc-700 dark:text-zinc-300',
163
+ count: 'ml-auto grid h-5 min-w-5 place-items-center rounded-full bg-white/70 px-1.5 text-[11px] font-medium text-zinc-500 shadow-sm dark:bg-white/10 dark:text-zinc-400',
164
+ indentPerLevel: 12,
165
+ },
166
+ }
167
+
168
+ const desktop: SidebarTheme = {
169
+ pane: 'bg-[#f3f3f1]/48 dark:bg-neutral-950/32 backdrop-blur-3xl backdrop-saturate-150 text-zinc-950 dark:text-white select-none',
170
+ layers: {
171
+ tint: 'bg-[linear-gradient(112deg,rgba(255,255,255,0.20)_0%,rgba(255,255,255,0.04)_45%,rgba(226,226,222,0.16)_100%)] dark:bg-[linear-gradient(112deg,rgba(255,255,255,0.08)_0%,rgba(255,255,255,0.02)_46%,rgba(255,255,255,0.06)_100%)]',
172
+ },
173
+ scrollArea: 'flex-1 overflow-y-auto overflow-x-hidden px-5 py-2',
174
+ sectionHeader: 'group/section flex w-full items-center gap-2.5 px-3 pb-1.5 pt-3 rounded-[14px] text-[12px] font-semibold uppercase tracking-[0.10em] text-zinc-500/80 dark:text-zinc-400/80',
175
+ sectionChevron: 'i-f7-chevron-down h-3 w-3 ml-auto opacity-0 group-hover/section:opacity-100 transition-opacity duration-150 transition-transform duration-200',
176
+ sectionGroup: 'flex flex-col space-y-1',
177
+ item: {
178
+ base: 'flex w-full items-center gap-3 rounded-[14px] px-3 py-1.5 text-[13.5px] leading-tight transition-colors duration-150',
179
+ hover: 'hover:bg-white/38 hover:text-zinc-950 dark:hover:bg-white/10',
180
+ active: 'bg-[#e8e8e6]/90 text-zinc-950 dark:bg-white/12 dark:text-white',
181
+ pressed: 'active:bg-white/50 dark:active:bg-white/15',
182
+ disabled: 'opacity-50 pointer-events-none',
183
+ disclosure: 'flex h-4 w-4 shrink-0 items-center justify-center',
184
+ chevron: 'i-f7-chevron-right h-3 w-3 opacity-50 transition-transform duration-200',
185
+ iconSlot: 'flex h-5 w-5 shrink-0 items-center justify-center',
186
+ icon: 'h-[18px] w-[18px] text-zinc-600 dark:text-zinc-400',
187
+ image: 'h-5 w-5 rounded object-cover',
188
+ label: 'flex-1 truncate text-left text-zinc-700 dark:text-zinc-300',
189
+ count: 'ml-auto grid h-5 min-w-5 place-items-center rounded-full bg-white/72 px-1.5 text-[11px] font-medium text-zinc-500 shadow-sm dark:bg-white/10 dark:text-zinc-400',
190
+ indentPerLevel: 12,
191
+ },
192
+ }
193
+
194
+ const solid: SidebarTheme = {
195
+ ...macos,
196
+ pane: 'bg-stone-100 dark:bg-neutral-900 text-black dark:text-white select-none',
197
+ layers: {},
198
+ }
199
+
200
+ const transparent: SidebarTheme = {
201
+ ...macos,
202
+ pane: 'bg-transparent text-black dark:text-white select-none',
203
+ layers: {},
204
+ }
205
+
206
+ const vibrancy: SidebarTheme = {
207
+ ...macos,
208
+ pane: 'bg-white/50 dark:bg-black/40 backdrop-blur-3xl backdrop-saturate-200 text-black dark:text-white select-none',
209
+ layers: {},
210
+ }
211
+
212
+ export const sidebarThemes: Record<string, SidebarTheme> = {
213
+ macos,
214
+ // Historical names people may still pass — all render the macOS look.
215
+ 'tahoe': macos,
216
+ 'macos-tahoe': macos,
217
+ 'macos-latest': macos,
218
+ workspace,
219
+ desktop,
220
+ solid,
221
+ transparent,
222
+ vibrancy,
223
+ }
224
+
225
+ export type SidebarThemeName = keyof typeof sidebarThemes
226
+
227
+ export function resolveSidebarTheme(name?: string): SidebarTheme {
228
+ return sidebarThemes[name || 'macos'] || sidebarThemes.macos
229
+ }
230
+
231
+ /**
232
+ * Resolve an item's icon tint. Accepts a macOS system color name
233
+ * (`"blue"`, `"red"`, …) or any CSS color, and falls back to the
234
+ * theme's default icon color when unset.
235
+ */
236
+ export function resolveIconColor(color?: string): string | undefined {
237
+ if (!color)
238
+ return undefined
239
+ return macosColors[color as MacosColor] || color
240
+ }
package/stx-plugin.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * stx plugin shim for @stacksjs/components.
3
+ *
4
+ * Register in a project's stx config to make every library component
5
+ * (<Sidebar>, <Button>, <Dialog>, …) resolvable by tag name — component
6
+ * resolution walks subdirectories, so the grouped `src/ui/<name>/` layout
7
+ * works as-is:
8
+ *
9
+ * ```ts
10
+ * // stx.config.ts / config/ui.ts
11
+ * export default {
12
+ * plugins: ['@stacksjs/components/stx-plugin'],
13
+ * }
14
+ * ```
15
+ */
16
+ export default {
17
+ name: '@stacksjs/components',
18
+ components: './src/ui',
19
+ }
@@ -1,330 +0,0 @@
1
- <script server>
2
- export const sections = $props.sections || []
3
- export const collapsed = $props.collapsed ?? false
4
- export const collapsible = $props.collapsible ?? true
5
- export const width = $props.width || 240
6
- export const minWidth = $props.minWidth || 64
7
- export const collapseMode = $props.collapseMode || 'rail'
8
- export const variant = $props.variant || 'tahoe'
9
- export const position = $props.position || 'left'
10
- export const placement = $props.placement || 'fixed'
11
- export const bordered = $props.bordered ?? true
12
- export const materialOpacity = $props.materialOpacity ?? (variant === 'desktop' ? 0.78 : 0.7)
13
- export const materialDarkOpacity = $props.materialDarkOpacity ?? (variant === 'desktop' ? 0.8 : materialOpacity)
14
- export const materialScheme = $props.materialScheme || 'system'
15
- export const persistKey = $props.persistKey || ''
16
- export const shellSelector = $props.shellSelector || ''
17
- export const widthVar = $props.widthVar || '--stx-sidebar-width'
18
- export const collapsedClass = $props.collapsedClass || ''
19
- export const nativeMaterialCollapse = $props.nativeMaterialCollapse ?? (variant === 'desktop' && collapseMode === 'hidden')
20
- export const className = $props.className || ''
21
-
22
- const variantStyles = {
23
- tahoe: {
24
- bg: 'bg-white/70 dark:bg-black/50',
25
- border: 'border-black/5 dark:border-white/10',
26
- backdrop: 'backdrop-blur-2xl backdrop-saturate-180',
27
- tint: true,
28
- },
29
- vibrancy: {
30
- bg: 'bg-white/50 dark:bg-black/40',
31
- border: 'border-white/20 dark:border-white/10',
32
- backdrop: 'backdrop-blur-3xl backdrop-saturate-200',
33
- tint: false,
34
- },
35
- solid: {
36
- bg: 'bg-stone-100 dark:bg-neutral-900',
37
- border: 'border-stone-200 dark:border-neutral-800',
38
- backdrop: '',
39
- tint: false,
40
- },
41
- transparent: {
42
- bg: 'bg-transparent',
43
- border: 'border-transparent',
44
- backdrop: '',
45
- tint: false,
46
- },
47
- workspace: {
48
- bg: 'bg-[#f4f4f3] dark:bg-neutral-950',
49
- border: 'border-transparent',
50
- backdrop: '',
51
- tint: false,
52
- nav: 'px-5 py-5 space-y-1',
53
- slot: 'px-5 py-5',
54
- },
55
- desktop: {
56
- bg: 'bg-[#f3f3f1]/48 dark:bg-neutral-950/32',
57
- border: 'border-white/15 dark:border-white/10',
58
- backdrop: 'backdrop-blur-3xl backdrop-saturate-150',
59
- tint: false,
60
- shimmer: true,
61
- nav: 'px-5 py-2 space-y-1',
62
- slot: 'px-5 py-2',
63
- },
64
- }
65
-
66
- const style = variantStyles[variant] || variantStyles.tahoe
67
- export const variantTint = style.tint
68
- export const variantShimmer = style.shimmer
69
- const borderSide = position === 'left' ? 'border-r' : 'border-l'
70
- const borderClasses = bordered ? `${borderSide} ${style.border}` : ''
71
- const positionClass = position === 'left' ? 'left-0' : 'right-0'
72
- const placementClasses = {
73
- fixed: `fixed inset-y-0 z-50 ${positionClass}`,
74
- sticky: 'sticky top-0 h-screen',
75
- static: 'relative h-full',
76
- }
77
-
78
- export const containerClasses = [
79
- placementClasses[placement] || placementClasses.fixed,
80
- 'flex flex-col',
81
- 'transition-all duration-300 ease-out',
82
- 'overflow-hidden',
83
- style.bg,
84
- style.backdrop,
85
- borderClasses,
86
- className,
87
- ].filter(Boolean).join(' ')
88
-
89
- export const collapseBtnPosition = variant === 'desktop'
90
- ? (position === 'left' ? 'left-[166px]' : 'right-[166px]')
91
- : (position === 'left' ? 'right-2' : 'left-2')
92
- export const navClasses = style.nav || 'px-3 py-2 space-y-1'
93
- export const slotClasses = style.slot || 'px-3 py-2'
94
- </script>
95
-
96
- <script client>
97
- const emit = defineEmits()
98
- const collapsedProp = {{ collapsed }}
99
- const collapsibleProp = {{ collapsible }}
100
- const widthProp = {{ width }}
101
- const minWidthProp = {{ minWidth }}
102
- const collapseModeProp = {{ collapseMode }}
103
- const sectionsProp = {{ sections }}
104
- const positionProp = {{ position }}
105
- const variantProp = {{ variant }}
106
- const materialOpacityProp = {{ materialOpacity }}
107
- const materialDarkOpacityProp = {{ materialDarkOpacity }}
108
- const materialSchemeProp = {{ materialScheme }}
109
- const persistKeyProp = {{ persistKey }}
110
- const shellSelectorProp = {{ shellSelector }}
111
- const widthVarProp = {{ widthVar }}
112
- const collapsedClassProp = {{ collapsedClass }}
113
- const nativeMaterialCollapseProp = {{ nativeMaterialCollapse }}
114
-
115
- const collapsed = state(collapsedProp)
116
- const isHovered = state(false)
117
- const hasNativeSidebar = state(false)
118
-
119
- const showExpanded = derived(() => !collapsed() || isHovered())
120
- const sidebarWidth = derived(() => {
121
- if (collapsed() && collapseModeProp === 'hidden') return '0px'
122
- if (collapsed() && !isHovered()) return `${minWidthProp}px`
123
- return `${widthProp}px`
124
- })
125
- const collapseAriaLabel = derived(() => collapsed() ? 'Expand sidebar' : 'Collapse sidebar')
126
-
127
- function applyCollapseEffects(nextCollapsed) {
128
- if (persistKeyProp) {
129
- try {
130
- window.localStorage?.setItem(persistKeyProp, nextCollapsed ? 'true' : 'false')
131
- }
132
- catch {}
133
- }
134
-
135
- if (shellSelectorProp) {
136
- document.querySelectorAll(shellSelectorProp).forEach((shell) => {
137
- shell.style?.setProperty?.(widthVarProp, nextCollapsed && collapseModeProp === 'hidden' ? '0px' : `${widthProp}px`)
138
- if (shell.dataset) shell.dataset.sidebarCollapsed = nextCollapsed ? 'true' : 'false'
139
- })
140
- }
141
-
142
- document.documentElement.dataset.stxSidebarCollapsed = nextCollapsed ? 'true' : 'false'
143
- if (collapsedClassProp) {
144
- document.documentElement.classList.toggle(collapsedClassProp, nextCollapsed)
145
- }
146
-
147
- if (nativeMaterialCollapseProp) {
148
- window.craft?.window?.setWebSidebarCollapsed?.(nextCollapsed).catch?.(() => {})
149
- }
150
- }
151
-
152
- function setCollapsed(nextCollapsed, shouldEmit = true) {
153
- collapsed.set(nextCollapsed)
154
- applyCollapseEffects(nextCollapsed)
155
- if (shouldEmit) emit('collapse', nextCollapsed)
156
- }
157
-
158
- function onMouseEnter() {
159
- if (collapsed() && collapsibleProp && collapseModeProp !== 'hidden') isHovered.set(true)
160
- }
161
-
162
- function onMouseLeave() {
163
- isHovered.set(false)
164
- }
165
-
166
- function toggleCollapse() {
167
- setCollapsed(!collapsed())
168
- }
169
-
170
- function onSectionToggle(sectionId) {
171
- emit('sectionToggle', sectionId)
172
- }
173
-
174
- function onItemClick(event) {
175
- emit('itemClick', event.detail)
176
- }
177
-
178
- function isCraftRuntime() {
179
- return window.__craftNativeSidebar === true
180
- || window.craft?.__craft_bridge_loaded === true
181
- || !!window.craft?.window
182
- || !!window.webkit?.messageHandlers?.craft
183
- }
184
-
185
- onMount(async () => {
186
- if (persistKeyProp) {
187
- try {
188
- const saved = window.localStorage?.getItem(persistKeyProp)
189
- if (saved === 'true' || saved === 'false') collapsed.set(saved === 'true')
190
- }
191
- catch {}
192
- }
193
-
194
- applyCollapseEffects(collapsed())
195
-
196
- if (variantProp === 'desktop' && isCraftRuntime()) {
197
- document.documentElement.classList.add('has-stx-sidebar-material')
198
- if (document.body?.dataset) document.body.dataset.sidebarMaterial = 'desktop'
199
- }
200
-
201
- hasNativeSidebar.set(isCraftRuntime() && window.craft?.hasNativeSidebar?.() === true)
202
-
203
- const nativeSidebarApi = window.craft?.nativeUI || window.craft?.components
204
- if (isCraftRuntime() && nativeSidebarApi?.createSidebar) {
205
- try {
206
- const sidebar = await nativeSidebarApi.createSidebar({
207
- position: positionProp,
208
- width: widthProp,
209
- minWidth: minWidthProp,
210
- collapseMode: collapseModeProp,
211
- collapsible: collapsibleProp,
212
- variant: variantProp,
213
- material: variantProp === 'desktop' ? 'sidebar' : undefined,
214
- backgroundEffect: variantProp === 'desktop' ? 'vibrancy' : undefined,
215
- materialOpacity: materialOpacityProp,
216
- materialDarkOpacity: materialDarkOpacityProp,
217
- materialScheme: materialSchemeProp,
218
- allowsVibrancy: variantProp === 'desktop' || variantProp === 'vibrancy' || variantProp === 'tahoe',
219
- sections: sectionsProp.map(s => ({
220
- id: s.id,
221
- label: s.label,
222
- items: s.items?.map(i => ({ id: i.id, label: i.label, icon: i.icon, href: i.href, badge: i.badge })),
223
- })),
224
- })
225
- if (sidebar) hasNativeSidebar.set(true)
226
- }
227
- catch (err) {
228
- console.warn('[Sidebar] Failed to create native sidebar, using web fallback:', err)
229
- }
230
- }
231
- })
232
-
233
- defineExpose({ collapsed, toggle: toggleCollapse })
234
- </script>
235
-
236
- <aside
237
- :show="!hasNativeSidebar()"
238
- class="{{ containerClasses }} sidebar-container"
239
- data-sidebar-variant="{{ variant }}"
240
- data-sidebar-material-scheme="{{ materialScheme }}"
241
- :aria-hidden="String(collapsed() && collapseModeProp === 'hidden')"
242
- :style="`width: ${sidebarWidth()}; --stx-sidebar-material-opacity: ${materialOpacityProp}; --stx-sidebar-material-dark-opacity: ${materialDarkOpacityProp}`"
243
- @mouseenter="onMouseEnter()"
244
- @mouseleave="onMouseLeave()"
245
- role="navigation"
246
- aria-label="Sidebar navigation"
247
- >
248
- @if(variantTint)
249
- <div class="absolute inset-0 pointer-events-none bg-gradient-to-b from-amber-50/30 via-orange-50/20 to-stone-100/30 dark:from-neutral-800/20 dark:via-neutral-900/10 dark:to-neutral-800/20 mix-blend-overlay"></div>
250
- @endif
251
- @if(variantShimmer)
252
- <div class="absolute inset-0 pointer-events-none bg-[linear-gradient(112deg,rgba(255,255,255,0.20)_0%,rgba(255,255,255,0.04)_45%,rgba(226,226,222,0.16)_100%)] dark:bg-[linear-gradient(112deg,rgba(255,255,255,0.08)_0%,rgba(255,255,255,0.02)_46%,rgba(255,255,255,0.06)_100%)]"></div>
253
- @endif
254
-
255
- @if($slots.header)
256
- <div class="flex-shrink-0 relative z-10">
257
- <slot name="header" />
258
- </div>
259
- @endif
260
-
261
- <div class="flex-1 overflow-y-auto overflow-x-hidden relative z-10">
262
- @if(sections.length > 0)
263
- <nav class="{{ navClasses }}">
264
- @foreach(section in sections)
265
- <SidebarSection
266
- :id="section.id"
267
- :label="section.label"
268
- :icon="section.icon"
269
- :items="section.items"
270
- :expanded="section.expanded !== false"
271
- :collapsible="section.collapsible !== false"
272
- :showLabel="showExpanded()"
273
- :variant="variant"
274
- @toggle="onSectionToggle(section.id)"
275
- @itemClick="onItemClick($event)"
276
- />
277
- @endforeach
278
- </nav>
279
- @else
280
- <div class="{{ slotClasses }}">
281
- <slot />
282
- </div>
283
- @endif
284
- </div>
285
-
286
- @if($slots.footer)
287
- <div class="flex-shrink-0 relative z-10">
288
- <slot name="footer" />
289
- </div>
290
- @endif
291
-
292
- @if(collapsible)
293
- <button
294
- type="button"
295
- class="absolute top-4 {{ collapseBtnPosition }} p-1.5 rounded-lg text-stone-400 hover:text-stone-600 hover:bg-black/5 dark:hover:text-neutral-300 dark:hover:bg-white/10 transition-colors z-20"
296
- @click="toggleCollapse()"
297
- :aria-label="collapseAriaLabel()"
298
- data-sidebar-collapse
299
- data-stx-sidebar-collapse-button
300
- >
301
- <div :show="!collapsed()" class="i-hugeicons-sidebar-left w-5 h-5"></div>
302
- <div :show="collapsed()" class="i-hugeicons-sidebar-right w-5 h-5"></div>
303
- </button>
304
- @endif
305
- </aside>
306
-
307
- <style scoped>
308
- .overflow-y-auto::-webkit-scrollbar { width: 6px; }
309
- .overflow-y-auto::-webkit-scrollbar-track { background: transparent; }
310
- .overflow-y-auto::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.15); border-radius: 3px; }
311
- .overflow-y-auto::-webkit-scrollbar-thumb:hover { background-color: rgba(0, 0, 0, 0.25); }
312
- .sidebar-container[data-sidebar-variant="desktop"] {
313
- background-color: rgba(246, 246, 244, var(--stx-sidebar-material-opacity, 0.78));
314
- backdrop-filter: blur(24px) saturate(1.08);
315
- -webkit-backdrop-filter: blur(24px) saturate(1.08);
316
- }
317
- html.has-native-sidebar [data-stx-native-window-controls] {
318
- display: none;
319
- }
320
- html.has-native-sidebar [data-stx-sidebar-web-chrome] {
321
- margin-left: 202px;
322
- }
323
- @media (prefers-color-scheme: dark) {
324
- .sidebar-container[data-sidebar-variant="desktop"]:not([data-sidebar-material-scheme="light"]) {
325
- background-color: rgba(24, 24, 23, var(--stx-sidebar-material-dark-opacity, 0.8));
326
- }
327
- .overflow-y-auto::-webkit-scrollbar-thumb { background-color: rgba(255, 255, 255, 0.15); }
328
- .overflow-y-auto::-webkit-scrollbar-thumb:hover { background-color: rgba(255, 255, 255, 0.25); }
329
- }
330
- </style>