@floegence/floe-webapp-core 0.50.6 → 0.52.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 (46) hide show
  1. package/dist/components/chat/input/ChatInput.js +8 -8
  2. package/dist/components/ui/Button.js +16 -13
  3. package/dist/components/ui/Card.js +125 -119
  4. package/dist/components/ui/Checkbox.js +178 -132
  5. package/dist/components/ui/CommandPalette.js +10 -10
  6. package/dist/components/ui/Dialog.d.ts +7 -0
  7. package/dist/components/ui/Dialog.js +160 -114
  8. package/dist/components/ui/DirectoryInput.js +5 -5
  9. package/dist/components/ui/Dropdown.d.ts +2 -0
  10. package/dist/components/ui/Dropdown.js +183 -160
  11. package/dist/components/ui/FloatingWindow.js +211 -199
  12. package/dist/components/ui/HighlightBlock.js +8 -8
  13. package/dist/components/ui/Input.js +40 -40
  14. package/dist/components/ui/Pagination.js +90 -88
  15. package/dist/components/ui/ProcessingIndicator.js +1 -1
  16. package/dist/components/ui/Progress.js +99 -88
  17. package/dist/components/ui/QuoteBlock.js +9 -9
  18. package/dist/components/ui/Radio.js +151 -127
  19. package/dist/components/ui/SegmentedControl.js +26 -25
  20. package/dist/components/ui/Stepper.js +114 -112
  21. package/dist/components/ui/Switch.js +34 -33
  22. package/dist/components/ui/Tabs.js +153 -150
  23. package/dist/components/ui/Tag.js +24 -21
  24. package/dist/components/ui/Tooltip.js +10 -10
  25. package/dist/components/ui/floatingPresence.js +40 -30
  26. package/dist/components/workbench/WorkbenchHud.js +9 -9
  27. package/dist/components/workbench/WorkbenchLockButton.js +4 -4
  28. package/dist/components/workbench/WorkbenchWidget.js +46 -43
  29. package/dist/context/FloeConfigContext.d.ts +5 -1
  30. package/dist/context/FloeConfigContext.js +10 -9
  31. package/dist/context/ThemeContext.d.ts +3 -1
  32. package/dist/context/ThemeContext.js +78 -71
  33. package/dist/floe.css +32 -0
  34. package/dist/full.js +839 -836
  35. package/dist/hooks/useOverlayMask.js +95 -83
  36. package/dist/index.js +150 -147
  37. package/dist/input-focus.css +0 -1
  38. package/dist/styles/themes/index.d.ts +3 -0
  39. package/dist/styles/themes/index.js +40 -31
  40. package/dist/styles/tokens.d.ts +36 -0
  41. package/dist/styles/tokens.js +12 -5
  42. package/dist/styles.css +1 -1
  43. package/dist/surface.css +467 -0
  44. package/dist/themes.d.ts +1 -1
  45. package/dist/ui.css +9 -0
  46. package/package.json +1 -1
