@floegence/floe-webapp-core 0.86.60 → 0.86.63
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/dist/app/FloeRegistryContributions.js +0 -3
- package/dist/components/chat/blocks/BlockRenderer.js +1 -1
- package/dist/components/chat/blocks/CodeDiffBlock.js +56 -56
- package/dist/components/chat/blocks/ImageBlock.js +1 -1
- package/dist/components/chat/blocks/ToolCallBlock.js +57 -57
- package/dist/components/chat/status/ConnectionStatus.js +10 -10
- package/dist/components/deck/DeckCell.js +25 -25
- package/dist/components/deck/DeckContextMenu.js +20 -20
- package/dist/components/deck/DeckGrid.js +1 -1
- package/dist/components/deck/DeckTopBar.js +42 -42
- package/dist/components/deck/DropZonePreview.js +6 -6
- package/dist/components/deck/LayoutSelector.js +53 -53
- package/dist/components/deck/WidgetFrame.js +28 -28
- package/dist/components/deck/WidgetPalette.js +31 -31
- package/dist/components/deck/WidgetTypeSwitcher.js +31 -31
- package/dist/components/file-browser/Breadcrumb.js +27 -27
- package/dist/components/file-browser/DirectoryTree.js +15 -15
- package/dist/components/file-browser/DragPreview.js +58 -58
- package/dist/components/file-browser/FileBrowser.js +17 -17
- package/dist/components/file-browser/FileBrowserStatusBar.js +18 -18
- package/dist/components/file-browser/FileBrowserToolbar.js +27 -27
- package/dist/components/file-browser/FileContextMenu.js +47 -47
- package/dist/components/file-browser/FileGridView.js +53 -51
- package/dist/components/file-browser/FileItemDecorations.js +30 -27
- package/dist/components/file-browser/FileListView.js +114 -114
- package/dist/components/icons/ActivityRailIcons.d.ts +30 -0
- package/dist/components/icons/ActivityRailIcons.js +68 -0
- package/dist/components/icons/index.d.ts +1 -0
- package/dist/components/icons/index.js +1357 -1357
- package/dist/components/launchpad/Launchpad.js +14 -14
- package/dist/components/launchpad/LaunchpadGrid.js +7 -7
- package/dist/components/launchpad/LaunchpadItem.js +11 -11
- package/dist/components/launchpad/LaunchpadPagination.js +16 -16
- package/dist/components/launchpad/LaunchpadSearch.js +9 -9
- package/dist/components/layout/ActivityBar.js +61 -63
- package/dist/components/layout/DisplayModeSwitcher.d.ts +10 -0
- package/dist/components/layout/DisplayModeSwitcher.js +102 -33
- package/dist/components/layout/MobileNavigationPanel.js +36 -36
- package/dist/components/layout/MobileTabBar.js +39 -39
- package/dist/components/layout/Panel.js +16 -16
- package/dist/components/layout/ResizeHandle.js +19 -19
- package/dist/components/layout/Shell.d.ts +0 -6
- package/dist/components/layout/Shell.js +208 -254
- package/dist/components/layout/Sidebar.js +37 -36
- package/dist/components/layout/SidebarPane.js +19 -19
- package/dist/components/layout/ThemeToggle.d.ts +19 -0
- package/dist/components/layout/ThemeToggle.js +39 -0
- package/dist/components/layout/TopBar.js +16 -16
- package/dist/components/layout/TopBarIconButton.js +15 -15
- package/dist/components/layout/index.d.ts +1 -1
- package/dist/components/loading/LoadingOverlay.js +9 -9
- package/dist/components/loading/Skeleton.js +11 -11
- package/dist/components/ui/Button.js +44 -36
- package/dist/components/ui/Card.d.ts +11 -62
- package/dist/components/ui/Card.js +64 -237
- package/dist/components/ui/Checkbox.js +79 -79
- package/dist/components/ui/CommandPalette.js +56 -55
- package/dist/components/ui/Dialog.js +91 -93
- package/dist/components/ui/DirectoryInput.js +22 -22
- package/dist/components/ui/DirectoryPicker.js +8 -8
- package/dist/components/ui/Dropdown.js +89 -87
- package/dist/components/ui/FeedbackIndicator.js +57 -57
- package/dist/components/ui/FileOpenPicker.js +37 -37
- package/dist/components/ui/FileSavePicker.js +41 -41
- package/dist/components/ui/FloatingWindow.js +213 -205
- package/dist/components/ui/Form.js +76 -76
- package/dist/components/ui/HighlightBlock.js +14 -14
- package/dist/components/ui/Input.js +106 -109
- package/dist/components/ui/Pagination.js +86 -86
- package/dist/components/ui/Popover.d.ts +33 -0
- package/dist/components/ui/Popover.js +133 -0
- package/dist/components/ui/ProcessingIndicator.js +344 -345
- package/dist/components/ui/Progress.js +94 -97
- package/dist/components/ui/QuoteBlock.js +25 -25
- package/dist/components/ui/Radio.js +91 -91
- package/dist/components/ui/SegmentedControl.js +24 -24
- package/dist/components/ui/Stepper.js +68 -60
- package/dist/components/ui/Switch.js +25 -25
- package/dist/components/ui/Tabs.js +188 -163
- package/dist/components/ui/Tag.js +12 -9
- package/dist/components/ui/Tooltip.js +30 -30
- package/dist/components/ui/floatingPresence.d.ts +7 -0
- package/dist/components/ui/floatingPresence.js +80 -39
- package/dist/components/ui/index.d.ts +2 -1
- package/dist/components/ui/picker/DirectoryFavorites.js +27 -27
- package/dist/components/ui/picker/PickerBase.js +107 -107
- package/dist/context/ComponentRegistry.d.ts +0 -8
- package/dist/context/ComponentRegistry.js +61 -67
- package/dist/context/FloeConfigContext.d.ts +0 -6
- package/dist/context/FloeConfigContext.js +9 -15
- package/dist/context/NotificationContext.js +4 -4
- package/dist/context/ThemeContext.js +71 -70
- package/dist/context/index.d.ts +1 -1
- package/dist/display-mode.css +2 -45
- package/dist/floe.css +205 -43
- package/dist/full.js +701 -695
- package/dist/icons.js +112 -102
- package/dist/language.css +320 -0
- package/dist/layout.js +33 -35
- package/dist/primitives.css +23 -17
- package/dist/standalone.css +1 -0
- package/dist/styles/themes/classicSemanticTokens.d.ts +122 -122
- package/dist/styles/themes/classicSemanticTokens.js +140 -138
- package/dist/styles/themes/index.d.ts +3 -1
- package/dist/styles/themes/index.js +10 -10
- package/dist/styles/themes/presets.d.ts +2 -2
- package/dist/styles/themes/presets.js +34 -34
- package/dist/styles/tokens.d.ts +152 -68
- package/dist/styles/tokens.js +87 -49
- package/dist/styles.css +1 -1
- package/dist/surface.css +214 -117
- package/dist/themes/dark.css +64 -64
- package/dist/themes/light.css +64 -64
- package/dist/ui.css +171 -255
- package/dist/ui.js +176 -178
- package/dist/utils/hotInteraction.d.ts +7 -0
- package/dist/utils/hotInteraction.js +12 -12
- package/dist/widgets/TerminalWidget.js +28 -28
- package/dist/workbench-composition.css +15 -15
- package/package.json +1 -1
- package/dist/components/layout/BottomBar.d.ts +0 -29
- package/dist/components/layout/BottomBar.js +0 -70
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)
|
|
17
|
-
--floe-surface-divider: color-mix(in srgb, var(--foreground)
|
|
18
|
-
--floe-surface-highlight:
|
|
19
|
-
--floe-surface-shade:
|
|
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
|
|
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
|
|
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
|
|
76
|
+
:where(:root:not([data-floe-shell-theme='hc-light']))
|
|
62
77
|
:is(
|
|
63
78
|
.bg-border,
|
|
64
79
|
.workbench-dock__divider,
|
|
@@ -69,69 +84,64 @@
|
|
|
69
84
|
) {
|
|
70
85
|
background-color: var(--floe-surface-divider);
|
|
71
86
|
}
|
|
72
|
-
:where(:root
|
|
87
|
+
:where(:root:not([data-floe-shell-theme='hc-light']))
|
|
73
88
|
.workbench-canvas__grid {
|
|
74
89
|
opacity: 0.35;
|
|
75
90
|
}
|
|
76
|
-
:root
|
|
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
|
|
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:
|
|
107
|
+
--floe-surface-shadow-raised: var(--floe-elevation-raised);
|
|
94
108
|
--floe-surface-shadow-inset: 0 0 transparent;
|
|
95
|
-
--floe-surface-shade:
|
|
109
|
+
--floe-surface-shade: color-mix(in srgb, #000 34%, transparent);
|
|
96
110
|
}
|
|
97
111
|
|
|
98
|
-
[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
|
|
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'],
|
|
110
123
|
[data-floe-surface-part='radio-dot'],
|
|
111
|
-
[data-floe-surface-part='progress-fill']
|
|
124
|
+
[data-floe-surface-part='progress-fill'],
|
|
125
|
+
/* The travelling pill's transition is a motion timeline, not a palette
|
|
126
|
+
* fade. This rule sits after the segment-pill rule and shares its
|
|
127
|
+
* specificity, so without this exemption it would win and freeze the
|
|
128
|
+
* pill's transform. */
|
|
129
|
+
[data-floe-surface-part='segment-pill']
|
|
112
130
|
)
|
|
113
131
|
) {
|
|
114
132
|
transition-property: color, background-color, border-color, opacity;
|
|
115
133
|
}
|
|
116
134
|
|
|
117
|
-
[data-floe-surface
|
|
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
|
-
);
|
|
135
|
+
[data-floe-surface='raised'] {
|
|
136
|
+
--floe-surface-decoration: var(--floe-surface-shadow-raised, var(--floe-elevation-raised));
|
|
123
137
|
}
|
|
124
|
-
[data-floe-surface
|
|
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
|
-
);
|
|
138
|
+
[data-floe-surface='inset'] {
|
|
139
|
+
--floe-surface-decoration: var(--floe-surface-shadow-inset, 0 0 transparent);
|
|
130
140
|
}
|
|
131
|
-
[data-floe-surface
|
|
141
|
+
[data-floe-surface='floating'] {
|
|
132
142
|
--floe-surface-decoration: var(
|
|
133
143
|
--floe-surface-shadow-floating,
|
|
134
|
-
var(--floe-
|
|
144
|
+
var(--floe-elevation-float)
|
|
135
145
|
);
|
|
136
146
|
background-color: var(--popover);
|
|
137
147
|
-webkit-backdrop-filter: none;
|
|
@@ -140,25 +150,24 @@
|
|
|
140
150
|
|
|
141
151
|
/* The geometry owner must not clip the moving panel's external shadow. Paint
|
|
142
152
|
* containment belongs on the existing panel that also owns content clipping. */
|
|
143
|
-
[data-floe-
|
|
153
|
+
[data-floe-geometry-surface='floating-window'] {
|
|
144
154
|
contain: layout style;
|
|
145
155
|
}
|
|
146
|
-
[data-floe-
|
|
156
|
+
[data-floe-floating-window-surface='true'] {
|
|
147
157
|
contain: paint;
|
|
148
158
|
}
|
|
149
|
-
[data-floe-
|
|
159
|
+
[data-floe-dialog-backdrop]:not(.floe-bottom-drawer-backdrop) {
|
|
150
160
|
background-color: rgb(0 0 0 / 16%);
|
|
151
161
|
-webkit-backdrop-filter: none;
|
|
152
162
|
backdrop-filter: none;
|
|
153
163
|
}
|
|
154
|
-
:root.dark[data-floe-
|
|
164
|
+
:root.dark[data-floe-dialog-backdrop]:not(.floe-bottom-drawer-backdrop) {
|
|
155
165
|
background-color: rgb(0 0 0 / 36%);
|
|
156
166
|
}
|
|
157
167
|
|
|
158
168
|
/* A new selection is visible in its first painted frame. Fading the two faces
|
|
159
169
|
* leaves the old choice visually active after the native state has changed. */
|
|
160
|
-
|
|
161
|
-
:is(
|
|
170
|
+
:is(
|
|
162
171
|
[data-floe-surface-part='choice'],
|
|
163
172
|
[data-floe-surface-part='indicator'],
|
|
164
173
|
[data-floe-surface-part='tab'],
|
|
@@ -167,20 +176,57 @@
|
|
|
167
176
|
transition: none;
|
|
168
177
|
}
|
|
169
178
|
|
|
179
|
+
/* A tab strip that continues past its box fades toward that edge. The fade is
|
|
180
|
+
* part of the rail, not a decoration on the tabs, so it follows the scroll
|
|
181
|
+
* state instead of any one item's position. */
|
|
182
|
+
[data-floe-surface-part='tab-rail'] {
|
|
183
|
+
--floe-rail-fade: 20px;
|
|
184
|
+
mask-image: none;
|
|
185
|
+
}
|
|
186
|
+
[data-floe-surface-part='tab-rail'][data-floe-overflow-start][data-floe-overflow-end] {
|
|
187
|
+
mask-image: linear-gradient(
|
|
188
|
+
to right,
|
|
189
|
+
transparent 0,
|
|
190
|
+
#000 var(--floe-rail-fade),
|
|
191
|
+
#000 calc(100% - var(--floe-rail-fade)),
|
|
192
|
+
transparent 100%
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
[data-floe-surface-part='tab-rail'][data-floe-overflow-start]:not([data-floe-overflow-end]) {
|
|
196
|
+
mask-image: linear-gradient(to right, transparent 0, #000 var(--floe-rail-fade));
|
|
197
|
+
}
|
|
198
|
+
[data-floe-surface-part='tab-rail'][data-floe-overflow-end]:not([data-floe-overflow-start]) {
|
|
199
|
+
mask-image: linear-gradient(to right, #000 calc(100% - var(--floe-rail-fade)), transparent 100%);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* A travelling pill is the selection, not a second copy of it: the faces under
|
|
203
|
+
* it stay flat so the only thing that moves is the material. It keeps the
|
|
204
|
+
* language's one motion family, and a reduced-motion request lands it in place
|
|
205
|
+
* without the sweep. */
|
|
206
|
+
[data-floe-surface-part='segment-pill'] {
|
|
207
|
+
transition:
|
|
208
|
+
transform var(--floe-duration-base) var(--floe-ease-out),
|
|
209
|
+
width var(--floe-duration-base) var(--floe-ease-out);
|
|
210
|
+
will-change: transform, width;
|
|
211
|
+
}
|
|
212
|
+
@media (prefers-reduced-motion: reduce) {
|
|
213
|
+
[data-floe-surface-part='segment-pill'] {
|
|
214
|
+
transition: none;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
170
218
|
/* A compound input has one decorative boundary, including host compositions
|
|
171
219
|
* that contain an Input component. Dense nested actions stay flat. */
|
|
172
|
-
[data-floe-
|
|
173
|
-
[data-floe-surface
|
|
174
|
-
[data-floe-surface='raised']
|
|
220
|
+
[data-floe-input-surface] [data-floe-surface],
|
|
221
|
+
[data-floe-surface='raised']
|
|
175
222
|
[data-floe-surface='raised']:not([data-floe-surface-part]),
|
|
176
|
-
[data-floe-surface
|
|
223
|
+
[data-floe-surface='flat'] {
|
|
177
224
|
--floe-surface-decoration: 0 0 transparent;
|
|
178
225
|
}
|
|
179
226
|
|
|
180
227
|
/* Decoration cannot replace independent keyboard indicators. Inputs, including
|
|
181
228
|
* button-based compound fields, continue to use input-focus.css exclusively. */
|
|
182
|
-
[data-floe-surface-
|
|
183
|
-
[data-floe-surface]:focus-visible:not(
|
|
229
|
+
[data-floe-surface]:focus-visible:not(
|
|
184
230
|
:where(input, textarea, select, [data-floe-input-surface], [data-floe-input-surface] *)
|
|
185
231
|
) {
|
|
186
232
|
box-shadow:
|
|
@@ -189,24 +235,23 @@
|
|
|
189
235
|
var(--floe-surface-decoration);
|
|
190
236
|
}
|
|
191
237
|
|
|
192
|
-
|
|
238
|
+
.workbench-widget[data-floe-surface] {
|
|
193
239
|
/* Some local palettes use translucent body tints. Composite onto an opaque
|
|
194
240
|
* card base instead of reintroducing glass or letting content bleed through. */
|
|
195
241
|
background: linear-gradient(var(--wb-widget-body), var(--wb-widget-body)), var(--card);
|
|
196
242
|
}
|
|
197
|
-
|
|
243
|
+
.workbench-widget.is-selected {
|
|
198
244
|
border-color: transparent;
|
|
199
245
|
}
|
|
200
|
-
|
|
201
|
-
.workbench-widget.is-selected
|
|
246
|
+
.workbench-widget.is-selected
|
|
202
247
|
.workbench-widget__header {
|
|
203
248
|
background: color-mix(in srgb, var(--wb-accent) 8%, var(--card));
|
|
204
249
|
color: var(--wb-accent);
|
|
205
250
|
}
|
|
206
|
-
[data-floe-surface-
|
|
251
|
+
[data-floe-surface-interacting='true'] {
|
|
207
252
|
--floe-surface-decoration: var(
|
|
208
253
|
--floe-surface-shadow-interacting,
|
|
209
|
-
|
|
254
|
+
var(--floe-elevation-raised)
|
|
210
255
|
);
|
|
211
256
|
transition: none;
|
|
212
257
|
}
|
|
@@ -215,13 +260,11 @@
|
|
|
215
260
|
* Tight static highlights carry material without large soft shadows per item.
|
|
216
261
|
* Functional transforms, progress widths, and SVG strokes retain their motion. */
|
|
217
262
|
@media (forced-colors: none) {
|
|
218
|
-
:root
|
|
219
|
-
--floe-surface-compact-raised:
|
|
220
|
-
|
|
221
|
-
--floe-surface-compact-inset:
|
|
222
|
-
inset 0 1px 1px var(--floe-surface-shade), inset 0 -1px 1px var(--floe-surface-highlight);
|
|
263
|
+
:root:not([data-floe-shell-theme='hc-light']) {
|
|
264
|
+
--floe-surface-compact-raised: var(--floe-elevation-raised);
|
|
265
|
+
--floe-surface-compact-inset: 0 0 transparent;
|
|
223
266
|
}
|
|
224
|
-
:where(:root
|
|
267
|
+
:where(:root:not([data-floe-shell-theme='hc-light'])) {
|
|
225
268
|
& [data-floe-surface-part][data-floe-surface='raised'] {
|
|
226
269
|
--floe-surface-decoration: var(
|
|
227
270
|
--floe-surface-shadow-raised,
|
|
@@ -234,12 +277,28 @@
|
|
|
234
277
|
var(--floe-surface-compact-inset)
|
|
235
278
|
);
|
|
236
279
|
}
|
|
280
|
+
/* Two tags, two gestures. An outline rides along with content; a wash
|
|
281
|
+
* wins the eye inside a list. The accent is the tone's own color, so one
|
|
282
|
+
* recipe covers every semantic variant in every preset. */
|
|
283
|
+
&
|
|
284
|
+
[data-floe-surface-part='badge'][data-floe-tone='soft']:not(
|
|
285
|
+
[data-floe-surface='flat']
|
|
286
|
+
) {
|
|
287
|
+
background-color: transparent;
|
|
288
|
+
border-color: var(--floe-line-strong);
|
|
289
|
+
}
|
|
290
|
+
&
|
|
291
|
+
[data-floe-surface-part='badge'][data-floe-tone='solid']:not(
|
|
292
|
+
[data-floe-surface='flat']
|
|
293
|
+
) {
|
|
294
|
+
background-color: color-mix(in srgb, var(--tag-accent) 15%, transparent);
|
|
295
|
+
border-color: color-mix(in srgb, var(--tag-accent) 26%, transparent);
|
|
296
|
+
}
|
|
237
297
|
& [data-floe-surface-part='badge'] {
|
|
238
298
|
/* These can repeat in dense lists. Keep lighting inside the painted box. */
|
|
239
299
|
--floe-surface-decoration: var(
|
|
240
300
|
--floe-surface-shadow-raised,
|
|
241
|
-
|
|
242
|
-
inset 0 -1px 1px var(--floe-surface-shade)
|
|
301
|
+
0 0 0 1px color-mix(in srgb, var(--foreground) 6%, transparent)
|
|
243
302
|
);
|
|
244
303
|
}
|
|
245
304
|
& [data-floe-surface-part='badge'][data-floe-surface='inset'],
|
|
@@ -259,10 +318,16 @@
|
|
|
259
318
|
--floe-surface-decoration: var(--floe-surface-shadow-raised, none);
|
|
260
319
|
}
|
|
261
320
|
& :is([data-floe-surface-part='rail'], [data-floe-surface-part='tab-rail']) {
|
|
262
|
-
background-color:
|
|
321
|
+
background-color: var(--floe-well);
|
|
263
322
|
}
|
|
264
323
|
/* Keep the existing box metrics, but let the carrying fill reach the edge.
|
|
265
324
|
* An opaque seam based on the page background becomes a dark frame here. */
|
|
325
|
+
/* A card is a filled plane on the canvas. Its separation is the fill step
|
|
326
|
+
* plus the canvas gutter, so it carries no contour and no resting shadow. */
|
|
327
|
+
& [data-floe-card-variant='default'][data-floe-surface] {
|
|
328
|
+
border-color: transparent;
|
|
329
|
+
--floe-surface-decoration: 0 0 transparent;
|
|
330
|
+
}
|
|
266
331
|
&
|
|
267
332
|
:is(
|
|
268
333
|
[data-floe-card-variant='default'],
|
|
@@ -282,26 +347,55 @@
|
|
|
282
347
|
[data-floe-surface-part='choice']:not([data-floe-selected='true']):hover:not(
|
|
283
348
|
:has(:disabled)
|
|
284
349
|
) {
|
|
285
|
-
background-color:
|
|
350
|
+
background-color: var(--floe-state-hover);
|
|
286
351
|
}
|
|
287
352
|
& [data-floe-surface-part='choice'][data-floe-selected='true'] {
|
|
288
|
-
background-color: var(--
|
|
289
|
-
}
|
|
290
|
-
/*
|
|
291
|
-
*
|
|
353
|
+
background-color: var(--floe-state-selected);
|
|
354
|
+
}
|
|
355
|
+
/* A committed compact choice is a filled control, because the fill is the
|
|
356
|
+
* state indicator and has to carry 3:1 against its rail by itself. A
|
|
357
|
+
* translucent wash with a 34% accent hairline measures about 1.1:1, which
|
|
358
|
+
* reads as the same surface turned slightly warmer, not as a selection.
|
|
359
|
+
* The semantic pair is the one opaque thing on the rail; relief would only
|
|
360
|
+
* restate the fill, so it stays off. */
|
|
292
361
|
& [data-floe-choice-variant='button'][data-floe-selected='true'],
|
|
293
362
|
& [data-floe-surface-part='segment'][aria-checked='true'] {
|
|
294
363
|
background-color: var(--primary);
|
|
295
364
|
color: var(--primary-foreground);
|
|
365
|
+
border-color: var(--primary);
|
|
296
366
|
--floe-surface-decoration: 0 0 transparent;
|
|
297
367
|
}
|
|
368
|
+
/* A selected tab is a surface sitting proud of its rail: solid fill, the
|
|
369
|
+
* language's hairline and the first elevation step. This is the one place a
|
|
370
|
+
* small control is allowed relief, because the rail sits directly behind it. */
|
|
371
|
+
& [data-floe-surface-part='tab']:not([aria-selected='true']):hover:not(:has(:disabled)) {
|
|
372
|
+
background-color: var(--floe-state-hover);
|
|
373
|
+
color: var(--foreground);
|
|
374
|
+
}
|
|
298
375
|
& [data-floe-surface-part='tab'][aria-selected='true'] {
|
|
299
|
-
background-color: var(--
|
|
376
|
+
background-color: var(--card);
|
|
300
377
|
color: var(--foreground);
|
|
378
|
+
border-color: var(--floe-line);
|
|
379
|
+
--floe-surface-decoration: var(--floe-elevation-raised);
|
|
380
|
+
}
|
|
381
|
+
/* The travelling pill is a selected tab at rest: same fill, same hairline,
|
|
382
|
+
* same first elevation step. It carries them by itself because it has no
|
|
383
|
+
* per-option state to inherit from. */
|
|
384
|
+
& [data-floe-surface-part='segment-pill'] {
|
|
385
|
+
background-color: var(--card);
|
|
386
|
+
border: 1px solid var(--floe-line);
|
|
387
|
+
--floe-surface-decoration: var(--floe-elevation-raised);
|
|
301
388
|
}
|
|
302
389
|
& [data-floe-surface-part='segment'][aria-checked='false'] {
|
|
303
390
|
background-color: transparent;
|
|
304
391
|
}
|
|
392
|
+
&
|
|
393
|
+
[data-floe-surface-part='segment'][aria-checked='false']:hover:not(
|
|
394
|
+
:disabled
|
|
395
|
+
):not(:has(:disabled)) {
|
|
396
|
+
background-color: var(--floe-state-hover);
|
|
397
|
+
color: var(--foreground);
|
|
398
|
+
}
|
|
305
399
|
& [data-floe-surface-part='tab'].border-primary {
|
|
306
400
|
border-color: color-mix(in srgb, var(--primary) 24%, var(--card));
|
|
307
401
|
}
|
|
@@ -314,8 +408,8 @@
|
|
|
314
408
|
background-color: var(--card);
|
|
315
409
|
}
|
|
316
410
|
& [data-floe-surface-part='step'][data-floe-step-state='current'] {
|
|
317
|
-
background-color:
|
|
318
|
-
color: var(--
|
|
411
|
+
background-color: var(--floe-state-selected-strong);
|
|
412
|
+
color: var(--foreground);
|
|
319
413
|
}
|
|
320
414
|
& [data-floe-surface-part='indicator']:not([data-floe-selected='true']),
|
|
321
415
|
& [data-floe-surface-part='switch-track']:not([data-floe-selected='true']) {
|
|
@@ -332,13 +426,8 @@
|
|
|
332
426
|
[data-floe-surface-part='indicator'],
|
|
333
427
|
[data-floe-surface-part='switch-track']
|
|
334
428
|
)[data-floe-surface='inset'] {
|
|
335
|
-
/*
|
|
336
|
-
|
|
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
|
-
);
|
|
429
|
+
/* Small indicators rely on their own fill contrast, not on relief. */
|
|
430
|
+
--floe-surface-decoration: var(--floe-surface-shadow-inset, 0 0 transparent);
|
|
342
431
|
}
|
|
343
432
|
& [data-floe-surface-part='switch-track'] {
|
|
344
433
|
transition-duration: 140ms;
|
|
@@ -348,7 +437,7 @@
|
|
|
348
437
|
/* A hollow hairline keeps an unchecked radio quieter than its selected dot.
|
|
349
438
|
* Border-box dimensions and hit targets stay fixed; there is no blurred well. */
|
|
350
439
|
border-width: 1px;
|
|
351
|
-
border-color: color-mix(in srgb, var(--muted-foreground)
|
|
440
|
+
border-color: color-mix(in srgb, var(--muted-foreground) 72%, var(--card));
|
|
352
441
|
background-color: transparent;
|
|
353
442
|
--floe-surface-decoration: var(--floe-surface-shadow-inset, none);
|
|
354
443
|
}
|
|
@@ -356,7 +445,7 @@
|
|
|
356
445
|
[data-floe-surface-part='indicator'][data-floe-selected='true']:has(
|
|
357
446
|
[data-floe-surface-part='radio-dot']
|
|
358
447
|
) {
|
|
359
|
-
background-color:
|
|
448
|
+
background-color: var(--floe-state-selected);
|
|
360
449
|
}
|
|
361
450
|
& [data-floe-surface-part='radio-dot'] {
|
|
362
451
|
background-color: var(--foreground);
|
|
@@ -366,10 +455,8 @@
|
|
|
366
455
|
transition-property: width, background-color;
|
|
367
456
|
}
|
|
368
457
|
& [data-floe-surface-part='action']:active:not(:disabled) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
var(--floe-surface-compact-inset)
|
|
372
|
-
);
|
|
458
|
+
/* Press settles the control instead of embossing it. */
|
|
459
|
+
--floe-surface-decoration: 0 0 transparent;
|
|
373
460
|
}
|
|
374
461
|
/* Hidden native inputs delegate their indicator to the visible existing
|
|
375
462
|
* face. A pointer selection does not receive this keyboard-only outline. */
|
|
@@ -382,34 +469,34 @@
|
|
|
382
469
|
box-shadow: var(--floe-surface-decoration);
|
|
383
470
|
}
|
|
384
471
|
}
|
|
385
|
-
:where(:root
|
|
472
|
+
:where(:root:not([data-floe-shell-theme='hc-light'])) {
|
|
386
473
|
& :is([data-floe-input-surface], input, textarea, select)[data-floe-surface='inset'] {
|
|
387
|
-
background-color:
|
|
474
|
+
background-color: var(--floe-well);
|
|
388
475
|
}
|
|
389
476
|
}
|
|
390
477
|
}
|
|
391
478
|
|
|
392
|
-
/* Text-labelled actions
|
|
393
|
-
*
|
|
479
|
+
/* Text-labelled actions and fields use one contour. Stacked inset and contact
|
|
480
|
+
* shadows turn a quiet toolbar into an embossed frame.
|
|
481
|
+
* A rail and a tab rail are wells, not planes: they recede behind their
|
|
482
|
+
* segments, and the relief belongs to the one selected item inside them. Giving
|
|
483
|
+
* the well the raised step made it cast a drop shadow in dark palettes while
|
|
484
|
+
* staying flat in light ones, so the same control had two different shapes.
|
|
394
485
|
* Small check/switch indicators retain their independent relief and contrast. */
|
|
395
|
-
|
|
396
|
-
:is(
|
|
486
|
+
:is(
|
|
397
487
|
[data-floe-surface-part='action'],
|
|
398
|
-
[data-floe-surface-part='rail'],
|
|
399
|
-
[data-floe-surface-part='tab-rail'],
|
|
400
488
|
[data-floe-surface-part='tab'],
|
|
401
489
|
[data-floe-surface-part='segment']
|
|
402
490
|
)[data-floe-surface] {
|
|
403
491
|
--floe-surface-decoration: var(--floe-surface-shadow-raised, 0 0 transparent);
|
|
404
492
|
}
|
|
405
|
-
[data-floe-surface-
|
|
406
|
-
:is([data-floe-input-surface], input, textarea, select)[data-floe-surface='inset'] {
|
|
493
|
+
:is([data-floe-input-surface], input, textarea, select)[data-floe-surface='inset'] {
|
|
407
494
|
--floe-surface-decoration: var(--floe-surface-shadow-inset, 0 0 transparent);
|
|
408
495
|
}
|
|
409
496
|
|
|
410
497
|
/* Explicit host opt-outs and compound input boundaries outrank compact states. */
|
|
411
|
-
:root[data-floe-surface
|
|
412
|
-
:root[data-floe-
|
|
498
|
+
:root[data-floe-surface='flat'],
|
|
499
|
+
:root[data-floe-input-surface] [data-floe-surface] {
|
|
413
500
|
--floe-surface-decoration: 0 0 transparent;
|
|
414
501
|
}
|
|
415
502
|
|
|
@@ -417,7 +504,7 @@
|
|
|
417
504
|
* distinct from ordinary Cards, Dialogs and menus. Only this visible panel owns
|
|
418
505
|
* material; the outer geometry owner and its input lifecycle stay unchanged. */
|
|
419
506
|
@media (forced-colors: none) {
|
|
420
|
-
:root
|
|
507
|
+
:root:not([data-floe-shell-theme='hc-light'])
|
|
421
508
|
[data-floe-floating-window-surface='true'][data-floe-surface='floating'] {
|
|
422
509
|
--floe-window-radius: var(--floe-radius-floating);
|
|
423
510
|
background: var(--floe-window-background);
|
|
@@ -462,36 +549,45 @@
|
|
|
462
549
|
}
|
|
463
550
|
}
|
|
464
551
|
|
|
465
|
-
/* Dark shell palettes use a restrained
|
|
466
|
-
*
|
|
467
|
-
:
|
|
552
|
+
/* Dark shell palettes use a restrained step for active chrome so the window
|
|
553
|
+
* remains discoverable without a bright outline or animated glow. The step is
|
|
554
|
+
* taken toward the foreground, not toward the page ground: an accent wash alone
|
|
555
|
+
* lands within three 8-bit units of a near-black window body (soft-black
|
|
556
|
+
* measured 19,20,21 on 16,17,19), so the titlebar stopped being identifiable in
|
|
557
|
+
* grayscale at all. Foreground is light in every dark preset, so mixing 8% of it
|
|
558
|
+
* guarantees an upward step from any accent while title text keeps 5.3:1 or
|
|
559
|
+
* better. Light presets take no such step: their background is already far from
|
|
560
|
+
* the accent wash, and darkening it there would eat title text contrast. */
|
|
561
|
+
:root.dark:not([data-floe-shell-theme='hc-light'])
|
|
468
562
|
[data-floe-floating-window-surface='true'][data-floe-floating-window-state='active']
|
|
469
563
|
> [data-floe-floating-window-titlebar='true'] {
|
|
470
|
-
background-color: color-mix(
|
|
564
|
+
background-color: color-mix(
|
|
565
|
+
in srgb,
|
|
566
|
+
var(--accent) 24%,
|
|
567
|
+
color-mix(in srgb, var(--foreground) 8%, var(--floe-window-titlebar-background))
|
|
568
|
+
);
|
|
471
569
|
}
|
|
472
570
|
|
|
473
571
|
/* FloatingWindow keeps rich text at scale 1 throughout its short presence
|
|
474
|
-
* motion
|
|
475
|
-
*
|
|
476
|
-
|
|
572
|
+
* motion: a window is a large reading surface, so a scale that suits a menu
|
|
573
|
+
* would blur its text mid-flight. The class lives on the panel, not on <html>,
|
|
574
|
+
* so the selector has to match the panel or the override never applies.
|
|
575
|
+
*
|
|
576
|
+
* Promotion belongs to the two transitioning states only; a settled window must
|
|
577
|
+
* not hold a compositor layer (see .floe-floating-presence in floe.css). */
|
|
578
|
+
.floe-floating-window-motion {
|
|
477
579
|
--floe-floating-enter-scale: 1;
|
|
478
580
|
--floe-floating-exit-scale: 1;
|
|
479
581
|
--floe-floating-enter-y: 3px;
|
|
480
582
|
--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
583
|
}
|
|
488
584
|
|
|
489
585
|
/* High contrast stays flat, with real boundaries and keyboard indicators. */
|
|
490
|
-
:root[data-floe-
|
|
586
|
+
:root[data-floe-shell-theme='hc-light']
|
|
491
587
|
[data-floe-surface] {
|
|
492
588
|
--floe-surface-decoration: 0 0 transparent;
|
|
493
589
|
}
|
|
494
|
-
:root[data-floe-
|
|
590
|
+
:root[data-floe-shell-theme='hc-light']
|
|
495
591
|
.workbench-widget.is-selected {
|
|
496
592
|
border-color: var(--wb-accent);
|
|
497
593
|
}
|
|
@@ -505,25 +601,28 @@
|
|
|
505
601
|
box-shadow: none;
|
|
506
602
|
transition: none;
|
|
507
603
|
}
|
|
508
|
-
[data-floe-surface
|
|
604
|
+
[data-floe-surface] {
|
|
509
605
|
--floe-surface-decoration: 0 0 transparent;
|
|
510
606
|
}
|
|
511
|
-
[data-floe-surface-style='soft-neumorphic']
|
|
512
607
|
[data-floe-surface]:focus-visible:not(
|
|
513
608
|
:where(input, textarea, select, [data-floe-input-surface], [data-floe-input-surface] *)
|
|
514
609
|
) {
|
|
515
610
|
outline: 2px solid Highlight;
|
|
516
611
|
outline-offset: 2px;
|
|
517
612
|
}
|
|
518
|
-
|
|
613
|
+
.workbench-widget.is-selected {
|
|
519
614
|
border-color: Highlight;
|
|
520
615
|
}
|
|
521
616
|
}
|
|
522
617
|
@media (prefers-reduced-motion: reduce) {
|
|
523
|
-
|
|
618
|
+
/* `:root` carries the specificity this reset needs: a surface part may pair
|
|
619
|
+
* two attribute selectors to own a transition of its own, and a bare
|
|
620
|
+
* `[data-floe-surface]` would lose to it. Reduced motion is a user setting,
|
|
621
|
+
* so it must win without `!important`. */
|
|
622
|
+
:root [data-floe-surface] {
|
|
524
623
|
transition: none;
|
|
525
624
|
}
|
|
526
|
-
|
|
625
|
+
:root [data-floe-surface-part='indicator'] > svg {
|
|
527
626
|
animation: none;
|
|
528
627
|
}
|
|
529
628
|
}
|
|
@@ -539,18 +638,16 @@
|
|
|
539
638
|
}
|
|
540
639
|
}
|
|
541
640
|
|
|
542
|
-
:root[data-floe-
|
|
641
|
+
:root[data-floe-shell-theme='hc-light']
|
|
543
642
|
[data-floe-surface-part='choice']:has([data-floe-choice-input]:focus-visible),
|
|
544
|
-
:root[data-floe-
|
|
643
|
+
:root[data-floe-shell-theme='hc-light']
|
|
545
644
|
[data-floe-choice-input]:focus-visible
|
|
546
645
|
+ :is([data-floe-surface-part='indicator'], [data-floe-surface-part='switch-track']) {
|
|
547
646
|
outline: 2px solid var(--ring);
|
|
548
647
|
outline-offset: 3px;
|
|
549
648
|
}
|
|
550
649
|
@media (forced-colors: active) {
|
|
551
|
-
[data-floe-surface-style='soft-neumorphic']
|
|
552
650
|
[data-floe-surface-part='choice']:has([data-floe-choice-input]:focus-visible),
|
|
553
|
-
[data-floe-surface-style='soft-neumorphic']
|
|
554
651
|
[data-floe-choice-input]:focus-visible
|
|
555
652
|
+ :is([data-floe-surface-part='indicator'], [data-floe-surface-part='switch-track']) {
|
|
556
653
|
outline: 2px solid Highlight;
|