@floegence/floe-webapp-core 0.86.60 → 0.86.62

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 (103) hide show
  1. package/dist/components/chat/blocks/BlockRenderer.js +1 -1
  2. package/dist/components/chat/blocks/CodeDiffBlock.js +56 -56
  3. package/dist/components/chat/blocks/ImageBlock.js +1 -1
  4. package/dist/components/chat/blocks/ToolCallBlock.js +57 -57
  5. package/dist/components/chat/status/ConnectionStatus.js +10 -10
  6. package/dist/components/deck/DeckCell.js +25 -25
  7. package/dist/components/deck/DeckContextMenu.js +20 -20
  8. package/dist/components/deck/DeckGrid.js +1 -1
  9. package/dist/components/deck/DeckTopBar.js +42 -42
  10. package/dist/components/deck/DropZonePreview.js +6 -6
  11. package/dist/components/deck/LayoutSelector.js +53 -53
  12. package/dist/components/deck/WidgetFrame.js +28 -28
  13. package/dist/components/deck/WidgetPalette.js +31 -31
  14. package/dist/components/deck/WidgetTypeSwitcher.js +31 -31
  15. package/dist/components/file-browser/Breadcrumb.js +27 -27
  16. package/dist/components/file-browser/DirectoryTree.js +15 -15
  17. package/dist/components/file-browser/DragPreview.js +58 -58
  18. package/dist/components/file-browser/FileBrowser.js +17 -17
  19. package/dist/components/file-browser/FileBrowserStatusBar.js +18 -18
  20. package/dist/components/file-browser/FileBrowserToolbar.js +27 -27
  21. package/dist/components/file-browser/FileContextMenu.js +47 -47
  22. package/dist/components/file-browser/FileGridView.js +53 -51
  23. package/dist/components/file-browser/FileItemDecorations.js +30 -27
  24. package/dist/components/file-browser/FileListView.js +114 -114
  25. package/dist/components/icons/index.js +1357 -1357
  26. package/dist/components/launchpad/Launchpad.js +14 -14
  27. package/dist/components/launchpad/LaunchpadGrid.js +7 -7
  28. package/dist/components/launchpad/LaunchpadItem.js +11 -11
  29. package/dist/components/launchpad/LaunchpadPagination.js +16 -16
  30. package/dist/components/launchpad/LaunchpadSearch.js +9 -9
  31. package/dist/components/layout/ActivityBar.js +52 -55
  32. package/dist/components/layout/BottomBar.js +17 -17
  33. package/dist/components/layout/MobileNavigationPanel.js +36 -36
  34. package/dist/components/layout/MobileTabBar.js +39 -39
  35. package/dist/components/layout/Panel.js +16 -16
  36. package/dist/components/layout/ResizeHandle.js +19 -19
  37. package/dist/components/layout/Shell.js +50 -50
  38. package/dist/components/layout/Sidebar.js +37 -36
  39. package/dist/components/layout/SidebarPane.js +19 -19
  40. package/dist/components/layout/TopBar.js +16 -16
  41. package/dist/components/layout/TopBarIconButton.js +15 -15
  42. package/dist/components/loading/LoadingOverlay.js +9 -9
  43. package/dist/components/loading/Skeleton.js +11 -11
  44. package/dist/components/ui/Button.js +36 -36
  45. package/dist/components/ui/Card.d.ts +11 -62
  46. package/dist/components/ui/Card.js +64 -237
  47. package/dist/components/ui/Checkbox.js +79 -79
  48. package/dist/components/ui/CommandPalette.js +56 -55
  49. package/dist/components/ui/Dialog.js +91 -93
  50. package/dist/components/ui/DirectoryInput.js +22 -22
  51. package/dist/components/ui/DirectoryPicker.js +8 -8
  52. package/dist/components/ui/Dropdown.js +89 -87
  53. package/dist/components/ui/FeedbackIndicator.js +57 -57
  54. package/dist/components/ui/FileOpenPicker.js +37 -37
  55. package/dist/components/ui/FileSavePicker.js +41 -41
  56. package/dist/components/ui/FloatingWindow.js +213 -205
  57. package/dist/components/ui/Form.js +76 -76
  58. package/dist/components/ui/HighlightBlock.js +14 -14
  59. package/dist/components/ui/Input.js +106 -109
  60. package/dist/components/ui/Pagination.js +86 -86
  61. package/dist/components/ui/Popover.d.ts +33 -0
  62. package/dist/components/ui/Popover.js +133 -0
  63. package/dist/components/ui/ProcessingIndicator.js +344 -345
  64. package/dist/components/ui/Progress.js +94 -97
  65. package/dist/components/ui/QuoteBlock.js +25 -25
  66. package/dist/components/ui/Radio.js +91 -91
  67. package/dist/components/ui/SegmentedControl.js +24 -24
  68. package/dist/components/ui/Stepper.js +68 -60
  69. package/dist/components/ui/Switch.js +25 -25
  70. package/dist/components/ui/Tabs.js +99 -87
  71. package/dist/components/ui/Tag.js +12 -9
  72. package/dist/components/ui/Tooltip.js +30 -30
  73. package/dist/components/ui/floatingPresence.d.ts +7 -0
  74. package/dist/components/ui/floatingPresence.js +80 -39
  75. package/dist/components/ui/index.d.ts +2 -1
  76. package/dist/components/ui/picker/DirectoryFavorites.js +27 -27
  77. package/dist/components/ui/picker/PickerBase.js +107 -107
  78. package/dist/context/NotificationContext.js +4 -4
  79. package/dist/context/ThemeContext.js +71 -70
  80. package/dist/floe.css +205 -43
  81. package/dist/full.js +650 -652
  82. package/dist/language.css +320 -0
  83. package/dist/primitives.css +23 -17
  84. package/dist/standalone.css +1 -0
  85. package/dist/styles/themes/classicSemanticTokens.d.ts +122 -122
  86. package/dist/styles/themes/classicSemanticTokens.js +140 -138
  87. package/dist/styles/themes/index.d.ts +3 -1
  88. package/dist/styles/themes/index.js +10 -10
  89. package/dist/styles/themes/presets.d.ts +2 -2
  90. package/dist/styles/themes/presets.js +34 -34
  91. package/dist/styles/tokens.d.ts +152 -68
  92. package/dist/styles/tokens.js +87 -49
  93. package/dist/styles.css +1 -1
  94. package/dist/surface.css +161 -116
  95. package/dist/themes/dark.css +64 -64
  96. package/dist/themes/light.css +64 -64
  97. package/dist/ui.css +171 -255
  98. package/dist/ui.js +176 -178
  99. package/dist/utils/hotInteraction.d.ts +7 -0
  100. package/dist/utils/hotInteraction.js +12 -12
  101. package/dist/widgets/TerminalWidget.js +28 -28
  102. package/dist/workbench-composition.css +15 -15
  103. package/package.json +1 -1
