@refrakt-md/lumina 0.16.0 → 0.16.1

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.
@@ -224,6 +224,14 @@
224
224
  "{content}"
225
225
  ]
226
226
  },
227
+ "FileRef": {
228
+ "block": "file-ref",
229
+ "root": ".rf-file-ref",
230
+ "dataRune": "file-ref",
231
+ "childOrder": [
232
+ "{content}"
233
+ ]
234
+ },
227
235
  "Aggregate": {
228
236
  "block": "aggregate",
229
237
  "root": ".rf-aggregate",
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,EAAE,iBAiL1B,CAAC"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,EAAE,iBA6L1B,CAAC"}
package/dist/tokens.js CHANGED
@@ -64,6 +64,17 @@ export const luminaTokens = {
64
64
  // alias via `extra` below for downstream CSS that still reads it.
65
65
  'inline-bg': '#e6e5e3',
66
66
  },
67
+ // WORK-304 — line-level annotation tokens shared by snippet /
68
+ // codegroup / diff. `highlight` is the neutral surface tint applied
69
+ // to `[data-line-status="highlight"]` rows; `highlight-rail` is the
70
+ // left-edge border colour (left as a soft accent so themes can
71
+ // repaint just the rail without touching the row background);
72
+ // `number` is the gutter colour for `pre[data-linenumbers]`.
73
+ line: {
74
+ highlight: 'color-mix(in srgb, var(--rf-color-text) 6%, transparent)',
75
+ 'highlight-rail': 'var(--rf-color-primary, var(--rf-color-text))',
76
+ number: 'var(--rf-color-muted)',
77
+ },
67
78
  },
68
79
  radius: {
69
80
  sm: '6px',
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
package/index.css CHANGED
@@ -54,6 +54,7 @@
54
54
  @import './styles/runes/collection.css';
55
55
  @import './styles/runes/relationships.css';
56
56
  @import './styles/runes/aggregate.css';
57
+ @import './styles/runes/file-ref.css';
57
58
  @import './styles/runes/progress.css';
58
59
  @import './styles/runes/changelog.css';
59
60
  @import './styles/runes/chart.css';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@refrakt-md/lumina",
3
3
  "description": "Lumina theme for refrakt.md — design tokens, CSS, identity transform, and layout configs",
4
- "version": "0.16.0",
4
+ "version": "0.16.1",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -83,9 +83,9 @@
83
83
  "build": "tsc"
84
84
  },
85
85
  "dependencies": {
86
- "@refrakt-md/runes": "0.16.0",
87
- "@refrakt-md/transform": "0.16.0",
88
- "@refrakt-md/types": "0.16.0"
86
+ "@refrakt-md/runes": "0.16.1",
87
+ "@refrakt-md/transform": "0.16.1",
88
+ "@refrakt-md/types": "0.16.1"
89
89
  },
