hd-garden 0.3.13 → 0.4.1

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 (60) hide show
  1. package/.agents/skills/garden-interactive-chart/SKILL.md +6 -6
  2. package/.agents/skills/garden-ui/SKILL.md +4 -4
  3. package/app/a2/[slug]/page.tsx +61 -0
  4. package/app/a2/page.tsx +27 -0
  5. package/app/blog/[...path]/page.tsx +32 -22
  6. package/app/blog/page.tsx +6 -7
  7. package/app/columns/[column]/[...path]/page.tsx +32 -25
  8. package/app/columns/page.tsx +38 -52
  9. package/app/components/ArticleMeasure.tsx +95 -0
  10. package/app/components/ArticleTitle.tsx +20 -0
  11. package/app/components/BlogArchive.tsx +106 -102
  12. package/app/components/MarkdownArticle.tsx +145 -66
  13. package/app/components/MermaidDiagram.tsx +44 -71
  14. package/app/components/SiteFooter.tsx +3 -7
  15. package/app/components/SiteHeader.tsx +55 -41
  16. package/app/components/TableOfContents.tsx +3 -2
  17. package/app/components/TagIndex.tsx +97 -0
  18. package/app/components/garden/GardenBlock.tsx +45 -0
  19. package/app/components/garden/blocks.tsx +530 -0
  20. package/app/components/garden/chrome.tsx +350 -0
  21. package/app/components/garden/sandbox-message.ts +16 -0
  22. package/app/garden-modules.d.ts +2 -0
  23. package/app/globals.css +17 -12
  24. package/app/layout.tsx +9 -0
  25. package/app/lib/a2-meta.ts +22 -0
  26. package/app/lib/a2-preview.ts +50 -0
  27. package/app/lib/content.ts +52 -6
  28. package/app/lib/markdown-plugins.ts +2 -0
  29. package/app/lib/remark-a2.mjs +208 -0
  30. package/app/lib/remark-garden.mjs +88 -0
  31. package/app/page.tsx +122 -93
  32. package/app/styles/a2.css +1383 -0
  33. package/app/styles/shell.css +438 -0
  34. package/app/styles/tokens.css +111 -0
  35. package/app/tags/page.tsx +28 -0
  36. package/app/ui-parity/page.tsx +3 -2
  37. package/package.json +1 -1
  38. package/scripts/build-content.mjs +11 -1
  39. package/scripts/check-post.mjs +18 -4
  40. package/scripts/new-post.mjs +3 -0
  41. package/site.config.yaml +1 -1
  42. package/templates/garden-blocks.md +128 -0
  43. package/templates/garden-snippets.plist +121 -0
  44. package/ui/garden/catalog.mjs +196 -0
  45. package/ui/garden/check.mjs +103 -0
  46. package/ui/garden/enhance.mjs +89 -0
  47. package/ui/garden/parse.mjs +198 -0
  48. package/ui/garden/snippets.mjs +109 -0
  49. package/ui/garden/title.mjs +37 -0
  50. package/ui/garden/word-count.mjs +266 -0
  51. package/ui/host.mjs +45 -28
  52. package/ui/parity-fixtures.mjs +1 -1
  53. package/ui/preview/dist/garden-ui.css +81 -77
  54. package/ui/preview/dist/garden-ui.esm.js +124 -89
  55. package/ui/preview/dist/garden-ui.iife.js +124 -89
  56. package/ui/prompt/rules.md +17 -1
  57. package/ui/react/CatalogPage.tsx +12 -0
  58. package/ui/react/components/display.tsx +1 -1
  59. package/ui/react/components/media.tsx +2 -2
  60. package/ui/react/ui.css +81 -77