package/dist/surface.css CHANGED
@@ -12,18 +12,22 @@
12
12
  * propagate that private value through its editor/content descendants. */
13
13
  /* Preblend quiet seams onto the carrying background. Opaque borders avoid an
14
14
  * extra blending path when native editors repaint their text and caret. */
15
+ /* The language owns the seams. Surfaces no longer invent their own edge
16
+ * percentages or shadows: a seam is the language's hairline preblended onto the
17
+ * carrying canvas (opaque, so editors repaint text and caret without a second
18
+ * blend pass), and elevation comes from the language's three-step ladder. */
15
19
  :root {
16
- --floe-surface-edge: color-mix(in srgb, var(--foreground) 11.5%, var(--background));
17
- --floe-surface-divider: color-mix(in srgb, var(--foreground) 8.5%, var(--background));
18
- --floe-surface-highlight: rgb(255 255 255 / 50%);
19
- --floe-surface-shade: rgb(0 0 0 / 12%);
20
+ --floe-surface-edge: color-mix(in srgb, var(--foreground) 9%, var(--background));
21
+ --floe-surface-divider: color-mix(in srgb, var(--foreground) 5.5%, var(--background));
22
+ --floe-surface-highlight: transparent;
23
+ --floe-surface-shade: color-mix(in srgb, #000 8%, transparent);
20
24
  }
21
25
 
22
26
  /* Keep dimensions and semantic palette tokens intact. Neutral boundaries become
23
27
  * quiet seams; material and existing fills carry the grouping. High contrast
24
28
  * retains the original boundaries. Explicit rich Card variants stay independent. */
25
29
  @media (forced-colors: none) {
26
- :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']))
30
+ :where(:root:not([data-floe-shell-theme='hc-light']))
27
31
  :is(
28
32
  .border-border,
29
33
  [class~='border-border/40'],
@@ -37,13 +41,24 @@
37
41
  .border-input,
38
42
  [data-floe-input-surface],
39
43
  [data-floe-input-surface] *,
44
+ /* A form field draws its own boundary. The seam is a hairline preblended
45
+ * onto the page ground, which is right for a divider sitting on that
46
+ * ground and wrong for a well carved into a card: 9% over the page
47
+ * against a 4.5% fill lands within one step of the fill, so the edge
48
+ * vanishes instead of quietening. Fields carry `--floe-field-edge`,
49
+ * measured against the plane they actually sit on. */
50
+ input,
51
+ textarea,
52
+ select,
53
+ .floe-field,
54
+ [data-floe-control],
40
55
  [data-floe-surface-part='tab'],
41
56
  [data-floe-card-variant]:not([data-floe-card-variant='default'])
42
57
  )
43
58
  ) {
44
59
  border-color: var(--floe-surface-edge);
45
60
  }
46
- :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']))
61
+ :where(:root:not([data-floe-shell-theme='hc-light']))
47
62
  :is(
48
63
  [data-floe-shell-slot],
49
64
  .workbench-widget__header,
@@ -58,7 +73,7 @@
58
73
  --terminal-panel-border: var(--floe-surface-divider);
59
74
  border-color: var(--floe-surface-divider);
60
75
  }
61
- :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']))
76
+ :where(:root:not([data-floe-shell-theme='hc-light']))
62
77
  :is(
63
78
  .bg-border,
64
79
  .workbench-dock__divider,
@@ -69,41 +84,39 @@
69
84
  ) {
70
85
  background-color: var(--floe-surface-divider);
71
86
  }
72
- :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']))
87
+ :where(:root:not([data-floe-shell-theme='hc-light']))
73
88
  .workbench-canvas__grid {
74
89
  opacity: 0.35;
75
90
  }
76
- :root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])
91
+ :root:not([data-floe-shell-theme='hc-light'])
77
92
  [data-floe-surface-divider] {
78
93
  border-color: var(--floe-surface-divider);
79
94
  }
