@smwb/ui-styles 1.6.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +157 -22
  2. package/less/components/appBar/appBar.less +108 -0
  3. package/less/components/badge/badge.less +8 -0
  4. package/less/components/button/button.less +7 -1
  5. package/less/components/checkbox/checkbox.less +37 -3
  6. package/less/components/chip/chip.less +15 -3
  7. package/less/components/commandPalette/commandPalette.entry.less +2 -0
  8. package/less/components/commandPalette/commandPalette.less +233 -0
  9. package/less/components/composer/composer.entry.less +2 -0
  10. package/less/components/composer/composer.less +294 -0
  11. package/less/components/composer/composer.variables.less +10 -0
  12. package/less/components/diffStat/diffStat.entry.less +2 -0
  13. package/less/components/diffStat/diffStat.less +76 -0
  14. package/less/components/kbd/kbd.entry.less +2 -0
  15. package/less/components/kbd/kbd.less +55 -0
  16. package/less/components/list/list.less +39 -0
  17. package/less/components/qrCode/qrCode.entry.less +2 -0
  18. package/less/components/qrCode/qrCode.less +35 -0
  19. package/less/components/radioButton/radioButton.less +32 -0
  20. package/less/components/selectField/selectField.less +21 -4
  21. package/less/components/sidebar/sidebar.less +26 -0
  22. package/less/components/statusDot/statusDot.entry.less +2 -0
  23. package/less/components/statusDot/statusDot.less +118 -0
  24. package/less/components/swimlanes/swimlanes.entry.less +2 -0
  25. package/less/components/swimlanes/swimlanes.less +126 -0
  26. package/less/components/tabGroups/tabGroups.entry.less +2 -0
  27. package/less/components/tabGroups/tabGroups.less +219 -0
  28. package/less/components/tabs/tab/tab.less +134 -2
  29. package/less/components/tabs/tabPanel/tabPanel.entry.less +2 -0
  30. package/less/components/tabs/tabPanel/tabPanel.less +8 -0
  31. package/less/components/tabs/tabs.less +26 -0
  32. package/less/components/textField/textField.less +34 -9
  33. package/less/components/toggle/toggle.less +28 -1
  34. package/less/components/treeView/treeView.less +40 -2
  35. package/less/components.less +9 -0
  36. package/less/default/globals/default.variables.less +84 -0
  37. package/less/foundation/sizing.less +41 -5
  38. package/less/variables.less +10 -0
  39. package/package.json +5 -2
package/README.md CHANGED
@@ -19,18 +19,18 @@ npm install @smwb/ui-styles
19
19
 
20
20
  ## What's inside
21
21
 
22
- | Path | Purpose |
23
- | --- | --- |
24
- | `less/components.less` | Aggregate entry — `@import`s foundation + every component |
25
- | `less/foundation/*` | Tokens: palette, spacing, radius, elevation, breakpoints, sizing |
26
- | `less/default/globals/default.variables.less` | Semantic colors + per-component LESS variables |
27
- | `less/variables.less` | Declares the `--smui-*` tokens and emits them on `:root` (light) + `[data-theme="dark"]` |
28
- | `less/components/<name>/<name>.entry.less` | Per-component entry (component rules only, no tokens) |
29
- | `less/tokens.entry.less` | The `--smui-*` tokens at zero specificity (`:where(:root)`) for per-component CSS |
30
- | `less/headless-base.less` | Foundation + theme variables only (no component rules) |
31
- | `less/connects/rt/table.entry.less` | The React TanStack Table adapter's own rules (column resizer) |
32
- | `less/themes/*` | Built-in theme packs (`summer`, `material`) |
33
- | `normalize.css` | Optional CSS reset |
22
+ | Path | Purpose |
23
+ | --------------------------------------------- | ---------------------------------------------------------------------------------------- |
24
+ | `less/components.less` | Aggregate entry — `@import`s foundation + every component |
25
+ | `less/foundation/*` | Tokens: palette, spacing, radius, elevation, breakpoints, sizing |
26
+ | `less/default/globals/default.variables.less` | Semantic colors + per-component LESS variables |
27
+ | `less/variables.less` | Declares the `--smui-*` tokens and emits them on `:root` (light) + `[data-theme="dark"]` |
28
+ | `less/components/<name>/<name>.entry.less` | Per-component entry (component rules only, no tokens) |
29
+ | `less/tokens.entry.less` | The `--smui-*` tokens at zero specificity (`:where(:root)`) for per-component CSS |
30
+ | `less/headless-base.less` | Foundation + theme variables only (no component rules) |
31
+ | `less/connects/rt/table.entry.less` | The React TanStack Table adapter's own rules (column resizer) |
32
+ | `less/themes/*` | Built-in theme packs (`summer`, `material`) |
33
+ | `normalize.css` | Optional CSS reset |
34
34
 
