@grove-dev/astro 0.2.19 → 0.2.20

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 (37) hide show
  1. package/dist/theme.test.js +14 -0
  2. package/dist/theme.test.js.map +1 -1
  3. package/package.json +1 -1
  4. package/src/components/CategoryGrid.astro +2 -11
  5. package/src/components/ContributorsGrid.astro +2 -11
  6. package/src/components/CurationGrid.astro +1 -1
  7. package/src/components/DecisionRow.astro +1 -1
  8. package/src/components/ExploreByCategory.astro +3 -3
  9. package/src/components/ExploreByStack.astro +3 -3
  10. package/src/components/FilterGroupMenu.astro +5 -5
  11. package/src/components/Hero.astro +8 -8
  12. package/src/components/Icon.astro +6 -33
  13. package/src/components/IndexRow.astro +5 -5
  14. package/src/components/ItemCard.astro +5 -16
  15. package/src/components/MinimalAbout.astro +2 -2
  16. package/src/components/OriginalCollection.astro +4 -4
  17. package/src/components/Pagination.astro +2 -2
  18. package/src/components/RecordSection.astro +1 -1
  19. package/src/components/RefinePanel.astro +1 -1
  20. package/src/components/SmartLensTabs.astro +3 -3
  21. package/src/components/StackGrid.astro +6 -15
  22. package/src/components/WhyThisExists.astro +3 -3
  23. package/src/layouts/Container.astro +4 -4
  24. package/src/layouts/Footer.astro +3 -3
  25. package/src/layouts/Header.astro +5 -5
  26. package/src/layouts/ThemeToggle.astro +1 -1
  27. package/src/styles.css +33 -339
  28. package/src/theme.test.ts +15 -0
  29. package/templates/default/public/icons/stacks/rag.svg +7 -0
  30. package/templates/default/public/llms-full.txt +1 -1
  31. package/templates/default/public/sitemap.xml +10 -10
  32. package/templates/default/src/pages/404.astro +5 -5
  33. package/templates/default/src/pages/[slug]/[recordSlug].astro +27 -31
  34. package/templates/default/src/pages/[slug]/index.astro +8 -8
  35. package/templates/default/src/pages/about.astro +9 -9
  36. package/templates/default/src/pages/contributors.astro +8 -17
  37. package/templates/default/src/pages/submit.astro +23 -31
@@ -101,18 +101,14 @@ const tags = record.tags ?? [];
101
101
  const healthStatus = proj?.health?.status;
102
102
  const healthLabel = healthStatus ? statusDisplay(healthStatus) : null;
103
103
 
104
- // ── Badge class lookup ────────────────────────────────────────────
105
- // The previous code interpolated `LabelId` straight into the class
106
- // string (`grove-badge-{l}`), which Astro renders as a literal class
107
- // name — every badge became `grove-badge-{l}` regardless of value.
108
- // Map each known id to its stable CSS class in the package
109
- // stylesheet. Unknown ids (e.g. legacy "featured") fall back to the
110
- // neutral `.grove-badge` look instead of producing broken classes.
104
+ // Map each known label to a static Tailwind class string so every
105
+ // utility is visible to Tailwind's source scanner. Unknown/legacy
106
+ // labels use the neutral secondary badge treatment.
111
107
  const labelClassMap: Record<LabelId, string> = {
112
- new: "grove-badge-new",
113
- hot: "grove-badge-hot",
114
- mature: "grove-badge-mature",
115
- featured: "grove-badge",
108
+ new: "!bg-emerald-50 !text-emerald-700 dark:!bg-emerald-950/80 dark:!text-emerald-300",
109
+ hot: "!bg-orange-50 !text-orange-800 dark:!bg-orange-950/80 dark:!text-orange-300",
110
+ mature: "!bg-blue-50 !text-blue-700 dark:!bg-blue-950/80 dark:!text-blue-300",
111
+ featured: "inline-flex min-h-5 items-center justify-center gap-1 rounded-full border border-transparent bg-secondary px-2 py-0.5 align-middle text-xs font-medium leading-none text-secondary-foreground",
116
112
  };
117
113
 
118
114
  // ── Optional content markdown (record.content is a path under
