Skip to content

Commit f9866e7

Browse files
committed
docs(quick-10): add icons to each button in the docs sidebar
1 parent e6a0698 commit f9866e7

File tree

2 files changed

+236
-1
lines changed

2 files changed

+236
-1
lines changed

.planning/STATE.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ See: .planning/PROJECT.md (updated 2026-02-28)
2525
Phase: 145 of 145 (Completely redesign the mesh packages page) — 4 of 4 plans COMPLETE
2626
Plan: 4 of 4 — DONE
2727
Status: Phase 145 Plan 04 complete — human visual verification passed; all automated smoke tests passed; redesign production-approved; ready to deploy
28-
Last activity: 2026-03-01 — Phase 145 Plan 04 complete: human approved redesign; all 7 checklist items verified; design parity with meshlang.dev confirmed
28+
Last activity: 2026-03-02 - Completed quick task 10: add icons to each button in the docs sidebar
2929

3030
Progress: [██████████] 100% (4/4 plans for phase 145) — PHASE COMPLETE
3131

@@ -178,6 +178,12 @@ None.
178178
- [Phase 140]: Registry storage abstraction (StorageBackend trait for S3/R2 migration path) needs design decision at planning time
179179
- [Phase 140]: Empty registry at launch ("ghost town" problem) — plan to publish stdlib packages as seed content during Phase 140
180180

181+
### Quick Tasks Completed
182+
183+
| # | Description | Date | Commit | Directory |
184+
|---|-------------|------|--------|-----------|
185+
| 10 | add icons to each button in the docs sidebar | 2026-03-02 | e6a0698b | [10-add-icons-to-each-button-in-the-docs-sid](./quick/10-add-icons-to-each-button-in-the-docs-sid/) |
186+
181187
## Session Continuity
182188

