@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
@@ -1,231 +1,182 @@
1
- /* Shared split layout — used by Feature, Hero, Step, and any rune with data-layout */
2
-
3
- [data-layout="split"],
4
- [data-layout="split-reverse"] {
1
+ /* Shared media + content layout — used by card, recipe, hero, feature, step,
2
+ * realm, faction, playlist (anything that uses the `splitLayoutAttributes`
3
+ * preset). Same vocabulary as `bento-cell`:
4
+ * • `data-media-position="top"` — media above content (default, block flow)
5
+ * • `data-media-position="bottom"` — media below content (flex column-reverse)
6
+ * • `data-media-position="start"` — media beside content on the left (grid)
7
+ * • `data-media-position="end"` — media beside content on the right (grid)
8
+ *
9
+ * Body source order is always media-first (the `splitMediaBodyFooter` helper
10
+ * splits `media --- content` zones in that order); the position dial decides
11
+ * the visual order independently. */
12
+
13
+ /* ─── Beside layouts (start / end): two-column grid ─────────────────── */
14
+
15
+ [data-media-position="start"],
16
+ [data-media-position="end"] {
5
17
  display: grid;
6
- grid-template-columns: var(--split-ratio, 1fr 1fr);
18
+ grid-template-columns: var(--media-share, 50%) 1fr;
7
19
  align-items: var(--split-valign, start);
8
- column-gap: var(--split-gap, var(--rf-spacing-lg));
9
20
  }
10
21
 
11
- [data-layout="split-reverse"] > [data-name="content"] { order: 2; }
12
- [data-layout="split-reverse"] > [data-name="media"] { order: 1; }
22
+ /* "end" flips the grid so media lands in column 2 (right) instead of column 1.
23
+ * Source order is media-first, so we re-place media via grid-column. */
24
+ [data-media-position="end"] {
25
+ grid-template-columns: 1fr var(--media-share, 50%);
26
+ }
27
+ [data-media-position="end"] > [data-section="media"],
28
+ [data-media-position="end"] > [data-name="media"] { grid-column: 2; }
29
+ [data-media-position="end"] > [data-name="content"] { grid-column: 1; }
30
+
31
+ /* `media-ratio` → media's share of the row width (shared across runes; mirrors
32
+ * the bento-cell scale at `bento.css:50-54`). When unset, columns fall back to
33
+ * the 50% default on `--media-share`. */
34
+ [data-media-ratio="1/3"] { --media-share: 33.333%; }
35
+ [data-media-ratio="2/5"] { --media-share: 40%; }
36
+ [data-media-ratio="1/2"] { --media-share: 50%; }
37
+ [data-media-ratio="3/5"] { --media-share: 60%; }
38
+ [data-media-ratio="2/3"] { --media-share: 66.667%; }
13
39
 
14
40
  /* Grid so children can use justify-self/align-self (default: stretch) */
15
- [data-layout="split"] > [data-name="media"],
16
- [data-layout="split-reverse"] > [data-name="media"],
17
- [data-layout="split"] > [data-section="media"],
18
- [data-layout="split-reverse"] > [data-section="media"] {
41
+ [data-media-position="start"] > [data-name="media"],
42
+ [data-media-position="end"] > [data-name="media"],
43
+ [data-media-position="start"] > [data-section="media"],
44
+ [data-media-position="end"] > [data-section="media"] {
19
45
  display: grid;
20
46
  }
21
47
 
22
- /* ─── 3-section grid placement (header + content + media) ─────────── */
23
- /* Only applies when a data-section="header" child exists (3-section runes).
24
- 2-section runes (e.g. Feature with display:contents on content) use the
25
- default 2-column flow from the base grid above. */
26
-
27
- /* Split: header + content in col 1, media in col 2 spanning both rows */
28
- [data-layout="split"]:has(> [data-section="header"]) > [data-section="header"] { grid-column: 1; grid-row: 1; }
29
- [data-layout="split"]:has(> [data-section="header"]) > [data-name="content"] { grid-column: 1; grid-row: 2; }
30
- [data-layout="split"]:has(> [data-section="header"]) > [data-section="media"] { grid-column: 2; grid-row: 1 / 3; }
31
-
32
- /* Split-reverse: media in col 1 spanning both rows, header + content in col 2 */
33
- [data-layout="split-reverse"]:has(> [data-section="header"]) > [data-section="header"] { grid-column: 2; grid-row: 1; }
34
- [data-layout="split-reverse"]:has(> [data-section="header"]) > [data-name="content"] { grid-column: 2; grid-row: 2; }
35
- [data-layout="split-reverse"]:has(> [data-section="header"]) > [data-section="media"] { grid-column: 1; grid-row: 1 / 3; }
36
-
37
- /* Reset margin on media zone in 3-section split layouts (header + content + media).
38
- 2-section runes (e.g. Hero) retain their per-rune media margins. */
39
- [data-layout="split"]:has(> [data-section="header"]) > [data-section="media"],
40
- [data-layout="split-reverse"]:has(> [data-section="header"]) > [data-section="media"] {
48
+ /* ─── Stacked layouts (top / bottom) ────────────────────────────────── */
49
+
50
+ /* "bottom" puts content on top, media at the bottom. Source order is media-
51
+ * first, so column-reverse flips it visually without changing the DOM. */
52
+ [data-media-position="bottom"] {
53
+ display: flex;
54
+ flex-direction: column-reverse;
55
+ }
56
+
57
+ /* ─── 3-section grid placement (header + content + media) ───────────── */
58
+ /* Applies only when a `data-section="header"` child exists. 2-section runes
59
+ * (e.g. hero, feature with the header rolled into content) fall through to
60
+ * the simple 2-column flow above. */
61
+
62
+ [data-media-position="start"]:has(> [data-section="header"]) > [data-section="header"] { grid-column: 1; grid-row: 1; }
63
+ [data-media-position="start"]:has(> [data-section="header"]) > [data-name="content"] { grid-column: 1; grid-row: 2; }
64
+ [data-media-position="start"]:has(> [data-section="header"]) > [data-section="media"] { grid-column: 2; grid-row: 1 / 3; }
65
+
66
+ [data-media-position="end"]:has(> [data-section="header"]) > [data-section="header"] { grid-column: 2; grid-row: 1; }
67
+ [data-media-position="end"]:has(> [data-section="header"]) > [data-name="content"] { grid-column: 2; grid-row: 2; }
68
+ [data-media-position="end"]:has(> [data-section="header"]) > [data-section="media"] { grid-column: 1; grid-row: 1 / 3; }
69
+
70
+ [data-media-position="start"]:has(> [data-section="header"]) > [data-section="media"],
71
+ [data-media-position="end"]:has(> [data-section="header"]) > [data-section="media"] {
41
72
  margin: 0;
42
73
  }
43
74
 
44
75
  /* ─── Media zone base styles ──────────────────────────────────────── */
45
76
 
46
- /* Clip overflow + round corners when the media zone holds media-like
47
- content: an image, or a self-contained block rune that wants the same
48
- "fills the slot" treatment (codegroup, snippet, chart, sandbox). Anything
49
- else keeps overflow visible so interactive bleed effects (preview,
50
- juxtapose) still work. Add new rune-typed media here when they should
51
- participate in the same chrome. */
52
- [data-section="media"]:where(
53
- :has(> img),
54
- :has(> [data-rune="code-group"]),
55
- :has(> [data-rune="snippet"]),
56
- :has(> [data-rune="chart"]),
57
- :has(> [data-rune="sandbox"])
58
- ) {
59
- border-radius: var(--rf-radius-lg);
77
+ /* Media-zone guest adaptation (WORK-339 / SPEC-084). Any visual guest fills the
78
+ * zone, and the zone clips, rounds, and becomes a container-query context — so
79
+ * a tall guest scales/clips instead of ballooning, and intrinsically responsive
80
+ * guests (e.g. `mockup`'s `cqi` auto-scale) resolve against the slot. Guests
81
+ * that manage their own bleed / interactive chrome (preview, juxtapose, an
82
+ * explicitly-bleeding showcase) self-declare an opt-out below so their
83
+ * displacement isn't clipped. */
84
+ [data-section="media"] {
85
+ container-type: inline-size;
60
86
  overflow: hidden;
87
+ /* Media guests use the smaller media radius tier so they never read as more
88
+ * rounded than the (larger-radius) container they sit in. */
89
+ border-radius: var(--rf-radius-media);
61
90
  }
62
- [data-section="media"] > :where(
63
- img,
64
- [data-rune="code-group"],
65
- [data-rune="snippet"],
66
- [data-rune="chart"],
67
- [data-rune="sandbox"]
68
- ) {
91
+ [data-section="media"] > * {
69
92
  display: block;
70
93
  width: 100%;
71
- height: auto;
72
- border-radius: var(--rf-radius-lg);
94
+ max-height: 100%;
95
+ border-radius: inherit;
73
96
  margin: 0;
74
97
  }
75
-
76
- /* Split layout images get subtle depth. Block runes carry their own visual
77
- weight (topbars, frames) so the shadow is image-only. */
78
- [data-layout="split"] > [data-section="media"] > img,
79
- [data-layout="split-reverse"] > [data-section="media"] > img {
80
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
98
+ [data-section="media"] > :is(img, video) {
99
+ height: 100%;
100
+ object-fit: cover;
81
101
  }
82
-
83
- /* ─── Collapse breakpoints ────────────────────────────────────────── */
84
-
85
- @media (max-width: 640px) {
86
- [data-layout^="split"][data-collapse="sm"] { grid-template-columns: 1fr; }
87
- [data-layout^="split"][data-collapse="sm"] > * { order: unset; }
102
+ [data-section="media"]:is(
103
+ :has(> [data-rune="preview"]),
104
+ :has(> [data-rune="juxtapose"]),
105
+ :has(> .rf-showcase[data-bleed]:not(.rf-showcase--in-bento-cell))
106
+ ) {
107
+ overflow: visible;
108
+ container-type: normal;
109
+ border-radius: 0;
88
110
  }
89
-
90
- @media (max-width: 768px) {
91
- [data-layout^="split"][data-collapse="md"] { grid-template-columns: 1fr; }
92
- [data-layout^="split"][data-collapse="md"] > * { order: unset; }
111
+ /* …and let those self-managing guests keep their intrinsic width/sizing: the
112
+ * generic `width: 100%` above would defeat a negative-margin breakout (e.g.
113
+ * `preview--in-feature` bleeding to the viewport edge on mobile). */
114
+ [data-section="media"]:is(
115
+ :has(> [data-rune="preview"]),
116
+ :has(> [data-rune="juxtapose"]),
117
+ :has(> .rf-showcase[data-bleed]:not(.rf-showcase--in-bento-cell))
118
+ ) > * {
119
+ width: auto;
120
+ max-height: none;
121
+ border-radius: 0;
93
122
  }
94
123
 
95
- @media (max-width: 1024px) {
96
- [data-layout^="split"][data-collapse="lg"] { grid-template-columns: 1fr; }
97
- [data-layout^="split"][data-collapse="lg"] > * { order: unset; }
124
+ /* (Figure-style media bleed used to live here — a negative-margin breakout that
125
+ * extended a top-position media banner past the container padding to leave a
126
+ * small consistent figure margin. Every consumer (card / recipe / realm /
127
+ * faction / playlist / bento-cell) moved to the thin-edge inset model — outer
128
+ * padding is a thin edge, the content zone fills the remaining inset — which
129
+ * achieves the same visual without the negative-margin hack. The rule was
130
+ * removed once it had no remaining matches; reintroduce a positive opt-in if
131
+ * a new rune ever wants figure bleed.) */
132
+
133
+ /* A code-group dropped into a media zone uses the media radius tier so its
134
+ * border and corners line up with the zone's clip instead of the larger
135
+ * container radius it gets as a standalone inset surface. */
136
+ [data-section="media"] .rf-codegroup {
137
+ border-radius: var(--rf-radius-media);
98
138
  }
99
139
 
100
- /* ─── Default mobile collapse for split layouts ───────────────────── */
101
- /* Runes without an explicit data-collapse always collapse at sm */
102
-
103
- @media (max-width: 640px) {
104
- [data-layout^="split"]:not([data-collapse]) {
105
- grid-template-columns: 1fr;
106
- }
107
- [data-layout^="split"]:not([data-collapse]) > [data-section="header"],
108
- [data-layout^="split"]:not([data-collapse]) > [data-name="content"],
109
- [data-layout^="split"]:not([data-collapse]) > [data-section="media"] {
110
- grid-column: auto;
111
- grid-row: auto;
112
- }
113
- [data-layout^="split"]:not([data-collapse]) > * {
114
- order: unset;
115
- }
116
-
117
- /* ─── Content-first collapse (data-media-position="top") ────────── */
118
- /* Media hoists above preamble as full-bleed card header */
119
- [data-media-position="top"] > [data-section="media"],
120
- [data-media-position="top"][data-layout="split"] > [data-section="media"],
121
- [data-media-position="top"][data-layout="split-reverse"] > [data-section="media"] {
122
- order: -1;
123
- grid-column: auto;
124
- grid-row: auto;
125
- width: calc(100% + 2 * var(--rune-padding, var(--rf-spacing-md)));
126
- margin: calc(-1 * var(--rune-padding, var(--rf-spacing-md)));
127
- margin-bottom: var(--rune-padding, var(--rf-spacing-md));
128
- border-radius: 0;
129
- }
130
- [data-media-position="top"] > [data-section="media"] > :where(
131
- img,
132
- [data-rune="code-group"],
133
- [data-rune="snippet"],
134
- [data-rune="chart"],
135
- [data-rune="sandbox"]
136
- ) {
137
- border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
138
- }
139
-
140
- /* Stacked layout also needs flex context for order to work */
141
- [data-media-position="top"][data-layout="stacked"] {
142
- display: flex;
143
- flex-direction: column;
144
- }
140
+ /* Beside-layout images get subtle depth. Block runes carry their own visual
141
+ * weight (topbars, frames) so the shadow is image-only. */
142
+ [data-media-position="start"] > [data-section="media"] > img,
143
+ [data-media-position="end"] > [data-section="media"] > img {
144
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
145
145
  }
146
146
 
147
- /* ─── Explicit collapse breakpoint + content-first ────────────────── */
147
+ /* ─── Collapse breakpoints ────────────────────────────────────────────
148
+ * Beside (start/end) layouts drop to a single column at the chosen breakpoint;
149
+ * the source-order (media-first) carries through, so media ends up on top of
150
+ * content after the collapse. `top` / `bottom` are already stacked and unchanged
151
+ * by collapse (their orientation is preserved). */
148
152
 
149
153
  @media (max-width: 640px) {
150
- [data-media-position="top"][data-collapse="sm"] > [data-section="media"],
151
- [data-media-position="top"][data-collapse="sm"][data-layout="split"] > [data-section="media"],
152
- [data-media-position="top"][data-collapse="sm"][data-layout="split-reverse"] > [data-section="media"] {
153
- order: -1;
154
- grid-column: auto;
155
- grid-row: auto;
156
- width: calc(100% + 2 * var(--rune-padding, var(--rf-spacing-md)));
157
- margin: calc(-1 * var(--rune-padding, var(--rf-spacing-md)));
158
- margin-bottom: var(--rune-padding, var(--rf-spacing-md));
159
- border-radius: 0;
160
- }
161
- [data-media-position="top"][data-collapse="sm"] > [data-section="media"] > :where(
162
- img,
163
- [data-rune="code-group"],
164
- [data-rune="snippet"],
165
- [data-rune="chart"],
166
- [data-rune="sandbox"]
167
- ) {
168
- border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
154
+ :is([data-media-position="start"], [data-media-position="end"])[data-collapse="sm"],
155
+ :is([data-media-position="start"], [data-media-position="end"]):not([data-collapse]) {
156
+ grid-template-columns: 1fr;
169
157
  }
170
- /* Reset grid placement on collapse for header/content */
171
- [data-media-position="top"][data-collapse="sm"] > [data-section="header"],
172
- [data-media-position="top"][data-collapse="sm"] > [data-name="content"] {
158
+ :is([data-media-position="start"], [data-media-position="end"])[data-collapse="sm"] > *,
159
+ :is([data-media-position="start"], [data-media-position="end"]):not([data-collapse]) > * {
173
160
  grid-column: auto;
174
161
  grid-row: auto;
175
162
  }
176
163
  }
177
164
 
178
165
  @media (max-width: 768px) {
179
- [data-media-position="top"][data-collapse="md"] > [data-section="media"],
180
- [data-media-position="top"][data-collapse="md"][data-layout="split"] > [data-section="media"],
181
- [data-media-position="top"][data-collapse="md"][data-layout="split-reverse"] > [data-section="media"] {
182
- order: -1;
183
- grid-column: auto;
184
- grid-row: auto;
185
- width: calc(100% + 2 * var(--rune-padding, var(--rf-spacing-md)));
186
- margin: calc(-1 * var(--rune-padding, var(--rf-spacing-md)));
187
- margin-bottom: var(--rune-padding, var(--rf-spacing-md));
188
- border-radius: 0;
189
- }
190
- [data-media-position="top"][data-collapse="md"] > [data-section="media"] > :where(
191
- img,
192
- [data-rune="code-group"],
193
- [data-rune="snippet"],
194
- [data-rune="chart"],
195
- [data-rune="sandbox"]
196
- ) {
197
- border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
166
+ :is([data-media-position="start"], [data-media-position="end"])[data-collapse="md"] {
167
+ grid-template-columns: 1fr;
198
168
  }
199
- [data-media-position="top"][data-collapse="md"] > [data-section="header"],
200
- [data-media-position="top"][data-collapse="md"] > [data-name="content"] {
169
+ :is([data-media-position="start"], [data-media-position="end"])[data-collapse="md"] > * {
201
170
  grid-column: auto;
202
171
  grid-row: auto;
203
172
  }
204
173
  }
205
174
 
206
175
  @media (max-width: 1024px) {
207
- [data-media-position="top"][data-collapse="lg"] > [data-section="media"],
208
- [data-media-position="top"][data-collapse="lg"][data-layout="split"] > [data-section="media"],
209
- [data-media-position="top"][data-collapse="lg"][data-layout="split-reverse"] > [data-section="media"] {
210
- order: -1;
211
- grid-column: auto;
212
- grid-row: auto;
213
- width: calc(100% + 2 * var(--rune-padding, var(--rf-spacing-md)));
214
- margin: calc(-1 * var(--rune-padding, var(--rf-spacing-md)));
215
- margin-bottom: var(--rune-padding, var(--rf-spacing-md));
216
- border-radius: 0;
217
- }
218
- [data-media-position="top"][data-collapse="lg"] > [data-section="media"] > :where(
219
- img,
220
- [data-rune="code-group"],
221
- [data-rune="snippet"],
222
- [data-rune="chart"],
223
- [data-rune="sandbox"]
224
- ) {
225
- border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
176
+ :is([data-media-position="start"], [data-media-position="end"])[data-collapse="lg"] {
177
+ grid-template-columns: 1fr;
226
178
  }
227
- [data-media-position="top"][data-collapse="lg"] > [data-section="header"],
228
- [data-media-position="top"][data-collapse="lg"] > [data-name="content"] {
179
+ :is([data-media-position="start"], [data-media-position="end"])[data-collapse="lg"] > * {
229
180
  grid-column: auto;
230
181
  grid-row: auto;
231
182
  }
@@ -41,5 +41,5 @@
41
41
  .rf-version-switcher__select:focus-visible {
42
42
  outline: none;
43
43
  border-color: var(--rf-color-primary);
44
- box-shadow: 0 0 0 2px var(--rf-color-primary-100, rgba(14, 165, 233, 0.15));
44
+ box-shadow: 0 0 0 2px var(--rf-color-primary-bg);
45
45
  }
@@ -58,7 +58,7 @@
58
58
  border-radius: var(--rf-radius-full, 9999px);
59
59
  border: none;
60
60
  background: var(--rf-color-primary);
61
- color: #fff;
61
+ color: var(--rf-color-on-primary);
62
62
  cursor: pointer;
63
63
  display: flex;
64
64
  align-items: center;