@@ -0,0 +1,467 @@
1
+ /* Surface roles change decoration only. The existing element owns layout,
2
+ * input, portals, and focus. Unlayered rules replace component/utility shadows.
3
+ * Token fallbacks resolve on the surface, so local Workbench colors participate
4
+ * and root-level theme.tokens overrides remain authoritative. */
5
+ @property --floe-surface-decoration {
6
+ syntax: '*';
7
+ inherits: false;
8
+ initial-value: 0 0 transparent;
9
+ }
10
+
11
+ /* Only the visible boundary owns its resolved decoration. A hot shell must not
12
+ * propagate that private value through its editor/content descendants. */
13
+ /* Preblend quiet seams onto the carrying background. Opaque borders avoid an
14
+ * extra blending path when native editors repaint their text and caret. */
15
+ :root {
16
+ --floe-surface-edge: color-mix(in srgb, var(--border) 18%, var(--background));
17
+ --floe-surface-divider: color-mix(in srgb, var(--border) 12%, var(--background));
18
+ --floe-surface-highlight: rgb(255 255 255 / 50%);
19
+ --floe-surface-shade: rgb(0 0 0 / 12%);
20
+ }
21
+
22
+ /* Keep dimensions and semantic palette tokens intact. Neutral boundaries become
23
+ * quiet seams; material and existing fills carry the grouping. High contrast
24
+ * retains the original boundaries. Explicit rich Card variants stay independent. */
25
+ @media (forced-colors: none) {
26
+ :root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']) {
27
+ /* A uniform muted border keeps focus readable without a bright frame.
28
+ * Different colors per edge force a costlier native-editor raster path. */
29
+ --floe-input-focus-color: color-mix(in srgb, var(--muted-foreground) 88%, var(--card));
30
+ }
31
+ :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']))
32
+ :is(
33
+ .border-border,
34
+ .border-input,
35
+ [class~='border-border/40'],
36
+ [data-floe-surface],
37
+ .workbench-hud,
38
+ .workbench-lock-button,
39
+ .workbench-overlay__frame
40
+ ):not(
41
+ :where(
42
+ [aria-invalid='true'],
43
+ [data-floe-surface-part='tab'],
44
+ [data-floe-card-variant]:not([data-floe-card-variant='default'])
45
+ )
46
+ ) {
47
+ border-color: var(--floe-surface-edge);
48
+ }
49
+ :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']))
50
+ :is(
51
+ [data-floe-shell-slot],
52
+ .workbench-widget__header,
53
+ [data-floe-floating-window-titlebar],
54
+ .chat-input-toolbar,
55
+ .workbench-overlay__header,
56
+ [data-floe-surface-divider]
57
+ ) {
58
+ --top-bar-border: var(--floe-surface-divider);
59
+ --bottom-bar-border: var(--floe-surface-divider);
60
+ --activity-bar-border: var(--floe-surface-divider);
61
+ --terminal-panel-border: var(--floe-surface-divider);
62
+ border-color: var(--floe-surface-divider);
63
+ }
64
+ :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']))
65
+ :is(
66
+ .bg-border,
67
+ .workbench-dock__divider,
68
+ .workbench-hud__divider,
69
+ .workbench-context-menu__separator,
70
+ .workbench-overlay__header-separator,
71
+ [data-floe-surface-divider='fill']
72
+ ) {
73
+ background-color: var(--floe-surface-divider);
74
+ }
75
+ :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']))
76
+ .workbench-canvas__grid {
77
+ opacity: 0.35;
78
+ }
79
+ :root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])
80
+ [data-floe-surface-divider] {
81
+ border-color: var(--floe-surface-divider);
82
+ }
83
+ :root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])
84
+ .workbench-surface {
85
+ --wb-dock-material-border: var(--floe-surface-edge);
86
+ --wb-dock-material-border-top: var(--floe-surface-edge);
87
+ --wb-dock-material-shadow: var(
88
+ --floe-surface-shadow-raised,
89
+ 0 2px 5px -2px var(--floe-surface-shade),
90
+ inset 0 1px 1px var(--floe-surface-highlight)
91
+ );
92
+ }
93
+ }
94
+ :root.dark {
95
+ --floe-surface-highlight: rgb(255 255 255 / 4%);
96
+ --floe-surface-shade: rgb(0 0 0 / 20%);
97
+ }
98
+
99
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface] {
100
+ --floe-surface-decoration: 0 0 transparent;
101
+ box-shadow: var(--floe-surface-decoration);
102
+ }
103
+
104
+ /* Material never owns movement. Keep the motion component's property list,
105
+ * duration and easing together instead of remapping its transition shorthand. */
106
+ [data-floe-surface-style='soft-neumorphic']
107
+ [data-floe-surface]:not(
108
+ :where(
109
+ .floe-floating-presence,
110
+ [data-floe-surface-part='switch-thumb'],
111
+ [data-floe-surface-part='radio-dot'],
112
+ [data-floe-surface-part='progress-fill']
113
+ )
114
+ ) {
115
+ transition-property: color, background-color, border-color, opacity;
116
+ }
117
+
118
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface='raised'] {
119
+ --floe-surface-decoration: var(
120
+ --floe-surface-shadow-raised,
121
+ 0 2px 5px -2px var(--floe-surface-shade),
122
+ inset 0 1px 1px var(--floe-surface-highlight)
123
+ );
124
+ }
125
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface='inset'] {
126
+ --floe-surface-decoration: var(
127
+ --floe-surface-shadow-inset,
128
+ inset 0 1px 2px var(--floe-surface-shade),
129
+ inset 0 -1px 1px var(--floe-surface-highlight)
130
+ );
131
+ }
132
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface='floating'] {
133
+ --floe-surface-decoration: var(
134
+ --floe-surface-shadow-floating,
135
+ 0 8px 20px -6px var(--floe-surface-shade),
136
+ inset 0 1px 1px var(--floe-surface-highlight)
137
+ );
138
+ background-color: var(--card);
139
+ -webkit-backdrop-filter: none;
140
+ backdrop-filter: none;
141
+ }
142
+
143
+ /* The geometry owner must not clip the moving panel's external shadow. Paint
144
+ * containment belongs on the existing panel that also owns content clipping. */
145
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-geometry-surface='floating-window'] {
146
+ contain: layout style;
147
+ }
148
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-floating-window-surface='true'] {
149
+ contain: paint;
150
+ }
151
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-dialog-backdrop]:not(.floe-bottom-drawer-backdrop) {
152
+ background-color: rgb(0 0 0 / 16%);
153
+ -webkit-backdrop-filter: none;
154
+ backdrop-filter: none;
155
+ }
156
+ :root.dark[data-floe-surface-style='soft-neumorphic'] [data-floe-dialog-backdrop]:not(.floe-bottom-drawer-backdrop) {
157
+ background-color: rgb(0 0 0 / 36%);
158
+ }
159
+
160
+ /* A new selection is visible in its first painted frame. Fading the two faces
161
+ * leaves the old choice visually active after the native state has changed. */
162
+ [data-floe-surface-style='soft-neumorphic']
163
+ :is(
164
+ [data-floe-surface-part='choice'],
165
+ [data-floe-surface-part='indicator'],
166
+ [data-floe-surface-part='tab'],
167
+ [data-floe-surface-part='segment']
168
+ ) {
169
+ transition: none;
170
+ }
171
+
172
+ /* A compound input has one decorative boundary, including host compositions
173
+ * that contain an Input component. Dense nested actions stay flat. */
174
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-input-surface] [data-floe-surface],
175
+ [data-floe-surface-style='soft-neumorphic']
176
+ [data-floe-surface='raised']
177
+ [data-floe-surface='raised']:not([data-floe-surface-part]),
178
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface='flat'] {
179
+ --floe-surface-decoration: 0 0 transparent;
180
+ }
181
+
182
+ /* Decoration cannot replace independent keyboard indicators. Inputs, including
183
+ * button-based compound fields, continue to use input-focus.css exclusively. */
184
+ [data-floe-surface-style='soft-neumorphic']
185
+ [data-floe-surface]:focus-visible:not(
186
+ :where(input, textarea, select, [data-floe-input-surface], [data-floe-input-surface] *)
187
+ ) {
188
+ box-shadow:
189
+ 0 0 0 2px var(--background),
190
+ 0 0 0 4px var(--ring),
191
+ var(--floe-surface-decoration);
192
+ }
193
+
194
+ [data-floe-surface-style='soft-neumorphic'] .workbench-widget[data-floe-surface] {
195
+ /* Some local palettes use translucent body tints. Composite onto an opaque
196
+ * card base instead of reintroducing glass or letting content bleed through. */
197
+ background: linear-gradient(var(--wb-widget-body), var(--wb-widget-body)), var(--card);
198
+ }
199
+ [data-floe-surface-style='soft-neumorphic'] .workbench-widget.is-selected {
200
+ border-color: transparent;
201
+ }
202
+ [data-floe-surface-style='soft-neumorphic']
203
+ .workbench-widget.is-selected
204
+ .workbench-widget__header {
205
+ background: color-mix(in srgb, var(--wb-accent) 8%, var(--card));
206
+ color: var(--wb-accent);
207
+ }
208
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface-interacting='true'] {
209
+ --floe-surface-decoration: var(
210
+ --floe-surface-shadow-interacting,
211
+ 0 1px 2px var(--floe-surface-shade)
212
+ );
213
+ transition: none;
214
+ }
215
+
216
+ /* Compact controls use the same four roles at their existing visible boundary.
217
+ * Tight static highlights carry material without large soft shadows per item.
218
+ * Functional transforms, progress widths, and SVG strokes retain their motion. */
219
+ @media (forced-colors: none) {
220
+ :root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light']) {
221
+ --floe-surface-compact-raised:
222
+ 0 1px 2px var(--floe-surface-shade), inset 0 1px 1px var(--floe-surface-highlight);
223
+ --floe-surface-compact-inset:
224
+ inset 0 1px 1px var(--floe-surface-shade), inset 0 -1px 1px var(--floe-surface-highlight);
225
+ }
226
+ :where(:root[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])) {
227
+ & [data-floe-surface-part][data-floe-surface='raised'] {
228
+ --floe-surface-decoration: var(
229
+ --floe-surface-shadow-raised,
230
+ var(--floe-surface-compact-raised)
231
+ );
232
+ }
233
+ & [data-floe-surface-part][data-floe-surface='inset'] {
234
+ --floe-surface-decoration: var(
235
+ --floe-surface-shadow-inset,
236
+ var(--floe-surface-compact-inset)
237
+ );
238
+ }
239
+ & [data-floe-surface-part='badge'] {
240
+ /* These can repeat in dense lists. Keep lighting inside the painted box. */
241
+ --floe-surface-decoration: var(
242
+ --floe-surface-shadow-raised,
243
+ inset 0 1px 1px var(--floe-surface-highlight),
244
+ inset 0 -1px 1px var(--floe-surface-shade)
245
+ );
246
+ }
247
+ & [data-floe-surface-part='badge'][data-floe-surface='inset'],
248
+ & [data-floe-surface-part='progress-fill'][data-floe-surface='inset'] {
249
+ --floe-surface-decoration: var(
250
+ --floe-surface-shadow-inset,
251
+ var(--floe-surface-compact-inset)
252
+ );
253
+ }
254
+ &
255
+ :is(
256
+ [data-floe-surface-part='switch-thumb'],
257
+ [data-floe-surface-part='progress-fill']
258
+ )[data-floe-surface='raised'] {
259
+ /* Frequent travel and width updates must not repaint decorative shadows.
260
+ * The fixed track carries depth; explicit host shadow overrides still apply. */
261
+ --floe-surface-decoration: var(--floe-surface-shadow-raised, none);
262
+ }
263
+ & :is([data-floe-surface-part='rail'], [data-floe-surface-part='tab-rail']) {
264
+ background-color: color-mix(in srgb, var(--card) 65%, var(--muted));
265
+ }
266
+ /* Keep the existing box metrics, but let the carrying fill reach the edge.
267
+ * An opaque seam based on the page background becomes a dark frame here. */
268
+ &
269
+ :is(
270
+ [data-floe-card-variant='default'],
271
+ [data-floe-surface-part='choice'],
272
+ [data-floe-surface-part='indicator'],
273
+ [data-floe-surface-part='rail'],
274
+ [data-floe-surface-part='tab-rail'],
275
+ [data-floe-surface-part='segment'],
276
+ [data-floe-surface-part='badge']
277
+ )[data-floe-surface] {
278
+ border-color: transparent;
279
+ }
280
+ & [data-floe-surface-part='choice'] {
281
+ background-color: var(--card);
282
+ }
283
+ &
284
+ [data-floe-surface-part='choice']:not([data-floe-selected='true']):hover:not(
285
+ :has(:disabled)
286
+ ) {
287
+ background-color: color-mix(in srgb, var(--primary) 3%, var(--card));
288
+ }
289
+ & [data-floe-surface-part='choice'][data-floe-selected='true'] {
290
+ background-color: color-mix(in srgb, var(--primary) 8%, var(--card));
291
+ }
292
+ & [data-floe-choice-variant='button'][data-floe-selected='true'] {
293
+ color: var(--foreground);
294
+ }
295
+ & [data-floe-surface-part='segment'][aria-checked='true'],
296
+ & [data-floe-surface-part='tab'][aria-selected='true'] {
297
+ background-color: color-mix(in srgb, var(--primary) 8%, var(--card));
298
+ color: var(--foreground);
299
+ }
300
+ & [data-floe-surface-part='segment'][aria-checked='false'] {
301
+ background-color: transparent;
302
+ }
303
+ & [data-floe-surface-part='tab'].border-primary {
304
+ border-color: color-mix(in srgb, var(--primary) 24%, var(--card));
305
+ }
306
+ & [data-floe-surface-part='indicator'] > svg {
307
+ animation: floe-choice-mark-in 100ms ease-out;
308
+ }
309
+
310
+ & [data-floe-surface-part='progress-circle'] {
311
+ border-radius: 50%;
312
+ background-color: var(--card);
313
+ }
314
+ & [data-floe-surface-part='step'][data-floe-step-state='current'] {
315
+ background-color: color-mix(in srgb, var(--primary) 16%, var(--card));
316
+ color: var(--primary);
317
+ }
318
+ & [data-floe-surface-part='indicator']:not([data-floe-selected='true']),
319
+ & [data-floe-surface-part='switch-track']:not([data-floe-selected='true']) {
320
+ /* The control face, not a strong outline, supplies non-text contrast. */
321
+ background-color: var(--muted-foreground);
322
+ }
323
+ & [data-floe-surface-part='switch-thumb'][data-floe-surface='raised'] {
324
+ /* Retain the component's stable background fill in both states. Changing
325
+ * its paint while translating defeats cheap motion in dark palettes. */
326
+ transition: translate 140ms cubic-bezier(0.2, 0.8, 0.2, 1);
327
+ }
328
+ &
329
+ :is(
330
+ [data-floe-surface-part='indicator'],
331
+ [data-floe-surface-part='switch-track']
332
+ )[data-floe-surface='inset'] {
333
+ /* At 14–24 px, a one-pixel light edge and tight well stay legible at 1x.
334
+ * Broad opposing blurs make both the contour and the glyph look unfocused. */
335
+ --floe-surface-decoration: var(
336
+ --floe-surface-shadow-inset,
337
+ inset 0 1px 1px var(--floe-surface-shade),
338
+ inset 0 -1px 0 var(--floe-surface-highlight)
339
+ );
340
+ }
341
+ & [data-floe-surface-part='switch-track'] {
342
+ transition-duration: 140ms;
343
+ transition-timing-function: ease-out;
344
+ }
345
+ & [data-floe-surface-part='indicator']:has([data-floe-surface-part='radio-dot']) {
346
+ /* A hollow hairline keeps an unchecked radio quieter than its selected dot.
347
+ * Border-box dimensions and hit targets stay fixed; there is no blurred well. */
348
+ border-width: 1px;
349
+ border-color: color-mix(in srgb, var(--muted-foreground) 88%, var(--card));
350
+ background-color: transparent;
351
+ --floe-surface-decoration: var(--floe-surface-shadow-inset, none);
352
+ }
353
+ &
354
+ [data-floe-surface-part='indicator'][data-floe-selected='true']:has(
355
+ [data-floe-surface-part='radio-dot']
356
+ ) {
357
+ background-color: color-mix(in srgb, var(--primary) 6%, var(--card));
358
+ }
359
+ & [data-floe-surface-part='radio-dot'] {
360
+ background-color: var(--foreground);
361
+ transition: scale 90ms ease-out;
362
+ }
363
+ & [data-floe-surface-part='progress-fill'] {
364
+ transition-property: width, background-color;
365
+ }
366
+ & [data-floe-surface-part='action']:active:not(:disabled) {
367
+ --floe-surface-decoration: var(
368
+ --floe-surface-shadow-inset,
369
+ var(--floe-surface-compact-inset)
370
+ );
371
+ }
372
+ /* Hidden native inputs delegate their indicator to the visible existing
373
+ * face. A pointer selection does not receive this keyboard-only outline. */
374
+ & [data-floe-surface-part='choice']:has([data-floe-choice-input]:focus-visible),
375
+ &
376
+ [data-floe-choice-input]:focus-visible
377
+ + :is([data-floe-surface-part='indicator'], [data-floe-surface-part='switch-track']) {
378
+ outline: 2px solid var(--ring);
379
+ outline-offset: 3px;
380
+ box-shadow: var(--floe-surface-decoration);
381
+ }
382
+ }
383
+ :where(
384
+ :root.dark[data-floe-surface-style='soft-neumorphic']:not([data-floe-shell-theme='hc-light'])
385
+ ) {
386
+ /* A solid carrying plane avoids repainting a large gradient behind editors. */
387
+ & [data-floe-card-variant='default'][data-floe-surface='raised'] {
388
+ background-color: color-mix(in srgb, var(--card) 98%, white);
389
+ }
390
+ & [data-floe-surface='floating']:not(.workbench-widget) {
391
+ background-color: color-mix(in srgb, var(--card) 94%, white);
392
+ }
393
+ & [data-floe-input-surface],
394
+ & :is(input, textarea)[data-floe-surface='inset'] {
395
+ background-color: color-mix(in srgb, var(--card) 94%, black);
396
+ }
397
+ }
398
+ }
399
+
400
+ /* Explicit host opt-outs and compound input boundaries outrank compact states. */
401
+ :root[data-floe-surface-style='soft-neumorphic'] [data-floe-surface='flat'],
402
+ :root[data-floe-surface-style='soft-neumorphic'] [data-floe-input-surface] [data-floe-surface] {
403
+ --floe-surface-decoration: 0 0 transparent;
404
+ }
405
+
406
+ /* High contrast stays flat, with real boundaries and keyboard indicators. */
407
+ :root[data-floe-surface-style='soft-neumorphic'][data-floe-shell-theme='hc-light']
408
+ [data-floe-surface] {
409
+ --floe-surface-decoration: 0 0 transparent;
410
+ }
411
+ :root[data-floe-surface-style='soft-neumorphic'][data-floe-shell-theme='hc-light']
412
+ .workbench-widget.is-selected {
413
+ border-color: var(--wb-accent);
414
+ }
415
+ @media (forced-colors: active) {
416
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface] {
417
+ --floe-surface-decoration: 0 0 transparent;
418
+ }
419
+ [data-floe-surface-style='soft-neumorphic']
420
+ [data-floe-surface]:focus-visible:not(
421
+ :where(input, textarea, select, [data-floe-input-surface], [data-floe-input-surface] *)
422
+ ) {
423
+ outline: 2px solid Highlight;
424
+ outline-offset: 2px;
425
+ }
426
+ [data-floe-surface-style='soft-neumorphic'] .workbench-widget.is-selected {
427
+ border-color: Highlight;
428
+ }
429
+ }
430
+ @media (prefers-reduced-motion: reduce) {
431
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface] {
432
+ transition: none;
433
+ }
434
+ [data-floe-surface-style='soft-neumorphic'] [data-floe-surface-part='indicator'] > svg {
435
+ animation: none;
436
+ }
437
+ }
438
+
439
+ @keyframes floe-choice-mark-in {
440
+ from {
441
+ opacity: 0.65;
442
+ transform: scale(0.9);
443
+ }
444
+ to {
445
+ opacity: 1;
446
+ transform: scale(1);
447
+ }
448
+ }
449
+
450
+ :root[data-floe-surface-style='soft-neumorphic'][data-floe-shell-theme='hc-light']
451
+ [data-floe-surface-part='choice']:has([data-floe-choice-input]:focus-visible),
452
+ :root[data-floe-surface-style='soft-neumorphic'][data-floe-shell-theme='hc-light']
453
+ [data-floe-choice-input]:focus-visible
454
+ + :is([data-floe-surface-part='indicator'], [data-floe-surface-part='switch-track']) {
455
+ outline: 2px solid var(--ring);
456
+ outline-offset: 3px;
457
+ }
458
+ @media (forced-colors: active) {
459
+ [data-floe-surface-style='soft-neumorphic']
460
+ [data-floe-surface-part='choice']:has([data-floe-choice-input]:focus-visible),
461
+ [data-floe-surface-style='soft-neumorphic']
462
+ [data-floe-choice-input]:focus-visible
463
+ + :is([data-floe-surface-part='indicator'], [data-floe-surface-part='switch-track']) {
464
+ outline: 2px solid Highlight;
465
+ outline-offset: 3px;
466
+ }
467
+ }
package/dist/themes.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { BUILT_IN_SHELL_THEME_DEFAULTS, REQUIRED_SHELL_THEME_TOKENS, assertUniqueThemePresetNames, builtInShellThemePresets, getShellThemePresetsForMode, normalizeShellThemeSelection, presetSupportsMode, resolveShellThemePresetName, type FloeShellThemeDefaults, type FloeShellThemeMode, type FloeShellThemeSelection, } from './styles/themes/presets';
2
- export type { FloeMonacoThemeDefinition, FloeMonacoTokenRule, FloeThemePreset, FloeThemePresetMode, FloeThemeTokenMap, FloeThemeTokenOverrides, } from './styles/themes';
2
+ export type { FloeSurfaceStyle, FloeMonacoThemeDefinition, FloeMonacoTokenRule, FloeThemePreset, FloeThemePresetMode, FloeThemeTokenMap, FloeThemeTokenOverrides, } from './styles/themes';
package/dist/ui.css CHANGED
@@ -1816,3 +1816,12 @@
1816
1816
  transform-origin: 0 0;
1817
1817
  }
1818
1818
  }
1819
+
1820
+ /* Field triggers use input-focus.css. Action triggers retain an independent
1821
+ * keyboard indicator without applying ring utilities to a conditional field. */
1822
+ @layer components {
1823
+ [data-floe-dropdown-trigger]:focus-visible:not([data-floe-input-surface]) {
1824
+ outline: 1px solid var(--ring);
1825
+ outline-offset: 0;
1826
+ }
1827
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floegence/floe-webapp-core",
3
- "version": "0.50.6",
3
+ "version": "0.52.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",