90
90
  "devDependencies": {
91
91
  "postcss": "^8.4.0"
@@ -42,3 +42,75 @@
42
42
  opacity: 1 !important;
43
43
  color: var(--rf-color-success, #4ade80);
44
44
  }
45
+
46
+ /* WORK-304 — fence-level annotations: line numbers + line highlight.
47
+ *
48
+ * Shiki renders each line as `<span class="line">…</span>`. The fence
49
+ * schema forwards `data-linenumbers` / `data-highlight-lines` /
50
+ * `data-lines` (start offset) from the AST node onto both `<pre>` and
51
+ * `<code>`; the highlight transform sets `data-line-status="highlight"`
52
+ * on the matching `span.line` rows post-Shiki (file-coordinate math
53
+ * lives there, not in CSS).
54
+ *
55
+ * The starting line counter is seeded from `--rf-start-line` which the
56
+ * fence transform inlines on `<pre>` when `data-lines` is set
57
+ * (e.g. lines="74-125" → --rf-start-line: 73 so the first
58
+ * counter-increment lands on 74). Defaults to 0 so unannotated fences
59
+ * count from 1.
60
+ *
61
+ * `<code>` becomes a CSS grid so:
62
+ * - consecutive `.line` rows tile perfectly with no subpixel cracks
63
+ * at non-100% zoom (the previous inline-block stacking left faint
64
+ * gaps the rail and bg shone through);
65
+ * - line bg + rail extend across the full horizontal scroll region
66
+ * (grid track width = max-content keeps lines full-width even
67
+ * when the pre scrolls).
68
+ *
69
+ * Every line carries a 3px transparent left border so geometry stays
70
+ * consistent across highlighted and plain rows — matches the diff row
71
+ * primitive at `runes/diff.css`. Highlighted rows just colour the
72
+ * border + add the surface tint. */
73
+ .rf-codeblock pre code {
74
+ display: grid;
75
+ min-width: max-content;
76
+ }
77
+
78
+ .rf-codeblock pre code .line {
79
+ border-left: 3px solid transparent;
80
+ }
81
+
82
+ /* Line numbers — gutter::before owns the left column. Strip the pre's
83
+ * horizontal padding when linenumbers are on so the gutter sits flush
84
+ * against the codeblock edge; the gutter's own padding + right border
85
+ * provides the visual separation from the code body. */
86
+ .rf-codeblock pre[data-linenumbers] {
87
+ padding-left: 0;
88
+ }
89
+
90
+ .rf-codeblock pre[data-linenumbers] code {
91
+ counter-reset: rf-line var(--rf-start-line, 0);
92
+ }
93
+
94
+ .rf-codeblock pre[data-linenumbers] code .line::before {
95
+ counter-increment: rf-line;
96
+ content: counter(rf-line);
97
+ display: inline-block;
98
+ width: 3em;
99
+ padding-inline: 0.75rem 0.5rem;
100
+ margin-right: 0.75rem;
101
+ text-align: right;
102
+ color: var(--rf-color-line-number);
103
+ font-variant-numeric: tabular-nums;
104
+ user-select: none;
105
+ border-right: 1px solid var(--rf-color-border);
106
+ }
107
+
108
+ /* Highlighted lines share diff's row template — neutral surface tint
109
+ * + primary-coloured left rail, no `+`/`-` sigil. The grid layout on
110
+ * `<code>` above keeps consecutive highlighted rows continuous (no
111
+ * subpixel gaps between the rails). Border is on every `.line` (above)
112
+ * so the content alignment doesn't jump when highlighting a row. */
113
+ .rf-codeblock pre code .line[data-line-status="highlight"] {
114
+ background: var(--rf-color-line-highlight);
115
+ border-left-color: var(--rf-color-line-highlight-rail);
116
+ }
@@ -43,22 +43,40 @@
43
43
 
44
44
  /* ─── Media zone base styles ──────────────────────────────────────── */
45
45
 
46
- /* Only clip overflow when media zone contains an image. Interactive content
47
- (preview, juxtapose) needs overflow visible for bleed effects. */
48
- [data-section="media"]:has(> img) {
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
+ ) {
49
59
  border-radius: var(--rf-radius-lg);
50
60
  overflow: hidden;
51
61
  }
52
- [data-section="media"] img {
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
+ ) {
53
69
  display: block;
54
70
  width: 100%;
55
71
  height: auto;
56
72
  border-radius: var(--rf-radius-lg);
73
+ margin: 0;
57
74
  }
58
75
 
59
- /* Split layout media gets subtle depth */
60
- [data-layout="split"] > [data-section="media"] img,
61
- [data-layout="split-reverse"] > [data-section="media"] img {
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 {
62
80
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
63
81
  }
64
82
 
@@ -109,7 +127,13 @@
109
127
  margin-bottom: var(--rune-padding, var(--rf-spacing-md));
110
128
  border-radius: 0;
111
129
  }
112
- [data-media-position="top"] > [data-section="media"] img {
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
+ ) {
113
137
  border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
114
138
  }
115
139
 
@@ -134,7 +158,13 @@
134
158
  margin-bottom: var(--rune-padding, var(--rf-spacing-md));
135
159
  border-radius: 0;
136
160
  }
137
- [data-media-position="top"][data-collapse="sm"] > [data-section="media"] img {
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
+ ) {
138
168
  border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
139
169
  }
140
170
  /* Reset grid placement on collapse for header/content */
@@ -157,7 +187,13 @@
157
187
  margin-bottom: var(--rune-padding, var(--rf-spacing-md));
158
188
  border-radius: 0;
159
189
  }
160
- [data-media-position="top"][data-collapse="md"] > [data-section="media"] img {
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
+ ) {
161
197
  border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
162
198
  }
163
199
  [data-media-position="top"][data-collapse="md"] > [data-section="header"],