80
- :root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])
95
+ :root:not([data-floe-shell-theme='hc-light'])
81
96
  .workbench-surface {
82
97
  --wb-dock-material-border: var(--floe-surface-edge);
83
98
  --wb-dock-material-border-top: var(--floe-surface-edge);
84
99
  --wb-dock-material-shadow: var(
85
100
  --floe-surface-shadow-raised,
86
- 0 2px 5px -2px var(--floe-surface-shade),
87
- inset 0 1px 1px var(--floe-surface-highlight)
101
+ 0 2px 5px -2px var(--floe-surface-shade)
88
102
  );
89
103
  }
90
104
  }
91
105
  :root.dark {
92
106
  --floe-surface-highlight: transparent;
93
- --floe-surface-shadow-raised: 0 0 transparent;
107
+ --floe-surface-shadow-raised: var(--floe-elevation-raised);
94
108
  --floe-surface-shadow-inset: 0 0 transparent;
95
- --floe-surface-shade: rgb(0 0 0 / 20%);
109
+ --floe-surface-shade: color-mix(in srgb, #000 34%, transparent);
96
110
  }
97
111
 
98
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface] {
112
+ [data-floe-surface] {
99
113
  --floe-surface-decoration: 0 0 transparent;
100
114
  box-shadow: var(--floe-surface-decoration);
101
115
  }
102
116
 
103
117
  /* Material never owns movement. Keep the motion component's property list,
104
118
  * duration and easing together instead of remapping its transition shorthand. */
105
- [data-floe-surface-style='soft-neumorphic']
106
- [data-floe-surface]:not(
119
+ [data-floe-surface]:not(
107
120
  :where(
108
121
  .floe-floating-presence,
109
122
  [data-floe-surface-part='switch-thumb'],
@@ -114,24 +127,16 @@
114
127
  transition-property: color, background-color, border-color, opacity;
115
128
  }
116
129
 
117
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface='raised'] {
118
- --floe-surface-decoration: var(
119
- --floe-surface-shadow-raised,
120
- 0 1px 3px -2px var(--floe-surface-shade),
121
- inset 0 1px 1px var(--floe-surface-highlight)
122
- );
130
+ [data-floe-surface='raised'] {
131
+ --floe-surface-decoration: var(--floe-surface-shadow-raised, var(--floe-elevation-raised));
123
132
  }
124
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface='inset'] {
125
- --floe-surface-decoration: var(
126
- --floe-surface-shadow-inset,
127
- inset 0 1px 1px var(--floe-surface-shade),
128
- inset 0 -1px 1px var(--floe-surface-highlight)
129
- );
133
+ [data-floe-surface='inset'] {
134
+ --floe-surface-decoration: var(--floe-surface-shadow-inset, 0 0 transparent);
130
135
  }
131
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface='floating'] {
136
+ [data-floe-surface='floating'] {
132
137
  --floe-surface-decoration: var(
133
138
  --floe-surface-shadow-floating,
134
- var(--floe-window-shadow)
139
+ var(--floe-elevation-float)
135
140
  );
136
141
  background-color: var(--popover);
137
142
  -webkit-backdrop-filter: none;
@@ -140,25 +145,24 @@
140
145
 
141
146
  /* The geometry owner must not clip the moving panel's external shadow. Paint
142
147
  * containment belongs on the existing panel that also owns content clipping. */
143
- [data-floe-surface-style='soft-neumorphic'] [data-floe-geometry-surface='floating-window'] {
148
+ [data-floe-geometry-surface='floating-window'] {
144
149
  contain: layout style;
145
150
  }
146
- [data-floe-surface-style='soft-neumorphic'] [data-floe-floating-window-surface='true'] {
151
+ [data-floe-floating-window-surface='true'] {
147
152
  contain: paint;
148
153
  }
149
- [data-floe-surface-style='soft-neumorphic'] [data-floe-dialog-backdrop]:not(.floe-bottom-drawer-backdrop) {
154
+ [data-floe-dialog-backdrop]:not(.floe-bottom-drawer-backdrop) {
150
155
  background-color: rgb(0 0 0 / 16%);
151
156
  -webkit-backdrop-filter: none;
152
157
  backdrop-filter: none;
153
158
  }
154
- :root.dark[data-floe-surface-style='soft-neumorphic'] [data-floe-dialog-backdrop]:not(.floe-bottom-drawer-backdrop) {
159
+ :root.dark[data-floe-dialog-backdrop]:not(.floe-bottom-drawer-backdrop) {
155
160
  background-color: rgb(0 0 0 / 36%);
156
161
  }
157
162
 
158
163
  /* A new selection is visible in its first painted frame. Fading the two faces
159
164
  * leaves the old choice visually active after the native state has changed. */
160
- [data-floe-surface-style='soft-neumorphic']
161
- :is(
165
+ :is(
162
166
  [data-floe-surface-part='choice'],
163
167
  [data-floe-surface-part='indicator'],
164
168
  [data-floe-surface-part='tab'],
@@ -169,18 +173,16 @@
169
173
 
170
174
  /* A compound input has one decorative boundary, including host compositions
171
175
  * that contain an Input component. Dense nested actions stay flat. */
172
- [data-floe-surface-style='soft-neumorphic'] [data-floe-input-surface] [data-floe-surface],
173
- [data-floe-surface-style='soft-neumorphic']
174
- [data-floe-surface='raised']
176
+ [data-floe-input-surface] [data-floe-surface],
177
+ [data-floe-surface='raised']
175
178
  [data-floe-surface='raised']:not([data-floe-surface-part]),
176
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface='flat'] {
179
+ [data-floe-surface='flat'] {
177
180
  --floe-surface-decoration: 0 0 transparent;
178
181
  }
179
182
 
180
183
  /* Decoration cannot replace independent keyboard indicators. Inputs, including
181
184
  * button-based compound fields, continue to use input-focus.css exclusively. */
182
- [data-floe-surface-style='soft-neumorphic']
183
- [data-floe-surface]:focus-visible:not(
185
+ [data-floe-surface]:focus-visible:not(
184
186
  :where(input, textarea, select, [data-floe-input-surface], [data-floe-input-surface] *)
185
187
  ) {
186
188
  box-shadow:
@@ -189,24 +191,23 @@
189
191
  var(--floe-surface-decoration);
190
192
  }
191
193
 
192
- [data-floe-surface-style='soft-neumorphic'] .workbench-widget[data-floe-surface] {
194
+ .workbench-widget[data-floe-surface] {
193
195
  /* Some local palettes use translucent body tints. Composite onto an opaque
194
196
  * card base instead of reintroducing glass or letting content bleed through. */
195
197
  background: linear-gradient(var(--wb-widget-body), var(--wb-widget-body)), var(--card);
196
198
  }
197
- [data-floe-surface-style='soft-neumorphic'] .workbench-widget.is-selected {
199
+ .workbench-widget.is-selected {
198
200
  border-color: transparent;
199
201
  }
200
- [data-floe-surface-style='soft-neumorphic']
201
- .workbench-widget.is-selected
202
+ .workbench-widget.is-selected
202
203
  .workbench-widget__header {
203
204
  background: color-mix(in srgb, var(--wb-accent) 8%, var(--card));
204
205
  color: var(--wb-accent);
205
206
  }
206
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface-interacting='true'] {
207
+ [data-floe-surface-interacting='true'] {
207
208
  --floe-surface-decoration: var(
208
209
  --floe-surface-shadow-interacting,
209
- 0 1px 2px var(--floe-surface-shade)
210
+ var(--floe-elevation-raised)
210
211
  );
211
212
  transition: none;
212
213
  }
@@ -215,13 +216,11 @@
215
216
  * Tight static highlights carry material without large soft shadows per item.
216
217
  * Functional transforms, progress widths, and SVG strokes retain their motion. */
217
218
  @media (forced-colors: none) {
218
- :root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']) {
219
- --floe-surface-compact-raised:
220
- 0 1px 2px var(--floe-surface-shade), inset 0 1px 1px var(--floe-surface-highlight);
221
- --floe-surface-compact-inset:
222
- inset 0 1px 1px var(--floe-surface-shade), inset 0 -1px 1px var(--floe-surface-highlight);
219
+ :root:not([data-floe-shell-theme='hc-light']) {
220
+ --floe-surface-compact-raised: var(--floe-elevation-raised);
221
+ --floe-surface-compact-inset: 0 0 transparent;
223
222
  }
224
- :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])) {
223
+ :where(:root:not([data-floe-shell-theme='hc-light'])) {
225
224
  & [data-floe-surface-part][data-floe-surface='raised'] {
226
225
  --floe-surface-decoration: var(
227
226
  --floe-surface-shadow-raised,
@@ -234,12 +233,28 @@
234
233
  var(--floe-surface-compact-inset)
235
234
  );
236
235
  }
236
+ /* Two tags, two gestures. An outline rides along with content; a wash
237
+ * wins the eye inside a list. The accent is the tone's own color, so one
238
+ * recipe covers every semantic variant in every preset. */
239
+ &
240
+ [data-floe-surface-part='badge'][data-floe-tone='soft']:not(
241
+ [data-floe-surface='flat']
242
+ ) {
243
+ background-color: transparent;
244
+ border-color: var(--floe-line-strong);
245
+ }
246
+ &
247
+ [data-floe-surface-part='badge'][data-floe-tone='solid']:not(
248
+ [data-floe-surface='flat']
249
+ ) {
250
+ background-color: color-mix(in srgb, var(--tag-accent) 15%, transparent);
251
+ border-color: color-mix(in srgb, var(--tag-accent) 26%, transparent);
252
+ }
237
253
  & [data-floe-surface-part='badge'] {
238
254
  /* These can repeat in dense lists. Keep lighting inside the painted box. */
239
255
  --floe-surface-decoration: var(
240
256
  --floe-surface-shadow-raised,
241
- inset 0 1px 1px var(--floe-surface-highlight),
242
- inset 0 -1px 1px var(--floe-surface-shade)
257
+ 0 0 0 1px color-mix(in srgb, var(--foreground) 6%, transparent)
243
258
  );
244
259
  }
245
260
  & [data-floe-surface-part='badge'][data-floe-surface='inset'],
@@ -259,10 +274,16 @@
259
274
  --floe-surface-decoration: var(--floe-surface-shadow-raised, none);
260
275
  }
261
276
  & :is([data-floe-surface-part='rail'], [data-floe-surface-part='tab-rail']) {
262
- background-color: color-mix(in srgb, var(--card) 65%, var(--muted));
277
+ background-color: var(--floe-well);
263
278
  }
264
279
  /* Keep the existing box metrics, but let the carrying fill reach the edge.
265
280
  * An opaque seam based on the page background becomes a dark frame here. */
281
+ /* A card is a filled plane on the canvas. Its separation is the fill step
282
+ * plus the canvas gutter, so it carries no contour and no resting shadow. */
283
+ & [data-floe-card-variant='default'][data-floe-surface] {
284
+ border-color: transparent;
285
+ --floe-surface-decoration: 0 0 transparent;
286
+ }
266
287
  &
267
288
  :is(
268
289
  [data-floe-card-variant='default'],
@@ -282,26 +303,47 @@
282
303
  [data-floe-surface-part='choice']:not([data-floe-selected='true']):hover:not(
283
304
  :has(:disabled)
284
305
  ) {
285
- background-color: color-mix(in srgb, var(--foreground) 4%, var(--card));
306
+ background-color: var(--floe-state-hover);
286
307
  }
287
308
  & [data-floe-surface-part='choice'][data-floe-selected='true'] {
288
- background-color: var(--accent);
289
- }
290
- /* Compact choices need a clear selected face, independent of material.
291
- * Large choice cards retain their native dot/checkmark and quiet tint. */
309
+ background-color: var(--floe-state-selected);
310
+ }
311
+ /* A committed compact choice is a filled control, because the fill is the
312
+ * state indicator and has to carry 3:1 against its rail by itself. A
313
+ * translucent wash with a 34% accent hairline measures about 1.1:1, which
314
+ * reads as the same surface turned slightly warmer, not as a selection.
315
+ * The semantic pair is the one opaque thing on the rail; relief would only
316
+ * restate the fill, so it stays off. */
292
317
  & [data-floe-choice-variant='button'][data-floe-selected='true'],
293
318
  & [data-floe-surface-part='segment'][aria-checked='true'] {
294
319
  background-color: var(--primary);
295
320
  color: var(--primary-foreground);
321
+ border-color: var(--primary);
296
322
  --floe-surface-decoration: 0 0 transparent;
297
323
  }
324
+ /* A selected tab is a surface sitting proud of its rail: solid fill, the
325
+ * language's hairline and the first elevation step. This is the one place a
326
+ * small control is allowed relief, because the rail sits directly behind it. */
327
+ & [data-floe-surface-part='tab']:not([aria-selected='true']):hover:not(:has(:disabled)) {
328
+ background-color: var(--floe-state-hover);
329
+ color: var(--foreground);
330
+ }
298
331
  & [data-floe-surface-part='tab'][aria-selected='true'] {
299
- background-color: var(--accent);
332
+ background-color: var(--card);
300
333
  color: var(--foreground);
334
+ border-color: var(--floe-line);
335
+ --floe-surface-decoration: var(--floe-elevation-raised);
301
336
  }
302
337
  & [data-floe-surface-part='segment'][aria-checked='false'] {
303
338
  background-color: transparent;
304
339
  }
340
+ &
341
+ [data-floe-surface-part='segment'][aria-checked='false']:hover:not(
342
+ :disabled
343
+ ):not(:has(:disabled)) {
344
+ background-color: var(--floe-state-hover);
345
+ color: var(--foreground);
346
+ }
305
347
  & [data-floe-surface-part='tab'].border-primary {
306
348
  border-color: color-mix(in srgb, var(--primary) 24%, var(--card));
307
349
  }
@@ -314,8 +356,8 @@
314
356
  background-color: var(--card);
315
357
  }
316
358
  & [data-floe-surface-part='step'][data-floe-step-state='current'] {
317
- background-color: color-mix(in srgb, var(--primary) 16%, var(--card));
318
- color: var(--primary);
359
+ background-color: var(--floe-state-selected-strong);
360
+ color: var(--foreground);
319
361
  }
320
362
  & [data-floe-surface-part='indicator']:not([data-floe-selected='true']),
321
363
  & [data-floe-surface-part='switch-track']:not([data-floe-selected='true']) {
@@ -332,13 +374,8 @@
332
374
  [data-floe-surface-part='indicator'],
333
375
  [data-floe-surface-part='switch-track']
334
376
  )[data-floe-surface='inset'] {
335
- /* At 14–24 px, a one-pixel light edge and tight well stay legible at 1x.
336
- * Broad opposing blurs make both the contour and the glyph look unfocused. */
337
- --floe-surface-decoration: var(
338
- --floe-surface-shadow-inset,
339
- inset 0 1px 1px var(--floe-surface-shade),
340
- inset 0 -1px 0 var(--floe-surface-highlight)
341
- );
377
+ /* Small indicators rely on their own fill contrast, not on relief. */
378
+ --floe-surface-decoration: var(--floe-surface-shadow-inset, 0 0 transparent);
342
379
  }
343
380
  & [data-floe-surface-part='switch-track'] {
344
381
  transition-duration: 140ms;
@@ -348,7 +385,7 @@
348
385
  /* A hollow hairline keeps an unchecked radio quieter than its selected dot.
349
386
  * Border-box dimensions and hit targets stay fixed; there is no blurred well. */
350
387
  border-width: 1px;
351
- border-color: color-mix(in srgb, var(--muted-foreground) 88%, var(--card));
388
+ border-color: color-mix(in srgb, var(--muted-foreground) 72%, var(--card));
352
389
  background-color: transparent;
353
390
  --floe-surface-decoration: var(--floe-surface-shadow-inset, none);
354
391
  }
@@ -356,7 +393,7 @@
356
393
  [data-floe-surface-part='indicator'][data-floe-selected='true']:has(
357
394
  [data-floe-surface-part='radio-dot']
358
395
  ) {
359
- background-color: color-mix(in srgb, var(--primary) 6%, var(--card));
396
+ background-color: var(--floe-state-selected);
360
397
  }
361
398
  & [data-floe-surface-part='radio-dot'] {
362
399
  background-color: var(--foreground);
@@ -366,10 +403,8 @@
366
403
  transition-property: width, background-color;
367
404
  }
368
405
  & [data-floe-surface-part='action']:active:not(:disabled) {
369
- --floe-surface-decoration: var(
370
- --floe-surface-shadow-inset,
371
- var(--floe-surface-compact-inset)
372
- );
406
+ /* Press settles the control instead of embossing it. */
407
+ --floe-surface-decoration: 0 0 transparent;
373
408
  }
374
409
  /* Hidden native inputs delegate their indicator to the visible existing
375
410
  * face. A pointer selection does not receive this keyboard-only outline. */
@@ -382,34 +417,34 @@
382
417
  box-shadow: var(--floe-surface-decoration);
383
418
  }
384
419
  }
385
- :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])) {
420
+ :where(:root:not([data-floe-shell-theme='hc-light'])) {
386
421
  & :is([data-floe-input-surface], input, textarea, select)[data-floe-surface='inset'] {
387
- background-color: color-mix(in srgb, var(--card) 50%, var(--background));
422
+ background-color: var(--floe-well);
388
423
  }
389
424
  }
390
425
  }
391
426
 
392
- /* Text-labelled actions, rails and fields use one contour. Stacked inset and
393
- * contact shadows turn a quiet toolbar into an embossed frame in light mode.
427
+ /* Text-labelled actions and fields use one contour. Stacked inset and contact
428
+ * shadows turn a quiet toolbar into an embossed frame.
429
+ * A rail and a tab rail are wells, not planes: they recede behind their
430
+ * segments, and the relief belongs to the one selected item inside them. Giving
431
+ * the well the raised step made it cast a drop shadow in dark palettes while
432
+ * staying flat in light ones, so the same control had two different shapes.
394
433
  * Small check/switch indicators retain their independent relief and contrast. */
395
- [data-floe-surface-style='soft-neumorphic']
396
- :is(
434
+ :is(
397
435
  [data-floe-surface-part='action'],
398
- [data-floe-surface-part='rail'],
399
- [data-floe-surface-part='tab-rail'],
400
436
  [data-floe-surface-part='tab'],
401
437
  [data-floe-surface-part='segment']
402
438
  )[data-floe-surface] {
403
439
  --floe-surface-decoration: var(--floe-surface-shadow-raised, 0 0 transparent);
404
440
  }
405
- [data-floe-surface-style='soft-neumorphic']
406
- :is([data-floe-input-surface], input, textarea, select)[data-floe-surface='inset'] {
441
+ :is([data-floe-input-surface], input, textarea, select)[data-floe-surface='inset'] {
407
442
  --floe-surface-decoration: var(--floe-surface-shadow-inset, 0 0 transparent);
408
443
  }
409
444
 
410
445
  /* Explicit host opt-outs and compound input boundaries outrank compact states. */
411
- :root[data-floe-surface-style='soft-neumorphic'] [data-floe-surface='flat'],
412
- :root[data-floe-surface-style='soft-neumorphic'] [data-floe-input-surface] [data-floe-surface] {
446
+ :root[data-floe-surface='flat'],
447
+ :root[data-floe-input-surface] [data-floe-surface] {
413
448
  --floe-surface-decoration: 0 0 transparent;
414
449
  }
415
450
 
@@ -417,7 +452,7 @@
417
452
  * distinct from ordinary Cards, Dialogs and menus. Only this visible panel owns
418
453
  * material; the outer geometry owner and its input lifecycle stay unchanged. */
419
454
  @media (forced-colors: none) {
420
- :root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])
455
+ :root:not([data-floe-shell-theme='hc-light'])
421
456
  [data-floe-floating-window-surface='true'][data-floe-surface='floating'] {
422
457
  --floe-window-radius: var(--floe-radius-floating);
423
458
  background: var(--floe-window-background);
@@ -462,36 +497,45 @@
462
497
  }
463
498
  }
464
499
 
465
- /* Dark shell palettes use a restrained dark step for active chrome so the
466
- * window remains discoverable without a bright outline or animated glow. */
467
- :root.dark[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])
500
+ /* Dark shell palettes use a restrained step for active chrome so the window
501
+ * remains discoverable without a bright outline or animated glow. The step is
502
+ * taken toward the foreground, not toward the page ground: an accent wash alone
503
+ * lands within three 8-bit units of a near-black window body (soft-black
504
+ * measured 19,20,21 on 16,17,19), so the titlebar stopped being identifiable in
505
+ * grayscale at all. Foreground is light in every dark preset, so mixing 8% of it
506
+ * guarantees an upward step from any accent while title text keeps 5.3:1 or
507
+ * better. Light presets take no such step: their background is already far from
508
+ * the accent wash, and darkening it there would eat title text contrast. */
509
+ :root.dark:not([data-floe-shell-theme='hc-light'])
468
510
  [data-floe-floating-window-surface='true'][data-floe-floating-window-state='active']
469
511
  > [data-floe-floating-window-titlebar='true'] {
470
- background-color: color-mix(in srgb, var(--background) 38%, var(--floe-window-titlebar-background));
512
+ background-color: color-mix(
513
+ in srgb,
514
+ var(--accent) 24%,
515
+ color-mix(in srgb, var(--foreground) 8%, var(--floe-window-titlebar-background))
516
+ );
471
517
  }
472
518
 
473
519
  /* FloatingWindow keeps rich text at scale 1 throughout its short presence
474
- * motion. Only presence promotes this panel; the existing geometry owner
475
- * already promotes direct drag/resize, so adding a second hot layer is wasteful. */
476
- :root[data-floe-surface-style='soft-neumorphic'] .floe-floating-window-motion {
520
+ * motion: a window is a large reading surface, so a scale that suits a menu
521
+ * would blur its text mid-flight. The class lives on the panel, not on <html>,
522
+ * so the selector has to match the panel or the override never applies.
523
+ *
524
+ * Promotion belongs to the two transitioning states only; a settled window must
525
+ * not hold a compositor layer (see .floe-floating-presence in floe.css). */
526
+ .floe-floating-window-motion {
477
527
  --floe-floating-enter-scale: 1;
478
528
  --floe-floating-exit-scale: 1;
479
529
  --floe-floating-enter-y: 3px;
480
530
  --floe-floating-exit-y: 2px;
481
- will-change: auto;
482
- }
483
- :root[data-floe-surface-style='soft-neumorphic'] .floe-floating-window-motion:is(
484
- [data-floating-presence='entering'], [data-floating-presence='exiting']
485
- ) {
486
- will-change: opacity, transform;
487
531
  }
488
532
 
489
533
  /* High contrast stays flat, with real boundaries and keyboard indicators. */
490
- :root[data-floe-surface-style='soft-neumorphic'][data-floe-shell-theme='hc-light']
534
+ :root[data-floe-shell-theme='hc-light']
491
535
  [data-floe-surface] {
492
536
  --floe-surface-decoration: 0 0 transparent;
493
537
  }
494
- :root[data-floe-surface-style='soft-neumorphic'][data-floe-shell-theme='hc-light']
538
+ :root[data-floe-shell-theme='hc-light']
495
539
  .workbench-widget.is-selected {
496
540
  border-color: var(--wb-accent);
497
541
  }
@@ -505,25 +549,28 @@
505
549
  box-shadow: none;
506
550
  transition: none;
507
551
  }
508
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface] {
552
+ [data-floe-surface] {
509
553
  --floe-surface-decoration: 0 0 transparent;
510
554
  }
511
- [data-floe-surface-style='soft-neumorphic']
512
555
  [data-floe-surface]:focus-visible:not(
513
556
  :where(input, textarea, select, [data-floe-input-surface], [data-floe-input-surface] *)
514
557
  ) {
515
558
  outline: 2px solid Highlight;
516
559
  outline-offset: 2px;
517
560
  }
518
- [data-floe-surface-style='soft-neumorphic'] .workbench-widget.is-selected {
561
+ .workbench-widget.is-selected {
519
562
  border-color: Highlight;
520
563
  }
521
564
  }
522
565
  @media (prefers-reduced-motion: reduce) {
523
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface] {
566
+ /* `:root` carries the specificity this reset needs: a surface part may pair
567
+ * two attribute selectors to own a transition of its own, and a bare
568
+ * `[data-floe-surface]` would lose to it. Reduced motion is a user setting,
569
+ * so it must win without `!important`. */
570
+ :root [data-floe-surface] {
524
571
  transition: none;
525
572
  }
526
- [data-floe-surface-style='soft-neumorphic'] [data-floe-surface-part='indicator'] > svg {
573
+ :root [data-floe-surface-part='indicator'] > svg {
527
574
  animation: none;
528
575
  }
529
576
  }
@@ -539,18 +586,16 @@
539
586
  }
540
587
  }
541
588
 
542
- :root[data-floe-surface-style='soft-neumorphic'][data-floe-shell-theme='hc-light']
589
+ :root[data-floe-shell-theme='hc-light']
543
590
  [data-floe-surface-part='choice']:has([data-floe-choice-input]:focus-visible),
544
- :root[data-floe-surface-style='soft-neumorphic'][data-floe-shell-theme='hc-light']
591
+ :root[data-floe-shell-theme='hc-light']
545
592
  [data-floe-choice-input]:focus-visible
546
593
  + :is([data-floe-surface-part='indicator'], [data-floe-surface-part='switch-track']) {
547
594
  outline: 2px solid var(--ring);
548
595
  outline-offset: 3px;
549
596
  }
550
597
  @media (forced-colors: active) {
551
- [data-floe-surface-style='soft-neumorphic']
552
598
  [data-floe-surface-part='choice']:has([data-floe-choice-input]:focus-visible),
553
- [data-floe-surface-style='soft-neumorphic']
554
599
  [data-floe-choice-input]:focus-visible
555
600
  + :is([data-floe-surface-part='indicator'], [data-floe-surface-part='switch-track']) {
556
601
  outline: 2px solid Highlight;