@arraypress/extension-kit 0.0.0-stage → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -2
  3. package/bin/extension-kit.js +35 -0
  4. package/package.json +67 -4
  5. package/src/about/links.d.ts +51 -0
  6. package/src/about/links.js +48 -0
  7. package/src/build/duplication.d.ts +116 -0
  8. package/src/build/duplication.js +119 -0
  9. package/src/build/index.d.ts +11 -0
  10. package/src/build/index.js +12 -0
  11. package/src/build/release-checks.d.ts +36 -0
  12. package/src/build/release-checks.js +61 -0
  13. package/src/build/toolbar-icons.d.ts +96 -0
  14. package/src/build/toolbar-icons.js +101 -0
  15. package/src/build/vendor.d.ts +26 -0
  16. package/src/build/vendor.js +43 -0
  17. package/src/build/zip.d.ts +31 -0
  18. package/src/build/zip.js +47 -0
  19. package/src/commands/shortcuts.d.ts +39 -0
  20. package/src/commands/shortcuts.js +31 -0
  21. package/src/dom/i18n.d.ts +39 -0
  22. package/src/dom/i18n.js +49 -0
  23. package/src/dom/page.d.ts +59 -0
  24. package/src/dom/page.js +93 -0
  25. package/src/index.d.ts +21 -0
  26. package/src/index.js +24 -0
  27. package/src/settings/saver.d.ts +76 -0
  28. package/src/settings/saver.js +98 -0
  29. package/src/settings/storage.d.ts +91 -0
  30. package/src/settings/storage.js +76 -0
  31. package/src/settings/store.d.ts +138 -0
  32. package/src/settings/store.js +173 -0
  33. package/src/styles/chrome-ui.css +1094 -0
  34. package/src/testing/e2e.d.ts +222 -0
  35. package/src/testing/e2e.js +327 -0
  36. package/src/testing/fake-storage.d.ts +71 -0
  37. package/src/testing/fake-storage.js +145 -0
  38. package/src/testing/index.d.ts +29 -0
  39. package/src/testing/index.js +24 -0
  40. package/src/testing/package.d.ts +39 -0
  41. package/src/testing/package.js +96 -0
  42. package/src/testing/server.d.ts +47 -0
  43. package/src/testing/server.js +90 -0
  44. package/src/toolbar/offscreen.d.ts +30 -0
  45. package/src/toolbar/offscreen.js +37 -0
  46. package/src/toolbar/scheme.d.ts +81 -0
  47. package/src/toolbar/scheme.js +87 -0
  48. package/src/toolbar/theme.d.ts +187 -0
  49. package/src/toolbar/theme.js +176 -0