@@ -179,7 +215,13 @@
179
215
  margin-bottom: var(--rune-padding, var(--rf-spacing-md));
180
216
  border-radius: 0;
181
217
  }
182
- [data-media-position="top"][data-collapse="lg"] > [data-section="media"] img {
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
+ ) {
183
225
  border-radius: var(--rf-radius-md) var(--rf-radius-md) 0 0;
184
226
  }
185
227
  [data-media-position="top"][data-collapse="lg"] > [data-section="header"],
@@ -95,6 +95,19 @@
95
95
  word-break: break-word;
96
96
  overflow-x: hidden;
97
97
  }
98
+ /* WORK-304 — the standalone codeblock CSS at `elements/code.css`
99
+ * sets `display: grid; min-width: max-content` on `pre code` so
100
+ * row tints + line numbers extend across the horizontal scroll
101
+ * region. That sizing actively prevents wrapping (the code track
102
+ * is locked at max-content). Inside `overflow="wrap"` codegroups
103
+ * we WANT wrapping, so revert the code element back to a plain
104
+ * block with no min-width constraint. Lines stay block-flow so
105
+ * Shiki's `<span class="line">` children wrap with the pre's
106
+ * `white-space: pre-wrap`. */
107
+ .rf-codegroup[data-overflow="wrap"] pre code {
108
+ display: block;
109
+ min-width: 0;
110
+ }
98
111
  .rf-codegroup[data-overflow="hide"] pre {
99
112
  overflow-x: hidden;
100
113
  }
@@ -2,7 +2,19 @@
2
2
  .rf-diff {
3
3
  border: 1px solid var(--rf-color-border);
4
4
  border-radius: var(--rf-radius-lg);
5
- overflow: hidden;
5
+ /* WORK-304 — horizontal scroll lives on `.rf-diff` itself, not on
6
+ * `.rf-diff__split-container`. On mobile the split-container's
7
+ * `min-width: 40rem` makes it wider than the viewport; previously
8
+ * `.rf-diff { overflow: hidden }` clipped the right panel and the
9
+ * touch scroll had nowhere to land. Moving the scroll affordance
10
+ * up one level lets users swipe the entire diff sideways to
11
+ * reveal the off-screen panel; the border-radius still clips the
12
+ * children at the rounded corners because `overflow-x: auto` is
13
+ * non-visible. `overflow-y: hidden` keeps the vertical clip for
14
+ * the rounded top/bottom. */
15
+ overflow-x: auto;
16
+ overflow-y: hidden;
17
+ overscroll-behavior-x: contain;
6
18
  font-size: 0.875rem;
7
19
  background: var(--rf-color-code-bg);
8
20
  }
@@ -14,12 +26,19 @@
14
26
  color: var(--rf-color-muted);
15
27
  background: var(--rf-color-surface);
16
28
  border-bottom: 1px solid var(--rf-color-border);
29
+ /* `.rf-diff` is the horizontal scroll container (so mobile users
30
+ * can swipe to reveal the off-screen split panel). Without
31
+ * sticky, the header would scroll horizontally along with the
32
+ * content and the filename label would slide off-screen.
33
+ * `position: sticky; left: 0` pins the header to the visible left
34
+ * edge of the scroll viewport at all scroll positions. WORK-304. */
35
+ position: sticky;
36
+ left: 0;
17
37
  }
18
38
  .rf-diff__split-container {
19
39
  display: grid;
20
40
  grid-template-columns: 1fr 1fr;
21
41
  min-width: 40rem;
22
- overflow-x: auto;
23
42
  }
24
43
  .rf-diff__split-container .rf-diff__panel:first-child {
25
44
  border-right: 1px solid var(--rf-color-border);
@@ -31,25 +50,68 @@
31
50
  border-radius: 0;
32
51
  background: var(--rf-color-code-bg);
33
52
  overflow-x: auto;
53
+ overscroll-behavior-x: contain;
34
54
  font-family: var(--rf-font-mono);
35
55
  font-size: 0.8125rem;
36
56
  line-height: 1.6;
37
57
  }