35
35
  ## Usage
36
36
 
@@ -93,21 +93,45 @@ is drawn for the 8th series). Light and dark are separate sets, so set both when
93
93
  show in the dark theme. At runtime, set `--smwb-chart-series-N` on the chart itself (its `style`): the chart declares the
94
94
  tokens on its own root, so a value set on `:root` or a wrapper does not reach it.
95
95
 
96
+ #### StatusDot colours
97
+
98
+ `StatusDot` reads only runtime tokens, so the dark theme follows without extra settings: `active` takes
99
+ `--smui-infoColor`, `attention` `--smui-warningColor`, `idle` `--smui-onSurfaceVariant`, `done`
100
+ `--smui-successColor`, `error` `--smui-errorColor` and `unknown` `--smui-outlineColor` (as a ring); `color` takes
101
+ the matching `--smui-*Color` token (`default` is `--smui-onSurfaceVariant`). Its sizes are `@statusDotSizeSm`
102
+ (8px), `@statusDotSizeMd` (12px) and `@statusDotRing` (2px, the ring of `unknown`) in `theme.config`.
103
+
104
+ Against `--smui-surfaceBackground` and `--smui-pageBackground`, the default theme's status colours measure
105
+ at least 3:1 in both modes (the lowest is `attention` on the light surface, 3.04:1). The semantic colours are one
106
+ value for both modes, so a theme that makes `@warningColor` lighter, or any of them darker for the dark theme,
107
+ changes the numbers: re-check them there. `color="secondary"` is the one name that falls short in the default dark
108
+ theme (2.65:1): use a status for state.
109
+
110
+ #### QrCode colours
111
+
112
+ `QrCode` never reads theme tokens for the code itself: the modules are always `@qrCodeDarkColor`
113
+ (`rgb(0 0 0)`) on `@qrCodeLightColor` (`rgb(255 255 255)`), including the quiet zone, so a phone camera
114
+ reads the code in the dark theme, under forced colours and in print (`forced-color-adjust: none` and
115
+ `print-color-adjust: exact` are set on the svg, and the same colours are written as SVG `fill` attributes,
116
+ so a copied or printed svg scans without the library CSS). Only the caption is themed
117
+ (`--smui-onSurfaceVariant`). The corner rounding is `@qrCodeRadius` (`@smui-radius-sm`); the svg clips to
118
+ it, and the quiet zone keeps the modules clear of the rounding.
119
+
96
120
  #### DagView colours
97
121
 
98
- The dependency graph (`DagView` in `@smwb/ui-solid`) takes its colours from theme variables that default to the
122
+ The dependency graph (`DagView` in `@smwb/ui-solid` and `@smwb/ui-react`) takes its colours from theme variables that default to the
99
123
  runtime tokens, so the dark theme follows without extra settings:
100
124
 
101
- | Variable (`theme.config`) | Runtime property on `.smwb-dag-view` | Default |
102
- | --- | --- | --- |
103
- | `@dagViewNodeBg` | `--smwb-dag-view-node-bg` | `var(--smui-surfaceBackground)` |
104
- | `@dagViewNodeBorder` | `--smwb-dag-view-node-border` | `var(--smui-outlineColor)` |
105
- | `@dagViewEdgeColor` | `--smwb-dag-view-edge-color` | `var(--smui-outlineColor)` |
106
- | `@dagViewEdgeActiveColor` | `--smwb-dag-view-edge-active-color` | `var(--smui-primaryColor)` |
107
- | `@dagViewSelectedRing` | `--smwb-dag-view-selected-ring` | `var(--smui-primaryColor)` |
125
+ | Variable (`theme.config`) | Runtime property on `.smwb-dag-view` | Default |
126
+ | ---------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------- |
127
+ | `@dagViewNodeBg` | `--smwb-dag-view-node-bg` | `var(--smui-surfaceBackground)` |
128
+ | `@dagViewNodeBorder` | `--smwb-dag-view-node-border` | `var(--smui-outlineColor)` |
129
+ | `@dagViewEdgeColor` | `--smwb-dag-view-edge-color` | `var(--smui-outlineColor)` |
130
+ | `@dagViewEdgeActiveColor` | `--smwb-dag-view-edge-active-color` | `var(--smui-primaryColor)` |
131
+ | `@dagViewSelectedRing` | `--smwb-dag-view-selected-ring` | `var(--smui-primaryColor)` |
108
132
  | `@dagViewStatusIdle` … `@dagViewStatusSkipped` | `--smwb-dag-view-status-idle` … `-skipped` | outline, on-surface variant, info, success, warning, error, disabled text |
