svelte-readme 4.1.0 → 4.3.0

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/dist/layout.css CHANGED
@@ -1,5 +1,7 @@
1
1
  .code-fence {
2
- padding: 1.5rem 0.9375rem;
2
+ padding: 1.5rem var(--sr-box-padding);
3
+ margin-right: calc(var(--sr-box-padding) * -1);
4
+ margin-left: calc(var(--sr-box-padding) * -1);
3
5
  border: 1px solid var(--sr-color-border-muted);
4
6
  border-bottom: 0;
5
7
  overflow-x: auto;
@@ -8,7 +10,7 @@
8
10
  .sr-layout {
9
11
  display: grid;
10
12
  grid-template-columns: 11.25rem minmax(0, 60rem) 11.25rem;
11
- column-gap: 2.5rem;
13
+ column-gap: 3.125rem;
12
14
  max-width: 87.5rem;
13
15
  margin: 0 auto;
14
16
  padding: 2.8125rem;
@@ -25,7 +27,7 @@ main.markdown-body {
25
27
  (mobile) — only one of the two is ever visible at a given viewport width. */
26
28
  .sr-toc {
27
29
  font-family: var(--sr-font-sans);
28
- font-size: 0.75rem;
30
+ font-size: var(--sr-text-xs);
29
31
  color: var(--sr-color-fg-muted);
30
32
  }
31
33
 
@@ -53,11 +55,48 @@ main.markdown-body {
53
55
  }
54
56
 
55
57
  .sr-toc a.sr-toc-active {
56
- color: var(--sr-color-fg);
57
- font-weight: 600;
58
+ color: var(--sr-color-link);
58
59
  text-decoration: underline;
59
60
  }
60
61
 
62
+ /* Rendered as the first child of both `.sr-toc` variants (sidebar and inline),
63
+ so it always sits directly above whichever TOC copy is visible at the current
64
+ viewport width. */
65
+ .sr-theme-toggle {
66
+ display: inline-flex;
67
+ align-items: center;
68
+ justify-content: center;
69
+ width: 1.75rem;
70
+ height: 1.75rem;
71
+ margin-bottom: 0.75rem;
72
+ padding: 0;
73
+ color: var(--sr-color-fg-muted);
74
+ cursor: pointer;
75
+ appearance: none;
76
+ background-color: var(--sr-color-canvas);
77
+ border: 1px solid var(--sr-color-border);
78
+ border-radius: 0.375rem;
79
+ }
80
+
81
+ .sr-theme-toggle:hover {
82
+ color: var(--sr-color-fg);
83
+ }
84
+
85
+ /* Same specificity trick as `.sr-copy-icon`/`.sr-copy-check` in `style.css`: one
86
+ class each, so source order (not selector specificity) decides which icon shows
87
+ before `data-sr-theme` flips it. */
88
+ .sr-theme-icon-moon {
89
+ display: none;
90
+ }
91
+
92
+ :root[data-sr-theme="dark"] .sr-theme-icon-sun {
93
+ display: none;
94
+ }
95
+
96
+ :root[data-sr-theme="dark"] .sr-theme-icon-moon {
97
+ display: block;
98
+ }
99
+
61
100
  .sr-toc-sidebar {
62
101
  grid-column: 3;
63
102
  min-width: 0;
@@ -66,7 +105,7 @@ main.markdown-body {
66
105
  top: 0;
67
106
  align-self: stretch;
68
107
  max-height: 100vh;
69
- padding: 2rem 0;
108
+ padding: 1.5rem 0 2rem;
70
109
  overflow-y: auto;
71
110
  }
72
111
 
@@ -75,7 +114,31 @@ main.markdown-body {
75
114
  }
76
115
 
77
116
  main.markdown-body p {
78
- max-width: 52ch;
117
+ max-width: 65ch;
118
+ }
119
+
120
+ /* Quotes read as set-apart material rather than the main body copy, so they get
121
+ more breathing room than a paragraph's measure — wider, but still a bounded
122
+ line length rather than stretching to the full column. */
123
+ main.markdown-body blockquote p {
124
+ max-width: 75ch;
125
+ }
126
+
127
+ main.markdown-body ol,
128
+ main.markdown-body ul {
129
+ max-width: 65ch;
130
+ }
131
+
132
+ /* A list introduced by the paragraph directly above it (e.g. "as follows:") reads
133
+ as a continuation of that sentence, not a new block — tighten the gap between
134
+ them below the uniform inter-block spacing every other pair of siblings gets
135
+ (`blockquote, details, dl, ol, p, pre, table, ul` in `style.css`). Negative
136
+ rather than a smaller positive value: adjacent margins collapse to
137
+ `positive max + negative min`, so this nets out to half of `--sr-spacing`
138
+ without having to know the preceding `p`'s own margin-bottom here. */
139
+ main.markdown-body p + ol,
140
+ main.markdown-body p + ul {
141
+ margin-top: calc(var(--sr-spacing) * -0.5);
79
142
  }
80
143
 
81
144
  /* Between the mobile breakpoint and the full desktop width, the fixed-width empty
@@ -99,6 +162,15 @@ main.markdown-body p {
99
162
  }
100
163
 
101
164
  @media (max-width: 900px) {
165
+ /* The desktop scale's upper steps (tuned for a wide text column) read as
166
+ oversized against a narrow mobile viewport — h1 in particular dwarfs the
167
+ content below it — so the top of the scale is compressed here. */
168
+ :root {
169
+ --sr-text-xl: 1.25rem;
170
+ --sr-text-2xl: 1.5rem;
171
+ --sr-text-3xl: 1.875rem;
172
+ }
173
+
102
174
  .sr-layout {
103
175
  grid-template-columns: 1fr;
104
176
  padding: 0.9375rem;
@@ -116,4 +188,25 @@ main.markdown-body p {
116
188
  display: block;
117
189
  margin-bottom: var(--sr-spacing-lg);
118
190
  }
191
+
192
+ /* The desktop bleed (negative margin equal to the box's own padding) assumes
193
+ room in the gutter outside the text column to bleed into. On mobile there's
194
+ no gutter — the layout padding above is already tight — so bleeding here would
195
+ just push these boxes past the viewport edge instead of aligning them. */
196
+ main.markdown-body .sr-table-wrapper,
197
+ main.markdown-body pre,
198
+ main.markdown-body .sr-code-block,
199
+ main.markdown-body .code-fence,
200
+ main.markdown-body details {
201
+ margin-right: 0;
202
+ margin-left: 0;
203
+ }
204
+
205
+ /* `table`'s desktop `width: fit-content` can still exceed the viewport here if a
206
+ cell's content can't wrap below some minimum width (e.g. a long unbroken
207
+ string) — cap it so the table's own horizontal scrollbar handles the overflow
208
+ instead of the page growing wider than the viewport. */
209
+ main.markdown-body table {
210
+ max-width: 100%;
211
+ }
119
212
  }
package/dist/shared.css CHANGED
@@ -1,24 +1,60 @@
1
+ /**
2
+ * Token colors as custom properties, overridden under `:root[data-sr-theme="dark"]`
3
+ * (same technique as `../styles/style.css`) so toggling the theme only recomputes
4
+ * inherited variable values instead of rematching a second, fully-duplicated set of
5
+ * dark-mode selectors — cheaper at both parse time and on every theme toggle.
6
+ *
7
+ * Shared across all grammar CSS files under `./`, so a color reused by more than one
8
+ * language (e.g. the same blue for numbers and object properties) stays a single
9
+ * source of truth here rather than being redefined per grammar.
10
+ */
11
+ :root {
12
+ --sr-hl-red: #d73a49;
13
+ --sr-hl-gray: #24292e;
14
+ --sr-hl-gray-muted: #6a737d;
15
+ --sr-hl-purple: #6f42c1;
16
+ --sr-hl-blue-dark: #032f62;
17
+ --sr-hl-blue: #005cc5;
18
+ --sr-hl-blue-mid: #0550ae;
19
+ --sr-hl-blue-deep: #0a3069;
20
+ --sr-hl-gray-dark: #24292f;
21
+ --sr-hl-green: #22863a;
22
+ }
23
+
24
+ :root[data-sr-theme="dark"] {
25
+ --sr-hl-red: #ff7b72;
26
+ --sr-hl-gray: #c9d1d9;
27
+ --sr-hl-gray-muted: #8b949e;
28
+ --sr-hl-purple: #d2a8ff;
29
+ --sr-hl-blue-dark: #a5d6ff;
30
+ --sr-hl-blue: #79c0ff;
31
+ --sr-hl-blue-mid: #79c0ff;
32
+ --sr-hl-blue-deep: #a5d6ff;
33
+ --sr-hl-gray-dark: #c9d1d9;
34
+ --sr-hl-green: #7ee787;
35
+ }
36
+
1
37
  .token.keyword {
2
- color: #d73a49;
38
+ color: var(--sr-hl-red);
3
39
  }
4
40
  .token.punctuation {
5
- color: #24292e;
41
+ color: var(--sr-hl-gray);
6
42
  }
7
43
  .token.operator {
8
- color: #d73a49;
44
+ color: var(--sr-hl-red);
9
45
  }
10
46
  .token.comment {
11
- color: #6a737d;
47
+ color: var(--sr-hl-gray-muted);
12
48
  }
13
49
  .token.function {
14
- color: #6f42c1;
50
+ color: var(--sr-hl-purple);
15
51
  }
16
52
  .token.string {
17
- color: #032f62;
53
+ color: var(--sr-hl-blue-dark);
18
54
  }
19
55
  .token.number {
20
- color: #005cc5;
56
+ color: var(--sr-hl-blue);
21
57
  }
22
58
  .token.boolean {
23
- color: #005cc5;
59
+ color: var(--sr-hl-blue);
24
60
  }
package/dist/style.css CHANGED
@@ -36,8 +36,63 @@
36
36
  --sr-color-border-table: #d4d4d8;
37
37
  --sr-color-hr: #e4e4e7;
38
38
 
39
- --sr-spacing: 1.25rem;
40
- --sr-spacing-lg: 2rem;
39
+ /* Glossy top-edge highlight on the code-fence/copy button and `summary`'s bevel
40
+ (see `.code-fence button`/`summary` below) — a fixed white reads as a subtle sheen
41
+ on the light canvas but as a harsh glowing line once the surface behind it goes
42
+ dark, so it's a variable rather than a literal `rgba(255, 255, 255, 0.6)`. */
43
+ --sr-color-bevel-highlight: rgba(255, 255, 255, 0.6);
44
+
45
+ --sr-spacing: 1.5rem;
46
+ --sr-spacing-lg: 2.5rem;
47
+ --sr-spacing-xl: 4rem;
48
+ --sr-box-padding: 1rem;
49
+
50
+ --sr-text-2xs: 0.6875rem;
51
+ --sr-text-xs: 0.75rem;
52
+ --sr-text-sm: 0.8125rem;
53
+ --sr-text-base: 0.875rem;
54
+ --sr-text-lg: 1.125rem;
55
+ --sr-text-xl: 1.375rem;
56
+ --sr-text-2xl: 1.75rem;
57
+ --sr-text-3xl: 2.5rem;
58
+
59
+ /* Declared here (rather than on `body`) so `purgeUnusedCss` — which only checks
60
+ tags/classes/attrs actually present in the server-rendered markup — has no reason
61
+ to strip it: `:root` has no selector to verify and is always kept, while `body`
62
+ never literally appears in Svelte's SSR output (which renders only what's inside
63
+ it). `<html>`'s background still paints the full viewport per the CSS spec's
64
+ canvas-background propagation, same as if this were set on `body`. */
65
+ background-color: var(--sr-color-canvas);
66
+
67
+ /* Tells the browser which palette this page actually uses, so native chrome that
68
+ isn't styled by the rules below — form controls, the scrollbar, `<select>`
69
+ dropdowns — switches to match instead of rendering a light widget on a dark page. */
70
+ color-scheme: light;
71
+ }
72
+
73
+ /* Set on `<html>` before first paint by the inline script in the HTML template
74
+ (see `THEME_INIT_SCRIPT` in `svelteReadme.ts`), so this never flashes the light
75
+ palette before switching to dark. */
76
+ :root[data-sr-theme="dark"] {
77
+ --sr-color-fg: #e4e4e7;
78
+ --sr-color-fg-muted: #a1a1aa;
79
+ --sr-color-fg-subtle: #d4d4d8;
80
+ --sr-color-link: #a5b4fc;
81
+
82
+ --sr-color-canvas: #18181b;
83
+ --sr-color-canvas-subtle: #27272a;
84
+ --sr-color-kbd-bg: #26262b;
85
+ --sr-color-code-bg: rgba(255, 255, 255, 0.08);
86
+
87
+ --sr-color-border: #3f3f46;
88
+ --sr-color-border-muted: #313136;
89
+ --sr-color-border-kbd: #52525b;
90
+ --sr-color-border-table: #52525b;
91
+ --sr-color-hr: #3f3f46;
92
+
93
+ --sr-color-bevel-highlight: rgba(255, 255, 255, 0.06);
94
+
95
+ color-scheme: dark;
41
96
  }
42
97
 
43
98
  .anchor {
@@ -66,7 +121,7 @@ main {
66
121
  line-height: 1.5;
67
122
  color: var(--sr-color-fg);
68
123
  font-family: var(--sr-font-sans);
69
- font-size: 0.9375rem;
124
+ font-size: var(--sr-text-base);
70
125
  word-wrap: break-word;
71
126
  }
72
127
 
@@ -91,11 +146,6 @@ strong {
91
146
  font-weight: bolder;
92
147
  }
93
148
 
94
- h1 {
95
- font-size: 2em;
96
- margin: 0.67em 0;
97
- }
98
-
99
149
  img {
100
150
  border-style: none;
101
151
  }
@@ -186,7 +236,7 @@ details summary {
186
236
  kbd {
187
237
  display: inline-block;
188
238
  padding: 0.1875rem 0.3125rem;
189
- font: 0.6875rem var(--sr-font-mono);
239
+ font: var(--sr-text-2xs) var(--sr-font-mono);
190
240
  line-height: 0.625rem;
191
241
  color: var(--sr-color-fg-subtle);
192
242
  vertical-align: middle;
@@ -196,55 +246,6 @@ kbd {
196
246
  box-shadow: inset 0 -1px 0 var(--sr-color-border-kbd);
197
247
  }
198
248
 
199
- h1,
200
- h2,
201
- h3,
202
- h4,
203
- h5,
204
- h6 {
205
- margin-top: 0;
206
- margin-bottom: 0;
207
- }
208
-
209
- h1 {
210
- font-size: 2rem;
211
- }
212
-
213
- h1,
214
- h2 {
215
- font-weight: 600;
216
- }
217
-
218
- h2 {
219
- font-size: 1.5rem;
220
- }
221
-
222
- h3 {
223
- font-size: 1.25rem;
224
- }
225
-
226
- h3,
227
- h4 {
228
- font-weight: 600;
229
- }
230
-
231
- h4 {
232
- font-size: 1rem;
233
- }
234
-
235
- h5 {
236
- font-size: 0.875rem;
237
- }
238
-
239
- h5,
240
- h6 {
241
- font-weight: 600;
242
- }
243
-
244
- h6 {
245
- font-size: 0.75rem;
246
- }
247
-
248
249
  p {
249
250
  margin-top: 0;
250
251
  margin-bottom: 0.625rem;
@@ -280,7 +281,6 @@ dd {
280
281
  code,
281
282
  pre {
282
283
  font-family: var(--sr-font-mono);
283
- font-size: 0.75rem;
284
284
  }
285
285
 
286
286
  pre {
@@ -377,33 +377,28 @@ h6 {
377
377
  }
378
378
 
379
379
  h1 {
380
- font-size: 2em;
381
- }
382
-
383
- h1,
384
- h2 {
385
- padding-bottom: 0.3em;
386
- border-bottom: 1px solid var(--sr-color-border-muted);
380
+ font-size: var(--sr-text-3xl);
387
381
  }
388
382
 
389
383
  h2 {
390
- font-size: 1.5em;
384
+ margin-top: var(--sr-spacing-xl);
385
+ font-size: var(--sr-text-2xl);
391
386
  }
392
387
 
393
388
  h3 {
394
- font-size: 1.25em;
389
+ font-size: var(--sr-text-xl);
395
390
  }
396
391
 
397
392
  h4 {
398
- font-size: 1em;
393
+ font-size: var(--sr-text-lg);
399
394
  }
400
395
 
401
396
  h5 {
402
- font-size: 0.875em;
397
+ font-size: var(--sr-text-sm);
403
398
  }
404
399
 
405
400
  h6 {
406
- font-size: 0.85em;
401
+ font-size: var(--sr-text-xs);
407
402
  color: var(--sr-color-fg-muted);
408
403
  }
409
404
 
@@ -413,11 +408,18 @@ ul {
413
408
  }
414
409
 
415
410
  ol ol,
416
- ol ul,
411
+ ol ul {
412
+ margin-top: 0;
413
+ margin-bottom: 0;
414
+ }
415
+
416
+ /* A numbered sub-list (above) reads as a continuation of its parent's sequence —
417
+ tight to its own numbering flow. A bulleted sub-list is a categorical grouping,
418
+ so it gets the extra bottom margin to set it apart from the next category. */
417
419
  ul ol,
418
420
  ul ul {
419
421
  margin-top: 0;
420
- margin-bottom: 0;
422
+ margin-bottom: var(--sr-spacing);
421
423
  }
422
424
 
423
425
  li {
@@ -449,10 +451,60 @@ dl dd {
449
451
  margin-bottom: var(--sr-spacing);
450
452
  }
451
453
 
454
+ /* Non-scrolling ancestor for `table`'s bleed (negative margin equal to `table`'s own
455
+ cell padding — same technique `.sr-code-block` uses for `pre`) so the scroll-shadow
456
+ gradients below can be positioned against a stationary box instead of `table`
457
+ itself, which scrolls horizontally and would carry an absolutely positioned
458
+ overlay along with it, out of view. */
459
+ .sr-table-wrapper {
460
+ position: relative;
461
+ margin-right: -0.8125rem;
462
+ margin-left: -0.8125rem;
463
+ }
464
+
465
+ .sr-table-wrapper table {
466
+ margin-right: 0;
467
+ margin-left: 0;
468
+ }
469
+
470
+ /* `pointer-events: none` so the gradient never intercepts clicks/selection on the
471
+ table content beneath it — it's a visual affordance, not an interactive layer.
472
+ Visibility is driven by `data-sr-overflow-left`/`-right` on the wrapper, toggled
473
+ from JS (`TABLE_SCROLL_SHADOW_SCRIPT` in `preprocessReadme.ts`) as the table is
474
+ scrolled, so each gradient only shows while there's still more to reveal in that
475
+ direction. */
476
+ .sr-table-wrapper::before,
477
+ .sr-table-wrapper::after {
478
+ position: absolute;
479
+ top: 0;
480
+ bottom: 0;
481
+ width: 1.5rem;
482
+ content: "";
483
+ pointer-events: none;
484
+ opacity: 0;
485
+ transition: opacity 0.15s;
486
+ }
487
+
488
+ .sr-table-wrapper::before {
489
+ left: 0;
490
+ background: linear-gradient(to right, var(--sr-color-canvas), transparent);
491
+ }
492
+
493
+ .sr-table-wrapper::after {
494
+ right: 0;
495
+ background: linear-gradient(to left, var(--sr-color-canvas), transparent);
496
+ }
497
+
498
+ .sr-table-wrapper[data-sr-overflow-left]::before,
499
+ .sr-table-wrapper[data-sr-overflow-right]::after {
500
+ opacity: 1;
501
+ }
502
+
452
503
  table {
453
504
  display: block;
454
- width: 100%;
505
+ width: fit-content;
455
506
  overflow: auto;
507
+ font-size: var(--sr-text-sm);
456
508
  }
457
509
 
458
510
  table th {
@@ -470,10 +522,6 @@ table tr {
470
522
  border-top: 1px solid var(--sr-color-border-table);
471
523
  }
472
524
 
473
- table tr:nth-child(2n) {
474
- background-color: var(--sr-color-canvas-subtle);
475
- }
476
-
477
525
  img {
478
526
  max-width: 100%;
479
527
  box-sizing: initial;
@@ -493,7 +541,6 @@ code {
493
541
  margin: 0;
494
542
  font-size: 85%;
495
543
  background-color: var(--sr-color-code-bg);
496
- border-radius: 0.1875rem;
497
544
  }
498
545
 
499
546
  pre {
@@ -521,12 +568,13 @@ pre > code {
521
568
 
522
569
  .highlight pre,
523
570
  pre {
524
- padding: var(--sr-spacing);
571
+ padding: var(--sr-box-padding);
572
+ margin-right: calc(var(--sr-box-padding) * -1);
573
+ margin-left: calc(var(--sr-box-padding) * -1);
525
574
  overflow: auto;
526
- font-size: 85%;
527
- line-height: 1.45;
575
+ font-size: var(--sr-text-xs);
576
+ line-height: 1.4;
528
577
  background-color: var(--sr-color-canvas-subtle);
529
- border-radius: 0.1875rem;
530
578
  }
531
579
 
532
580
  pre code {
@@ -547,7 +595,7 @@ p {
547
595
  min-height: 1.75rem;
548
596
  }
549
597
  pre {
550
- margin-bottom: 3rem;
598
+ margin-bottom: var(--sr-spacing-xl);
551
599
  }
552
600
 
553
601
  /* Default code-fence button styling (formerly `button.css`). */
@@ -558,7 +606,7 @@ pre {
558
606
  position: relative;
559
607
  display: inline-block;
560
608
  padding: 0.1875rem 0.625rem;
561
- font-size: 0.75rem;
609
+ font-size: var(--sr-text-xs);
562
610
  font-weight: 500;
563
611
  line-height: 1rem;
564
612
  white-space: nowrap;
@@ -576,7 +624,7 @@ pre {
576
624
  );
577
625
  box-shadow:
578
626
  0 1px 0 rgba(24, 24, 27, 0.04),
579
- inset 0 1px 0 rgba(255, 255, 255, 0.6);
627
+ inset 0 1px 0 var(--sr-color-bevel-highlight);
580
628
  transition:
581
629
  background 0.2s cubic-bezier(0.3, 0, 0.5, 1),
582
630
  box-shadow 0.15s;
@@ -594,3 +642,151 @@ pre {
594
642
  background: var(--sr-color-canvas-subtle);
595
643
  box-shadow: inset 0 1px 2px rgba(24, 24, 27, 0.08);
596
644
  }
645
+
646
+ /* Copy-to-clipboard button rendered as the last child of each code snippet's `<pre>`
647
+ (see `COPY_BUTTON_MARKUP` in `preprocessReadme.ts`) and overlaid on its corner via
648
+ `position: absolute`. Anchored against `.sr-code-block` (a non-scrolling wrapper
649
+ `preprocessReadme.ts` adds around every highlighted `<pre>`) rather than `<pre>`
650
+ itself — `<pre>` has its own `overflow: auto` for long lines, and a `right`-anchored
651
+ absolute child of a scrolling box drifts with that box's horizontal scroll.
652
+
653
+ The bleed (negative margin equal to `pre`'s own padding, same technique as the
654
+ table/details bleed above) moves from `pre` to `.sr-code-block` here, with `pre`'s
655
+ own margin canceled below — otherwise `.sr-code-block`'s box (what the button is
656
+ positioned against) would stop short of `pre`'s bled edge, leaving the button
657
+ inset from the visible corner by the full bleed amount. */
658
+
659
+ .sr-code-block {
660
+ position: relative;
661
+ margin-right: calc(var(--sr-box-padding) * -1);
662
+ margin-left: calc(var(--sr-box-padding) * -1);
663
+ }
664
+
665
+ .sr-code-block pre {
666
+ margin-right: 0;
667
+ margin-left: 0;
668
+ }
669
+
670
+ .sr-copy-button {
671
+ position: absolute;
672
+ top: 0.5rem;
673
+ right: 0.5rem;
674
+ display: flex;
675
+ align-items: center;
676
+ justify-content: center;
677
+ padding: 0.25rem;
678
+ color: var(--sr-color-fg-muted);
679
+ cursor: pointer;
680
+ appearance: none;
681
+ background-color: var(--sr-color-canvas);
682
+ border: 1px solid var(--sr-color-border);
683
+ border-radius: 0.375rem;
684
+ }
685
+
686
+ /* Hidden until hover/focus so it doesn't clutter every code block at rest — but
687
+ only on devices that actually have a hover affordance. On touch-only devices
688
+ `(hover: none)` there's no hover to reveal it, so it stays visible instead. */
689
+ @media (hover: hover) {
690
+ .sr-copy-button {
691
+ opacity: 0;
692
+ transition: opacity 0.15s;
693
+ }
694
+
695
+ .sr-code-block:hover .sr-copy-button,
696
+ .sr-copy-button:focus-visible,
697
+ .sr-copy-button.sr-copy-copied {
698
+ opacity: 1;
699
+ }
700
+ }
701
+
702
+ /* Same specificity as `.sr-copy-check` below (one class each) so source order
703
+ decides the default state — a `.sr-copy-button svg` selector here would win on
704
+ specificity regardless of order and defeat that rule's `display: none`. */
705
+ .sr-copy-icon,
706
+ .sr-copy-check {
707
+ display: block;
708
+ fill: currentColor;
709
+ }
710
+
711
+ /* `.sr-copy-copied` is toggled client-side by the copy-button script for 2s of
712
+ feedback after a successful copy — never present in server-rendered markup. */
713
+ .sr-copy-check {
714
+ display: none;
715
+ }
716
+
717
+ .sr-copy-button.sr-copy-copied .sr-copy-icon {
718
+ display: none;
719
+ }
720
+
721
+ .sr-copy-button.sr-copy-copied .sr-copy-check {
722
+ display: block;
723
+ }
724
+
725
+ /* `details`/`summary`, styled with the same clickable material as the code-fence
726
+ button above. `summary` bleeds out to the `details` border via a negative
727
+ margin (same technique as the table/pre bleed) so its background spans the
728
+ full box edge-to-edge, rather than sitting inset with the revealed content. */
729
+
730
+ details {
731
+ padding: 0 var(--sr-box-padding);
732
+ margin-right: calc(var(--sr-box-padding) * -1);
733
+ margin-left: calc(var(--sr-box-padding) * -1);
734
+ border: 1px solid var(--sr-color-border-muted);
735
+ }
736
+
737
+ /* Bottom padding only applies once there's revealed content to pad — otherwise
738
+ (closed, just the summary bar) it'd reserve empty space below the summary,
739
+ showing as a hollow gap before the box's own bottom border. */
740
+ details[open] {
741
+ padding-bottom: var(--sr-spacing);
742
+ }
743
+
744
+ summary {
745
+ padding: 0.75rem var(--sr-box-padding);
746
+ margin: 0 calc(var(--sr-box-padding) * -1);
747
+ font-weight: 500;
748
+ color: var(--sr-color-fg);
749
+ background: linear-gradient(
750
+ 180deg,
751
+ var(--sr-color-kbd-bg),
752
+ var(--sr-color-canvas-subtle)
753
+ );
754
+ box-shadow:
755
+ 0 1px 0 rgba(24, 24, 27, 0.04),
756
+ inset 0 1px 0 var(--sr-color-bevel-highlight);
757
+ transition:
758
+ background 0.2s cubic-bezier(0.3, 0, 0.5, 1),
759
+ box-shadow 0.15s;
760
+ }
761
+
762
+ summary:hover {
763
+ background: linear-gradient(
764
+ 180deg,
765
+ var(--sr-color-canvas-subtle),
766
+ var(--sr-color-border)
767
+ );
768
+ }
769
+
770
+ summary:active {
771
+ background: var(--sr-color-canvas-subtle);
772
+ box-shadow: inset 0 1px 2px rgba(24, 24, 27, 0.08);
773
+ }
774
+
775
+ details[open] > summary {
776
+ border-bottom: 1px solid var(--sr-color-border-muted);
777
+ }
778
+
779
+ summary + * {
780
+ margin-top: var(--sr-spacing);
781
+ }
782
+
783
+ /* Adjacent `details` conjoin into a single accordion: no gap and no doubled
784
+ border at the seam between them. */
785
+ details + details {
786
+ margin-top: 0;
787
+ border-top: 0;
788
+ }
789
+
790
+ details:has(+ details) {
791
+ margin-bottom: 0;
792
+ }