@refrakt-md/lumina 0.18.0 → 0.20.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/base.css +4 -0
- package/contracts/structures.json +509 -223
- package/dist/config.js +5 -5
- package/dist/config.js.map +1 -1
- package/dist/presets/tideline.d.ts.map +1 -1
- package/dist/presets/tideline.js +0 -14
- package/dist/presets/tideline.js.map +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +20 -23
- package/dist/tokens.js.map +1 -1
- package/index.css +5 -1
- package/package.json +4 -4
- package/styles/base/attributes.css +9 -0
- package/styles/dimensions/cover.css +140 -0
- package/styles/dimensions/frame.css +54 -0
- package/styles/dimensions/guest-posture.css +27 -0
- package/styles/dimensions/media.css +1 -1
- package/styles/dimensions/sections.css +4 -1
- package/styles/dimensions/sequence.css +1 -1
- package/styles/dimensions/state.css +1 -1
- package/styles/dimensions/substrate.css +101 -0
- package/styles/dimensions/surfaces.css +35 -2
- package/styles/global.css +68 -0
- package/styles/layouts/on-this-page.css +2 -2
- package/styles/layouts/search.css +2 -2
- package/styles/layouts/split.css +134 -183
- package/styles/layouts/version-switcher.css +1 -1
- package/styles/runes/audio.css +1 -1
- package/styles/runes/bento.css +193 -99
- package/styles/runes/bg.css +24 -0
- package/styles/runes/bond.css +1 -1
- package/styles/runes/budget.css +1 -1
- package/styles/runes/card.css +77 -9
- package/styles/runes/character.css +1 -1
- package/styles/runes/chart.css +69 -0
- package/styles/runes/design-context.css +7 -5
- package/styles/runes/event.css +1 -1
- package/styles/runes/faction.css +32 -6
- package/styles/runes/feature.css +21 -16
- package/styles/runes/figure.css +1 -2
- package/styles/runes/gallery.css +39 -8
- package/styles/runes/itinerary.css +2 -2
- package/styles/runes/lore.css +1 -1
- package/styles/runes/palette.css +3 -3
- package/styles/runes/plan-progress.css +15 -62
- package/styles/runes/playlist.css +32 -0
- package/styles/runes/plot.css +2 -2
- package/styles/runes/realm.css +32 -6
- package/styles/runes/recipe.css +38 -2
- package/styles/runes/sandbox.css +1 -1
- package/styles/runes/showcase.css +7 -63
- package/styles/runes/spacing.css +4 -4
- package/styles/runes/swatch.css +1 -1
- package/styles/runes/tint.css +7 -7
- package/styles/runes/typography.css +7 -7
- package/styles/runes/xref.css +1 -1
- package/tokens/base.css +14 -14
- package/tokens/dark.css +18 -14
package/styles/runes/bento.css
CHANGED
|
@@ -1,126 +1,220 @@
|
|
|
1
|
-
/* Bento */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
text-transform: uppercase;
|
|
13
|
-
color: var(--rf-color-primary);
|
|
14
|
-
margin: 0 0 0.5rem;
|
|
15
|
-
}
|
|
16
|
-
.rf-bento__eyebrow:has(a) {
|
|
17
|
-
display: inline-block;
|
|
18
|
-
position: relative;
|
|
19
|
-
padding: 0.25rem 0.875rem;
|
|
20
|
-
border: 1px solid var(--rf-color-border);
|
|
21
|
-
border-radius: var(--rf-radius-full);
|
|
22
|
-
color: var(--rf-color-text);
|
|
23
|
-
font-weight: 400;
|
|
24
|
-
text-transform: none;
|
|
25
|
-
letter-spacing: 0;
|
|
26
|
-
transition: border-color 150ms ease;
|
|
27
|
-
}
|
|
28
|
-
.rf-bento__eyebrow:has(a):hover { border-color: var(--rf-color-muted); }
|
|
29
|
-
.rf-bento__eyebrow:has(a) a { color: var(--rf-color-primary); font-weight: 600; text-decoration: none; }
|
|
30
|
-
.rf-bento__eyebrow:has(a) a::before { content: ''; position: absolute; inset: 0; border-radius: inherit; }
|
|
31
|
-
.rf-bento__headline {
|
|
32
|
-
margin-top: 0;
|
|
33
|
-
}
|
|
34
|
-
.rf-bento__blurb {
|
|
35
|
-
color: var(--rf-color-muted);
|
|
36
|
-
margin-bottom: 0;
|
|
37
|
-
}
|
|
38
|
-
.rf-bento__image {
|
|
39
|
-
margin-bottom: 1rem;
|
|
40
|
-
}
|
|
1
|
+
/* Bento — a grid of cells, each a card-in-a-grid-track (SPEC-085). */
|
|
2
|
+
|
|
3
|
+
/* The grid responds to its OWN width, not the viewport, so it reduces columns and
|
|
4
|
+
* stacks correctly inside any container — a doc preview, a sidebar, a narrow page
|
|
5
|
+
* track — not just at viewport breakpoints. The progressive-reduction and collapse
|
|
6
|
+
* rules below are `@container rf-bento` queries against this. */
|
|
7
|
+
.rf-bento {
|
|
8
|
+
container-type: inline-size;
|
|
9
|
+
container-name: rf-bento;
|
|
10
|
+
}
|
|
11
|
+
|
|
41
12
|
.rf-bento__grid {
|
|
13
|
+
/* Effective column count: the author's `--bento-columns` (default 6),
|
|
14
|
+
* progressively reduced at breakpoints below. A CSS variable (not the inline
|
|
15
|
+
* `--bento-columns`) so the media queries can override it without !important. */
|
|
16
|
+
--bento-cols-effective: var(--bento-columns, 6);
|
|
42
17
|
display: grid;
|
|
43
|
-
grid-template-columns: repeat(var(--bento-
|
|
18
|
+
grid-template-columns: repeat(var(--bento-cols-effective), 1fr);
|
|
19
|
+
/* Uniform fixed row tracks in grid mode — never tied to column width (which
|
|
20
|
+
* would explode vertically on collapse). Makes row spans meaningful and bounds
|
|
21
|
+
* tall guests instead of letting them balloon a row. When the grid collapses to
|
|
22
|
+
* a single column it becomes a STACK, and `--bento-row-track` is flipped to
|
|
23
|
+
* `auto` (below) so cells size to their content and text is never clipped.
|
|
24
|
+
* `--bento-row-height` is the author's `row-height` preset (below); it falls
|
|
25
|
+
* back to the theme default `--rf-bento-row-height`. */
|
|
26
|
+
grid-auto-rows: var(--bento-row-track, var(--bento-row-height, var(--rf-bento-row-height, 24rem)));
|
|
44
27
|
gap: var(--bento-gap, 1rem);
|
|
45
28
|
}
|
|
29
|
+
|
|
30
|
+
/* Author row-height presets — the uniform grid row track height (grid mode). */
|
|
31
|
+
.rf-bento[data-row-height="sm"] { --bento-row-height: 16rem; }
|
|
32
|
+
.rf-bento[data-row-height="md"] { --bento-row-height: 24rem; }
|
|
33
|
+
.rf-bento[data-row-height="lg"] { --bento-row-height: 32rem; }
|
|
34
|
+
.rf-bento[data-row-height="xl"] { --bento-row-height: 40rem; }
|
|
35
|
+
|
|
36
|
+
/* content-height / media-ratio — perpendicular text-zone knobs, each settable as
|
|
37
|
+
* a GRID default (on .rf-bento, inherited by every cell) or a per-CELL override
|
|
38
|
+
* (on .rf-bento-cell, shadowing the inherited value). A cell is either a column
|
|
39
|
+
* cell or a beside cell, so the two never collide:
|
|
40
|
+
* • column cells (top/bottom/no-media): `content-height` pins the TEXT height;
|
|
41
|
+
* the media zone (flex:1) absorbs the rest of the row track.
|
|
42
|
+
* • beside cells (start/end): `media-ratio` pins the MEDIA width; the content
|
|
43
|
+
* (flex:1) absorbs the rest of the cell width.
|
|
44
|
+
* The cascade is pure CSS variable inheritance — grid sets the var on the
|
|
45
|
+
* section, a cell redeclares it on itself. */
|
|
46
|
+
.rf-bento[data-content-height="sm"], .rf-bento-cell[data-content-height="sm"] { --cell-content-height: 6rem; --cell-content-overflow: hidden; }
|
|
47
|
+
.rf-bento[data-content-height="md"], .rf-bento-cell[data-content-height="md"] { --cell-content-height: 10rem; --cell-content-overflow: hidden; }
|
|
48
|
+
.rf-bento[data-content-height="lg"], .rf-bento-cell[data-content-height="lg"] { --cell-content-height: 14rem; --cell-content-overflow: hidden; }
|
|
49
|
+
.rf-bento[data-content-height="xl"], .rf-bento-cell[data-content-height="xl"] { --cell-content-height: 18rem; --cell-content-overflow: hidden; }
|
|
50
|
+
|
|
51
|
+
.rf-bento[data-media-ratio="1/3"], .rf-bento-cell[data-media-ratio="1/3"] { --cell-media-ratio: 33.333%; }
|
|
52
|
+
.rf-bento[data-media-ratio="2/5"], .rf-bento-cell[data-media-ratio="2/5"] { --cell-media-ratio: 40%; }
|
|
53
|
+
.rf-bento[data-media-ratio="1/2"], .rf-bento-cell[data-media-ratio="1/2"] { --cell-media-ratio: 50%; }
|
|
54
|
+
.rf-bento[data-media-ratio="3/5"], .rf-bento-cell[data-media-ratio="3/5"] { --cell-media-ratio: 60%; }
|
|
55
|
+
.rf-bento[data-media-ratio="2/3"], .rf-bento-cell[data-media-ratio="2/3"] { --cell-media-ratio: 66.667%; }
|
|
56
|
+
|
|
57
|
+
/* content-height pins the text area on COLUMN cells only (top/bottom — and
|
|
58
|
+
* no-media cells, which default to `top`). Positive selectors keep this at the
|
|
59
|
+
* same specificity as the mobile/collapse stack resets below, so those still win
|
|
60
|
+
* and the text area returns to natural height when stacked. Both the basis and
|
|
61
|
+
* the clip are gated on content-height being set (via the cascading vars): when
|
|
62
|
+
* unset they fall back to `auto` / `visible`, so the content zone keeps its
|
|
63
|
+
* default overflow and a bleeding showcase (negative margins) still reaches the
|
|
64
|
+
* cell edge. The clip only engages when you actually pin the height. */
|
|
65
|
+
.rf-bento-cell[data-media-position="top"] > .rf-bento-cell__content,
|
|
66
|
+
.rf-bento-cell[data-media-position="bottom"] > .rf-bento-cell__content {
|
|
67
|
+
flex-basis: var(--cell-content-height, auto);
|
|
68
|
+
overflow: var(--cell-content-overflow, visible);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Cell footprint: resolved `cols`/`rows` spans, auto-capped to the effective
|
|
72
|
+
* column count so a wide cell never overflows the (reduced) grid. */
|
|
46
73
|
.rf-bento-cell {
|
|
47
|
-
|
|
48
|
-
|
|
74
|
+
grid-column: span min(var(--cell-cols, 1), var(--bento-cols-effective, 6));
|
|
75
|
+
grid-row: span var(--cell-rows, 1);
|
|
76
|
+
min-width: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* The cell. `:where()` keeps the background at zero specificity so a per-cell
|
|
80
|
+
* `{% tint %}` can repaint it (tint-deferrable). */
|
|
81
|
+
:where(.rf-bento-cell) {
|
|
49
82
|
background: var(--rf-color-surface);
|
|
50
|
-
overflow: hidden;
|
|
51
|
-
}
|
|
52
|
-
.rf-bento-cell--full {
|
|
53
|
-
grid-column: 1 / -1;
|
|
54
|
-
}
|
|
55
|
-
.rf-bento-cell--large {
|
|
56
|
-
grid-column: span 2;
|
|
57
|
-
grid-row: span 2;
|
|
58
|
-
}
|
|
59
|
-
.rf-bento-cell--medium {
|
|
60
|
-
grid-column: span 2;
|
|
61
|
-
}
|
|
62
|
-
.rf-bento-cell--small {
|
|
63
|
-
grid-column: span 1;
|
|
64
|
-
}
|
|
65
|
-
/* Span mode: heading level determines column span via CSS variable */
|
|
66
|
-
.rf-bento-cell--span {
|
|
67
|
-
grid-column: span var(--cell-span, 1);
|
|
68
|
-
}
|
|
69
|
-
.rf-bento-cell__icon {
|
|
70
|
-
margin-bottom: 0.75rem;
|
|
71
|
-
line-height: 0;
|
|
72
|
-
}
|
|
73
|
-
.rf-bento-cell__icon svg {
|
|
74
|
-
width: 1.5rem;
|
|
75
|
-
height: 1.5rem;
|
|
76
|
-
color: var(--rf-color-accent);
|
|
77
|
-
}
|
|
78
|
-
.rf-bento-cell__icon .rf-icon {
|
|
79
|
-
width: 1.5rem;
|
|
80
|
-
height: 1.5rem;
|
|
81
|
-
color: var(--rf-color-accent);
|
|
82
83
|
}
|
|
84
|
+
.rf-bento-cell {
|
|
85
|
+
border-radius: var(--rf-radius-container);
|
|
86
|
+
/* The cell hugs its media with only a thin edge margin; the content zone adds
|
|
87
|
+
* the remaining inset back (below), so copy still sits at the full padding
|
|
88
|
+
* while media — and a media banner — sits close to the cell border.
|
|
89
|
+
* `--bento-media-gap` is the always-on space between media and content (in any
|
|
90
|
+
* `data-media-position`), regardless of cell size; the flex gap handles both
|
|
91
|
+
* column / column-reverse (top/bottom) and row / row-reverse (start/end). */
|
|
92
|
+
--container-padding: 1.25rem;
|
|
93
|
+
--bento-cell-edge: 0.5rem;
|
|
94
|
+
--bento-media-gap: var(--rf-spacing-md);
|
|
95
|
+
padding: var(--bento-cell-edge);
|
|
96
|
+
border: 1px solid var(--rf-color-border);
|
|
97
|
+
overflow: hidden;
|
|
98
|
+
position: relative; /* anchor for the stretched link */
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
gap: var(--bento-media-gap);
|
|
102
|
+
min-height: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Media placement (data-media-position): top stacks media above content (the
|
|
106
|
+
* default flow order); bottom flips it; start/end place media beside the body. */
|
|
107
|
+
.rf-bento-cell[data-media-position="bottom"] { flex-direction: column-reverse; }
|
|
108
|
+
.rf-bento-cell[data-media-position="start"] { flex-direction: row; align-items: stretch; }
|
|
109
|
+
.rf-bento-cell[data-media-position="end"] { flex-direction: row-reverse; align-items: stretch; }
|
|
110
|
+
.rf-bento-cell[data-media-position="start"] > .rf-bento-cell__media,
|
|
111
|
+
.rf-bento-cell[data-media-position="end"] > .rf-bento-cell__media {
|
|
112
|
+
flex: 0 0 var(--cell-media-ratio, 42%);
|
|
113
|
+
margin-bottom: 0;
|
|
114
|
+
align-self: stretch;
|
|
115
|
+
}
|
|
116
|
+
.rf-bento-cell[data-media-position="start"] > .rf-bento-cell__content,
|
|
117
|
+
.rf-bento-cell[data-media-position="end"] > .rf-bento-cell__content { flex: 1; min-width: 0; }
|
|
118
|
+
|
|
119
|
+
/* Zones. The WORK-339 media-zone selector sizes/clips any guest — no per-guest
|
|
120
|
+
* CSS here. */
|
|
121
|
+
.rf-bento-cell__media { margin-bottom: 0; }
|
|
122
|
+
/* Text-first: the body keeps its natural height and is never squeezed out by
|
|
123
|
+
* media. In a fixed grid track the media zone (top/bottom) absorbs the leftover
|
|
124
|
+
* height and crops (cover); once collapsed to a stack it becomes an aspect-ratio
|
|
125
|
+
* banner (below). `--bento-media-aspect` is the knob SPEC-086's `frame-aspect`
|
|
126
|
+
* will drive; `--bento-media-anchor` picks which slice shows when cropped (e.g.
|
|
127
|
+
* `top` to peek the top of a tall guest like a phone mockup). */
|
|
128
|
+
/* Add the remaining inset back on top of the cell's thin edge, so text sits at
|
|
129
|
+
* the full `--container-padding`. The top inset doubles as the gap between media
|
|
130
|
+
* and copy (media zone carries no margin of its own). */
|
|
131
|
+
.rf-bento-cell__content {
|
|
132
|
+
display: flex;
|
|
133
|
+
flex-direction: column;
|
|
134
|
+
min-height: 0;
|
|
135
|
+
flex: 0 0 auto;
|
|
136
|
+
padding: calc(var(--container-padding) - var(--bento-cell-edge));
|
|
137
|
+
}
|
|
138
|
+
.rf-bento-cell[data-media-position="top"] > .rf-bento-cell__media,
|
|
139
|
+
.rf-bento-cell[data-media-position="bottom"] > .rf-bento-cell__media {
|
|
140
|
+
flex: 1 1 auto;
|
|
141
|
+
min-height: 0;
|
|
142
|
+
aspect-ratio: var(--bento-media-aspect, 16 / 9);
|
|
143
|
+
}
|
|
144
|
+
.rf-bento-cell__media > :is(img, video) { object-position: var(--bento-media-anchor, center); }
|
|
145
|
+
/* SPEC-086 — reconcile `frame` facets with bento's existing media vars rather
|
|
146
|
+
* than duplicating them: frame-aspect/anchor (landed on the media zone by the
|
|
147
|
+
* engine) feed the knobs bento already consumes (incl. the collapse path). */
|
|
148
|
+
.rf-bento-cell__media[style*="--frame-aspect"] { --bento-media-aspect: var(--frame-aspect); }
|
|
149
|
+
.rf-bento-cell__media[style*="--frame-anchor"] { --bento-media-anchor: var(--frame-anchor); }
|
|
83
150
|
.rf-bento-cell__title {
|
|
84
151
|
font-size: 1.125rem;
|
|
85
152
|
font-weight: 600;
|
|
86
153
|
margin: 0 0 0.5rem;
|
|
87
154
|
}
|
|
155
|
+
.rf-bento-cell__body { min-height: 0; }
|
|
88
156
|
.rf-bento-cell__body > span[property],
|
|
89
157
|
.rf-bento-cell__body > meta { display: none; }
|
|
158
|
+
.rf-bento-cell__body > :first-child { margin-top: 0; }
|
|
90
159
|
.rf-bento-cell__body p:last-child { margin-bottom: 0; }
|
|
91
|
-
.rf-bento-
|
|
160
|
+
.rf-bento-cell__footer {
|
|
161
|
+
margin-top: auto;
|
|
162
|
+
padding-top: 0.5rem;
|
|
163
|
+
border-top: 1px solid var(--rf-color-border);
|
|
164
|
+
font-size: 0.8125em;
|
|
165
|
+
color: var(--rf-color-muted);
|
|
166
|
+
}
|
|
167
|
+
.rf-bento-cell__body img {
|
|
92
168
|
width: 100%;
|
|
93
169
|
height: auto;
|
|
94
170
|
border-radius: var(--rf-radius-sm);
|
|
95
171
|
margin-bottom: 0.75rem;
|
|
96
172
|
}
|
|
97
|
-
|
|
173
|
+
|
|
174
|
+
/* Whole-cell link — stretched overlay; nested links stay clickable. */
|
|
175
|
+
.rf-bento-cell__link { position: absolute; inset: 0; z-index: 0; }
|
|
176
|
+
.rf-bento-cell a:not(.rf-bento-cell__link) { position: relative; z-index: 1; }
|
|
177
|
+
|
|
178
|
+
/* Drop padding on bleed edges when a showcase bleeds (clipped to a peek by
|
|
179
|
+
* the media-zone + cell overflow). */
|
|
98
180
|
.rf-bento-cell:has(.rf-showcase[data-bleed="bottom"]),
|
|
99
181
|
.rf-bento-cell:has(.rf-showcase[data-bleed="both"]),
|
|
100
|
-
.rf-bento-cell:has(.rf-showcase[data-bleed="bottom-end"]) {
|
|
101
|
-
padding-bottom: 0;
|
|
102
|
-
}
|
|
182
|
+
.rf-bento-cell:has(.rf-showcase[data-bleed="bottom-end"]) { padding-bottom: 0; }
|
|
103
183
|
.rf-bento-cell:has(.rf-showcase[data-bleed="end"]),
|
|
104
184
|
.rf-bento-cell:has(.rf-showcase[data-bleed="bottom-end"]),
|
|
105
|
-
.rf-bento-cell:has(.rf-showcase[data-bleed="top-end"]) {
|
|
106
|
-
padding-inline-end: 0;
|
|
107
|
-
}
|
|
185
|
+
.rf-bento-cell:has(.rf-showcase[data-bleed="top-end"]) { padding-inline-end: 0; }
|
|
108
186
|
.rf-bento-cell:has(.rf-showcase[data-bleed="top"]),
|
|
109
187
|
.rf-bento-cell:has(.rf-showcase[data-bleed="both"]),
|
|
110
|
-
.rf-bento-cell:has(.rf-showcase[data-bleed="top-end"]) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.rf-bento-
|
|
124
|
-
.rf-bento-cell
|
|
125
|
-
.rf-bento-cell
|
|
188
|
+
.rf-bento-cell:has(.rf-showcase[data-bleed="top-end"]) { padding-top: 0; }
|
|
189
|
+
|
|
190
|
+
/* ─── Binary collapse ─── Above the chosen breakpoint, the grid renders exactly
|
|
191
|
+
* as authored: `columns` and per-cell `cols`/`rows` are honored (span auto-cap
|
|
192
|
+
* above still keeps wide cells inside the grid). Below it, the grid drops to a
|
|
193
|
+
* single stacked column with auto row tracks (cells size to content, never
|
|
194
|
+
* clipped). Default = `sm` (640px), so phones stack but tablet-portrait and up
|
|
195
|
+
* keep the grid. Authors who want graceful column reduction between desktop and
|
|
196
|
+
* stack can opt in via `collapse="gradual"` (not yet implemented). */
|
|
197
|
+
@container rf-bento (max-width: 640px) {
|
|
198
|
+
/* `[data-collapse=""]` matches the unset (default) case — the engine emits
|
|
199
|
+
* the attribute with an empty value when the author doesn't set it, so a bare
|
|
200
|
+
* `:not([data-collapse])` wouldn't match. */
|
|
201
|
+
:is(.rf-bento[data-collapse="sm"], .rf-bento[data-collapse=""]) .rf-bento__grid { --bento-cols-effective: 1; --bento-row-track: auto; }
|
|
202
|
+
:is(.rf-bento[data-collapse="sm"], .rf-bento[data-collapse=""]) .rf-bento-cell[data-media-position="start"],
|
|
203
|
+
:is(.rf-bento[data-collapse="sm"], .rf-bento[data-collapse=""]) .rf-bento-cell[data-media-position="end"] { flex-direction: column; }
|
|
204
|
+
:is(.rf-bento[data-collapse="sm"], .rf-bento[data-collapse=""]) .rf-bento-cell[data-media-position] > .rf-bento-cell__media { flex: 0 0 auto; aspect-ratio: var(--bento-media-aspect, 16 / 9); }
|
|
205
|
+
:is(.rf-bento[data-collapse="sm"], .rf-bento[data-collapse=""]) .rf-bento-cell[data-media-position] > .rf-bento-cell__content { flex: 0 0 auto; }
|
|
206
|
+
}
|
|
207
|
+
@container rf-bento (max-width: 768px) {
|
|
208
|
+
.rf-bento[data-collapse="md"] .rf-bento__grid { --bento-cols-effective: 1; --bento-row-track: auto; }
|
|
209
|
+
.rf-bento[data-collapse="md"] .rf-bento-cell[data-media-position="start"],
|
|
210
|
+
.rf-bento[data-collapse="md"] .rf-bento-cell[data-media-position="end"] { flex-direction: column; }
|
|
211
|
+
.rf-bento[data-collapse="md"] .rf-bento-cell[data-media-position] > .rf-bento-cell__media { flex: 0 0 auto; aspect-ratio: var(--bento-media-aspect, 16 / 9); }
|
|
212
|
+
.rf-bento[data-collapse="md"] .rf-bento-cell[data-media-position] > .rf-bento-cell__content { flex: 0 0 auto; }
|
|
213
|
+
}
|
|
214
|
+
@container rf-bento (max-width: 1024px) {
|
|
215
|
+
.rf-bento[data-collapse="lg"] .rf-bento__grid { --bento-cols-effective: 1; --bento-row-track: auto; }
|
|
216
|
+
.rf-bento[data-collapse="lg"] .rf-bento-cell[data-media-position="start"],
|
|
217
|
+
.rf-bento[data-collapse="lg"] .rf-bento-cell[data-media-position="end"] { flex-direction: column; }
|
|
218
|
+
.rf-bento[data-collapse="lg"] .rf-bento-cell[data-media-position] > .rf-bento-cell__media { flex: 0 0 auto; aspect-ratio: var(--bento-media-aspect, 16 / 9); }
|
|
219
|
+
.rf-bento[data-collapse="lg"] .rf-bento-cell[data-media-position] > .rf-bento-cell__content { flex: 0 0 auto; }
|
|
126
220
|
}
|
package/styles/runes/bg.css
CHANGED
|
@@ -51,3 +51,27 @@
|
|
|
51
51
|
[data-name="bg-overlay"][data-bg-overlay="light"] {
|
|
52
52
|
background: rgba(255, 255, 255, 0.6);
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
/* Scrim (SPEC-088) — a structured legibility treatment behind overlaid text.
|
|
56
|
+
* gradient: a directional darken/lighten; frost: a backdrop blur + tint.
|
|
57
|
+
* `tone` picks the colour, `dir` the heavy edge; strength/blur come inline. */
|
|
58
|
+
[data-name="scrim"] {
|
|
59
|
+
position: absolute;
|
|
60
|
+
inset: 0;
|
|
61
|
+
pointer-events: none;
|
|
62
|
+
z-index: 1;
|
|
63
|
+
}
|
|
64
|
+
[data-name="scrim"][data-scrim-tone="dark"] { --scrim-color: 0 0 0; }
|
|
65
|
+
[data-name="scrim"][data-scrim-tone="light"] { --scrim-color: 255 255 255; }
|
|
66
|
+
[data-name="scrim"][data-scrim-dir="top"] { --scrim-grad-dir: to top; }
|
|
67
|
+
[data-name="scrim"][data-scrim-dir="bottom"] { --scrim-grad-dir: to bottom; }
|
|
68
|
+
[data-name="scrim"][data-scrim-dir="left"] { --scrim-grad-dir: to left; }
|
|
69
|
+
[data-name="scrim"][data-scrim-dir="right"] { --scrim-grad-dir: to right; }
|
|
70
|
+
[data-name="scrim"][data-scrim="gradient"] {
|
|
71
|
+
background-image: linear-gradient(var(--scrim-grad-dir, to top), transparent, rgb(var(--scrim-color, 0 0 0) / var(--scrim-strength, 0.55)));
|
|
72
|
+
}
|
|
73
|
+
[data-name="scrim"][data-scrim="frost"] {
|
|
74
|
+
-webkit-backdrop-filter: blur(var(--scrim-blur, 8px));
|
|
75
|
+
backdrop-filter: blur(var(--scrim-blur, 8px));
|
|
76
|
+
background: rgb(var(--scrim-color, 0 0 0) / 0.18);
|
|
77
|
+
}
|
package/styles/runes/bond.css
CHANGED
package/styles/runes/budget.css
CHANGED
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
gap: 1rem;
|
|
87
87
|
}
|
|
88
88
|
.rf-budget-line-item + .rf-budget-line-item {
|
|
89
|
-
border-top: 1px solid var(--rf-color-border
|
|
89
|
+
border-top: 1px solid var(--rf-color-border);
|
|
90
90
|
}
|
|
91
91
|
.rf-budget-line-item__description {
|
|
92
92
|
font-size: 0.875rem;
|
package/styles/runes/card.css
CHANGED
|
@@ -1,20 +1,75 @@
|
|
|
1
1
|
/* card — generic content card (SPEC-070).
|
|
2
2
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* data-
|
|
3
|
+
* A soft surface fill plus a subtle border; the surface + border + structure
|
|
4
|
+
* (media split, footer separator) carry the card. The media|content split +
|
|
5
|
+
* responsive collapse + inset media banner come from the shared
|
|
6
|
+
* layouts/split.css (keyed off data-layout / data-section="media" /
|
|
7
|
+
* data-name="content" / data-media-position).
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
.rf-card {
|
|
11
|
-
position: relative; /* anchor for the stretched link
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
position: relative; /* anchor for the stretched link */
|
|
12
|
+
/* Thin-edge inset model (matches `bento-cell`): the card hugs its media with
|
|
13
|
+
* only a small edge margin, and the content zone adds the remaining inset
|
|
14
|
+
* back below — so text still sits at the full `--rune-padding` while media
|
|
15
|
+
* sits close to the card border, with no negative-margin bleed.
|
|
16
|
+
* • `--rf-card-edge` — fixed thin outer padding.
|
|
17
|
+
* • `--rf-card-media-gap` — generous gap when media stacks above/below
|
|
18
|
+
* content (stacked layout, or split collapsed
|
|
19
|
+
* to one column on mobile).
|
|
20
|
+
* • `--rf-card-split-gap` — tight gap when media sits beside content,
|
|
21
|
+
* so the card reads as a unified surface
|
|
22
|
+
* rather than two loosely-related panels. */
|
|
23
|
+
--rf-card-edge: 0.5rem;
|
|
24
|
+
--rf-card-media-gap: var(--rf-spacing-lg);
|
|
25
|
+
--rf-card-split-gap: var(--rf-spacing-sm);
|
|
26
|
+
padding: var(--rf-card-edge);
|
|
27
|
+
border-radius: var(--rf-radius-container);
|
|
28
|
+
border: 1px solid var(--rf-color-border);
|
|
14
29
|
background: var(--rf-color-surface);
|
|
15
30
|
}
|
|
16
31
|
|
|
17
|
-
|
|
32
|
+
/* Content zone fills the remaining inset so total text padding still equals
|
|
33
|
+
* `--rune-padding`. `max(0, …)` keeps dense+ (rune-padding < edge) safe — at
|
|
34
|
+
* tighter densities the edge becomes the floor. */
|
|
35
|
+
.rf-card__content {
|
|
36
|
+
padding: max(0px, calc(var(--rune-padding, var(--rf-spacing-md)) - var(--rf-card-edge)));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Split layout: tight column-gap when media sits beside content. `row-gap`
|
|
40
|
+
* stays at the larger media-gap so the collapse step (split grid → single
|
|
41
|
+
* column) automatically picks up the generous stacked spacing instead of
|
|
42
|
+
* inheriting the tight beside-gap. (2-section cards have only one grid row in
|
|
43
|
+
* the uncollapsed state, so row-gap is dormant until collapse.) `stretch`
|
|
44
|
+
* lets the media zone match the content's height — the image inside uses
|
|
45
|
+
* `object-fit: cover` so it re-crops rather than letterboxing when content
|
|
46
|
+
* is taller than the media's natural aspect ratio.
|
|
47
|
+
*
|
|
48
|
+
* We set `column-gap` / `row-gap` / `align-items` directly rather than override
|
|
49
|
+
* `--split-gap` / `--split-valign`: the shared split layout transform emits
|
|
50
|
+
* those vars as inline styles even when the author doesn't set the matching
|
|
51
|
+
* attributes, so a CSS-variable override loses to inline-style specificity.
|
|
52
|
+
* Direct grid properties win via selector specificity and stay in our control. */
|
|
53
|
+
.rf-card[data-media-position="start"],
|
|
54
|
+
.rf-card[data-media-position="end"] {
|
|
55
|
+
column-gap: var(--rf-card-split-gap);
|
|
56
|
+
row-gap: var(--rf-card-media-gap);
|
|
57
|
+
align-items: stretch;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Stacked layouts (top / bottom): explicit gap between the media banner and
|
|
61
|
+
* the content zone (replaces the figure-style margin-bottom from the bleed
|
|
62
|
+
* model). For `bottom`, column-reverse already flips the visual order, so the
|
|
63
|
+
* gap still reads as the space between the two zones. */
|
|
64
|
+
.rf-card[data-media-position="top"] > [data-section="media"] {
|
|
65
|
+
margin-bottom: var(--rf-card-media-gap);
|
|
66
|
+
}
|
|
67
|
+
.rf-card[data-media-position="bottom"] > [data-section="media"] {
|
|
68
|
+
margin-top: var(--rf-card-media-gap);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Only a linked card (stretched `.rf-card__link` present) reacts to hover. */
|
|
72
|
+
.rf-card:has(.rf-card__link):hover {
|
|
18
73
|
background: var(--rf-color-surface-hover);
|
|
19
74
|
}
|
|
20
75
|
|
|
@@ -70,6 +125,19 @@
|
|
|
70
125
|
flex: 1;
|
|
71
126
|
}
|
|
72
127
|
|
|
128
|
+
/* Height knob (SPEC-089) — a named intrinsic-height scale for cover / bg-only
|
|
129
|
+
* cards that have no natural content height to fill. `aspect` (→ aspect-ratio,
|
|
130
|
+
* emitted inline by the engine) is the proportional alternative; `height` is the
|
|
131
|
+
* absolute one. Both are authority overrides: they win over the cover variant's
|
|
132
|
+
* default media aspect because they sit on the card root with a real value, and
|
|
133
|
+
* over an external grid track only when the card isn't being stretched by one.
|
|
134
|
+
* For cover cards the grid stack (cover.css) fills this box; for bg-only cards
|
|
135
|
+
* the min-height simply gives the surface a poster shape. */
|
|
136
|
+
.rf-card[data-height="sm"] { min-height: 12rem; }
|
|
137
|
+
.rf-card[data-height="md"] { min-height: 18rem; }
|
|
138
|
+
.rf-card[data-height="lg"] { min-height: 26rem; }
|
|
139
|
+
.rf-card[data-height="xl"] { min-height: 34rem; }
|
|
140
|
+
|
|
73
141
|
/* Whole-card stretched link — covers the card; real links in body/footer
|
|
74
142
|
* sit above it via position:relative. */
|
|
75
143
|
.rf-card__link {
|
package/styles/runes/chart.css
CHANGED
|
@@ -1,11 +1,74 @@
|
|
|
1
1
|
/* Chart */
|
|
2
2
|
.rf-chart {
|
|
3
|
+
/* ─── Theming contract (SPEC-083 / WORK-353) ───
|
|
4
|
+
* Every paint + geometry value the renderer uses is one of these
|
|
5
|
+
* `--rf-chart-*` properties; a theme overrides any of them (here, on a tint
|
|
6
|
+
* scope, or inline) with no renderer or selector changes. A non-CSS provider
|
|
7
|
+
* (canvas) reads the same props via getComputedStyle. */
|
|
8
|
+
|
|
9
|
+
/* Series palette — a dedicated categorical palette, deliberately distinct
|
|
10
|
+
* from the semantic status tokens (those are reserved for sentiment mode). */
|
|
11
|
+
--rf-chart-series-1: #6366f1;
|
|
12
|
+
--rf-chart-series-2: #06b6d4;
|
|
13
|
+
--rf-chart-series-3: #22c55e;
|
|
14
|
+
--rf-chart-series-4: #f59e0b;
|
|
15
|
+
--rf-chart-series-5: #a855f7;
|
|
16
|
+
--rf-chart-series-6: #ec4899;
|
|
17
|
+
|
|
18
|
+
/* Geometry (layout values the renderer reads via getComputedStyle). */
|
|
19
|
+
--rf-chart-bar-ratio: 0.75; /* bar width as a fraction of its slot */
|
|
20
|
+
--rf-chart-bar-thickness: 48px; /* max bar width cap */
|
|
21
|
+
--rf-chart-bar-gap: 0.15; /* slot fraction reserved as inter-group gap */
|
|
22
|
+
--rf-chart-bar-radius: 2px;
|
|
23
|
+
--rf-chart-point-radius: 4px;
|
|
24
|
+
--rf-chart-line-width: 2px;
|
|
25
|
+
|
|
26
|
+
/* Typography / grid (paint values applied by CSS below). */
|
|
27
|
+
--rf-chart-label-size: 12px;
|
|
28
|
+
--rf-chart-label-color: var(--rf-color-muted);
|
|
29
|
+
--rf-chart-grid-color: var(--rf-color-border);
|
|
30
|
+
--rf-chart-grid-width: 1px;
|
|
31
|
+
|
|
3
32
|
display: block;
|
|
4
33
|
border-radius: var(--rf-radius-lg);
|
|
5
34
|
padding: 1.5rem;
|
|
6
35
|
margin: 0;
|
|
7
36
|
}
|
|
8
37
|
|
|
38
|
+
/* ─── SVG paint — driven entirely by the contract (the renderer sets no inline
|
|
39
|
+
* fill/stroke; it only tags elements with a class + data-series + optional
|
|
40
|
+
* data-meta-sentiment). ─── */
|
|
41
|
+
.rf-chart__axis { stroke: var(--rf-chart-grid-color); stroke-width: var(--rf-chart-grid-width); }
|
|
42
|
+
.rf-chart__label { fill: var(--rf-chart-label-color); font-size: var(--rf-chart-label-size); }
|
|
43
|
+
.rf-chart__bar { rx: var(--rf-chart-bar-radius); fill: var(--rf-chart-series-1); }
|
|
44
|
+
.rf-chart__point { fill: var(--rf-chart-series-1); }
|
|
45
|
+
.rf-chart__line { fill: none; stroke: var(--rf-chart-series-1); stroke-width: var(--rf-chart-line-width); }
|
|
46
|
+
|
|
47
|
+
/* Series palette rotation (data-series 0 is the default above). */
|
|
48
|
+
.rf-chart__bar[data-series="1"], .rf-chart__point[data-series="1"] { fill: var(--rf-chart-series-2); }
|
|
49
|
+
.rf-chart__bar[data-series="2"], .rf-chart__point[data-series="2"] { fill: var(--rf-chart-series-3); }
|
|
50
|
+
.rf-chart__bar[data-series="3"], .rf-chart__point[data-series="3"] { fill: var(--rf-chart-series-4); }
|
|
51
|
+
.rf-chart__bar[data-series="4"], .rf-chart__point[data-series="4"] { fill: var(--rf-chart-series-5); }
|
|
52
|
+
.rf-chart__bar[data-series="5"], .rf-chart__point[data-series="5"] { fill: var(--rf-chart-series-6); }
|
|
53
|
+
.rf-chart__line[data-series="1"] { stroke: var(--rf-chart-series-2); }
|
|
54
|
+
.rf-chart__line[data-series="2"] { stroke: var(--rf-chart-series-3); }
|
|
55
|
+
.rf-chart__line[data-series="3"] { stroke: var(--rf-chart-series-4); }
|
|
56
|
+
.rf-chart__line[data-series="4"] { stroke: var(--rf-chart-series-5); }
|
|
57
|
+
.rf-chart__line[data-series="5"] { stroke: var(--rf-chart-series-6); }
|
|
58
|
+
|
|
59
|
+
/* Sentiment colouring — a data cell carrying `data-meta-sentiment` overrides the
|
|
60
|
+
* rotating palette with the semantic token (the roadmap chart renders green-done
|
|
61
|
+
* / red-blocked with no per-chart config). After the palette so it wins at equal
|
|
62
|
+
* specificity. */
|
|
63
|
+
.rf-chart__bar[data-meta-sentiment="positive"], .rf-chart__point[data-meta-sentiment="positive"] { fill: var(--rf-color-success); }
|
|
64
|
+
.rf-chart__bar[data-meta-sentiment="negative"], .rf-chart__point[data-meta-sentiment="negative"] { fill: var(--rf-color-danger); }
|
|
65
|
+
.rf-chart__bar[data-meta-sentiment="caution"], .rf-chart__point[data-meta-sentiment="caution"] { fill: var(--rf-color-warning); }
|
|
66
|
+
.rf-chart__bar[data-meta-sentiment="neutral"], .rf-chart__point[data-meta-sentiment="neutral"] { fill: var(--rf-color-muted); }
|
|
67
|
+
.rf-chart__line[data-meta-sentiment="positive"] { stroke: var(--rf-color-success); }
|
|
68
|
+
.rf-chart__line[data-meta-sentiment="negative"] { stroke: var(--rf-color-danger); }
|
|
69
|
+
.rf-chart__line[data-meta-sentiment="caution"] { stroke: var(--rf-color-warning); }
|
|
70
|
+
.rf-chart__line[data-meta-sentiment="neutral"] { stroke: var(--rf-color-muted); }
|
|
71
|
+
|
|
9
72
|
/* Data table — the no-JS / screen-reader source of truth. Visible as the
|
|
10
73
|
* fallback; visually-hidden (but kept for assistive tech) once rf-chart has
|
|
11
74
|
* rendered the svg. */
|
|
@@ -60,4 +123,10 @@
|
|
|
60
123
|
width: 12px;
|
|
61
124
|
height: 12px;
|
|
62
125
|
border-radius: 2px;
|
|
126
|
+
background: var(--rf-chart-series-1);
|
|
63
127
|
}
|
|
128
|
+
.rf-chart__legend-color[data-series="1"] { background: var(--rf-chart-series-2); }
|
|
129
|
+
.rf-chart__legend-color[data-series="2"] { background: var(--rf-chart-series-3); }
|
|
130
|
+
.rf-chart__legend-color[data-series="3"] { background: var(--rf-chart-series-4); }
|
|
131
|
+
.rf-chart__legend-color[data-series="4"] { background: var(--rf-chart-series-5); }
|
|
132
|
+
.rf-chart__legend-color[data-series="5"] { background: var(--rf-chart-series-6); }
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
gap: 1.5rem;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/* Children inside design-context: remove card styling
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
/* Children inside design-context: remove card styling so the outer
|
|
14
|
+
* design-context surface is the only visible frame. Structural selector
|
|
15
|
+
* (`> [data-rune]` inside the sections wrapper) catches every direct rune
|
|
16
|
+
* child, not just the four design-system runes — so dropping a future rune
|
|
17
|
+
* into a design-context will Just Work without a per-rune CSS update. */
|
|
18
|
+
.rf-design-context__sections > [data-rune] {
|
|
18
19
|
background: none;
|
|
20
|
+
border: none;
|
|
19
21
|
border-radius: 0;
|
|
20
22
|
padding: 0;
|
|
21
23
|
}
|
package/styles/runes/event.css
CHANGED
package/styles/runes/faction.css
CHANGED
|
@@ -4,15 +4,41 @@
|
|
|
4
4
|
* "preamble"), and dimensions/metadata.css ([data-zone-layout]). This file
|
|
5
5
|
* only carries faction-specific chrome. */
|
|
6
6
|
|
|
7
|
-
/*
|
|
8
|
-
|
|
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` — faction content can be long (multiple sections),
|
|
11
|
+
* so we let the scene keep its natural aspect ratio rather than cover-cropping. */
|
|
12
|
+
.rf-faction {
|
|
13
|
+
--rf-faction-edge: 0.5rem;
|
|
14
|
+
--rf-faction-media-gap: var(--rf-spacing-lg);
|
|
15
|
+
--rf-faction-split-gap: var(--rf-spacing-sm);
|
|
16
|
+
padding: var(--rf-faction-edge);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.rf-faction__content {
|
|
20
|
+
padding: max(0px, calc(var(--rune-padding, var(--rf-spacing-md)) - var(--rf-faction-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-faction[data-media-position="start"],
|
|
26
|
+
.rf-faction[data-media-position="end"] {
|
|
27
|
+
column-gap: var(--rf-faction-split-gap);
|
|
28
|
+
row-gap: var(--rf-faction-media-gap);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Stacked layouts — beside layouts handled by shared split.css */
|
|
32
|
+
.rf-faction[data-media-position="top"] {
|
|
9
33
|
display: flex;
|
|
10
34
|
flex-direction: column;
|
|
11
35
|
}
|
|
12
36
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
37
|
+
.rf-faction[data-media-position="top"] > .rf-faction__scene {
|
|
38
|
+
margin-bottom: var(--rf-faction-media-gap);
|
|
39
|
+
}
|
|
40
|
+
.rf-faction[data-media-position="bottom"] > .rf-faction__scene {
|
|
41
|
+
margin-top: var(--rf-faction-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-
|
|
66
|
+
color: var(--rf-color-text);
|
|
41
67
|
}
|
|
42
68
|
.rf-faction-section__body ul,
|
|
43
69
|
.rf-faction-section__body ol {
|