forked from package-url/packageurl-js
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathoverrides.css
More file actions
3926 lines (3573 loc) · 115 KB
/
overrides.css
File metadata and controls
3926 lines (3573 loc) · 115 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* ------------------------------------------------------------------
* Docs-site stylesheet — owns 100% of the visual rules.
*
* scripts/tour.mts copies this over meander's emitted walkthrough.css
* wholesale during post-processing; every styling decision on the
* deployed site comes from here. Meander owns the HTML shape (class
* names, structure); this file owns the look.
*
* Organised top-down:
* 1. @font-face (Geist + Geist Mono, self-hosted variable)
* 2. Theme tokens (system / light / dark / synthwave)
* 3. Reset + base typography
* 4. Layout shell (topbar, main, footer)
* 5. Buttons, pills, keycaps
* 6. Tables, TOC rows, three-column feature grid
* 7. Annotated part pages (pair-grid, code-section, file-block)
* 8. Code / diff table styles
* 9. Doc body prose
* 10. Theme toggle UI
* 11. Comment shim UI
* ------------------------------------------------------------------ */
/* ── 1. Fonts ─────────────────────────────────────────────────────── */
@font-face {
font-family: 'Geist';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/Geist.woff2') format('woff2-variations');
}
@font-face {
font-family: 'Geist Mono';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/GeistMono.woff2') format('woff2-variations');
}
/* Opt every same-origin navigation into the View Transitions API.
* Supporting browsers (Chrome 126+, Safari 18+) cross-fade the old
* page's frame into the new one — no JS, no rework needed in the
* build. Non-supporting browsers ignore the rule; no polyfill
* shipped. Honors reduced-motion at the UA level. */
@view-transition {
navigation: auto;
}
/* ── 2. Theme tokens ──────────────────────────────────────────────── */
/* Light theme — eggshell + warm-pastel palette. Neutrals lean
* WARM (hint of cream) instead of cool grey so the page reads
* as "off-white stationery" rather than "grey UI". Contrast
* ladder stays: --ink (near-black for headings) → --body
* (warm medium for prose) → --muted (lighter warm for meta).
* Pastels for accent/status so status badges stay gentle. */
:root,
html[data-theme='light'] {
--bg: #fdfaf5;
--bg-elev: #fbf6ec;
--bg-tint: #f6efe0;
--surface: #fdfaf5;
--ink: #1a1714;
--body: #5a5248;
--muted: #8a8074;
--border: #ece3d2;
--border-strong: #d8cdb8;
/* Light-mode accent — warm brown rust. Less red, more
* hazelnut so the accent sits with the cream eggshell and
* sepia body text as one "spice" family rather than a red
* callout that fights the warm palette. */
--accent: #92551c;
--accent-fg: #7a4619;
--accent-bg: #f3e3ca;
/* Nav state tokens — warm eggshell tints of the page bg, one
* family so hover-over-selected doesn't clash. Selected is a
* touch deeper than hover so the active row still reads as the
* anchor when a sibling is hovered. */
--nav-hover-bg: #f2e8d2;
--nav-selected-bg: #ede2c8;
--danger: #c2410c;
--success: #15803d;
/* Code panel — warm starry-night charcoal, not ink-black.
* Lifts the right-panel tone off "hole in the page" into
* "deep dark room" so the cream light palette and the code
* surface share the same warmth family. */
--code-bg: #363230;
--code-fg: #f5e9c8;
--code-comment: #8b949e;
--code-keyword: #ff7b72;
--code-string: #a5d6ff;
--code-number: #79c0ff;
--code-function: #d2a8ff;
--code-type: #ffa657;
--diff-add-bg: rgba(46, 160, 67, 0.15);
--diff-del-bg: rgba(248, 81, 73, 0.15);
--diff-add-mark: #2ea043;
--diff-del-mark: #f85149;
--shadow-sm: 0 1px 2px rgba(90, 60, 30, 0.06);
--shadow-md: 0 2px 8px rgba(90, 60, 30, 0.08);
--shadow-lg: 0 8px 24px rgba(90, 60, 30, 0.1);
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 14px;
--transition-fast: 120ms ease;
--transition-med: 220ms ease;
/* Universal tokens (theme-independent). --font-mono is the
* one mono stack every code surface uses — Geist Mono (self-
* hosted above), then system fallbacks. --col-split is the
* splitter ratio drag.js writes back via inline style on :root. */
--font-mono:
'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas,
'Liberation Mono', monospace;
--col-split: 42;
}
/* System preference — follow prefers-color-scheme. Only applies when
* the user hasn't picked an explicit theme (data-theme missing or
* absent). The drag.js bootstrap resolves 'system' to 'light' or
* 'dark' on page load, so this block is a fallback for pre-JS paint. */
@media (prefers-color-scheme: dark) {
:root {
--bg: #0a0a0a;
--bg-elev: #111111;
--bg-tint: #161616;
--surface: #111111;
--ink: #fafafa;
--body: #a1a1aa;
--muted: #9ca3af;
--border: #262626;
--border-strong: #404040;
--accent: #93c5fd;
--accent-fg: #93c5fd;
--accent-bg: #1a2540;
/* Code panel — starry-night cool charcoal, not pure black.
* Sits above --bg (#0a0a0a) / --surface (#111111) so the
* code surface reads as lifted, not as void. */
--code-bg: #1c2028;
}
}
/* Dark theme — explicit toggle pick. Higher-contrast-than-system so
* code blocks and diff markers read cleanly. */
html[data-theme='dark'] {
--bg: #0a0a0a;
--bg-elev: #111111;
--bg-tint: #161616;
--surface: #111111;
--ink: #fafafa;
--body: #a1a1aa;
--muted: #71717a;
--border: #262626;
--border-strong: #404040;
--accent: #93c5fd;
--accent-fg: #93c5fd;
--accent-bg: #1a2540;
--nav-hover-bg: #0a0a0a;
--nav-selected-bg: #1e2a3a;
/* Code panel — starry-night cool charcoal, not pure black.
* Sits above --bg (#0a0a0a) / --surface (#111111) so the
* code surface reads as lifted, not as void. */
--code-bg: #1c2028;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-md: 0 2px 8px rgba(0, 0, 0, 0.4);
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5);
}
/* Synthwave — deep midnight purple with magenta accent. Not a
* light/dark variant; its own coherent palette. Background uses a
* subtle radial toward a warmer plum corner per the reference album
* art. Active-state pink matches the cassette-icon tag color. */
html[data-theme='synthwave'] {
--bg: radial-gradient(
ellipse at top right,
#3a2450 0%,
#2a1b3d 40%,
#1a0f2e 100%
);
--bg-elev: #2a1b3d;
--bg-tint: #241633;
--surface: #2a1b3d;
--ink: #f8f0ff;
--body: #c8b8e8;
--muted: #b6a6d4;
--border: #3d2a5a;
--border-strong: #5a3d7a;
--accent: #ff7edb;
--accent-fg: #ff7edb;
--accent-bg: rgba(255, 126, 219, 0.18);
/* Hover is darker than --bg-elev (#2a1b3d), selected is lighter
* — selected reads as lit-up, hover reads as pressed-into. Both
* use --accent (pink) for text. */
--nav-hover-bg: #1a0f2e;
--nav-selected-bg: #3d2a5a;
--danger: #ff6b9d;
--success: #4ade80;
--code-bg: #22143a;
--code-fg: #f8f0ff;
--code-comment: #9080b0;
--code-keyword: #ff7edb;
--code-string: #ffb86c;
--code-number: #bd93f9;
--code-function: #8be9fd;
--code-type: #50fa7b;
--diff-add-bg: rgba(80, 250, 123, 0.15);
--diff-del-bg: rgba(255, 107, 157, 0.15);
--diff-add-mark: #50fa7b;
--diff-del-mark: #ff6b9d;
--shadow-sm: 0 1px 2px rgba(255, 126, 219, 0.1);
--shadow-md: 0 2px 12px rgba(255, 126, 219, 0.15);
--shadow-lg: 0 8px 32px rgba(255, 126, 219, 0.2);
}
/* ── 3. Reset + base typography ──────────────────────────────────── */
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
background: var(--bg);
color: var(--body);
font-family:
'Geist',
system-ui,
-apple-system,
'Segoe UI',
sans-serif;
font-size: 17px;
line-height: 1.55;
font-feature-settings: 'ss01', 'cv01';
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Headings — near-black ink, tight tracking, extra-bold display
* weight. The giant H1 is the signature of the editorial look. */
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
color: var(--ink);
font-family: 'Geist', system-ui, sans-serif;
font-weight: 800;
letter-spacing: -0.025em;
line-height: 1.08;
}
/* H1 is small by default — sized for UI labels like the topbar
* wordmark. Hero headlines are opt-in via a parent class (e.g.
* .doc-body h1 or .hero h1) so we don't clobber chrome-sized
* occurrences. */
h1 {
font-size: 22px;
letter-spacing: -0.02em;
line-height: 1.15;
margin-bottom: 20px;
}
h2 {
font-size: 26px;
letter-spacing: -0.02em;
line-height: 1.2;
margin: 48px 0 18px;
}
h3 {
font-size: 22px;
letter-spacing: -0.02em;
line-height: 1.25;
margin: 40px 0 14px;
}
h4 {
font-size: 17px;
font-weight: 700;
letter-spacing: -0.01em;
margin: 28px 0 10px;
}
p {
margin: 0 0 16px;
color: var(--body);
line-height: 1.6;
}
/* Inline code — Geist Mono, faint box, no color. Meant to sit in
* prose without pulling focus. The first inline code token after a
* heading often reads in --ink for emphasis. */
code {
font-family: var(--font-mono);
font-size: 0.92em;
padding: 2px 6px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-tint);
color: var(--ink);
font-feature-settings: 'ss01';
}
/* First inline code in a paragraph is often a package/API name and
* should carry ink weight to match the "@pierre/diffs"-style opening. */
p > code:first-child,
li > code:first-child {
color: var(--ink);
font-weight: 500;
}
/* Inline code that drag.js routed through hljs for syntax coloring
* (typically single-backtick `@example` spans inside .annotation-md).
* The github-dark theme adds its own block-level padding + background
* to .hljs; we want the span to read like regular inline code plus
* syntax tint, not a floating code block. Keep our own box + padding;
* let hljs's per-token colors flow through. */
code.hljs.hljs-inline {
display: inline;
/* Tight internal padding so the pill hugs the text, plus
* generous horizontal + vertical margin so adjacent pills
* don't touch and wrapped pills stacking across lines get
* a clear row gap. Vertical margin is deliberately larger
* than horizontal so stacked pills read as separate rows
* even at cramped viewport widths. */
padding: 1px 6px;
margin: 4px 4px;
/* Starry-night charcoal — one notch lighter than the right-
* panel --code-bg so the pill reads as "mini right-panel"
* without punching a pure-black slot into the page. hljs
* tokens (blue numbers, purple types, red keywords, lilac
* functions) land on the dark pill the way they're designed
* to, matching the right-side code surface's language. One
* tone across every theme so inline pills keep a coherent
* identity when the user flips themes. */
background: #3e3a37;
color: var(--code-fg);
border: 1px solid color-mix(in srgb, var(--code-fg) 14%, transparent);
/* Keep inline code on a single line — a snippet split mid-
* token (`{ ca` / `che }`) reads worse than horizontal
* overflow. Same treatment as `.wt-purl`. */
white-space: nowrap;
}
/* PURL-shaped inline code (`pkg:npm/lodash@4.17.21` etc.) and bare
* identifier tokens (`arch`, `hex`, …). drag.js sniffs the content
* and tags these instead of running hljs — PURLs are a fixed-
* grammar identifier, not a language hljs can parse, and auto-
* detect mis-tokenizes them as stray punctuation. Render as the
* default inline-code box (monospace + tint + border) with no
* syntax coloring. Keep on a single line — a PURL split mid-token
* (e.g. `pkg:np` / `m/...`) is worse than horizontal overflow. */
code.wt-purl,
.annotation-md code.wt-purl {
/* Same deep-charcoal pill treatment as hljs inline. The
* second selector bumps specificity above meander's
* `.annotation-md code` default so the color/background
* aren't silently overridden. Since `.wt-purl` carries no
* hljs token classes, we paint the content in --code-fg
* (cream) directly — the pill reads as a single-tone quoted
* identifier. */
padding: 1px 6px;
margin: 4px 4px;
background: #3e3a37;
color: var(--code-fg);
border: 1px solid color-mix(in srgb, var(--code-fg) 14%, transparent);
white-space: nowrap;
}
/* Parameter-name pill on the `@param` top strip. The inline
* pill is a dark charcoal panel across all themes; --code-number
* (github-dark blue) renders as the hljs "params" token color
* on the right-side panel, so left/right reads as the same
* token. */
code.wt-jsdoc-param-name {
color: var(--code-number);
}
/* @example code blocks in the annotation prose should match
* the right-side code panel exactly — both sides run through
* hljs with github-dark's stylesheet, so token colors already
* match. We only override the BACKGROUND so the left block
* sits on the same surface tone as the right panel (and picks
* up a faint outline to read as "a code panel, not a quote").
* Leave token classes (.hljs-keyword etc.) untouched — they
* resolve to the CDN github-dark values that the right side
* also uses. */
.annotation-md pre:has(> code.hljs),
.annotation-md pre code.hljs {
background: var(--code-bg);
}
/* Inline hljs pills in .annotation-md — meander's base CSS
* sets its own color/background on `.annotation-md code`, so
* this rule bumps specificity to keep our deep-charcoal pill
* from being silently overridden. Matches the base rule above. */
.annotation-md code.hljs.hljs-inline {
background: #3e3a37;
color: var(--code-fg);
border: 1px solid color-mix(in srgb, var(--code-fg) 14%, transparent);
}
/* Identifier / class / builtin tokens inside inline pills — the
* github-dark CDN stylesheet paints these `#f0883e` (muddy
* amber) which reads as "too dark" on our lifted charcoal
* pill. Force identifier classes back to --code-fg so a lone
* class name like `PurlBuilder` reads cream instead of dim
* amber. Colored tokens (keyword / string / number / type)
* keep their github-dark palette since those provide the
* syntax signal the user expects. */
code.hljs.hljs-inline .hljs-title,
code.hljs.hljs-inline .hljs-title.class_,
code.hljs.hljs-inline .hljs-title.function_,
code.hljs.hljs-inline .hljs-built_in,
code.hljs.hljs-inline .hljs-variable.language_,
code.hljs.hljs-inline .hljs-attr,
code.hljs.hljs-inline .hljs-name,
code.hljs.hljs-inline .hljs-params,
.annotation-md code.hljs.hljs-inline .hljs-title,
.annotation-md code.hljs.hljs-inline .hljs-title.class_,
.annotation-md code.hljs.hljs-inline .hljs-title.function_,
.annotation-md code.hljs.hljs-inline .hljs-built_in,
.annotation-md code.hljs.hljs-inline .hljs-variable.language_,
.annotation-md code.hljs.hljs-inline .hljs-attr,
.annotation-md code.hljs.hljs-inline .hljs-name,
.annotation-md code.hljs.hljs-inline .hljs-params {
color: var(--code-fg);
}
.annotation-md pre:has(> code.hljs) {
border: 1px solid var(--border);
border-radius: 8px;
}
pre {
margin: 0;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.55;
}
pre code {
padding: 0;
border: none;
background: transparent;
font-size: inherit;
}
a {
color: var(--ink);
text-decoration: underline;
text-decoration-color: var(--border-strong);
text-underline-offset: 3px;
text-decoration-thickness: 1px;
transition: text-decoration-color var(--transition-fast);
}
a:hover {
text-decoration-color: var(--ink);
}
a:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: 2px;
}
hr {
border: none;
border-top: 1px solid var(--border);
margin: 48px 0;
}
ul,
ol {
padding-left: 24px;
margin: 0 0 20px;
}
li {
margin-bottom: 6px;
color: var(--body);
}
blockquote {
margin: 24px 0;
padding: 4px 0 4px 20px;
border-left: 2px solid var(--border-strong);
color: var(--body);
font-style: normal;
}
/* ── 4. Layout shell ──────────────────────────────────────────────── */
/* Topbar — wordmark only. Actions used to live here; they moved
* into the part-nav strip so this band is pure identity. No sticky;
* it scrolls away with the page. */
.topbar {
background: var(--bg);
border-bottom: 1px solid var(--border);
padding: 12px 32px;
display: flex;
align-items: center;
gap: 16px;
}
html[data-theme='synthwave'] .topbar {
background: color-mix(in srgb, #1a0f2e 88%, transparent);
}
/* Topbar wordmark. On the index:
* <code class="wt-product">packageurl-js</code>
* <span class="wt-descriptor">override by Socket</span>
* The product is the npm package this tour documents, styled as
* code (literal coordinate, not a marketing name). The descriptor
* sits alongside in a sly purple saying what Socket contributes.
* On part/doc pages this <h1> carries the part title as plain
* text — the rules below only style the wordmark shape when the
* children are present, so they no-op on part pages. */
.topbar h1 {
margin: 0;
font-size: 18px;
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.1;
color: var(--ink);
}
.topbar h1 .wt-product {
font-family: var(--font-mono);
font-size: 18px;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ink);
background: transparent;
border: none;
padding: 0;
/* Keep ligatures off so the hyphen in `packageurl-js` doesn't
* collapse into a figure-dash or similar glyph. */
font-variant-ligatures: none;
-webkit-font-feature-settings:
'liga' 0,
'clig' 0,
'calt' 0;
font-feature-settings:
'liga' 0,
'clig' 0,
'calt' 0;
}
/* Descriptor — "override by Socket" as a trademark-style marker
* raised from the baseline like <sup>. Small, sly purple,
* signature tint across every theme. Stays in the inline flow so
* it can't get clipped by the topbar or positioned off-screen;
* vertical-align handles the raise. */
.topbar h1 .wt-descriptor {
display: inline-block;
margin-left: 6px;
vertical-align: top;
white-space: nowrap;
font-size: 10px;
font-weight: 500;
letter-spacing: 0;
line-height: 1;
color: var(--accent-fg);
}
.topbar p {
display: none;
}
/* Actions cluster (theme toggle, export, comments) — lives on the
* right side of .part-nav. `margin-left: auto` pushes it to the
* end of the flex row regardless of how many part pills precede
* it, so the cluster is always right-aligned. Items bottom-align
* so the visual baseline of the download square, chat-bubble body,
* and cassette outline share a horizontal guideline — even though
* each icon has its own viewBox + render size. */
.topbar-actions {
margin-left: auto;
display: flex;
align-items: flex-end;
gap: 4px;
}
/* Pre-reserve the action cluster width so the theme-toggle
* bolt lands in its final position on first paint — no jump
* when comments.js hydrates the two shim buttons.
*
* Part pages (body[data-has-comments]) will eventually carry
* 3 buttons × 30px + 2 × 4px gap = 98px. Reserving that
* min-width means the lone bolt, rendered first (prepended by
* drag.js), sits at the LEFT edge of a right-aligned 98px
* cluster. When comments.js appends the export + unresolved
* buttons, they fill the reserved slots to its right; the bolt
* doesn't move.
*
* Doc + index pages never get the comments buttons, but we
* still want the bolt at the rightmost slot for visual parity
* — same 98px reservation, different end-alignment via
* margin-left: auto on the bolt itself. */
body[data-has-comments] .topbar-actions {
min-width: 98px;
}
body:not([data-has-comments]) .topbar-actions .theme-toggle-wrapper {
margin-left: 68px;
}
/* <main> is a generic container — meander uses it for part-page
* file stacks (wide, two-column) and we use it for doc-page prose
* (narrow, single column). Keep the base rule wide; narrower
* constraints belong on `.doc-body` and `.wt-contents` (the index
* TOC), not on every <main>. */
main {
margin: 0 auto;
padding: 8px 32px 80px;
}
/* Index TOC: narrow editorial column — reading material, not a
* data view. */
main .wt-contents {
max-width: 720px;
margin-left: auto;
margin-right: auto;
}
/* Part pages (.files-stack) fill the viewport width up to a
* generous cap — wide monitors shouldn't have prose + code
* wedged into a small center strip when there's real estate to
* spread out. Leaves each column room to breathe. */
main.files-stack {
max-width: 1800px;
padding-left: 40px;
padding-right: 40px;
}
/* Code sections paint immediately — no opacity gate, no
* wait-for-hljs fade-in. Since hljs now only touches blocks
* with an explicit `language-*` class (and ours all do) and the
* script is `defer`-loaded, the unhighlighted-to-tokenized
* flip happens within ~10ms of first paint. Hiding + fading in
* was hurting LCP for no real visual payoff. */
/* ── 5. Buttons, pills, keycaps ──────────────────────────────────── */
/* Part-nav — a thin minimal strip directly beneath the topbar.
* Shows "[home] Parts: 1 2 3 … 8" and sits at a fixed position on
* every page (index, parts, docs) so the nav never visually jumps
* when you click between pages. Lightweight chrome — compact
* pills, small type, subtle tint — so it recedes when you're
* actually reading. Active pill gets the "reversed elevation"
* treatment: white fill with a thin border while siblings stay
* transparent. */
.part-nav {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 3px;
padding: 4px 32px;
background: var(--bg-elev);
border-bottom: 1px solid var(--border);
font-size: 11px;
/* Reserve the full 38px nav height (30px action-button height
* + 4px top/bottom padding) BEFORE comments.js injects its
* download + chat buttons. Without this, the nav initially
* sizes to the 26px pill height and grows ~4px when the
* buttons land → every reload jumps the page content. */
min-height: 38px;
}
.topbar-actions {
/* Same deal — keep the actions cluster at its final height
* from first paint so its flex parent doesn't reflow when
* the buttons hydrate. */
min-height: 30px;
}
.wt-parts-label {
color: var(--muted);
font-weight: 500;
/* Zero right margin so the label sits at the same flex `gap`
* distance from "1" as "1" sits from "2". Extra left margin
* widens the separation between the home icon and "Parts:" so
* the home link reads as its own visual group. */
margin: 0 0 0 16px;
font-size: 11px;
}
.part-nav a,
.wt-home-link {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 26px;
height: 26px;
padding: 0 8px;
font-size: 12px;
font-weight: 500;
color: var(--muted);
background: transparent;
border: 1px solid transparent;
border-radius: 5px;
text-decoration: none;
transition:
background var(--transition-fast),
border-color var(--transition-fast),
color var(--transition-fast);
}
/* Hover = darker than the strip bg, with accent-color text. Reads
* as "I'm pressing into this option". */
.part-nav a:hover,
.wt-home-link:hover {
background: var(--nav-hover-bg);
border-color: var(--border-strong);
color: var(--accent-fg);
}
/* Selected (active) = lighter than the strip bg, with accent-color
* text. Reads as "you are here, lit up". Same text color as hover
* so the two states share a signal (accent-colored = interactive
* or selected); bg direction distinguishes them. Applies to the
* active part pill AND to the home link when the user is on the
* index — visual parity between "on home" and "on part N". */
.part-nav a.active,
.wt-home-link.active {
background: var(--nav-selected-bg);
border-color: var(--accent-fg);
color: var(--accent-fg);
box-shadow: var(--shadow-sm);
}
.wt-home-link {
padding: 0 6px;
min-width: 26px;
color: var(--muted);
}
.wt-home-link svg {
display: block;
width: 18px;
height: 18px;
}
.wt-topics-nav {
display: inline-flex;
align-items: center;
gap: 6px;
margin-left: 8px;
}
/* Doc pills in the unified Topics row. Same shape and tone as
* numbered part pills (one word each) so the row reads as a
* continuous sequence: "Anatomy Building … VERS | Architecture
* Builders …". Slightly tighter padding than the number pills
* since word labels carry their own width. */
.wt-topic-doc {
padding: 0 8px;
}
/* First doc pill = the one NOT preceded by another .wt-topic-doc.
* `:first-of-type` doesn't work here because every pill is an
* <a>, so :first-of-type matches the first Part (Anatomy), not
* the first doc. The `:not(:has(...))` form reads: "doc pill
* whose previous sibling isn't also a doc pill," i.e. the first
* in the sequence. */
.wt-topic-doc:not(.wt-topic-doc + .wt-topic-doc) {
margin-left: 14px;
position: relative;
}
/* Pretty vertical separator between the parts (code) and the
* docs (articles). Accent-tinted, full-height, soft gradient
* fade at top/bottom so it doesn't feel like a hard chop.
* Sits in the 8px gap flex creates between the last part pill
* and the first doc pill. */
.wt-topic-doc:not(.wt-topic-doc + .wt-topic-doc)::before {
content: '';
position: absolute;
left: -9px;
top: 2px;
bottom: 2px;
width: 1px;
background: linear-gradient(
to bottom,
transparent 0%,
color-mix(in srgb, var(--accent-fg) 50%, transparent) 20%,
color-mix(in srgb, var(--accent-fg) 50%, transparent) 80%,
transparent 100%
);
}
/* Hover callback to the home page badges: code pages (parts)
* pick up the blue used by the "CODE" kind badge; article pages
* (docs) pick up the green "ARTICLE" tint. Subtle — just the
* border and a faint fill. Same colors as .wt-badge-kind-code /
* .wt-badge-kind-article so the connection reads. Only applies
* to the non-active state; .active keeps its accent-fg framing. */
/* Code-page pills (blue) and article-page pills (green) on
* hover — mirror the .wt-badge-size tier formula exactly so
* the menu hovers read as the same color language as the
* homepage TOC size chips (where "SMALL" is green). Text
* follows the border/ink side of the chip, not the full-
* saturation hue, so the pill stays readable on either the
* warm light bg or dark navbar. */
.part-nav a:not(.wt-topic-doc):not(.active):hover {
border-color: color-mix(in srgb, #7aa5c7 30%, var(--border-strong));
background: color-mix(in srgb, #7aa5c7 14%, var(--nav-hover-bg));
color: color-mix(in srgb, #7aa5c7 85%, var(--ink));
}
.wt-topic-doc:not(.active):hover {
border-color: color-mix(in srgb, #6ec3a5 30%, var(--border-strong));
background: color-mix(in srgb, #6ec3a5 14%, var(--nav-hover-bg));
color: color-mix(in srgb, #6ec3a5 85%, var(--ink));
}
/* Generic button — used by doc pages and any future CTA surface.
* Black pill with white text, primary-CTA style like
* `bun i @pierre/diffs`. */
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
font-family: 'Geist', system-ui, sans-serif;
font-size: 14px;
font-weight: 500;
color: var(--bg);
background: var(--ink);
border: 1px solid var(--ink);
border-radius: var(--radius-md);
cursor: pointer;
text-decoration: none;
/* Enumerate properties instead of `all` — transition:all forces
* style recalc + transition setup for every changed property,
* including layout-triggering ones. Explicit list keeps the
* hover change on the compositor (transform) + paint (shadow,
* background, border, color) without style-engine churn. */
transition:
background var(--transition-fast),
border-color var(--transition-fast),
color var(--transition-fast),
box-shadow var(--transition-fast),
transform var(--transition-fast);
}
.btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-secondary {
color: var(--ink);
background: var(--bg-tint);
border-color: transparent;
}
.btn-secondary:hover {
background: var(--bg-elev);
}
/* Keycap chip — for keyboard shortcuts in tables + prose. Thin
* border, soft white fill, monospace label. */
.kbd,
kbd {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 28px;
padding: 0 8px;
font-family: var(--font-mono);
font-size: 12px;
font-weight: 500;
color: var(--ink);
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: 6px;
box-shadow:
0 1px 0 var(--border-strong),
inset 0 0 0 1px var(--bg);
line-height: 1;
white-space: nowrap;
}
/* ── 6. Tables, TOC rows, feature grid ────────────────────────────── */
/* Tables — hairline rows, faint tinted header, generous row height.
* Used for API references, keyboard shortcuts, any comparison table. */
table {
width: 100%;
border-collapse: collapse;
margin: 24px 0;
font-size: 15px;
}
thead th {
background: var(--bg-elev);
padding: 14px 16px;
text-align: left;
font-weight: 500;
color: var(--muted);
font-size: 13px;
letter-spacing: 0.02em;
border-bottom: 1px solid var(--border);
}
tbody tr {
border-bottom: 1px solid var(--border);
}
tbody tr:last-child {
border-bottom: none;
}
tbody td {
padding: 16px;
vertical-align: top;
color: var(--body);
}
tbody td:first-child {
color: var(--ink);
font-weight: 500;
}
/* TOC on the index page — each part/topic is a row with a bold
* title, a muted summary, and a count badge on the right. Replaces
* meander's default <ul> TOC with a typeset list. */
.wt-contents {
margin: 48px 0;
padding: 0;
}
.wt-contents > h3 {
margin: 0 0 20px;
font-size: 13px;
font-weight: 500;
color: var(--muted);
letter-spacing: 0.04em;
text-transform: uppercase;
}
.wt-contents-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24px;
padding: 18px 0;
border-bottom: 1px solid var(--border);
}
.wt-contents-row:last-of-type {
border-bottom: none;
}
.wt-contents-main {
flex: 1;
min-width: 0;
}
.wt-contents-title {
display: inline-block;
font-size: 18px;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--ink);
text-decoration: none;
margin-bottom: 4px;
}
.wt-contents-title:hover {
text-decoration: underline;
text-decoration-color: var(--ink);
text-underline-offset: 4px;
}
.wt-contents-summary {
margin: 0;
font-size: 15px;
color: var(--body);
line-height: 1.5;
}
/* Intro block above the TOC on the index. Establishes the npm
* coordinates (the tour title is a product name; the installable
* name differs). Copy-ready `npm install` line so a visitor can
* start using the library before reading a single part. */
.wt-intro {
max-width: 720px;
margin: 24px auto 32px;