@classic-homes/theme-docs 0.0.23 → 0.0.25

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.
@@ -4,10 +4,11 @@
4
4
  *
5
5
  * Provides a mobile flyout panel and optional desktop sidebar for the
6
6
  * TableOfContents component. Features a paper folder tab trigger on mobile
7
- * that slides out a panel from the right side.
7
+ * that slides out a panel from the right side. The tab is physically attached
8
+ * to the panel so they move together as one unit.
8
9
  */
9
10
  import { onMount } from 'svelte';
10
- import { fly, fade } from 'svelte/transition';
11
+ import { fade } from 'svelte/transition';
11
12
  import { cn } from '../utils.js';
12
13
  import TableOfContents from './TableOfContents.svelte';
13
14
 
@@ -41,19 +42,25 @@
41
42
  }: Props = $props();
42
43
 
43
44
  let open = $state(false);
44
- let closeButtonRef = $state<HTMLButtonElement | null>(null);
45
+ let tabButtonRef = $state<HTMLButtonElement | null>(null);
45
46
  let previousActiveElement: HTMLElement | null = null;
46
47
 
47
- function openPanel() {
48
- // Store the currently focused element to restore focus when closing
49
- previousActiveElement = document.activeElement as HTMLElement;
50
- open = true;
48
+ function toggle() {
49
+ if (open) {
50
+ open = false;
51
+ // Restore focus to the tab when closing
52
+ previousActiveElement?.focus();
53
+ } else {
54
+ // Store the currently focused element to restore focus when closing
55
+ previousActiveElement = document.activeElement as HTMLElement;
56
+ open = true;
57
+ }
51
58
  }
52
59
 
53
60
  function close() {
54
61
  open = false;
55
- // Restore focus to the element that opened the panel
56
- previousActiveElement?.focus();
62
+ // Focus the tab button when closing via backdrop/escape
63
+ tabButtonRef?.focus();
57
64
  }
58
65
 
59
66
  function handleKeydown(event: KeyboardEvent) {
@@ -63,14 +70,6 @@
63
70
  }
64
71
  }
65
72
 
66
- // Focus the close button when panel opens
67
- $effect(() => {
68
- if (open && closeButtonRef) {
69
- // Small delay to ensure the panel is rendered
70
- setTimeout(() => closeButtonRef?.focus(), 0);
71
- }
72
- });
73
-
74
73
  onMount(() => {
75
74
  // Add global escape key listener
76
75
  document.addEventListener('keydown', handleKeydown);
@@ -92,63 +91,104 @@
92
91
  '2xl': 'hidden 2xl:block',
93
92
  }[breakpoint]
94
93
  );
95
- </script>
96
94
 
