@smwb/ui-styles 2.4.0 → 2.6.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
@@ -143,8 +143,33 @@ runtime tokens, so the dark theme follows without extra settings:
143
143
  | `@dagViewSelectedRing` | `--smwb-dag-view-selected-ring` | `var(--smui-primaryColor)` |
144
144
  | `@dagViewStatusIdle` … `@dagViewStatusSkipped` | `--smwb-dag-view-status-idle` … `-skipped` | outline, on-surface variant, info, success, warning, error, disabled text |
145
145
  | `@dagViewDimOpacity` | `--smwb-dag-view-dim-opacity` | `0.35` |
146
+ | `@dagViewEdgeFlowColor` | `--smwb-dag-view-edge-flow-color` | `var(--smwb-dag-view-status-running)` |
147
+ | `@dagViewEdgeFlowTrackColor` | `--smwb-dag-view-edge-flow-track-color` | the flow colour mixed 45% with transparent |
148
+ | `@dagViewNodeGlowColor` | `--smwb-dag-view-node-glow-color` | `false`: the node's own accent |
149
+ | `@dagViewEdgeFlowWidth` | `--smwb-dag-view-edge-flow-width` | `2.5px` |
150
+ | `@dagViewEdgeFlowDuration` | `--smwb-dag-view-edge-flow-duration` | `0.6s` (one 24px dash period) |
151
+ | `@dagViewEdgeGlowOpacity` | `--smwb-dag-view-edge-glow-opacity` | `0.25` |
152
+ | `@dagViewNodeGlowOpacity` | `--smwb-dag-view-node-glow-opacity` | `0.6` (blur strength of the running node's inner glow) |
153
+ | `@dagViewNodePulseDuration` | `--smwb-dag-view-node-pulse-duration` | `1.4s` (the running node's dot and frame) |
146
154
  | `@dagViewHeight` | — (the graph's `height`) | `480px` |
147
155
 
156
+ The flow colours (`@dagViewEdgeFlowColor`, `@dagViewEdgeFlowTrackColor`, `@dagViewNodeGlowColor`) are colours and have
157
+ `…Dark` twins, `false` by default; the width, durations and opacities are not colours and have none. The pulses' dash
158
+ pattern (`@dagViewEdgeFlowDash` / `@dagViewEdgeFlowGap`, `6px` / `18px`) is compile time only: the flow keyframes move
159
+ exactly one period, so `dash + gap` must stay the keyframe's stride.
160
+
161
+ An edge the app marks `active` shows work flowing along it (`DagViewEdge.active`, `@smwb/ui-solid`): the edge lights in
162
+ the flow colour over a paler track, a static glow lies under it and pulses run from `source` to `target`; a running
163
+ node's frame glows and pulses with its dot. This `active` is not the selection highlight — the selected node's edges
164
+ keep `@dagViewEdgeActiveColor`, and where both apply the flowing edge wins the colour and the arrow. All flow rules and
165
+ the node glow are gated on markup only `@smwb/ui-solid` writes (`data-animate-running` on the root, the flowing
166
+ classes on the edge paths), so the shared CSS is inert for `@smwb/ui-react` until its own issue lands; the only
167
+ change React sees is the new custom properties on its root and the dot's duration read from
168
+ `--smwb-dag-view-node-pulse-duration`, whose default is the 1.4 s of before. Motion is CSS only: with
169
+ `prefers-reduced-motion: reduce` the pulses stay hidden, the flowing edge stays lit in the full flow colour, and a
170
+ running node keeps a still glow; `animateRunning={false}` stops a running node's glow and dot pulse without reduced
171
+ motion.
172
+
148
173
  The statuses are `idle`, `pending`, `running`, `success`, `warning`, `error` and `skipped`; a node with any other
149
174
  status is drawn as `idle`. Each colour variable has a `…Dark` twin (e.g. `@dagViewStatusSuccessDark`), `false` by
150
175
  default: set it to give the dark theme its own value. At runtime, set the properties on the graph itself, since it
@@ -53,6 +53,13 @@
53
53
  &:hover {
54
54
  background-color: var(--smui-onSurfaceActive);
55
55
  }
56
+
57
+ // On a coarse pointer the "..." button is a square of the sm step (44px there): it is
58
+ // otherwise as tall as a line of text and as wide as its dots.
59
+ @media (pointer: coarse) {
60
+ min-width: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
61
+ min-height: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
62
+ }
56
63
  }
57
64
 
58
65
  // One line: the list never wraps, the crumbs shrink and cut their text with an
@@ -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
  }
@@ -6,14 +6,14 @@
6
6
  box-sizing: border-box;
7
7
  margin: 0;
8
8
  border: 0;
9
- padding: @composerFieldPaddingY @composerFieldPaddingX;
9
+ padding: var(--smwb-composer-field-padding-y, @composerFieldPaddingY) @composerFieldPaddingX;
10
10
  font: inherit;
11
11
  letter-spacing: inherit;
12
12
  line-height: @composerLineHeight;
13
13
  overflow-wrap: anywhere;
14
14
  // The height of N rows of text, with the text's own padding.
15
- min-height: ~"calc(var(--smwb-composer-min-rows, 1) * @{composerLineHeight} + 2 * @{composerFieldPaddingY})";
16
- max-height: ~"calc(var(--smwb-composer-max-rows, 8) * @{composerLineHeight} + 2 * @{composerFieldPaddingY})";
15
+ min-height: ~"calc(var(--smwb-composer-min-rows, 1) * @{composerLineHeight} + 2 * var(--smwb-composer-field-padding-y, @{composerFieldPaddingY}))";
16
+ max-height: ~"calc(var(--smwb-composer-max-rows, 8) * @{composerLineHeight} + 2 * var(--smwb-composer-field-padding-y, @{composerFieldPaddingY}))";
17
17
  }
