@refrakt-md/lumina 0.18.0 → 0.19.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.
Files changed (50) hide show
  1. package/contracts/structures.json +285 -191
  2. package/dist/config.js +5 -5
  3. package/dist/config.js.map +1 -1
  4. package/dist/presets/tideline.d.ts.map +1 -1
  5. package/dist/presets/tideline.js +0 -14
  6. package/dist/presets/tideline.js.map +1 -1
  7. package/dist/tokens.d.ts.map +1 -1
  8. package/dist/tokens.js +14 -23
  9. package/dist/tokens.js.map +1 -1
  10. package/index.css +1 -1
  11. package/package.json +4 -4
  12. package/styles/dimensions/media.css +1 -1
  13. package/styles/dimensions/sections.css +4 -1
  14. package/styles/dimensions/sequence.css +1 -1
  15. package/styles/dimensions/state.css +1 -1
  16. package/styles/dimensions/surfaces.css +15 -2
  17. package/styles/global.css +68 -0
  18. package/styles/layouts/on-this-page.css +2 -2
  19. package/styles/layouts/search.css +2 -2
  20. package/styles/layouts/split.css +134 -183
  21. package/styles/layouts/version-switcher.css +1 -1
  22. package/styles/runes/audio.css +1 -1
  23. package/styles/runes/bento.css +186 -97
  24. package/styles/runes/bond.css +1 -1
  25. package/styles/runes/budget.css +1 -1
  26. package/styles/runes/card.css +64 -9
  27. package/styles/runes/character.css +1 -1
  28. package/styles/runes/chart.css +69 -0
  29. package/styles/runes/design-context.css +7 -5
  30. package/styles/runes/event.css +1 -1
  31. package/styles/runes/faction.css +32 -6
  32. package/styles/runes/feature.css +21 -16
  33. package/styles/runes/figure.css +1 -2
  34. package/styles/runes/gallery.css +39 -8
  35. package/styles/runes/itinerary.css +2 -2
  36. package/styles/runes/lore.css +1 -1
  37. package/styles/runes/palette.css +3 -3
  38. package/styles/runes/plan-progress.css +15 -62
  39. package/styles/runes/playlist.css +32 -0
  40. package/styles/runes/plot.css +2 -2
  41. package/styles/runes/realm.css +32 -6
  42. package/styles/runes/recipe.css +38 -2
  43. package/styles/runes/sandbox.css +1 -1
  44. package/styles/runes/spacing.css +4 -4
  45. package/styles/runes/swatch.css +1 -1
  46. package/styles/runes/tint.css +7 -7
  47. package/styles/runes/typography.css +7 -7
  48. package/styles/runes/xref.css +1 -1
  49. package/tokens/base.css +10 -14
  50. package/tokens/dark.css +16 -14
@@ -60,35 +60,40 @@
60
60
  .rf-feature[data-align="right"] h2 {
61
61
  text-align: right;
62
62
  }
63
+ /* Definitions stack in a single column by default; the `--definitions-grid`
64
+ * modifier (SPEC-091 media-position variant: top/bottom) tiles them as a grid. */
63
65
  .rf-feature__definitions {
66
+ display: block;
67
+ margin: 0;
68
+ }
69
+ .rf-feature--definitions-grid .rf-feature__definitions {
64
70
  display: grid;
65
71
  grid-template-columns: repeat(auto-fit, minmax(max(240px, calc((100% - 3rem) / 3)), 1fr));
66
72
  gap: 1.5rem;
67
- margin: 0;
68
73
  }