97
- <!-- Mobile TOC Tab Trigger - Paper folder tab style -->
98
- {#if html && !open}
99
- <button
100
- class={cn(breakpointHide, 'toc-tab-trigger')}
101
- onclick={openPanel}
102
- aria-label="Open table of contents"
103
- >
104
- <span class="toc-tab-text">
105
- {title}
106
- </span>
107
- </button>
108
- {/if}
95
+ // Tailwind classes for the attached tab button
96
+ const tabClasses = [
97
+ // Positioning - absolute to panel container, sticks out on left
98
+ 'absolute top-[30%] right-full',
99
+ '-translate-y-1/2',
100
+ // Tab shape - compact
101
+ 'py-5 px-2',
102
+ // Visual styling - white background with black border
103
+ 'bg-background',
104
+ 'border border-foreground border-r-0',
105
+ 'rounded-l',
106
+ 'shadow-[-2px_0_8px_-2px_rgba(0,0,0,0.1)]',
107
+ // Typography
108
+ 'font-sans leading-normal',
109
+ // Interactions
110
+ 'cursor-pointer',
111
+ 'hover:bg-muted',
112
+ // Focus
113
+ 'focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2',
114
+ ].join(' ');
109
115
 
110
- <!-- Mobile TOC Flyout Panel -->
111
- {#if html && open}
112
- <!-- Backdrop -->
113
- <button
114
- class={cn(breakpointHide, 'toc-backdrop')}
115
- onclick={close}
116
- aria-label="Close table of contents"
117
- transition:fade={{ duration: 200 }}
118
- ></button>
116
+ // Tailwind classes for the backdrop
117
+ const backdropClasses = [
118
+ 'fixed inset-0 z-[60]',
119
+ 'bg-black/50',
120
+ 'cursor-pointer',
121
+ 'focus-visible:outline-none',
122
+ ].join(' ');
123
+
124
+ // Tailwind classes for the close button in header
125
+ const closeButtonClasses = [
126
+ 'inline-flex items-center justify-center',
127
+ 'w-9 h-9',
128
+ 'rounded-md',
129
+ 'text-muted-foreground',
130
+ 'bg-transparent border-0 p-0 m-0',
131
+ 'cursor-pointer',
132
+ 'transition-colors duration-150 ease-in-out',
133
+ 'hover:bg-muted hover:text-foreground',
134
+ 'focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2',
135
+ ].join(' ');
136
+ </script>
119
137
 
120
- <!-- Panel -->
138
+ <!-- Mobile TOC Panel with attached tab - always rendered, slides via CSS transform -->
139
+ {#if html}
140
+ <!-- Backdrop - only shown when open -->
141
+ {#if open}
142
+ <button
143
+ class={cn(breakpointHide, backdropClasses)}
144
+ onclick={close}
145
+ aria-label="Close table of contents"
146
+ transition:fade={{ duration: 300 }}
147
+ ></button>
148
+ {/if}
149
+
150
+ <!-- Panel container with attached tab -->
121
151
  <div
122
- class={cn(breakpointHide, 'toc-panel', mobileClass)}
123
- transition:fly={{ x: 320, duration: 300 }}
152
+ class={cn(breakpointHide, 'toc-panel-container', open && 'toc-panel-open', mobileClass)}
124
153
  {...restProps}
125
154
  >
126
- <div class="toc-panel-header">
127
- <span class="text-sm font-medium text-foreground">{title}</span>
128
- <button
129
- bind:this={closeButtonRef}
130
- class="toc-close-button"
131
- onclick={close}
132
- aria-label="Close table of contents"
133
- >
134
- <svg
135
- class="h-5 w-5"
136
- fill="none"
137
- stroke="currentColor"
138
- viewBox="0 0 24 24"
139
- aria-hidden="true"
140
- >
141
- <path
142
- stroke-linecap="round"
143
- stroke-linejoin="round"
144
- stroke-width="2"
145
- d="M6 18L18 6M6 6l12 12"
146
- />
147
- </svg>
148
- </button>
149
- </div>
150
- <div class="p-5">
151
- <TableOfContents {html} {maxDepth} title="" />
155
+ <!-- Tab - physically attached to panel -->
156
+ <button
157
+ bind:this={tabButtonRef}
158
+ class={tabClasses}
159
+ onclick={toggle}
160
+ aria-label={open ? 'Close table of contents' : 'Open table of contents'}
161
+ aria-expanded={open}
162
+ >
163
+ <span class="toc-tab-text">
164
+ {title}
165
+ </span>
166
+ </button>
167
+
168
+ <!-- Panel content -->
169
+ <div class="toc-panel-content">
170
+ <div class="toc-panel-header">
171
+ <span class="text-sm font-medium text-foreground">{title}</span>
172
+ <button class={closeButtonClasses} onclick={close} aria-label="Close table of contents">
173
+ <svg
174
+ class="h-5 w-5"
175
+ fill="none"
176
+ stroke="currentColor"
177
+ viewBox="0 0 24 24"
178
+ aria-hidden="true"
179
+ >
180
+ <path
181
+ stroke-linecap="round"
182
+ stroke-linejoin="round"
183
+ stroke-width="2"
184
+ d="M6 18L18 6M6 6l12 12"
185
+ />
186
+ </svg>
187
+ </button>
188
+ </div>
189
+ <div class="p-5">
190
+ <TableOfContents {html} {maxDepth} title="" />
191
+ </div>
152
192
  </div>
153
193
  </div>
154
194
  {/if}
@@ -167,31 +207,11 @@
167
207
  {/if}
168
208
 
169
209
  <style>
170
- /* Paper folder tab trigger - positioned on right edge */
171
- .toc-tab-trigger {
172
- position: fixed;
173
- top: 25%;
174
- right: 0;
175
- transform: translateY(-50%);
176
- z-index: 21;
177
- /* Tab shape - taller for vertical text */
178
- padding: 1rem 0.375rem;
179
- /* Visual styling */
180
- background-color: hsl(var(--card));
181
- border: 1px solid hsl(var(--border));
182
- border-right: none;
183
- border-radius: 0.5rem 0 0 0.5rem;
184
- box-shadow: -2px 0 8px -2px rgba(0, 0, 0, 0.1);
185
- /* Hover effect */
186
- transition:
187
- background-color 0.15s ease,
188
- padding-right 0.15s ease;
189
- }
190
-
191
- .toc-tab-trigger:hover {
192
- background-color: hsl(var(--muted));
193
- padding-right: 0.5rem;
194
- }
210
+ /*
211
+ * Non-button styles that don't conflict with Tailwind preflight.
212
+ * Button styles are applied via Tailwind utility classes in the script
213
+ * to ensure proper specificity with postcss-cascade-layers.
214
+ */
195
215
 
196
216
  /* Vertical text for folder tab */
197
217
  .toc-tab-text {
@@ -199,33 +219,40 @@
199
219
  writing-mode: vertical-rl;
200
220
  text-orientation: mixed;
201
221
  transform: rotate(180deg);
202
- font-size: 0.75rem;
203
- font-weight: 500;
204
- color: hsl(var(--muted-foreground));
222
+ font-size: 0.8125rem;
223
+ font-weight: 600;
224
+ color: hsl(var(--foreground));
205
225
  white-space: nowrap;
206
- letter-spacing: 0.025em;
226
+ letter-spacing: 0.05em;
207
227
  }
208
228
 
209
- /* Backdrop overlay */
210
- .toc-backdrop {
211
- position: fixed;
212
- inset: 0;
213
- background-color: rgba(0, 0, 0, 0.5);
214
- z-index: 20;
215
- }
216
-
217
- /* Flyout panel */
218
- .toc-panel {
229
+ /* Panel container - holds both tab and panel, slides as one unit */
230
+ .toc-panel-container {
219
231
  position: fixed;
220
232
  top: 0;
221
233
  right: 0;
222
234
  height: 100%;
223
235
  width: 20rem;
224
236
  max-width: 85vw;
237
+ z-index: 61;
238
+ /* Start off-screen (translated right by panel width) */
239
+ transform: translateX(100%);
240
+ /* Smooth slide animation */
241
+ transition: transform 0.3s ease-in-out;
242
+ }
243
+
244
+ /* When open, slide into view */
245
+ .toc-panel-open {
246
+ transform: translateX(0);
247
+ }
248
+
249
+ /* Panel content area */
250
+ .toc-panel-content {
251
+ height: 100%;
252
+ width: 100%;
225
253
  background-color: hsl(var(--card));
226
254
  border-left: 1px solid hsl(var(--border));
227
255
  box-shadow: -4px 0 16px -4px rgba(0, 0, 0, 0.15);
228
- z-index: 22;
229
256
  overflow-y: auto;
230
257
  overscroll-behavior: contain;
231
258
  }
@@ -241,23 +268,4 @@
241
268
  background-color: hsl(var(--card));
242
269
  border-bottom: 1px solid hsl(var(--border));
243
270
  }
244
-
245
- /* Close button */
246
- .toc-close-button {
247
- display: inline-flex;
248
- align-items: center;
249
- justify-content: center;
250
- width: 2.25rem;
251
- height: 2.25rem;
252
- border-radius: 0.375rem;
253
- color: hsl(var(--muted-foreground));
254
- transition:
255
- background-color 0.15s ease,
256
- color 0.15s ease;
257
- }
258
-
259
- .toc-close-button:hover {
260
- background-color: hsl(var(--muted));
261
- color: hsl(var(--foreground));
262
- }
263
271
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classic-homes/theme-docs",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "description": "Markdown documentation components for the Classic theme system",
5
5
  "type": "module",
6
6
  "main": "./dist/lib/index.js",