@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 +3 -3
- package/src/DocsLayout.astro +37 -47
- package/src/TagList.astro +17 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogsbay/docs-layout",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
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.
|
|
34
|
-
"@dogsbay/ui": "0.2.0-beta.
|
|
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",
|
package/src/DocsLayout.astro
CHANGED
|
@@ -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
|
-
|
|
857
|
-
|
|
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
|
-
|
|
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" ? "
|
|
1271
|
-
nextLabel={chrome === "blog" ? "
|
|
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-
|
|
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
|
-
|
|
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
|
) : (
|