183189
Last session: 2026-03-01
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
---
2+
phase: quick-10
3+
plan: 01
4+
type: execute
5+
wave: 1
6+
depends_on: []
7+
files_modified:
8+
- website/docs/.vitepress/theme/composables/useSidebar.ts
9+
- website/docs/.vitepress/config.mts
10+
- website/docs/.vitepress/theme/components/docs/DocsSidebarItem.vue
11+
autonomous: true
12+
requirements: []
13+
must_haves:
14+
truths:
15+
- "Each sidebar link has a small icon to its left"
16+
- "Active sidebar item icon is visible and styled consistently with text"
17+
- "Icons come from the already-installed lucide-vue-next package"
18+
artifacts:
19+
- path: "website/docs/.vitepress/theme/composables/useSidebar.ts"
20+
provides: "SidebarItem interface with optional icon field"
21+
contains: "icon?: string"
22+
- path: "website/docs/.vitepress/theme/components/docs/DocsSidebarItem.vue"
23+
provides: "Renders icon component inline before item text"
24+
- path: "website/docs/.vitepress/config.mts"
25+
provides: "Every leaf sidebar item has an icon name assigned"
26+
key_links:
27+
- from: "website/docs/.vitepress/config.mts"
28+
to: "website/docs/.vitepress/theme/components/docs/DocsSidebarItem.vue"
29+
via: "icon property on SidebarItem passed through VitePress theme config"
30+
---
31+
32+
<objective>
33+
Add a Lucide icon to every sidebar navigation link in the docs site.
34+
35+
Purpose: Improve visual hierarchy and scannability of the docs sidebar — each item should have a recognizable icon that matches its content topic.
36+
Output: Updated SidebarItem type, icon-aware DocsSidebarItem component, icon assignments on every config entry.
37+
</objective>
38+
39+
<execution_context>
40+
@/Users/sn0w/.claude/get-shit-done/workflows/execute-plan.md
41+
@/Users/sn0w/.claude/get-shit-done/templates/summary.md
42+
</execution_context>
43+
44+
<context>
45+
@.planning/STATE.md
46+
47+
Relevant files:
48+
- website/docs/.vitepress/config.mts — sidebar config with all items
49+
- website/docs/.vitepress/theme/composables/useSidebar.ts — SidebarItem interface
50+
- website/docs/.vitepress/theme/components/docs/DocsSidebarItem.vue — renders each link
51+
52+
Tech: lucide-vue-next ^0.564.0 is already installed (see website/package.json).
53+
54+
<interfaces>
55+
<!-- Current SidebarItem (useSidebar.ts) -->
56+
export interface SidebarItem {
57+
text?: string
58+
link?: string
59+
items?: SidebarItem[]
60+
collapsed?: boolean
61+
base?: string
62+
docFooterText?: string
63+
}
64+
65+
<!-- Current DocsSidebarItem.vue template — renders a plain anchor tag, no icon -->
66+
<a :href="item.link" class="block rounded-md px-2 py-1.5 text-[13px] ...">
67+
{{ item.text }}
68+
</a>
69+
70+
<!-- Current sidebar leaf items from config.mts -->
71+
{ text: 'Introduction', link: '/docs/getting-started/' }
72+
{ text: 'Language Basics', link: '/docs/language-basics/' }
73+
{ text: 'Type System', link: '/docs/type-system/' }
74+
{ text: 'Iterators', link: '/docs/iterators/' }
75+
{ text: 'Concurrency', link: '/docs/concurrency/' }
76+
{ text: 'Web', link: '/docs/web/' }
77+
{ text: 'Databases', link: '/docs/databases/' }
78+
{ text: 'Distributed Actors', link: '/docs/distributed/' }
79+
{ text: 'Developer Tools', link: '/docs/tooling/' }
80+
{ text: 'Standard Library', link: '/docs/stdlib/' }
81+
{ text: 'Testing', link: '/docs/testing/' }
82+
{ text: 'Syntax Cheatsheet', link: '/docs/cheatsheet/' }
83+
</interfaces>
84+
</context>
85+
86+
<tasks>
87+
88+
<task type="auto">
89+
<name>Task 1: Extend SidebarItem type and wire icon rendering in DocsSidebarItem</name>
90+
<files>
91+
website/docs/.vitepress/theme/composables/useSidebar.ts
92+
website/docs/.vitepress/theme/components/docs/DocsSidebarItem.vue
93+
</files>
94+
<action>
95+
**useSidebar.ts** — Add `icon?: string` field to the SidebarItem interface. No other changes needed.
96+
97+
**DocsSidebarItem.vue** — Use lucide-vue-next's dynamic component pattern to render the icon. Import `defineAsyncComponent` is not needed — lucide-vue-next exports every icon as a named component. Use the `<component :is="...">` approach with a computed lookup.
98+
99+
Concrete implementation:
100+
101+
```vue
102+
<script setup lang="ts">
103+
import { computed } from 'vue'
104+
import { useData } from 'vitepress'
105+
import { isActive, type SidebarItem } from '@/composables/useSidebar'
106+
import * as LucideIcons from 'lucide-vue-next'
107+
108+
const props = defineProps<{
109+
item: SidebarItem
110+
}>()
111+
112+
const { page } = useData()
113+
const active = computed(() => isActive(page.value.relativePath, props.item.link))
114+
115+
const iconComponent = computed(() => {
116+
if (!props.item.icon) return null
117+
return (LucideIcons as Record<string, unknown>)[props.item.icon] ?? null
118+
})
119+
</script>
120+
121+
<template>
122+
<div>
123+
<a
124+
:href="item.link"
125+
class="flex items-center gap-2 rounded-md px-2 py-1.5 text-[13px] transition-colors"
126+
:class="[
127+
active
128+
? 'bg-accent text-foreground font-semibold'
129+
: 'text-muted-foreground hover:text-foreground hover:bg-accent',
130+
]"
131+
>
132+
<component
133+
v-if="iconComponent"
134+
:is="iconComponent"
135+
class="size-3.5 shrink-0"
136+
/>
137+
{{ item.text }}
138+
</a>
139+
<!-- Recursive children with left padding -->
140+
<ul v-if="item.items?.length" class="flex flex-col gap-0.5 pl-3 mt-0.5">
141+
<li v-for="child in item.items" :key="child.text">
142+
<DocsSidebarItem :item="child" />
143+
</li>
144+
</ul>
145+
</div>
146+
</template>
147+
```
148+
149+
Key decisions:
150+
- `import * as LucideIcons` gives access to every icon by PascalCase name; no bundler issue since tree-shaking still works per export
151+
- Icon size `size-3.5` (14px) keeps it compact alongside 13px text
152+
- `shrink-0` prevents icon squishing on narrow sidebars
153+
- `flex items-center gap-2` replaces `block` on the anchor so icon and text align
154+
- Items without `icon` set render unchanged (icon slot stays empty)
155+
</action>
156+
<verify>npx tsc --noEmit -p /Users/sn0w/Documents/dev/mesh/website/docs/.vitepress/tsconfig.json 2>/dev/null || echo "no tsconfig — skip type check"</verify>
157+
<done>DocsSidebarItem renders an icon when item.icon is set; items without icon still render correctly; no TypeScript errors</done>
158+
</task>
159+
160+
<task type="auto">
161+
<name>Task 2: Assign icons to every sidebar item in config.mts</name>
162+
<files>
163+
website/docs/.vitepress/config.mts
164+
</files>
165+
<action>
166+
Update every leaf item in the `/docs/` sidebar to include an `icon` property. Use lucide-vue-next PascalCase names.
167+
168+
Mapping (chosen for semantic fit):
169+
- Introduction → `BookOpen`
170+
- Language Basics → `Code2`
171+
- Type System → `Shapes`
172+
- Iterators → `Repeat`
173+
- Concurrency → `Workflow`
174+
- Web → `Globe`
175+
- Databases → `Database`
176+
- Distributed Actors → `Network`
177+
- Developer Tools → `Wrench`
178+
- Standard Library → `Library`
179+
- Testing → `FlaskConical`
180+
- Syntax Cheatsheet → `ClipboardList`
181+
182+
Example diff for the Getting Started section:
183+
```ts
184+
{
185+
text: 'Getting Started',
186+
items: [
187+
{ text: 'Introduction', link: '/docs/getting-started/', icon: 'BookOpen' },
188+
],
189+
},
190+
```
191+
192+
Apply the same pattern to all 12 leaf items. Do not add icons to group headers (the `text`-only objects with `items` arrays) — those render via DocsSidebarGroup.vue which uses plain text.
193+
194+
Also extend the VitePress `themeConfig` type declaration if TypeScript complains — the `SidebarItem` from VitePress's own types won't have `icon`. Cast each item object with `as any` inline, or add a short module augmentation at the top of config.mts:
195+
196+
```ts
197+
declare module 'vitepress' {
198+
interface UserConfig {
199+
// No-op — using custom SidebarItem type from composable
200+
}
201+
}
202+
```
203+
204+
Actually the simpler fix: the sidebar array is typed via VitePress's own `DefaultTheme.SidebarItem`. Since the custom `DocsSidebarItem.vue` reads from `item.icon` directly (and our `useSidebar.ts` SidebarItem already has the field), VitePress just passes the raw config object through. TypeScript may warn about the extra property — suppress with `// @ts-expect-error` on each icon line, or cast the entire sidebar array `as any`. Use `as any` on the full sidebar value (cleanest).
205+
</action>
206+
<verify>cd /Users/sn0w/Documents/dev/mesh/website && npm run build 2>&1 | tail -20</verify>
207+
<done>Build succeeds with no errors; all 12 sidebar items have icon names assigned in config.mts</done>
208+
</task>
209+
210+
</tasks>
211+
212+
<verification>
213+
After both tasks:
214+
1. Run `cd /Users/sn0w/Documents/dev/mesh/website && npm run build` — build must complete without errors
215+
2. Run `npm run preview` and open http://localhost:4173/docs/ to confirm icons appear in sidebar
216+
3. Verify icons render on both active and inactive items
217+
4. Verify items without explicit icon set (group headers) are unaffected
218+
</verification>
219+
220+
<success_criteria>
221+
- Every leaf sidebar link in the docs has a small Lucide icon to its left
222+
- Build passes without errors or TypeScript failures
223+
- Icons are visually aligned with text (flex row, 14px, muted color matching text)
224+
- No visual regression on mobile sidebar (MobileSidebar.vue also uses DocsSidebarItem)
225+
</success_criteria>
226+
227+
<output>
228+
After completion, create `.planning/quick/10-add-icons-to-each-button-in-the-docs-sid/10-SUMMARY.md`
229+
</output>

0 commit comments

Comments
 (0)