58
+
59
+ /* WORK-304 — inner rows wrapper, sized as `display: grid; min-width:
60
+ * max-content` so every `.rf-diff__line` (grid item) spans the
61
+ * widest line's intrinsic width. The pre is the scroll container
62
+ * (`overflow-x: auto`); this wrapper is what actually expands past
63
+ * the pre's visible width when a line overflows, triggering the
64
+ * pre's scroll and keeping the row tint (+/-/highlight) contiguous
65
+ * across the full scroll region. Mirrors the
66
+ * `pre code { display: grid; min-width: max-content }` pattern in
67
+ * the standalone codeblock at `elements/code.css`. */
68
+ .rf-diff__rows {
69
+ display: grid;
70
+ min-width: max-content;
71
+ }
72
+ /* WORK-304 — annotated line primitive. Three states share one row
73
+ * shape (`add`, `remove`, `highlight`); only the tint token and the
74
+ * gutter rail colour change. The same selector form is reused by the
75
+ * standalone code block for `data-line-status="highlight"` via the
76
+ * `.rf-codeblock` rules below.
77
+ *
78
+ * `display: block` + inline-block children (not `display: flex`) for
79
+ * the line container so the line's intrinsic max-content is the
80
+ * unambiguous sum of children's intrinsic widths — flex container
81
+ * max-content was being clipped by the flex-basis / flex-grow /
82
+ * flex-shrink combination depending on browser, leaving the row
83
+ * tint cut off at the text width when the user scrolled
84
+ * horizontally. `white-space: nowrap` keeps the inline-block
85
+ * children on a single row (the children themselves preserve their
86
+ * own `white-space: pre` for the code). `min-width: max-content`
87
+ * keeps the line at least as wide as its content (full row tint
88
+ * when content overflows the visible viewport); default width =
89
+ * 100% of pre keeps it filling the visible width on short rows. */
38
90
  .rf-diff__line {
91
+ /* `display: flex` for internal layout (gutters + content side
92
+ * by side). The line's WIDTH is determined by the parent grid
93
+ * track (`.rf-diff__code`'s `grid-template-columns:
94
+ * minmax(100%, max-content)`), not by the flex sizing here —
95
+ * so the row tint covers the full grid column regardless of
96
+ * how flex sizing would have resolved. */
39
97
  display: flex;
40
98
  min-height: 1.6em;
41
99
  border-left: 3px solid transparent;
42
100
  }
