@smwb/ui-styles 2.5.0 → 2.7.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/README.md CHANGED
@@ -117,6 +117,23 @@ value for both modes, so a theme that makes `@warningColor` lighter, or any of t
117
117
  changes the numbers: re-check them there. `color="secondary"` is the one name that falls short in the default dark
118
118
  theme (2.65:1): use a status for state.
119
119
 
120
+ #### IdentityBadge colours
121
+
122
+ `IdentityBadge` is a frame with no fill, so its colour is a border, an icon and 11px of text. It reads one custom
123
+ property, `--smwb-identity-badge-accent`, which the component sets from its `accent` prop; without it (or with an empty
124
+ one) the badge resolves `--smwb-identity-badge-color` to `@identityBadgeColor`, `var(--smui-onSurfaceVariant)` —
125
+ neutral, following the dark theme. No status token (`infoColor`, `successColor`, `warningColor`, `errorColor`) appears
126
+ in the badge's rules: the marker must not read as a state the way `StatusDot` does. An accent is any CSS colour, so its
127
+ contrast is the caller's problem: at `@identityBadgeFontSize` (11px) pick one that reaches 4.5:1 against the surface it
128
+ sits on.
129
+
130
+ The box comes from `@identityBadgeHeight` (18px), `@identityBadgePaddingX` (4px), `@identityBadgeGap` (2px, icon to
131
+ text), `@identityBadgeIconSize` (12px), `@identityBadgeBorderWidth` and `@identityBadgeRadius`; the text from
132
+ `@identityBadgeFontFamily` (a monospace stack), `@identityBadgeFontSize` and `@identityBadgeFontWeight`; the row of a
133
+ group from `@identityBadgeGroupGap` (4px). All are `theme.config` variables, and the colour is the only one that reads a
134
+ `--smui-*` runtime token. Under forced colours the frame and the text draw in `CanvasText`, and a clickable "+N" in
135
+ `ButtonText`.
136
+
120
137
  #### QrCode colours
121
138
 
122
139
  `QrCode` never reads theme tokens for the code itself: the modules are always `@qrCodeDarkColor`
@@ -143,8 +160,33 @@ runtime tokens, so the dark theme follows without extra settings:
143
160
  | `@dagViewSelectedRing` | `--smwb-dag-view-selected-ring` | `var(--smui-primaryColor)` |
144
161
  | `@dagViewStatusIdle` … `@dagViewStatusSkipped` | `--smwb-dag-view-status-idle` … `-skipped` | outline, on-surface variant, info, success, warning, error, disabled text |
145
162
  | `@dagViewDimOpacity` | `--smwb-dag-view-dim-opacity` | `0.35` |