109
- | `@dagViewDimOpacity` | `--smwb-dag-view-dim-opacity` | `0.35` |
110
- | `@dagViewHeight` | — (the graph's `height`) | `480px` |
133
+ | `@dagViewDimOpacity` | `--smwb-dag-view-dim-opacity` | `0.35` |
134
+ | `@dagViewHeight` | — (the graph's `height`) | `480px` |
111
135
 
112
136
  The statuses are `idle`, `pending`, `running`, `success`, `warning`, `error` and `skipped`; a node with any other
113
137
  status is drawn as `idle`. Each colour variable has a `…Dark` twin (e.g. `@dagViewStatusSuccessDark`), `false` by
@@ -120,6 +144,117 @@ node's accent comes from `--smwb-dag-view-accent`. For a status of your own, set
120
144
  The graph's default height, `@dagViewHeight`, is set by the stylesheet, so its `height` prop or a height in its
121
145
  `style` wins over it.
122
146
 
147
+ #### Swimlanes
148
+
149
+ `Swimlanes` (in `@smwb/ui-react` and `@smwb/ui-solid`) reads only runtime tokens, so the dark theme follows without
150
+ extra settings: the grid's surface and the sticky header's opaque background come from `--smui-surfaceBackground`,
151
+ the row divider from `--smui-dividerColor`, the hover tint from `--smui-onSurfaceHover`, the selected tint, the
152
+ selection stripe and the focus ring from `--smui-primaryColor`, and a disabled lane's text from
153
+ `--smui-disabledTextColor`.
154
+
155
+ | Variable (`theme.config`) | Runtime property on `.smwb-swimlanes` | Default |
156
+ | ------------------------- | ----------------------------------------------- | ------- |
157
+ | `@swimlanesHeaderWidth` | the fallback of `--smwb-swimlanes-header-width` | `240px` |
158
+ | `@swimlanesHeight` | — (the grid's `height`) | `480px` |
159
+
160
+ Three custom properties drive the layout and the component writes them on its own root:
161
+ `--smwb-swimlanes-row-height` (always, from `rowHeight`), `--smwb-swimlanes-header-width` (only with `headerWidth`)
162
+ and `--smwb-swimlanes-content-min-width` (only with `contentMinWidth`). The last two are yours to set, through the
163
+ props or at runtime on the grid itself (its `style`, which is merged last and wins). The row height is not: the
164
+ virtual window's maths needs it in JS, so it goes through `rowHeight` alone, and a different value in `style` would
165
+ resize the rows in CSS while the window still counts with `rowHeight`, leaving gaps or overlaps. A row's state is one
166
+ internal property, `--smwb-swimlanes-tint`, set on each row and read by both cells so the lane reads as one band:
167
+ `transparent`, `--smui-onSurfaceHover` on hover, and 12% of `--smui-primaryColor` when selected. It is declared on
168
+ the row, so a value set on the grid never reaches the cells.
169
+
170
+ `@swimlanesHeight` is set by the stylesheet, so the `height` prop or a height in `style` wins over it. A selected
171
+ lane also gets a 3px primary stripe on its header, so the selection is not shown by tint alone; forced colours draw
172
+ it as a `Highlight` outline on both cells instead. Cells clip, so content that needs more room needs a wider
173
+ `contentMinWidth`.
174
+
175
+ #### Composer
176
+
177
+ `Composer` is the task input: a textarea that grows from `minRows` to `maxRows`, an optional attach button, a
178
+ `toolbar` slot, a character counter and a send button that turns into Stop while the agent is busy. Its stylesheet is
179
+ `components/composer/composer.less` (per-component entry: `composer.entry.less`). The root's children are flat, and CSS
180
+ grid areas place them for each layout; the spacing is margins, so an absent child leaves no gap.
181
+
182
+ | Class | Element |
183
+ | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
184
+ | `.smwb-composer` | the root; `_stacked` (field on top, a bar under it: attach, toolbar, counter, action) or `_inline` (one row: attach, field, counter, action; the toolbar, if any, under it) |
185
+ | `_focused`, `_disabled`, `_drag-active`, `_over-limit`, `_busy` | root states; `_focused` follows the textarea's focus, not `:focus-within` |
186
+ | `.smwb-composer__attachments`, `__toolbar` | slots above the field and in the bar |
187
+ | `.smwb-composer__field` | the textarea's wrapper; its `data-value` mirrors the text, and `--smwb-composer-min-rows` / `--smwb-composer-max-rows` set the height range |
188
+ | `.smwb-composer__input` | the textarea |
189
+ | `.smwb-composer__icon-button` | a round native button; with `__attach` and `__action` (`__action_stop` while busy) |
190
+ | `.smwb-composer__counter` (`_error`) | the `n / max` text |
191
+ | `.smwb-composer__status` | the visually hidden live region for the counter |
192
+
193
+ The textarea grows in CSS only: `.smwb-composer__field::after` repeats the text (`content: attr(data-value)`) in a
194
+ hidden box that shares the textarea's font, padding and line height, and the textarea fills the grid cell that box
195
+ sizes, up to `max-height`. Nothing is measured, so the height is right in server-rendered markup and follows a width
196
+ change. The send button is not natively disabled while it is unavailable: `aria-disabled="true"` mutes it, so it keeps
197
+ focus.
198
+
199
+ The colours are theme variables that default to the runtime tokens, so the dark theme follows without extra settings:
200
+
201
+ | Variable (`theme.config`) | Default |
202
+ | -------------------------------------------------------- | --------------------------------------------------------------------------------------- |
203
+ | `@composerBg`, `@composerBorder`, `@composerBorderFocus` | `var(--smui-surfaceBackground)`, `var(--smui-outlineColor)`, `var(--smui-primaryColor)` |
204
+ | `@composerText`, `@composerPlaceholder` | `var(--smui-onSurfaceColor)`, `var(--smui-onSurfaceVariant)` |
205
+ | `@composerCounter`, `@composerCounterError` | `var(--smui-onSurfaceVariant)`, `var(--smui-errorColor)` |
206
+ | `@composerActionBg`, `@composerActionColor` | `var(--smui-primaryColor)`, `var(--smui-onPrimaryColor)` |
207
+ | `@composerStopBg`, `@composerStopColor` | `var(--smui-onSurfaceColor)`, `var(--smui-surfaceBackground)` |
208
+ | `@composerDragBg` | `var(--smui-primaryContainer)` |
209
+
210
+ Sizes live in `composer.variables.less`: `@composerPadding`, `@composerGap`, `@composerRadius`, `@composerLineHeight`
211
+ (one text line, `1.5rem`), `@composerFieldPaddingY` / `@composerFieldPaddingX` and `@composerIconButtonSize`.
212
+
213
+ #### DiffStat colours
214
+
215
+ `DiffStat` draws its figures and cells in the theme's `--smui-successColor` and `--smui-errorColor`, each mixed toward
216
+ `--smui-onSurfaceColor` so that the text keeps 4.5:1 contrast (the raw tokens measure about 3.9–4.3:1) and a cell
217
+ keeps 3:1 against a neutral one, in the light and the dark theme alike. A cell nobody owns is an outline of the surface ink
218
+ (60% of it, 3.69:1 or better against the page and the surface in every bundled theme, light and dark), not a fill:
219
+ a fill light enough to sit quietly measured 1.08–1.29:1 against a light page and made "4 of 5" look like "5 of 5":
220
+
221
+ | Variable (`theme.config`) | Runtime property on `.smwb-diff-stat` | Default |
222
+ | ------------------------- | ------------------------------------- | --------------------------------------------------------------- |
223
+ | `@diffStatColorMix` | — (compiled into the two below) | `55%` of the success or error token |
224
+ | — | `--smwb-diff-stat-added` | success token mixed toward the surface ink |
225
+ | — | `--smwb-diff-stat-removed` | error token mixed toward the surface ink |
226
+ | `@diffStatNeutralMix` | `--smwb-diff-stat-neutral` | the surface ink at `60%`, the outline colour of an unowned cell |
227
+
228
+ Sizes are `@diffStatFontSize`, `@diffStatCompactFontSize`, `@diffStatCellWidth`, `@diffStatCellHeight`,
229
+ `@diffStatCompactCellHeight`, `@diffStatCellGap` and `@diffStatCellRadius`. At runtime, set the three properties on the
230
+ `DiffStat` itself (its `style`, or a class that outranks `.smwb-diff-stat`).
231
+
232
+ #### AppBar window controls
233
+
234
+ `AppBar variant="window"` is the title bar of a desktop app window (Electron, Tauri, an installed PWA with Window
235
+ Controls Overlay): the bar drags the window (`-webkit-app-region: drag`) and every control inside it is taken out of
236
+ the drag region. The width reserved for the OS window buttons is `@appBarWindowControlsLeft` (64px, the macOS traffic
237
+ lights) and `@appBarWindowControlsRight` (138px, three 46px Windows/Linux caption buttons). Both are read through one
238
+ runtime property, so an app retunes them without recompiling:
239
+
240
+ ```css
241
+ .smwb-appbar.smwb-appbar_variant-window {
242
+ --smui-appBarWindowControlsWidth: 84px;
243
+ }
244
+ ```
245
+
246
+ `--smui-appBarWindowControlsWidth` is deliberately **not** declared in `variables.less`: it is read with a fallback,
247
+ so unset means the Less defaults above. With `windowControls="auto"` the insets come from the shell instead —
248
+ `env(titlebar-area-x)` / `env(titlebar-area-width)` — and are 0 in a plain browser tab; `left`, `right` and `none`
249
+ pin them with a class. The insets are `padding-left` / `padding-right` on the bar, added to `@appBarPaddingX`, and
250
+ they survive `dense`. Overlays are `no-drag` globally — `[data-smwb-overlay-layer]`, `.smwb-menu`, `.smwb-tooltip`,
251
+ `.smwb-snackbars` and the library's own full-screen portals (`.smwb-chat-attachment-image__overlay`,
252
+ `.smwb-image-modal__overlay`, `.smwb-thumb-card__preview`, `.smwb-chat-shell`): Electron hit-tests drag regions by
253
+ geometry, and those popups portal to `<body>`. Inside the bar, the roles that keep `tabindex="-1"` while the group
254
+ holds the tab stop (`menuitemcheckbox`, `menuitemradio`, `treeitem`, `spinbutton`, `gridcell`, `listbox`,
255
+ `scrollbar`) are listed next to the focusable elements. A control the generic list cannot see opts out with
256
+ `.smwb-appbar__no-drag` or `data-no-drag`.
257
+
123
258
  ## License
124
259
 
125
260
  [MIT](./LICENSE)
@@ -39,6 +39,90 @@
39
39
  padding: @appBarActionPadding;
40
40
  height: 48px;
41
41
  }
42
+ // `variant="window"`: the bar is the title bar of a desktop app window (Electron, Tauri, an installed PWA
43
+ // with Window Controls Overlay). Compound and ordered after `&.smwb-dense` above, so its side insets
44
+ // out-rank the dense `padding` shorthand instead of being wiped by it.
45
+ &.smwb-appbar_variant-window {
46
+ -webkit-app-region: drag;
47
+ app-region: drag;
48
+ // Dragging the window out of the title must not select the title text.
49
+ user-select: none;
50
+ // Width to keep clear of the OS window buttons on each side. With no class at all (`windowControls="auto"`)
51
+ // the area comes from the shell: `env(titlebar-area-*)` is exact under Window Controls Overlay, and in a
52
+ // plain browser tab the fallbacks make both insets 0, so the bar looks like `variant="default"`.
53
+ --smwb-appbar-wc-left: env(titlebar-area-x, 0px);
54
+ --smwb-appbar-wc-right: calc(100vw - env(titlebar-area-x, 0px) - env(titlebar-area-width, 100vw));
55
+ padding-left: calc(@appBarPaddingX + var(--smwb-appbar-wc-left));
56
+ padding-right: calc(@appBarPaddingX + var(--smwb-appbar-wc-right));
57
+ // `left`: the macOS traffic lights (`titleBarStyle: "hiddenInset"`), which Electron does not report
58
+ // through env() unless `titleBarOverlay` is on, hence the fixed width.
59
+ &.smwb-appbar_window-controls-left {
60
+ --smwb-appbar-wc-left: env(titlebar-area-x, var(--smui-appBarWindowControlsWidth, @appBarWindowControlsLeft));
61
+ }
62
+ // `right`: the caption buttons Windows/Linux draw at the right edge of the window. The fallback keeps
63
+ // their width clear by shortening the area the shell would have reported.
64
+ &.smwb-appbar_window-controls-right {
65
+ --smwb-appbar-wc-right: calc(
66
+ 100vw - env(titlebar-area-x, 0px) -
67
+ env(titlebar-area-width, calc(100vw - var(--smui-appBarWindowControlsWidth, @appBarWindowControlsRight)))
68
+ );
69
+ }
70
+ &.smwb-appbar_window-controls-none {
71
+ --smwb-appbar-wc-left: 0px;
72
+ --smwb-appbar-wc-right: 0px;
73
+ }
74
+ // Electron delivers no mouse events at all inside a drag region, so every control in the bar has to opt
75
+ // out. Focusable and role-tagged elements are matched generically (the list is `:where()`, so an app's own
76
+ // selector still wins); a custom control that is neither takes `.smwb-appbar__no-drag` or `data-no-drag`.
77
+ :where(
78
+ a[href],
79
+ button,
80
+ input,
81
+ select,
82
+ textarea,
83
+ label,
84
+ summary,
85
+ iframe,
86
+ video[controls],
87
+ [contenteditable]:not([contenteditable="false"]),
88
+ [tabindex]:not([tabindex="-1"]),
89
+ [role="button"],
90
+ [role="link"],
91
+ [role="menuitem"],
92
+ [role="tab"],
93
+ [role="checkbox"],
94
+ [role="switch"],
95
+ [role="radio"],
96
+ [role="combobox"],
97
+ [role="textbox"],
98
+ [role="searchbox"],
99
+ [role="slider"],
100
+ [role="option"],
101
+ // Roving-tabindex widgets and the cells of composite widgets: while the group holds
102
+ // the tab stop their items carry `tabindex="-1"`, so `[tabindex]:not([tabindex="-1"])`
103
+ // above never matches them and only the role gets them out of the drag region.
104
+ [role="menuitemcheckbox"],
105
+ [role="menuitemradio"],
106
+ [role="treeitem"],
107
+ [role="spinbutton"],
108
+ [role="gridcell"],
109
+ [role="listbox"],
110
+ [role="scrollbar"],
111
+ .smwb-tooltip__wrapper,
112
+ .smwb-appbar__no-drag,
113
+ [data-no-drag]
114
+ ) {
115
+ -webkit-app-region: no-drag;
116
+ app-region: no-drag;
117
+ }
118
+ // Dense keeps the side insets and drops the vertical padding, which the 48px height has no room for.
119
+ // The insets are repeated here on purpose: `responsive.less` is imported after every component and resets
120
+ // `.smwb-appbar.smwb-dense` at phone widths, which would otherwise beat the two longhands above.
121
+ &.smwb-dense {
122
+ padding: 0 calc(@appBarPaddingX + var(--smwb-appbar-wc-right)) 0
123
+ calc(@appBarPaddingX + var(--smwb-appbar-wc-left));
124
+ }
125
+ }
42
126
  &__inner {
43
127
  display: flex;
44
128
  align-items: center;
@@ -52,6 +136,12 @@
52
136
  gap: @appBarActionsGap;
53
137
  white-space: nowrap;
54
138
  }
139
+ // The title may be a heading (`titleAs`): drop the UA margins and font so it
140
+ // looks the same as the div.
141
+ &__left__title {
142
+ margin: 0;
143
+ font: inherit;
144
+ }
55
145
  &__right {
56
146
  white-space: nowrap;
57
147
  }
@@ -74,6 +164,24 @@
74
164
  }
