astro-better-docs-sidebar 0.2.0 → 1.0.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.
package/README.md CHANGED
@@ -122,6 +122,38 @@ hidden: true
122
122
  ---
123
123
  ```
124
124
 
125
+ ## Non-routable folder index pages
126
+
127
+ Set `route: false` in a folder's `index.mdx` to prevent the sidebar from linking to it while still using it to set the folder's title and `order`. The folder heading appears as a plain label with no link.
128
+
129
+ ```yaml
130
+ # docs/core-concepts/index.mdx
131
+ ---
132
+ title: Core Concepts
133
+ order: 2
134
+ route: false
135
+ ---
136
+ ```
137
+
138
+ This is equivalent to `sidebar.folderBehavior: unclickable` but scoped to the index page itself rather than the `folderBehavior` prop. Use it when a folder should never have a clickable header, regardless of the global `folderBehavior` setting.
139
+
140
+ Make sure your content collection schema includes `route`:
141
+
142
+ ```ts
143
+ const docs = defineCollection({
144
+ schema: z.object({
145
+ title: z.string(),
146
+ order: z.number().optional(),
147
+ hidden: z.boolean().optional(),
148
+ route: z.boolean().optional(),
149
+ sidebar: z.object({
150
+ label: z.string().optional(),
151
+ folderBehavior: z.enum(['page', 'unclickable', 'overview']).optional(),
152
+ }).optional(),
153
+ }),
154
+ });
155
+ ```
156
+
125
157
  ## Controlling sort order
126
158
 
127
159
  Set `order` in front matter to control sort position within a folder. Lower numbers sort first. Pages without `order` sort last, then alphabetically by title.
package/Sidebar.astro CHANGED
@@ -134,7 +134,7 @@ const treeArray = getSortedArray(treeRoot.children);
134
134
  ---
135
135
 
136
136
 
137
- <nav class="nav">
137
+ <nav>
138
138
  {treeArray.map(node => <SidebarItem node={node} />)}
139
139
  </nav>
140
140
 
@@ -142,61 +142,58 @@ const treeArray = getSortedArray(treeRoot.children);
142
142
  @reference "tailwindcss";
143
143
 
144
144
  aside.aside { @apply w-64 shrink-0 border-r border-gray-200 bg-white pt-2 pr-2; }
145
- .nav { @apply flex flex-col gap-1; }
145
+ #side-nav nav { @apply flex flex-col gap-1; }
146
146
 
147
- /* DEPTH LINE */
148
- .nav details > div { @apply flex flex-col gap-0.5 pl-3 ml-3 border-l border-slate-200; }
147
+ /* depth line */
148
+ #side-nav nav details > div { @apply flex flex-col gap-0.5 pl-3 ml-3 border-l border-slate-200; }
149
149
 
150
- /* `s` = summary wrapper container */
151
- .s { @apply flex items-center justify-between cursor-pointer select-none rounded gap-1 transition-colors text-slate-700; list-style: none; }
152
- .s::-webkit-details-marker { display: none; }
150
+ /* folder header row */
151
+ #side-nav nav summary { @apply flex items-center justify-between cursor-pointer select-none rounded gap-1 transition-colors text-slate-700; list-style: none; }
152
+ #side-nav nav summary::-webkit-details-marker { display: none; }
153
153
 
154
- /* `l` = standalone leaf link OR the text link inside the summary */
155
- .l { @apply flex-1 text-sm py-1 px-2 rounded transition-colors no-underline block text-slate-700; }
156
- a.l { @apply cursor-pointer; }
157
- span.l { @apply cursor-default; }
154
+ /* leaf links and folder labels */
155
+ #side-nav nav :is(a, span) { @apply flex-1 text-sm py-1 px-2 rounded transition-colors no-underline block text-slate-700; }
156
+ #side-nav nav a { @apply cursor-pointer; }
157
+ #side-nav nav span { @apply cursor-default; }
158
158
 
159
- /* `c` = chevron element */
160
- .c { @apply flex items-center justify-center w-7 h-7 rounded text-gray-400 shrink-0 transition-transform; }
161
- .c::after { content: ''; @apply w-[0.4rem] h-[0.4rem] border-r-2 border-b-2 border-current block -rotate-45 transition-transform box-border; }
162
- details[open] > .s .c::after { @apply rotate-45; }
159
+ /* chevron */
160
+ #side-nav nav i { @apply flex items-center justify-center w-7 h-7 rounded text-gray-400 shrink-0 transition-transform; }
161
+ #side-nav nav i::after { content: ''; @apply w-[0.4rem] h-[0.4rem] border-r-2 border-b-2 border-current block -rotate-45 transition-transform box-border; }
162
+ #side-nav nav details[open] > summary i::after { @apply rotate-45; }
163
163
 
164
- /* JS-FREE FOLDER EXPANSION */
165
- details:not([open]) > .s a.l { pointer-events: none; }
164
+ /* clicking a closed folder opens it; disables the link's own navigation */
165
+ #side-nav nav details:not([open]) > summary a { pointer-events: none; }
166
166
 
167
- /* HOVERS: Group Hover (Leaves, Closed Folders, No-Link Folders) */
168
- :is(a.l, details:not([open]) > .s, details:not(:has(a.l)) > .s):hover { @apply bg-slate-100; }
169
- a.l:hover { @apply text-indigo-600; }
170
- :is(details:not([open]) > .s, details:not(:has(a.l)) > .s):hover .l { @apply text-indigo-600; }
171
-
172
- /* HOVERS: Separate Hovers (Open Folders WITH Links) */
173
- details[open] > .s:has(a.l) a.l:hover { @apply bg-slate-100 text-indigo-600; }
174
- details[open] > .s:has(a.l) .c:hover { @apply bg-slate-100 text-slate-700; }
167
+ /* HOVERS */
168
+ #side-nav nav a:not(summary a):hover { @apply bg-slate-100 text-indigo-600; }
169
+ #side-nav nav details:not([open]) > summary:hover,
170
+ #side-nav nav summary:not(:has(a)):hover { @apply bg-slate-100; }
171
+ #side-nav nav details:not([open]) > summary:hover :is(a, span),
172
+ #side-nav nav summary:not(:has(a)):hover span { @apply text-indigo-600; }
173
+ #side-nav nav details[open] > summary:has(a) a:hover { @apply bg-slate-100 text-indigo-600; }
174
+ #side-nav nav details[open] > summary:has(a) i:hover { @apply bg-slate-100 text-slate-700; }
175
175
 
176
176
  /* ACTIVE & ANCESTOR STATES */
177
- .l[aria-current="page"], .s:has(> .l[aria-current="page"]) .l { @apply font-semibold text-indigo-600 bg-indigo-50; }
178
- .l[data-a], .s[data-a] .l { @apply font-semibold text-slate-900; }
179
- .s:has(> [aria-current="page"]) .c, details:has([aria-current="page"]) > .s .c { @apply text-indigo-500; }
180
-
181
- /* --- DARK MODE --- */
177
+ #side-nav nav a[aria-current="page"],
178
+ #side-nav nav summary:has(> a[aria-current="page"]) :is(a, span) { @apply font-semibold text-indigo-600 bg-indigo-50; }
179
+ #side-nav nav a[data-a],
180
+ #side-nav nav summary[data-a] :is(a, span) { @apply font-semibold text-slate-900; }
181
+ #side-nav nav summary:has(> [aria-current="page"]) i,
182
+ #side-nav nav details:has([aria-current="page"]) > summary i { @apply text-indigo-500; }
183
+
184
+ /* DARK MODE */
182
185
  .dark aside.aside { @apply border-gray-800 bg-gray-900; }
183
-
184
- /* Dimmed inactive text to increase contrast with bold active items */
185
- .dark :is(.l, .s) { @apply text-slate-400; }
186
-
187
- /* Dark mode border color for the depth line */
188
- .dark .nav details > div { @apply border-slate-700; }
189
-
190
- .dark :is(a.l, details:not([open]) > .s, details:not(:has(a.l)) > .s):hover { @apply bg-slate-800; }
191
- .dark a.l:hover { @apply text-indigo-400; }
192
- .dark :is(details:not([open]) > .s, details:not(:has(a.l)) > .s):hover .l { @apply text-indigo-400; }
193
-
194
- .dark details[open] > .s:has(a.l) a.l:hover { @apply bg-slate-800 text-indigo-400; }
195
- .dark details[open] > .s:has(a.l) .c:hover { @apply bg-slate-800 text-slate-200; }
196
-
197
- .dark .l[aria-current="page"],
198
- .dark .s:has(> .l[aria-current="page"]) .l { @apply text-indigo-400 bg-indigo-950/40; }
199
-
200
- .dark .l[data-a],
201
- .dark .s[data-a] .l { @apply text-white; }
186
+ .dark #side-nav nav :is(a, span) { @apply text-slate-400; }
187
+ .dark #side-nav nav details > div { @apply border-slate-700; }
188
+ .dark #side-nav nav a:not(summary a):hover { @apply bg-slate-800 text-indigo-400; }
189
+ .dark #side-nav nav details:not([open]) > summary:hover,
190
+ .dark #side-nav nav summary:not(:has(a)):hover { @apply bg-slate-800; }
191
+ .dark #side-nav nav details:not([open]) > summary:hover :is(a, span),
192
+ .dark #side-nav nav summary:not(:has(a)):hover span { @apply text-indigo-400; }
193
+ .dark #side-nav nav details[open] > summary:has(a) a:hover { @apply bg-slate-800 text-indigo-400; }
194
+ .dark #side-nav nav details[open] > summary:has(a) i:hover { @apply bg-slate-800 text-slate-200; }
195
+ .dark #side-nav nav a[aria-current="page"],
196
+ .dark #side-nav nav summary:has(> a[aria-current="page"]) :is(a, span) { @apply text-indigo-400 bg-indigo-950/40; }
197
+ .dark #side-nav nav a[data-a],
198
+ .dark #side-nav nav summary[data-a] :is(a, span) { @apply text-white; }
202
199
  </style>
package/SidebarItem.astro CHANGED
@@ -11,18 +11,17 @@ const isAnc = node.isActive && !node.isCurrentPage;
11
11
 
12
12
  {childNodes.length > 0 ? (
13
13
  <details open={node.isOpen || undefined}>
14
- <summary class="s" data-a={isAnc || undefined}>
14
+ <summary data-a={isAnc || undefined}>
15
15
  {node.href ? (
16
16
  <a
17
17
  href={`${node.href}/`}
18
- class="l"
19
18
  aria-current={node.isCurrentPage ? "page" : undefined}
20
19
  set:html={htmlTitle}
21
20
  ></a>
22
21
  ) : (
23
- <span class="l" set:html={htmlTitle}></span>
22
+ <span set:html={htmlTitle}></span>
24
23
  )}
25
- <i class="c"></i>
24
+ <i></i>
26
25
  </summary>
27
26
  <div>
28
27
  {childNodes.map((child: any) => <Astro.self node={child} />)}
@@ -31,7 +30,6 @@ const isAnc = node.isActive && !node.isCurrentPage;
31
30
  ) : (
32
31
  <a
33
32
  href={node.href}
34
- class="l"
35
33
  aria-current={node.isCurrentPage ? "page" : undefined}
36
34
  data-a={isAnc || undefined}
37
35
  set:html={htmlTitle}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-better-docs-sidebar",
3
- "version": "0.2.0",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./Sidebar.astro": "./Sidebar.astro",