@@ -0,0 +1,1094 @@
1
+ /*
2
+ * chrome-ui.css — the look of Chrome's own pages (chrome://settings,
3
+ * chrome://bookmarks) for ArrayPress's extension pages.
4
+ *
5
+ * THE LIBRARY IS THE SOURCE: this file ships in @arraypress/extension-kit
6
+ * (src/styles/chrome-ui.css). An extension never commits a copy: its build,
7
+ * or `npm install` for an extension without one, copies it in from
8
+ * node_modules, so every extension always has the installed version.
9
+ *
10
+ * Values come from Chromium's source (chromium/src, main, Nov 2026) and were
11
+ * checked against computed styles of chrome://settings and chrome://bookmarks
12
+ * in Chromium 156 and Chrome 157 (light and dark):
13
+ *
14
+ * ui/webui/resources/cr_elements/cr_shared_vars.css
15
+ * --cr-section-min-height 48px, --cr-section-two-line-min-height 64px,
16
+ * --cr-section-padding 20px, --cr-section-vertical-padding 12px,
17
+ * --cr-centered-card-max-width 680px (width 96%),
18
+ * --cr-separator-line 1px solid rgba(0,0,0,.06) / dark rgba(255,255,255,.1),
19
+ * --cr-card-border-radius 8px, --cr-card-background-color white /
20
+ * dark #292a2d, --cr-card-shadow = --cr-elevation-2, page background
21
+ * white / dark #202124, --cr-button-height 36px, --cr-icon-size 20px,
22
+ * --cr-toolbar-height 56px, and the GM3 fallback colours
23
+ * (--cr-fallback-color-*) used for every token below.
24
+ * ui/webui/resources/cr_elements/cr_shared_style_lit.css
25
+ * .cr-row (border-top separator, 48px, 0 20px), .cr-secondary-text,
26
+ * .cr-centered-card-container, .cr-form-field-label (11px/16px 500).
27
+ * chrome/browser/resources/settings/settings_shared_lit.css
28
+ * .settings-box, .two-line (64px), .settings-box.first (no separator).
29
+ * chrome/browser/resources/settings/settings_page/settings_section.css
30
+ * section title: 108% (14px) weight 400, letter-spacing .25px,
31
+ * margin 21px 0 12px, padding 8px 0 4px; card radius/shadow/background.
32
+ * ui/webui/resources/cr_elements/cr_toggle/cr_toggle.css
33
+ * bar 26x16 with a 1px border, knob 8px off / 12px on; on = primary
34
+ * track + on-primary knob; off = surface-variant track, outline knob.
35
+ * ui/webui/resources/cr_elements/md_select_lit.css
36
+ * 200x36, radius 8, 1px neutral-outline border, 12px text, 7px arrow
37
+ * 10px from the end.
38
+ * ui/webui/resources/cr_elements/cr_input/cr_input_style_lit.css
39
+ * filled field: surface-variant, radius 8 8 0 0, 1px outline underline
40
+ * (2px primary when focused), 10px padding, 12px/16px text.
41
+ * ui/webui/resources/cr_elements/cr_button/cr_button.css
42
+ * 36px pill, 8px 16px, 13px/500, tonal-outline border; .action-button
43
+ * filled primary.
44
+ * ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.css (32px).
45
+ * ui/webui/resources/cr_elements/cr_link_row/cr_link_row.css
46
+ * 48px, 0 20px, label 12px vertical padding, 20px end icon.
47
+ * ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.css
48
+ * radius 4, elevation-2, items min 32px padding 8px 24px.
49
+ * ui/webui/resources/cr_elements/cr_dialog/cr_dialog.css
50
+ * 512px, radius 8, title 15px (20px 20px 16px), body secondary text,
51
+ * buttons 16px, backdrop rgba(0,0,0,.6).
52
+ * ui/webui/resources/cr_elements/cr_toast/cr_toast.css
53
+ * inverse surface, radius 8, min-height 52px, 288–568px, 24px margin.
54
+ * ui/webui/resources/cr_elements/cr_drawer/cr_drawer.css (256px).
55
+ * ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.css,
56
+ * cr_toolbar_search_field: 56px bar, 22px/500 title, 36px pill search.
57
+ * chrome/browser/resources/settings/settings_menu (cr-nav-menu-item):
58
+ * 40px items, padding 10px 16px 10px 23px, radius 0 100px 100px 0,
59
+ * 14px/500, 20px icon + 20px gap; selected google-blue-50 / blue-700,
60
+ * dark google-blue-300 / grey-900.
61
+ * chrome/browser/resources/bookmarks/{item,list,shared_vars,app}.css
62
+ * 40px rows, list card max 960px, sidebar 256px.
63
+ * Side panels (checked against chrome://read-later.top-chrome, the Reading
64
+ * list panel, in Chromium 156, light and dark):
65
+ * chrome/browser/resources/side_panel/shared/sp_shared_vars.css,
66
+ * sp_shared_style.css, sp_heading.css, sp_empty_state.css, sp_footer.css
67
+ * 8px body padding, card radius 12 / 8px 0 padding / 12px gap, 36px
68
+ * headings 14px/500, empty state 16px/500 heading + 13px body, 56px
69
+ * footer; colours from chrome/browser/ui/color/material_side_panel_color_mixer.cc
70
+ * (panel = sys base-container, card = base-container-elevated).
71
+ * ui/webui/resources/cr_elements/cr_url_list_item/cr_url_list_item.css
72
+ * 48px rows, 4px 16px, 40px icon box radius 8, title 12px/500,
73
+ * description 11px, suffix buttons 24px shown on hover/focus.
74
+ * ui/webui/resources/cr_elements/cr_button/cr_button.css .floating-button
75
+ * tonal, radius 8, 40px, 8px 16px 8px 12px with a 20px icon.
76
+ * ui/webui/resources/cr_elements/cr_tabs/cr_tabs.css
77
+ * 48px, 14px/500, secondary text, selected primary with a 2px bar.
78
+ *
79
+ * Not used: the "settings-refresh-2026" experiment (16px cards without
80
+ * separators); it isn't on in Chrome 157 by default. Re-measure if it ships.
81
+ */
82
+
83
+ :root {
84
+ color-scheme: light dark;
85
+
86
+ /* Colours (Chrome's --cr-fallback-color-* and --cr-* values). */
87
+ --page-bg: #ffffff;
88
+ --surface: #ffffff; /* --cr-card-background-color */
89
+ --card: var(--surface);
90
+ --text: rgb(31 31 31); /* --cr-primary-text-color */
91
+ --text-secondary: rgb(68 71 70); /* --cr-secondary-text-color */
92
+ --text-subtle: rgb(71 71 71); /* on-surface-subtle: field labels */
93
+ --primary: rgb(11 87 208); /* links, focus ring, toggle on */
94
+ --on-primary: #ffffff;
95
+ --separator: rgb(0 0 0 / 0.06); /* --cr-separator-color */
96
+ --hover: rgb(31 31 31 / 0.08); /* --cr-hover-background-color */
97
+ --hover-subtle: rgb(31 31 31 / 0.06);
98
+ --active: rgb(31 31 31 / 0.12);
99
+ --button-border: rgb(168 199 250); /* tonal-outline */
100
+ --field-border: rgb(199 199 199); /* neutral-outline */
101
+ --field-bg: rgb(225 227 225); /* surface-variant (cr-input) */
102
+ --outline: rgb(116 119 117);
103
+ --switch-track-off: rgb(225 227 225);
104
+ --switch-outline-off: rgb(116 119 117);
105
+ --error: rgb(179 38 30);
106
+ --icon: rgb(68 71 70);
107
+ --icon-muted: rgb(95 99 104); /* google-grey-700: menu and link-row icons */
108
+ --selected-bg: rgb(232 240 254); /* google-blue-50 */
109
+ --selected-text: rgb(25 103 210); /* google-blue-700 */
110
+ --folder-text: rgb(90 90 90); /* --cr-title-text-color: bookmarks folders */
111
+ --chip-bg: color-mix(in srgb, var(--primary) 10%, transparent);
112
+ --search-bg: rgb(236 239 247); /* base-container */
113
+ --menu-bg: #ffffff;
114
+ --dialog-bg: #ffffff;
115
+ --toast-bg: rgb(48 48 48); /* inverse-surface */
116
+ --toast-text: rgb(242 242 242);
117
+ --toast-action: rgb(168 199 250); /* inverse-primary */
118
+ --warning-bg: rgb(254 247 224);
119
+ --warning-text: rgb(31 31 31);
120
+ --warning-icon: rgb(176 96 0);
121
+ --card-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 2px 6px 2px rgb(0 0 0 / 0.15);
122
+ --menu-shadow: var(--card-shadow);
123
+ --dialog-shadow: 0 0 16px rgb(0 0 0 / 0.12), 0 16px 16px rgb(0 0 0 / 0.24);
124
+ --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='12' fill='%235F6368'%3E%3Cpath d='M0 0h24L12 12z'/%3E%3C/svg%3E");
125
+
126
+ /* Side panels (the Reading list panel's colours). */
127
+ --sp-bg: rgb(237 242 250); /* sys base-container */
128
+ --sp-card: #ffffff; /* base-container-elevated */
129
+ --sp-text: rgb(31 31 31);
130
+ --sp-text-secondary: rgb(71 71 71); /* on-surface-subtle */
131
+ --sp-icon-bg: rgb(242 242 242); /* list-item-url-favicon-background */
132
+ --sp-search-bg: #ffffff;
133
+ --sp-divider: rgb(211 227 253);
134
+ --sp-scrollbar: rgb(168 199 250); /* tonal-outline */
135
+ --tonal-bg: rgb(211 227 253); /* button-background-tonal */
136
+ --tonal-text: rgb(4 30 73);
137
+ --elevation-3: 0 1px 3px 0 rgb(60 64 67 / 0.3), 0 4px 8px 3px rgb(60 64 67 / 0.15);
138
+
139
+ /* Icons for link rows (cr:open-in-new, cr:arrow-right / subpage arrow). */
140
+ --icon-open-in-new: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 19H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z'/%3E%3C/svg%3E");
141
+ --icon-arrow-right: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z'/%3E%3C/svg%3E");
142
+
143
+ /* Sizes. */
144
+ --radius: 8px; /* cards, fields, dialogs */
145
+ --row-min-height: 48px;
146
+ --row-two-line-min-height: 64px;
147
+ --row-padding: 0 20px;
148
+ --row-vertical-padding: 12px;
149
+ --button-height: 36px;
150
+ --page-width: 680px;
151
+ --toolbar-height: 56px;
152
+ --sidebar-width: 256px;
153
+ --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
154
+
155
+ font: 13px/1.54 system-ui, sans-serif;
156
+ }
157
+
158
+ @media (prefers-color-scheme: dark) {
159
+ :root {
160
+ --page-bg: rgb(32 33 36); /* google-grey-900 */
161
+ --surface: rgb(41 42 45); /* #292a2d */
162
+ --text: rgb(227 227 227);
163
+ --text-secondary: rgb(196 199 197);
164
+ --text-subtle: rgb(199 199 199);
165
+ --primary: rgb(168 199 250);
166
+ --on-primary: rgb(6 46 111);
167
+ --separator: rgb(255 255 255 / 0.1);
168
+ --hover: rgb(227 227 227 / 0.08);
169
+ --hover-subtle: rgb(253 252 251 / 0.1);
170
+ --active: rgb(227 227 227 / 0.12);
171
+ --button-border: rgb(4 125 183);
172
+ --field-border: rgb(117 117 117);
173
+ --field-bg: rgb(68 71 70);
174
+ --outline: rgb(142 145 143);
175
+ --switch-track-off: rgb(68 71 70);
176
+ --switch-outline-off: rgb(142 145 143);
177
+ --error: rgb(242 184 181);
178
+ --icon: rgb(196 199 197);
179
+ --icon-muted: rgb(154 160 166); /* google-grey-500 */
180
+ --selected-bg: rgb(138 180 248); /* google-blue-300 */
181
+ --selected-text: rgb(32 33 36);
182
+ --folder-text: rgb(154 160 166);
183
+ --search-bg: rgb(0 0 0 / 0.22);
184
+ --menu-bg: rgb(41 42 45);
185
+ --dialog-bg: rgb(41 42 45); /* grey-900 + 4% white */
186
+ --toast-bg: rgb(227 227 227);
187
+ --toast-text: rgb(31 31 31);
188
+ --toast-action: rgb(11 87 208);
189
+ --warning-bg: rgb(66 55 28);
190
+ --warning-text: rgb(240 230 205);
191
+ --warning-icon: rgb(253 214 99);
192
+ --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='12' fill='%239aa0a6'%3E%3Cpath d='M0 0h24L12 12z'/%3E%3C/svg%3E");
193
+ --sp-bg: rgb(40 40 40);
194
+ --sp-card: rgb(60 60 60);
195
+ --sp-text: rgb(227 227 227);
196
+ --sp-text-secondary: rgb(199 199 199);
197
+ --sp-icon-bg: rgb(40 40 40);
198
+ --sp-search-bg: rgb(60 60 60);
199
+ --sp-divider: rgb(94 94 94);
200
+ --sp-scrollbar: rgb(4 125 183);
201
+ --tonal-bg: rgb(0 74 119);
202
+ --tonal-text: rgb(194 231 255);
203
+ --elevation-3: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 4px 8px 3px rgb(0 0 0 / 0.15);
204
+ }
205
+ }
206
+
207
+ /* ------------------------------------------------------------------ Base */
208
+
209
+ * { box-sizing: border-box; }
210
+
211
+ body {
212
+ margin: 0;
213
+ /* Chrome's stylesheet for extension pages sets body to 75%; its own pages use 13px. */
214
+ font-size: 13px;
215
+ background: var(--page-bg);
216
+ color: var(--text);
217
+ }
218
+
219
+ h1, h2, h3, p { margin: 0; }
220
+
221
+ [hidden] { display: none !important; }
222
+
223
+ .secondary { color: var(--text-secondary); font-weight: 400; }
224
+ .error { color: var(--error); }
225
+ .mono { font-family: var(--mono); }
226
+
227
+ .visually-hidden {
228
+ position: absolute;
229
+ width: 1px;
230
+ height: 1px;
231
+ overflow: hidden;
232
+ clip-path: inset(50%);
233
+ white-space: nowrap;
234
+ }
235
+
236
+ a { color: var(--primary); text-decoration: none; }
237
+ a:hover { text-decoration: underline; }
238
+
239
+ :focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
240
+
241
+ /* --------------------------------------------------------------- Buttons */
242
+
243
+ /* cr-button: outlined pill; .action is the filled action-button. */
244
+ button {
245
+ font: 500 13px/20px system-ui, sans-serif;
246
+ height: var(--button-height);
247
+ padding: 0 16px;
248
+ border: 1px solid var(--button-border);
249
+ border-radius: 100px;
250
+ background: transparent;
251
+ color: var(--primary);
252
+ cursor: pointer;
253
+ flex: none;
254
+ white-space: nowrap;
255
+ }
256
+
257
+ button:hover { background: var(--hover-subtle); }
258
+ button:active { background: var(--active); }
259
+
260
+ button.action {
261
+ background: var(--primary);
262
+ border-color: transparent;
263
+ color: var(--on-primary);
264
+ }
265
+
266
+ button.action:hover { background: color-mix(in srgb, var(--primary), var(--on-primary) 8%); }
267
+
268
+ button:disabled { opacity: 0.38; cursor: default; pointer-events: none; }
269
+
270
+ /* A button that reads as a link (action-link). */
271
+ button.link {
272
+ height: auto;
273
+ padding: 0;
274
+ border: 0;
275
+ border-radius: 0;
276
+ background: none;
277
+ font-weight: 400;
278
+ color: var(--primary);
279
+ }
280
+
281
+ button.link:hover { background: none; text-decoration: underline; }
282
+
283
+ /* cr-icon-button: a 32px circle holding a 20px icon. */
284
+ .icon-button {
285
+ display: inline-grid;
286
+ place-items: center;
287
+ width: 32px;
288
+ height: 32px;
289
+ padding: 0;
290
+ border: 0;
291
+ border-radius: 50%;
292
+ color: var(--icon);
293
+ }
294
+
295
+ .icon-button:hover { background: var(--hover); }
296
+
297
+ .icon { width: 20px; height: 20px; fill: currentColor; flex: none; }
298
+
299
+ .sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
300
+
301
+ /* --------------------------------------------------------------- Fields */
302
+
303
+ /* md-select. */
304
+ select {
305
+ appearance: none;
306
+ font: 12px system-ui, sans-serif;
307
+ color: var(--text);
308
+ width: 200px;
309
+ max-width: 100%;
310
+ height: 36px;
311
+ padding: 0 20px 0 10px;
312
+ border: 1px solid var(--field-border);
313
+ border-radius: var(--radius);
314
+ background: var(--select-arrow) calc(100% - 10px) center / 7px no-repeat transparent;
315
+ cursor: pointer;
316
+ }
317
+
318
+ select:hover { background-color: var(--hover-subtle); }
319
+ select:focus-visible { outline: 2px solid var(--primary); outline-offset: -1px; }
320
+ select:disabled { opacity: 0.38; cursor: default; }
321
+ select option, select optgroup { background: var(--surface); color: var(--text); }
322
+
323
+ /* cr-input: filled, rounded top, an underline that turns primary on focus. */
324
+ input:is([type="text"], [type="number"], [type="search"], [type="url"], [type="email"], [type="tel"], [type="date"], [type="time"], [type="datetime-local"]),
325
+ textarea {
326
+ font: 12px/16px system-ui, sans-serif;
327
+ color: var(--text);
328
+ min-height: 36px;
329
+ padding: 10px;
330
+ border: 0;
331
+ border-bottom: 1px solid var(--outline);
332
+ border-radius: 8px 8px 0 0;
333
+ background: var(--field-bg);
334
+ outline: none;
335
+ }
336
+
337
+ textarea { font-size: 13px; line-height: 20px; resize: vertical; }
338
+
339
+ input:is([type="text"], [type="number"], [type="search"], [type="url"], [type="email"], [type="tel"], [type="date"], [type="time"], [type="datetime-local"]):hover,
340
+ textarea:hover { background: linear-gradient(var(--hover), var(--hover)) var(--field-bg); }
341
+
342
+ input:is([type="text"], [type="number"], [type="search"], [type="url"], [type="email"], [type="tel"], [type="date"], [type="time"], [type="datetime-local"]):focus,
343
+ textarea:focus {
344
+ border-bottom-color: var(--primary);
345
+ box-shadow: inset 0 -1px 0 var(--primary);
346
+ outline: none;
347
+ }
348
+
349
+ input::placeholder, textarea::placeholder { color: var(--text-subtle); opacity: 1; }
350
+ input[type="search"]::-webkit-search-cancel-button { display: none; }
351
+ input:disabled, textarea:disabled { opacity: 0.38; }
352
+
353
+ [aria-invalid="true"] { border-bottom-color: var(--error) !important; box-shadow: inset 0 -1px 0 var(--error) !important; }
354
+
355
+ /* .cr-form-field-label, above a field. */
356
+ .field-label,
357
+ .field > label {
358
+ display: block;
359
+ margin-bottom: 8px;
360
+ color: var(--text-subtle);
361
+ font-size: 11px;
362
+ font-weight: 500;
363
+ line-height: 16px;
364
+ letter-spacing: 0.4px;
365
+ }
366
+
367
+ .field { display: grid; }
368
+ .field > input, .field > textarea, .field > select { width: 100%; }
369
+ .field > .hint { margin-top: 4px; }
370
+ .hint { color: var(--text-secondary); font-size: 12px; }
371
+
372
+ /* cr-checkbox and cr-radio-button. */
373
+ input[type="checkbox"]:not(.switch),
374
+ input[type="radio"] {
375
+ accent-color: var(--primary);
376
+ width: 16px;
377
+ height: 16px;
378
+ margin: 0;
379
+ flex: none;
380
+ cursor: pointer;
381
+ }
382
+
383
+ /* cr-toggle, on a checkbox: <input type="checkbox" class="switch" role="switch">. */
384
+ input.switch {
385
+ appearance: none;
386
+ flex: none;
387
+ position: relative;
388
+ width: 26px;
389
+ height: 16px;
390
+ margin: 0;
391
+ border: 1px solid var(--switch-outline-off);
392
+ border-radius: 50px;
393
+ background: var(--switch-track-off);
394
+ cursor: pointer;
395
+ transition: background-color 80ms linear, border-color 80ms linear;
396
+ }
397
+
398
+ input.switch::after {
399
+ content: "";
400
+ position: absolute;
401
+ top: 50%;
402
+ left: 7px;
403
+ width: 8px;
404
+ height: 8px;
405
+ border-radius: 50%;
406
+ background: var(--switch-outline-off);
407
+ transform: translate(-50%, -50%);
408
+ transition: left 80ms linear, width 80ms linear, height 80ms linear, background-color 80ms linear;
409
+ }
410
+
411
+ /* The 20px hover halo around the knob. */
412
+ input.switch:hover::before {
413
+ content: "";
414
+ position: absolute;
415
+ top: 50%;
416
+ left: 7px;
417
+ width: 20px;
418
+ height: 20px;
419
+ border-radius: 50%;
420
+ background: var(--hover-subtle);
421
+ transform: translate(-50%, -50%);
422
+ }
423
+
424
+ input.switch:active::after { width: 10px; height: 10px; }
425
+ input.switch:checked { background: var(--primary); border-color: var(--primary); }
426
+ input.switch:checked::after { left: 17px; width: 12px; height: 12px; background: var(--on-primary); }
427
+ input.switch:checked:hover::before { left: 17px; }
428
+ input.switch:checked:active::after { width: 14px; height: 14px; }
429
+ input.switch:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
430
+ input.switch:disabled { opacity: 0.38; cursor: default; }
431
+ input.switch:disabled::before { display: none; }
432
+
433
+ @media (prefers-reduced-motion: reduce) {
434
+ input.switch, input.switch::after { transition: none; }
435
+ }
436
+
437
+ /* ------------------------------------------------------------ Toolbar */
438
+
439
+ /* cr-toolbar: 56px; the title, a centred pill search, actions at the end. */
440
+ .toolbar {
441
+ flex: none;
442
+ display: grid;
443
+ grid-template-columns: var(--sidebar-width) minmax(0, 1fr) var(--sidebar-width);
444
+ align-items: center;
445
+ height: var(--toolbar-height);
446
+ padding-right: 12px;
447
+ }
448
+
449
+ .toolbar-start { display: flex; align-items: center; gap: 12px; padding-left: 20px; min-width: 0; }
450
+ .toolbar-start h1, .toolbar > h1 {
451
+ font-size: 22px;
452
+ font-weight: 500;
453
+ letter-spacing: 0.25px;
454
+ white-space: nowrap;
455
+ overflow: hidden;
456
+ text-overflow: ellipsis;
457
+ }
458
+ .toolbar-center { display: flex; justify-content: center; min-width: 0; }
459
+ .toolbar-end { display: flex; justify-content: flex-end; align-items: center; gap: 4px; }
460
+
461
+ /* cr-toolbar-search-field. */
462
+ .search-field {
463
+ display: flex;
464
+ align-items: center;
465
+ gap: 6px;
466
+ width: 100%;
467
+ max-width: 680px;
468
+ height: 36px;
469
+ padding: 0 6px 0 10px;
470
+ border-radius: 100px;
471
+ background: var(--search-bg);
472
+ color: var(--icon);
473
+ }
474
+
475
+ .search-field:focus-within { outline: 2px solid var(--primary); outline-offset: 0; }
476
+
477
+ .search-field input,
478
+ .search-field input:hover,
479
+ .search-field input:focus {
480
+ flex: 1;
481
+ min-width: 0;
482
+ height: 100%;
483
+ min-height: 0;
484
+ padding: 0 0 0 4px;
485
+ border: 0;
486
+ border-radius: 0;
487
+ background: none;
488
+ box-shadow: none;
489
+ color: var(--text);
490
+ font: 500 12px system-ui, sans-serif;
491
+ }
492
+
493
+ .search-field input::placeholder { color: var(--text-secondary); }
494
+
495
+ /* --------------------------------------------- Settings page (chrome://settings) */
496
+
497
+ /* The centred column: 680px, 96% of narrower windows. */
498
+ .page {
499
+ width: 96%;
500
+ max-width: var(--page-width);
501
+ margin: 0 auto;
502
+ padding-bottom: 48px;
503
+ }
504
+
505
+ /* settings-section's title, above its card. */
506
+ .section-title {
507
+ display: block;
508
+ margin: 21px 0 12px;
509
+ padding: 8px 0 4px;
510
+ color: var(--text);
511
+ font-size: 14px;
512
+ font-weight: 400;
513
+ line-height: 20px;
514
+ letter-spacing: 0.25px;
515
+ }
516
+
517
+ /* A sub-heading inside a card (settings' h2). */
518
+ .card-heading {
519
+ display: flex;
520
+ align-items: center;
521
+ min-height: var(--row-min-height);
522
+ padding: var(--row-padding);
523
+ color: var(--text-secondary);
524
+ font-size: 13px;
525
+ font-weight: 500;
526
+ }
527
+
528
+ .card {
529
+ background: var(--card);
530
+ border-radius: var(--radius);
531
+ box-shadow: var(--card-shadow);
532
+ overflow: hidden;
533
+ }
534
+
535
+ /* .settings-box / .cr-row: one setting per row, separators between rows only. */
536
+ .row {
537
+ display: flex;
538
+ align-items: center;
539
+ justify-content: space-between;
540
+ gap: 16px;
541
+ min-height: var(--row-min-height);
542
+ margin: 0;
543
+ padding: var(--row-padding);
544
+ border-top: 1px solid var(--separator);
545
+ }
546
+
547
+ /* No separator above a card's first row (also when rows sit in a wrapper);
548
+ .first / .continuation mark it by hand, as Chrome does. */
549
+ .card > :is(.row, .link-row):first-child,
550
+ .card > :first-child > :is(.row, .link-row):first-child,
551
+ .card > [hidden]:first-child + :is(.row, .link-row),
552
+ .row.first,
553
+ .row.continuation,
554
+ .link-row.first { border-top: 0; }
555
+
556
+ .row.two-line { min-height: var(--row-two-line-min-height); }
557
+
558
+ /* The text side of a row: a label, and secondary text below it. */
559
+ .row .label {
560
+ flex: 1;
561
+ min-width: 0;
562
+ padding: var(--row-vertical-padding) 0;
563
+ }
564
+
565
+ .row .label > .secondary,
566
+ .row .label > .error { display: block; }
567
+
568
+ /* A row whose label is a whole paragraph of help. */
569
+ .row.text { color: var(--text-secondary); }
570
+
571
+ /* A value shown at the end of a row (a shortcut, a status). */
572
+ .row-value { color: var(--text-secondary); white-space: nowrap; }
573
+
574
+ label.row, .row[actionable] { cursor: pointer; }
575
+
576
+ /* cr-link-row: a whole row that opens something, with an arrow or ↗ at the end. */
577
+ .link-row {
578
+ display: flex;
579
+ align-items: center;
580
+ gap: 16px;
581
+ width: 100%;
582
+ height: auto;
583
+ min-height: var(--row-min-height);
584
+ margin: 0;
585
+ padding: var(--row-padding);
586
+ border: 0;
587
+ border-top: 1px solid var(--separator);
588
+ border-radius: 0;
589
+ background: none;
590
+ color: var(--text);
591
+ font: inherit;
592
+ text-align: start;
593
+ text-decoration: none;
594
+ white-space: normal;
595
+ cursor: pointer;
596
+ }
597
+
598
+ .link-row:hover { background: var(--hover-subtle); text-decoration: none; }
599
+ .link-row:active { background: var(--active); }
600
+ .link-row:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
601
+ .link-row .label { flex: 1; min-width: 0; padding: var(--row-vertical-padding) 0; }
602
+ .link-row .label > .secondary { display: block; }
603
+
604
+ .link-row::after {
605
+ content: "";
606
+ flex: none;
607
+ width: 20px;
608
+ height: 20px;
609
+ margin-right: -2px;
610
+ background: var(--icon-muted);
611
+ mask: var(--icon-arrow-right) center / 20px no-repeat;
612
+ }
613
+
614
+ .link-row.external::after { mask-image: var(--icon-open-in-new); }
615
+
616
+ /* A list inside a card: each item is a row. */
617
+ .list { list-style: none; margin: 0; padding: 0; }
618
+ .list:empty { display: none; }
619
+
620
+ /* About card, as chrome://settings/help (about_page.css): a 32px logo 10px from a 20px title. */
621
+ .about-product { justify-content: flex-start; gap: 10px; }
622
+ .product-title { font-size: 20px; }
623
+
624
+ /* "Saved" beside the toolbar title: shown briefly after a write (dom/page.js statusFlasher). */
625
+ .save-status { opacity: 0; transition: opacity 0.2s; }
626
+ .save-status.show { opacity: 1; }
627
+
628
+ /* -------------------------------- Settings menu (settings-menu, cr-nav-menu-item) */
629
+
630
+ .nav { padding: 8px 0 5px; }
631
+ .nav-list { list-style: none; margin: 0; padding: 0; }
632
+
633
+ .nav-item {
634
+ display: flex;
635
+ align-items: center;
636
+ gap: 20px;
637
+ min-height: 40px;
638
+ margin: 0 2px 0 1px;
639
+ padding: 10px 16px 10px 23px;
640
+ border-radius: 0 100px 100px 0;
641
+ color: var(--text);
642
+ font-size: 14px;
643
+ font-weight: 500;
644
+ line-height: 14px;
645
+ text-decoration: none;
646
+ cursor: pointer;
647
+ user-select: none;
648
+ }
649
+
650
+ .nav-item:hover { background: var(--hover); text-decoration: none; }
651
+ .nav-item:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
652
+ .nav-item .icon { color: var(--icon-muted); }
653
+ .nav-item[aria-current="page"], .nav-item.selected { background: var(--selected-bg); color: var(--selected-text); }
654
+ .nav-item[aria-current="page"] .icon, .nav-item.selected .icon { color: inherit; }
655
+ .nav-separator { margin: 8px 0; border: 0; border-top: 1px solid var(--separator); }
656
+
657
+ /* ------------------------------------- Bookmarks-style manager (chrome://bookmarks) */
658
+
659
+ /* The page behind a manager: toolbar on top, a 256px sidebar, the list. */
660
+ .manager { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
661
+ .layout { flex: 1; min-height: 0; display: grid; grid-template-columns: var(--sidebar-width) minmax(0, 1fr); }
662
+ .sidebar { overflow: auto; padding: 8px 0 24px; }
663
+ .main { overflow: auto; padding: 24px 32px 96px 17px; min-width: 0; }
664
+
665
+ /* A folder in the sidebar (bookmarks-folder-node): the same pill as the settings menu. */
666
+ .tree { list-style: none; margin: 0; padding: 0; }
667
+
668
+ .tree-item {
669
+ --depth: 0;
670
+ display: flex;
671
+ align-items: center;
672
+ gap: 8px;
673
+ min-height: 40px;
674
+ margin: 0 3px 0 1px;
675
+ padding: 0 4px 0 calc(8px + var(--depth) * 20px);
676
+ border-radius: 0 100px 100px 0;
677
+ color: var(--folder-text);
678
+ font-size: 14px;
679
+ font-weight: 500;
680
+ cursor: pointer;
681
+ user-select: none;
682
+ outline-offset: -2px;
683
+ text-decoration: none;
684
+ }
685
+
686
+ .tree-item:hover { background: var(--hover); text-decoration: none; }
687
+ .tree-item .icon { color: var(--icon-muted); }
688
+ .tree-item[aria-selected="true"], .tree-item[aria-current="true"], .tree-item[aria-current="page"] {
689
+ background: var(--selected-bg);
690
+ color: var(--selected-text);
691
+ }
692
+ .tree-item:is([aria-selected="true"], [aria-current="true"], [aria-current="page"]) .icon { color: inherit; }
693
+
694
+ /* The list card (bookmarks-list #list.card): 40px rows, no separators. */
695
+ .list-card {
696
+ max-width: 960px;
697
+ margin: 0 auto;
698
+ padding: 8px 0;
699
+ border-radius: var(--radius);
700
+ background: var(--card);
701
+ box-shadow: var(--card-shadow);
702
+ }
703
+
704
+ .item {
705
+ display: flex;
706
+ align-items: center;
707
+ gap: 20px;
708
+ height: 40px;
709
+ padding: 0 8px 0 20px;
710
+ user-select: none;
711
+ }
712
+
713
+ .item:hover { background: var(--hover); }
714
+ .item[aria-selected="true"] { background: var(--selected-bg); color: var(--selected-text); }
715
+
716
+ /* ----------------------------------------------------- Menu (cr-action-menu) */
717
+
718
+ .menu {
719
+ position: fixed;
720
+ inset: auto;
721
+ margin: 0;
722
+ min-width: 128px;
723
+ padding: 8px 0;
724
+ border: 0;
725
+ border-radius: 4px;
726
+ background: var(--menu-bg);
727
+ color: var(--text);
728
+ box-shadow: var(--menu-shadow);
729
+ }
730
+
731
+ .menu::backdrop { background: transparent; }
732
+
733
+ .menu [role="menuitem"], .menu .dropdown-item {
734
+ display: flex;
735
+ align-items: center;
736
+ width: 100%;
737
+ height: auto;
738
+ min-height: 32px;
739
+ padding: 8px 24px;
740
+ border: 0;
741
+ border-radius: 0;
742
+ background: none;
743
+ color: var(--text);
744
+ font: 400 13px/20px system-ui, sans-serif;
745
+ text-align: start;
746
+ white-space: nowrap;
747
+ }
748
+
749
+ .menu [role="menuitem"]:hover,
750
+ .menu [role="menuitem"]:focus { background: var(--hover); outline: none; }
751
+ .menu [role="menuitem"]:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
752
+ .menu [role="menuitem"]:disabled { opacity: 0.38; background: none; }
753
+ .menu hr { margin: 8px 0; border: 0; border-top: 1px solid var(--separator); }
754
+
755
+ /* ------------------------------------------------------- Dialog (cr-dialog) */
756
+
757
+ .dialog {
758
+ width: min(512px, calc(100vw - 32px));
759
+ max-height: calc(100vh - 48px);
760
+ padding: 0;
761
+ border: 0;
762
+ border-radius: var(--radius);
763
+ background: var(--dialog-bg);
764
+ color: var(--text);
765
+ line-height: 20px;
766
+ box-shadow: var(--dialog-shadow);
767
+ }
768
+
769
+ .dialog::backdrop { background: rgb(0 0 0 / 0.6); }
770
+ .dialog > form, .dialog > .dialog-content { display: flex; flex-direction: column; max-height: calc(100vh - 48px); }
771
+ .dialog-title { padding: 20px 20px 16px; font-size: 15px; font-weight: 400; line-height: 1.2; }
772
+ .dialog-body { display: grid; gap: 16px; padding: 0 20px; overflow: auto; color: var(--text-secondary); }
773
+ .dialog-body :is(.field, input, textarea, select, label) { color: var(--text); }
774
+ .dialog-body .field > label { color: var(--text-subtle); }
775
+ .dialog-buttons { display: flex; justify-content: flex-end; gap: 8px; padding: 16px; }
776
+
777
+ /* ------------------------------------------------------- Toast (cr-toast) */
778
+
779
+ .toast {
780
+ position: fixed;
781
+ left: 0;
782
+ bottom: 0;
783
+ z-index: 2;
784
+ display: flex;
785
+ align-items: center;
786
+ gap: 32px;
787
+ min-width: 288px;
788
+ max-width: min(568px, calc(100vw - 48px));
789
+ min-height: 52px;
790
+ margin: 24px;
791
+ padding: 0 16px;
792
+ border-radius: 8px;
793
+ background: var(--toast-bg);
794
+ color: var(--toast-text);
795
+ line-height: 20px;
796
+ box-shadow: 0 2px 4px 0 rgb(0 0 0 / 0.28);
797
+ }
798
+
799
+ .toast button {
800
+ height: auto;
801
+ margin-right: -8px;
802
+ padding: 8px;
803
+ border: 0;
804
+ background: none;
805
+ color: var(--toast-action);
806
+ font-size: 13px;
807
+ font-weight: 500;
808
+ text-decoration: none;
809
+ }
810
+
811
+ /* ------------------------------------------------------ Drawer (cr-drawer) */
812
+
813
+ .drawer {
814
+ position: fixed;
815
+ inset: 0 auto 0 0;
816
+ width: 256px;
817
+ max-width: calc(100vw - 56px);
818
+ height: 100%;
819
+ max-height: none;
820
+ margin: 0;
821
+ padding: 0;
822
+ border: 0;
823
+ background: var(--page-bg);
824
+ color: var(--text);
825
+ box-shadow: var(--card-shadow);
826
+ }
827
+
828
+ .drawer[open] { animation: cr-drawer-in 200ms ease; }
829
+ .drawer::backdrop { background: rgb(0 0 0 / 0.5); }
830
+ .drawer-head { display: flex; align-items: center; gap: 12px; height: var(--toolbar-height); padding-left: 24px; font-size: 22px; font-weight: 500; letter-spacing: 0.25px; }
831
+
832
+ @keyframes cr-drawer-in { from { transform: translateX(-100%); } }
833
+
834
+ @media (prefers-reduced-motion: reduce) {
835
+ .drawer[open] { animation: none; }
836
+ }
837
+
838
+ /* ------------------------------------------------------- Side panel (sp-*) */
839
+
840
+ /* A side panel page: <body class="side-panel">. Chrome draws the header (title,
841
+ pin, close); the page is the rest. Its own text colours, as the panel's are. */
842
+ .side-panel {
843
+ --text: var(--sp-text);
844
+ --text-secondary: var(--sp-text-secondary);
845
+ display: flex;
846
+ flex-direction: column;
847
+ height: 100vh;
848
+ overflow: hidden;
849
+ background: var(--sp-bg);
850
+ color: var(--sp-text);
851
+ }
852
+
853
+ /* cr-tabs, as panels with more than one view have them: 48px, the chosen one
854
+ in primary with a 2px bar. <div class="tabs" role="tablist"> of
855
+ <button class="tab" role="tab" aria-selected="true">. */
856
+ .tabs {
857
+ flex: none;
858
+ display: flex;
859
+ height: 48px;
860
+ user-select: none;
861
+ }
862
+
863
+ .tabs .tab {
864
+ position: relative;
865
+ flex: auto;
866
+ display: flex;
867
+ align-items: center;
868
+ justify-content: center;
869
+ height: 100%;
870
+ padding: 0 8px;
871
+ border: 0;
872
+ border-radius: 0;
873
+ background: none;
874
+ color: var(--text-secondary);
875
+ font: 500 14px/20px system-ui, sans-serif;
876
+ }
877
+
878
+ /* cr-tabs has no hover fill; a pressed tab shows only its ripple. */
879
+ .tabs .tab:hover, .tabs .tab:active { background: none; }
880
+ .tabs .tab:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
881
+ .tabs .tab[aria-selected="true"] { color: var(--primary); }
882
+
883
+ .tabs .tab[aria-selected="true"]::after {
884
+ content: "";
885
+ position: absolute;
886
+ left: 0;
887
+ right: 0;
888
+ bottom: 0;
889
+ height: 2px;
890
+ border-radius: 2px 2px 0 0;
891
+ background: var(--primary);
892
+ }
893
+
894
+ /* The scrolling body (.sp-scroller): the scrollbar sits in the 8px gutter. */
895
+ .sp-scroller {
896
+ flex: 1;
897
+ min-height: 0;
898
+ overflow-x: hidden;
899
+ overflow-y: auto;
900
+ scrollbar-gutter: stable;
901
+ }
902
+
903
+ .sp-scroller::-webkit-scrollbar { width: 8px; background: transparent; }
904
+ .sp-scroller::-webkit-scrollbar-thumb {
905
+ background: var(--sp-scrollbar);
906
+ background-clip: content-box;
907
+ border: solid 1.5px transparent;
908
+ border-radius: 100px;
909
+ }
910
+
911
+ /* The search field above a panel's list (cr-toolbar-search-field in the Bookmarks panel). */
912
+ .side-panel .search-field {
913
+ flex: none;
914
+ width: auto;
915
+ margin: 8px;
916
+ background: var(--sp-search-bg);
917
+ }
918
+
919
+ /* .sp-card: rows on a 12px-rounded card, 8px from the panel's edge. */
920
+ .sp-card {
921
+ margin: 0 0 0 8px;
922
+ padding: 8px 0;
923
+ border-radius: 12px;
924
+ background: var(--sp-card);
925
+ }
926
+
927
+ .sp-card + .sp-card { margin-top: 8px; }
928
+
929
+ /* sp-heading: a 36px heading in a card, with 24px buttons at its end
930
+ (a group's collapse chevron). <div class="sp-heading"><h2>…</h2>buttons</div> */
931
+ .sp-heading {
932
+ display: flex;
933
+ align-items: center;
934
+ gap: 4px;
935
+ height: 36px;
936
+ padding: 0 16px;
937
+ }
938
+
939
+ .sp-card > .sp-heading + .sp-heading,
940
+ .sp-card > .sp-item + .sp-heading,
941
+ .sp-card > .sp-list + .sp-heading { margin-top: 12px; }
942
+
943
+ .sp-heading > :is(h1, h2, h3) {
944
+ flex: 1;
945
+ min-width: 0;
946
+ overflow: hidden;
947
+ color: var(--text);
948
+ font-size: 14px;
949
+ font-weight: 500;
950
+ line-height: 20px;
951
+ text-overflow: ellipsis;
952
+ white-space: nowrap;
953
+ }
954
+
955
+ .sp-heading .meta { flex: none; color: var(--text-secondary); font-size: 11px; line-height: 16px; }
956
+
957
+ /* 24px icon buttons with 16px icons (sp-icon-buttons-row, the row suffix). */
958
+ .sp-heading .icon-button,
959
+ .sp-item .icon-button { width: 24px; height: 24px; color: var(--text-secondary); }
960
+ .sp-heading .icon-button .icon,
961
+ .sp-item .icon-button .icon { width: 16px; height: 16px; }
962
+
963
+ /* A group's chevron (a right-pointing one, turned): up when open, down when
964
+ folded, as the Reading list's expand-less / expand-more. */
965
+ .sp-heading .expand .icon { transform: rotate(90deg); transition: transform 100ms; }
966
+ .sp-heading .expand[aria-expanded="true"] .icon { transform: rotate(-90deg); }
967
+
968
+ /* cr-url-list-item: a 48px row, a 40px icon box, a title and a description;
969
+ buttons at its end show on hover or focus. <li class="sp-item"> holding
970
+ <button class="sp-item-main"> (the row's own action) and <span class="sp-suffix">. */
971
+ .sp-list { list-style: none; margin: 0; padding: 0; }
972
+
973
+ .sp-item {
974
+ position: relative;
975
+ display: flex;
976
+ align-items: center;
977
+ min-height: 48px;
978
+ padding: 4px 16px;
979
+ }
980
+
981
+ .sp-item:hover, .sp-item:focus-within { background: var(--hover); }
982
+ .sp-item:active { background: var(--active); }
983
+
984
+ /* The row's whole area clicks; the suffix buttons sit above it. */
985
+ .sp-item-main {
986
+ position: absolute;
987
+ inset: 0;
988
+ width: 100%;
989
+ height: 100%;
990
+ padding: 0;
991
+ border: 0;
992
+ border-radius: 0;
993
+ background: none;
994
+ cursor: pointer;
995
+ }
996
+
997
+ .sp-item-main:hover, .sp-item-main:active { background: none; }
998
+ .sp-item-main:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
999
+
1000
+ .sp-icon {
1001
+ flex: none;
1002
+ display: grid;
1003
+ place-items: center;
1004
+ width: 40px;
1005
+ height: 40px;
1006
+ margin-right: 16px;
1007
+ border-radius: 8px;
1008
+ background: var(--sp-icon-bg);
1009
+ color: var(--text-secondary);
1010
+ pointer-events: none;
1011
+ }
1012
+
1013
+ .sp-text {
1014
+ flex: 1;
1015
+ min-width: 0;
1016
+ display: grid;
1017
+ gap: 2px;
1018
+ pointer-events: none;
1019
+ }
1020
+
1021
+ .sp-title, .sp-description {
1022
+ overflow: hidden;
1023
+ text-overflow: ellipsis;
1024
+ white-space: nowrap;
1025
+ }
1026
+
1027
+ .sp-title { color: var(--text); font-size: 12px; font-weight: 500; line-height: 16px; }
1028
+ .sp-description { color: var(--text-secondary); font-size: 11px; line-height: 14px; }
1029
+
1030
+ .sp-suffix {
1031
+ position: relative;
1032
+ flex: none;
1033
+ display: none;
1034
+ align-items: center;
1035
+ gap: 4px;
1036
+ margin: 0 -4px 0 8px;
1037
+ }
1038
+
1039
+ .sp-item:is(:hover, :focus-within) .sp-suffix,
1040
+ .sp-suffix.always { display: flex; }
1041
+
1042
+ /* sp-empty-state: a centred heading and a line of help. */
1043
+ .sp-empty {
1044
+ display: flex;
1045
+ flex-direction: column;
1046
+ align-items: center;
1047
+ padding: 40px 16px;
1048
+ text-align: center;
1049
+ }
1050
+
1051
+ .sp-empty .icon { width: 48px; height: 48px; margin-bottom: 16px; color: var(--primary); }
1052
+ .sp-empty h2 { max-width: 259px; margin-bottom: 8px; color: var(--text); font-size: 16px; font-weight: 500; line-height: 24px; }
1053
+ .sp-empty p { max-width: 229px; margin-bottom: 12px; color: var(--text-secondary); font-size: 13px; line-height: 20px; }
1054
+
1055
+ /* sp-footer pinned at the bottom, holding the floating pill
1056
+ (cr-button.floating-button): "Add current tab", "Add new command". */
1057
+ .sp-footer {
1058
+ flex: none;
1059
+ height: 56px;
1060
+ padding: 8px;
1061
+ }
1062
+
1063
+ button.floating {
1064
+ display: inline-flex;
1065
+ align-items: center;
1066
+ gap: 8px;
1067
+ height: 40px;
1068
+ padding: 8px 16px 8px 12px;
1069
+ border: 0;
1070
+ border-radius: 8px;
1071
+ background: var(--tonal-bg);
1072
+ color: var(--tonal-text);
1073
+ transition: box-shadow 80ms linear;
1074
+ }
1075
+
1076
+ button.floating:hover { background: var(--tonal-bg); box-shadow: var(--elevation-3); }
1077
+ button.floating .icon { width: 20px; height: 20px; }
1078
+
1079
+ /* The toast in a panel: the panel's width less 8px each side, above the footer. */
1080
+ .side-panel .toast {
1081
+ left: 8px;
1082
+ right: 8px;
1083
+ bottom: 56px;
1084
+ min-width: 0;
1085
+ max-width: none;
1086
+ margin: 0;
1087
+ }
1088
+
1089
+ /* ------------------------------------------------------------ Narrow */
1090
+
1091
+ @media (max-width: 720px) {
1092
+ .toolbar { grid-template-columns: auto minmax(0, 1fr) auto; gap: 8px; padding-right: 8px; }
1093
+ .toolbar-start { padding-left: 16px; }
1094
+ }