43
- .rf-diff__line[data-type="equal"] { background: transparent; }
44
- .rf-diff__line[data-type="remove"] {
101
+ .rf-diff__line[data-line-status="equal"] { background: transparent; }
102
+ .rf-diff__line[data-line-status="remove"] {
45
103
  background: color-mix(in srgb, var(--rf-color-danger) 18%, transparent);
46
104
  border-left-color: var(--rf-color-danger);
47
105
  }
48
- .rf-diff__line[data-type="add"] {
106
+ .rf-diff__line[data-line-status="add"] {
49
107
  background: color-mix(in srgb, var(--rf-color-success) 18%, transparent);
50
108
  border-left-color: var(--rf-color-success);
51
109
  }
52
- .rf-diff__line[data-type="empty"] { background: transparent; }
110
+ .rf-diff__line[data-line-status="highlight"] {
111
+ background: var(--rf-color-line-highlight);
112
+ border-left-color: var(--rf-color-line-highlight-rail, var(--rf-color-primary));
113
+ }
114
+ .rf-diff__line[data-line-status="empty"] { background: transparent; }
53
115
  .rf-diff__gutter-num {
54
116
  flex-shrink: 0;
55
117
  padding: 0 0.5rem;
@@ -65,17 +127,23 @@
65
127
  * Remove lines colour the before-number; add lines colour the after-number.
66
128
  * The opposite side renders empty (no number) which itself signals "nothing
67
129
  * here on this side". */
68
- .rf-diff__line[data-type="remove"] .rf-diff__gutter-num[data-side="before"] {
130
+ .rf-diff__line[data-line-status="remove"] .rf-diff__gutter-num[data-side="before"] {
69
131
  color: var(--rf-color-danger);
70
132
  }
71
- .rf-diff__line[data-type="add"] .rf-diff__gutter-num[data-side="after"] {
133
+ .rf-diff__line[data-line-status="add"] .rf-diff__gutter-num[data-side="after"] {
72
134
  color: var(--rf-color-success);
73
135
  }
74
136
  .rf-diff .rf-codeblock {
75
137
  margin: 0;
76
138
  }
77
139
  .rf-diff__line-content {
78
- flex: 1;
140
+ /* `flex: 1 0 auto` so line-content contributes its
141
+ * `white-space: pre` text width to the line's intrinsic
142
+ * max-content (which the parent grid track reads from); grow:1
143
+ * lets it fill any free space in the row beyond the gutters
144
+ * when the grid track is wider than the line's natural size
145
+ * (short content / wide visible viewport). WORK-304. */
146
+ flex: 1 0 auto;
79
147
  white-space: pre;
80
148
  padding-right: 1rem;
81
149
  /* Pin the row to 1.6em even when the text is empty — the highlighted
@@ -70,8 +70,26 @@ section.rf-drawer .rf-drawer__title {
70
70
  section.rf-drawer .rf-drawer__body > :first-child { margin-top: 0; }
71
71
  section.rf-drawer .rf-drawer__body > :last-child { margin-bottom: 0; }
72
72
 
73
+ /* In-flow footer — small text, top divider, mirrors the dialog-mode look so
74
+ * authors get the same chrome whether JS is enhancing or not. */
75
+ section.rf-drawer .rf-drawer__footer {
76
+ display: block;
77
+ margin-top: var(--rf-spacing-md);
78
+ padding-top: var(--rf-spacing-sm);
79
+ border-top: 1px solid var(--rf-drawer-border);
80
+ font-size: 0.875rem;
81
+ color: var(--rf-color-muted);
82
+ }
83
+ section.rf-drawer .rf-drawer__footer > :first-child { margin-top: 0; }
84
+ section.rf-drawer .rf-drawer__footer > :last-child { margin-bottom: 0; }
85
+
73
86
  /* ─── JS / dialog mode ─────────────────────────────────────────── */
74
87
 
88
+ /* Dialog base. Crucially the `display: flex` rule below is scoped to the
89
+ * `[open]` state — without that scope, declaring `display: flex` on the
90
+ * bare `dialog.rf-drawer` overrides the browser UA's `display: none` for
91
+ * closed dialogs, so the closed dialog renders visibly as an empty box
92
+ * positioned by the side `inset` rules. */
75
93
  dialog.rf-drawer {
76
94
  box-sizing: border-box;
77
95
  background: var(--rf-drawer-bg);
@@ -81,28 +99,36 @@ dialog.rf-drawer {
81
99
  box-shadow: var(--rf-drawer-shadow);
82
100
  padding: 0;
83
101
  margin: 0;
84
- overflow: auto;
102
+ overflow: hidden;
85
103
  max-width: calc(100vw - 2 * var(--rf-drawer-gutter));
86
104
  max-height: calc(100vh - 2 * var(--rf-drawer-gutter));
87
105
  }
88
106
 
107
+ /* Flex column layout only when the dialog is actually open. The header
108
+ * and footer pin via `flex: 0 0 auto`; the body scrolls via `flex: 1 1
109
+ * auto; overflow-y: auto`. The dialog's `max-height` cap on the dialog
110
+ * provides the scroll context. */
111
+ dialog.rf-drawer[open] {
112
+ display: flex;
113
+ flex-direction: column;
114
+ }
115
+
89
116
  dialog.rf-drawer::backdrop {
90
117
  background: rgb(0 0 0 / 0.35);
91
118
  backdrop-filter: blur(2px);
92
119
  }
93
120
 
94
121
  dialog.rf-drawer .rf-drawer__header {
122
+ flex: 0 0 auto;
95
123
  display: flex;
96
124
  align-items: center;
97
125
  justify-content: space-between;
98
126
  gap: var(--rf-spacing-md);
99
127
  padding: var(--rf-spacing-md) var(--rf-spacing-lg);
100
128
  border-bottom: 1px solid var(--rf-drawer-border);
101
- position: sticky;
102
- top: 0;
103
129
  background: var(--rf-drawer-bg);
104
- z-index: 1;
105
- }dialog.rf-drawer .rf-drawer__title {
130
+ }
131
+ dialog.rf-drawer .rf-drawer__title {
106
132
  margin: 0;
107
133
  font-size: 1.05rem;
108
134
  font-weight: 600;
@@ -126,8 +152,26 @@ dialog.rf-drawer .rf-drawer__close:hover {
126
152
  }
127
153
 
128
154
  dialog.rf-drawer .rf-drawer__body {
155
+ flex: 1 1 auto;
156
+ overflow-y: auto;
129
157
  padding: var(--rf-spacing-md) var(--rf-spacing-lg);
158
+ min-height: 0;
159
+ }
160
+
161
+ /* Dialog footer — small text, top divider, pinned at bottom via flex.
162
+ * Stays visible regardless of body scroll depth so links like "View
163
+ * source on GitHub →" (the SPEC-078 use case) are always one tap away. */
164
+ dialog.rf-drawer .rf-drawer__footer {
165
+ flex: 0 0 auto;
166
+ padding: var(--rf-spacing-sm) var(--rf-spacing-lg);
167
+ border-top: 1px solid var(--rf-drawer-border);
168
+ background: var(--rf-drawer-bg);
169
+ font-size: 0.875rem;
170
+ color: var(--rf-color-muted);
130
171
  }
172
+ dialog.rf-drawer .rf-drawer__footer > :first-child { margin-top: 0; }
173
+ dialog.rf-drawer .rf-drawer__footer > :last-child { margin-bottom: 0; }
174
+ dialog.rf-drawer .rf-drawer__footer p { margin: 0; }
131
175
 
132
176
  /* Collapse the vertical margins of first / last children inside the body
133
177
  * so the drawer's own padding is what spaces content from the chrome —
@@ -282,7 +326,8 @@ dialog.rf-drawer[data-state="closing"]::backdrop {
282
326
  */
283
327
  @media (max-width: 640px) {
284
328
  dialog.rf-drawer .rf-drawer__header,
285
- dialog.rf-drawer .rf-drawer__body {
329
+ dialog.rf-drawer .rf-drawer__body,
330
+ dialog.rf-drawer .rf-drawer__footer {
286
331
  padding: 0.75rem 1rem;
287
332
  }
288
333
  }
@@ -0,0 +1,31 @@
1
+ /* file-ref — path-based inline reference to a project file (SPEC-078).
2
+ *
3
+ * Renders as `<span class="rf-file-ref"><a>label</a></span>` in prose
4
+ * (plus an invisible hoist sentinel `<meta>` when preview="drawer").
5
+ * The span carries the data-rune class; the inner `<a>` is the click
6
+ * target with its own visual treatment.
7
+ *
8
+ * Without preview, the link points at the canonical GitHub URL — give
9
+ * it a small external-link affordance so readers know they're about to
10
+ * leave the page. With preview, it points at the hoisted drawer's
11
+ * fragment id; visually the same inline-link affordance — what makes
12
+ * it a "preview link" rather than a "navigate-away link" is the drawer
13
+ * that opens on click, not extra inline chrome.
14
+ */
15
+
16
+ .rf-file-ref {
17
+ display: inline;
18
+ }
19
+
20
+ .rf-file-ref > a {
21
+ color: var(--rf-color-primary);
22
+ text-decoration: underline;
23
+ text-decoration-thickness: 1px;
24
+ text-underline-offset: 2px;
25
+ font-family: var(--rf-font-mono, ui-monospace, SFMono-Regular, monospace);
26
+ font-size: 0.9375em;
27
+ }
28
+
29
+ .rf-file-ref > a:hover {
30
+ text-decoration-thickness: 2px;
31
+ }
package/tokens/base.css CHANGED
@@ -52,6 +52,20 @@
52
52
  --rf-color-success-bg: #e0eee4;
53
53
  --rf-color-success-border: #b8d4be;
54
54
 
55
+ /* WORK-304 — neutral tint for line highlight (the third
56
+ * `[data-line-status]` value that snippet / codegroup / diff share).
57
+ * Subtle surface tint so highlighted rows read as emphasized but not
58
+ * status-coloured (which would compete with diff's add/remove channel
59
+ * and the semantic palette above). `*-rail` is the optional left-edge
60
+ * border colour; falls back to the primary accent via the consuming
61
+ * CSS for themes that don't override it. */
62
+ --rf-color-line-highlight: color-mix(in srgb, var(--rf-color-text) 6%, transparent);
63
+ --rf-color-line-highlight-rail: var(--rf-color-primary, var(--rf-color-text));
64
+ /* Gutter colour for `pre[data-linenumbers]` line counter. Defers to
65
+ * the existing muted text token by default; themes can pin a
66
+ * dedicated hue without overriding muted. */
67
+ --rf-color-line-number: var(--rf-color-muted);
68
+
55
69
  /* Radii */
56
70
  --rf-radius-sm: 6px;
57
71
  --rf-radius-md: 10px;