18
18
 
19
19
  // A task input: a growing textarea with an attach button, a toolbar slot, a
@@ -36,6 +36,15 @@
36
36
  background-color: @composerBg;
37
37
  color: @composerText;
38
38
 
39
+ // Touch density: the icon buttons are the sm step (44px there) at least, and the one-line
40
+ // field grows with them (24px of text + 2 * padding = the button), so the row stays level.
41
+ // @composerIconButtonSize is a literal that composer.variables.less declares again after the
42
+ // theme, so the step is the way to raise it: @smui-control-h-sm-coarse.
43
+ @media (pointer: coarse) {
44
+ --smwb-composer-icon-button-size: max(@composerIconButtonSize, var(--smui-control-h-sm, @smui-control-h-sm-coarse));
45
+ --smwb-composer-field-padding-y: calc((var(--smwb-composer-icon-button-size) - @composerLineHeight) / 2);
46
+ }
47
+
39
48
  &__attachments {
40
49
  grid-area: attachments;
41
50
  min-width: 0;
@@ -86,8 +95,8 @@
86
95
  justify-content: center;
87
96
  box-sizing: border-box;
88
97
  flex: none;
89
- width: @composerIconButtonSize;
90
- height: @composerIconButtonSize;
98
+ width: var(--smwb-composer-icon-button-size, @composerIconButtonSize);
99
+ height: var(--smwb-composer-icon-button-size, @composerIconButtonSize);
91
100
  margin: 0;
92
101
  padding: 0;
93
102
  border: 0;
@@ -244,7 +253,7 @@
244
253
  .smwb-composer__counter {
245
254
  // On the bottom row of text, level with the action.
246
255
  align-self: end;
247
- padding-bottom: ~"calc((@{composerIconButtonSize} - 1rem) / 2)";
256
+ padding-bottom: ~"calc((var(--smwb-composer-icon-button-size, @{composerIconButtonSize}) - 1rem) / 2)";
248
257
  }
249
258
 
250
259
  .smwb-composer__attach ~ .smwb-composer__toolbar {
@@ -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);
@@ -267,6 +331,11 @@
267
331
  width: 32px;
268
332
  height: 32px;
269
333
  padding: 0;
334
+ // On a coarse pointer the buttons are the sm step (44px there) at least.
335
+ @media (pointer: coarse) {
336
+ width: max(32px, var(--smui-control-h-sm, @smui-control-h-sm-coarse));
337
+ height: max(32px, var(--smui-control-h-sm, @smui-control-h-sm-coarse));
338
+ }
270
339
  border: 1px solid var(--smui-dividerColor);
271
340
  border-radius: @smui-radius-md;
272
341
  background-color: var(--smui-surfaceBackground);
@@ -299,10 +368,23 @@
299
368
  .smwb-dag-view-tokens("Dark");
300
369
  }