@@ -0,0 +1,438 @@
1
+
2
+ /* ===== Hedon Garden shell (A2). Tokens shared with the A2 article. ===== */
3
+ :root{
4
+ --paper:#F8F7F3; --card:#FFFFFF; --ink:#1B1A18; --text:#2D2C29; --soft:#4A4844; --muted:#6E6B64;
5
+ --rule:#E6E3DC; --rule-strong:#D6D2C9; --well:#F0EEE8; --tint:rgba(27,26,24,.055);
6
+ --blue:#2C55B4; --blue-tint:#EAF0FC; --blue-line:rgba(44,85,180,.28); --cover:#5E86E0;
7
+ --sans:-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Noto Sans SC","Noto Sans CJK SC","Microsoft YaHei","Segoe UI",system-ui,sans-serif;
8
+ --serif:"Source Han Serif SC","Noto Serif SC","Noto Serif CJK SC","Songti SC","STSong","Source Serif 4",Georgia,serif;
9
+ --fs-display:46px; --fs-h2:26px; --fs-lead:20px; --fs-body:17px; --fs-small:14px; --fs-meta:13px;
10
+ --r:12px; --r-lg:24px; --shell:1200px; --list:760px; --side:200px;
11
+ --hdr:64px; --ease:cubic-bezier(.2,.8,.2,1);
12
+ color-scheme:light;
13
+ }
14
+ html[data-theme="dark"]{
15
+ --paper:#161513; --card:#1E1D1A; --ink:#EEECE7; --text:#DCD9D2; --soft:#BDB9B0; --muted:#A09B91;
16
+ --rule:#2F2D29; --rule-strong:#45423C; --well:#22211D; --tint:rgba(238,236,231,.07);
17
+ --blue:#93B2F4; --blue-tint:rgba(147,178,244,.13); --blue-line:rgba(147,178,244,.35); --cover:#4C72C9;
18
+ color-scheme:dark;
19
+ }
20
+ *,*::before,*::after{box-sizing:border-box}
21
+ html{-webkit-text-size-adjust:100%;text-size-adjust:100%;scroll-padding-top:calc(var(--hdr) + 16px)}
22
+ body{margin:0;background:var(--paper);color:var(--text);font:400 var(--fs-body)/1.75 var(--sans);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
23
+ a{color:inherit}
24
+ img,svg{max-width:100%}
25
+ :focus-visible{outline:2px solid var(--blue);outline-offset:3px;border-radius:6px}
26
+ .sh-skip{position:absolute;left:12px;top:-60px;z-index:100;background:var(--ink);color:var(--paper);padding:10px 14px;border-radius:var(--r);text-decoration:none;font-size:var(--fs-small)}
27
+ .sh-skip:focus{top:12px}
28
+ .vh{position:absolute!important;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
29
+ .nw{white-space:nowrap}
30
+ .num{font-variant-numeric:tabular-nums}
31
+
32
+ /* ---------- header ---------- */
33
+ .sh-header{position:sticky;top:0;z-index:40;background:var(--paper);border-bottom:1px solid transparent;transition:border-color .2s ease}
34
+ .sh-header.is-scrolled{border-bottom-color:var(--rule)}
35
+ .sh-bar{max-width:var(--shell);margin:0 auto;height:var(--hdr);padding:0 32px;display:flex;align-items:center;gap:28px}
36
+ .sh-word{font:600 var(--fs-lead)/1 var(--serif);color:var(--ink);text-decoration:none;white-space:nowrap;padding:8px 0}
37
+ .sh-nav{display:flex;gap:4px;margin-left:auto}
38
+ .sh-nav a{font-size:15px;color:var(--soft);text-decoration:none;padding:8px 12px;border-radius:var(--r);transition:color .15s ease,background-color .15s ease}
39
+ .sh-nav a:hover{color:var(--ink);background:var(--tint)}
40
+ .sh-nav a[aria-current="page"],.sh-nav a[aria-current="true"]{color:var(--blue);font-weight:600}
41
+ .sh-tools{display:flex;align-items:center;gap:8px}
42
+ .sh-search{display:inline-flex;align-items:center;gap:10px;height:38px;padding:0 8px 0 12px;border:1px solid var(--rule);background:var(--card);color:var(--muted);border-radius:var(--r);font:400 var(--fs-small)/1 var(--sans);cursor:pointer;appearance:none;transition:border-color .15s ease,color .15s ease}
43
+ .sh-search:hover{border-color:var(--rule-strong);color:var(--soft)}
44
+ .sh-search svg,.sh-icon svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex:none}
45
+ .sh-search .lbl{min-width:84px;text-align:left}
46
+ kbd{font:500 12px/1 var(--sans);color:var(--muted);border:1px solid var(--rule);border-bottom-width:2px;border-radius:6px;padding:3px 6px;background:var(--paper)}
47
+ .sh-search .kbd-mod{display:inline-block;min-width:2.5em;text-align:end}
48
+ .sh-search .kbd-other{display:none}
49
+ html[data-mod="other"] .sh-search .kbd-mac{display:none}
50
+ html[data-mod="other"] .sh-search .kbd-other{display:inline-block}
51
+ .sh-search-m{color:var(--soft);opacity:1}
52
+ .sh-search-m svg{opacity:1}
53
+ .sh-icon{display:inline-grid;place-items:center;width:40px;height:40px;border:0;background:transparent;color:var(--soft);border-radius:var(--r);cursor:pointer;transition:background-color .15s ease,color .15s ease}
54
+ @media (hover:hover){.sh-icon:hover{background:var(--tint);color:var(--ink)}}
55
+ .sh-menu-btn,.sh-search-m{display:none}
56
+ html[data-theme="dark"] .i-moon,html:not([data-theme="dark"]) .i-sun{display:none}
57
+ .sh-menu{display:none}
58
+
59
+ /* ---------- page frame ---------- */
60
+ .sh-main{max-width:var(--shell);margin:0 auto;padding:0 32px}
61
+ .sh-head{padding:72px 0 36px;max-width:var(--list)}
62
+ .sh-head h1{font:700 var(--fs-display)/1.2 var(--sans);color:var(--ink);margin:0;letter-spacing:0;text-wrap:balance}
63
+ .sh-deck{margin:14px 0 0;font-size:var(--fs-body);color:var(--soft);max-width:36em}
64
+ .sh-back{display:inline-flex;align-items:center;gap:6px;margin:0 0 18px;font-size:var(--fs-small);color:var(--muted);text-decoration:none;padding:4px 0}
65
+ .sh-back:hover{color:var(--blue)}
66
+ .sh-section{margin:72px 0 0}
67
+ .sh-sec-head{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin:0 0 8px;padding-bottom:14px;border-bottom:1px solid var(--rule)}
68
+ .sh-sec-head h2{font:700 var(--fs-h2)/1.3 var(--sans);color:var(--ink);margin:0}
69
+ .sh-more{font-size:15px;color:var(--soft);text-decoration:none;white-space:nowrap;padding:6px 0}
70
+ .sh-more:hover{color:var(--blue)}
71
+ .sh-more::after{content:" →"}
72
+
73
+ /* ---------- list layout with right rail (mirrors the article's right TOC) ---------- */
74
+ .sh-listgrid{display:grid;grid-template-columns:minmax(0,var(--list)) var(--side);gap:0 72px;align-items:start;justify-content:space-between}
75
+ .sh-rail{position:sticky;top:calc(var(--hdr) + 28px);font-size:var(--fs-small);color:#737069}
76
+ html[data-theme="dark"] .sh-rail{color:var(--muted)}
77
+ .sh-rail h2{font:600 var(--fs-small)/1.4 var(--sans);color:var(--soft);margin:0 0 10px}
78
+ .sh-rail > p{margin:0;line-height:1.7}
79
+ .sh-rail ol{list-style:none;margin:0 0 28px;padding:0}
80
+ .sh-rail li a{display:flex;justify-content:space-between;gap:12px;padding:5px 0;text-decoration:none;color:inherit}
81
+ .sh-rail li a:hover{color:var(--ink)}
82
+ .sh-rail li a[aria-current="true"]{color:var(--blue)}
83
+ .sh-rail li[hidden]{display:none}
84
+
85
+ /* ---------- filter tabs ---------- */
86
+ .sh-filters{display:flex;flex-wrap:wrap;gap:6px 4px;margin:0 0 8px;padding:0;list-style:none}
87
+ .sh-chip{display:inline-flex;align-items:center;gap:6px;min-height:36px;padding:0 12px;border-radius:var(--r);border:1px solid transparent;background:transparent;color:var(--soft);
88
+ font:400 15px/1 var(--sans);text-decoration:none;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease,border-color .15s ease}
89
+ .sh-chip .n{font-size:var(--fs-meta);color:var(--muted);font-variant-numeric:tabular-nums}
90
+ @media (hover:hover){.sh-chip:hover{background:var(--tint);color:var(--ink)}}
91
+ .sh-chip[aria-current="true"],.sh-chip[aria-pressed="true"]{background:var(--blue-tint);color:var(--blue);font-weight:600}
92
+ .sh-chip[aria-current="true"] .n,.sh-chip[aria-pressed="true"] .n{color:var(--blue)}
93
+ .sh-filters .extra[hidden]{display:none}
94
+ .sh-chip.toggle{color:var(--muted)}
95
+ .sh-chip.toggle svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.8;transition:transform .2s ease}
96
+ .sh-chip.toggle[aria-expanded="true"] svg{transform:rotate(180deg)}
97
+ .sh-status{display:flex;align-items:center;gap:12px;min-height:40px;margin:4px 0 0;font-size:var(--fs-small);color:var(--muted)}
98
+ .sh-status > button,.sh-status > a{font:inherit;color:var(--blue);background:none;border:0;padding:6px 0;cursor:pointer;text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
99
+ .sh-view{display:inline-flex;margin-left:auto;border:1px solid var(--rule);border-radius:var(--r);padding:2px;background:var(--card)}
100
+ .sh-view button{font:400 13px/1 var(--sans);color:var(--muted);background:none;border:0;border-radius:9px;padding:7px 10px;cursor:pointer;min-height:30px}
101
+ .sh-view button[aria-pressed="true"]{background:var(--well);color:var(--ink);font-weight:600}
102
+
103
+ /* ---------- post rows ---------- */
104
+ .sh-year{margin:0}
105
+ .sh-year > h2{font:700 var(--fs-lead)/1.3 var(--sans);color:var(--ink);margin:40px 0 0;padding:0 0 12px;border-bottom:1px solid var(--rule-strong);display:flex;align-items:baseline;gap:10px;scroll-margin-top:calc(var(--hdr) + 16px)}
106
+ .sh-year > h2 .n{font:400 var(--fs-meta)/1 var(--sans);color:var(--muted)}
107
+ .sh-year[hidden],.sh-row[hidden]{display:none}
108
+ .sh-rows{list-style:none;margin:0;padding:0}
109
+ .sh-row{position:relative;display:grid;grid-template-columns:72px minmax(0,1fr) auto;gap:4px 20px;padding:18px 0 19px;border-bottom:1px solid var(--rule)}
110
+ .sh-row time{grid-row:1;font-size:var(--fs-small);line-height:1.6;color:var(--muted);font-variant-numeric:tabular-nums;padding-top:2px;white-space:nowrap}
111
+ .sh-row h3{grid-column:2;margin:0;font:600 var(--fs-body)/1.55 var(--sans);color:var(--ink)}
112
+ .sh-row h3 a{text-decoration:none}
113
+ .sh-row h3 a::after{content:"";position:absolute;inset:0} /* whole row clickable, one link in the a11y tree */
114
+ .sh-row h3 a:focus-visible{outline:none}
115
+ .sh-row:has(h3 a:focus-visible){outline:2px solid var(--blue);outline-offset:4px;border-radius:var(--r)}
116
+ .sh-row p{grid-column:2;margin:0;font-size:var(--fs-small);line-height:1.7;color:var(--muted);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
117
+ .sh-row .cat{grid-column:3;grid-row:1;position:relative;z-index:1;font-size:var(--fs-meta);line-height:1.7;color:var(--muted);text-decoration:none;padding:2px 0 0;white-space:nowrap}
118
+ .sh-row .cat:hover{color:var(--blue);text-decoration:underline;text-underline-offset:3px}
119
+ .sh-row .pin{font:400 12px/1 var(--sans);color:var(--blue);background:var(--blue-tint);border-radius:6px;padding:3px 6px;margin-left:8px;vertical-align:2px;white-space:nowrap}
120
+ .sh-row .series{font-weight:400;color:var(--muted);font-size:var(--fs-meta);margin-left:8px;white-space:nowrap}
121
+ .sh-row:hover h3 a{color:var(--blue)}
122
+ .sh-row h3 a{transition:color .15s ease}
123
+ .is-compact .sh-row{padding:11px 0 12px}
124
+ .is-compact .sh-row p{display:none}
125
+ .is-compact .sh-row h3{font-weight:500}
126
+ .sh-empty{padding:28px 0;color:var(--muted);font-size:15px}
127
+
128
+ /* ---------- home ---------- */
129
+ .sh-intro{padding:80px 0 8px;max-width:var(--list)}
130
+ .sh-intro h1{font:700 var(--fs-display)/1.25 var(--sans);color:var(--ink);margin:0;letter-spacing:0;max-width:var(--list);text-wrap:balance}
131
+ .sh-intro p{margin:18px 0 0;font-size:var(--fs-lead);line-height:1.7;color:var(--soft);max-width:32em;text-wrap:balance}
132
+ .sh-intro .links{display:flex;flex-wrap:wrap;gap:8px 22px;margin-top:22px;font-size:15px}
133
+ .sh-intro .links a{color:var(--soft);text-decoration:underline;text-decoration-color:var(--rule-strong);text-underline-offset:5px;text-decoration-thickness:1px;padding:4px 0}
134
+ .sh-intro .links a:hover{color:var(--blue);text-decoration-color:currentColor}
135
+ .sh-feature{position:relative;display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:48px;align-items:center;margin:56px 0 0}
136
+ .sh-feature:not(:has(.cover)){grid-template-columns:minmax(0,1fr)}
137
+ .sh-feature .cover{border-radius:var(--r-lg);aspect-ratio:auto;display:block;overflow:hidden;background:none}
138
+ .sh-feature .cover img{display:block;width:100%;height:auto}
139
+ .sh-feature .cover .cover-art{height:86%;width:auto}
140
+ .sh-feature .kicker{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:var(--fs-small);color:var(--muted);margin:0 0 12px}
141
+ .sh-feature h2{font:700 30px/1.35 var(--sans);color:var(--ink);margin:0;text-wrap:balance}
142
+ .sh-feature h2 a{text-decoration:none}
143
+ .sh-feature h2 a::after{content:"";position:absolute;inset:0;border-radius:var(--r-lg)}
144
+ .sh-feature:hover h2 a{color:var(--blue)}
145
+ .sh-feature:has(h2 a:focus-visible){outline:2px solid var(--blue);outline-offset:8px;border-radius:var(--r-lg)}
146
+ .sh-feature h2 a:focus-visible{outline:none}
147
+ .sh-feature p{margin:14px 0 0;font-size:var(--fs-body);color:var(--soft)}
148
+ .sh-feature .tags{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:16px;font-size:var(--fs-meta);color:var(--muted)}
149
+ .sh-home-grid{display:grid;grid-template-columns:minmax(0,var(--list)) minmax(0,320px);gap:0 72px;justify-content:space-between;align-items:start}
150
+ .sh-home-grid .sh-row{grid-template-columns:96px minmax(0,1fr) auto}
151
+ .sh-side-list{list-style:none;margin:0;padding:0}
152
+ .sh-side-list li{position:relative;padding:16px 0;border-bottom:1px solid var(--rule)}
153
+ .sh-side-list a{font:600 15px/1.6 var(--sans);color:var(--ink);text-decoration:none}
154
+ .sh-side-list a::after{content:"";position:absolute;inset:0}
155
+ .sh-side-list li:hover a{color:var(--blue)}
156
+ .sh-side-list span{display:block;margin-top:4px;font-size:var(--fs-meta);color:var(--muted)}
157
+
158
+ /* ---------- column (series) cards ---------- */
159
+ .sh-cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;margin-top:24px}
160
+ .sh-col{position:relative;display:flex;flex-direction:column;background:var(--card);border:1px solid var(--rule);border-radius:var(--r-lg);overflow:hidden;transition:border-color .15s ease}
161
+ .sh-col:hover{border-color:var(--rule-strong)}
162
+ .sh-col .cover{aspect-ratio:16/9;display:flex;align-items:center;justify-content:center;border-radius:calc(var(--r-lg) - 8px);margin:8px 8px 0;overflow:hidden}
163
+ .sh-col .cover .cover-art{height:84%;width:auto}
164
+ .sh-col .body{padding:18px 22px 22px}
165
+ .sh-col h3{margin:0;font:700 var(--fs-lead)/1.4 var(--sans);color:var(--ink)}
166
+ .sh-col h3 a{text-decoration:none}
167
+ .sh-col h3 a::after{content:"";position:absolute;inset:0}
168
+ .sh-col:hover h3 a{color:var(--blue)}
169
+ .sh-col:has(h3 a:focus-visible){outline:2px solid var(--blue);outline-offset:3px}
170
+ .sh-col h3 a:focus-visible{outline:none}
171
+ .sh-col .count{font-size:var(--fs-meta);color:var(--muted);margin:4px 0 0}
172
+ .sh-col p{margin:10px 0 0;font-size:15px;line-height:1.7;color:var(--soft)}
173
+
174
+ /* columns page */
175
+
176
+ /* ---------- tags ---------- */
177
+ .sh-tagcloud{display:flex;flex-wrap:wrap;gap:6px 4px;list-style:none;margin:0;padding:0}
178
+ .sh-tagcloud .sh-chip{border-color:var(--rule);background:var(--card)}
179
+ @media (hover:hover){.sh-tagcloud .sh-chip:hover{border-color:var(--rule-strong);background:var(--card)}}
180
+ .sh-tagcloud .sh-chip[aria-pressed="true"]{background:var(--blue-tint);border-color:var(--blue-line)}
181
+ .sh-once{margin:20px 0 0}
182
+ .sh-once summary{display:inline-flex;align-items:center;gap:8px;cursor:pointer;font-size:15px;color:var(--soft);padding:8px 0;list-style:none}
183
+ .sh-once summary::-webkit-details-marker{display:none}
184
+ .sh-once summary svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.8;transition:transform .2s ease}
185
+ .sh-once[open] summary svg{transform:rotate(180deg)}
186
+ .sh-once .sh-tagcloud{margin-top:10px}
187
+ .sh-once .sh-chip{min-height:32px;font-size:var(--fs-small)}
188
+ .sh-tagresult{margin-top:40px;scroll-margin-top:calc(var(--hdr) + 16px)}
189
+ .sh-tagresult h2{font:700 var(--fs-lead)/1.3 var(--sans);color:var(--ink);margin:0;padding-bottom:12px;border-bottom:1px solid var(--rule-strong)}
190
+ .sh-tagresult .hint{color:var(--muted);font-size:15px;padding:20px 0}
191
+
192
+ /* ---------- about ---------- */
193
+ .sh-about{display:grid;grid-template-columns:minmax(0,var(--list)) var(--side);gap:0 72px;justify-content:space-between}
194
+ .sh-prose p{margin:0 0 1.1em}
195
+ .sh-prose h2{font:700 var(--fs-h2)/1.3 var(--sans);color:var(--ink);margin:56px 0 16px}
196
+ .sh-prose ul{margin:0;padding-left:1.2em}
197
+ .sh-prose li{margin:.35em 0}
198
+ .sh-prose a{color:var(--blue);text-underline-offset:4px;text-decoration-thickness:1px}
199
+ .sh-note{font-size:var(--fs-meta);color:var(--muted);margin-top:12px}
200
+
201
+ /* ---------- 404 ---------- */
202
+ .sh-404{padding:96px 0 24px;max-width:var(--list)}
203
+ .sh-404 .code{font:400 var(--fs-small)/1 var(--sans);color:var(--muted);margin:0 0 16px}
204
+ .sh-404 form{display:flex;gap:8px;margin-top:28px;max-width:520px}
205
+ .sh-input{flex:1;min-width:0;height:48px;padding:0 16px;border:1px solid var(--rule-strong);border-radius:var(--r);background:var(--card);color:var(--ink);font:400 16px/1 var(--sans)}
206
+ .sh-input::placeholder{color:var(--muted)}
207
+ .sh-btn{height:48px;padding:0 18px;border:0;border-radius:var(--r);background:var(--ink);color:var(--paper);font:600 15px/1 var(--sans);cursor:pointer}
208
+ .sh-btn:hover{background:var(--blue)}
209
+
210
+ /* ---------- footer ---------- */
211
+ .sh-footer{margin-top:120px;border-top:1px solid var(--rule)}
212
+ .sh-foot{max-width:var(--shell);margin:0 auto;padding:32px 32px 48px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:16px 32px;font-size:var(--fs-small);color:var(--muted)}
213
+ .sh-foot p{margin:0}
214
+ .sh-foot strong{color:var(--ink);font:600 15px/1 var(--serif);margin-right:12px}
215
+ .sh-foot nav{display:flex;flex-wrap:wrap;gap:4px 20px}
216
+ .sh-foot a{color:var(--soft);text-decoration:none;padding:4px 0}
217
+ .sh-foot a:hover{color:var(--blue)}
218
+ .sh-demo{width:100%;font-size:var(--fs-meta);color:var(--muted)}
219
+ .sh-demo a{text-decoration:underline;text-underline-offset:3px}
220
+
221
+ /* ---------- search palette ---------- */
222
+ .sh-pal{position:fixed;inset:0;z-index:80;display:flex;justify-content:center;align-items:flex-start;padding:12vh 20px 20px}
223
+ .sh-pal[hidden]{display:none}
224
+ .sh-pal-scrim{position:absolute;inset:0;background:rgba(20,19,18,.32);animation:sh-fade .16s ease}
225
+ html[data-theme="dark"] .sh-pal-scrim{background:rgba(0,0,0,.55)}
226
+ .sh-pal-box{position:relative;width:min(640px,100%);max-height:min(560px,76vh);display:flex;flex-direction:column;background:var(--card);border:1px solid var(--rule);border-radius:var(--r-lg);
227
+ box-shadow:0 18px 48px rgba(27,26,24,.18),0 2px 8px rgba(27,26,24,.06);overflow:hidden;animation:sh-pop .18s var(--ease)}
228
+ .sh-pal-top{display:flex;align-items:center;gap:12px;padding:0 12px 0 20px;border-bottom:1px solid var(--rule)}
229
+ .sh-pal-top svg{width:20px;height:20px;fill:none;stroke:var(--muted);stroke-width:1.7;stroke-linecap:round;flex:none}
230
+ .sh-pal-input{flex:1;min-width:0;height:60px;border:0;background:transparent;color:var(--ink);font:400 18px/1 var(--sans);outline:none}
231
+ .sh-pal-input::placeholder{color:var(--muted)}
232
+ .sh-pal-input::-webkit-search-cancel-button{display:none}
233
+ .sh-pal-cancel{display:none;font:400 16px/1 var(--sans);color:var(--blue);background:none;border:0;padding:12px 4px;cursor:pointer}
234
+ .sh-pal-list{overflow-y:auto;overscroll-behavior:contain;padding:8px}
235
+ .sh-pal-group{font:600 var(--fs-meta)/1 var(--sans);color:var(--muted);padding:14px 12px 8px}
236
+ .sh-pal-item{display:block;padding:10px 12px;border-radius:var(--r);text-decoration:none;color:var(--text);scroll-margin:8px}
237
+ .sh-pal-item .t{display:block;font:600 15px/1.5 var(--sans);color:var(--ink)}
238
+ .sh-pal-item .m{display:flex;flex-wrap:wrap;gap:2px 12px;margin-top:2px;font-size:var(--fs-meta);color:var(--muted)}
239
+ .sh-pal-item .x{display:block;margin-top:2px;font-size:var(--fs-meta);line-height:1.6;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
240
+ .sh-pal-item[aria-selected="true"]{background:var(--blue-tint)}
241
+ .sh-pal-item[aria-selected="true"] .t{color:var(--blue)}
242
+ .sh-pal-item mark{background:transparent;color:var(--blue);font-weight:700}
243
+ .sh-pal-empty{padding:28px 12px;color:var(--muted);font-size:15px;text-align:center}
244
+ .sh-pal-foot{display:flex;gap:18px;padding:10px 20px;border-top:1px solid var(--rule);font-size:12px;color:var(--muted);background:var(--paper)}
245
+ .sh-pal-foot span{display:inline-flex;align-items:center;gap:6px}
246
+ @keyframes sh-fade{from{opacity:0}}
247
+ @keyframes sh-pop{from{opacity:0;transform:translateY(-6px) scale(.985)}}
248
+
249
+ /* ---------- responsive ---------- */
250
+ @media (max-width:1080px){
251
+ .sh-listgrid,.sh-about{grid-template-columns:minmax(0,1fr)}
252
+ .sh-rail{display:none}
253
+ .sh-home-grid{grid-template-columns:minmax(0,1fr)}
254
+ .sh-cols{grid-template-columns:repeat(2,minmax(0,1fr))}
255
+ }
256
+ @media (max-width:820px){
257
+ :root,:root[data-theme="light"],:root[data-theme="dark"],html[data-theme="dark"]{--hdr:56px;--fs-display:34px;--fs-h2:22px}
258
+ .sh-bar{padding:0 12px 0 20px;gap:4px}
259
+ .sh-nav,.sh-search{display:none}
260
+ .sh-word{margin-right:auto}
261
+ .sh-menu-btn,.sh-search-m{display:inline-grid;width:44px;height:44px}
262
+ .sh-menu{display:block;position:absolute;left:0;right:0;top:100%;background:var(--paper);border-bottom:1px solid var(--rule);padding:8px 20px 20px;
263
+ opacity:0;transform:translateY(-6px);visibility:hidden;transition:opacity .18s ease,transform .18s var(--ease),visibility 0s .18s}
264
+ .sh-menu.is-open{opacity:1;transform:none;visibility:visible;transition:opacity .18s ease,transform .18s var(--ease)}
265
+ .sh-menu a{display:flex;align-items:center;min-height:52px;font-size:var(--fs-lead);color:var(--text);text-decoration:none;border-bottom:1px solid var(--rule)}
266
+ .sh-menu a[aria-current="page"],.sh-menu a[aria-current="true"]{color:var(--blue);font-weight:600}
267
+ .sh-menu .theme-row{display:flex;align-items:center;justify-content:space-between;min-height:56px;font-size:15px;color:var(--soft)}
268
+ .sh-menu .theme-row button{font:400 15px/1 var(--sans);color:var(--ink);background:var(--card);border:1px solid var(--rule);border-radius:var(--r);padding:0 14px;min-height:40px;cursor:pointer}
269
+ .sh-theme{display:none}
270
+ .sh-main{padding:0 20px}
271
+ .sh-head{padding:40px 0 24px}
272
+ .sh-intro{padding:44px 0 0}
273
+ .sh-intro p{font-size:var(--fs-body)}
274
+ .sh-feature{grid-template-columns:minmax(0,1fr);gap:20px;margin-top:36px}
275
+ .sh-feature h2{font-size:24px}
276
+ .sh-section{margin-top:56px}
277
+ .sh-cols{grid-template-columns:minmax(0,1fr);gap:16px}
278
+ /* one-line, scrollable filter row on phones */
279
+ .sh-filters{flex-wrap:nowrap;overflow-x:auto;margin:0 -20px 4px;padding:0 20px;scrollbar-width:none;-webkit-mask-image:linear-gradient(90deg,transparent 0,#000 20px,#000 calc(100% - 28px),transparent)}
280
+ .sh-filters::-webkit-scrollbar{display:none}
281
+ .sh-filters.is-open{flex-wrap:wrap;overflow:visible;-webkit-mask-image:none}
282
+ .sh-row,.sh-home-grid .sh-row{grid-template-columns:minmax(0,1fr) auto;gap:2px 12px;padding:16px 0 17px}
283
+ .sh-row time{grid-column:1;grid-row:1;font-size:var(--fs-meta);padding:0}
284
+ .sh-row .cat{grid-column:2;grid-row:1;padding:0}
285
+ .sh-row h3,.sh-row p{grid-column:1 / -1}
286
+ .sh-row h3{font-size:var(--fs-body)}
287
+ .sh-404{padding:48px 0 0}
288
+ .sh-footer{margin-top:80px}
289
+ .sh-foot{padding:24px 20px 40px;flex-direction:column}
290
+ /* palette becomes a full-screen sheet */
291
+ .sh-pal{padding:0}
292
+ .sh-pal-box{width:100%;max-height:none;height:100%;border-radius:0;border:0;animation:none}
293
+ .sh-pal-top{padding:max(env(safe-area-inset-top),0px) 12px 0 16px}
294
+ .sh-pal-cancel{display:block}
295
+ .sh-pal-foot{display:none}
296
+ .sh-pal-input{font-size:17px;height:56px}
297
+ }
298
+ @media (prefers-reduced-motion:reduce){
299
+ *,*::before,*::after{animation:none!important;transition:none!important}
300
+ html{scroll-behavior:auto!important}
301
+ }
302
+
303
+ /* ===== v2: sub-categories, breadcrumb, 随想 stream, live GitHub ===== */
304
+ .sh-crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;margin:0 0 14px;font-size:var(--fs-small);color:var(--muted)}
305
+ .sh-crumbs[hidden]{display:none}
306
+ .sh-crumbs a{color:var(--muted);text-decoration:none;padding:4px 0}
307
+ .sh-crumbs a:hover{color:var(--blue);text-decoration:underline;text-underline-offset:3px}
308
+ .sh-crumbs .sep{color:var(--muted)}
309
+ .sh-crumbs [aria-current]{color:var(--soft)}
310
+ .sh-subs{display:flex;flex-wrap:wrap;gap:6px 6px;list-style:none;margin:6px 0 4px;padding:12px 0 0;border-top:1px solid var(--rule)}
311
+ .sh-subs[hidden]{display:none}
312
+ .sh-sub{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:0 11px;border:1px solid var(--rule);border-radius:var(--r);background:var(--card);
313
+ color:var(--soft);font:400 var(--fs-small)/1 var(--sans);text-decoration:none;white-space:nowrap;transition:border-color .15s ease,color .15s ease}
314
+ .sh-sub .n{font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
315
+ @media (hover:hover){.sh-sub:hover{border-color:var(--rule-strong);color:var(--ink)}}
316
+ .sh-sub[aria-current="true"]{border-color:var(--blue-line);background:var(--blue-tint);color:var(--blue);font-weight:600}
317
+ .sh-sub[aria-current="true"] .n{color:var(--blue)}
318
+ .sh-rail li > span.row{display:flex;justify-content:space-between;gap:12px;padding:5px 0}
319
+
320
+ /* 随想: a narrower stream, full text, media inline, meta at the foot */
321
+ .sh-stream{max-width:620px}
322
+ .sh-notice{margin:0 0 8px;padding:14px 18px;border:1px dashed var(--rule-strong);border-radius:var(--r);font-size:var(--fs-small);line-height:1.7;color:var(--soft)}
323
+ .sh-notice strong{color:var(--ink)}
324
+ .sh-month > h2{font:700 var(--fs-lead)/1.3 var(--sans);color:var(--ink);margin:40px 0 0;padding:0 0 12px;border-bottom:1px solid var(--rule-strong);display:flex;align-items:baseline;gap:10px}
325
+ .sh-month > h2 .n{font:400 var(--fs-meta)/1 var(--sans);color:var(--muted)}
326
+ .sh-note{padding:28px 0 26px;border-bottom:1px solid var(--rule);scroll-margin-top:calc(var(--hdr) + 16px)}
327
+ .sh-note:target{background:linear-gradient(var(--blue-tint),var(--blue-tint)) no-repeat;background-size:100% 100%;box-shadow:-16px 0 0 var(--blue-tint),16px 0 0 var(--blue-tint)}
328
+ .sh-note h3{margin:0 0 6px;font:700 var(--fs-body)/1.55 var(--sans);color:var(--ink)}
329
+ .sh-note-body p{margin:0 0 .7em;font-size:var(--fs-body);line-height:1.8;color:var(--text)}
330
+ .sh-note-body p:last-child{margin-bottom:0}
331
+ .sh-note-imgs{display:grid;gap:6px;margin:16px 0 0;border-radius:var(--r);overflow:hidden}
332
+ .sh-note-imgs img{display:block;width:100%;height:100%;object-fit:cover;background:var(--well)}
333
+ .sh-note-imgs.one img{aspect-ratio:3/2}
334
+ .sh-note-imgs.two{grid-template-columns:1fr 1fr}.sh-note-imgs.two img{aspect-ratio:1/1}
335
+ .sh-note-imgs.three{grid-template-columns:2fr 1fr;grid-template-rows:1fr 1fr;aspect-ratio:3/2}
336
+ .sh-note-imgs.three img:first-child{grid-row:1 / 3}
337
+ .sh-note-video{display:block;width:100%;aspect-ratio:16/9;margin:16px 0 0;border-radius:var(--r);background:#000;object-fit:cover}
338
+ .sh-note-audio{margin:16px 0 0;padding:12px 14px;border:1px solid var(--rule);border-radius:var(--r);background:var(--card)}
339
+ .sh-note-audio figcaption{font-size:var(--fs-meta);color:var(--muted);margin:0 0 8px}
340
+ .sh-note-audio audio{display:block;width:100%;height:36px}
341
+ .sh-note-link{display:grid;gap:2px;margin:16px 0 0;padding:14px 16px;border:1px solid var(--rule);border-radius:var(--r);background:var(--card);text-decoration:none;transition:border-color .15s ease}
342
+ @media (hover:hover){.sh-note-link:hover{border-color:var(--rule-strong)}.sh-note-link:hover .t{color:var(--blue)}}
343
+ .sh-note-link .h{font-size:var(--fs-meta);color:var(--muted)}
344
+ .sh-note-link .t{font:600 15px/1.5 var(--sans);color:var(--ink)}
345
+ .sh-note-link .t::after{content:" ↗";color:var(--muted);font-weight:400}
346
+ .sh-note-link .d{font-size:var(--fs-small);line-height:1.6;color:var(--soft)}
347
+ .sh-note footer{display:flex;flex-wrap:wrap;align-items:center;gap:4px 14px;margin-top:14px;font-size:var(--fs-meta);color:var(--muted)}
348
+ .sh-note .when{color:var(--muted);text-decoration:none;font-variant-numeric:tabular-nums;padding:4px 0}
349
+ .sh-note .when:hover{color:var(--blue);text-decoration:underline;text-underline-offset:3px}
350
+ .sh-note .tag{color:var(--soft)}
351
+ .sh-note .ex{font-size:12px;line-height:1;color:var(--muted);border:1px dashed var(--rule-strong);border-radius:6px;padding:3px 6px}
352
+ html[data-theme="dark"] .sh-note-imgs img,html[data-theme="dark"] .sh-note-video{filter:brightness(.92)}
353
+
354
+ @media (max-width:820px){
355
+ .sh-subs{flex-wrap:nowrap;overflow-x:auto;margin:6px -20px 4px;padding:12px 20px 0;scrollbar-width:none}
356
+ .sh-subs::-webkit-scrollbar{display:none}
357
+ .sh-note:target{box-shadow:-20px 0 0 var(--blue-tint),20px 0 0 var(--blue-tint)}
358
+ }
359
+
360
+
361
+ /* ===== About: GitHub project cards (live) ===== */
362
+ .sh-repos{list-style:none;margin:0;padding:0!important;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
363
+ .sh-repos > li{position:relative;margin:0;display:grid;grid-template-rows:24px 48px 20px;row-gap:6px;padding:16px 18px;
364
+ border:1px solid var(--rule-strong);border-radius:var(--r);background:transparent;transition:border-color .15s ease}
365
+ .sh-repos h3{margin:0;display:flex;align-items:center;gap:8px;min-width:0;font:600 16px/24px var(--sans)}
366
+ .sh-repos h3 a{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink);text-decoration:none}
367
+ .sh-repos h3 a::after{content:"";position:absolute;inset:0;border-radius:inherit}
368
+ .sh-repos h3 a:focus-visible{outline:none}
369
+ .sh-repos > li:has(a:focus-visible){outline:2px solid var(--blue);outline-offset:2px}
370
+ .sh-repos .arr{margin-left:auto;flex:none;color:var(--muted);font-weight:400;font-size:14px;transition:color .15s ease,transform .15s ease}
371
+ .sh-repos .desc{margin:0;font-size:14.5px;line-height:24px;color:var(--soft);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
372
+ .sh-repos .desc.none{color:var(--muted)}
373
+ .sh-repos .meta{display:flex;align-items:center;gap:14px;min-width:0;overflow:hidden;white-space:nowrap;font-size:var(--fs-meta);line-height:20px;color:var(--muted);font-variant-numeric:tabular-nums}
374
+ .sh-repos .meta > span{display:inline-flex;align-items:center;gap:5px}
375
+ .sh-repos .dot{width:10px;height:10px;border-radius:50%;background:var(--c,var(--muted));box-shadow:inset 0 0 0 1px rgba(0,0,0,.08)}
376
+ .sh-repos .meta svg{width:14px;height:14px;fill:currentColor}
377
+ .sh-repos .meta time{margin-left:auto}
378
+ @media (hover:hover){
379
+ .sh-repos > li:not(.sk):hover{border-color:var(--muted)}
380
+ .sh-repos > li:not(.sk):hover h3 a{color:var(--blue)}
381
+ .sh-repos > li:not(.sk):hover .arr{color:var(--blue);transform:translate(2px,-2px)}
382
+ }
383
+ /* skeleton card: same box, same rows */
384
+ .sh-repos > li.sk{overflow:hidden;border-color:var(--rule)}
385
+ .sh-repos .sk i{display:block;border-radius:6px;background:var(--well)}
386
+ .sh-repos .sk .a{width:46%;height:14px;margin-top:5px}
387
+ .sh-repos .sk .b{width:94%;height:12px;margin:6px 0}.sh-repos .sk .b + .b{width:62%}
388
+ .sh-repos .sk .c{width:58%;height:11px;margin-top:5px}
389
+ .sh-repos > li.sk::after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,transparent,rgba(255,255,255,.5),transparent);transform:translateX(-100%);animation:sh-shimmer 1.4s infinite}
390
+ html[data-theme="dark"] .sh-repos > li.sk::after{background:linear-gradient(90deg,transparent,rgba(255,255,255,.05),transparent)}
391
+ @keyframes sh-shimmer{to{transform:translateX(100%)}}
392
+ @media (max-width:820px){.sh-repos{grid-template-columns:minmax(0,1fr);gap:12px}}
393
+ @media (prefers-reduced-motion:reduce){.sh-repos > li.sk::after{display:none}.sh-repos .arr{transition:none}}
394
+
395
+ /* ===== 专栏 v4: moderate cover, no numbers, dates, link to category ===== */
396
+ .sh-series{display:grid;grid-template-columns:240px minmax(0,760px);gap:0 40px;padding:36px 0;border-top:1px solid var(--rule)}
397
+ .sh-series:first-of-type{border-top:0;padding-top:8px}
398
+ .sh-series .cover{aspect-ratio:16/9;border-radius:var(--r);display:flex;align-items:center;justify-content:center;overflow:hidden;align-self:start}
399
+ .sh-series .cover .cover-art{height:84%;width:auto}
400
+ .sh-series h2{font:700 var(--fs-h2)/1.3 var(--sans);color:var(--ink);margin:0;scroll-margin-top:calc(var(--hdr) + 16px)}
401
+ .sh-series .count{font-size:var(--fs-small);color:var(--muted);margin:6px 0 0}
402
+ .sh-series .desc{font-size:var(--fs-body);line-height:1.7;color:var(--soft);margin:12px 0 0}
403
+ .sh-series ol{margin:18px 0 0;padding:0;list-style:none}
404
+ .sh-series li{display:flex;align-items:baseline;gap:16px;border-top:1px solid var(--rule)}
405
+ .sh-series li:last-child{border-bottom:1px solid var(--rule)}
406
+ .sh-series li[hidden]{display:none}
407
+ .sh-series li a{flex:1;min-width:0;padding:11px 0;font-size:15px;line-height:1.6;color:var(--text);text-decoration:none}
408
+ .sh-series li time{flex:none;font-size:var(--fs-meta);color:var(--muted);font-variant-numeric:tabular-nums}
409
+ @media (hover:hover){.sh-series li a:hover{color:var(--blue)}}
410
+ .sh-series .acts{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px 20px;margin-top:14px}
411
+ .sh-series .expand{font:400 var(--fs-small)/1 var(--sans);color:var(--soft);background:none;border:1px solid var(--rule);border-radius:var(--r);padding:10px 14px;margin:0;cursor:pointer}
412
+ @media (hover:hover){.sh-series .expand:hover{border-color:var(--rule-strong);color:var(--ink)}}
413
+ .sh-series .tocat{margin-left:auto;font-size:var(--fs-small);color:var(--blue);text-decoration:none;padding:8px 0}
414
+ .sh-series .tocat:hover{text-decoration:underline;text-underline-offset:4px}
415
+ .sh-colnote{margin:12px 0 0;font-size:var(--fs-small);color:var(--soft)}
416
+ .sh-colnote[hidden]{display:none}
417
+ .sh-colnote a{color:var(--blue);text-underline-offset:3px}
418
+ @media (max-width:1080px){.sh-series{grid-template-columns:180px minmax(0,1fr);gap:0 28px}}
419
+ @media (max-width:820px){
420
+ .sh-series{grid-template-columns:104px minmax(0,1fr);column-gap:16px;padding:28px 0}
421
+ .sh-series > .body{display:contents}
422
+ .sh-series .cover{grid-column:1;grid-row:1 / 3;align-self:center;border-radius:10px}
423
+ .sh-series h2{grid-column:2;grid-row:1;align-self:end;font-size:21px}
424
+ .sh-series .count{grid-column:2;grid-row:2;align-self:start;margin-top:4px}
425
+ .sh-series .desc,.sh-series ol,.sh-series .acts{grid-column:1 / -1}
426
+ .sh-series .desc{font-size:15px;margin-top:14px}
427
+ .sh-series li{flex-direction:column;align-items:stretch;gap:0;padding:10px 0}
428
+ .sh-series li a{padding:0}
429
+ .sh-series li time{margin-top:2px}
430
+ .sh-series .tocat{margin-left:0}
431
+ }
432
+
433
+ .sh-row .incol{grid-column:2;justify-self:start;position:relative;z-index:1;margin-top:4px;font-size:var(--fs-meta);line-height:1.6;color:var(--muted);text-decoration:none}
434
+ @media (hover:hover){.sh-row .incol:hover{color:var(--blue);text-decoration:underline;text-underline-offset:3px}}
435
+ .is-compact .sh-row .incol{display:none}
436
+ @media (max-width:820px){.sh-row .incol{grid-column:1 / -1}}
437
+
438
+
@@ -0,0 +1,111 @@
1
+ /* A2 tokens. One source for the site, the article, and every component. */
2
+
3
+ :root,
4
+ :root[data-theme="light"] {
5
+ --paper: #f8f7f3;
6
+ --paper-deep: #f0eee8;
7
+ --card: #ffffff;
8
+ --ink: #1b1a18;
9
+ --ink-soft: #2d2c29;
10
+ --text: #2d2c29;
11
+ --soft: #4a4844;
12
+ --muted: #6e6b64;
13
+ --grey: #737069;
14
+ --faint: #737069;
15
+ --rule: #e6e3dc;
16
+ --rule-strong: #d6d2c9;
17
+ --line: #e6e3dc;
18
+ --line-strong: #d6d2c9;
19
+ --well: #f0eee8;
20
+ --tint: rgb(27 26 24 / 5.5%);
21
+ --blue: #2c55b4;
22
+ --blue-tint: #eaf0fc;
23
+ --blue-line: rgb(44 85 180 / 28%);
24
+ --cover: #5e86e0;
25
+ --accent: var(--blue);
26
+ --accent-soft: var(--blue-tint);
27
+ --selection: #eaf0fc;
28
+ --hl: #ffe58f;
29
+ --red: #d82d48;
30
+ --red-text: #c2263f;
31
+ --red-tint: #fbeef0;
32
+ --sans: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", "Noto Sans CJK SC", "Microsoft YaHei", "Segoe UI", system-ui, sans-serif;
33
+ --serif: "Source Han Serif SC", "Noto Serif SC", "Noto Serif CJK SC", "Songti SC", "STSong", "Source Serif 4", Georgia, serif;
34
+ --mono: "JetBrains Mono", "SF Mono", ui-monospace, Menlo, Consolas, "Noto Sans Mono CJK SC", monospace;
35
+ --font-mono: var(--mono);
36
+ --fs-display: 46px;
37
+ --fs-hero: 52px;
38
+ --fs-h2: 26px;
39
+ --fs-lead: 20px;
40
+ --fs-body: 17px;
41
+ --fs-small: 14px;
42
+ --fs-meta: 13px;
43
+ --r: 12px;
44
+ --r-lg: 24px;
45
+ --col: 700px;
46
+ --rail: 232px;
47
+ --gap: 48px;
48
+ --content: var(--col);
49
+ --wide: 1200px;
50
+ --shell: 1200px;
51
+ --hdr: 64px;
52
+ color-scheme: light;
53
+ }
54
+
55
+ :root[data-theme="dark"],
56
+ html[data-theme="dark"] {
57
+ --paper: #161513;
58
+ --paper-deep: #22211d;
59
+ --card: #1e1d1a;
60
+ --ink: #eeece7;
61
+ --ink-soft: #dcd9d2;
62
+ --text: #dcd9d2;
63
+ --soft: #bdb9b0;
64
+ --muted: #a09b91;
65
+ --grey: #a09b91;
66
+ --faint: #a09b91;
67
+ --rule: #2f2d29;
68
+ --rule-strong: #45423c;
69
+ --line: #2f2d29;
70
+ --line-strong: #45423c;
71
+ --well: #22211d;
72
+ --tint: rgb(238 236 231 / 7%);
73
+ --blue: #93b2f4;
74
+ --blue-tint: rgb(147 178 244 / 13%);
75
+ --blue-line: rgb(147 178 244 / 35%);
76
+ --cover: #4c72c9;
77
+ --accent: var(--blue);
78
+ --accent-soft: var(--blue-tint);
79
+ --selection: rgb(147 178 244 / 22%);
80
+ --hl: #6b5a2a;
81
+ --red: #f29aab;
82
+ --red-text: #f29aab;
83
+ --red-tint: rgb(242 154 171 / 12%);
84
+ color-scheme: dark;
85
+ }
86
+
87
+ @media (min-width: 1400px) {
88
+ :root,
89
+ :root[data-theme="light"],
90
+ :root[data-theme="dark"],
91
+ html[data-theme="dark"] { --fs-hero: 56px; }
92
+ }
93
+
94
+ @media (max-width: 1239px) {
95
+ :root,
96
+ :root[data-theme="light"],
97
+ :root[data-theme="dark"],
98
+ html[data-theme="dark"] { --fs-hero: 44px; }
99
+ }
100
+
101
+ @media (max-width: 640px) {
102
+ :root,
103
+ :root[data-theme="light"],
104
+ :root[data-theme="dark"],
105
+ html[data-theme="dark"] {
106
+ --fs-hero: 30px;
107
+ --fs-h2: 23px;
108
+ --fs-lead: 18px;
109
+ --fs-body: 16.5px;
110
+ }
111
+ }
@@ -0,0 +1,28 @@
1
+ import { Suspense } from "react";
2
+ import { posts } from "../lib/content";
3
+ import { TagIndex } from "../components/TagIndex";
4
+ import { siteDocumentTitle } from "../site.config";
5
+
6
+ export const metadata = {
7
+ title: siteDocumentTitle,
8
+ description: "按标签浏览文章。标签不是专栏,也不是分类。",
9
+ };
10
+
11
+ export default function TagsPage() {
12
+ const counts = new Map<string, number>();
13
+ for (const post of posts) {
14
+ for (const tag of post.tags ?? []) counts.set(tag, (counts.get(tag) ?? 0) + 1);
15
+ }
16
+ const once = [...counts.values()].filter((count) => count === 1).length;
17
+ return (
18
+ <main className="sh-main" id="content">
19
+ <div className="sh-head">
20
+ <h1>标签</h1>
21
+ <p className="sh-deck">{counts.size} 个标签,按使用次数排序;只用过一次的 {once} 个收在下面。点选一个标签,下方列出对应文章。</p>
22
+ </div>
23
+ <Suspense>
24
+ <TagIndex posts={posts} />
25
+ </Suspense>
26
+ </main>
27
+ );
28
+ }
@@ -1,3 +1,4 @@
1
+ import Image from "next/image";
1
2
  import { ParityInteractions } from "../../ui/react/parity";
2
3
 
3
4
  const slices = [
@@ -61,7 +62,7 @@ export default function UiParityPage() {
61
62
  <p className="garden-ui-text-sm">统计期为 2024 年 8 月至 2025 年 8 月。</p>
62
63
  <p className="garden-ui-caption">
63
64
  {"抖音电商官方报告,经新华社报道。该增速不能直接代表抖音电商整体 GMV 增速。"}<a id="parity-cite" className="garden-ui-cite" href="https://www.news.cn">
64
- <img src="https://www.google.com/s2/favicons?domain=www.news.cn&sz=32" alt="" width={12} height={12} />
65
+ <Image src="https://www.google.com/s2/favicons?domain=www.news.cn&sz=32" alt="" width={12} height={12} unoptimized />
65
66
  Xinhua Net
66
67
  </a>
67
68
  </p>
@@ -72,7 +73,7 @@ export default function UiParityPage() {
72
73
  <p className="garden-ui-text-xs">商品条目</p>
73
74
  <p className="garden-ui-caption">
74
75
  {"阿里巴巴披露其 AI 搜索推荐系统索引了超过 20 亿商品条目。"}<a className="garden-ui-cite" href="https://www.alibabagroup.com">
75
- <img src="https://www.google.com/s2/favicons?domain=www.alibabagroup.com&sz=32" alt="" width={12} height={12} />
76
+ <Image src="https://www.google.com/s2/favicons?domain=www.alibabagroup.com&sz=32" alt="" width={12} height={12} unoptimized />
76
77
  Alibaba Group
77
78
  </a>
78
79
  </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hd-garden",
3
- "version": "0.3.13",
3
+ "version": "0.4.1",
4
4
  "private": false,
5
5
  "description": "用 Markdown 写作、用 GitHub Pages 发布的个人博客引擎。",
6
6
  "license": "MIT",
@@ -6,6 +6,7 @@ import { marked } from "marked";
6
6
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
7
7
  import { canonicalSiteDate } from "./site-date.mjs";
8
8
  import { contentForFeed, contentToSearchText, transformUiContent } from "../ui/core/transform.mjs";
9
+ import { countDemoWords } from "../ui/garden/word-count.mjs";
9
10
 
10
11
  function resolveSiteRoot() {
11
12
  if (process.env.GARDEN_SITE) return path.resolve(process.env.GARDEN_SITE);
@@ -527,9 +528,17 @@ assertUnique(
527
528
  );
528
529
  assertUnique(thoughts, (item) => item.slug, "随想 slug");
529
530
 
531
+ const parityDir = path.join(root, "parity", "articles");
532
+ const a2WordCounts = fs.existsSync(parityDir)
533
+ ? Object.fromEntries(fs.readdirSync(parityDir).filter((name) => name.endsWith(".md")).sort().map((name) => {
534
+ const parsed = matter(fs.readFileSync(path.join(parityDir, name), "utf8"));
535
+ return [name.slice(0, -3), countDemoWords(parsed.content)];
536
+ }))
537
+ : {};
538
+
530
539
  const contentHash = crypto
531
540
  .createHash("sha256")
532
- .update(JSON.stringify({ posts, columns, thoughts }))
541
+ .update(JSON.stringify({ posts, columns, thoughts, a2WordCounts }))
533
542
  .digest("hex")
534
543
  .slice(0, 12);
535
544
 
@@ -581,6 +590,7 @@ export const contentHash = "${contentHash}";
581
590
  export const posts = ${JSON.stringify(posts, null, 2)} as ContentEntry[];
582
591
  export const columns = ${JSON.stringify(columns, null, 2)} as ContentEntry[];
583
592
  export const thoughts = ${JSON.stringify(thoughts, null, 2)} as ContentEntry[];
593
+ export const a2WordCounts = ${JSON.stringify(a2WordCounts, null, 2)} as Record<string, number>;
584
594
  `;
585
595
 
586
596
  fs.mkdirSync(path.dirname(output), { recursive: true });