163
+ | `@dagViewEdgeFlowColor` | `--smwb-dag-view-edge-flow-color` | `var(--smwb-dag-view-status-running)` |
164
+ | `@dagViewEdgeFlowTrackColor` | `--smwb-dag-view-edge-flow-track-color` | the flow colour mixed 45% with transparent |
165
+ | `@dagViewNodeGlowColor` | `--smwb-dag-view-node-glow-color` | `false`: the node's own accent |
166
+ | `@dagViewEdgeFlowWidth` | `--smwb-dag-view-edge-flow-width` | `2.5px` |
167
+ | `@dagViewEdgeFlowDuration` | `--smwb-dag-view-edge-flow-duration` | `0.6s` (one 24px dash period) |
168
+ | `@dagViewEdgeGlowOpacity` | `--smwb-dag-view-edge-glow-opacity` | `0.25` |
169
+ | `@dagViewNodeGlowOpacity` | `--smwb-dag-view-node-glow-opacity` | `0.6` (blur strength of the running node's inner glow) |
170
+ | `@dagViewNodePulseDuration` | `--smwb-dag-view-node-pulse-duration` | `1.4s` (the running node's dot and frame) |
146
171
  | `@dagViewHeight` | — (the graph's `height`) | `480px` |
147
172
 
173
+ The flow colours (`@dagViewEdgeFlowColor`, `@dagViewEdgeFlowTrackColor`, `@dagViewNodeGlowColor`) are colours and have
174
+ `…Dark` twins, `false` by default; the width, durations and opacities are not colours and have none. The pulses' dash
175
+ pattern (`@dagViewEdgeFlowDash` / `@dagViewEdgeFlowGap`, `6px` / `18px`) is compile time only: the flow keyframes move
176
+ exactly one period, so `dash + gap` must stay the keyframe's stride.
177
+
178
+ An edge the app marks `active` shows work flowing along it (`DagViewEdge.active`, `@smwb/ui-solid`): the edge lights in
179
+ the flow colour over a paler track, a static glow lies under it and pulses run from `source` to `target`; a running
180
+ node's frame glows and pulses with its dot. This `active` is not the selection highlight — the selected node's edges
181
+ keep `@dagViewEdgeActiveColor`, and where both apply the flowing edge wins the colour and the arrow. All flow rules and
182
+ the node glow are gated on markup only `@smwb/ui-solid` writes (`data-animate-running` on the root, the flowing
183
+ classes on the edge paths), so the shared CSS is inert for `@smwb/ui-react` until its own issue lands; the only
184
+ change React sees is the new custom properties on its root and the dot's duration read from
185
+ `--smwb-dag-view-node-pulse-duration`, whose default is the 1.4 s of before. Motion is CSS only: with
186
+ `prefers-reduced-motion: reduce` the pulses stay hidden, the flowing edge stays lit in the full flow colour, and a
187
+ running node keeps a still glow; `animateRunning={false}` stops a running node's glow and dot pulse without reduced
188
+ motion.
189
+
148
190
  The statuses are `idle`, `pending`, `running`, `success`, `warning`, `error` and `skipped`; a node with any other
149
191
  status is drawn as `idle`. Each colour variable has a `…Dark` twin (e.g. `@dagViewStatusSuccessDark`), `false` by
150
192
  default: set it to give the dark theme its own value. At runtime, set the properties on the graph itself, since it
@@ -179,4 +179,99 @@
179
179
  .smui-chip-color(success; var(--smui-successColor); var(--smui-onSuccessColor));
180
180
  .smui-chip-color(warning; var(--smui-warningColor); var(--smui-onWarningColor));
181
181
  .smui-chip-color(info; var(--smui-infoColor); var(--smui-onInfoColor));
182
+
183
+ // A custom color (Chip's customColor prop) repaints the chip like the palette
184
+ // colors above, from custom properties the component sets inline on the root
185
+ // (internal: apps neither set nor target them):
186
+ // - --smwb-chip-custom-color: the color to draw, any CSS color (a translucent
187
+ // one the component parses comes composited over the page background);
188
+ // - --smwb-chip-custom-fill / --smwb-chip-custom-on-color: the same color and
189
+ // its black-or-white label, for the colors the component could measure;
190
+ // - --smwb-chip-custom-text-color: the caller's label color, when given.
191
+ // The guard mirrors the palette mixin's: the color class outranks the neutral
192
+ // rules, a selected filled chip keeps its fill, and a disabled chip keeps the
193
+ // disabled look. An outlined chip keeps the theme's label color: an arbitrary
194
+ // color as text on the page fails contrast on one of the themes.
195
+ &.smwb-chip_custom-color:not(&.smwb-disabled) {
196
+ // Without relative color syntax (below) only measured colors are painted;
197
+ // the others draw as the plain chip rather than under a guessed label color.
198
+ // The fill stays put on hover / active: no filter, it would dim the label.
199
+ &.smwb-filled {
200
+ &,
201
+ &:hover,
202
+ &:active,
203
+ &.smwb-selected {
204
+ background-color: var(--smwb-chip-custom-fill, var(--smui-chipsBg));
205
+ }
206
+ color: var(--smwb-chip-custom-text-color, var(--smwb-chip-custom-on-color, var(--smui-primaryTextColor)));
207
+ // The delete control follows the label color, so it reads on any fill;
208
+ // hovered, it gets a round tint of that color.
209
+ .smwb-chip__delete-icon {
210
+ border-radius: 50%;
211
+ svg {
212
+ &,
213
+ &:hover {
214
+ fill: currentColor;
215
+ }
216
+ }
217
+ &:hover {
218
+ background-color: color-mix(in srgb, currentColor 16%, transparent);
219
+ }
220
+ }
221
+ }
222
+ &.smwb-outlined {
223
+ color: var(--smwb-chip-custom-text-color, var(--smui-primaryTextColor));
224
+ border-color: var(--smwb-chip-custom-color);
225
+ &:hover {
226
+ background-color: color-mix(in srgb, var(--smwb-chip-custom-color) 12%, transparent);
227
+ }
228
+ &:active,
229
+ &.smwb-selected {
230
+ background-color: color-mix(in srgb, var(--smwb-chip-custom-color) 20%, transparent);
231
+ }
232
+ // A caller's label color is the delete control's too.
233
+ .smwb-chip__delete-icon svg {
234
+ fill: var(--smwb-chip-custom-text-color, var(--smui-chipsIconColor));
235
+ &:hover {
236
+ fill: var(--smwb-chip-custom-text-color, var(--smui-chipsIconColorHover));
237
+ }
238
+ }
239
+ }
240
+ }
241
+
242
+ // With relative color syntax the stylesheet picks the label itself, from the
243
+ // color as the browser understands it, so any valid CSS color is painted:
244
+ // - solid: the color made opaque (a translucent one the component could not
245
+ // parse is drawn opaque, so the label is picked against what is shown);
246
+ // - pick: white while the fill's WCAG relative luminance is <= 0.17913, where
247
+ // black and white contrast equally, black above: >= 4.58:1 either way;
248
+ // - label: the caller's text color, or the pick;
249
+ // - away: the same pick made for the label, i.e. black for a light label and
250
+ // white for a dark one. Hover / active mix it into the fill, which moves the
251
+ // fill away from the label: the contrast only grows.
252
+ // A value the browser cannot read makes these invalid at computed-value time:
253
+ // the fill is then transparent and the label inherits the surrounding text
254
+ // color, a readable plain chip.
255
+ @supports (color: color(from red srgb r g b)) {
256
+ @pick-channel: ~"clamp(0, (0.17913 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * 100000, 1)";
257
+ &.smwb-chip_custom-color:not(&.smwb-disabled) {
258
+ --smwb-chip-custom-solid: ~"color(from var(--smwb-chip-custom-color) srgb r g b / 1)";
259
+ --smwb-chip-custom-pick: ~"color(from var(--smwb-chip-custom-solid) srgb-linear @{pick-channel} @{pick-channel} @{pick-channel} / 1)";
260
+ --smwb-chip-custom-label: var(--smwb-chip-custom-text-color, var(--smwb-chip-custom-pick));
261
+ --smwb-chip-custom-away: ~"color(from var(--smwb-chip-custom-label) srgb-linear @{pick-channel} @{pick-channel} @{pick-channel} / 1)";
262
+ &.smwb-filled {
263
+ &,
264
+ &.smwb-selected {
265
+ background-color: var(--smwb-chip-custom-solid);
266
+ }
267
+ &:hover {
268
+ background-color: color-mix(in srgb, var(--smwb-chip-custom-solid), var(--smwb-chip-custom-away) 8%);
269
+ }
270
+ &:active {
271
+ background-color: color-mix(in srgb, var(--smwb-chip-custom-solid), var(--smwb-chip-custom-away) 14%);
272
+ }
273
+ color: var(--smwb-chip-custom-label);
274
+ }
275
+ }
276
+ }
182
277
  }
@@ -21,6 +21,9 @@
21
21
  .smwb-dag-view-token(status-warning; StatusWarning; @suffix);
22
22
  .smwb-dag-view-token(status-error; StatusError; @suffix);
23
23
  .smwb-dag-view-token(status-skipped; StatusSkipped; @suffix);
24
+ .smwb-dag-view-token(edge-flow-color; EdgeFlowColor; @suffix);
25
+ .smwb-dag-view-token(edge-flow-track-color; EdgeFlowTrackColor; @suffix);
26
+ .smwb-dag-view-token(node-glow-color; NodeGlowColor; @suffix);
24
27
  }
25
28
  // The line style of the node frame, theme-wide and per built-in status. The status
26
29
  // ones are `false` by default: they emit nothing, so the node falls back to the
@@ -48,6 +51,11 @@
48
51
  .smwb-dag-view {
49
52
  .smwb-dag-view-tokens("");
50
53
  --smwb-dag-view-dim-opacity: @dagViewDimOpacity;
54
+ --smwb-dag-view-edge-flow-width: @dagViewEdgeFlowWidth;
55
+ --smwb-dag-view-edge-flow-duration: @dagViewEdgeFlowDuration;
56
+ --smwb-dag-view-edge-glow-opacity: @dagViewEdgeGlowOpacity;
57
+ --smwb-dag-view-node-glow-opacity: @dagViewNodeGlowOpacity;
58
+ --smwb-dag-view-node-pulse-duration: @dagViewNodePulseDuration;
51
59
  --smwb-dag-view-node-border-style: @dagViewNodeBorderStyle;
52
60
  .smwb-dag-view-border-style(pending; Pending);
53
61
  .smwb-dag-view-border-style(running; Running);
@@ -132,6 +140,62 @@
132
140
  fill: var(--smwb-dag-view-edge-active-color);
133
141
  }
134
142
 
143
+ // An edge the app marks `active`: work flows along it. The track is lit, a
144
+ // static glow lies under it and pulses run over it from source to target
145
+ // (the pulses only move, and only show, without reduced motion: see the end).
146
+ &__edge--flowing {
147
+ stroke: var(--smwb-dag-view-edge-flow-color);
148
+ stroke-width: var(--smwb-dag-view-edge-flow-width);
149
+ }
150
+
151
+ &__arrow--flow {
152
+ fill: var(--smwb-dag-view-edge-flow-color);
153
+ }
154
+
155
+ &__edge-glow {
156
+ fill: none;
157
+ stroke: var(--smwb-dag-view-edge-flow-color);
158
+ stroke-width: calc(var(--smwb-dag-view-edge-flow-width) * 3);
159
+ stroke-linecap: round;
160
+ stroke-opacity: var(--smwb-dag-view-edge-glow-opacity);
161
+ transition: opacity 0.12s;
162
+ }
163
+
164
+ &__edge-pulse {
165
+ display: none;
166
+ fill: none;
167
+ stroke: var(--smwb-dag-view-edge-flow-color);
168
+ stroke-width: calc(var(--smwb-dag-view-edge-flow-width) + 1px);
169
+ stroke-linecap: round;
170
+ stroke-dasharray: @dagViewEdgeFlowDash @dagViewEdgeFlowGap;
171
+ transition: opacity 0.12s;
172
+ }
173
+
174
+ &__edge-glow--dimmed,
175
+ &__edge-pulse--dimmed {
176
+ opacity: var(--smwb-dag-view-dim-opacity);
177
+ }
178
+
179
+ // A running node's frame glows from inside, in its accent (the node clips its
180
+ // overflow, so the glow cannot go outside). Only where the graph asks for it:
181
+ // the attribute is written by @smwb/ui-solid.
182
+ &[data-animate-running="true"] &__node[data-status="running"]::before {
183
+ content: "";
184
+ position: absolute;
185
+ inset: 0;
186
+ z-index: -1;
187
+ pointer-events: none;
188
+ @glow: ~"var(--smwb-dag-view-node-glow-color, var(--smwb-dag-view-accent))";
189
+ box-shadow:
190
+ inset 0 0 0 1px @glow,
191
+ inset 0 0 12px 2px ~"color-mix(in srgb, @{glow} calc(var(--smwb-dag-view-node-glow-opacity) * 100%), transparent)";
192
+ }
193
+
194
+ // animateRunning={false}: running nodes stay still, the dot too.
195
+ &[data-animate-running="false"] &__node[data-status="running"] &__dot {
196
+ animation: none;
197
+ }
198
+
135
199
  &__node {
136
200
  --smwb-dag-view-accent: var(--smwb-dag-view-node-accent, var(--smwb-dag-view-status-idle));
137
201
  .smwb-dag-view-status(pending);
@@ -304,10 +368,23 @@
304
368
  .smwb-dag-view-tokens("Dark");
305
369
  }
306
370
 
307
- // A running node's dot pulses, unless motion is reduced.
371
+ // Motion only when the user has not asked for less: a running node's dot and
372
+ // frame pulse, and pulses run along the active edges over a paler track.
373
+ // With reduced motion the edges stay lit in the full flow colour and the
374
+ // running node keeps a still glow.
308
375
  @media (prefers-reduced-motion: no-preference) {
309
376
  .smwb-dag-view__node[data-status="running"] .smwb-dag-view__dot {
310
- animation: smwb-dag-view-pulse 1.4s ease-in-out infinite;
377
+ animation: smwb-dag-view-pulse var(--smwb-dag-view-node-pulse-duration) ease-in-out infinite;
378
+ }
379
+ .smwb-dag-view[data-animate-running="true"] .smwb-dag-view__node[data-status="running"]::before {
380
+ animation: smwb-dag-view-pulse var(--smwb-dag-view-node-pulse-duration) ease-in-out infinite;
381
+ }
382
+ .smwb-dag-view__edge--flowing {
383
+ stroke: var(--smwb-dag-view-edge-flow-track-color);
384
+ }
385
+ .smwb-dag-view__edge-pulse {
386
+ display: inline;
387
+ animation: smwb-dag-view-flow var(--smwb-dag-view-edge-flow-duration) linear infinite;
311
388
  }
312
389
  }
313
390
 
@@ -316,3 +393,12 @@
316
393
  opacity: 0.35;
317
394
  }