69
- /* Split layout: override defaults when in split mode */
70
- .rf-feature[data-layout="split"] .rf-feature__content,
71
- .rf-feature[data-layout="split-reverse"] .rf-feature__content {
74
+ /* Beside layouts (media on start/end): definitions stay stacked (no grid
75
+ * modifier) inside the content track. */
76
+ .rf-feature[data-media-position="start"] .rf-feature__content,
77
+ .rf-feature[data-media-position="end"] .rf-feature__content {
72
78
  display: block;
73
79
  }
74
- .rf-feature[data-layout="split"] .rf-feature__preamble,
75
- .rf-feature[data-layout="split-reverse"] .rf-feature__preamble,
76
- .rf-feature[data-layout="split"] h2,
77
- .rf-feature[data-layout="split-reverse"] h2 {
80
+ .rf-feature[data-media-position="start"] .rf-feature__preamble,
81
+ .rf-feature[data-media-position="end"] .rf-feature__preamble,
82
+ .rf-feature[data-media-position="start"] h2,
83
+ .rf-feature[data-media-position="end"] h2 {
78
84
  text-align: left;
79
85
  }
80
- .rf-feature[data-layout="split"] .rf-feature__definitions,
81
- .rf-feature[data-layout="split-reverse"] .rf-feature__definitions {
82
- display: block;
83
- }
84
- .rf-feature[data-layout="split"] .rf-feature__feature-item,
85
- .rf-feature[data-layout="split-reverse"] .rf-feature__feature-item {
86
+ .rf-feature[data-media-position="start"] .rf-feature__feature-item,
87
+ .rf-feature[data-media-position="end"] .rf-feature__feature-item {
86
88
  background: none;
89
+ border: none;
90
+ border-radius: 0;
87
91
  padding: 0.75rem 0;
88
92
  }
89
93
  .rf-feature__feature-item {
90
94
  padding: 1.75rem;
91
- border-radius: var(--rf-radius-md);
95
+ border-radius: var(--rf-radius-container);
96
+ border: 1px solid var(--rf-color-border);
92
97
  background: var(--rf-color-surface);
93
98
  }
94
99
  .rf-feature__feature-item dt {
@@ -106,7 +111,7 @@
106
111
  width: 1.5rem;
107
112
  height: 1.5rem;
108
113
  margin-bottom: 0.5rem;
109
- color: var(--rf-color-accent);
114
+ color: var(--rf-color-primary);
110
115
  }
111
116
  .rf-feature__feature-item dd {
112
117
  margin: 0;
@@ -2,8 +2,7 @@
2
2
  .rf-figure img {
3
3
  max-width: 100%;
4
4
  height: auto;
5
- border-radius: var(--rf-radius-md);
6
- box-shadow: var(--rf-shadow-sm);
5
+ border-radius: var(--rf-radius-media);
7
6
  }
8
7
  .rf-figure figcaption {
9
8
  margin-top: 0.625rem;
@@ -1,7 +1,18 @@
1
- /* Gallery — Multi-image container with grid/carousel/masonry layout */
1
+ /* Gallery — Multi-image container with grid/carousel/masonry layout.
2
+ *
3
+ * Thin-edge tile mosaic: a gallery is media all the way down (every child is an
4
+ * image), so the outer padding is treated as a frame margin and locked to the
5
+ * same value as the inter-item gap — outer = inter = a single visual rhythm.
6
+ * Both are theme tokens (`--rf-gallery-edge` / `--rf-gallery-gap`), not author
7
+ * attributes; the `gap` attribute was removed because aesthetic spacing is the
8
+ * theme's concern. `columns` stays an author attribute — that's content density,
9
+ * not aesthetics. */
2
10
  .rf-gallery {
3
11
  position: relative;
4
12
  margin: 0;
13
+ --rf-gallery-edge: 0.5rem;
14
+ --rf-gallery-gap: 0.5rem;
15
+ padding: var(--rf-gallery-edge);
5
16
  }
6
17
 
7
18
  /* Layout modifier classes */
@@ -13,7 +24,7 @@
13
24
  .rf-gallery__items {
14
25
  display: grid;
15
26
  grid-template-columns: repeat(var(--gallery-columns, 3), 1fr);
16
- gap: var(--gallery-gap, var(--rf-spacing-md));
27
+ gap: var(--rf-gallery-gap);
17
28
  }
18
29
 
19
30
  /* Individual gallery items */
@@ -85,16 +96,36 @@
85
96
  .rf-gallery__nav:hover {
86
97
  opacity: 1;
87
98
  }
99
+ /* Inset the carousel nav buttons from the image edge by the same 0.5rem the
100
+ * gallery uses as its thin-edge frame, so the buttons overlap onto the image
101
+ * slightly rather than sitting flush against it. The gallery's outer padding
102
+ * already inserts 0.5rem between gallery border and image, so the total offset
103
+ * from the gallery border is `edge + 0.5rem` = 1rem. */
88
104
  .rf-gallery__nav--prev {
89
- left: 0.5rem;
105
+ left: calc(var(--rf-gallery-edge) + 0.5rem);
90
106
  }
91
107
  .rf-gallery__nav--next {
92
- right: 0.5rem;
108
+ right: calc(var(--rf-gallery-edge) + 0.5rem);
93
109
  }
94
110
 
95
- /* Masonry layout (CSS progressive enhancement) */
111
+ /* Masonry layout CSS multi-column flow gives true variable-height masonry in
112
+ * every browser. (`grid-template-rows: masonry` is Firefox-only behind a flag
113
+ * and silently falls back to uniform-height grid rows everywhere else, which is
114
+ * why heights used to only vary in the collapsed single-column case.) Items
115
+ * flow top-to-bottom inside each column, then move to the next column. */
96
116
  .rf-gallery[data-layout="masonry"] .rf-gallery__items {
97
- grid-template-rows: masonry;
117
+ display: block;
118
+ column-count: var(--gallery-columns, 3);
119
+ column-gap: var(--rf-gallery-gap);
120
+ }
121
+ .rf-gallery[data-layout="masonry"] .rf-gallery__item {
122
+ break-inside: avoid;
123
+ margin-bottom: var(--rf-gallery-gap);
124
+ }
125
+ /* In masonry the row height isn't pinned, so let each image take its natural
126
+ * height (grid mode keeps `height: 100%` for uniform tiles). */
127
+ .rf-gallery[data-layout="masonry"] .rf-gallery__item img {
128
+ height: auto;
98
129
  }
99
130
 
100
131
  /* Lightbox cursor hint */
@@ -157,11 +188,11 @@
157
188
  right: 1rem;
158
189
  }
159
190
 
160
- /* Responsive: reset custom columns/gap and collapse on small screens */
191
+ /* Responsive: collapse columns on small screens. (Gap is theme-locked at all
192
+ * widths — no override needed.) */
161
193
  @media (max-width: 768px) {
162
194
  .rf-gallery {
163
195
  --gallery-columns: 2 !important;
164
- --gallery-gap: var(--rf-spacing-md) !important;
165
196
  }
166
197
  .rf-gallery[data-layout="carousel"] .rf-gallery__item {
167
198
  flex-basis: 80%;
@@ -116,8 +116,8 @@
116
116
  box-shadow: 0 0 0 2px var(--rf-color-success);
117
117
  }
118
118
  .rf-itinerary-stop--activity::before {
119
- background: var(--rf-color-accent);
120
- box-shadow: 0 0 0 2px var(--rf-color-accent);
119
+ background: var(--rf-color-primary);
120
+ box-shadow: 0 0 0 2px var(--rf-color-primary);
121
121
  }
122
122
  .rf-itinerary-stop--rest::before {
123
123
  background: var(--rf-color-muted);
@@ -19,7 +19,7 @@
19
19
  font-size: 1.5rem;
20
20
  font-weight: 700;
21
21
  margin-bottom: 0.5rem;
22
- color: var(--rf-color-heading);
22
+ color: var(--rf-color-text);
23
23
  }
24
24
 
25
25
  /* Spoiler mode */
@@ -15,7 +15,7 @@
15
15
  font-weight: 600;
16
16
  text-transform: uppercase;
17
17
  letter-spacing: 0.05em;
18
- color: var(--rf-color-text-muted, #6b7280);
18
+ color: var(--rf-color-muted);
19
19
  margin-bottom: 0.75rem;
20
20
  }
21
21
  .rf-palette__grid {
@@ -49,12 +49,12 @@
49
49
  .rf-palette__swatch-value {
50
50
  font-family: var(--rf-font-mono, monospace);
51
51
  font-size: 0.75rem;
52
- color: var(--rf-color-text-muted, #6b7280);
52
+ color: var(--rf-color-muted);
53
53
  }
54
54
  .rf-palette__swatch-contrast {
55
55
  font-family: var(--rf-font-mono, monospace);
56
56
  font-size: 0.7rem;
57
- color: var(--rf-color-text-muted, #6b7280);
57
+ color: var(--rf-color-muted);
58
58
  }
59
59
  .rf-palette__swatch-a11y {
60
60
  display: flex;
@@ -1,69 +1,22 @@
1
- /* Plan Progress */
1
+ /* plan-progress (SPEC-076 / WORK-296) — a stack of per-type blocks, each a type
2
+ * heading + an `aggregate` composition (progress bar + status badges). The bar
3
+ * and badge-row chrome come from the `progress` / `aggregate` / `badge` runes;
4
+ * this file only lays out the per-type grouping. */
2
5
  .rf-plan-progress {
3
- background: var(--rf-color-surface);
4
- border: 1px solid var(--rf-color-border);
5
- border-radius: var(--rf-radius-md);
6
- padding: 0.75rem 1.25rem;
7
- }
8
- .rf-plan-progress__row {
9
6
  display: flex;
10
- align-items: baseline;
11
- gap: 0.5rem;
12
- padding-block: 0.25rem;
13
- }
14
- .rf-plan-progress__row + .rf-plan-progress__row {
15
- border-block-start: 1px solid var(--rf-color-border);
16
- }
17
- .rf-plan-progress__label {
18
- font-weight: 600;
19
- font-size: 0.8125rem;
20
- color: var(--rf-color-text);
21
- min-width: 8rem;
7
+ flex-direction: column;
8
+ gap: var(--rf-spacing-lg, 2rem);
22
9
  }
23
- .rf-plan-progress__counts {
10
+ .rf-plan-progress__group {
24
11
  display: flex;
25
- flex-wrap: wrap;
26
- gap: 0.25rem 0.5rem;
27
- }
28
- .rf-plan-progress__count {
29
- font-size: 0.75rem;
30
- padding: 0.125rem 0.5rem;
31
- border-radius: var(--rf-radius-sm);
32
- white-space: nowrap;
12
+ flex-direction: column;
13
+ gap: var(--rf-spacing-sm, 0.75rem);
33
14
  }
34
-
35
- /* Status-colored counts */
36
- .rf-plan-progress__count[data-status="done"],
37
- .rf-plan-progress__count[data-status="fixed"],
38
- .rf-plan-progress__count[data-status="accepted"],
39
- .rf-plan-progress__count[data-status="active"],
40
- .rf-plan-progress__count[data-status="complete"] {
41
- color: var(--rf-color-success);
42
- background: var(--rf-color-success-bg);
43
- }
44
- .rf-plan-progress__count[data-status="in-progress"],
45
- .rf-plan-progress__count[data-status="confirmed"],
46
- .rf-plan-progress__count[data-status="review"] {
47
- color: var(--rf-color-warning);
48
- background: var(--rf-color-warning-bg);
49
- }
50
- .rf-plan-progress__count[data-status="ready"],
51
- .rf-plan-progress__count[data-status="reported"],
52
- .rf-plan-progress__count[data-status="proposed"],
53
- .rf-plan-progress__count[data-status="planning"] {
54
- color: var(--rf-color-info);
55
- background: var(--rf-color-info-bg);
56
- }
57
- .rf-plan-progress__count[data-status="blocked"] {
58
- color: var(--rf-color-danger);
59
- background: var(--rf-color-danger-bg);
60
- }
61
- .rf-plan-progress__count[data-status="draft"],
62
- .rf-plan-progress__count[data-status="pending"],
63
- .rf-plan-progress__count[data-status="superseded"],
64
- .rf-plan-progress__count[data-status="deprecated"],
65
- .rf-plan-progress__count[data-status="wontfix"],
66
- .rf-plan-progress__count[data-status="duplicate"] {
15
+ .rf-plan-progress__heading {
16
+ margin: 0;
17
+ font-size: 0.8125rem;
18
+ font-weight: 600;
19
+ text-transform: uppercase;
20
+ letter-spacing: 0.05em;
67
21
  color: var(--rf-color-muted);
68
- background: var(--rf-color-surface-active);
69
22
  }
@@ -1,5 +1,37 @@
1
1
  /* Playlist */
2
2
 
3
+ /* Thin-edge inset model (matches `.rf-card` / `.rf-recipe`): card outer padding
4
+ * is a thin edge, the content column adds the remaining inset back so text sits
5
+ * at the full `--rune-padding` without negative-margin bleed. Like recipe, no
6
+ * `align-items: stretch` — the cover is a square album image (aspect-ratio: 1
7
+ * on `__media img` below), and stretching the cell would just produce empty
8
+ * card padding around it. */
9
+ .rf-playlist {
10
+ --rf-playlist-edge: 0.5rem;
11
+ --rf-playlist-media-gap: var(--rf-spacing-lg);
12
+ --rf-playlist-split-gap: var(--rf-spacing-sm);
13
+ padding: var(--rf-playlist-edge);
14
+ }
15
+
16
+ .rf-playlist__content {
17
+ padding: max(0px, calc(var(--rune-padding, var(--rf-spacing-md)) - var(--rf-playlist-edge)));
18
+ }
19
+
20
+ /* Direct grid properties (column-gap / row-gap) win via selector specificity
21
+ * over the shared media-position layout vars. */
22
+ .rf-playlist[data-media-position="start"],
23
+ .rf-playlist[data-media-position="end"] {
24
+ column-gap: var(--rf-playlist-split-gap);
25
+ row-gap: var(--rf-playlist-media-gap);
26
+ }
27
+
28
+ .rf-playlist[data-media-position="top"] > [data-section="media"] {
29
+ margin-bottom: var(--rf-playlist-media-gap);
30
+ }
31
+ .rf-playlist[data-media-position="bottom"] > [data-section="media"] {
32
+ margin-top: var(--rf-playlist-media-gap);
33
+ }
34
+
3
35
  /* Inner header (headline + description) inside content */
4
36
  .rf-playlist__content > .rf-playlist__header {
5
37
  flex-direction: column;
@@ -8,7 +8,7 @@
8
8
  font-size: 1.5rem;
9
9
  font-weight: 700;
10
10
  margin-bottom: 0.5rem;
11
- color: var(--rf-color-heading);
11
+ color: var(--rf-color-text);
12
12
  }
13
13
 
14
14
  /* Description */
@@ -48,7 +48,7 @@
48
48
  .rf-beat > span[data-field="label"] {
49
49
  font-weight: 600;
50
50
  font-size: 1rem;
51
- color: var(--rf-color-heading);
51
+ color: var(--rf-color-text);
52
52
  }
53
53
  .rf-beat__body {
54
54
  margin-top: 0.25rem;
@@ -4,15 +4,41 @@
4
4
  * "preamble"), and dimensions/metadata.css ([data-zone-layout]). This file
5
5
  * only carries realm-specific chrome. */
6
6
 
7
- /* Stacked layout split / split-reverse are handled by shared split.css */
8
- .rf-realm[data-layout="stacked"] {
7
+ /* Thin-edge inset model (matches `.rf-card` / `.rf-recipe`): card outer padding
8
+ * is a thin edge, the content column adds the remaining inset back so text sits
9
+ * at the full `--rune-padding` without negative-margin bleed. Like recipe, no
10
+ * `--split-valign: stretch` — realm content can be long (multiple sections), so
11
+ * we let the scene keep its natural aspect ratio rather than cover-cropping. */
12
+ .rf-realm {
13
+ --rf-realm-edge: 0.5rem;
14
+ --rf-realm-media-gap: var(--rf-spacing-lg);
15
+ --rf-realm-split-gap: var(--rf-spacing-sm);
16
+ padding: var(--rf-realm-edge);
17
+ }
18
+
19
+ .rf-realm__content {
20
+ padding: max(0px, calc(var(--rune-padding, var(--rf-spacing-md)) - var(--rf-realm-edge)));
21
+ }
22
+
23
+ /* Direct grid properties (column-gap / row-gap) win via selector specificity
24
+ * over the shared media-position layout vars. */
25
+ .rf-realm[data-media-position="start"],
26
+ .rf-realm[data-media-position="end"] {
27
+ column-gap: var(--rf-realm-split-gap);
28
+ row-gap: var(--rf-realm-media-gap);
29
+ }
30
+
31
+ /* Stacked layouts — beside layouts handled by shared split.css */
32
+ .rf-realm[data-media-position="top"] {
9
33
  display: flex;
10
34
  flex-direction: column;
11
35
  }
12
36
 
13
- /* Scene image — base styles deferred to shared [data-section="media"] in split.css */
14
- .rf-realm__scene {
15
- margin-bottom: var(--rf-spacing-md);
37
+ .rf-realm[data-media-position="top"] > .rf-realm__scene {
38
+ margin-bottom: var(--rf-realm-media-gap);
39
+ }
40
+ .rf-realm[data-media-position="bottom"] > .rf-realm__scene {
41
+ margin-top: var(--rf-realm-media-gap);
16
42
  }
17
43
 
18
44
  /* Content */
@@ -37,7 +63,7 @@
37
63
  font-weight: 700;
38
64
  line-height: 1.3;
39
65
  margin-bottom: 0.5rem;
40
- color: var(--rf-color-heading);
66
+ color: var(--rf-color-text);
41
67
  }
42
68
  .rf-realm-section__body ul,
43
69
  .rf-realm-section__body ol {
@@ -1,5 +1,39 @@
1
1
  /* Recipe — editorial cookbook aesthetic */
2
2
 
3
+ /* Thin-edge inset model (matches `.rf-card`): card outer padding is a thin edge,
4
+ * the content column adds the remaining inset back so text sits at the full
5
+ * `--rune-padding` without negative-margin bleed. Unlike `card`, the split
6
+ * layout keeps `align-items: start` (no `--split-valign: stretch`) — recipe
7
+ * content can be very tall (ingredients + many steps + tips), so stretching the
8
+ * media to match would crop the image hard. The image keeps its natural ratio
9
+ * and any leftover height in the media column sits as quiet card padding. */
10
+ .rf-recipe {
11
+ --rf-recipe-edge: 0.5rem;
12
+ --rf-recipe-media-gap: var(--rf-spacing-lg);
13
+ --rf-recipe-split-gap: var(--rf-spacing-sm);
14
+ padding: var(--rf-recipe-edge);
15
+ }
16
+
17
+ .rf-recipe__content {
18
+ padding: max(0px, calc(var(--rune-padding, var(--rf-spacing-md)) - var(--rf-recipe-edge)));
19
+ }
20
+
21
+ /* Direct grid properties (column-gap / row-gap) rather than overriding shared
22
+ * media-position layout vars — the per-rune rules win via selector specificity
23
+ * and stay in our control regardless of inline styles. */
24
+ .rf-recipe[data-media-position="start"],
25
+ .rf-recipe[data-media-position="end"] {
26
+ column-gap: var(--rf-recipe-split-gap);
27
+ row-gap: var(--rf-recipe-media-gap);
28
+ }
29
+
30
+ .rf-recipe[data-media-position="top"] > [data-section="media"] {
31
+ margin-bottom: var(--rf-recipe-media-gap);
32
+ }
33
+ .rf-recipe[data-media-position="bottom"] > [data-section="media"] {
34
+ margin-top: var(--rf-recipe-media-gap);
35
+ }
36
+
3
37
  .rf-recipe .rf-recipe__eyebrow {
4
38
  font-size: 0.8rem;
5
39
  font-weight: 600;
@@ -100,9 +134,11 @@
100
134
  margin: 0;
101
135
  }
102
136
 
103
- /* Media zone — domain-specific overrides (base styles in split.css) */
137
+ /* Media zone — base sizing/clip come from the shared [data-section="media"]
138
+ * rule in split.css. The image uses the media radius tier so it reads as a
139
+ * guest nested inside the (larger-radius) recipe card. */
104
140
  .rf-recipe__media img {
105
- border-radius: var(--rf-radius-lg);
141
+ border-radius: var(--rf-radius-media);
106
142
  }
107
143
 
108
144
  /* Metadata — prep / cook / serves / difficulty are short, symmetric facts,
@@ -33,7 +33,7 @@
33
33
  font-size: 0.8125rem;
34
34
  line-height: 1.25rem;
35
35
  font-weight: 600;
36
- color: var(--rf-color-warning-fg, #78350f);
36
+ color: var(--rf-color-warning);
37
37
  background: var(--rf-color-warning-bg, #fef3c7);
38
38
  border-bottom: 1px solid var(--rf-color-warning, #b45309);
39
39
  }
@@ -15,7 +15,7 @@
15
15
  font-weight: 600;
16
16
  text-transform: uppercase;
17
17
  letter-spacing: 0.05em;
18
- color: var(--rf-color-text-muted, #6b7280);
18
+ color: var(--rf-color-muted);
19
19
  margin-bottom: 0.75rem;
20
20
  }
21
21
 
@@ -32,7 +32,7 @@
32
32
  }
33
33
  .rf-spacing__scale-bar {
34
34
  height: 1.25rem;
35
- background: var(--rf-color-primary, #0ea5e9);
35
+ background: var(--rf-color-primary);
36
36
  border-radius: var(--rf-radius-sm, 4px);
37
37
  opacity: 0.8;
38
38
  min-width: 4px;
@@ -45,7 +45,7 @@
45
45
  flex-shrink: 0;
46
46
  }
47
47
  .rf-spacing__scale-multiplier {
48
- color: var(--rf-color-text-muted, #6b7280);
48
+ color: var(--rf-color-muted);
49
49
  margin-left: 0.25rem;
50
50
  }
51
51
 
@@ -75,7 +75,7 @@
75
75
  .rf-spacing__radius-value {
76
76
  font-size: 0.7rem;
77
77
  font-family: var(--rf-font-mono, monospace);
78
- color: var(--rf-color-text-muted, #6b7280);
78
+ color: var(--rf-color-muted);
79
79
  }
80
80
 
81
81
  /* Shadows — sample cards */
@@ -21,7 +21,7 @@
21
21
  font-weight: 500;
22
22
  }
23
23
  .rf-swatch__value {
24
- color: var(--rf-color-text-muted, #6b7280);
24
+ color: var(--rf-color-muted);
25
25
  font-size: 0.75em;
26
26
  font-family: var(--rf-font-mono, monospace);
27
27
  vertical-align: middle;
@@ -56,11 +56,11 @@
56
56
  background-color: var(--rf-color-bg);
57
57
 
58
58
  --cs-bg: #1a1a17;
59
- --cs-surface: #211f1c;
59
+ --cs-surface: #1f1f1c;
60
60
  --cs-text: #f6f4ef;
61
61
  --cs-muted: #94908a;
62
62
  --cs-accent: #f6f4ef;
63
- --cs-border: #2a2825;
63
+ --cs-border: #282825;
64
64
 
65
65
  --rf-color-bg: var(--cs-bg);
66
66
  --rf-color-surface: var(--cs-surface);
@@ -70,9 +70,9 @@
70
70
  --rf-color-border: var(--cs-border);
71
71
 
72
72
  --rf-color-primary-hover: #d4cfc5;
73
- --rf-color-surface-hover: #2a2825;
74
- --rf-color-surface-active: #353330;
75
- --rf-color-surface-raised: #292723;
73
+ --rf-color-surface-hover: #282825;
74
+ --rf-color-surface-active: #333330;
75
+ --rf-color-surface-raised: #272723;
76
76
 
77
77
  --rf-color-info: #9bb4c7;
78
78
  --rf-color-info-bg: #1f2530;
@@ -87,7 +87,7 @@
87
87
  --rf-color-success-bg: #1a2a1f;
88
88
  --rf-color-success-border: #2a4a35;
89
89
 
90
- --rf-color-code-bg: #222220;
90
+ --rf-color-code-bg: #1c1c19;
91
91
  --rf-color-code-text: #f6f4ef;
92
92
  --rf-color-code-inline-bg: #2b2b29;
93
93
  --rf-color-inline-code-bg: #2b2b29;
@@ -101,7 +101,7 @@
101
101
  --rf-syntax-variable: #f6f4ef;
102
102
 
103
103
  --rf-syntax-foreground: #f6f4ef;
104
- --rf-syntax-background: #222220;
104
+ --rf-syntax-background: #1c1c19;
105
105
  --rf-syntax-token-keyword: #7eb6bc;
106
106
  --rf-syntax-token-string: #c79a9a;
107
107
  --rf-syntax-token-string-expression: #c79a9a;
@@ -11,7 +11,7 @@
11
11
  }
12
12
  .rf-typography__specimen {
13
13
  border-radius: var(--rf-radius-lg, 12px);
14
- padding: 1.5rem;
14
+ padding: 0;
15
15
  }
16
16
  .rf-typography__specimen-header {
17
17
  display: flex;
@@ -26,14 +26,14 @@
26
26
  font-weight: 600;
27
27
  text-transform: uppercase;
28
28
  letter-spacing: 0.05em;
29
- color: var(--rf-color-primary, #0ea5e9);
30
- background: var(--rf-color-primary-bg, rgba(14, 165, 233, 0.1));
29
+ color: var(--rf-color-muted);
30
+ background: var(--rf-color-surface-raised);
31
31
  padding: 0.125rem 0.5rem;
32
32
  border-radius: var(--rf-radius-sm, 4px);
33
33
  }
34
34
  .rf-typography__specimen-family {
35
35
  font-size: 0.9rem;
36
- color: var(--rf-color-text-muted, #6b7280);
36
+ color: var(--rf-color-muted);
37
37
  }
38
38
  .rf-typography__sizes {
39
39
  display: flex;
@@ -54,7 +54,7 @@
54
54
  flex-shrink: 0;
55
55
  font-size: 0.7rem;
56
56
  font-family: var(--rf-font-mono, monospace);
57
- color: var(--rf-color-text-muted, #6b7280);
57
+ color: var(--rf-color-muted);
58
58
  min-width: 3rem;
59
59
  text-align: right;
60
60
  }
@@ -73,7 +73,7 @@
73
73
  .rf-typography__weight-label {
74
74
  font-size: 0.7rem;
75
75
  font-family: var(--rf-font-mono, monospace);
76
- color: var(--rf-color-text-muted, #6b7280);
76
+ color: var(--rf-color-muted);
77
77
  }
78
78
  .rf-typography__charset {
79
79
  margin-top: 1rem;
@@ -83,7 +83,7 @@
83
83
  line-height: 1.8;
84
84
  letter-spacing: 0.05em;
85
85
  word-break: break-all;
86
- color: var(--rf-color-text-muted, #6b7280);
86
+ color: var(--rf-color-muted);
87
87
  }
88
88
 
89
89
  /* Context-aware: no outer border when inside design-context (parent already has one) */
@@ -23,6 +23,6 @@
23
23
  /* Unresolved reference */
24
24
  .rf-xref--unresolved {
25
25
  text-decoration: underline dashed;
26
- color: var(--rf-color-text-muted);
26
+ color: var(--rf-color-muted);
27
27
  cursor: help;
28
28
  }
package/tokens/base.css CHANGED
@@ -11,19 +11,6 @@
11
11
  --rf-font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
12
12
  --rf-font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, 'Cascadia Code', monospace;
13
13
 
14
- /* Primary scale — warm-neutral axis, near-bg → near-text */
15
- --rf-color-primary-50: #fbfaf7;
16
- --rf-color-primary-100: #f5f4f1;
17
- --rf-color-primary-200: #ecebe8;
18
- --rf-color-primary-300: #e2e0dd;
19
- --rf-color-primary-400: #bfbab2;
20
- --rf-color-primary-500: #94908a;
21
- --rf-color-primary-600: #76716a;
22
- --rf-color-primary-700: #5a564f;
23
- --rf-color-primary-800: #3a342d;
24
- --rf-color-primary-900: #2a2622;
25
- --rf-color-primary-950: #1c1a17;
26
-
27
14
  /* Core palette */
28
15
  --rf-color-text: #1c1a17;
29
16
  --rf-color-muted: #6b6661;
@@ -31,6 +18,9 @@
31
18
  --rf-color-bg: #f5f4f1;
32
19
  --rf-color-primary: #1c1a17;
33
20
  --rf-color-primary-hover: #3a342d;
21
+ /* Derived subtle primary wash — tracks `primary` in both modes. */
22
+ --rf-color-primary-bg: color-mix(in oklch, var(--rf-color-primary) 10%, transparent);
23
+ --rf-color-on-primary: #ffffff;
34
24
 
35
25
  /* Surfaces */
36
26
  --rf-color-surface: #fbfaf7;
@@ -58,7 +48,13 @@
58
48
  * status-coloured (which would compete with diff's add/remove channel
59
49
  * and the semantic palette above). `*-rail` is the optional left-edge
60
50
  * border colour; falls back to the primary accent via the consuming
61
- * CSS for themes that don't override it. */
51
+ * CSS for themes that don't override it.
52
+ *
53
+ * WORK-341 — these three (and `--rf-color-primary-bg` above) are
54
+ * **intentionally shared**: each derives from a mode-aware token
55
+ * (`text` / `primary` / `muted`), so they track dark mode automatically
56
+ * and deliberately have no dark.css override (a fixed dark value would
57
+ * break the derivation). */
62
58
  --rf-color-line-highlight: color-mix(in srgb, var(--rf-color-text) 6%, transparent);
63
59
  --rf-color-line-highlight-rail: var(--rf-color-primary, var(--rf-color-text));
64
60
  /* Gutter colour for `pre[data-linenumbers]` line counter. Defers to