srcdev-nuxt-components 9.4.4 → 9.4.6

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.
Files changed (116) hide show
  1. package/.claude/commands/migrate-component.md +106 -0
  2. package/.claude/component-ledger/artifact-url.txt +1 -0
  3. package/.claude/component-ledger/audit.json +1 -0
  4. package/.claude/component-ledger/build.mjs +111 -0
  5. package/.claude/component-ledger/output.html +559 -0
  6. package/.claude/component-ledger/template.html +559 -0
  7. package/.claude/hooks/refresh-component-ledger.sh +32 -0
  8. package/.claude/settings.json +10 -0
  9. package/.claude/skills/components/animated-svg-text.md +78 -0
  10. package/.claude/skills/components/breadcrumb.md +1 -0
  11. package/.claude/skills/components/canvas-switcher.md +61 -0
  12. package/.claude/skills/components/clip-element.md +67 -0
  13. package/.claude/skills/components/clipped-panel.md +64 -0
  14. package/.claude/skills/components/content-docs.md +2 -0
  15. package/.claude/skills/components/deep-expanding-menu-classic.md +94 -0
  16. package/.claude/skills/components/deep-expanding-menu.md +92 -0
  17. package/.claude/skills/components/display-banner.md +60 -0
  18. package/.claude/skills/components/display-tooltip-defined.md +139 -0
  19. package/.claude/skills/components/display-tooltip.md +85 -0
  20. package/.claude/skills/components/marquee-scroller.md +130 -0
  21. package/.claude/skills/components/navigation-items.md +1 -0
  22. package/.claude/skills/components/responsive-header.md +3 -0
  23. package/.claude/skills/components/site-header.md +4 -0
  24. package/.claude/skills/components/site-navigation.md +1 -0
  25. package/.claude/skills/components/tab-navigation.md +1 -0
  26. package/.claude/skills/composable-tooltips-guide.md +10 -0
  27. package/.claude/skills/index.md +8 -1
  28. package/.claude/skills/storybook-add-story.md +30 -0
  29. package/.vscode/srcdev-component-animated-svg-text.code-snippets +15 -0
  30. package/.vscode/srcdev-component-breadcrumb.code-snippets +13 -0
  31. package/.vscode/srcdev-component-canvas-switcher.code-snippets +9 -0
  32. package/.vscode/srcdev-component-clip-element.code-snippets +20 -0
  33. package/.vscode/srcdev-component-clipped-panel.code-snippets +23 -0
  34. package/.vscode/srcdev-component-content-docs.code-snippets +18 -0
  35. package/.vscode/srcdev-component-deep-expanding-menu-classic.code-snippets +9 -0
  36. package/.vscode/srcdev-component-deep-expanding-menu.code-snippets +9 -0
  37. package/.vscode/srcdev-component-display-banner.code-snippets +30 -0
  38. package/.vscode/srcdev-component-display-tooltip-defined.code-snippets +29 -0
  39. package/.vscode/srcdev-component-display-tooltip.code-snippets +38 -0
  40. package/.vscode/srcdev-component-marquee-scroller.code-snippets +93 -0
  41. package/.vscode/srcdev-component-responsive-header.code-snippets +12 -0
  42. package/.vscode/srcdev-component-site-header.code-snippets +17 -0
  43. package/.vscode/srcdev-component-site-navigation.code-snippets +30 -0
  44. package/.vscode/srcdev-component-slider-gallery.code-snippets +38 -0
  45. package/.vscode/srcdev-component-tab-navigation.code-snippets +30 -0
  46. package/app/components/01.atoms/animations/animated-svg-text/AnimatedSvgText.vue +58 -0
  47. package/app/components/01.atoms/animations/animated-svg-text/CONSUMER-STYLING.md +34 -0
  48. package/app/components/01.atoms/animations/animated-svg-text/stories/AnimatedSvgText.stories.ts +69 -0
  49. package/app/components/01.atoms/animations/animated-svg-text/tests/AnimatedSvgText.spec.ts +63 -0
  50. package/app/components/01.atoms/animations/animated-svg-text/tests/__snapshots__/AnimatedSvgText.spec.ts.snap +7 -0
  51. package/app/components/01.atoms/animations/clip-element/CONSUMER-STYLING.md +27 -0
  52. package/app/components/{clip-element → 01.atoms/animations/clip-element}/ClipElement.vue +16 -11
  53. package/app/components/01.atoms/animations/clip-element/stories/ClipElement.stories.ts +133 -0
  54. package/app/components/01.atoms/animations/clip-element/tests/ClipElement.spec.ts +70 -0
  55. package/app/components/01.atoms/animations/clip-element/tests/__snapshots__/ClipElement.spec.ts.snap +7 -0
  56. package/app/components/01.atoms/animations/marquee-scroller/CONSUMER-STYLING.md +94 -0
  57. package/app/components/01.atoms/animations/marquee-scroller/MarqueeScroller.vue +331 -0
  58. package/app/components/01.atoms/animations/marquee-scroller/stories/MarqueeScroller.stories.ts +151 -0
  59. package/app/components/01.atoms/animations/marquee-scroller/tests/MarqueeScroller.spec.ts +315 -0
  60. package/app/components/01.atoms/animations/marquee-scroller/tests/__snapshots__/MarqueeScroller.spec.ts.snap +28 -0
  61. package/app/components/01.atoms/canvas-switcher/CONSUMER-STYLING.md +27 -0
  62. package/app/components/01.atoms/canvas-switcher/CanvasSwitcher.vue +111 -0
  63. package/app/components/01.atoms/canvas-switcher/stories/CanvasSwitcher.stories.ts +75 -0
  64. package/app/components/01.atoms/canvas-switcher/tests/CanvasSwitcher.spec.ts +67 -0
  65. package/app/components/01.atoms/clipped-panel/CONSUMER-STYLING.md +39 -0
  66. package/app/components/01.atoms/clipped-panel/ClippedPanel.vue +68 -0
  67. package/app/components/01.atoms/clipped-panel/stories/ClippedPanel.stories.ts +105 -0
  68. package/app/components/01.atoms/clipped-panel/tests/ClippedPanel.spec.ts +67 -0
  69. package/app/components/01.atoms/clipped-panel/tests/__snapshots__/ClippedPanel.spec.ts.snap +3 -0
  70. package/app/components/01.atoms/content-wrappers/docs-pages/ContentDocs.vue +8 -2
  71. package/app/components/01.atoms/display-banner/CONSUMER-STYLING.md +35 -0
  72. package/app/components/{display-banner → 01.atoms/display-banner}/DisplayBanner.vue +13 -35
  73. package/app/components/01.atoms/display-banner/stories/DisplayBanner.stories.ts +100 -0
  74. package/app/components/01.atoms/display-banner/tests/DisplayBanner.spec.ts +78 -0
  75. package/app/components/01.atoms/display-banner/tests/__snapshots__/DisplayBanner.spec.ts.snap +8 -0
  76. package/app/components/01.atoms/display-tooltip/CONSUMER-STYLING.md +62 -0
  77. package/app/components/01.atoms/display-tooltip/DisplayTooltip.vue +188 -0
  78. package/app/components/01.atoms/display-tooltip/stories/DisplayTooltip.stories.ts +94 -0
  79. package/app/components/01.atoms/display-tooltip/tests/DisplayTooltip.spec.ts +75 -0
  80. package/app/components/01.atoms/display-tooltip/tests/__snapshots__/DisplayTooltip.spec.ts.snap +12 -0
  81. package/app/components/01.atoms/navigation/breadcrumb/Breadcrumb.vue +4 -1
  82. package/app/components/02.molecules/display-tooltip-defined/CONSUMER-STYLING.md +42 -0
  83. package/app/components/02.molecules/display-tooltip-defined/DisplayTooltipDefined.vue +116 -0
  84. package/app/components/02.molecules/display-tooltip-defined/stories/DisplayTooltipDefined.stories.ts +126 -0
  85. package/app/components/02.molecules/display-tooltip-defined/tests/DisplayTooltipDefined.spec.ts +66 -0
  86. package/app/components/02.molecules/display-tooltip-defined/tests/__snapshots__/DisplayTooltipDefined.spec.ts.snap +17 -0
  87. package/app/components/02.molecules/navigation/deep-expanding-menu/CONSUMER-STYLING.md +44 -0
  88. package/app/components/{deep-expanding-menu → 02.molecules/navigation/deep-expanding-menu}/DeepExpandingMenu.vue +53 -64
  89. package/app/components/02.molecules/navigation/deep-expanding-menu/stories/DeepExpandingMenu.stories.ts +79 -0
  90. package/app/components/02.molecules/navigation/deep-expanding-menu/tests/DeepExpandingMenu.spec.ts +92 -0
  91. package/app/components/02.molecules/navigation/deep-expanding-menu/tests/__snapshots__/DeepExpandingMenu.spec.ts.snap +17 -0
  92. package/app/components/02.molecules/navigation/deep-expanding-menu-classic/CONSUMER-STYLING.md +40 -0
  93. package/app/components/02.molecules/navigation/deep-expanding-menu-classic/DeepExpandingMenuClassic.vue +200 -0
  94. package/app/components/02.molecules/navigation/deep-expanding-menu-classic/stories/DeepExpandingMenuClassic.stories.ts +79 -0
  95. package/app/components/02.molecules/navigation/deep-expanding-menu-classic/tests/DeepExpandingMenuClassic.spec.ts +82 -0
  96. package/app/components/02.molecules/navigation/deep-expanding-menu-classic/tests/__snapshots__/DeepExpandingMenuClassic.spec.ts.snap +18 -0
  97. package/app/components/02.molecules/navigation/site-navigation/SiteNavigation.vue +4 -1
  98. package/app/components/02.molecules/navigation/tab-navigation/TabNavigation.vue +4 -1
  99. package/app/components/03.organisms/image-galleries/slider-gallery/SliderGallery.vue +16 -4
  100. package/app/components/03.organisms/responsive-header/NavigationItems.vue +4 -1
  101. package/app/components/03.organisms/responsive-header/ResponsiveHeader.vue +16 -3
  102. package/app/components/03.organisms/site-header/SiteHeader.vue +16 -1
  103. package/app/components/03.organisms/site-header/tests/__snapshots__/SiteHeader.spec.ts.snap +1 -1
  104. package/app/types/components/display-tooltip-defined.d.ts +10 -0
  105. package/app/types/components/index.ts +2 -0
  106. package/app/types/components/marquee-scroller.d.ts +10 -0
  107. package/package.json +1 -1
  108. package/app/components/animated-svg-text/AnimatedSvgText.vue +0 -89
  109. package/app/components/canvas-switcher/CanvasSwitcher.vue +0 -104
  110. package/app/components/clipped-panels/ClippedPanel.vue +0 -87
  111. package/app/components/deep-expanding-menu/DeepExpandingMenuOld.vue +0 -218
  112. package/app/components/display-details/DisplayDetailsCore.vue +0 -122
  113. package/app/components/display-tooltip/DisplayTooltip.vue +0 -170
  114. package/app/components/display-tooltip/DisplayTooltipDefined.vue +0 -103
  115. package/app/components/magnetic-navigation/MagneticNavigation.vue +0 -162
  116. package/app/components/marquee-scroller/MarqueeScroller.vue +0 -289