318
395
  }
396
+
397
+ // One dash period along the path: a negative offset moves the dashes from the
398
+ // path's start (the source) toward its end (the target).
399
+ @dagViewEdgeFlowPeriod: (@dagViewEdgeFlowDash + @dagViewEdgeFlowGap);
400
+ @keyframes smwb-dag-view-flow {
401
+ to {
402
+ stroke-dashoffset: -@dagViewEdgeFlowPeriod;
403
+ }
404
+ }
@@ -71,12 +71,22 @@
71
71
  width: 100%;
72
72
  box-sizing: border-box;
73
73
 
74
+ // The class display beats the UA `[hidden]` rule, so it is pinned explicitly.
75
+ &[hidden] {
76
+ display: none;
77
+ }
78
+
74
79
  &__row {
75
80
  display: flex;
76
81
  flex-wrap: wrap;
77
82
  width: 100%;
78
83
  box-sizing: border-box;
79
84
 
85
+ // Same as on the grid: `hidden` on the node wins without app CSS.
86
+ &[hidden] {
87
+ display: none;
88
+ }
89
+
80
90
  .generate-spacing-class-count(spacing);
81
91
 
82
92
  &--nowrap {
@@ -0,0 +1,2 @@
1
+ @import (reference) "../../_prelude.less";
2
+ @import "./identityBadge.less";
@@ -0,0 +1,110 @@
1
+ // A compact identity marker: an outlined frame with no fill, a glyph on the left and a
2
+ // short monospace text on the right. It must not read as a status, so its colour is the
3
+ // caller's accent or the theme's neutral — never a status token — and the frame, not a
4
+ // fill, carries it. One size, one look: no modifier classes.
5
+ .smwb-identity-badge {
6
+ --smwb-identity-badge-color: var(--smwb-identity-badge-accent, @identityBadgeColor);
7
+ display: inline-flex;
8
+ align-items: center;
9
+ gap: @identityBadgeGap;
10
+ flex: none;
11
+ box-sizing: border-box;
12
+ height: @identityBadgeHeight;
13
+ padding: 0 @identityBadgePaddingX;
14
+ vertical-align: middle;
15
+ white-space: nowrap;
16
+ border: @identityBadgeBorderWidth solid var(--smwb-identity-badge-color);
17
+ border-radius: @identityBadgeRadius;
18
+ background-color: transparent;
19
+ color: var(--smwb-identity-badge-color);
20
+ font-family: @identityBadgeFontFamily;
21
+ font-size: @identityBadgeFontSize;
22
+ font-weight: @identityBadgeFontWeight;
23
+ line-height: 1;
24
+
25
+ &__icon {
26
+ display: inline-flex;
27
+ flex: none;
28
+ align-items: center;
29
+ justify-content: center;
30
+ width: @identityBadgeIconSize;
31
+ height: @identityBadgeIconSize;
32
+ overflow: hidden;
33
+
34
+ // The caller picks the icon's own size, so the box clamps it: an <Icon size={20}> is scaled
35
+ // into the 12px slot instead of stretching the badge's fixed height. The Icon's wrapper
36
+ // takes the whole box, so that its glyph has a definite size to be clamped to.
37
+ > .smwb-icon__wrapper {
38
+ width: 100%;
39
+ height: 100%;
40
+ }
41
+
42
+ svg,
43
+ img {
44
+ max-width: 100%;
45
+ max-height: 100%;
46
+ }
47
+
48
+ // A font glyph sizes itself with an inline font-size, which only !important can take over.
49
+ .material-symbols-outlined {
50
+ font-size: @identityBadgeIconSize !important;
51
+ }
52
+ }
53
+ }
54
+
55
+ .smwb-identity-badge-group {
56
+ display: inline-flex;
57
+ align-items: center;
58
+ gap: @identityBadgeGroupGap;
59
+ vertical-align: middle;
60
+
61
+ // The children past `max` are drawn for hydration and hidden: a server that left them
62
+ // out would leave the browser without nodes to hydrate them from.
63
+ &__hidden {
64
+ display: none;
65
+ }
66
+
67
+ // A clickable "+N" is a real button, so a keyboard reaches it. normalize.css strips a
68
+ // button's looks and its outline, so both come back here.
69
+ button&__surplus {
70
+ position: relative; // holds the coarse-pointer hit area
71
+ margin: 0;
72
+ appearance: none;
73
+ background-color: transparent;
74
+ cursor: pointer;
75
+
76
+ &:focus-visible {
77
+ outline: 2px solid var(--smui-primaryColor);
78
+ outline-offset: 2px;
79
+ }
80
+
81
+ // A finger needs a 44px target and the badge must keep its 18px row, so the hit area
82
+ // hangs off a pseudo-element, centred over the button: the row does not grow
83
+ // (SelectField's chip-delete precedent).
84
+ @media (pointer: coarse) {
85
+ &::after {
86
+ content: "";
87
+ position: absolute;
88
+ top: 50%;
89
+ left: 50%;
90
+ width: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
91
+ height: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
92
+ transform: translate(-50%, -50%);
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ // Forced colours drop the accent: draw the frame and the text in the text colour, and a
99
+ // clickable "+N" in the button colour.
100
+ @media (forced-colors: active) {
101
+ .smwb-identity-badge {
102
+ border-color: CanvasText;
103
+ color: CanvasText;
104
+ }
105
+
106
+ button.smwb-identity-badge-group__surplus {
107
+ border-color: ButtonText;
108
+ color: ButtonText;
109
+ }
110
+ }
@@ -11,6 +11,16 @@
11
11
  }
12
12
 
13
13
  .smwb-stack {
14
+ // Display lives in the class, not the inline style: an inline display would
15
+ // beat the UA `[hidden]` rule and keep a mounted, hidden Stack visible.
16
+ display: flex;
17
+
18
+ // The UA `[hidden]` rule loses to the class display above, so it is pinned
19
+ // explicitly — `hidden` on the node wins with no app CSS and no !important.
20
+ &[hidden] {
21
+ display: none;
22
+ }
23
+
14
24
  // layout is driven by inline flex props; this hook is for overrides
15
25
  min-width: 0;
16
26
 
@@ -22,6 +22,11 @@
22
22
  align-items: center;
23
23
  justify-content: center;
24
24
  cursor: pointer;
25
+ // Let the target shrink below its content width inside a fixed-width parent,
26
+ // so noWrap content (Typography noWrap) is truncated with an ellipsis instead
27
+ // of overflowing the parent.
28
+ min-width: 0;
29
+ max-width: 100%;
25
30
  }
26
31
 
27
32
  &__wrapper {
@@ -29,6 +34,11 @@
29
34
  display: inline-flex;
30
35
  align-items: center;
31
36
  justify-content: center;
37
+ // Without it the inline-flex wrapper cannot shrink below the content width
38
+ // (the flex automatic minimum size), so a long noWrap label overflows a
39
+ // fixed-width parent instead of truncating.
40
+ min-width: 0;
41
+ max-width: 100%;
32
42
  }
33
43
  }
34
44
 
@@ -15,6 +15,7 @@
15
15
  @import "./components/meter/meter.less";
16
16
  @import "./components/diffStat/diffStat.less";
17
17
  @import "./components/statusDot/statusDot.less";
18
+ @import "./components/identityBadge/identityBadge.less";
18
19
  @import "./components/qrCode/qrCode.less";
19
20
  @import "./components/radioButton/radioButton.less";
20
21
  @import "./components/sidebar/sidebar.less";
@@ -204,6 +204,20 @@
204
204
  @statusDotDiamondRotation: 45deg; // corner-up, the diamond of the onreel thread marker
205
205
  @statusDotDiamondRadius: @smui-radius-none; // sharp corners; the dot's are a circle
206
206
 
207
+ // A compact identity marker sits inside a dense row, so its height is off-grid and below
208
+ // the control scale on purpose: one 14px line at the DagView node's line-height 1.3.
209
+ @identityBadgeHeight: 18px;
210
+ @identityBadgePaddingX: @smui-space-xs; // 4px
211
+ @identityBadgeGap: @smui-space-xxs; // 2px, icon to text
212
+ @identityBadgeIconSize: 12px; // @smui-space-3
213
+ @identityBadgeBorderWidth: 1px;
214
+ @identityBadgeRadius: @smui-radius-sm;
215
+ @identityBadgeFontFamily: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
216
+ @identityBadgeFontSize: 11px; // off-grid
217
+ @identityBadgeFontWeight: 500;
218
+ @identityBadgeColor: ~"var(--smui-onSurfaceVariant)"; // neutral, follows the dark theme; never a status token
219
+ @identityBadgeGroupGap: @smui-space-xs; // 4px between badges
220
+
207
221
  // A QR code is always dark modules on a white square — never theme tokens, so no
208
222
  // theme (or forced-colours mode, see the component's print/forced-color-adjust) can
209
223
  // invert it.
@@ -589,6 +603,21 @@
589
603
  @dagViewStatusSkippedBorderStyle: false;
590
604
  // How far the nodes and edges away from the selection fade, with dimUnrelated.
591
605
  @dagViewDimOpacity: 0.35;
606
+ // An edge the app marks `active`: work flows along it. The flow colour defaults
607
+ // to the running status colour, so the dark theme follows on its own.
608
+ @dagViewEdgeFlowColor: ~"var(--smwb-dag-view-status-running)";
609
+ // The flowing edge's track: the flow colour mixed 45% with transparent.
610
+ @dagViewEdgeFlowTrackColor: ~"color-mix(in srgb, var(--smwb-dag-view-edge-flow-color) 45%, transparent)";
611
+ // A running node's inner glow colour; `false`: the node's own accent.
612
+ @dagViewNodeGlowColor: false;
613
+ @dagViewEdgeFlowWidth: 2.5px;
614
+ @dagViewEdgeFlowDuration: 0.6s;
615
+ @dagViewEdgeGlowOpacity: 0.25;
616
+ @dagViewNodeGlowOpacity: 0.6;
617
+ @dagViewNodePulseDuration: 1.4s;
618
+ // The pulses' dash pattern, compile time only: the flow keyframes move one period.
619
+ @dagViewEdgeFlowDash: 6px;
620
+ @dagViewEdgeFlowGap: 18px;
592
621
  // The graph's height when the app sets none (its `height` prop or `style`).
593
622
  @dagViewHeight: 480px;
594
623
 
@@ -817,3 +846,7 @@
817
846
  @dagViewStatusWarningDark: false;
818
847
  @dagViewStatusErrorDark: false;
819
848
  @dagViewStatusSkippedDark: false;
849
+ // Dark-only values of the flow colours; `false` = not set: the light value (a runtime token) applies.
850
+ @dagViewEdgeFlowColorDark: false;
851
+ @dagViewEdgeFlowTrackColorDark: false;
852
+ @dagViewNodeGlowColorDark: false;
@@ -35,6 +35,8 @@
35
35
  // - ProgressIndicator: indicator diameter (28px), not a control box
36
36
  // - StatusDot: decorative dot (@statusDotSizeSm/Md); also uses the
37
37
  // _size-sm suffix, a different class convention
38
+ // - IdentityBadge: compact identity marker (@identityBadgeHeight 18px),
39
+ // not a control — it must fit a dense TreeView row and a DagView node
38
40
  // - Slider: size scales track thickness and thumb diameter, not a
39
41
  // control height
40
42
  // - Modal: size selects dialog width, not control height
@@ -53,7 +55,8 @@
53
55
  // Menu dense items, List dense rows, and the targets the controls above do not
54
56
  // cover: Composer icon buttons, Breadcrumbs "..." button, DagView zoom buttons,
55
57
  // Slider rail and thumb zones, SelectField clear button and chip delete zones, Sheet
56
- // header buttons, Table sort control, DatePicker adornment button). A mouse keeps the sizes above.
58
+ // header buttons, Table sort control, DatePicker adornment button, IdentityBadgeGroup's
59
+ // clickable "+N"). A mouse keeps the sizes above.
57
60
  // Still exempt on touch: the --filled and --inline fields, the compact Toggle
58
61
  // (capped at @toggleCompactMaxHeight) and a small Chip that is neither a button
59
62
  // nor a link.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smwb/ui-styles",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "description": "Framework-agnostic LESS design tokens and component styles (smwb-*) shared by the @smwb UI libraries. Prebuilt aggregate CSS lives in @smwb/ui-css.",
5
5
  "license": "MIT",
6
6
  "sideEffects": [