@dogsbay/docs-layout 0.2.0-beta.95 → 0.2.0-beta.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogsbay/docs-layout",
3
- "version": "0.2.0-beta.95",
3
+ "version": "0.2.0-beta.98",
4
4
  "description": "Standard documentation layout components for Dogsbay",
5
5
  "type": "module",
6
6
  "exports": {
@@ -30,8 +30,8 @@
30
30
  "./json-ld": "./src/json-ld.ts"
31
31
  },
32
32
  "dependencies": {
33
- "@dogsbay/primitives": "0.2.0-beta.95",
34
- "@dogsbay/ui": "0.2.0-beta.95"
33
+ "@dogsbay/primitives": "0.2.0-beta.98",
34
+ "@dogsbay/ui": "0.2.0-beta.98"
35
35
  },
36
36
  "devDependencies": {
37
37
  "happy-dom": "^20.10.6",
@@ -432,18 +432,6 @@ interface Props {
432
432
  readingMinutes?: number;
433
433
  /** Optional hero image URL, rendered above the prose. */
434
434
  heroImage?: string;
435
- /**
436
- * Series position, when the post belongs to one. Derived at emit time
437
- * from publication order, so "Part 3 of 7" cannot disagree with reality
438
- * the way a hand-typed line does.
439
- */
440
- series?: {
441
- name: string;
442
- part: number;
443
- total: number;
444
- /** URL of part 1, so a reader landing mid-series can start over. */
445
- startHref?: string;
446
- };
447
435
  /**
448
436
  * Table-of-contents placement. Default `"top"`.
449
437
  * - `"top"` — expandable "On this page" disclosure at the top of the
@@ -533,7 +521,6 @@ const {
533
521
  updatedDate,
534
522
  readingMinutes,
535
523
  heroImage,
536
- series,
537
524
  linkIcons,
538
525
  class: className,
539
526
  } = Astro.props;
@@ -852,10 +839,39 @@ const siteIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
852
839
  ></script>
853
840
  )}
854
841
 
842
+ {/*
843
+ Theme bootstrap. Inline and BLOCKING on purpose — it must run
844
+ before first paint or the page flashes light before going dark.
845
+
846
+ An explicit choice wins in BOTH directions. Someone on a
847
+ dark-mode OS who deliberately picked light must stay light, so
848
+ "light" is checked rather than treated as "not dark". Only the
849
+ absence of a stored value falls through to the OS preference.
850
+
851
+ That fallback is the point of this block. Previously the only
852
+ condition was `=== "dark"`, so a visitor whose OS is in dark mode
853
+ got a light page until they found the toggle — and nothing else
854
+ in the CSS consulted prefers-color-scheme either. It also made
855
+ dark mode untestable by any tool that emulates the media query
856
+ (DevTools' Rendering panel, Playwright's colorScheme), because
857
+ the page did not read it.
858
+
859
+ localStorage access throws in some privacy modes; a throw here
860
+ would skip the class entirely, so it is guarded and degrades to
861
+ the OS preference.
862
+ */}
855
863
  <script is:inline>
856
- if (localStorage.getItem("theme") === "dark") {
857
- document.documentElement.classList.add("dark");
858
- }
864
+ (function () {
865
+ var stored = null;
866
+ try {
867
+ stored = localStorage.getItem("theme");
868
+ } catch (e) {}
869
+ var dark =
870
+ stored === "dark" ||
871
+ (stored !== "light" &&
872
+ window.matchMedia("(prefers-color-scheme: dark)").matches);
873
+ if (dark) document.documentElement.classList.add("dark");
874
+ })();
859
875
  </script>
860
876
  <slot name="head" />
861
877
  </head>
@@ -1168,34 +1184,6 @@ const siteIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
1168
1184
  </div>
1169
1185
  )}
1170
1186
 