301
370
 
302
- // 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.
303
375
  @media (prefers-reduced-motion: no-preference) {
304
376
  .smwb-dag-view__node[data-status="running"] .smwb-dag-view__dot {
305
- 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;
306
388
  }
307
389
  }
308
390
 
@@ -311,3 +393,12 @@
311
393
  opacity: 0.35;
312
394
  }
313
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
+ }
@@ -93,6 +93,19 @@
93
93
  padding: @datePickerYearPadding;
94
94
  }
95
95
 
96
+ // On a coarse pointer the TextField adornment button is a square of the sm step (44px there)
97
+ // at least, the icon in the middle; the 36px it was is padding around the 24px glyph. The
98
+ // popup's small month and year arrows keep their 34px.
99
+ @media (pointer: coarse) {
100
+ &:not(&--small) {
101
+ box-sizing: border-box;
102
+ align-items: center;
103
+ justify-content: center;
104
+ min-width: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
105
+ min-height: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
106
+ }
107
+ }
108
+
96
109
  &--edge-start {
97
110
  margin-left: -12px;
98
111
  }
@@ -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 {
@@ -99,6 +99,40 @@
99
99
  cursor: pointer;
100
100
  }
101
101
 
102
+ // Touch density: on a coarse pointer the clear button and the delete button of a value chip
103
+ // answer a press from a square of the sm step (44px there), not the small icon they draw.
104
+ // The clear button is that square itself, in the actions row: its neighbours, the dropdown
105
+ // arrow and the control, keep their own boxes, so a press on the arrow opens the menu and
106
+ // never clears it. The negative margin keeps the height the icon gave the field; the row
107
+ // grows wider by what the square adds. A chip's delete button is laid over a bigger box by
108
+ // a pseudo-element (a chip must not grow): the box starts at the icon, so it never reaches
109
+ // back over the label, whose press opens the menu, and extends to the right, into the
110
+ // chip's padding; the next chip is positioned later and paints over it.
111
+ @media (pointer: coarse) {
112
+ &__clear {
113
+ align-items: center;
114
+ justify-content: center;
115
+ box-sizing: border-box;
116
+ width: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
117
+ height: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
118
+ margin: calc((16px - var(--smui-control-h-sm, @smui-control-h-sm-coarse)) / 2) 0;
119
+ }
120
+
121
+ .smwb-chip__delete-icon {
122
+ position: relative;
123
+
124
+ &::after {
125
+ content: "";
126
+ position: absolute;
127
+ top: 50%;
128
+ left: 0;
129
+ width: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
130
+ height: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
131
+ transform: translateY(-50%);
132
+ }
133
+ }
134
+ }
135
+
102
136
  &__values {
103
137
  white-space: nowrap;
104
138
  font: inherit;
@@ -39,6 +39,12 @@
39
39
 
40
40
  .smwb-button {
41
41
  min-width: 0;
42
+
43
+ // On a coarse pointer the close and size buttons are as wide as they are tall (the sm
44
+ // step, 44px there); their padding made them 42px wide.
45
+ @media (pointer: coarse) {
46
+ min-width: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
47
+ }
42
48
  }
43
49
  }
44
50
 
@@ -292,3 +292,29 @@
292
292
  white-space: nowrap;
293
293
  opacity: 0.7;
294
294
  }
295
+
296
+ // Touch density: on a coarse pointer the zone that takes a press on the rail, the padding around
297
+ // it, is the sm step (44px there) across, 30px otherwise, and so is the thumb's own zone. A mouse
298
+ // keeps all of it. The mark labels hang from the marks, which are centred on the rail, so they follow it.
299
+ @sliderTouchHit: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
300
+ @media (pointer: coarse) {
301
+ .smwb-slider {
302
+ padding-top: max(@sliderPaddingY, calc((@sliderTouchHit - 4px) / 2));
303
+ padding-bottom: max(@sliderPaddingY, calc((@sliderTouchHit - 4px) / 2));
304
+ }
305
+ .smwb-slider_size-small {
306
+ padding-top: max(@sliderPaddingY, calc((@sliderTouchHit - 2px) / 2));
307
+ padding-bottom: max(@sliderPaddingY, calc((@sliderTouchHit - 2px) / 2));
308
+ }
309
+ .smwb-slider_size-large {
310
+ padding-top: max(@sliderPaddingY, calc((@sliderTouchHit - 6px) / 2));
311
+ padding-bottom: max(@sliderPaddingY, calc((@sliderTouchHit - 6px) / 2));
312
+ }
313
+ .smwb-slider--vertical {
314
+ padding: 0 max(@sliderPaddingY, calc((@sliderTouchHit - 4px) / 2));
315
+ }
316
+ .smwb-slider__thumb:after {
317
+ width: max(42px, @sliderTouchHit);
318
+ height: max(42px, @sliderTouchHit);
319
+ }
320
+ }
@@ -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
 