75
165
  }
76
166
 
167
+ // Overlays opened from a `variant="window"` bar are portaled to `<body>`, outside the bar's own subtree, but
168
+ // Electron hit-tests drag regions by geometry: a dropdown drawn over the bar would be unclickable. `no-drag`
169
+ // is ignored outside app windows, so this is harmless for every other page. The library's own full-screen
170
+ // portals are listed by class because they carry no `data-smwb-overlay-layer`: a lightbox opened over the top
171
+ // of the bar would otherwise stay a drag region, so a backdrop click there would drag the window instead of
172
+ // closing it.
173
+ [data-smwb-overlay-layer],
174
+ .smwb-menu,
175
+ .smwb-tooltip,
176
+ .smwb-snackbars,
177
+ .smwb-chat-attachment-image__overlay,
178
+ .smwb-image-modal__overlay,
179
+ .smwb-thumb-card__preview,
180
+ .smwb-chat-shell {
181
+ -webkit-app-region: no-drag;
182
+ app-region: no-drag;
183
+ }
184
+
77
185
  .smwb-appbar_color-primary {
78
186
  background-color: var(--smui-primaryColor);
79
187
  color: var(--smui-onPrimaryColor);
@@ -51,6 +51,14 @@
51
51
  background-color: var(--smui-primaryColor);
52
52
  }