1171
- {/*
1172
- Series banner. Sits above the byline because it is the
1173
- first thing a reader arriving from search needs: which
1174
- series is this, where am I in it, and where does it start.
1175
- A blog index is chronological, so someone can easily meet
1176
- part 7 first.
1177
- */}
1178
- {chrome === "blog" && series && (
1179
- <div
1180
- class="not-prose mb-4 flex flex-wrap items-center gap-x-2 gap-y-1 rounded-md border border-border bg-muted/40 px-3 py-2 text-sm"
1181
- data-post-series
1182
- data-pagefind-ignore
1183
- >
1184
- <span class="font-medium text-foreground">
1185
- Part {series.part} of {series.total}
1186
- </span>
1187
- <span class="text-muted-foreground">in {series.name}</span>
1188
- {series.startHref && series.part !== 1 && (
1189
- <a
1190
- href={series.startHref}
1191
- class="ml-auto text-foreground underline underline-offset-2"
1192
- >
1193
- Start at part 1
1194
- </a>
1195
- )}
1196
- </div>
1197
- )}
1198
-
1199
1187
  {autoLede && description && (
1200
1188
  <p class="text-lg text-muted-foreground mb-6">
1201
1189
  {description}
@@ -1265,10 +1253,12 @@ const siteIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
1265
1253
  next={next}
1266
1254
  {/*
1267
1255
  Blog adjacency is chronological, so "Previous/Next"
1268
- would describe the wrong axis prev is the NEWER post.
1256
+ describes the wrong axis. Left is older, right is newer
1257
+ a timeline runs that way, and so does a series: the
1258
+ right-hand link is the next part.
1269
1259
  */}
1270
- prevLabel={chrome === "blog" ? "Newer" : undefined}
1271
- nextLabel={chrome === "blog" ? "Older" : undefined}
1260
+ prevLabel={chrome === "blog" ? "Older" : undefined}
1261
+ nextLabel={chrome === "blog" ? "Newer" : undefined}
1272
1262
  copyright={copyright}
1273
1263
  llmsLink={llmFooterLink}
1274
1264
  llmsLinkHref={llmsLinkHrefResolved}
package/src/TagList.astro CHANGED
@@ -72,8 +72,13 @@ const chips = buildChips(tags, { indexPath, prefixes, labels });
72
72
  */
73
73
  const PALETTE: Record<TagPaletteName, string> = {
74
74
  blue: "border-blue-500/40 bg-blue-500/15 text-blue-700 dark:text-blue-300",
75
+ // amber-800, not -700, in light mode: amber-700 on amber-500/15 is
76
+ // 4.48:1 — under 4.5:1 by a hair, and failing regardless of the
77
+ // opacity bug below. -800 gives 6.32:1. Every other colour clears it
78
+ // at -700 (4.76–8.60:1). Dark mode is unaffected: text-*-300 on the
79
+ // same tint over a dark page is 9–12:1 across the palette.
75
80
  amber:
76
- "border-amber-500/40 bg-amber-500/15 text-amber-700 dark:text-amber-300",
81
+ "border-amber-500/40 bg-amber-500/15 text-amber-800 dark:text-amber-300",
77
82
  emerald:
78
83
  "border-emerald-500/40 bg-emerald-500/15 text-emerald-700 dark:text-emerald-300",
79
84
  violet:
@@ -108,7 +113,17 @@ const DEFAULT_CLASS =
108
113
  >
109
114
  {chip.prefixLabel ? (
110
115
  <Fragment>
111
- <span class="opacity-70">{chip.prefixLabel}:</span>
116
+ {/*
117
+ No opacity here. `opacity-70` on the prefix blended the
118
+ text 30% toward the chip background and put EVERY colour
119
+ in the palette below 4.5:1 — measured 2.75–3.95:1, worst
120
+ amber, best slate. It is 12px text, so 4.5:1 applies.
121
+
122
+ The prefix/leaf distinction is carried by font-weight
123
+ instead: the leaf below is font-semibold against the
124
+ anchor's font-medium. Weight costs no contrast.
125
+ */}
126
+ <span>{chip.prefixLabel}:</span>
112
127
  <span class="font-semibold">{chip.leafLabel}</span>
113
128
  </Fragment>
114
129
  ) : (