@@ -182,6 +182,14 @@
182
182
  outline: 2px solid var(--smui-primaryColor);
183
183
  outline-offset: 2px;
184
184
  }
185
+ // On a coarse pointer the sort control is the sm step (44px there) at least, a line of text
186
+ // tall otherwise. The header cell (56px) already has the room.
187
+ @media (pointer: coarse) {
188
+ .smwb-head-cell_sortable .smwb-head-cell__content {
189
+ min-width: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
190
+ min-height: var(--smui-control-h-sm, @smui-control-h-sm-coarse);
191
+ }
192
+ }
185
193
 
186
194
  .smwb-table__row_selected {
187
195
  background-color: var(--smui-onSurfaceHover);
@@ -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
 
@@ -589,6 +589,21 @@
589
589
  @dagViewStatusSkippedBorderStyle: false;
590
590
  // How far the nodes and edges away from the selection fade, with dimUnrelated.
591
591
  @dagViewDimOpacity: 0.35;
592
+ // An edge the app marks `active`: work flows along it. The flow colour defaults
593
+ // to the running status colour, so the dark theme follows on its own.
594
+ @dagViewEdgeFlowColor: ~"var(--smwb-dag-view-status-running)";
595
+ // The flowing edge's track: the flow colour mixed 45% with transparent.
596
+ @dagViewEdgeFlowTrackColor: ~"color-mix(in srgb, var(--smwb-dag-view-edge-flow-color) 45%, transparent)";
597
+ // A running node's inner glow colour; `false`: the node's own accent.
598
+ @dagViewNodeGlowColor: false;
599
+ @dagViewEdgeFlowWidth: 2.5px;
600
+ @dagViewEdgeFlowDuration: 0.6s;
601
+ @dagViewEdgeGlowOpacity: 0.25;
602
+ @dagViewNodeGlowOpacity: 0.6;
603
+ @dagViewNodePulseDuration: 1.4s;
604
+ // The pulses' dash pattern, compile time only: the flow keyframes move one period.
605
+ @dagViewEdgeFlowDash: 6px;
606
+ @dagViewEdgeFlowGap: 18px;
592
607
  // The graph's height when the app sets none (its `height` prop or `style`).
593
608
  @dagViewHeight: 480px;
594
609
 
@@ -817,3 +832,7 @@
817
832
  @dagViewStatusWarningDark: false;
818
833
  @dagViewStatusErrorDark: false;
819
834
  @dagViewStatusSkippedDark: false;
835
+ // Dark-only values of the flow colours; `false` = not set: the light value (a runtime token) applies.
836
+ @dagViewEdgeFlowColorDark: false;
837
+ @dagViewEdgeFlowTrackColorDark: false;
838
+ @dagViewNodeGlowColorDark: false;
@@ -50,7 +50,10 @@
50
50
  // an `@media (pointer: coarse)` block, which is why most components need no
51
51
  // rule of their own; only what sits off the scale has one (small clickable
52
52
  // or link Chip, Tabs dense and its scroll buttons, TabGroups dense actions button,
53
- // Menu dense items, List dense rows). A mouse keeps the sizes above.
53
+ // Menu dense items, List dense rows, and the targets the controls above do not
54
+ // cover: Composer icon buttons, Breadcrumbs "..." button, DagView zoom buttons,
55
+ // 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.
54
57
  // Still exempt on touch: the --filled and --inline fields, the compact Toggle
55
58
  // (capped at @toggleCompactMaxHeight) and a small Chip that is neither a button
56
59
  // nor a link.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smwb/ui-styles",
3
- "version": "2.4.0",
3
+ "version": "2.6.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": [