53
53
 
54
+ &_warning {
55
+ background-color: var(--smui-warningColor);
56
+ // The amber fill is too light for the white the other colors use; see
57
+ // @badgeWarningColor. An app that retunes --smui-warningColor at runtime
58
+ // retunes this ink to match, through --smui-badgeWarningColor.
59
+ color: var(--smui-badgeWarningColor, @badgeWarningColor);
60
+ }
61
+
54
62
  &_no-content {
55
63
  padding: 0;
56
64
  width: 8px;
@@ -33,14 +33,17 @@
33
33
  &.smwb-small {
34
34
  font-size: @smallButtonFontSize;
35
35
  padding: @buttonPaddingSmall;
36
+ min-height: var(--smui-control-h-sm, @smui-control-h-sm);
36
37
  }
37
38
  &.smwb-medium {
38
39
  font-size: @mediumButtonFontSize;
39
40
  padding: @buttonPaddingMedium;
41
+ min-height: var(--smui-control-h-md, @smui-control-h-md);
40
42
  }
41
43
  &.smwb-large {
42
44
  font-size: @largeButtonFontSize;
43
45
  padding: @buttonPaddingLarge;
46
+ min-height: var(--smui-control-h-lg, @smui-control-h-lg);
44
47
  }
45
48
 
46
49
  &.smwb-contained {
@@ -52,7 +55,10 @@
52
55
  }
53
56
  &:hover {
54
57
  background-color: var(--smui-buttonContainedHoverBg);
55
- box-shadow: 0 4px 22px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
58
+ box-shadow:
59
+ 0 4px 22px -2px rgba(0, 0, 0, 0.2),
60
+ 0 2px 2px 0 rgba(0, 0, 0, 0.14),
61
+ 0 1px 5px 0 rgba(0, 0, 0, 0.12);
56
62
  }
57
63
  &:disabled {
58
64
  background-color: var(--smui-buttonDisabledBg);
@@ -1,10 +1,11 @@
1
1
  .smwb-text-field-checkbox {
2
- min-width: 0 !important;
2
+ min-width: 0 !important;
3
3
  }
4
4
 
5
5
  .smwb-checkbox {
6
- // The box's size, which the native input takes too.
7
- @box-size: 18px;
6
+ // The box's size, which the native input takes too. The medium default is the
7
+ // selection token (18px), so the component's unstyled rendering is unchanged.
8
+ @box-size: @smui-selection-md;
8
9
 
9
10
  &__icon {
10
11
  opacity: 0;
@@ -26,6 +27,10 @@
26
27
  position: relative;
27
28
  transition: all 0.2s ease-in-out;
28
29
  &::before {
30
+ // The hover/active halo stays 40px at every size. It is already the box's
31
+ // own minimum touch target (40px >= the md row height) and scales from the
32
+ // box's center, so shrinking it for sm would buy nothing and drop the
33
+ // largest size's halo below the 22px box it is meant to surround.
29
34
  width: 40px;
30
35
  height: 40px;
31
36
  border-radius: @circleBorderRadius;
@@ -123,4 +128,33 @@
123
128
  }
124
129
  }
125
130
  }
131
+
132
+ // The size prop maps the row onto the control-height scale and the box onto the
133
+ // selection scale. The size class rides the wrapper (the row), so min-height
134
+ // goes there; the box keeps a hard height, since a checkbox box must not stretch
135
+ // to fill the row. The hidden native input is resized with the box so its hit
136
+ // area stays coincident. medium is the component default and repeats today's
137
+ // 18px box exactly, so unstyled rendering does not change.
138
+ &_size-small {
139
+ min-height: var(--smui-control-h-sm, @smui-control-h-sm);
140
+ .smwb-checkbox__btn,
141
+ .smwb-checkbox__input {
142
+ width: @smui-selection-sm;
143
+ height: @smui-selection-sm;
144
+ }
145
+ }
146
+ // The medium box stays on the base rule's @box-size: @smui-selection-md
147
+ // read — repeating it here would only raise specificity and break consumer
148
+ // overrides of the default checkbox.
149
+ &_size-medium {
150
+ min-height: var(--smui-control-h-md, @smui-control-h-md);
151
+ }
152
+ &_size-large {
153
+ min-height: var(--smui-control-h-lg, @smui-control-h-lg);
154
+ .smwb-checkbox__btn,
155
+ .smwb-checkbox__input {
156
+ width: @smui-selection-lg;
157
+ height: @smui-selection-lg;
158
+ }
159
+ }
126
160
  }
@@ -1,7 +1,9 @@
1
1
  .smwb-chip {
2
2
  position: relative;
3
3
  user-select: none;
4
- height: 32px;
4
+ // Chip's default box is 32px, which is the sm step of the control scale, not md
5
+ // (Chip has no large size, so sm is the only step it maps to).
6
+ min-height: var(--smui-control-h-sm, @smui-control-h-sm);
5
7
  display: inline-flex;
6
8
  align-items: center;
7
9
  justify-content: center;
@@ -33,7 +35,10 @@
33
35
  }
34
36
  &:active {
35
37
  background-color: var(--smui-chipsActiveBg);
36
- box-shadow: 0px 3px 5px -1px rgb(0 0 0 / 16%), 0px 1px 1px 0px rgb(0 0 0 / 14%), 0px 2px 3px 0px rgb(0 0 0 / 12%);
38
+ box-shadow:
39
+ 0px 3px 5px -1px rgb(0 0 0 / 16%),
40
+ 0px 1px 1px 0px rgb(0 0 0 / 14%),
41
+ 0px 2px 3px 0px rgb(0 0 0 / 12%);
37
42
  }
38
43
  &.smwb-selected {
39
44
  background-color: var(--smui-chipsSelectedBg);
@@ -47,7 +52,10 @@
47
52
  }
48
53
  &:active {
49
54
  background-color: var(--smui-chipsActiveBg);
50
- box-shadow: 0px 3px 5px -1px rgb(0 0 0 / 11%), 0px 1px 1px 0px rgb(0 0 0 / 14%), 0px 2px 3px 0px rgb(0 0 0 / 12%);
55
+ box-shadow:
56
+ 0px 3px 5px -1px rgb(0 0 0 / 11%),
57
+ 0px 1px 1px 0px rgb(0 0 0 / 14%),
58
+ 0px 2px 3px 0px rgb(0 0 0 / 12%);
51
59
  }
52
60
  &.smwb-selected {
53
61
  background-color: var(--smui-chipsSelectedBg);
@@ -119,7 +127,11 @@
119
127
  }
120
128
 
121
129
  &_size-small {
130
+ // 24px is deliberately below the control scale (Fork C): it stays a literal,
131
+ // not a token. min-height is repeated because the base rule floors the box at
132
+ // the sm step (32px); without it the height alone would be clamped back up.
122
133
  height: 24px;
134
+ min-height: 24px;
123
135
  padding: 0 @smui-space-sm;
124
136
  font-size: 0.75rem;
125
137
  }
@@ -0,0 +1,2 @@
1
+ @import (reference) "../../_prelude.less";
2
+ @import "./commandPalette.less";