@@ -248,8 +244,8 @@ if (isProject && proj) {
248
244
  submitHref="/submit"
249
245
  submitLabel={`Submit ${name}`}
250
246
  >
251
- <article class="grove-section">
252
- <div class="grove-container-wide mx-auto px-5 sm:px-7">
247
+ <article class="py-12 sm:py-16">
248
+ <div class="mx-auto w-full max-w-wide px-5 sm:px-7">
253
249
  <nav class="mb-6 text-2xs text-ink-500 dark:text-ink-400" aria-label="Breadcrumb">
254
250
  <a href={`/${slug}`} class="hover:text-ink-900 dark:hover:text-ink-100 capitalize">
255
251
  {slug}
@@ -259,7 +255,7 @@ if (isProject && proj) {
259
255
  </nav>
260
256
 
261
257
  {/* ── AppCard-style header ──────────────────────────────── */}
262
- <header class="grove-card flex flex-col gap-5 p-6 sm:flex-row sm:items-start">
258
+ <header class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors flex flex-col gap-5 p-6 sm:flex-row sm:items-start">
263
259
  <div class="flex-shrink-0">
264
260
  {avatarSrc ? (
265
261
  <img
@@ -292,7 +288,7 @@ if (isProject && proj) {
292
288
  </span>
293
289
  )}
294
290
  {curationLabels.map((l) => (
295
- <span class={`grove-badge ${labelClassMap[l as LabelId] ?? ""}`} title={`Curated: ${labelDisplay(l) ?? l}`}>
291
+ <span class={`inline-flex min-h-5 items-center justify-center gap-1 rounded-full border border-transparent bg-secondary px-2 py-0.5 align-middle text-xs font-medium leading-none text-secondary-foreground ${labelClassMap[l as LabelId] ?? ""}`} title={`Curated: ${labelDisplay(l) ?? l}`}>
296
292
  {labelDisplay(l) ?? prettySlug(l)}
297
293
  </span>
298
294
  ))}
@@ -308,7 +304,7 @@ if (isProject && proj) {
308
304
  </span>
309
305
  )}
310
306
  {healthLabel && (
311
- <span class="grove-badge grove-badge-stale">{healthLabel}</span>
307
+ <span class="inline-flex min-h-5 items-center justify-center gap-1 rounded-full border border-transparent bg-secondary px-2 py-0.5 align-middle text-xs font-medium leading-none text-secondary-foreground !bg-amber-50 !text-amber-800 dark:!bg-amber-950/80 dark:!text-amber-300">{healthLabel}</span>
312
308
  )}
313
309
  </div>
314
310
 
@@ -350,7 +346,7 @@ if (isProject && proj) {
350
346
  href={repoUrl}
351
347
  target="_blank"
352
348
  rel="noopener noreferrer"
353
- class="grove-btn grove-btn-primary"
349
+ class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 bg-primary text-primary-foreground hover:bg-primary/90"
354
350
  >
355
351
  View on GitHub →
356
352
  </a>
@@ -360,7 +356,7 @@ if (isProject && proj) {
360
356
  href={homepageUrl}
361
357
  target="_blank"
362
358
  rel="noopener noreferrer"
363
- class="grove-btn grove-btn-outline"
359
+ class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 border-border bg-secondary text-foreground hover:bg-accent"
364
360
  >
365
361
  Homepage
366
362
  </a>
@@ -371,7 +367,7 @@ if (isProject && proj) {
371
367
 
372
368
  {/* ── Score bars ───────────────────────────────────────── */}
373
369
  {proj && (
374
- <section class="mt-8 grove-card p-6" aria-labelledby="scores-heading">
370
+ <section class="mt-8 rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors p-6" aria-labelledby="scores-heading">
375
371
  <h2 id="scores-heading" class="m-0 text-lg font-semibold tracking-tight text-ink-900 dark:text-ink-100">
376
372
  Scores
377
373
  </h2>
@@ -396,7 +392,7 @@ if (isProject && proj) {
396
392
 
397
393
  {/* ── GitHub signals dl ────────────────────────────────── */}
398
394
  {isProject && github && (
399
- <section class="mt-8 grove-card p-6" aria-labelledby="signals-heading">
395
+ <section class="mt-8 rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors p-6" aria-labelledby="signals-heading">
400
396
  <h2 id="signals-heading" class="m-0 text-lg font-semibold tracking-tight text-ink-900 dark:text-ink-100">
401
397
  GitHub signals
402
398
  </h2>
@@ -461,8 +457,8 @@ if (isProject && proj) {
461
457
  {/* ── Decision signals (3-col) ──────────────────────────── */}
462
458
  {isProject && github && (
463
459
  <section class="mt-8 grid grid-cols-1 gap-4 lg:grid-cols-3" aria-label="Decision signals">
464
- {/* Repository health 3-col card */}
465
- <div class="grove-card p-5">
460
+ {/* Repository health card */}
461
+ <div class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors p-5">
466
462
  <h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
467
463
  Repository health
468
464
  </h3>
@@ -501,7 +497,7 @@ if (isProject && proj) {
501
497
  </div>
502
498
 
503
499
  {/* Activity bar chart */}
504
- <div class="grove-card p-5">
500
+ <div class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors p-5">
505
501
  <h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
506
502
  Activity
507
503
  </h3>
@@ -537,7 +533,7 @@ if (isProject && proj) {
537
533
  </div>
538
534
 
539
535
  {/* Contribution readiness chips */}
540
- <div class="grove-card p-5">
536
+ <div class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors p-5">
541
537
  <h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
542
538
  Contribution readiness
543
539
  </h3>
@@ -563,7 +559,7 @@ if (isProject && proj) {
563
559
 
564
560
  {/* ── Language mix ─────────────────────────────────────── */}
565
561
  {isProject && languages.length > 0 && (
566
- <section class="mt-4 grove-card p-5">
562
+ <section class="mt-4 rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors p-5">
567
563
  <h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
568
564
  Language mix
569
565
  </h3>
@@ -597,7 +593,7 @@ if (isProject && proj) {
597
593
  Stack
598
594
  </span>
599
595
  {stacks.map((s) => (
600
- <span class="grove-badge inline-flex items-center gap-1.5">
596
+ <span class="inline-flex min-h-5 items-center justify-center gap-1 rounded-full border border-transparent bg-secondary px-2 py-0.5 align-middle text-xs font-medium leading-none text-secondary-foreground items-center gap-1.5">
601
597
  <Icon name={s} category="stack" size={14} />
602
598
  {s}
603
599
  </span>
@@ -610,7 +606,7 @@ if (isProject && proj) {
610
606
  Platform
611
607
  </span>
612
608
  {platforms.map((p) => (
613
- <span class="grove-badge inline-flex items-center gap-1.5">
609
+ <span class="inline-flex min-h-5 items-center justify-center gap-1 rounded-full border border-transparent bg-secondary px-2 py-0.5 align-middle text-xs font-medium leading-none text-secondary-foreground items-center gap-1.5">
614
610
  <Icon name={p} category="platform" size={14} />
615
611
  {p}
616
612
  </span>
@@ -632,7 +628,7 @@ if (isProject && proj) {
632
628
  <li>
633
629
  <a
634
630
  href={`/${slug}?q=${encodeURIComponent(t)}`}
635
- class="grove-badge inline-flex items-center gap-1.5"
631
+ class="inline-flex min-h-5 items-center justify-center gap-1 rounded-full border border-transparent bg-secondary px-2 py-0.5 align-middle text-xs font-medium leading-none text-secondary-foreground items-center gap-1.5"
636
632
  >
637
633
  {t}
638
634
  </a>
@@ -650,12 +646,12 @@ if (isProject && proj) {
650
646
  </h2>
651
647
  <ul class="mt-4 m-0 flex list-none flex-col gap-2 p-0">
652
648
  {distribution.map((c) => (
653
- <li class="grove-card flex flex-wrap items-baseline justify-between gap-2 p-3">
649
+ <li class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors flex flex-wrap items-baseline justify-between gap-2 p-3">
654
650
  <div class="flex flex-col">
655
651
  <span class="text-sm font-semibold text-ink-900 dark:text-ink-100">
656
652
  {c.label ?? c.type}
657
653
  {c.verified && (
658
- <span class="ml-2 grove-badge grove-badge-new">Verified</span>
654
+ <span class="ml-2 inline-flex min-h-5 items-center justify-center gap-1 rounded-full border border-transparent bg-secondary px-2 py-0.5 align-middle text-xs font-medium leading-none text-secondary-foreground !bg-emerald-50 !text-emerald-700 dark:!bg-emerald-950/80 dark:!text-emerald-300">Verified</span>
659
655
  )}
660
656
  </span>
661
657
  {c.notes && (
@@ -666,7 +662,7 @@ if (isProject && proj) {
666
662
  href={c.url}
667
663
  target="_blank"
668
664
  rel="noopener noreferrer"
669
- class="grove-btn grove-btn-outline"
665
+ class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 border-border bg-secondary text-foreground hover:bg-accent"
670
666
  >
671
667
  {c.platform ?? c.type} →
672
668
  </a>
@@ -110,7 +110,7 @@ function hrefForRemove(key: keyof IndexFilters, value: string): string {
110
110
  submitLabel={`Submit ${itemLabel()}`}
111
111
  >
112
112
  <section class="border-b border-ink-200 bg-white dark:border-ink-800 dark:bg-ink-950">
113
- <div class="grove-container-wide mx-auto px-5 pt-10 sm:px-7 sm:pt-12">
113
+ <div class="mx-auto w-full max-w-wide px-5 pt-10 sm:px-7 sm:pt-12">
114
114
  <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
115
115
  {slug}
116
116
  </span>
@@ -171,7 +171,7 @@ function hrefForRemove(key: keyof IndexFilters, value: string): string {
171
171
  <path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" />
172
172
  </svg>
173
173
  </label>
174
- <button type="submit" class="grove-btn grove-btn-primary">Search</button>
174
+ <button type="submit" class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 bg-primary text-primary-foreground hover:bg-primary/90">Search</button>
175
175
  </form>
176
176
 
177
177
  <div class="mt-4">
@@ -192,8 +192,8 @@ function hrefForRemove(key: keyof IndexFilters, value: string): string {
192
192
  </div>
193
193
  </section>
194
194
 
195
- <section class="grove-section-tight">
196
- <div class="grove-container-wide mx-auto px-5 sm:px-7">
195
+ <section class="py-10 sm:py-12">
196
+ <div class="mx-auto w-full max-w-wide px-5 sm:px-7">
197
197
  <header class="mb-6">
198
198
  <h2 class="m-0 text-[1.5rem] sm:text-[1.625rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
199
199
  {`All ${itemLabelPlural()}`}
@@ -220,7 +220,7 @@ function hrefForRemove(key: keyof IndexFilters, value: string): string {
220
220
  {chips.map((chip) => (
221
221
  <a
222
222
  href={hrefForRemove(chip.key, chip.value)}
223
- class="grove-badge transition-colors hover:border-ink-400"
223
+ class="inline-flex min-h-5 items-center justify-center gap-1 rounded-full border border-transparent bg-secondary px-2 py-0.5 align-middle text-xs font-medium leading-none text-secondary-foreground transition-colors hover:border-ink-400"
224
224
  >
225
225
  {chip.label} <span aria-hidden="true">x</span>
226
226
  </a>
@@ -248,7 +248,7 @@ function hrefForRemove(key: keyof IndexFilters, value: string): string {
248
248
  <p class="m-0 text-sm font-medium text-ink-700 dark:text-ink-200">
249
249
  {`No ${itemLabel()}s match these filters.`}
250
250
  </p>
251
- <a href={`/${slug}`} class="grove-btn grove-btn-outline mt-4">Clear filters</a>
251
+ <a href={`/${slug}`} class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 border-border bg-secondary text-foreground hover:bg-accent mt-4">Clear filters</a>
252
252
  </div>
253
253
  <nav class="mt-8 flex flex-wrap items-center justify-center gap-1.5" aria-label="Pagination">
254
254
  <Pagination current={page} total={pages} hrefFor={hrefForPage} />
@@ -257,7 +257,7 @@ function hrefForRemove(key: keyof IndexFilters, value: string): string {
257
257
  </section>
258
258
 
259
259
  <section class="border-t border-ink-200 dark:border-ink-800">
260
- <div class="grove-container-wide mx-auto px-5 sm:px-7">
260
+ <div class="mx-auto w-full max-w-wide px-5 sm:px-7">
261
261
  <div class="flex flex-col items-center gap-3 py-12 text-center">
262
262
  <h2 class="m-0 text-xl font-semibold tracking-tight text-ink-900 dark:text-ink-100">
263
263
  {`Know a real ${itemLabel()} that belongs here?`}
@@ -267,7 +267,7 @@ function hrefForRemove(key: keyof IndexFilters, value: string): string {
267
267
  </p>
268
268
  <a
269
269
  href={siteConfig.submitUrl ?? "/submit"}
270
- class="grove-btn grove-btn-primary mt-2"
270
+ class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 bg-primary text-primary-foreground hover:bg-primary/90 mt-2"
271
271
  >
272
272
  {`Submit ${itemLabel()}`}
273
273
  </a>
@@ -13,7 +13,7 @@
13
13
  * - New: "A growing community knowledge site. A curated, health-
14
14
  * aware developer directory…"
15
15
  *
16
- * The card utility classes (`grove-card`, `grove-btn-*`) match the
16
+ * The Tailwind card and button utilities match the
17
17
  * rest of the framework, and the data fields it references
18
18
  * (`siteConfig.tagline`, `siteConfig.repoUrl`) all flow from the
19
19
  * site config — no hardcoded brand or copy.
@@ -57,7 +57,7 @@ const item = itemLabel();
57
57
  </p>
58
58
  </header>
59
59
 
60
- <section class="grove-card mt-8 p-5 sm:p-6">
60
+ <section class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors mt-8 p-5 sm:p-6">
61
61
  <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
62
62
  Why
63
63
  </span>
@@ -78,7 +78,7 @@ const item = itemLabel();
78
78
  </p>
79
79
  </section>
80
80
 
81
- <section class="grove-card mt-4 p-5 sm:p-6">
81
+ <section class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors mt-4 p-5 sm:p-6">
82
82
  <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
83
83
  Why real {item}s
84
84
  </span>
@@ -95,7 +95,7 @@ const item = itemLabel();
95
95
  </p>
96
96
  </section>
97
97
 
98
- <section class="grove-card mt-4 p-5 sm:p-6">
98
+ <section class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors mt-4 p-5 sm:p-6">
99
99
  <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
100
100
  What you get
101
101
  </span>
@@ -130,7 +130,7 @@ const item = itemLabel();
130
130
  </ul>
131
131
  </section>
132
132
 
133
- <section class="grove-card mt-4 p-5 sm:p-6">
133
+ <section class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors mt-4 p-5 sm:p-6">
134
134
  <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
135
135
  Generic by design
136
136
  </span>
@@ -145,7 +145,7 @@ const item = itemLabel();
145
145
  </p>
146
146
  </section>
147
147
 
148
- <section class="grove-card mt-4 p-5 sm:p-6">
148
+ <section class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors mt-4 p-5 sm:p-6">
149
149
  <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
150
150
  Free to fork, free to extend
151
151
  </span>
@@ -164,18 +164,18 @@ const item = itemLabel();
164
164
  </section>
165
165
 
166
166
  <section class="mt-8 flex flex-wrap items-center gap-3">
167
- <a href={`/${slug}`} class="grove-btn grove-btn-primary">Browse the directory</a>
167
+ <a href={`/${slug}`} class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 bg-primary text-primary-foreground hover:bg-primary/90">Browse the directory</a>
168
168
  {githubUrl && (
169
169
  <a
170
170
  href={githubUrl}
171
171
  target="_blank"
172
172
  rel="noopener noreferrer"
173
- class="grove-btn grove-btn-outline"
173
+ class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 border-border bg-secondary text-foreground hover:bg-accent"
174
174
  >
175
175
  View on GitHub
176
176
  </a>
177
177
  )}
178
- <a href="/submit" class="grove-btn grove-btn-ghost">Submit a {item} →</a>
178
+ <a href="/submit" class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-[var(--radius)] border border-transparent px-3 text-sm font-medium leading-none no-underline outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground">Submit a {item} →</a>
179
179
  </section>
180
180
  </article>
181
181
  </Container>
@@ -82,8 +82,8 @@ const contributorsGraphHref = repoUrl ? `${repoUrl.replace(/\/$/, "")}/graphs/co
82
82
  ---
83
83
 
84
84
  <BaseLayout title={title} description={description} site={siteConfig}>
85
- <section class="grove-section">
86
- <div class="grove-container-wide mx-auto px-5 py-12 sm:px-7 sm:py-16">
85
+ <section class="py-12 sm:py-16">
86
+ <div class="mx-auto w-full max-w-wide px-5 py-12 sm:px-7 sm:py-16">
87
87
  <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
88
88
  Contributors
89
89
  </span>
@@ -112,9 +112,9 @@ const contributorsGraphHref = repoUrl ? `${repoUrl.replace(/\/$/, "")}/graphs/co
112
112
  </section>
113
113
 
114
114
  {sorted.length === 0 ? (
115
- <section class="grove-section">
116
- <div class="grove-container mx-auto px-5 pb-16 sm:px-7">
117
- <div class="grove-card flex flex-col items-center gap-3 p-8 text-center">
115
+ <section class="py-12 sm:py-16">
116
+ <div class="mx-auto w-full max-w-container px-5 pb-16 sm:px-7">
117
+ <div class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors flex flex-col items-center gap-3 p-8 text-center">
118
118
  <p class="m-0 text-sm text-ink-500 dark:text-ink-400">
119
119
  No contributors synced yet. Once the first sync runs, every
120
120
  contributor avatar lands here.
@@ -123,8 +123,8 @@ const contributorsGraphHref = repoUrl ? `${repoUrl.replace(/\/$/, "")}/graphs/co
123
123
  </div>
124
124
  </section>
125
125
  ) : (
126
- <section class="grove-section" aria-label="Contributors list">
127
- <div class="grove-container-wide mx-auto px-5 pb-16 sm:px-7">
126
+ <section class="py-12 sm:py-16" aria-label="Contributors list">
127
+ <div class="mx-auto w-full max-w-wide px-5 pb-16 sm:px-7">
128
128
  <ul
129
129
  class="m-0 grid list-none grid-cols-2 gap-3 p-0 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6"
130
130
  aria-label="Contributors"
@@ -140,7 +140,7 @@ const contributorsGraphHref = repoUrl ? `${repoUrl.replace(/\/$/, "")}/graphs/co
140
140
  href={href}
141
141
  target="_blank"
142
142
  rel="noopener noreferrer"
143
- class="group grove-card flex flex-col items-center gap-2 p-3 text-center no-underline transition-colors hover:border-ink-300 dark:hover:border-ink-700"
143
+ class="group rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors flex flex-col items-center gap-2 p-3 text-center no-underline hover:border-ink-300 dark:hover:border-ink-700"
144
144
  data-event="contributor_click"
145
145
  data-event-source="contributors_page"
146
146
  >
@@ -173,12 +173,3 @@ const contributorsGraphHref = repoUrl ? `${repoUrl.replace(/\/$/, "")}/graphs/co
173
173
  </section>
174
174
  )}
175
175
  </BaseLayout>
176
-
177
- <style>
178
- .line-clamp-1 {
179
- display: -webkit-box;
180
- -webkit-line-clamp: 1;
181
- -webkit-box-orient: vertical;
182
- overflow: hidden;
183
- }
184
- </style>
@@ -60,7 +60,7 @@ const pleaseAvoid = [
60
60
  ["2", "Edit the draft", "Fix the category, stack, platforms, and notes."],
61
61
  ["3", "Open a pull request", "Reviewers will validate and merge the record."],
62
62
  ].map(([number, heading, copy]) => (
63
- <li class="card flex items-start gap-2 p-3">
63
+ <li class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors flex items-start gap-2 p-3">
64
64
  <span class="mt-0.5 inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-ink-300 font-mono font-semibold text-ink-700 dark:border-ink-700 dark:text-ink-300">{number}</span>
65
65
  <div>
66
66
  <div class="font-medium text-ink-900 dark:text-ink-100">{heading}</div>
@@ -71,43 +71,43 @@ const pleaseAvoid = [
71
71
  </ol>
72
72
 
73
73
  <div class="mt-8 grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(360px,0.8fr)]">
74
- <form id="submit-form" class="card p-5">
74
+ <form id="submit-form" class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors p-5">
75
75
  <div class="grid gap-5">
76
76
  <label class="grid gap-1.5">
77
- <span class="label">GitHub URL</span>
77
+ <span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">GitHub URL</span>
78
78
  <div class="flex flex-col gap-2 sm:flex-row">
79
- <input id="repo-url" type="url" required placeholder="https://github.com/owner/repo" class="field flex-1" />
80
- <button id="fetch-repo" type="button" class="btn-primary min-h-10 justify-center">Generate draft</button>
79
+ <input id="repo-url" type="url" required placeholder="https://github.com/owner/repo" class="min-h-10 flex-1 rounded-[var(--radius)] border border-input bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35" />
80
+ <button id="fetch-repo" type="button" class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-[var(--radius)] border border-transparent bg-primary px-3 text-sm font-medium leading-none text-primary-foreground no-underline outline-none transition-colors hover:bg-primary/90 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 min-h-10 justify-center">Generate draft</button>
81
81
  </div>
82
- <span class="help">The repository must be public. We never write to it.</span>
82
+ <span class="text-2xs text-muted-foreground">The repository must be public. We never write to it.</span>
83
83
  </label>
84
84
 
85
85
  <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
86
- <label class="grid gap-1.5"><span class="label">Name</span><input id="item-name" type="text" class="field" /></label>
87
- <label class="grid gap-1.5"><span class="label">Slug</span><input id="item-slug" type="text" class="field font-mono" /><span class="help">URL fragment, auto-filled from the repository name.</span></label>
86
+ <label class="grid gap-1.5"><span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Name</span><input id="item-name" type="text" class="min-h-10 rounded-[var(--radius)] border border-input bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35" /></label>
87
+ <label class="grid gap-1.5"><span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Slug</span><input id="item-slug" type="text" class="min-h-10 rounded-[var(--radius)] border border-input bg-background px-3 py-2 font-mono text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35" /><span class="text-2xs text-muted-foreground">URL fragment, auto-filled from the repository name.</span></label>
88
88
  </div>
89
89
 
90
90
  <label class="grid gap-1.5">
91
- <span class="label">Description</span>
92
- <textarea id="item-description" rows="3" class="field resize-y"></textarea>
93
- <span class="help">One or two sentences in plain language.</span>
91
+ <span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Description</span>
92
+ <textarea id="item-description" rows="3" class="min-h-10 resize-y rounded-[var(--radius)] border border-input bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35"></textarea>
93
+ <span class="text-2xs text-muted-foreground">One or two sentences in plain language.</span>
94
94
  </label>
95
95
 
96
96
  <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
97
97
  <label class="grid gap-1.5">
98
- <span class="label">Category</span>
99
- <input id="item-category" list="category-options" class="field" />
98
+ <span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Category</span>
99
+ <input id="item-category" list="category-options" class="min-h-10 rounded-[var(--radius)] border border-input bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35" />
100
100
  <datalist id="category-options">{categories.map((value) => <option value={value} />)}</datalist>
101
101
  </label>
102
102
  <label class="grid gap-1.5">
103
- <span class="label">Primary stack</span>
104
- <input id="primary-stack" list="stack-options" class="field" />
103
+ <span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Primary stack</span>
104
+ <input id="primary-stack" list="stack-options" class="min-h-10 rounded-[var(--radius)] border border-input bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35" />
105
105
  <datalist id="stack-options">{stacks.map((value) => <option value={value} />)}</datalist>
106
106
  </label>
107
107
  </div>
108
108
 
109
109
  <fieldset class="grid gap-2">
110
- <legend class="label">Platforms</legend>
110
+ <legend class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Platforms</legend>
111
111
  <div class="grid grid-cols-2 gap-2 sm:grid-cols-3">
112
112
  {platformOptions.map((platform) => (
113
113
  <label class="flex min-h-9 items-center gap-2 rounded-md border border-ink-200 px-2 text-sm dark:border-ink-800">
@@ -118,14 +118,14 @@ const pleaseAvoid = [
118
118
  </div>
119
119
  </fieldset>
120
120
 
121
- <label class="grid gap-1.5"><span class="label">Tags</span><input id="item-tags" type="text" placeholder="self-hosted, developer-tools, offline-first" class="field" /></label>
122
- <label class="grid gap-1.5"><span class="label">Website</span><input id="item-website" type="url" placeholder="https://example.com" class="field" /></label>
123
- <label class="grid gap-1.5"><span class="label">Best for</span><textarea id="best-for" rows="3" class="field resize-y" placeholder="One use case per line"></textarea></label>
121
+ <label class="grid gap-1.5"><span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Tags</span><input id="item-tags" type="text" placeholder="self-hosted, developer-tools, offline-first" class="min-h-10 rounded-[var(--radius)] border border-input bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35" /></label>
122
+ <label class="grid gap-1.5"><span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Website</span><input id="item-website" type="url" placeholder="https://example.com" class="min-h-10 rounded-[var(--radius)] border border-input bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35" /></label>
123
+ <label class="grid gap-1.5"><span class="text-2xs font-medium uppercase tracking-[.05em] text-muted-foreground">Best for</span><textarea id="best-for" rows="3" class="min-h-10 resize-y rounded-[var(--radius)] border border-input bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35" placeholder="One use case per line"></textarea></label>
124
124
  </div>
125
125
  </form>
126
126
 
127
127
  <aside class="flex flex-col gap-4">
128
- <div class="card p-5">
128
+ <div class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors p-5">
129
129
  <h2 class="m-0 text-[0.9375rem] font-semibold text-ink-900 dark:text-ink-100">Submission criteria</h2>
130
130
  <p class="m-0 mt-1 text-2xs text-ink-500 dark:text-ink-400">Use this checklist before opening the draft.</p>
131
131
  <div class="mt-4 grid grid-cols-1 gap-4 text-2xs sm:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2">
@@ -140,12 +140,12 @@ const pleaseAvoid = [
140
140
  </div>
141
141
  </div>
142
142
 
143
- <div class="card flex min-h-[420px] flex-col p-5">
143
+ <div class="rounded-[calc(var(--radius)+0.25rem)] border-0 bg-muted shadow-none transition-colors flex min-h-[420px] flex-col p-5">
144
144
  <div class="flex flex-wrap items-center justify-between gap-3">
145
145
  <h2 class="m-0 text-base font-semibold text-ink-900 dark:text-ink-100">YAML draft</h2>
146
146
  <div class="flex items-center gap-2">
147
- <button id="copy-yaml" type="button" class="btn-outline pointer-events-none opacity-50" disabled>Copy YAML</button>
148
- <a id="open-pr-link" href="#" target="_blank" rel="noopener noreferrer" class="btn-outline pointer-events-none opacity-50">Open PR draft</a>
147
+ <button id="copy-yaml" type="button" class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-[var(--radius)] border border-border bg-secondary px-3 text-sm font-medium leading-none text-foreground no-underline outline-none transition-colors hover:bg-accent focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 pointer-events-none opacity-50" disabled>Copy YAML</button>
148
+ <a id="open-pr-link" href="#" target="_blank" rel="noopener noreferrer" class="inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-[var(--radius)] border border-border bg-secondary px-3 text-sm font-medium leading-none text-foreground no-underline outline-none transition-colors hover:bg-accent focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/35 pointer-events-none opacity-50">Open PR draft</a>
149
149
  </div>
150
150
  </div>
151
151
  <pre class="mt-4 min-h-0 flex-1 overflow-auto rounded-md border border-ink-200 bg-ink-50 p-3 text-xs leading-relaxed text-ink-800 dark:border-ink-800 dark:bg-ink-900/60 dark:text-ink-200"><code id="yaml-preview">Paste a GitHub URL to generate a draft.</code></pre>
@@ -156,14 +156,6 @@ const pleaseAvoid = [
156
156
  </section>
157
157
  </Container>
158
158
 
159
- <style>
160
- .field { min-height: 2.5rem; border-radius: .375rem; border: 1px solid var(--color-ink-200); background: white; padding: .5rem .75rem; color: var(--color-ink-900); font-size: .875rem; outline: none; }
161
- .field:focus { border-color: var(--color-ink-500); }
162
- :global(.dark) .field { border-color: var(--color-ink-800); background: var(--color-ink-950); color: var(--color-ink-100); }
163
- .label { font-size: var(--text-2xs); font-weight: 500; text-transform: uppercase; letter-spacing: .05em; color: var(--color-ink-500); }
164
- .help { font-size: var(--text-2xs); color: var(--color-ink-500); }
165
- </style>
166
-
167
159
  <script is:inline define:vars={{ existingSlugs, existingFullNames, repoUrl }}>
168
160
  // @ts-nocheck
169
161
  const $ = (selector) => document.querySelector(selector);