@@ -0,0 +1,559 @@
1
+ <title>Component Ledger</title>
2
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap">
3
+ <style>
4
+ :root{
5
+ --paper:#eef0ea;
6
+ --surface:#ffffff;
7
+ --surface-2:#e4e7de;
8
+ --ink:#1b2420;
9
+ --ink-soft:#51594f;
10
+ --ink-faint:#838a7c;
11
+ --line:#cdd2c4;
12
+ --accent:#2f6f5e;
13
+ --accent-soft:#dce9e2;
14
+ --warn:#9c6a1f;
15
+ --warn-soft:#f3e4c8;
16
+ --bad:#a1423a;
17
+ --bad-soft:#f4dcd8;
18
+ --shadow: 0 1px 2px rgba(27,36,32,0.06), 0 8px 24px -12px rgba(27,36,32,0.18);
19
+ }
20
+ @media (prefers-color-scheme: dark){
21
+ :root:not([data-theme="light"]){
22
+ --paper:#12160f;
23
+ --surface:#191f18;
24
+ --surface-2:#222922;
25
+ --ink:#e7ebe0;
26
+ --ink-soft:#aab2a0;
27
+ --ink-faint:#707868;
28
+ --line:#333c30;
29
+ --accent:#67b79d;
30
+ --accent-soft:#1e3630;
31
+ --warn:#d9a54f;
32
+ --warn-soft:#3a2f18;
33
+ --bad:#e0847b;
34
+ --bad-soft:#3d201d;
35
+ --shadow: 0 1px 2px rgba(0,0,0,0.4), 0 8px 24px -12px rgba(0,0,0,0.6);
36
+ }
37
+ }
38
+ :root[data-theme="dark"]{
39
+ --paper:#12160f;
40
+ --surface:#191f18;
41
+ --surface-2:#222922;
42
+ --ink:#e7ebe0;
43
+ --ink-soft:#aab2a0;
44
+ --ink-faint:#707868;
45
+ --line:#333c30;
46
+ --accent:#67b79d;
47
+ --accent-soft:#1e3630;
48
+ --warn:#d9a54f;
49
+ --warn-soft:#3a2f18;
50
+ --bad:#e0847b;
51
+ --bad-soft:#3d201d;
52
+ --shadow: 0 1px 2px rgba(0,0,0,0.4), 0 8px 24px -12px rgba(0,0,0,0.6);
53
+ }
54
+
55
+ *{box-sizing:border-box;}
56
+ body{
57
+ background:var(--paper);
58
+ color:var(--ink);
59
+ font-family:"IBM Plex Sans", system-ui, sans-serif;
60
+ font-size:1.4rem;
61
+ line-height:1.5;
62
+ }
63
+ .mono{font-family:"IBM Plex Mono","SFMono-Regular",Menlo,monospace;}
64
+ ::selection{background:var(--accent-soft); color:var(--ink);}
65
+
66
+ .wrap{
67
+ max-width:112rem;
68
+ margin:0 auto;
69
+ padding:4.8rem 3.2rem 8rem;
70
+ }
71
+
72
+ /* ---------- header ---------- */
73
+ header.top{
74
+ display:flex;
75
+ justify-content:space-between;
76
+ align-items:flex-end;
77
+ gap:3.2rem;
78
+ border-bottom:0.1rem solid var(--line);
79
+ padding-bottom:2.4rem;
80
+ margin-bottom:3.2rem;
81
+ flex-wrap:wrap;
82
+ }
83
+ .title-block .eyebrow{
84
+ font-family:"IBM Plex Mono", monospace;
85
+ font-size:1.1rem;
86
+ letter-spacing:0.14em;
87
+ text-transform:uppercase;
88
+ color:var(--accent);
89
+ margin:0 0 0.8rem;
90
+ }
91
+ h1{
92
+ font-size:3.4rem;
93
+ font-weight:700;
94
+ margin:0 0 0.6rem;
95
+ text-wrap:balance;
96
+ letter-spacing:-0.01em;
97
+ }
98
+ .title-block p{
99
+ margin:0;
100
+ max-width:56rem;
101
+ color:var(--ink-soft);
102
+ font-size:1.5rem;
103
+ }
104
+ .meta-block{
105
+ text-align:right;
106
+ font-family:"IBM Plex Mono", monospace;
107
+ font-size:1.2rem;
108
+ color:var(--ink-faint);
109
+ white-space:nowrap;
110
+ }
111
+ .meta-block strong{color:var(--ink-soft); font-weight:500;}
112
+
113
+ /* ---------- stat strip ---------- */
114
+ .stats{
115
+ display:grid;
116
+ grid-template-columns:repeat(6, 1fr);
117
+ gap:1.2rem;
118
+ margin-bottom:3.6rem;
119
+ }
120
+ .stat{
121
+ background:var(--surface);
122
+ border:0.1rem solid var(--line);
123
+ border-radius:0.6rem;
124
+ padding:1.6rem 1.6rem 1.4rem;
125
+ box-shadow:var(--shadow);
126
+ }
127
+ .stat .num{
128
+ font-family:"IBM Plex Mono", monospace;
129
+ font-variant-numeric:tabular-nums;
130
+ font-size:2.6rem;
131
+ font-weight:600;
132
+ line-height:1;
133
+ }
134
+ .stat .label{
135
+ margin-top:0.6rem;
136
+ font-size:1.15rem;
137
+ color:var(--ink-soft);
138
+ text-transform:uppercase;
139
+ letter-spacing:0.05em;
140
+ }
141
+ .stat .bar{
142
+ margin-top:1rem;
143
+ height:0.4rem;
144
+ border-radius:99rem;
145
+ background:var(--surface-2);
146
+ overflow:hidden;
147
+ }
148
+ .stat .bar > span{
149
+ display:block;
150
+ height:100%;
151
+ background:var(--accent);
152
+ border-radius:99rem;
153
+ }
154
+ .stat.low .bar > span{background:var(--bad);}
155
+ .stat.mid .bar > span{background:var(--warn);}
156
+
157
+ /* ---------- narrative ---------- */
158
+ .finding{
159
+ display:grid;
160
+ grid-template-columns:1.4fr 1fr;
161
+ gap:2.4rem;
162
+ margin-bottom:4rem;
163
+ }
164
+ @media (max-width:820px){ .finding{grid-template-columns:1fr;} }
165
+ .finding h2{
166
+ font-size:1.9rem;
167
+ margin:0 0 1rem;
168
+ }
169
+ .finding p{margin:0 0 1rem; color:var(--ink-soft);}
170
+ .finding p:last-child{margin-bottom:0;}
171
+ .finding .card{
172
+ background:var(--surface);
173
+ border:0.1rem solid var(--line);
174
+ border-radius:0.6rem;
175
+ padding:2rem 2.2rem;
176
+ box-shadow:var(--shadow);
177
+ }
178
+ .card ol, .card ul{margin:0; padding-left:1.8rem;}
179
+ .card li{margin-bottom:0.7rem; color:var(--ink-soft);}
180
+ .card li:last-child{margin-bottom:0;}
181
+ .card li strong{color:var(--ink);}
182
+
183
+ /* ---------- controls ---------- */
184
+ .controls{
185
+ display:flex;
186
+ flex-wrap:wrap;
187
+ align-items:center;
188
+ gap:1rem;
189
+ margin-bottom:1.6rem;
190
+ }
191
+ .chip-group{display:flex; flex-wrap:wrap; gap:0.6rem;}
192
+ .chip{
193
+ font-family:"IBM Plex Mono", monospace;
194
+ font-size:1.15rem;
195
+ padding:0.5rem 1.1rem;
196
+ border-radius:99rem;
197
+ border:0.1rem solid var(--line);
198
+ background:var(--surface);
199
+ color:var(--ink-soft);
200
+ cursor:pointer;
201
+ transition:background .12s, color .12s, border-color .12s;
202
+ }
203
+ .chip:hover{border-color:var(--accent);}
204
+ .chip.active{
205
+ background:var(--ink);
206
+ border-color:var(--ink);
207
+ color:var(--paper);
208
+ }
209
+ .search{
210
+ margin-left:auto;
211
+ display:flex;
212
+ align-items:center;
213
+ gap:0.6rem;
214
+ background:var(--surface);
215
+ border:0.1rem solid var(--line);
216
+ border-radius:0.5rem;
217
+ padding:0.5rem 1rem;
218
+ }
219
+ .search input{
220
+ border:none;
221
+ background:transparent;
222
+ outline:none;
223
+ font-family:"IBM Plex Mono", monospace;
224
+ font-size:1.3rem;
225
+ color:var(--ink);
226
+ width:18rem;
227
+ }
228
+ .search input::placeholder{color:var(--ink-faint);}
229
+ .count-note{
230
+ font-family:"IBM Plex Mono", monospace;
231
+ font-size:1.15rem;
232
+ color:var(--ink-faint);
233
+ margin-bottom:1rem;
234
+ }
235
+
236
+ /* ---------- table ---------- */
237
+ .table-scroll{
238
+ overflow-x:auto;
239
+ border:0.1rem solid var(--line);
240
+ border-radius:0.6rem;
241
+ box-shadow:var(--shadow);
242
+ }
243
+ table{
244
+ width:100%;
245
+ border-collapse:collapse;
246
+ background:var(--surface);
247
+ min-width:78rem;
248
+ }
249
+ thead th{
250
+ position:sticky;
251
+ top:0;
252
+ background:var(--surface-2);
253
+ z-index:2;
254
+ text-align:left;
255
+ font-size:1.05rem;
256
+ text-transform:uppercase;
257
+ letter-spacing:0.05em;
258
+ color:var(--ink-soft);
259
+ font-weight:600;
260
+ padding:1rem 1rem;
261
+ border-bottom:0.1rem solid var(--line);
262
+ cursor:pointer;
263
+ white-space:nowrap;
264
+ user-select:none;
265
+ }
266
+ thead th:hover{color:var(--ink);}
267
+ thead th.sorted::after{content:" ▾"; color:var(--accent);}
268
+ thead th.sorted.asc::after{content:" ▴";}
269
+ tbody td{
270
+ padding:0.9rem 1rem;
271
+ border-bottom:0.1rem solid var(--line);
272
+ font-size:1.3rem;
273
+ vertical-align:middle;
274
+ }
275
+ tbody tr:last-child td{border-bottom:none;}
276
+ tbody tr:hover td{background:var(--surface-2);}
277
+ td.path{
278
+ font-family:"IBM Plex Mono", monospace;
279
+ font-size:1.2rem;
280
+ color:var(--ink);
281
+ }
282
+ td.path .n{color:var(--ink-faint); font-size:1.05rem; margin-left:0.4rem;}
283
+ .tier-pill{
284
+ font-family:"IBM Plex Mono", monospace;
285
+ font-size:1.05rem;
286
+ padding:0.15rem 0.6rem;
287
+ border-radius:0.3rem;
288
+ background:var(--surface-2);
289
+ color:var(--ink-soft);
290
+ white-space:nowrap;
291
+ }
292
+ .tier-pill.none{background:var(--bad-soft); color:var(--bad);}
293
+ td.check{text-align:center; font-size:1.5rem; line-height:1;}
294
+ .yes{color:var(--accent);}
295
+ .no{color:var(--ink-faint); opacity:0.5;}
296
+ .priv-count{
297
+ font-family:"IBM Plex Mono", monospace;
298
+ font-variant-numeric:tabular-nums;
299
+ font-size:1.2rem;
300
+ }
301
+ .priv-count.zero{color:var(--ink-faint);}
302
+ .priv-count.some{color:var(--ink-soft);}
303
+ .priv-count.many{color:var(--warn); font-weight:600;}
304
+ .score-bar{
305
+ display:inline-flex;
306
+ gap:0.2rem;
307
+ vertical-align:middle;
308
+ }
309
+ .score-bar span{
310
+ width:0.7rem;
311
+ height:0.7rem;
312
+ border-radius:50%;
313
+ background:var(--surface-2);
314
+ border:0.1rem solid var(--line);
315
+ }
316
+ .score-bar span.on{background:var(--accent); border-color:var(--accent);}
317
+
318
+ footer.note{
319
+ margin-top:2.4rem;
320
+ font-size:1.2rem;
321
+ color:var(--ink-faint);
322
+ max-width:80rem;
323
+ }
324
+ footer.note code{
325
+ font-family:"IBM Plex Mono", monospace;
326
+ background:var(--surface-2);
327
+ padding:0.1rem 0.4rem;
328
+ border-radius:0.3rem;
329
+ }
330
+
331
+ @media (max-width:900px){
332
+ .stats{grid-template-columns:repeat(3,1fr);}
333
+ }
334
+ @media (max-width:560px){
335
+ .stats{grid-template-columns:repeat(2,1fr);}
336
+ .wrap{padding:3.2rem 1.6rem 6rem;}
337
+ h1{font-size:2.6rem;}
338
+ }
339
+ </style>
340
+
341
+ <div class="wrap">
342
+
343
+ <header class="top">
344
+ <div class="title-block">
345
+ <p class="eyebrow">nuxt-components · definition of done</p>
346
+ <h1>Component Ledger</h1>
347
+ <p>A scripted pass over every component folder against the current standard: tier placement, consumer styling docs, tests, Storybook coverage, public skill docs, and VS Code snippets. Regenerated automatically whenever a component change is staged for commit.</p>
348
+ </div>
349
+ <div class="meta-block">
350
+ last rebuilt <strong id="audited-at">—</strong><br>
351
+ <span id="rowcount">—</span> component groups · <span id="vuecount">—</span> .vue files
352
+ </div>
353
+ </header>
354
+
355
+ <section class="stats" id="stats"></section>
356
+
357
+ <section class="finding">
358
+ <div id="narrative"></div>
359
+ <div class="card">
360
+ <h2 style="margin-top:0;">Reference-quality components</h2>
361
+ <p style="margin-bottom:1rem; color:var(--ink-soft);">Pass every doc/test/story/skill/snippet check, sit in a tier, no stray <code class="mono">variants/</code> folder. Point future work at these when in doubt about the shape of "done":</p>
362
+ <ol id="reference-list"></ol>
363
+ </div>
364
+ </section>
365
+
366
+ <div class="controls">
367
+ <div class="chip-group" id="tier-filters"></div>
368
+ <div class="chip-group">
369
+ <button class="chip" data-gap-filter="0">only incomplete</button>
370
+ </div>
371
+ <div class="search">
372
+ <span class="mono" style="color:var(--ink-faint);">⌕</span>
373
+ <input type="text" id="search" placeholder="filter by path…">
374
+ </div>
375
+ </div>
376
+ <p class="count-note"><span id="visible-count">0</span> shown &middot; click a column heading to sort</p>
377
+
378
+ <div class="table-scroll">
379
+ <table>
380
+ <thead>
381
+ <tr>
382
+ <th data-key="compdir" data-type="str">Component</th>
383
+ <th data-key="tier" data-type="str">Tier</th>
384
+ <th data-key="consumer_styling" data-type="bool" title="CONSUMER-STYLING.md present">Styling doc</th>
385
+ <th data-key="priv_tokens" data-type="num" title="Count of --_ prefixed vars — informational, not a defect count; see footer">--_ vars</th>
386
+ <th data-key="tests" data-type="bool">Tests</th>
387
+ <th data-key="stories" data-type="bool">Story</th>
388
+ <th data-key="skill" data-type="bool">Skill doc</th>
389
+ <th data-key="snippet" data-type="bool">Snippet</th>
390
+ <th data-key="variants" data-type="bool" title="Uses a variants/ subfolder">Variants folder</th>
391
+ <th data-key="legacy_props" data-type="bool" title="Still options-style defineProps({...}), not defineProps&lt;Props&gt;()">Legacy props</th>
392
+ <th data-key="score" data-type="num">Score</th>
393
+ </tr>
394
+ </thead>
395
+ <tbody id="tbody"></tbody>
396
+ </table>
397
+ </div>
398
+
399
+ <footer class="note">
400
+ <p><strong>Methodology:</strong> "Styling doc", "Tests", "Story" and "Variants folder" are filesystem checks (<code>CONSUMER-STYLING.md</code>, a <code>tests/</code> dir, a <code>stories/</code> dir or <code>*.stories.ts</code> file, a <code>variants/</code> subfolder). "Skill doc" and "Snippet" match the component's kebab-case name against <code>.claude/skills/components/*.md</code> and <code>.vscode/*.code-snippets</code> &mdash; a small number of near-miss names may read as false negatives and are worth a manual glance before acting. "Score" is the count of the five doc/test/story/skill/snippet checks passed, out of 5, and is what "reference-quality" and "only incomplete" are based on. "Legacy props" is a source check for options-style <code>defineProps({...})</code> rather than <code>defineProps&lt;Props&gt;()</code> &mdash; like "Variants folder" it is <em>not</em> counted in the 5-point score (deliberately, so it stays visible instead of vanishing into an already-high score), but it does factor into "reference-quality" and "only incomplete", same as "Variants folder".</p>
401
+ <p style="margin-top:0.8rem;"><strong>On <code>--_</code> vars:</strong> the public/private split is decided by consumer relevance, not reuse count &mdash; a private var is correct even used once, as long as the value has no plausible consumer override. This column is a raw count of <code>--_</code>-prefixed CSS custom properties per group; it is <em>not</em> scored and doesn't factor into "reference-quality" or "score." A high count is only a prompt to check each one by hand for a value that should have been public (a colour, size, or per-state variant with no override token) &mdash; the bug class the <code>ServicesCard</code> fix (CLAUDE.md pitfall #20) caught, not the presence of the prefix itself.</p>
402
+ <p style="margin-top:0.8rem;"><strong>This page regenerates from <code>.claude/component-ledger/build.mjs</code></strong>, triggered whenever a staged commit touches <code>app/components/</code> (see the <code>create-commit-message</code> skill's hook). All numbers on this page, including the narrative above, are computed live from the latest scan &mdash; nothing here is hand-written prose that can drift out of date.</p>
403
+ </footer>
404
+
405
+ </div>
406
+
407
+ <script>
408
+ const PAYLOAD = {"auditedAt":"2026-09-08","rows":[{"compdir":"01.atoms/animations/animated-svg-text","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"01.atoms/animations/clip-element","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":1,"score":5},{"compdir":"01.atoms/animations/entry","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"01.atoms/animations/marquee-scroller","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":2,"score":5},{"compdir":"01.atoms/animations/scroll-reveal-frame","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":3,"score":3},{"compdir":"01.atoms/animations/scroll-reveal-image","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":1,"score":3},{"compdir":"01.atoms/banner-video","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":4,"score":5},{"compdir":"01.atoms/canvas-switcher","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"01.atoms/card","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":13,"score":5},{"compdir":"01.atoms/clipped-panel","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":1,"score":5},{"compdir":"01.atoms/content-wrappers/content-columns-2","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":0,"score":2},{"compdir":"01.atoms/content-wrappers/content-width","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":0,"score":2},{"compdir":"01.atoms/content-wrappers/docs-pages","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":51,"score":4},{"compdir":"01.atoms/content-wrappers/layout-grid/layout-grid-by-cols","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":1,"score":3},{"compdir":"01.atoms/content-wrappers/layout-grid/layout-grid-by-width","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":1,"score":2},{"compdir":"01.atoms/cookie-consent-banner","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":7,"score":3},{"compdir":"01.atoms/display-avatar","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"01.atoms/display-banner","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"01.atoms/display-dialog","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":21,"score":4},{"compdir":"01.atoms/display-pill","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":14,"score":3},{"compdir":"01.atoms/display-tooltip","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":1,"score":5},{"compdir":"01.atoms/glass-panel","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":true,"snippet":false,"priv_tokens":4,"score":1},{"compdir":"01.atoms/grids/data-grid","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"01.atoms/grids/grid-stack","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"01.atoms/navigation/breadcrumb","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":7,"score":5},{"compdir":"01.atoms/page-row","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":5,"score":4},{"compdir":"01.atoms/prompt","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":4},{"compdir":"01.atoms/qr-code","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"01.atoms/text-block","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"01.atoms/text-blocks/eyebrow-text","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"01.atoms/text-blocks/hero-text","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":1,"score":4},{"compdir":"01.atoms/text-blocks/link-text","tier":"01.atoms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"01.atoms/toast","tier":"01.atoms","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":6,"score":5},{"compdir":"02.molecules/action-menu","tier":"02.molecules","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":21,"score":4},{"compdir":"02.molecules/alert-content","tier":"02.molecules","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":1,"score":3},{"compdir":"02.molecules/alert-masked-content","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":1,"score":2},{"compdir":"02.molecules/display-chip","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":1,"score":3},{"compdir":"02.molecules/display-tooltip-defined","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"02.molecules/expandable/accordian","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"02.molecules/expandable/expanding-panel","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"02.molecules/expandable/expanding-panel-classic","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"02.molecules/input-copy","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":16,"score":5},{"compdir":"02.molecules/navigation/deep-expanding-menu","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":2,"score":5},{"compdir":"02.molecules/navigation/deep-expanding-menu-classic","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":1,"score":5},{"compdir":"02.molecules/navigation/navigation-horizontal","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":20,"score":3},{"compdir":"02.molecules/navigation/site-navigation","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":45,"score":4},{"compdir":"02.molecules/navigation/tab-navigation","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":28,"score":5},{"compdir":"02.molecules/price-list","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":8,"score":2},{"compdir":"02.molecules/pricing-card","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":28,"score":5},{"compdir":"02.molecules/profile-section","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"02.molecules/qr-code","tier":"02.molecules","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"02.molecules/samaritan-prompt","tier":"02.molecules","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":false,"skill":true,"snippet":false,"priv_tokens":14,"score":2},{"compdir":"02.molecules/social-icons-list","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":2,"score":3},{"compdir":"02.molecules/stepper-list","tier":"02.molecules","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":22,"score":4},{"compdir":"03.organisms/colour-finder","tier":"03.organisms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":20,"score":0},{"compdir":"03.organisms/image-galleries/slider-gallery","tier":"03.organisms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":false,"snippet":true,"priv_tokens":12,"score":3},{"compdir":"03.organisms/responsive-header","tier":"03.organisms","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":18,"score":5},{"compdir":"03.organisms/services/service-detail","tier":"03.organisms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"03.organisms/services/service-summary","tier":"03.organisms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":1,"score":5},{"compdir":"03.organisms/services/services-card","tier":"03.organisms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"03.organisms/services/services-grids","tier":"03.organisms","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"03.organisms/site-header","tier":"03.organisms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":true,"snippet":true,"priv_tokens":0,"score":5},{"compdir":"04.templates/page-hero-highlights","tier":"04.templates","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":0,"score":3},{"compdir":"05.forms/form-errors","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"05.forms/form-field","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":4,"score":0},{"compdir":"05.forms/form-fieldset","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"05.forms/form-wrapper","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"05.forms/input-button","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":2,"score":3},{"compdir":"05.forms/input-checkbox","tier":"05.forms","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"05.forms/input-checkbox-radio","tier":"05.forms","n_vue":3,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":3,"score":3},{"compdir":"05.forms/input-description","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"05.forms/input-label","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"05.forms/input-number","tier":"05.forms","n_vue":2,"variants":true,"legacy_props":false,"consumer_styling":true,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":4,"score":1},{"compdir":"05.forms/input-radio","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"05.forms/input-range","tier":"05.forms","n_vue":2,"variants":true,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"05.forms/input-range-fancy","tier":"05.forms","n_vue":2,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"05.forms/input-select","tier":"05.forms","n_vue":2,"variants":true,"legacy_props":false,"consumer_styling":true,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":6,"score":2},{"compdir":"05.forms/input-text","tier":"05.forms","n_vue":4,"variants":true,"legacy_props":false,"consumer_styling":true,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":5,"score":2},{"compdir":"05.forms/input-textarea","tier":"05.forms","n_vue":2,"variants":true,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"05.forms/pending-effect","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"05.forms/toggle-switch","tier":"05.forms","n_vue":3,"variants":true,"legacy_props":false,"consumer_styling":true,"tests":false,"stories":true,"skill":false,"snippet":true,"priv_tokens":15,"score":3},{"compdir":"05.forms/triple-toggle-switch","tier":"05.forms","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":true,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":11,"score":2},{"compdir":"alert-mask","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":true,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"carousels","tier":"NONE","n_vue":3,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":true,"stories":true,"skill":true,"snippet":false,"priv_tokens":6,"score":3},{"compdir":"container-glow","tier":"NONE","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":0,"score":2},{"compdir":"display-theme-switch","tier":"NONE","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":8,"score":1},{"compdir":"glowing-border","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"layout-grids","tier":"NONE","n_vue":2,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":true,"stories":true,"skill":false,"snippet":false,"priv_tokens":3,"score":2},{"compdir":"masonry-grid","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":4,"score":0},{"compdir":"masonry-grid-ordered","tier":"NONE","n_vue":2,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":12,"score":1},{"compdir":"masonry-grid-sorted","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":3,"score":0},{"compdir":"parallax","tier":"NONE","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":true,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"pop-over","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"rotating-carousel","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":5,"score":0},{"compdir":"skip-links","tier":"NONE","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"tabs","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":11,"score":0},{"compdir":"test-storybook","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":true,"skill":false,"snippet":false,"priv_tokens":0,"score":1},{"compdir":"typography","tier":"NONE","n_vue":1,"variants":false,"legacy_props":false,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"ui-block-decorated","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0},{"compdir":"view-timeline","tier":"NONE","n_vue":1,"variants":false,"legacy_props":true,"consumer_styling":false,"tests":false,"stories":false,"skill":false,"snippet":false,"priv_tokens":0,"score":0}]};
409
+ const DATA = PAYLOAD.rows;
410
+
411
+ document.getElementById("audited-at").textContent = PAYLOAD.auditedAt;
412
+ document.getElementById("rowcount").textContent = DATA.length;
413
+ document.getElementById("vuecount").textContent = DATA.reduce((s,d) => s + d.n_vue, 0);
414
+
415
+ // ---- stat strip ----
416
+ const statDefs = [
417
+ { key:"consumer_styling", label:"Styling doc" },
418
+ { key:"tests", label:"Tests" },
419
+ { key:"stories", label:"Story" },
420
+ { key:"skill", label:"Skill doc" },
421
+ { key:"snippet", label:"Snippet" },
422
+ { key:"placed", label:"In a tier", derive:d => d.tier !== "NONE" },
423
+ ];
424
+ const statsEl = document.getElementById("stats");
425
+ statDefs.forEach(sd => {
426
+ const count = DATA.filter(d => sd.derive ? sd.derive(d) : d[sd.key]).length;
427
+ const pct = DATA.length ? Math.round((count / DATA.length) * 100) : 0;
428
+ const cls = pct < 40 ? "low" : pct < 70 ? "mid" : "";
429
+ const el = document.createElement("div");
430
+ el.className = "stat " + cls;
431
+ el.innerHTML = `
432
+ <div class="num">${pct}<span style="font-size:1.5rem;">%</span></div>
433
+ <div class="label">${sd.label} &middot; ${count}/${DATA.length}</div>
434
+ <div class="bar"><span style="width:${pct}%"></span></div>
435
+ `;
436
+ statsEl.appendChild(el);
437
+ });
438
+
439
+ // ---- dynamic narrative (every number computed from DATA, nothing hand-written) ----
440
+ const fullyCompliant = DATA.filter(d => d.score === 5 && d.tier !== "NONE" && !d.variants && !d.legacy_props);
441
+ const unplaced = DATA.filter(d => d.tier === "NONE");
442
+ const forkedVariants = DATA.filter(d => d.variants);
443
+ const legacyProps = DATA.filter(d => d.legacy_props);
444
+ const noSkillAndUnplaced = unplaced.filter(d => !d.skill).length;
445
+ const topPrivate = DATA.filter(d => d.priv_tokens > 0).sort((a,b) => b.priv_tokens - a.priv_tokens).slice(0, 3);
446
+
447
+ const narrativeEl = document.getElementById("narrative");
448
+ narrativeEl.innerHTML = `
449
+ <h2>What's dragging the average down</h2>
450
+ <p>Only <strong>${fullyCompliant.length} of ${DATA.length}</strong> component groups clear every check. The gaps cluster in a predictable order: the older a component, the less likely it ever got a skill doc or CONSUMER-STYLING.md written for it, because both postdate a lot of what's in this tree.</p>
451
+ <p><strong>${unplaced.length} groups</strong> still sit loose at <code class="mono">app/components/</code> top level rather than inside a numbered tier. ${noSkillAndUnplaced} of those ${unplaced.length} also have no skill doc &mdash; an unplaced component tends to also be an undocumented one.</p>
452
+ <p>${forkedVariants.length ? `<strong>${forkedVariants.length} group${forkedVariants.length===1?"":"s"}</strong> still fork${forkedVariants.length===1?"s":""} variants into a <code class="mono">variants/</code> subfolder &mdash; deprecated in favour of documenting the variation in the parent skill.md.` : `No component groups currently fork variants into a <code class="mono">variants/</code> subfolder.`}</p>
453
+ <p>${legacyProps.length ? `<strong>${legacyProps.length} group${legacyProps.length===1?"":"s"}</strong> still use${legacyProps.length===1?"s":""} options-style <code class="mono">defineProps({...})</code> instead of <code class="mono">defineProps&lt;Props&gt;()</code> &mdash; this doesn't move the 5-point score, so it can hide behind an otherwise-complete-looking group.` : `No component groups currently use options-style defineProps.`}</p>
454
+ <p><strong>${topPrivate.length ? "On --_ vars:" : ""}</strong> ${topPrivate.length
455
+ ? `the largest private-token surfaces right now are ${topPrivate.map(d => `<code class="mono">${d.compdir.split("/").pop()}</code> (${d.priv_tokens})`).join(", ")} &mdash; worth a manual look for a value that should have been public, per the corrected rule in the footer below. A high count on its own isn't a defect.`
456
+ : ""}</p>
457
+ `;
458
+
459
+ // ---- reference list ----
460
+ const refList = document.getElementById("reference-list");
461
+ fullyCompliant.forEach(d => {
462
+ const li = document.createElement("li");
463
+ li.innerHTML = `<strong>${d.compdir.split("/").pop()}</strong>`;
464
+ refList.appendChild(li);
465
+ });
466
+ if (!refList.children.length) {
467
+ refList.innerHTML = "<li>No component currently clears every check.</li>";
468
+ }
469
+
470
+ // ---- tier filter chips ----
471
+ const tiers = [...new Set(DATA.map(d => d.tier))].sort();
472
+ const tierFilters = document.getElementById("tier-filters");
473
+ let activeTier = "all";
474
+ const allChip = mkChip("all components", "all");
475
+ tierFilters.appendChild(allChip);
476
+ tiers.forEach(t => tierFilters.appendChild(mkChip(t === "NONE" ? "unplaced" : t, t)));
477
+
478
+ function mkChip(label, value){
479
+ const b = document.createElement("button");
480
+ b.className = "chip" + (value === "all" ? " active" : "");
481
+ b.textContent = label;
482
+ b.dataset.tier = value;
483
+ b.addEventListener("click", () => {
484
+ activeTier = value;
485
+ [...tierFilters.children].forEach(c => c.classList.toggle("active", c === b));
486
+ render();
487
+ });
488
+ return b;
489
+ }
490
+
491
+ let onlyIncomplete = false;
492
+ document.querySelector("[data-gap-filter]").addEventListener("click", (e) => {
493
+ onlyIncomplete = !onlyIncomplete;
494
+ e.target.classList.toggle("active", onlyIncomplete);
495
+ render();
496
+ });
497
+
498
+ const searchEl = document.getElementById("search");
499
+ searchEl.addEventListener("input", render);
500
+
501
+ let sortKey = "score";
502
+ let sortAsc = true;
503
+ document.querySelectorAll("thead th").forEach(th => {
504
+ th.addEventListener("click", () => {
505
+ const key = th.dataset.key;
506
+ if (sortKey === key) sortAsc = !sortAsc;
507
+ else { sortKey = key; sortAsc = key !== "score"; }
508
+ document.querySelectorAll("thead th").forEach(t => t.classList.remove("sorted","asc"));
509
+ th.classList.add("sorted");
510
+ if (sortAsc) th.classList.add("asc");
511
+ render();
512
+ });
513
+ });
514
+
515
+ function tierLabel(t){ return t === "NONE" ? "unplaced" : t; }
516
+
517
+ function render(){
518
+ let rows = DATA.slice();
519
+ if (activeTier !== "all") rows = rows.filter(d => d.tier === activeTier);
520
+ if (onlyIncomplete) rows = rows.filter(d => d.score < 5 || d.tier === "NONE" || d.variants || d.legacy_props);
521
+ const q = searchEl.value.trim().toLowerCase();
522
+ if (q) rows = rows.filter(d => d.compdir.toLowerCase().includes(q));
523
+
524
+ rows.sort((a,b) => {
525
+ let av = a[sortKey], bv = b[sortKey];
526
+ if (typeof av === "boolean") { av = av ? 1 : 0; bv = bv ? 1 : 0; }
527
+ if (typeof av === "string") return sortAsc ? av.localeCompare(bv) : bv.localeCompare(av);
528
+ return sortAsc ? av - bv : bv - av;
529
+ });
530
+
531
+ document.getElementById("visible-count").textContent = rows.length;
532
+
533
+ const tbody = document.getElementById("tbody");
534
+ tbody.innerHTML = rows.map(d => {
535
+ const privClass = d.priv_tokens === 0 ? "zero" : d.priv_tokens > 15 ? "many" : "some";
536
+ const dots = [d.consumer_styling, d.tests, d.stories, d.skill, d.snippet]
537
+ .map(v => `<span class="${v ? "on" : ""}"></span>`).join("");
538
+ return `<tr>
539
+ <td class="path">${d.compdir}<span class="n">${d.n_vue > 1 ? "×" + d.n_vue : ""}</span></td>
540
+ <td><span class="tier-pill ${d.tier === "NONE" ? "none" : ""} mono">${tierLabel(d.tier)}</span></td>
541
+ <td class="check">${check(d.consumer_styling)}</td>
542
+ <td class="priv-count ${privClass}">${d.priv_tokens}</td>
543
+ <td class="check">${check(d.tests)}</td>
544
+ <td class="check">${check(d.stories)}</td>
545
+ <td class="check">${check(d.skill)}</td>
546
+ <td class="check">${check(d.snippet)}</td>
547
+ <td class="check">${d.variants ? '<span class="no" style="opacity:1;color:var(--warn);">●</span>' : '<span class="no">–</span>'}</td>
548
+ <td class="check">${d.legacy_props ? '<span class="no" style="opacity:1;color:var(--warn);">●</span>' : '<span class="no">–</span>'}</td>
549
+ <td><span class="score-bar">${dots}</span></td>
550
+ </tr>`;
551
+ }).join("");
552
+ }
553
+
554
+ function check(v){
555
+ return v ? '<span class="yes">●</span>' : '<span class="no">–</span>';
556
+ }
557
+
558
+ render();
559
+ </script>