emdash-classic-theme 0.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.
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/package.json +53 -0
- package/src/env.d.ts +9 -0
- package/src/index.ts +52 -0
- package/src/inject.ts +38 -0
- package/src/middleware-configured.ts +13 -0
- package/src/middleware.ts +13 -0
- package/src/serve.ts +32 -0
- package/src/skin.css +3184 -0
package/src/skin.css
ADDED
|
@@ -0,0 +1,3184 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
skin.css (v4)
|
|
3
|
+
"WordPress" skin for the EmDash admin panel. Served from the site itself and injected
|
|
4
|
+
into the <head> of /_emdash/admin by the outer middleware (src/cms/painel-wp.ts).
|
|
5
|
+
Does not touch core: if this file disappears, the panel goes back to normal.
|
|
6
|
+
|
|
7
|
+
v4 = the owner's v3 plus the 9 fixes from the 2026-09-07 audit (29 agents,
|
|
8
|
+
four fronts, each finding passed through a skeptic). Each fix is commented
|
|
9
|
+
at the point where it applies, with the measured reason.
|
|
10
|
+
========================================================================== */
|
|
11
|
+
|
|
12
|
+
/* ==========================================================================
|
|
13
|
+
[0] WHITE LABEL TOKENS (change only here, per client)
|
|
14
|
+
The html:root[data-mode] selector has specificity (0,2,1): it beats EmDash's
|
|
15
|
+
"classic" theme ([data-theme="classic"]:not([data-mode="dark"]) = 0,2,0),
|
|
16
|
+
which defines several of these same tokens.
|
|
17
|
+
========================================================================== */
|
|
18
|
+
html:root[data-mode] {
|
|
19
|
+
/* FIX 1 (2026-09-07 audit): kumo's dark mode comes from light-dark() on ~130
|
|
20
|
+
tokens. Declaring color-scheme here disarms all of them at once, and it's
|
|
21
|
+
what stops the panel from mixing the skin's white surface with the dark
|
|
22
|
+
theme's light text (contrast measured at 1.28:1 on the success notice).
|
|
23
|
+
WordPress has no dark mode, so forcing light is consistent.
|
|
24
|
+
Accepted consequence: the panel's theme switcher becomes inert. */
|
|
25
|
+
color-scheme: light;
|
|
26
|
+
|
|
27
|
+
/* Status text: the skin paints the surface white, and the classic theme's
|
|
28
|
+
values (#00a32a, #dba617) would measure 3.35:1 and ~2:1. These pass AA on
|
|
29
|
+
#fff. */
|
|
30
|
+
--text-color-kumo-danger: #b32d2e;
|
|
31
|
+
--text-color-kumo-success: #007017;
|
|
32
|
+
--text-color-kumo-warning: #996800;
|
|
33
|
+
--text-color-kumo-info: #2271b1;
|
|
34
|
+
|
|
35
|
+
--wp-accent: #2271b1; /* client's accent color */
|
|
36
|
+
--wp-ink: #1d2327; /* toolbar and menu */
|
|
37
|
+
--wp-canvas: #f0f0f1; /* page background */
|
|
38
|
+
--wp-surface: #ffffff; /* widgets, tables, fields */
|
|
39
|
+
/* round 5 (2026-09-08, owner's request): ONE border color for everything, and
|
|
40
|
+
weaker. Section outline, widget frame, table header and field resting
|
|
41
|
+
border all now read the same #dcdcde. CONSCIOUS DECISION AGAINST WCAG:
|
|
42
|
+
#dcdcde measures 1.36:1 on white, below the 3:1 that 1.4.11 asks for on
|
|
43
|
+
component outlines. The owner was told the number and kept it ("the
|
|
44
|
+
borders are too heavy... even against contrast"). What holds
|
|
45
|
+
accessibility in place instead of the border: field hover darkens to
|
|
46
|
+
#8c8f94 (line ~722) and focus still relies on the 2px accent ring, which
|
|
47
|
+
is what the standard actually requires to be visible. */
|
|
48
|
+
--wp-line: #dcdcde; /* borders: section, widget, table, band */
|
|
49
|
+
--wp-field-line: #dcdcde; /* field resting border: the SAME as the section's (hover goes to #8c8f94) */
|
|
50
|
+
--wp-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
51
|
+
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
52
|
+
|
|
53
|
+
--wp-toolbar-h: 32px;
|
|
54
|
+
--wp-menu-w: 230px; /* WP uses 160px; the labels here are bigger */
|
|
55
|
+
--wp-radius: 4px; /* corner radius of widgets and buttons */
|
|
56
|
+
--wp-icon: 20px; /* menu icons (dashicons = 20px) */
|
|
57
|
+
--wp-icon-bold: 8; /* extra weight for Phosphor icons (0 = regular, 8 = bold) */
|
|
58
|
+
|
|
59
|
+
/* derived values: fixed fallback for browsers without color-mix (the @supports below overrides) */
|
|
60
|
+
--wp-accent-hover: #135e96;
|
|
61
|
+
--wp-accent-deep: #0a4b78;
|
|
62
|
+
--wp-accent-soft: #72aee6;
|
|
63
|
+
--wp-accent-tint: #f0f6fc;
|
|
64
|
+
--wp-accent-contrast: #ffffff;
|
|
65
|
+
--wp-ink-hover: #2c3338;
|
|
66
|
+
--wp-ink-text: #f0f0f1;
|
|
67
|
+
--wp-ink-icon: rgba(240, 246, 252, .72);
|
|
68
|
+
--wp-ink-muted: rgba(240, 246, 252, .52);
|
|
69
|
+
--wp-ink-line: rgba(255, 255, 255, .08);
|
|
70
|
+
--wp-text: #1d2327;
|
|
71
|
+
--wp-muted: #646970;
|
|
72
|
+
--wp-line-soft: #dcdcde;
|
|
73
|
+
--wp-button-bg: #f6f7f7;
|
|
74
|
+
|
|
75
|
+
--wp-ease: cubic-bezier(.2, .7, .2, 1);
|
|
76
|
+
--wp-ease-out: cubic-bezier(.16, 1, .3, 1);
|
|
77
|
+
--wp-fast: .15s;
|
|
78
|
+
--wp-normal: .28s;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@supports (color: color-mix(in oklab, red 50%, white)) {
|
|
82
|
+
html:root[data-mode] {
|
|
83
|
+
--wp-accent-hover: color-mix(in oklab, var(--wp-accent) 82%, black);
|
|
84
|
+
--wp-accent-deep: color-mix(in oklab, var(--wp-accent) 66%, black);
|
|
85
|
+
--wp-accent-soft: color-mix(in oklab, var(--wp-accent) 52%, white);
|
|
86
|
+
--wp-accent-tint: color-mix(in oklab, var(--wp-accent) 7%, var(--wp-surface));
|
|
87
|
+
--wp-ink-hover: color-mix(in oklab, var(--wp-ink) 90%, white);
|
|
88
|
+
--wp-ink-text: color-mix(in oklab, var(--wp-ink) 6%, white);
|
|
89
|
+
--wp-ink-icon: color-mix(in oklab, var(--wp-ink-text) 72%, transparent);
|
|
90
|
+
--wp-ink-muted: color-mix(in oklab, var(--wp-ink-text) 56%, transparent);
|
|
91
|
+
--wp-ink-line: color-mix(in oklab, white 8%, transparent);
|
|
92
|
+
--wp-text: var(--wp-ink);
|
|
93
|
+
--wp-muted: color-mix(in oklab, var(--wp-ink) 66%, var(--wp-canvas));
|
|
94
|
+
/* round 5: no longer derived from --wp-line. It only feeds --color-kumo-recessed, which
|
|
95
|
+
is a BACKGROUND, not a border; with the new border (#dcdcde) the math returned
|
|
96
|
+
#eaeaeb and lightened that background without anyone asking for it. Pinned to the
|
|
97
|
+
value it always had. */
|
|
98
|
+
--wp-button-bg: color-mix(in oklab, var(--wp-canvas) 40%, var(--wp-surface));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* ==========================================================================
|
|
103
|
+
[1] BRIDGE TO THE EMDASH DESIGN SYSTEM
|
|
104
|
+
========================================================================== */
|
|
105
|
+
html:root[data-mode] {
|
|
106
|
+
/* brand and focus color: EmDash's blue becomes the client's accent color */
|
|
107
|
+
--color-kumo-brand: var(--wp-accent);
|
|
108
|
+
--color-kumo-brand-hover: var(--wp-accent-hover);
|
|
109
|
+
--color-kumo-focus: var(--wp-accent);
|
|
110
|
+
--text-color-kumo-link: var(--wp-accent);
|
|
111
|
+
|
|
112
|
+
/* surfaces */
|
|
113
|
+
--color-kumo-canvas: var(--wp-canvas);
|
|
114
|
+
--color-kumo-elevated: var(--wp-canvas);
|
|
115
|
+
--color-kumo-base: var(--wp-surface);
|
|
116
|
+
--color-kumo-control: var(--wp-surface);
|
|
117
|
+
--color-kumo-overlay: var(--wp-surface);
|
|
118
|
+
--color-kumo-tint: var(--wp-canvas);
|
|
119
|
+
--color-kumo-hairline: var(--wp-line);
|
|
120
|
+
--color-kumo-line: var(--wp-line);
|
|
121
|
+
--color-kumo-recessed: var(--wp-line-soft);
|
|
122
|
+
|
|
123
|
+
/* text */
|
|
124
|
+
--text-color-kumo-default: var(--wp-text);
|
|
125
|
+
--text-color-kumo-subtle: var(--wp-muted);
|
|
126
|
+
|
|
127
|
+
/* radius: WP is nearly square; 4px on widgets and buttons, 3px on fields */
|
|
128
|
+
--radius-sm: 2px;
|
|
129
|
+
--radius-md: 3px;
|
|
130
|
+
--radius-lg: var(--wp-radius);
|
|
131
|
+
--radius-xl: 6px;
|
|
132
|
+
--radius-2xl: 8px;
|
|
133
|
+
|
|
134
|
+
/* wp-admin type scale: 13 body, 12 secondary, 23 heading */
|
|
135
|
+
--text-base: 13px;
|
|
136
|
+
--text-base--line-height: 1.5;
|
|
137
|
+
--text-sm: 12px;
|
|
138
|
+
--text-sm--line-height: 1.5;
|
|
139
|
+
--text-xs: 11px;
|
|
140
|
+
--text-2xl: 23px;
|
|
141
|
+
--text-2xl--line-height: 1.3;
|
|
142
|
+
--font-emdash: var(--wp-font);
|
|
143
|
+
|
|
144
|
+
/* EmDash's shadows also switch to the client's ink color */
|
|
145
|
+
--color-kumo-shadow-drop: rgba(29, 35, 39, .08);
|
|
146
|
+
--color-kumo-shadow-edge: rgba(29, 35, 39, .12);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
html[data-mode] body {
|
|
150
|
+
font-family: var(--wp-font);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* menu width and colors: EmDash sets this inline on the wrapper */
|
|
154
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) {
|
|
155
|
+
--sidebar-width: var(--wp-menu-w) !important;
|
|
156
|
+
--sidebar-bg: var(--wp-ink) !important;
|
|
157
|
+
--sidebar-active-bg: var(--wp-accent) !important;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* inside the menu, the text and line tokens switch to the dark ink ones;
|
|
161
|
+
anything in there using text-kumo-* or border-kumo-line follows on its own */
|
|
162
|
+
html[data-mode] :is(aside,nav).emdash-sidebar {
|
|
163
|
+
--text-color-kumo-default: var(--wp-ink-text);
|
|
164
|
+
--text-color-kumo-strong: var(--wp-accent-contrast);
|
|
165
|
+
--text-color-kumo-subtle: var(--wp-ink-muted);
|
|
166
|
+
--text-color-kumo-inactive: var(--wp-ink-muted);
|
|
167
|
+
--color-kumo-line: var(--wp-ink-line);
|
|
168
|
+
--color-kumo-hairline: var(--wp-ink-line);
|
|
169
|
+
--color-kumo-elevated: var(--wp-ink);
|
|
170
|
+
--color-kumo-base: var(--wp-ink);
|
|
171
|
+
--color-kumo-fill: var(--wp-ink-hover);
|
|
172
|
+
--color-kumo-fill-hover: var(--wp-ink-hover);
|
|
173
|
+
color: var(--wp-ink-text);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* the toolbar too */
|
|
177
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header {
|
|
178
|
+
--text-color-kumo-default: var(--wp-ink-text);
|
|
179
|
+
--text-color-kumo-subtle: var(--wp-ink-icon);
|
|
180
|
+
--color-kumo-elevated: var(--wp-ink);
|
|
181
|
+
--color-kumo-base: var(--wp-ink);
|
|
182
|
+
--color-kumo-line: transparent;
|
|
183
|
+
--color-kumo-fill: var(--wp-ink-hover);
|
|
184
|
+
--color-kumo-fill-hover: var(--wp-ink-hover);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* ==========================================================================
|
|
188
|
+
[2] TOOLBAR = #wpadminbar
|
|
189
|
+
========================================================================== */
|
|
190
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header {
|
|
191
|
+
height: var(--wp-toolbar-h) !important;
|
|
192
|
+
min-height: 0 !important;
|
|
193
|
+
padding: 0 !important;
|
|
194
|
+
border-bottom: 0 !important;
|
|
195
|
+
font-size: 13px;
|
|
196
|
+
box-shadow: 0 1px 0 rgba(0, 0, 0, .25), 0 6px 16px -10px rgba(0, 0, 0, .45);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header > div {
|
|
200
|
+
gap: 0 !important;
|
|
201
|
+
height: 100%;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header a,
|
|
205
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header button {
|
|
206
|
+
height: var(--wp-toolbar-h) !important;
|
|
207
|
+
min-height: 0 !important;
|
|
208
|
+
padding: 0 8px !important;
|
|
209
|
+
margin: 0 !important;
|
|
210
|
+
border-radius: 0 !important;
|
|
211
|
+
background: transparent !important;
|
|
212
|
+
color: var(--wp-ink-text) !important;
|
|
213
|
+
font-size: 13px !important;
|
|
214
|
+
font-weight: 400 !important;
|
|
215
|
+
line-height: var(--wp-toolbar-h) !important;
|
|
216
|
+
box-shadow: none !important;
|
|
217
|
+
-webkit-transition: background-color var(--wp-fast) ease, color var(--wp-fast) ease;
|
|
218
|
+
transition: background-color var(--wp-fast) ease, color var(--wp-fast) ease;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header a:hover,
|
|
222
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header button:hover,
|
|
223
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header a:focus-visible,
|
|
224
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header button:focus-visible {
|
|
225
|
+
background: var(--wp-ink-hover) !important;
|
|
226
|
+
color: var(--wp-accent-soft) !important;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* FIX 6 (audit): without this, keyboard focus on the toolbar is invisible (the hover
|
|
230
|
+
background gives 1.16:1 against normal). Negative offset because the toolbar is 32px
|
|
231
|
+
and an outward ring would get clipped. WCAG 2.4.7 and 2.4.11. */
|
|
232
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header a:focus-visible,
|
|
233
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header button:focus-visible {
|
|
234
|
+
outline: 2px solid var(--wp-accent-soft) !important;
|
|
235
|
+
outline-offset: -2px !important;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header svg {
|
|
239
|
+
width: 16px;
|
|
240
|
+
height: 16px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header button[data-base-ui-tooltip-trigger] {
|
|
244
|
+
width: var(--wp-toolbar-h) !important;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* avatar: small square, like WP's Gravatar */
|
|
248
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header .rounded-full {
|
|
249
|
+
width: 18px !important;
|
|
250
|
+
height: 18px !important;
|
|
251
|
+
border-radius: 2px !important;
|
|
252
|
+
background: var(--wp-accent) !important;
|
|
253
|
+
color: var(--wp-accent-contrast) !important;
|
|
254
|
+
font-size: 10px !important;
|
|
255
|
+
font-weight: 600 !important;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* --------------------------------------------------------------------------
|
|
259
|
+
Desktop layout: toolbar fixed on top; menu and content drop down 32px;
|
|
260
|
+
the menu header (logo + site name) becomes the toolbar's left corner.
|
|
261
|
+
Below 768px EmDash uses its own mobile layout and none of this applies.
|
|
262
|
+
-------------------------------------------------------------------------- */
|
|
263
|
+
@media (min-width: 768px) {
|
|
264
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header > [data-sidebar="trigger"] {
|
|
265
|
+
display: none !important; /* the collapse button is already in the menu footer */
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) {
|
|
269
|
+
padding-top: var(--wp-toolbar-h) !important;
|
|
270
|
+
-webkit-box-sizing: border-box;
|
|
271
|
+
box-sizing: border-box;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header {
|
|
275
|
+
position: fixed !important;
|
|
276
|
+
top: 0 !important;
|
|
277
|
+
left: 0 !important;
|
|
278
|
+
right: 0 !important;
|
|
279
|
+
z-index: 30 !important; /* below the collapsed menu (z-40): the site name
|
|
280
|
+
needs to stay above the bar */
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="header"] {
|
|
284
|
+
position: fixed !important;
|
|
285
|
+
top: 0 !important;
|
|
286
|
+
left: 0 !important;
|
|
287
|
+
z-index: 31 !important;
|
|
288
|
+
width: auto !important;
|
|
289
|
+
max-width: 45vw;
|
|
290
|
+
padding-right: 12px !important;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* collapsed menu: EmDash swaps the container to fixed inset-y-0;
|
|
294
|
+
here it starts below the toolbar */
|
|
295
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar)[data-state="collapsed"] :is(aside,nav).emdash-sidebar > [data-sidebar="content-container"] {
|
|
296
|
+
top: var(--wp-toolbar-h) !important;
|
|
297
|
+
bottom: 0 !important;
|
|
298
|
+
height: auto !important;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* menu header (logo + site name) */
|
|
303
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="header"] {
|
|
304
|
+
height: var(--wp-toolbar-h) !important;
|
|
305
|
+
padding: 0 8px !important;
|
|
306
|
+
gap: 0 !important;
|
|
307
|
+
border: 0 !important;
|
|
308
|
+
background: var(--wp-ink) !important;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="header"] a {
|
|
312
|
+
height: var(--wp-toolbar-h) !important;
|
|
313
|
+
width: auto !important;
|
|
314
|
+
max-width: none !important;
|
|
315
|
+
padding: 0 !important;
|
|
316
|
+
gap: 8px !important;
|
|
317
|
+
color: var(--wp-ink-text) !important;
|
|
318
|
+
font-size: 13px !important;
|
|
319
|
+
font-weight: 400 !important;
|
|
320
|
+
line-height: var(--wp-toolbar-h) !important;
|
|
321
|
+
text-decoration: none !important;
|
|
322
|
+
-webkit-transition: color var(--wp-fast) ease;
|
|
323
|
+
transition: color var(--wp-fast) ease;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="header"] a:hover {
|
|
327
|
+
color: var(--wp-accent-soft) !important;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* the site name doesn't truncate or disappear when the menu collapses */
|
|
331
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="header"] a > span {
|
|
332
|
+
grid-template-columns: 1fr !important;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="header"] a span {
|
|
336
|
+
width: auto !important;
|
|
337
|
+
max-width: none !important;
|
|
338
|
+
font-weight: 400 !important;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="header"] svg {
|
|
342
|
+
width: 18px !important;
|
|
343
|
+
height: 18px !important;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* ==========================================================================
|
|
347
|
+
[3] SIDE MENU = #adminmenu
|
|
348
|
+
========================================================================== */
|
|
349
|
+
html[data-mode] :is(aside,nav).emdash-sidebar,
|
|
350
|
+
html[data-mode] :is(aside,nav).emdash-sidebar > [data-sidebar="content-container"] {
|
|
351
|
+
background: var(--wp-ink) !important;
|
|
352
|
+
border-right: 0 !important;
|
|
353
|
+
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .18);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* edge to edge, no lateral breathing room, and NO scrollbar:
|
|
357
|
+
scrolling keeps working (mouse wheel, trackpad, keyboard),
|
|
358
|
+
only the track disappears. It was the track that ran over the little arrow. */
|
|
359
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="content"] > div {
|
|
360
|
+
padding: 0 !important;
|
|
361
|
+
scrollbar-width: none;
|
|
362
|
+
-ms-overflow-style: none;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="content"] > div::-webkit-scrollbar {
|
|
366
|
+
display: none;
|
|
367
|
+
width: 0;
|
|
368
|
+
height: 0;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="group"] {
|
|
372
|
+
gap: 0 !important;
|
|
373
|
+
margin: 0 !important;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu"] {
|
|
377
|
+
gap: 0 !important;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* item */
|
|
381
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] {
|
|
382
|
+
position: relative !important;
|
|
383
|
+
min-height: 36px !important;
|
|
384
|
+
/* round 3: 16px on the right so the label doesn't touch the active item's arrow or the
|
|
385
|
+
bar's border. Asymmetric on purpose: the left stays at 8px because it's what fixes
|
|
386
|
+
the icon's horizontal position, the same in both states. */
|
|
387
|
+
padding: 0 16px 0 8px !important;
|
|
388
|
+
border-radius: 0 !important;
|
|
389
|
+
font-size: 14px !important;
|
|
390
|
+
font-weight: 400 !important;
|
|
391
|
+
line-height: 1.3 !important;
|
|
392
|
+
box-shadow: none !important;
|
|
393
|
+
text-decoration: none !important;
|
|
394
|
+
-webkit-transition: background-color var(--wp-fast) ease, color var(--wp-fast) ease;
|
|
395
|
+
transition: background-color var(--wp-fast) ease, color var(--wp-fast) ease;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] > div {
|
|
399
|
+
gap: 8px !important;
|
|
400
|
+
translate: none !important;
|
|
401
|
+
-webkit-transform: none !important;
|
|
402
|
+
transform: none !important;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* hover: in EmDash, hover and active use the same token; in WP they're different */
|
|
406
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"]:hover:not([data-active="true"]) {
|
|
407
|
+
background: var(--wp-ink-hover) !important;
|
|
408
|
+
color: var(--wp-accent-soft) !important;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"]:focus-visible,
|
|
412
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="trigger"]:focus-visible {
|
|
413
|
+
outline: 2px solid var(--wp-accent-soft) !important;
|
|
414
|
+
outline-offset: -2px !important;
|
|
415
|
+
box-shadow: none !important;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* current item */
|
|
419
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"][data-active="true"] {
|
|
420
|
+
background: var(--wp-accent) !important;
|
|
421
|
+
color: var(--wp-accent-contrast) !important;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/* the little arrow: removed from content flow and placed on top of the active item */
|
|
425
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"][data-active="true"]::after {
|
|
426
|
+
content: "";
|
|
427
|
+
position: absolute;
|
|
428
|
+
right: 0;
|
|
429
|
+
top: 50%;
|
|
430
|
+
width: 0;
|
|
431
|
+
height: 0;
|
|
432
|
+
margin-top: -8px;
|
|
433
|
+
border: 8px solid transparent;
|
|
434
|
+
border-right-color: var(--wp-canvas);
|
|
435
|
+
pointer-events: none;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* collapsed menu: the icon does NOT move. In round 3 the owner asked for the icon to stay in
|
|
439
|
+
the same horizontal position in both states, so `justify-content: center` was removed (it
|
|
440
|
+
becomes flex-start in block [17c]) and `padding: 0` was removed, which was what made the
|
|
441
|
+
icon shift when collapsing. Its spacing still comes from the single item padding rule, which
|
|
442
|
+
applies in both states: that's what guarantees they never diverge. */
|
|
443
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar)[data-state="collapsed"] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] > div {
|
|
444
|
+
flex: 0 0 auto !important;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar)[data-state="collapsed"] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] > div > span {
|
|
448
|
+
display: none !important;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar)[data-state="collapsed"] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"][data-active="true"]::after {
|
|
452
|
+
display: none;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* submenu (if any item has children) */
|
|
456
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-sub"] {
|
|
457
|
+
background: var(--wp-ink-hover) !important;
|
|
458
|
+
border: 0 !important;
|
|
459
|
+
margin: 0 !important;
|
|
460
|
+
padding: 6px 0 !important;
|
|
461
|
+
gap: 0 !important;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-sub-button"] {
|
|
465
|
+
min-height: 28px !important;
|
|
466
|
+
padding: 0 12px 0 40px !important;
|
|
467
|
+
border-radius: 0 !important;
|
|
468
|
+
background: transparent !important;
|
|
469
|
+
color: var(--wp-ink-icon) !important;
|
|
470
|
+
font-size: 13px !important;
|
|
471
|
+
font-weight: 400 !important;
|
|
472
|
+
box-shadow: none !important;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-sub-button"]:hover {
|
|
476
|
+
color: var(--wp-accent-soft) !important;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-sub-button"][data-active="true"] {
|
|
480
|
+
color: var(--wp-accent-contrast) !important;
|
|
481
|
+
font-weight: 600 !important;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/* group labels ("Conteudo", "Gerenciar", "Admin"): subtle separators */
|
|
485
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="group-label"] {
|
|
486
|
+
margin: 5px 0 0 !important;
|
|
487
|
+
border: 0 !important;
|
|
488
|
+
border-top: 1px solid var(--wp-ink-line) !important;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="group-label"] > div > div {
|
|
492
|
+
margin: 0 !important;
|
|
493
|
+
padding: 10px 8px 4px !important;
|
|
494
|
+
color: var(--wp-ink-muted) !important;
|
|
495
|
+
font-size: 11px !important;
|
|
496
|
+
font-weight: 600 !important;
|
|
497
|
+
letter-spacing: .06em !important;
|
|
498
|
+
text-transform: uppercase !important;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* footer = "Recolher menu" */
|
|
502
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] {
|
|
503
|
+
height: 40px !important;
|
|
504
|
+
padding: 0 !important;
|
|
505
|
+
background: var(--wp-ink) !important;
|
|
506
|
+
border-top: 1px solid var(--wp-ink-line) !important;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] > div {
|
|
510
|
+
display: none !important; /* the site name is already in the toolbar */
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"] {
|
|
514
|
+
width: 100% !important;
|
|
515
|
+
height: 40px !important;
|
|
516
|
+
justify-content: flex-start !important;
|
|
517
|
+
gap: 8px !important;
|
|
518
|
+
padding: 0 8px !important;
|
|
519
|
+
border-radius: 0 !important;
|
|
520
|
+
background: transparent !important;
|
|
521
|
+
color: var(--wp-ink-icon) !important;
|
|
522
|
+
font-size: 14px !important;
|
|
523
|
+
-webkit-transition: background-color var(--wp-fast) ease, color var(--wp-fast) ease;
|
|
524
|
+
transition: background-color var(--wp-fast) ease, color var(--wp-fast) ease;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/* the text stays inside the button itself: the whole clickable area */
|
|
528
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"]::after {
|
|
529
|
+
content: "Recolher menu";
|
|
530
|
+
white-space: nowrap;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"][aria-expanded="false"]::after {
|
|
534
|
+
display: none;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/* round 3: the collapse button also stops zeroing padding and centering, so its
|
|
538
|
+
icon doesn't shift when the bar shrinks. The justify becomes flex-start in block [17c]. */
|
|
539
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"][aria-expanded="false"] {
|
|
540
|
+
justify-content: flex-start !important;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"]:hover {
|
|
544
|
+
background: var(--wp-ink-hover) !important;
|
|
545
|
+
color: var(--wp-accent-soft) !important;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/* FIX 3 (audit): the mobile drawer's scrim uses bg-kumo-recessed, which the token
|
|
549
|
+
bridge maps to a light gray. Without this the scrim comes out almost white over the page.
|
|
550
|
+
kumo's opacity-80 already gives the transparency: here it's just the ink color. */
|
|
551
|
+
html[data-mode] [data-sidebar-backdrop] {
|
|
552
|
+
background-color: var(--wp-ink) !important;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/* ==========================================================================
|
|
556
|
+
[4] MENU ICONS (Phosphor, regular weight)
|
|
557
|
+
Bigger and with more presence. The weight comes from a stroke laid over the path:
|
|
558
|
+
Phosphor regular is a 16-unit outline on a 256 grid;
|
|
559
|
+
a stroke of 8 turns it into 24, which is exactly the family's "bold" weight.
|
|
560
|
+
No icon is swapped, so an EmDash update doesn't break it.
|
|
561
|
+
========================================================================== */
|
|
562
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] svg {
|
|
563
|
+
width: var(--wp-icon) !important;
|
|
564
|
+
height: var(--wp-icon) !important;
|
|
565
|
+
flex: 0 0 var(--wp-icon);
|
|
566
|
+
opacity: 1 !important;
|
|
567
|
+
color: var(--wp-ink-icon);
|
|
568
|
+
overflow: visible;
|
|
569
|
+
/* round 3: only the color interpolates. The owner asked to remove the icon's movement on hover,
|
|
570
|
+
and the transform transition here was the leftover half after removing the translate. */
|
|
571
|
+
-webkit-transition: color var(--wp-fast) ease;
|
|
572
|
+
transition: color var(--wp-fast) ease;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] svg[viewBox="0 0 256 256"] path {
|
|
576
|
+
stroke: currentColor;
|
|
577
|
+
stroke-width: var(--wp-icon-bold);
|
|
578
|
+
stroke-linejoin: round;
|
|
579
|
+
stroke-linecap: round;
|
|
580
|
+
paint-order: stroke fill;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"]:hover svg {
|
|
584
|
+
color: var(--wp-accent-soft);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"][data-active="true"] svg {
|
|
588
|
+
color: var(--wp-accent-contrast);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"] svg,
|
|
592
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header [data-sidebar="trigger"] svg {
|
|
593
|
+
width: var(--wp-icon) !important;
|
|
594
|
+
height: var(--wp-icon) !important;
|
|
595
|
+
opacity: 1 !important;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/* FIX 5 (audit): the extra weight only applies to Phosphor (256 grid). The "Recolher menu"
|
|
599
|
+
button's icon is kumo's PanelIcon, viewBox 24 with a 1.5 stroke: thickening it by 8
|
|
600
|
+
would paint a solid block. It's already born with the right weight, nothing to do here. */
|
|
601
|
+
|
|
602
|
+
/* ==========================================================================
|
|
603
|
+
[5] CONTENT
|
|
604
|
+
The core padding stays as EmDash does it (p-6). Only what is WP:
|
|
605
|
+
light-weight heading, "Adicionar novo" button next to the heading in lists,
|
|
606
|
+
widgets in the .postbox pattern, zebra-striped table.
|
|
607
|
+
========================================================================== */
|
|
608
|
+
|
|
609
|
+
/* page title: 23px comes from the --text-2xl token; the weight WP wants is light */
|
|
610
|
+
html[data-mode] main h1 {
|
|
611
|
+
font-weight: 400 !important;
|
|
612
|
+
color: var(--wp-text) !important;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/* REMOVED on 2026-09-07: the rule that forced `justify-content: flex-start` on the
|
|
616
|
+
title row. It stuck the button next to the h1 like in WordPress, but only worked on
|
|
617
|
+
screens WITHOUT a subtitle. On screens with a subtitle (Menus, Categories, Comentarios)
|
|
618
|
+
the h1 and the subtitle live in the same div, the button rose to vertical center and the
|
|
619
|
+
whole row went out of alignment. EmDash's layout is already `flex justify-between`, which
|
|
620
|
+
solves both cases: the right call here is to have no rule, and not push toward the other
|
|
621
|
+
side. The rules below stay, because they paint the BUTTON (the .page-title-action's
|
|
622
|
+
clothing), never the row's geometry. */
|
|
623
|
+
|
|
624
|
+
/* = .page-title-action (on both screens) */
|
|
625
|
+
html[data-mode] main h1 + div > a,
|
|
626
|
+
html[data-mode] main h1 + div > button {
|
|
627
|
+
height: 30px !important;
|
|
628
|
+
min-height: 0 !important;
|
|
629
|
+
padding: 0 10px !important;
|
|
630
|
+
gap: 4px !important;
|
|
631
|
+
border: 1px solid var(--wp-accent) !important;
|
|
632
|
+
border-radius: 3px !important;
|
|
633
|
+
background: var(--wp-button-bg) !important;
|
|
634
|
+
color: var(--wp-accent) !important;
|
|
635
|
+
font-size: 13px !important;
|
|
636
|
+
font-weight: 400 !important;
|
|
637
|
+
line-height: 28px !important;
|
|
638
|
+
box-shadow: 0 1px 1px rgba(29, 35, 39, .04) !important;
|
|
639
|
+
text-decoration: none !important;
|
|
640
|
+
-webkit-transition: background-color var(--wp-fast) ease, border-color var(--wp-fast) ease, color var(--wp-fast) ease, box-shadow var(--wp-fast) ease, -webkit-transform var(--wp-fast) ease;
|
|
641
|
+
transition: background-color var(--wp-fast) ease, border-color var(--wp-fast) ease, color var(--wp-fast) ease, box-shadow var(--wp-fast) ease, transform var(--wp-fast) ease;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
html[data-mode] main > div > div:has(> div > h1) > a,
|
|
645
|
+
html[data-mode] main > div > div:has(> div > h1) > button {
|
|
646
|
+
height: 30px !important;
|
|
647
|
+
min-height: 0 !important;
|
|
648
|
+
padding: 0 10px !important;
|
|
649
|
+
gap: 4px !important;
|
|
650
|
+
border: 1px solid var(--wp-accent) !important;
|
|
651
|
+
border-radius: 3px !important;
|
|
652
|
+
background: var(--wp-button-bg) !important;
|
|
653
|
+
color: var(--wp-accent) !important;
|
|
654
|
+
font-size: 13px !important;
|
|
655
|
+
font-weight: 400 !important;
|
|
656
|
+
line-height: 28px !important;
|
|
657
|
+
box-shadow: 0 1px 1px rgba(29, 35, 39, .04) !important;
|
|
658
|
+
text-decoration: none !important;
|
|
659
|
+
-webkit-transition: background-color var(--wp-fast) ease, border-color var(--wp-fast) ease, color var(--wp-fast) ease, box-shadow var(--wp-fast) ease, -webkit-transform var(--wp-fast) ease;
|
|
660
|
+
transition: background-color var(--wp-fast) ease, border-color var(--wp-fast) ease, color var(--wp-fast) ease, box-shadow var(--wp-fast) ease, transform var(--wp-fast) ease;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/* (the rules with :has are kept separate from the ones without :has on purpose:
|
|
664
|
+
a browser without support discards only the invalid rule, not both) */
|
|
665
|
+
html[data-mode] main h1 + div > a:hover,
|
|
666
|
+
html[data-mode] main h1 + div > button:hover {
|
|
667
|
+
background: var(--wp-surface) !important;
|
|
668
|
+
border-color: var(--wp-accent-deep) !important;
|
|
669
|
+
color: var(--wp-accent-deep) !important;
|
|
670
|
+
box-shadow: 0 2px 6px -2px rgba(29, 35, 39, .18) !important;
|
|
671
|
+
-webkit-transform: translateY(-1px);
|
|
672
|
+
transform: translateY(-1px);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
html[data-mode] main > div > div:has(> div > h1) > a:hover,
|
|
676
|
+
html[data-mode] main > div > div:has(> div > h1) > button:hover {
|
|
677
|
+
background: var(--wp-surface) !important;
|
|
678
|
+
border-color: var(--wp-accent-deep) !important;
|
|
679
|
+
color: var(--wp-accent-deep) !important;
|
|
680
|
+
box-shadow: 0 2px 6px -2px rgba(29, 35, 39, .18) !important;
|
|
681
|
+
-webkit-transform: translateY(-1px);
|
|
682
|
+
transform: translateY(-1px);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
html[data-mode] main h1 + div > a:active,
|
|
686
|
+
html[data-mode] main h1 + div > button:active {
|
|
687
|
+
-webkit-transform: translateY(0);
|
|
688
|
+
transform: translateY(0);
|
|
689
|
+
box-shadow: none !important;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
html[data-mode] main > div > div:has(> div > h1) > a:active,
|
|
693
|
+
html[data-mode] main > div > div:has(> div > h1) > button:active {
|
|
694
|
+
-webkit-transform: translateY(0);
|
|
695
|
+
transform: translateY(0);
|
|
696
|
+
box-shadow: none !important;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
html[data-mode] main h1 + div > a svg,
|
|
700
|
+
html[data-mode] main h1 + div > button svg {
|
|
701
|
+
width: 14px !important;
|
|
702
|
+
height: 14px !important;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/* FIX 7 (audit): the rules above write box-shadow with !important, and in Tailwind
|
|
706
|
+
v4 kumo's ring IS box-shadow: the focus ring dies. Two separate rules on purpose,
|
|
707
|
+
so a browser without :has() doesn't discard both. --wp-accent-deep because the button already
|
|
708
|
+
has a border in the accent color, and a ring of the same color would read as a thicker border. */
|
|
709
|
+
html[data-mode] main h1 + div > a:focus-visible,
|
|
710
|
+
html[data-mode] main h1 + div > button:focus-visible {
|
|
711
|
+
outline: 2px solid var(--wp-accent-deep) !important;
|
|
712
|
+
outline-offset: 2px !important;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
html[data-mode] main > div > div:has(> div > h1) > a:focus-visible,
|
|
716
|
+
html[data-mode] main > div > div:has(> div > h1) > button:focus-visible {
|
|
717
|
+
outline: 2px solid var(--wp-accent-deep) !important;
|
|
718
|
+
outline-offset: 2px !important;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
html[data-mode] main > div > div:has(> div > h1) > a svg,
|
|
722
|
+
html[data-mode] main > div > div:has(> div > h1) > button svg {
|
|
723
|
+
width: 14px !important;
|
|
724
|
+
height: 14px !important;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/* fields: WP's border (#8c8f94) comes in through Tailwind's own ring */
|
|
728
|
+
html[data-mode] main input.ring,
|
|
729
|
+
html[data-mode] main textarea.ring,
|
|
730
|
+
html[data-mode] main select.ring,
|
|
731
|
+
html[data-mode] main [role="combobox"].ring {
|
|
732
|
+
--tw-ring-color: var(--wp-field-line);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/* round 4: with the lighter resting border, the pointer restores the measured 3.245:1 */
|
|
736
|
+
html[data-mode] main input.ring:not(:disabled):hover,
|
|
737
|
+
html[data-mode] main textarea.ring:not(:disabled):hover,
|
|
738
|
+
html[data-mode] main select.ring:not(:disabled):hover,
|
|
739
|
+
html[data-mode] main [role="combobox"].ring:not(:disabled):hover {
|
|
740
|
+
--tw-ring-color: #8c8f94;
|
|
741
|
+
}
|
|
742
|
+
html[data-mode] main [role="combobox"].ring:not(:disabled):focus-visible {
|
|
743
|
+
--tw-ring-color: var(--wp-accent); /* combobox is a <button>, its focus comes only from the token */
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
html[data-mode] main input.ring:focus-visible,
|
|
747
|
+
html[data-mode] main textarea.ring:focus-visible,
|
|
748
|
+
html[data-mode] main select.ring:focus-visible {
|
|
749
|
+
--tw-ring-color: var(--wp-accent);
|
|
750
|
+
box-shadow: 0 0 0 1px var(--wp-accent), 0 0 0 4px var(--wp-accent-tint);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/* content links */
|
|
754
|
+
html[data-mode] main a {
|
|
755
|
+
-webkit-transition: color var(--wp-fast) ease, background-color var(--wp-fast) ease;
|
|
756
|
+
transition: color var(--wp-fast) ease, background-color var(--wp-fast) ease;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/* --------------------------------------------------------------------------
|
|
760
|
+
widgets = .postbox (border and radius already come from the tokens; here only the header
|
|
761
|
+
and the body without the second frame)
|
|
762
|
+
-------------------------------------------------------------------------- */
|
|
763
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline {
|
|
764
|
+
background: var(--wp-surface) !important;
|
|
765
|
+
overflow: hidden;
|
|
766
|
+
-webkit-transition: box-shadow var(--wp-normal) var(--wp-ease), -webkit-transform var(--wp-normal) var(--wp-ease);
|
|
767
|
+
transition: box-shadow var(--wp-normal) var(--wp-ease), transform var(--wp-normal) var(--wp-ease);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline > div:first-child {
|
|
771
|
+
margin: 0 !important;
|
|
772
|
+
padding: 0 12px !important;
|
|
773
|
+
min-height: 36px !important;
|
|
774
|
+
border-bottom: 1px solid var(--wp-line) !important;
|
|
775
|
+
background: var(--wp-surface) !important;
|
|
776
|
+
color: var(--wp-text) !important;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline > div:first-child h2,
|
|
780
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline > div:first-child h3 {
|
|
781
|
+
margin: 0 !important;
|
|
782
|
+
padding: 0 !important;
|
|
783
|
+
font-size: 14px !important;
|
|
784
|
+
font-weight: 600 !important;
|
|
785
|
+
line-height: 1.4 !important;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline > .ring-kumo-fill,
|
|
789
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline > div:not(:first-child) {
|
|
790
|
+
--tw-ring-color: transparent; /* removes the inner frame, the Tailwind way */
|
|
791
|
+
margin: 0 !important;
|
|
792
|
+
padding: 12px !important;
|
|
793
|
+
border-radius: 0 !important;
|
|
794
|
+
background: var(--wp-surface) !important;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline .text-3xl {
|
|
798
|
+
font-size: 24px !important;
|
|
799
|
+
font-weight: 600 !important;
|
|
800
|
+
line-height: 1.2 !important;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/* round 5 (2026-09-08): EDGE TO EDGE, the same rule already applied to the side menu.
|
|
804
|
+
The widget body loses its lateral breathing room WHEN it holds a list, and that room moves
|
|
805
|
+
inside the row itself. This way the hover highlight covers the card's full width, like
|
|
806
|
+
in wp-admin, instead of floating inset 12px on each side.
|
|
807
|
+
|
|
808
|
+
WHY `:has(> .space-y-1)` AND NOT THE WHOLE RULE: the same element (LayerCard.Primary,
|
|
809
|
+
anchored by .ring-kumo-fill) is also the body of the metric cards and of any
|
|
810
|
+
plugin widget, where the content has NO inset of its own and would touch the border. The
|
|
811
|
+
`.space-y-1` is the wrapper EmDash puts only around the Dashboard's link lists
|
|
812
|
+
(CollectionList and RecentActivity, admin/dist/index.js). Without :has(), nothing changes: it
|
|
813
|
+
degrades to today's inset.
|
|
814
|
+
The `space-y-1` also disappears: with a separator between rows, the 4px gap becomes a hole. */
|
|
815
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline > .ring-kumo-fill:has(> .space-y-1) {
|
|
816
|
+
padding: 0 !important;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/* round 5.1 (2026-09-08): `margin-block`, not `margin-top`. Measured in the fixture: the
|
|
820
|
+
Tailwind v4 EmDash compiles puts the `space-y-1` gap as `margin-block-end` on the
|
|
821
|
+
PREVIOUS element, not as margin-top on the next one, so `margin-top: 0` zeroed the half that
|
|
822
|
+
was already zero and a 4px gap remained between rows. That was the "empty space on hover":
|
|
823
|
+
the highlight band would stop and a white strip appeared before the next one. */
|
|
824
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline .space-y-1 > * {
|
|
825
|
+
margin-block: 0 !important;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/* list rows inside the widget */
|
|
829
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a[class*="justify-between"],
|
|
830
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a[class*="rounded-md"] {
|
|
831
|
+
/* 12px on the side = the same inset the body used to have, now inside the row */
|
|
832
|
+
padding: 8px 12px !important;
|
|
833
|
+
border-radius: 0 !important;
|
|
834
|
+
color: var(--wp-accent) !important;
|
|
835
|
+
border-bottom: 1px solid var(--wp-canvas);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a[class*="justify-between"]:last-child,
|
|
839
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a[class*="rounded-md"]:last-child {
|
|
840
|
+
border-bottom: 0;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a[class*="justify-between"]:hover,
|
|
844
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a[class*="rounded-md"]:hover {
|
|
845
|
+
color: var(--wp-accent-hover) !important;
|
|
846
|
+
background: var(--wp-accent-tint) !important;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a .font-medium {
|
|
850
|
+
font-weight: 400 !important;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/* --------------------------------------------------------------------------
|
|
854
|
+
listing tables = .wp-list-table
|
|
855
|
+
-------------------------------------------------------------------------- */
|
|
856
|
+
html[data-mode] main table thead th {
|
|
857
|
+
background: var(--wp-surface) !important;
|
|
858
|
+
border-bottom: 1px solid var(--wp-line) !important;
|
|
859
|
+
font-weight: 400 !important;
|
|
860
|
+
padding: 8px 10px !important;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
html[data-mode] main table tbody tr {
|
|
864
|
+
-webkit-transition: background-color var(--wp-fast) ease;
|
|
865
|
+
transition: background-color var(--wp-fast) ease;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
html[data-mode] main table tbody tr:nth-child(odd) {
|
|
869
|
+
background: var(--wp-button-bg) !important;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
html[data-mode] main table tbody tr:hover {
|
|
873
|
+
background: var(--wp-accent-tint) !important;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
html[data-mode] main table tbody td {
|
|
877
|
+
border-top: 1px solid var(--wp-canvas) !important;
|
|
878
|
+
padding: 8px 10px !important;
|
|
879
|
+
vertical-align: middle !important;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
html[data-mode] main table tbody td a[class*="font-medium"] {
|
|
883
|
+
font-weight: 600 !important;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/* ==========================================================================
|
|
887
|
+
[6] EDITOR'S RIGHT PANEL
|
|
888
|
+
This is another EmDash "sidebar". It stays in its own design system, it only gets
|
|
889
|
+
the border and surface background to match the rest.
|
|
890
|
+
========================================================================== */
|
|
891
|
+
html[data-mode] aside[data-side="right"] > [data-sidebar="content-container"] {
|
|
892
|
+
background: var(--wp-surface) !important;
|
|
893
|
+
border-left: 1px solid var(--wp-line) !important;
|
|
894
|
+
box-shadow: -8px 0 24px -20px rgba(29, 35, 39, .25);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/* ==========================================================================
|
|
898
|
+
[7] SHADOW AND MOTION
|
|
899
|
+
========================================================================== */
|
|
900
|
+
|
|
901
|
+
/* Elevated surface shadow (widgets, tables, settings blocks).
|
|
902
|
+
Four layers: 1px outline, contact, distant ambient and diffuse tip.
|
|
903
|
+
|
|
904
|
+
WHY `border-color: transparent` (measured on the panel on 2026-09-07):
|
|
905
|
+
the outline is drawn by the FIRST shadow layer (`0 0 0 1px`), and the targets arrive
|
|
906
|
+
here in two different border states:
|
|
907
|
+
- panel card: kumo's `ring`, `border-width: 0` -> nothing to erase
|
|
908
|
+
- table and settings: 1px `border` in `--color-kumo-line`, which is the SAME color
|
|
909
|
+
(#c3c4c7) as our first layer -> was drawing a DOUBLE 2px line
|
|
910
|
+
Zeroing the COLOR (not the width) keeps the box the same size, so nothing shifts, and
|
|
911
|
+
leaves a single 1px outline in both cases. The internal separator (`divide-y`) is not
|
|
912
|
+
affected: it lives on the children, not on this element.
|
|
913
|
+
|
|
914
|
+
GUARDS (2026-09-07 audit): `div.border.bg-kumo-base` is a painting utility, not a
|
|
915
|
+
contract, and it hits about 110 elements in the panel. Since the shadow is `!important`, it
|
|
916
|
+
was REPLACING the own elevation of anything that already had its own. Four families were
|
|
917
|
+
hit in production: the marketplace's loading skeleton (it pulsed with a real card's
|
|
918
|
+
shadow), the user detail skeleton (it doesn't carry the class, it inherits the pulsing parent,
|
|
919
|
+
hence `:not(.animate-pulse *)`), the text editor's floating bar and the plugin
|
|
920
|
+
consent dialog (they lost `shadow-lg`), and the Widgets drag layer.
|
|
921
|
+
The guard fails safe: if the class disappears in an upgrade, it stops excluding and the
|
|
922
|
+
behavior goes back to today's, never worse. Do NOT add `:has(.animate-pulse)`: that
|
|
923
|
+
would erase the shadow on the user list's skeleton table frame, where it's
|
|
924
|
+
correct. */
|
|
925
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline,
|
|
926
|
+
html[data-mode] main .overflow-x-auto.border:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]),
|
|
927
|
+
html[data-mode] main div.border.bg-kumo-base:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]) {
|
|
928
|
+
/* round 5.1 (2026-09-08, owner's preference): `border`, not `border-color`. With a 1px
|
|
929
|
+
TRANSPARENT border the card's own background paints underneath it (default background-clip
|
|
930
|
+
is border-box), so between the content and the shadow's first-layer hairline there's a 1px
|
|
931
|
+
gap of surface color left over: it's the "hairline one pixel away" that the table and the
|
|
932
|
+
settings blocks already had by accident (they arrive here with Tailwind's 1px `border`) and
|
|
933
|
+
that the Dashboard card didn't have (it arrives with `border-width: 0` and only the ring). One
|
|
934
|
+
word equalizes the technique across three families: metric cards, Dashboard widgets (Content
|
|
935
|
+
and Recent Activity), listing tables and settings blocks.
|
|
936
|
+
OUT OF SCOPE: the editor's right panel, which is a band, not a card (block [7] is
|
|
937
|
+
scoped to `main`, and its sections are <section>, not `div.border.bg-kumo-base`). */
|
|
938
|
+
border: 1px solid transparent !important;
|
|
939
|
+
box-shadow:
|
|
940
|
+
0 0 0 1px var(--wp-line),
|
|
941
|
+
0 1px 1px rgba(29, 35, 39, .03),
|
|
942
|
+
0 32px 25px -35px rgba(29, 35, 39, .15),
|
|
943
|
+
0 12px 28px -18px rgba(29, 35, 39, .22) !important;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline:hover,
|
|
947
|
+
html[data-mode] main .overflow-x-auto.border:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]):hover,
|
|
948
|
+
html[data-mode] main div.border.bg-kumo-base:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]):hover {
|
|
949
|
+
box-shadow:
|
|
950
|
+
0 0 0 1px var(--wp-line),
|
|
951
|
+
0 1px 2px rgba(29, 35, 39, .04),
|
|
952
|
+
0 36px 28px -34px rgba(29, 35, 39, .18),
|
|
953
|
+
0 20px 40px -20px rgba(29, 35, 39, .28) !important;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/* only the card lifts on hover: table and settings block are not fully clickable */
|
|
957
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline:hover {
|
|
958
|
+
-webkit-transform: translateY(-1px);
|
|
959
|
+
transform: translateY(-1px);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/* entrances ------------------------------------------------------------- */
|
|
963
|
+
@-webkit-keyframes wp-rise {
|
|
964
|
+
from { opacity: 0; -webkit-transform: translateY(8px); transform: translateY(8px); }
|
|
965
|
+
to { opacity: 1; -webkit-transform: none; transform: none; }
|
|
966
|
+
}
|
|
967
|
+
@keyframes wp-rise {
|
|
968
|
+
from { opacity: 0; -webkit-transform: translateY(8px); transform: translateY(8px); }
|
|
969
|
+
to { opacity: 1; -webkit-transform: none; transform: none; }
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
@-webkit-keyframes wp-slide {
|
|
973
|
+
from { opacity: 0; -webkit-transform: translateX(-10px); transform: translateX(-10px); }
|
|
974
|
+
to { opacity: 1; -webkit-transform: none; transform: none; }
|
|
975
|
+
}
|
|
976
|
+
@keyframes wp-slide {
|
|
977
|
+
from { opacity: 0; -webkit-transform: translateX(-10px); transform: translateX(-10px); }
|
|
978
|
+
to { opacity: 1; -webkit-transform: none; transform: none; }
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
@-webkit-keyframes wp-fade {
|
|
982
|
+
from { opacity: 0; }
|
|
983
|
+
to { opacity: 1; }
|
|
984
|
+
}
|
|
985
|
+
@keyframes wp-fade {
|
|
986
|
+
from { opacity: 0; }
|
|
987
|
+
to { opacity: 1; }
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/* load hierarchy: toolbar, then the menu item by item */
|
|
991
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header {
|
|
992
|
+
-webkit-animation: wp-fade .35s ease-out both;
|
|
993
|
+
animation: wp-fade .35s ease-out both;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"] {
|
|
997
|
+
-webkit-animation: wp-slide .4s var(--wp-ease-out) both;
|
|
998
|
+
animation: wp-slide .4s var(--wp-ease-out) both;
|
|
999
|
+
-webkit-animation-delay: calc(.06s + var(--wp-i, 0) * 22ms);
|
|
1000
|
+
animation-delay: calc(.06s + var(--wp-i, 0) * 22ms);
|
|
1001
|
+
}
|
|
1002
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"]:nth-child(2) { --wp-i: 1; }
|
|
1003
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"]:nth-child(3) { --wp-i: 2; }
|
|
1004
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"]:nth-child(4) { --wp-i: 3; }
|
|
1005
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"]:nth-child(5) { --wp-i: 4; }
|
|
1006
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"]:nth-child(6) { --wp-i: 5; }
|
|
1007
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"]:nth-child(7) { --wp-i: 6; }
|
|
1008
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"]:nth-child(n+8) { --wp-i: 7; }
|
|
1009
|
+
/* each group enters a bit after the previous one */
|
|
1010
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="group"]:nth-child(2) [data-sidebar="menu-item"] { -webkit-animation-delay: calc(.14s + var(--wp-i, 0) * 22ms); animation-delay: calc(.14s + var(--wp-i, 0) * 22ms); }
|
|
1011
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="group"]:nth-child(3) [data-sidebar="menu-item"] { -webkit-animation-delay: calc(.30s + var(--wp-i, 0) * 22ms); animation-delay: calc(.30s + var(--wp-i, 0) * 22ms); }
|
|
1012
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="group"]:nth-child(n+4) [data-sidebar="menu-item"] { -webkit-animation-delay: calc(.46s + var(--wp-i, 0) * 22ms); animation-delay: calc(.46s + var(--wp-i, 0) * 22ms); }
|
|
1013
|
+
|
|
1014
|
+
/* content: title, then cards, then widgets; runs on every screen change
|
|
1015
|
+
because EmDash (an SPA) mounts the blocks again */
|
|
1016
|
+
html[data-mode] main > div > * {
|
|
1017
|
+
-webkit-animation: wp-rise .38s var(--wp-ease-out) both;
|
|
1018
|
+
animation: wp-rise .38s var(--wp-ease-out) both;
|
|
1019
|
+
}
|
|
1020
|
+
html[data-mode] main > div > *:nth-child(2) { -webkit-animation-delay: .06s; animation-delay: .06s; }
|
|
1021
|
+
html[data-mode] main > div > *:nth-child(3) { -webkit-animation-delay: .12s; animation-delay: .12s; }
|
|
1022
|
+
html[data-mode] main > div > *:nth-child(4) { -webkit-animation-delay: .18s; animation-delay: .18s; }
|
|
1023
|
+
html[data-mode] main > div > *:nth-child(5) { -webkit-animation-delay: .24s; animation-delay: .24s; }
|
|
1024
|
+
html[data-mode] main > div > *:nth-child(n+6) { -webkit-animation-delay: .30s; animation-delay: .30s; }
|
|
1025
|
+
|
|
1026
|
+
/* inside the widget, the rows arrive after the frame */
|
|
1027
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a[class*="justify-between"],
|
|
1028
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a[class*="rounded-md"],
|
|
1029
|
+
html[data-mode] main table tbody tr {
|
|
1030
|
+
-webkit-animation: wp-fade .3s ease-out both;
|
|
1031
|
+
animation: wp-fade .3s ease-out both;
|
|
1032
|
+
-webkit-animation-delay: calc(.22s + var(--wp-i, 0) * 30ms);
|
|
1033
|
+
animation-delay: calc(.22s + var(--wp-i, 0) * 30ms);
|
|
1034
|
+
}
|
|
1035
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a:nth-child(2), html[data-mode] main table tbody tr:nth-child(2) { --wp-i: 1; }
|
|
1036
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a:nth-child(3), html[data-mode] main table tbody tr:nth-child(3) { --wp-i: 2; }
|
|
1037
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a:nth-child(4), html[data-mode] main table tbody tr:nth-child(4) { --wp-i: 3; }
|
|
1038
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a:nth-child(5), html[data-mode] main table tbody tr:nth-child(5) { --wp-i: 4; }
|
|
1039
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a:nth-child(6), html[data-mode] main table tbody tr:nth-child(6) { --wp-i: 5; }
|
|
1040
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a:nth-child(7), html[data-mode] main table tbody tr:nth-child(7) { --wp-i: 6; }
|
|
1041
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a:nth-child(8), html[data-mode] main table tbody tr:nth-child(8) { --wp-i: 7; }
|
|
1042
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a:nth-child(n+9), html[data-mode] main table tbody tr:nth-child(n+9) { --wp-i: 8; }
|
|
1043
|
+
|
|
1044
|
+
/* browser view transitions: they apply when navigation reloads the
|
|
1045
|
+
document (login, returning from the site). Inside EmDash's SPA, a screen
|
|
1046
|
+
exit would need document.startViewTransition() in the router, which is JS. */
|
|
1047
|
+
@view-transition {
|
|
1048
|
+
navigation: auto;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
::view-transition-old(root) {
|
|
1052
|
+
-webkit-animation: wp-vt-out .12s ease-out both;
|
|
1053
|
+
animation: wp-vt-out .12s ease-out both;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
::view-transition-new(root) {
|
|
1057
|
+
-webkit-animation: wp-vt-in .24s var(--wp-ease-out) both;
|
|
1058
|
+
animation: wp-vt-in .24s var(--wp-ease-out) both;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
@-webkit-keyframes wp-vt-out { to { opacity: 0; } }
|
|
1062
|
+
@keyframes wp-vt-out { to { opacity: 0; } }
|
|
1063
|
+
@-webkit-keyframes wp-vt-in {
|
|
1064
|
+
from { opacity: 0; -webkit-transform: translateY(4px); transform: translateY(4px); }
|
|
1065
|
+
}
|
|
1066
|
+
@keyframes wp-vt-in {
|
|
1067
|
+
from { opacity: 0; -webkit-transform: translateY(4px); transform: translateY(4px); }
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/* anyone who turned off system animations doesn't see any of this */
|
|
1071
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1072
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header,
|
|
1073
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-item"],
|
|
1074
|
+
html[data-mode] main > div > *,
|
|
1075
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline a,
|
|
1076
|
+
html[data-mode] main table tbody tr {
|
|
1077
|
+
-webkit-animation: none !important;
|
|
1078
|
+
animation: none !important;
|
|
1079
|
+
}
|
|
1080
|
+
/* FIX 8 (audit): in a separate rule. A browser without ::view-transition (Safari 17,
|
|
1081
|
+
Firefox ESR) invalidates the WHOLE list, and anyone who asked for less motion would receive all
|
|
1082
|
+
the animations. Here the worst case is losing only these two lines. */
|
|
1083
|
+
::view-transition-old(root),
|
|
1084
|
+
::view-transition-new(root) {
|
|
1085
|
+
-webkit-animation: none !important;
|
|
1086
|
+
animation: none !important;
|
|
1087
|
+
}
|
|
1088
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline:hover {
|
|
1089
|
+
-webkit-transform: none;
|
|
1090
|
+
transform: none;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/* content scrollbar: REMOVED in round 3 (2026-09-08). The track disappears,
|
|
1095
|
+
scrolling stays. The live rules are in block [R3.1] at the end of the file. */
|
|
1096
|
+
|
|
1097
|
+
/* ==========================================================================
|
|
1098
|
+
ROUND 2 OF ADJUSTMENTS (2026-09-07)
|
|
1099
|
+
Everything here was born from measuring the panel's bundle and source maps
|
|
1100
|
+
(@emdash-cms/admin 0.36.0, @cloudflare/kumo 2.6.0, embedded Base UI),
|
|
1101
|
+
never from reading a screenshot. Each rule states which CONTRACT it anchored on:
|
|
1102
|
+
a Base UI attribute, HTML semantics or a named class that kumo writes by
|
|
1103
|
+
hand. A Tailwind utility class only appears where no contract exists,
|
|
1104
|
+
and always with an upgrade-watch comment.
|
|
1105
|
+
|
|
1106
|
+
GOLDEN RULE OF THIS FILE: whoever positions a popup is Floating UI, via JS,
|
|
1107
|
+
in the Positioner. No rule below writes transform, position, inset or
|
|
1108
|
+
overflow on a popup. The only displacement used is the `translate` property,
|
|
1109
|
+
which is independent of `transform` and stays outside the popup's transition.
|
|
1110
|
+
========================================================================== */
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
/* ==========================================================================
|
|
1114
|
+
[R] LOG OF THE TWO REMOVALS IN THIS ROUND (already applied above)
|
|
1115
|
+
Kept here because the next person will want to know why they disappeared.
|
|
1116
|
+
|
|
1117
|
+
R1. The forced alignment on the page header. Exact snippet to
|
|
1118
|
+
look for (the comment that went with it also goes):
|
|
1119
|
+
|
|
1120
|
+
html[data-mode] main > div > div:has(> div > h1) {
|
|
1121
|
+
justify-content: flex-start !important;
|
|
1122
|
+
gap: 12px !important;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
WHY IT'S GONE: it matched whenever the h1 lives inside an intermediate
|
|
1126
|
+
div (subtitle, language selector, Media's min-w-0) and
|
|
1127
|
+
swapped EmDash's `justify-between` for flex-start. Result: the
|
|
1128
|
+
action rose to the middle of the row and hung next to the title,
|
|
1129
|
+
with the subtitle below it. The only two correct screens were Users and
|
|
1130
|
+
Dashboard, precisely the ones that did NOT match (h1 is a direct child). EmDash
|
|
1131
|
+
already handles both cases with justify-between: the right call here is to have no rule.
|
|
1132
|
+
STATE: already deleted in this file's working tree. What's still
|
|
1133
|
+
operational: `dist/client/painel-wp.css` still ships the old version,
|
|
1134
|
+
so it needs a build before deploy, otherwise the stale CSS goes live.
|
|
1135
|
+
|
|
1136
|
+
R2. The two selector lists in block [7] (elevated surface shadow)
|
|
1137
|
+
need a guard. Look for these three lines:
|
|
1138
|
+
|
|
1139
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline,
|
|
1140
|
+
html[data-mode] main .overflow-x-auto.border,
|
|
1141
|
+
html[data-mode] main div.border.bg-kumo-base {
|
|
1142
|
+
|
|
1143
|
+
and swap them for:
|
|
1144
|
+
|
|
1145
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline,
|
|
1146
|
+
html[data-mode] main .overflow-x-auto.border:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]),
|
|
1147
|
+
html[data-mode] main div.border.bg-kumo-base:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]) {
|
|
1148
|
+
|
|
1149
|
+
and do the same on the :hover list right below (same guards, with
|
|
1150
|
+
`:hover` at the end of each guarded line).
|
|
1151
|
+
|
|
1152
|
+
WHY: `div.border.bg-kumo-base` is a painting utility, not a
|
|
1153
|
+
contract, and it hits about 110 elements in the panel, not just table
|
|
1154
|
+
and settings block. Since the shadow is `!important`, it REPLACES the
|
|
1155
|
+
own elevation of anything that already had its own. Measured, four families were
|
|
1156
|
+
hit today, in production: the marketplace and theme loading skeleton
|
|
1157
|
+
(`animate-pulse ... border bg-kumo-base`), which pulsed with a
|
|
1158
|
+
real card's shadow; the user detail skeleton card, which doesn't carry
|
|
1159
|
+
the class but inherits the pulsing parent (hence
|
|
1160
|
+
`:not(.animate-pulse *)`); the text editor's floating bar and the
|
|
1161
|
+
plugin consent dialog, which lost `shadow-lg`; and the
|
|
1162
|
+
Widgets drag layer, which went flat during the drag.
|
|
1163
|
+
The guard fails safe: if the class disappears in an upgrade, it stops
|
|
1164
|
+
excluding and the behavior goes back to today's, never worse.
|
|
1165
|
+
Do NOT add `:has(.animate-pulse)`: that would erase the shadow on the
|
|
1166
|
+
UserList skeleton table frame, where the shadow is correct.
|
|
1167
|
+
========================================================================== */
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
/* ==========================================================================
|
|
1171
|
+
[8] USER MENU IN THE BAR = #wp-admin-bar-my-account .ab-sub-wrapper
|
|
1172
|
+
|
|
1173
|
+
The user menu is NOT a DropdownMenu: it's a kumo Popover
|
|
1174
|
+
(Header.tsx:66-113, `<Popover.Content className="w-56 p-2" align="end">`)
|
|
1175
|
+
with a PORTAL into <body>. Real DOM chain:
|
|
1176
|
+
<div data-base-ui-portal> direct child of <body>
|
|
1177
|
+
<div role="presentation" ...> Positioner, transform inline via JS
|
|
1178
|
+
<div role="dialog" class="... kumo-popover-popup"> Popup
|
|
1179
|
+
|
|
1180
|
+
In other words: the popup does NOT live inside the <header>, so no ancestor
|
|
1181
|
+
reaches it, and the toolbar's token bridge (block [1], lines 165-173) doesn't
|
|
1182
|
+
reach it. That's why the tokens are redeclared here, on the element itself,
|
|
1183
|
+
exactly the same trick already used in the side menu (lines 148-162).
|
|
1184
|
+
|
|
1185
|
+
HOOK: the security link, which only exists in this popover.
|
|
1186
|
+
`.kumo-popover-popup` alone is NOT enough: it's a named class kumo
|
|
1187
|
+
writes by hand (popover.tsx:217) on EVERY Popover.Content, including the
|
|
1188
|
+
content ones (BylineFilter, ContentList's date range, byline selector,
|
|
1189
|
+
code block language, PortableTextEditor's link editor). The href
|
|
1190
|
+
comes from TanStack Router with basepath "/_emdash/admin", so
|
|
1191
|
+
"/_emdash/admin/settings/security"; we use `*=` and not `$=` to survive
|
|
1192
|
+
a future trailingSlash or querystring. The other three occurrences of
|
|
1193
|
+
/settings/security in the bundle are the Settings page nav, the command
|
|
1194
|
+
palette item and the route definition: none of them inside a popover.
|
|
1195
|
+
The hook keeps holding during the exit animation, because the link only
|
|
1196
|
+
unmounts together with the popup.
|
|
1197
|
+
|
|
1198
|
+
WHY NOT THE TRIGGER: it would need a `:has()` anchored on <html> with a
|
|
1199
|
+
descendant argument, re-evaluated on every DOM mutation in a panel whose
|
|
1200
|
+
editor changes the DOM on every keystroke, and today it wouldn't catch anything
|
|
1201
|
+
besides this same menu (the theme toggle is a plain Button, the palette is CommandPalette).
|
|
1202
|
+
WATCH: if EmDash gains a SECOND menu in the bar, extend this
|
|
1203
|
+
selector with that menu's content hook, never with :has() at the root.
|
|
1204
|
+
========================================================================== */
|
|
1205
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"]) {
|
|
1206
|
+
/* token bridge: custom properties inherit, so children read them without needing
|
|
1207
|
+
their own selector */
|
|
1208
|
+
--text-color-kumo-default: var(--wp-ink-text);
|
|
1209
|
+
--text-color-kumo-subtle: var(--wp-ink-muted);
|
|
1210
|
+
--text-color-kumo-strong: var(--wp-accent-contrast);
|
|
1211
|
+
--text-color-kumo-link: var(--wp-accent-soft);
|
|
1212
|
+
/* 8.0:1 on #1d2327 and 6.5:1 on the hover #2c3338. The #b32d2e from line 30
|
|
1213
|
+
was tuned for a WHITE surface and measures 2.5:1 on the dark ink, meaning it
|
|
1214
|
+
would fail AA right on the "Sair" item. */
|
|
1215
|
+
--text-color-kumo-danger: #f4a3a3;
|
|
1216
|
+
--color-kumo-base: var(--wp-ink);
|
|
1217
|
+
--color-kumo-tint: var(--wp-ink-hover); /* hover:bg-kumo-tint on the items */
|
|
1218
|
+
--color-kumo-line: var(--wp-ink-line);
|
|
1219
|
+
--color-kumo-hairline: var(--wp-ink-line);
|
|
1220
|
+
--color-kumo-fill: var(--wp-ink-line); /* outline outline-kumo-fill */
|
|
1221
|
+
/* the arrow is drawn by three <path>s: the body uses fill-kumo-base, already
|
|
1222
|
+
darkened above, but the border uses fill-kumo-tip-shadow, which under the
|
|
1223
|
+
`color-scheme: light` from line 26 resolves to light gray. Without these
|
|
1224
|
+
two lines a light hairline would remain around it. */
|
|
1225
|
+
--color-kumo-tip-shadow: var(--wp-ink-line);
|
|
1226
|
+
--color-kumo-tip-stroke: transparent;
|
|
1227
|
+
|
|
1228
|
+
background-color: var(--wp-ink) !important;
|
|
1229
|
+
color: var(--wp-ink-text) !important;
|
|
1230
|
+
outline-color: var(--wp-ink-line) !important;
|
|
1231
|
+
/* round 3: the radius is now asymmetric and the shadow has four layers. Both things
|
|
1232
|
+
live in block [16], which comes later and wins by source order. Here only what didn't change remains. */
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/* [8b] STUCK TO THE BAR. kumo asks for `sideOffset: 8` (popover.tsx:189) and
|
|
1236
|
+
Floating UI applies that to the Positioner's TRANSFORM, which is forbidden to touch.
|
|
1237
|
+
The `translate` property is independent of `transform`, composes before it,
|
|
1238
|
+
doesn't enter the popup's `transition-[transform,scale,opacity]` (so it applies
|
|
1239
|
+
instantly, without animating) and doesn't change the layout box that Floating UI
|
|
1240
|
+
measures. The popup also doesn't use its own `transform`: Tailwind v4 compiles
|
|
1241
|
+
`scale-90` into the standalone `scale` property, so there's no collision with the
|
|
1242
|
+
entry and exit animation.
|
|
1243
|
+
Both directions are covered because flip() throws the menu upward in a
|
|
1244
|
+
short window. The bar has 32px forced on line 179, the popup used to be born at 40px
|
|
1245
|
+
and now returns to 32px, flush, without overlapping the trigger.
|
|
1246
|
+
Older Safari ignores `translate` and falls back to the 8px gap: clean degradation.
|
|
1247
|
+
The 8px fallback is deliberate: if the number should ever live in a single
|
|
1248
|
+
token, just create `--wp-popover-offset: 8px` in block [0] and nothing else changes. */
|
|
1249
|
+
/* round 3: this pair is gone. `translate` is ONE property, so the X and Y axes need
|
|
1250
|
+
to be written together, and the new pair (with the 5px that Base UI's shift() steals on the
|
|
1251
|
+
right) is in block [16a]. Keeping both pairs would make the lower one silently erase the upper one. */
|
|
1252
|
+
|
|
1253
|
+
/* [8c] THE ARROW DISAPPEARS. WordPress has no beak on the bar's submenu, and with the menu
|
|
1254
|
+
flush it would paint a triangle right on top of the bar itself.
|
|
1255
|
+
ANCHOR: kumo renders <Popover.Arrow> as the popup's first child and
|
|
1256
|
+
Base UI delivers it as <div aria-hidden="true" data-side ...>. No other
|
|
1257
|
+
direct child of the popup has the aria-hidden + data-side pair.
|
|
1258
|
+
WHY `visibility` AND NOT `display: none`: the arrow element is read by JS
|
|
1259
|
+
at three points, not one (the shift limiter, which IS positioning; the
|
|
1260
|
+
--transform-origin calculation; and Floating UI's arrow() middleware, which
|
|
1261
|
+
measures getDimensions). `display: none` zeroes all three and changes geometry;
|
|
1262
|
+
`visibility: hidden` keeps the measured 20x10 and leaves everything identical to
|
|
1263
|
+
today, just out of the paint. The arrow is decorative (aria-hidden), so leaving
|
|
1264
|
+
the paint is enough and there's no accessibility cost.
|
|
1265
|
+
`!important` because the arrow carries Tailwind's `flex` utility. */
|
|
1266
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"])
|
|
1267
|
+
> [aria-hidden="true"][data-side] {
|
|
1268
|
+
visibility: hidden !important;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/* [8d] INTERNAL BORDERS. Tailwind v4's preflight leaves `border: 0 solid`,
|
|
1272
|
+
so the identity block's `border-b` and the <hr> before "Sair" inherit
|
|
1273
|
+
currentColor: over dark ink that turns into a glaring white streak.
|
|
1274
|
+
The rule is SCOPED to the menu on purpose. The generic version (a `*` applying
|
|
1275
|
+
to every `.kumo-popover-popup`) has specificity (0,3,1) and would override
|
|
1276
|
+
any `border-kumo-*` utility (0,1,0) inside ANY
|
|
1277
|
+
popover: measured, it would break the `border-t border-kumo-line` on the
|
|
1278
|
+
ContentList's date selector footer, swapping a 10%-black hairline for a solid
|
|
1279
|
+
black line. */
|
|
1280
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"])
|
|
1281
|
+
:is(div, hr) {
|
|
1282
|
+
border-color: var(--wp-ink-line);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/* [8e] OPTIONAL, just finishing, and it does NOT darken anything. The CONTENT menus
|
|
1286
|
+
are already light on their own, by construction: neither Select (popup role="presentation"
|
|
1287
|
+
with a role="listbox" inside) nor DropdownMenu (popup role="menu") carries the
|
|
1288
|
+
kumo-popover-popup class, and the content popover doesn't satisfy the [8]
|
|
1289
|
+
hook. This here is the WP card applied to those two families, so they
|
|
1290
|
+
don't end up with kumo's light shadow next to the listings.
|
|
1291
|
+
|
|
1292
|
+
MEASURED PITFALL: in Tailwind v4 the `ring ring-kumo-line` these two
|
|
1293
|
+
popups use is NOT its own property, it's drawn INSIDE box-shadow.
|
|
1294
|
+
Overwriting box-shadow without re-adding the layer erases the 1px border and the menu
|
|
1295
|
+
becomes a white card with no outline over a white surface. That's why the line
|
|
1296
|
+
comes back explicit as the FIRST layer, in the same color the ring already had.
|
|
1297
|
+
Don't repeat `border-radius` or `--color-kumo-line` here: both already apply
|
|
1298
|
+
by inheritance from block [1] and would be a no-op. */
|
|
1299
|
+
html[data-mode] [data-base-ui-portal] [role="menu"],
|
|
1300
|
+
html[data-mode] [data-base-ui-portal] [role="presentation"]:has(> [role="listbox"]) {
|
|
1301
|
+
box-shadow:
|
|
1302
|
+
0 0 0 1px var(--wp-line),
|
|
1303
|
+
0 3px 5px rgba(0, 0, 0, .2) !important;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
|
|
1307
|
+
/* [8f] EDGE TO EDGE (round 5, 2026-09-08). Same rule as the side menu: the item occupies
|
|
1308
|
+
the popup's full width and the highlight has no rounded corner.
|
|
1309
|
+
|
|
1310
|
+
WHAT WAS HAPPENING, measured in the fixture with the panel's real CSS: the user
|
|
1311
|
+
menu's `Popover.Content` is born with `px-4 py-3` from kumo's base plus the
|
|
1312
|
+
`w-56 p-2` that EmDash's Header passes, and `px-4` WINS over `p-2` in the Tailwind
|
|
1313
|
+
v4 cascade (padding-inline after padding). Measured result: 12px 16px padding on the popup and
|
|
1314
|
+
3px border-radius on the items, meaning every hover floated inset 16px on each side.
|
|
1315
|
+
|
|
1316
|
+
`overflow: hidden` because, without padding, the first and last item's highlight would
|
|
1317
|
+
spill over the popup's rounded corner.
|
|
1318
|
+
Don't touch the arrow: it's already out of the picture in [8c]. */
|
|
1319
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"]) {
|
|
1320
|
+
padding: 0 !important;
|
|
1321
|
+
overflow: hidden;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/* the identity block (name + email) is the only child with border-b */
|
|
1325
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"])
|
|
1326
|
+
> div.border-b {
|
|
1327
|
+
margin-bottom: 0 !important;
|
|
1328
|
+
padding: 10px 12px !important;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/* the item grid loses the gap: flush rows, the wp-admin way */
|
|
1332
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"])
|
|
1333
|
+
> div.grid {
|
|
1334
|
+
gap: 0 !important;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"])
|
|
1338
|
+
:is(a, button) {
|
|
1339
|
+
border-radius: 0 !important;
|
|
1340
|
+
padding: 8px 12px !important;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
/* the <hr> before "Sair" runs corner to corner (its color already comes from [8d]) */
|
|
1344
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"]) hr {
|
|
1345
|
+
margin: 0 !important;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/* [8g] THE SAME FOR THE MENUS PORTED FROM KUMO (DropdownMenu, Select, Combobox,
|
|
1349
|
+
Autocomplete). Measured in the installed package: DropdownMenu's popup is born `min-w-36 p-1.5`
|
|
1350
|
+
with item `rounded-md px-2 py-1.5` and separator `-mx-1 my-1`; Select and Autocomplete
|
|
1351
|
+
swap the popup's padding for `mx-1.5` on EACH item. Both forms produce the same look:
|
|
1352
|
+
an inset highlight with a round corner. Both die here.
|
|
1353
|
+
|
|
1354
|
+
SCOPE: only menu items and list options (their own `role`). `margin-inline` is NOT zeroed
|
|
1355
|
+
on just any child on purpose: the Combobox's search field is `mx-1.5` with
|
|
1356
|
+
`w-[calc(100%-0.75rem)]`, so zeroing its margin would leave 12px of slack on the right. */
|
|
1357
|
+
html[data-mode] [data-base-ui-portal] [role="menu"],
|
|
1358
|
+
html[data-mode] [data-base-ui-portal] [role="listbox"] {
|
|
1359
|
+
padding: 0 !important;
|
|
1360
|
+
overflow: hidden;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
html[data-mode] [data-base-ui-portal] :is([role="menuitem"], [role="menuitemcheckbox"],
|
|
1364
|
+
[role="menuitemradio"], [role="option"]) {
|
|
1365
|
+
border-radius: 0 !important;
|
|
1366
|
+
margin-inline: 0 !important;
|
|
1367
|
+
padding-inline: 12px !important;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/* separator: `-mx-1` left 2px on each side inside the `p-1.5`; with no padding on the popup it
|
|
1371
|
+
just needs to stop pulling outward. Two selectors because kumo draws the separator
|
|
1372
|
+
sometimes with a role, sometimes as a plain <div class="h-px">. */
|
|
1373
|
+
html[data-mode] [data-base-ui-portal] :is([role="menu"], [role="listbox"]) [role="separator"],
|
|
1374
|
+
html[data-mode] [data-base-ui-portal] :is([role="menu"], [role="listbox"]) > .h-px {
|
|
1375
|
+
margin-inline: 0 !important;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
/* ==========================================================================
|
|
1380
|
+
[9] MODAL SCRIM = wp-admin's overlay (rgba(0,0,0,.7))
|
|
1381
|
+
|
|
1382
|
+
Two of the panel's scrims LIGHTEN instead of darkening, and both come from our
|
|
1383
|
+
own token bridge in block [1]:
|
|
1384
|
+
Dialog and AlertDialog: `bg-kumo-recessed opacity-80` (dialog.tsx:231), and
|
|
1385
|
+
--color-kumo-recessed becomes var(--wp-line-soft) = #dcdcde (line 109).
|
|
1386
|
+
Command palette: `bg-kumo-overlay opacity-80` (command-palette.tsx:164,
|
|
1387
|
+
mounted in the Shell), and --color-kumo-overlay becomes var(--wp-surface) =
|
|
1388
|
+
#ffffff (line 105). This is the worst one: a WHITE scrim at 80%.
|
|
1389
|
+
The same defect was already fixed in the mobile drawer (FIX 3, line 537) and wasn't
|
|
1390
|
+
propagated. It can't be fixed at the token level: bg-kumo-recessed and bg-kumo-overlay
|
|
1391
|
+
also paint recessed surfaces on the menubar, tabs, select item, combobox
|
|
1392
|
+
and sidebar.
|
|
1393
|
+
|
|
1394
|
+
The hook has two halves, STRUCTURE and the WRONG CLASS:
|
|
1395
|
+
[data-base-ui-portal] > ... every Base UI portal is a <div
|
|
1396
|
+
data-base-ui-portal> on <body>, and the scrim is a
|
|
1397
|
+
DIRECT child of it. The ">" matters: the Select
|
|
1398
|
+
popup is also role="presentation", but it lives
|
|
1399
|
+
inside the Positioner.
|
|
1400
|
+
role="presentation" the DialogBackdrop's contract.
|
|
1401
|
+
[data-open] / [data-closed] both states, otherwise the scrim would flash
|
|
1402
|
+
light again during the 150ms exit animation, when
|
|
1403
|
+
Base UI swaps data-open for data-closed.
|
|
1404
|
+
:is(.bg-kumo-recessed, ONLY the scrims painted with a light token. Without this
|
|
1405
|
+
.bg-kumo-overlay) half the rule would also catch the user detail
|
|
1406
|
+
scrim, which is already `bg-black/50` and does NOT have
|
|
1407
|
+
opacity-80: its alpha lives in the
|
|
1408
|
+
background-color itself, so overwriting it would give
|
|
1409
|
+
a 100% opaque screen instead of a scrim.
|
|
1410
|
+
MEASURED ON THE PANEL ON 2026-09-07, and it dropped a guard the audit asked for:
|
|
1411
|
+
the previous version of this rule had `:not([data-base-ui-inert])`, to exclude
|
|
1412
|
+
the InternalBackdrop (invisible click blocker, also a direct child of the
|
|
1413
|
+
portal). Except the one carrying `data-base-ui-inert` is the VISIBLE scrim, not the
|
|
1414
|
+
blocker: with the guard, the rule matched ZERO elements and the background kept
|
|
1415
|
+
lightening. What actually separates the two is the `.bg-kumo-recessed` class, which the
|
|
1416
|
+
blocker doesn't have. Verified in the DOM with the "Criar novo menu" modal open:
|
|
1417
|
+
the selector below matches exactly 1 element, 1223 by 879 pixels.
|
|
1418
|
+
|
|
1419
|
+
Don't touch opacity: kumo animates .8 to 0 on exit, and an !important there
|
|
1420
|
+
would kill the fade and leave the scrim opaque until unmount. A lighter scrim is made in the
|
|
1421
|
+
ink's alpha, never in the property kumo uses to animate.
|
|
1422
|
+
POPOVER HAS NO SCRIM: kumo doesn't render Popover.Backdrop, only Dialog,
|
|
1423
|
+
AlertDialog and the drawer. The bar popover's darkening is block [8].
|
|
1424
|
+
========================================================================== */
|
|
1425
|
+
html[data-mode] [data-base-ui-portal] > [role="presentation"]:is([data-open], [data-closed]):is(.bg-kumo-recessed, .bg-kumo-overlay) {
|
|
1426
|
+
background-color: var(--wp-ink) !important; /* #1d2327 at 80% = WP's rgba(0,0,0,.7) */
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
/* ==========================================================================
|
|
1431
|
+
[10] EDITOR'S RIGHT PANEL = METABOXES
|
|
1432
|
+
|
|
1433
|
+
There's no conflict with block [6]: it paints the <div data-sidebar="content-container">
|
|
1434
|
+
and the rules here paint DESCENDANTS of that div. They're different elements,
|
|
1435
|
+
not the same declaration fighting.
|
|
1436
|
+
|
|
1437
|
+
ANCHOR: <section data-sorting data-disclosure>, emitted by
|
|
1438
|
+
SortableContentSettingsSection. That PAIR of attributes is exclusive to this
|
|
1439
|
+
component across the whole admin, so there's no positional chain here. The drag
|
|
1440
|
+
handle is [data-sortable-handle] and the collapsible header is
|
|
1441
|
+
button[aria-expanded] (Base UI's Collapsible.Trigger, which emits
|
|
1442
|
+
aria-expanded in both states): also an attribute.
|
|
1443
|
+
FORBIDDEN in this section, and it's not a preference: `:nth-child` to find a block.
|
|
1444
|
+
The order is draggable and gets saved per user and per collection in localStorage,
|
|
1445
|
+
so a child position would paint the wrong block after the client's first drag. And no
|
|
1446
|
+
`transform` or `position` on the section: dnd-kit writes an inline
|
|
1447
|
+
transform during the drag and the handle is absolute inside it.
|
|
1448
|
+
|
|
1449
|
+
TWO NUMBERS TO WATCH ON EMDASH UPGRADE:
|
|
1450
|
+
1. the -16px inset mirrors the `p-4` EmDash puts on the section's child.
|
|
1451
|
+
If it becomes p-5, the inset becomes -20px.
|
|
1452
|
+
2. the title's DEPTH. Measured today, the h3 is exactly two
|
|
1453
|
+
levels below the section:
|
|
1454
|
+
section > div.p-4 > h3 (Publicar, Propriedade, SEO, plugins)
|
|
1455
|
+
section > div.p-4 > div > h3 (Traducoes, Taxonomias)
|
|
1456
|
+
section > div.p-4 > div.flex > h3 (Bylines)
|
|
1457
|
+
The rules below lock onto these two levels on purpose. A loose
|
|
1458
|
+
descendant (`section h3`) would also reach the INTERNAL h3 of a
|
|
1459
|
+
plugin panel, which is third-party markup: there the negative inset would throw the
|
|
1460
|
+
plugin's title outside the card, and since the tree above is overflow-x-hidden
|
|
1461
|
+
the result is CLIPPED text. Gaining a wrapper level in an upgrade, the
|
|
1462
|
+
title loses the band (silent degradation, doesn't break) and the fix is
|
|
1463
|
+
adding `> div` to the chain.
|
|
1464
|
+
RTL WATCH: like block [6], these rules use data-side="right",
|
|
1465
|
+
which becomes "left" in an RTL interface. The panel is pt-BR, so it applies today.
|
|
1466
|
+
========================================================================== */
|
|
1467
|
+
|
|
1468
|
+
/* the gap between the boxes becomes WP's gray screen.
|
|
1469
|
+
Two lines on purpose: the first catches the sections' direct wrapper
|
|
1470
|
+
(ContentSettingsPanel's root), the second catches the scroll area, otherwise
|
|
1471
|
+
a white band is left below the last box. The content-container stays white
|
|
1472
|
+
(block [6]) so the top action bar keeps sitting on a surface. */
|
|
1473
|
+
/* round 5 (2026-09-08): the gray gap is GONE. The owner asked for the end of the floating
|
|
1474
|
+
metabox look: the sections become flush bands, separated only by a hairline, and with no gap
|
|
1475
|
+
there's no gray screen to paint. Background goes back to being a surface. */
|
|
1476
|
+
html[data-mode] :is(aside, nav)[data-side="right"] div:has(> section[data-sorting]),
|
|
1477
|
+
html[data-mode] :is(aside, nav)[data-side="right"] div.overflow-y-auto:has(section[data-sorting]) {
|
|
1478
|
+
background: var(--wp-surface) !important;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
/* the scrollbar OVERLAYS, and the only way to do that is to not draw one at all.
|
|
1482
|
+
Measured and already noted in round 4 (line ~1880): "only shows up while scrolling" isn't CSS, it's a
|
|
1483
|
+
system preference; with "Always" on macOS any visible bar takes up layout
|
|
1484
|
+
space, and that's what showed up as a dark strip next to "Publicar". Hidden,
|
|
1485
|
+
scrolling stays fully working (wheel, trackpad, keyboard, drag), exactly as it already is on the
|
|
1486
|
+
side menu since round 3. ACCEPTED COST: the permanent hint that there's more
|
|
1487
|
+
content below disappears. */
|
|
1488
|
+
html[data-mode] :is(aside, nav)[data-side="right"] div.overflow-y-auto {
|
|
1489
|
+
scrollbar-width: none !important;
|
|
1490
|
+
-ms-overflow-style: none;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
html[data-mode] :is(aside, nav)[data-side="right"] div.overflow-y-auto::-webkit-scrollbar {
|
|
1494
|
+
display: none;
|
|
1495
|
+
width: 0;
|
|
1496
|
+
height: 0;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
/* the box */
|
|
1500
|
+
html[data-mode] section[data-sorting][data-disclosure] {
|
|
1501
|
+
/* NOT OPTIONAL: React sets `inline-size: 100%` as an INLINE style. With
|
|
1502
|
+
the 12px side margin that gives 100% + 24px inside a parent that is
|
|
1503
|
+
overflow-x-hidden: it doesn't become a scrollbar (the visual gate would slip past unnoticed), it becomes a
|
|
1504
|
+
CLIP, and every box's right border disappears. */
|
|
1505
|
+
inline-size: auto !important;
|
|
1506
|
+
/* round 5: ZERO margin. The box becomes a band: no corner, no shadow, no side border, just
|
|
1507
|
+
the top hairline separating one section from the next (EmDash already emits `border-t` with
|
|
1508
|
+
`first:border-t-0`, so the first one doesn't get a doubled hairline). The alignment with the
|
|
1509
|
+
action bar's buttons no longer comes from the margin: it comes from the `p-4` EmDash itself puts
|
|
1510
|
+
on the section's child, which is the same 16px as that bar's `px-4`. */
|
|
1511
|
+
margin: 0 !important;
|
|
1512
|
+
border: 0 !important;
|
|
1513
|
+
border-top: 1px solid var(--wp-line) !important;
|
|
1514
|
+
border-radius: 0 !important;
|
|
1515
|
+
background: var(--wp-surface) !important;
|
|
1516
|
+
box-shadow: none !important;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/* `:first-of-type` and not `:first-child`: dnd-kit's DndContext renders the
|
|
1520
|
+
HiddenText/LiveRegion pair as a sibling of the sections, with no portal. Today it comes out
|
|
1521
|
+
AFTER, so `:first-child` would work; `:first-of-type` stays correct if
|
|
1522
|
+
it ever comes out before. */
|
|
1523
|
+
html[data-mode] section[data-sorting][data-disclosure]:first-of-type {
|
|
1524
|
+
margin-top: 0 !important;
|
|
1525
|
+
border-top: 0 !important; /* the action bar above already closes with its own hairline */
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
/* header band: the title is a loose <h3> (Publicar, SEO, Taxonomias,
|
|
1529
|
+
Propriedade, Traducoes, plugin panels). Two levels because the
|
|
1530
|
+
depth changes from block to block (see note 2 above). */
|
|
1531
|
+
html[data-mode] section[data-sorting] > div > h3,
|
|
1532
|
+
html[data-mode] section[data-sorting] > div > div > h3 {
|
|
1533
|
+
display: block;
|
|
1534
|
+
margin: -16px -16px 16px !important;
|
|
1535
|
+
padding-block: 8px;
|
|
1536
|
+
/* round 5: 16px, not 12px. With the section edge to edge and its body in `p-4`, the
|
|
1537
|
+
title only lines up with the field below and with the action bar's button if it uses the
|
|
1538
|
+
same 16px. 40px still reserves room for the drag handle. */
|
|
1539
|
+
padding-inline: 16px 40px;
|
|
1540
|
+
border-bottom: 1px solid var(--wp-line);
|
|
1541
|
+
background: var(--wp-button-bg);
|
|
1542
|
+
color: var(--wp-text);
|
|
1543
|
+
font-size: 14px !important;
|
|
1544
|
+
font-weight: 400 !important;
|
|
1545
|
+
line-height: 1.4 !important;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
/* "Bylines" wraps the title in a flex row alongside a help button: there
|
|
1549
|
+
the band is the WHOLE row, and the h3 goes back to being just text.
|
|
1550
|
+
The `flex` class is the discriminator because `div:has(> h3)` without the class would also
|
|
1551
|
+
catch the Traducoes panel's root, which wraps the h3 AND the whole list:
|
|
1552
|
+
the panel would become one giant band. If EmDash removes `flex` from that row,
|
|
1553
|
+
Bylines loses the band (silent degradation, doesn't break). */
|
|
1554
|
+
html[data-mode] section[data-sorting] > div > div.flex:has(> h3) {
|
|
1555
|
+
margin: -16px -16px 16px !important;
|
|
1556
|
+
padding-block: 8px !important;
|
|
1557
|
+
padding-inline: 16px 40px !important; /* round 5: same ruler as the loose h3 */
|
|
1558
|
+
border-bottom: 1px solid var(--wp-line);
|
|
1559
|
+
background: var(--wp-button-bg);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
html[data-mode] section[data-sorting] > div > div.flex:has(> h3) > h3 {
|
|
1563
|
+
margin: 0 !important;
|
|
1564
|
+
padding: 0 !important;
|
|
1565
|
+
border: 0 !important;
|
|
1566
|
+
background: none !important;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
/* collapsible header (Sumario, Revisoes) = Collapsible.Trigger. EmDash sets
|
|
1570
|
+
`width: calc(100% - 1.5rem)` and `inset-inline-start: -0.75rem` INLINE: only
|
|
1571
|
+
!important wins. */
|
|
1572
|
+
html[data-mode] section[data-disclosure="true"] button[aria-expanded] {
|
|
1573
|
+
width: calc(100% + 32px) !important;
|
|
1574
|
+
inset-inline-start: 0 !important;
|
|
1575
|
+
margin: -16px -16px 12px !important;
|
|
1576
|
+
padding-block: 8px !important;
|
|
1577
|
+
padding-inline: 12px 40px !important;
|
|
1578
|
+
height: auto !important;
|
|
1579
|
+
min-height: 36px !important;
|
|
1580
|
+
border-radius: 0 !important;
|
|
1581
|
+
border-bottom: 1px solid var(--wp-line) !important;
|
|
1582
|
+
background: var(--wp-button-bg) !important;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
/* The label does NOT inherit the button's typography: it's a <Text bold as="span"> and
|
|
1586
|
+
kumo puts `text-base font-medium` on the span ITSELF. Inheritance doesn't beat the
|
|
1587
|
+
element's own class. With `--text-base: 13px` (line 123) and
|
|
1588
|
+
`--font-weight-medium: 500`, "Sumario" and "Revisoes" would come out 13px/500
|
|
1589
|
+
sitting next to seven 14px/400 titles, in the same column. */
|
|
1590
|
+
html[data-mode] section[data-disclosure="true"] button[aria-expanded] span {
|
|
1591
|
+
font-size: 14px !important;
|
|
1592
|
+
font-weight: 400 !important;
|
|
1593
|
+
line-height: 1.4 !important;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
html[data-mode] section[data-disclosure="true"] button[aria-expanded]:hover {
|
|
1597
|
+
background: var(--wp-accent-tint) !important;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
/* the drag handle now lives inside the band.
|
|
1601
|
+
`>` on purpose and safe: the credits editor also emits
|
|
1602
|
+
[data-sortable-handle] on the credit rows, but there the button is a GRANDCHILD of the
|
|
1603
|
+
section; the DIRECT child is only the section's own. */
|
|
1604
|
+
html[data-mode] section[data-sorting="false"] > button[data-sortable-handle] {
|
|
1605
|
+
top: 4px !important; /* round 5.2: (36.6 - 28) / 2, the same math as the other two */
|
|
1606
|
+
inset-inline-end: 6px !important;
|
|
1607
|
+
border-radius: 3px !important; /* round 5.2: same as the (i) and the "+", see block [21] */
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
/* while dragging, EmDash hides the content and shows [data-sortable-heading] */
|
|
1611
|
+
html[data-mode] section[data-sorting="true"] [data-sortable-heading] {
|
|
1612
|
+
min-height: 36px !important; /* EmDash sets 48 inline */
|
|
1613
|
+
padding-block: 8px !important;
|
|
1614
|
+
padding-inline: 12px 40px !important;
|
|
1615
|
+
background: var(--wp-button-bg);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
html[data-mode] section[data-sorting="true"] [data-sortable-heading] span {
|
|
1619
|
+
font-size: 14px !important; /* EmDash sets text-[15px] on the span */
|
|
1620
|
+
font-weight: 400 !important;
|
|
1621
|
+
line-height: 1.4 !important;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
/* the delete block, at the panel's foot, uses the same framing (round 5.1: the
|
|
1625
|
+
framing is now a band, not a box. With the sections already edge to edge, this was the
|
|
1626
|
+
last floating card in the right panel: a frame was left around the
|
|
1627
|
+
"Mover para a lixeira" button, inside another frame. Only the top hairline remains, like the sections. */
|
|
1628
|
+
html[data-mode] [data-testid="content-trash-actions"] {
|
|
1629
|
+
margin: 0 !important;
|
|
1630
|
+
border: 0 !important;
|
|
1631
|
+
border-top: 1px solid var(--wp-line) !important;
|
|
1632
|
+
border-radius: 0 !important;
|
|
1633
|
+
padding: 12px 16px !important;
|
|
1634
|
+
background: var(--wp-surface) !important;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
/* ==========================================================================
|
|
1639
|
+
[11] LISTING TABLE STATE CELL (empty, loading, error)
|
|
1640
|
+
|
|
1641
|
+
The squeeze was OURS, not EmDash's: it sets `py-8` (32px top and
|
|
1642
|
+
bottom) on those cells, and the table block's `main table tbody td { padding: 8px 10px
|
|
1643
|
+
!important }` rule (line 824) erases it. Here the breathing room comes back,
|
|
1644
|
+
and ONLY where it's a state, never on a data row.
|
|
1645
|
+
|
|
1646
|
+
ANCHOR: td[colspan] (an attribute that doesn't disappear, it's what makes the cell span the
|
|
1647
|
+
table) plus the breathing-room class. Measured in the bundle, this selector matches 13
|
|
1648
|
+
cells, ALL state rows, ZERO data rows:
|
|
1649
|
+
Comentarios (loading and empty), Artigos e colecoes (loading, empty and
|
|
1650
|
+
no-search-results), Lixeira, Tipos de conteudo, byline schema
|
|
1651
|
+
(loading, error with a Repetir button, and empty at py-12) and Usuarios.
|
|
1652
|
+
LEFT OUT, on purpose, are the other td[colspan] that are NOT a state:
|
|
1653
|
+
"Carregar mais pastas" and Media's folder error row (py-3), which are
|
|
1654
|
+
action and alert, and the two loading rows that coexist WITH data rows
|
|
1655
|
+
(py-6 in Media and py-4 in Usuarios), where a 96px band in the middle of the
|
|
1656
|
+
list would be worse than the misalignment. Accepted side effect: on those two
|
|
1657
|
+
screens there's still a small jump between "Carregando" and "Nenhum".
|
|
1658
|
+
The table the client writes in the article body is also left out with no
|
|
1659
|
+
guard needed: it's editor HTML, it carries no utility class.
|
|
1660
|
+
|
|
1661
|
+
`height` on a table cell is a MINIMUM HEIGHT, so a long state message
|
|
1662
|
+
(the "nenhum usuario" one has a button alongside) grows normally, doesn't clip.
|
|
1663
|
+
WATCH: if an upgrade swaps `py-8` for another value, the cell just goes back to
|
|
1664
|
+
being squeezed, nothing breaks, and the fix is adding the class to the :is().
|
|
1665
|
+
========================================================================== */
|
|
1666
|
+
html[data-mode] main table tbody td[colspan]:is(.py-8, .py-12) {
|
|
1667
|
+
height: 96px; /* minimum, about 3 lines */
|
|
1668
|
+
padding: 24px 12px !important;
|
|
1669
|
+
text-align: center; /* EmDash already sets text-center; this is belt and suspenders */
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
/* the state row is excluded from the zebra stripe and hover: in WP it's a clean band, not
|
|
1673
|
+
a clickable row. Specificity (0,3,6) against the (0,2,5) of the zebra and
|
|
1674
|
+
hover (lines 816 and 820), so it wins even with both at !important.
|
|
1675
|
+
A browser without :has() only loses THIS rule: the row goes back to getting the zebra stripe and the
|
|
1676
|
+
breathing room above still applies. */
|
|
1677
|
+
html[data-mode] main table tbody tr:has(> td[colspan]:is(.py-8, .py-12)),
|
|
1678
|
+
html[data-mode] main table tbody tr:has(> td[colspan]:is(.py-8, .py-12)):hover {
|
|
1679
|
+
background: var(--wp-surface) !important;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
/* ==========================================================================
|
|
1684
|
+
[12] LISTING EMDASH BUILDS WITH <div> INSTEAD OF <table>
|
|
1685
|
+
(Taxonomias/Categorias and Redirecionamentos)
|
|
1686
|
+
|
|
1687
|
+
WHY IT WAS MISSING: this container arrives as `border rounded-lg` WITHOUT `bg-kumo-base`
|
|
1688
|
+
and WITHOUT `overflow-x-auto`, so it doesn't match any of block [7]'s three
|
|
1689
|
+
card selectors. It was left with a raw 1px outline, no background and no shadow,
|
|
1690
|
+
alone among the listings (comments, for example, is
|
|
1691
|
+
`rounded-md border bg-kumo-base overflow-x-auto` and is caught by the last two).
|
|
1692
|
+
|
|
1693
|
+
ANCHOR = the header band WITH ITS BACKGROUND TINT: a direct child that has
|
|
1694
|
+
`border-b` AND `bg-kumo-tint/50`. `border-b` alone is NOT enough: measured in the bundle,
|
|
1695
|
+
`div.border.rounded-lg:has(> .border-b)` matches 17 times, and 14 are a card,
|
|
1696
|
+
modal or panel (the plugin consent dialog, the editor's image detail
|
|
1697
|
+
panel, which has TWO children like this, the type editor's fields panel,
|
|
1698
|
+
the device-authorization badge, and ten WordPress importer cards,
|
|
1699
|
+
including the two red ERROR ones, which would end up white).
|
|
1700
|
+
Worse: in Redirecionamentos every list ROW is a direct child with
|
|
1701
|
+
`border-b`, so the band's rule would repaint the whole list as a header.
|
|
1702
|
+
With `bg-kumo-tint/50` added, the pair exists on 6 divs in the bundle and only 3 are
|
|
1703
|
+
a DIRECT child of a `div.border.rounded-lg`: the taxonomy and the two redirect
|
|
1704
|
+
lists. Zero false positives.
|
|
1705
|
+
|
|
1706
|
+
`rounded-lg` is left as is on purpose: the sibling listing (comments) is
|
|
1707
|
+
`rounded-md` and isn't squared by the skin either. Squaring it only here
|
|
1708
|
+
would recreate the very divergence being fixed.
|
|
1709
|
+
========================================================================== */
|
|
1710
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50) {
|
|
1711
|
+
background: var(--wp-surface) !important;
|
|
1712
|
+
border-color: transparent !important; /* the outline comes from the shadow's first layer, like in [7] */
|
|
1713
|
+
box-shadow:
|
|
1714
|
+
0 0 0 1px var(--wp-line),
|
|
1715
|
+
0 1px 1px rgba(29, 35, 39, .03),
|
|
1716
|
+
0 32px 25px -35px rgba(29, 35, 39, .15),
|
|
1717
|
+
0 12px 28px -18px rgba(29, 35, 39, .22) !important;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50):hover {
|
|
1721
|
+
box-shadow:
|
|
1722
|
+
0 0 0 1px var(--wp-line),
|
|
1723
|
+
0 1px 2px rgba(29, 35, 39, .04),
|
|
1724
|
+
0 36px 28px -34px rgba(29, 35, 39, .18),
|
|
1725
|
+
0 20px 40px -20px rgba(29, 35, 39, .28) !important;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
/* the header band gets the same treatment as real tables' <thead>
|
|
1729
|
+
(line 804), MINUS the padding: in a <table> the th and td both get
|
|
1730
|
+
8px 10px and stay aligned; here only the band would be touched and the
|
|
1731
|
+
labels would slide 6px to the left relative to the cells, which are
|
|
1732
|
+
px-4. The `py-2 px-4` that already comes from EmDash gives the same 8px vertically. */
|
|
1733
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50) > .border-b.bg-kumo-tint\/50 {
|
|
1734
|
+
border-bottom: 1px solid var(--wp-line) !important;
|
|
1735
|
+
background: var(--wp-surface) !important;
|
|
1736
|
+
font-weight: 400 !important;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
|
|
1740
|
+
/* ==========================================================================
|
|
1741
|
+
[13] HEADER BUTTONS WITH THE SAME VISUAL WEIGHT
|
|
1742
|
+
|
|
1743
|
+
Two things measured, one about CASCADE and one about REACH.
|
|
1744
|
+
|
|
1745
|
+
(a) CASCADE. The two existing .page-title-action families (lines
|
|
1746
|
+
610 and 629) write `color: ... !important` in a sheet WITHOUT @layer, and
|
|
1747
|
+
that LOSES to kumo's !important, which lives inside
|
|
1748
|
+
`@layer utilities` (`.\!text-kumo-default{color:...!important}`, and
|
|
1749
|
+
`!text-kumo-default` is in the `secondary` variant, which is <Button>'s
|
|
1750
|
+
default). Cascade rule with layers: important INSIDE a layer
|
|
1751
|
+
beats important OUTSIDE a layer. Practical effect today, on every screen:
|
|
1752
|
+
the button already uses WordPress's background and border, but the text stays #1d2327
|
|
1753
|
+
instead of the accent blue. The path that works is going through the TOKEN, which
|
|
1754
|
+
kumo's own !important resolves. One line per family, additive.
|
|
1755
|
+
|
|
1756
|
+
(b) REACH. Neither family reaches an action living inside a GROUPING WRAPPER.
|
|
1757
|
+
In Taxonomias both buttons live inside a `div.flex.gap-2` because
|
|
1758
|
+
of the language selector, and in Media inside a `div.shrink-0`:
|
|
1759
|
+
in both, the h1 is in the block with a subtitle (kills the `h1 + div` family) and
|
|
1760
|
+
the buttons are GRANDCHILDREN (kills the `:has(> div > h1) > a` family). It's not
|
|
1761
|
+
a design decision, it's a gap between the two families: the family WITHOUT
|
|
1762
|
+
a subtitle already paints an action inside a grouping wrapper (Bylines screen,
|
|
1763
|
+
`h1 + div > a`). The block below only completes the symmetry, no new shape.
|
|
1764
|
+
`:not(:has(h1))` excludes the title block, the only other div-child
|
|
1765
|
+
of the row (checked in Tipos de conteudo, Redirecionamentos, Secoes,
|
|
1766
|
+
Widgets, Menus, byline schema, Conteudo and Comentarios: in none
|
|
1767
|
+
of them does the other div have a button).
|
|
1768
|
+
ACCEPTED CONSEQUENCE: EmDash's hierarchy on that screen (outline for
|
|
1769
|
+
"Nova taxonomia", secondary for "Adicionar termo") disappears, as it already
|
|
1770
|
+
disappears on the other nine screens where the skin dresses up the header action.
|
|
1771
|
+
WATCH: if EmDash puts a control that is NOT an action in that wrapper,
|
|
1772
|
+
it will get dressed as .page-title-action. Visual failure, doesn't break.
|
|
1773
|
+
========================================================================== */
|
|
1774
|
+
|
|
1775
|
+
/* (a) the two families that already exist: the text color goes through the token */
|
|
1776
|
+
html[data-mode] main h1 + div > a,
|
|
1777
|
+
html[data-mode] main h1 + div > button {
|
|
1778
|
+
--text-color-kumo-default: var(--wp-accent);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
html[data-mode] main > div > div:has(> div > h1) > a,
|
|
1782
|
+
html[data-mode] main > div > div:has(> div > h1) > button {
|
|
1783
|
+
--text-color-kumo-default: var(--wp-accent);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
html[data-mode] main h1 + div > a:hover,
|
|
1787
|
+
html[data-mode] main h1 + div > button:hover {
|
|
1788
|
+
--text-color-kumo-default: var(--wp-accent-deep);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
html[data-mode] main > div > div:has(> div > h1) > a:hover,
|
|
1792
|
+
html[data-mode] main > div > div:has(> div > h1) > button:hover {
|
|
1793
|
+
--text-color-kumo-default: var(--wp-accent-deep);
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
/* (b) the missing family: action inside the grouping wrapper, on the row WITH a subtitle.
|
|
1797
|
+
Same declarations as the other two, on purpose: in the header WordPress
|
|
1798
|
+
dresses every action as .page-title-action.
|
|
1799
|
+
Contrast checked: #2271b1 on #f6f7f7 = 4.80:1, passes AA for normal
|
|
1800
|
+
13px text, and it's the same pair the panel already uses on the other nine screens.
|
|
1801
|
+
Touch target: 30px tall, above the gate's 24px. */
|
|
1802
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > a,
|
|
1803
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > button {
|
|
1804
|
+
--text-color-kumo-default: var(--wp-accent);
|
|
1805
|
+
height: 30px !important;
|
|
1806
|
+
min-height: 0 !important;
|
|
1807
|
+
padding: 0 10px !important;
|
|
1808
|
+
gap: 4px !important;
|
|
1809
|
+
border: 1px solid var(--wp-accent) !important;
|
|
1810
|
+
border-radius: 3px !important;
|
|
1811
|
+
background: var(--wp-button-bg) !important;
|
|
1812
|
+
color: var(--wp-accent) !important;
|
|
1813
|
+
font-size: 13px !important;
|
|
1814
|
+
font-weight: 400 !important;
|
|
1815
|
+
line-height: 28px !important;
|
|
1816
|
+
box-shadow: 0 1px 1px rgba(29, 35, 39, .04) !important;
|
|
1817
|
+
text-decoration: none !important;
|
|
1818
|
+
-webkit-transition: background-color var(--wp-fast) ease, border-color var(--wp-fast) ease, color var(--wp-fast) ease, box-shadow var(--wp-fast) ease, -webkit-transform var(--wp-fast) ease;
|
|
1819
|
+
transition: background-color var(--wp-fast) ease, border-color var(--wp-fast) ease, color var(--wp-fast) ease, box-shadow var(--wp-fast) ease, transform var(--wp-fast) ease;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > a:hover,
|
|
1823
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > button:hover {
|
|
1824
|
+
--text-color-kumo-default: var(--wp-accent-deep);
|
|
1825
|
+
background: var(--wp-surface) !important;
|
|
1826
|
+
border-color: var(--wp-accent-deep) !important;
|
|
1827
|
+
color: var(--wp-accent-deep) !important;
|
|
1828
|
+
box-shadow: 0 2px 6px -2px rgba(29, 35, 39, .18) !important;
|
|
1829
|
+
-webkit-transform: translateY(-1px);
|
|
1830
|
+
transform: translateY(-1px);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > a:active,
|
|
1834
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > button:active {
|
|
1835
|
+
-webkit-transform: translateY(0);
|
|
1836
|
+
transform: translateY(0);
|
|
1837
|
+
box-shadow: none !important;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
/* the box-shadow above is !important and kumo's ring IS box-shadow: without this
|
|
1841
|
+
rule the focus ring dies. Same FIX 7 already applied to the other two
|
|
1842
|
+
families (lines 694 and 700). */
|
|
1843
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > a:focus-visible,
|
|
1844
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > button:focus-visible {
|
|
1845
|
+
outline: 2px solid var(--wp-accent-deep) !important;
|
|
1846
|
+
outline-offset: 2px !important;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > a svg,
|
|
1850
|
+
html[data-mode] main > div > div:has(> div > h1) > div:not(:has(h1)) > button svg {
|
|
1851
|
+
width: 14px !important;
|
|
1852
|
+
height: 14px !important;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
/* ==========================================================================
|
|
1856
|
+
[4B] "RECOLHER MENU" BUTTON: arrow in a circle, flipping with the state
|
|
1857
|
+
kumo already puts an icon there: Sidebar.PanelIcon, a panel rectangle
|
|
1858
|
+
with an inner line that slides (viewBox 24, stroke 1.5). It reads as
|
|
1859
|
+
"panel", not as "collapse", and it gets lost among the menu icons.
|
|
1860
|
+
Swapped for Phosphor 2.1.10's ArrowCircle, which is already installed: same
|
|
1861
|
+
family as the other items, same 256 grid, same weight trick
|
|
1862
|
+
(stroke 8 = the family's bold) that section [4] applies to the rest of the menu.
|
|
1863
|
+
State hook = the button's OWN aria-expanded, not the wrapper's data-state:
|
|
1864
|
+
kumo has a third state ("peeking") that breaks a rule pinned to
|
|
1865
|
+
[data-state="collapsed"].
|
|
1866
|
+
mask-image + currentColor (not background-image): the icon inherits the theme's
|
|
1867
|
+
color and gets the blue hover for free, with a single SVG per direction.
|
|
1868
|
+
========================================================================== */
|
|
1869
|
+
|
|
1870
|
+
/* kumo's panel icon is removed, only in the footer (the header trigger stays as is) */
|
|
1871
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"] > svg {
|
|
1872
|
+
display: none !important;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
/* expanded: arrow points LEFT (about to shrink) */
|
|
1876
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"]::before {
|
|
1877
|
+
content: "";
|
|
1878
|
+
flex: 0 0 var(--wp-icon);
|
|
1879
|
+
width: var(--wp-icon);
|
|
1880
|
+
height: var(--wp-icon);
|
|
1881
|
+
background-color: currentColor;
|
|
1882
|
+
-webkit-mask-repeat: no-repeat;
|
|
1883
|
+
mask-repeat: no-repeat;
|
|
1884
|
+
-webkit-mask-position: center;
|
|
1885
|
+
mask-position: center;
|
|
1886
|
+
-webkit-mask-size: contain;
|
|
1887
|
+
mask-size: contain;
|
|
1888
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='%23000'%20stroke='%23000'%20stroke-width='8'%20stroke-linejoin='round'%20stroke-linecap='round'%20paint-order='stroke'%3E%3Cpath%20d='M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm48-88a8,8,0,0,1-8,8H107.31l18.35,18.34a8,8,0,0,1-11.32,11.32l-32-32a8,8,0,0,1,0-11.32l32-32a8,8,0,0,1,11.32,11.32L107.31,120H168A8,8,0,0,1,176,128Z'/%3E%3C/svg%3E");
|
|
1889
|
+
mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='%23000'%20stroke='%23000'%20stroke-width='8'%20stroke-linejoin='round'%20stroke-linecap='round'%20paint-order='stroke'%3E%3Cpath%20d='M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm48-88a8,8,0,0,1-8,8H107.31l18.35,18.34a8,8,0,0,1-11.32,11.32l-32-32a8,8,0,0,1,0-11.32l32-32a8,8,0,0,1,11.32,11.32L107.31,120H168A8,8,0,0,1,176,128Z'/%3E%3C/svg%3E");
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
/* collapsed: arrow points RIGHT (about to expand) */
|
|
1893
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"][aria-expanded="false"]::before {
|
|
1894
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='%23000'%20stroke='%23000'%20stroke-width='8'%20stroke-linejoin='round'%20stroke-linecap='round'%20paint-order='stroke'%3E%3Cpath%20d='M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm45.66-93.66a8,8,0,0,1,0,11.32l-32,32a8,8,0,0,1-11.32-11.32L148.69,136H88a8,8,0,0,1,0-16h60.69l-18.35-18.34a8,8,0,0,1,11.32-11.32Z'/%3E%3C/svg%3E");
|
|
1895
|
+
mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20256%20256'%20fill='%23000'%20stroke='%23000'%20stroke-width='8'%20stroke-linejoin='round'%20stroke-linecap='round'%20paint-order='stroke'%3E%3Cpath%20d='M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm45.66-93.66a8,8,0,0,1,0,11.32l-32,32a8,8,0,0,1-11.32-11.32L148.69,136H88a8,8,0,0,1,0-16h60.69l-18.35-18.34a8,8,0,0,1,11.32-11.32Z'/%3E%3C/svg%3E");
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
/* ==========================================================================
|
|
1899
|
+
ROUND 3 OF ADJUSTMENTS (2026-09-07)
|
|
1900
|
+
Paste at the END of public/painel-wp.css, after block [4B].
|
|
1901
|
+
|
|
1902
|
+
Everything here was measured in the bundle, the source maps and the running panel
|
|
1903
|
+
(@emdash-cms/admin 0.36, @cloudflare/kumo 2.6.0, embedded Base UI), and each
|
|
1904
|
+
item went through a skeptic who refuted the proposal before it became a rule.
|
|
1905
|
+
|
|
1906
|
+
THREE LAWS THAT APPLY TO THE WHOLE BLOCK:
|
|
1907
|
+
1. NO @layer, on purpose. EmDash's [data-theme=classic]:not([data-mode=dark])
|
|
1908
|
+
block is UNLAYERED (measured with postcss: zero ancestors, 28 declarations,
|
|
1909
|
+
offset 199597 in styles.css), and in the cascade a rule with no layer beats ANY
|
|
1910
|
+
layer. Emitting these rules inside @layer base would make all of them silently
|
|
1911
|
+
lose to classic: no error, no console, just the wrong color.
|
|
1912
|
+
2. html[data-mode] prefix on everything. The admin's ThemeProvider writes
|
|
1913
|
+
root.setAttribute("data-theme","classic") and ("data-mode", resolvedTheme) in the
|
|
1914
|
+
SAME useEffect (dist/index.js:95-97), so [data-mode] always exists
|
|
1915
|
+
whenever classic applies. html:root[data-mode] = (0,2,1) beats classic
|
|
1916
|
+
(0,2,0). Where the target is a TOKEN, the selector is html:root[data-mode] and not
|
|
1917
|
+
html[data-mode]: (0,1,1) would lose to block [0] on line 20 and the rule
|
|
1918
|
+
would become dead letter.
|
|
1919
|
+
3. Whoever positions a popup is Floating UI, via JS, in the Positioner. No rule
|
|
1920
|
+
below writes transform, position, inset or overflow on a popup. The only
|
|
1921
|
+
displacement used is `translate`, a property independent of `transform`.
|
|
1922
|
+
|
|
1923
|
+
WHAT DIDN'T MAKE IT INTO THIS ROUND, and why (so nobody re-proposes it):
|
|
1924
|
+
. Route B for the collapsed label (the <span> itself becoming a tab via position:fixed):
|
|
1925
|
+
measured, the tab doesn't follow the menu's scroll, a 1-to-1 error against the
|
|
1926
|
+
displacement, and the menu really does scroll here (scrollHeight 1030 against
|
|
1927
|
+
clientHeight 773). Route A stays, where Floating UI keeps owning the
|
|
1928
|
+
position. It would only be worth revisiting if it ever relies on anchor positioning.
|
|
1929
|
+
. `mask-image: none` on the menu's scroll viewport: the only measured reason to
|
|
1930
|
+
kill it was to free up route B. Without route B, the fade at the ends becomes the
|
|
1931
|
+
ONLY hint that the menu scrolls, because the track is already hidden on lines
|
|
1932
|
+
349-357. Left as is.
|
|
1933
|
+
. Removing the `translateY(-1px)` from the panel card's hover (lines 895-898) and
|
|
1934
|
+
from the header buttons (656, 666, 1676): it's the owner's preference about
|
|
1935
|
+
motion in the CONTENT, and this round he asked for MORE interpolation in
|
|
1936
|
+
the content. It needs explicit sign-off, and if it goes in it has to go into all four
|
|
1937
|
+
spots, otherwise the card stops rising and the button keeps rising.
|
|
1938
|
+
. JS-based menu resize handle: kumo's `isResizing` is unreachable from outside,
|
|
1939
|
+
so the resize would have a 250ms transition fighting !important on every
|
|
1940
|
+
frame. The wp-admin being imitated has no resizable menu: it has a fixed width and
|
|
1941
|
+
a fold button, which the panel already has.
|
|
1942
|
+
========================================================================== */
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
/* ==========================================================================
|
|
1946
|
+
[R3] WHAT GOES OUT OF THE CURRENT FILE
|
|
1947
|
+
|
|
1948
|
+
The new rules below win by ORDER (same selector, same specificity,
|
|
1949
|
+
declared later), so the panel is already correct even if the removals
|
|
1950
|
+
aren't done. TWO exceptions are MANDATORY, marked with [!]: they erase
|
|
1951
|
+
`padding: 0 !important` in rules of HIGHER specificity, and without the removal
|
|
1952
|
+
the collapsed menu's icon touches the border instead of stopping at 8px.
|
|
1953
|
+
|
|
1954
|
+
R3.1 Content scrollbar. Delete lines 1032 to 1046 entirely:
|
|
1955
|
+
|
|
1956
|
+
html[data-mode] main {
|
|
1957
|
+
scrollbar-width: thin;
|
|
1958
|
+
scrollbar-color: #a7aaad var(--wp-canvas);
|
|
1959
|
+
}
|
|
1960
|
+
html[data-mode] main::-webkit-scrollbar { width: 12px; }
|
|
1961
|
+
html[data-mode] main::-webkit-scrollbar-thumb {
|
|
1962
|
+
background: #a7aaad;
|
|
1963
|
+
border: 3px solid var(--wp-canvas);
|
|
1964
|
+
border-radius: 8px;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
Replaced by the live rules right below, in this same section.
|
|
1968
|
+
|
|
1969
|
+
R3.2 [!] Collapsed menu, item centering. On lines 423 to 432, delete
|
|
1970
|
+
the TWO marked declarations:
|
|
1971
|
+
|
|
1972
|
+
... [data-sidebar="menu-button"] {
|
|
1973
|
+
justify-content: center !important; <- becomes flex-start in [17c]
|
|
1974
|
+
padding: 0 !important; <- DELETE, no replacement
|
|
1975
|
+
}
|
|
1976
|
+
... [data-sidebar="menu-button"] > div {
|
|
1977
|
+
flex: 0 0 auto !important;
|
|
1978
|
+
justify-content: center !important; <- becomes flex-start in [17c]
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
The collapsed item's padding has to keep coming from line 372, which
|
|
1982
|
+
applies in BOTH states: that's exactly what stops the icon from moving on
|
|
1983
|
+
collapse. Redeclaring 8px here, in a more specific rule, is what would make
|
|
1984
|
+
the two states silently diverge on the next upgrade.
|
|
1985
|
+
|
|
1986
|
+
R3.3 [!] Collapsed "Recolher menu" button. On lines 524 to 527, delete only the
|
|
1987
|
+
`padding: 0 !important;` line (the justify-content becomes flex-start in
|
|
1988
|
+
[17c]). Its breathing room now comes from line 500, which also applies to
|
|
1989
|
+
both states.
|
|
1990
|
+
|
|
1991
|
+
R3.4 Icon displacement on hover. Two edits in block [4]:
|
|
1992
|
+
. lines 555 and 556: remove the `transform var(--wp-normal)
|
|
1993
|
+
var(--wp-ease-out)` half from the list, leaving only `color var(--wp-fast) ease`.
|
|
1994
|
+
. lines 569 and 570: delete both transform lines:
|
|
1995
|
+
-webkit-transform: translateX(1px);
|
|
1996
|
+
transform: translateX(1px);
|
|
1997
|
+
The movement was OURS, not kumo's: on its side there's only
|
|
1998
|
+
`group-hover/menu-button:opacity-100` on the chevron, pure opacity.
|
|
1999
|
+
|
|
2000
|
+
R3.5 Breathing room on the item's right. Line 372, swap
|
|
2001
|
+
padding: 0 8px !important;
|
|
2002
|
+
for
|
|
2003
|
+
padding: 0 16px 0 8px !important;
|
|
2004
|
+
(rule [17d] does the same on top, for anyone who'd rather just append).
|
|
2005
|
+
THE MENU WIDTH STAYS AT 230px. The owner asked for a NARROWER bar this
|
|
2006
|
+
round, so the +10px compensation on line 45 is left out: the label
|
|
2007
|
+
drops from 186px to 178px, about one character. If truncation ever
|
|
2008
|
+
becomes a problem, the fix is ONE edit on line 45 (230px to 240px), never
|
|
2009
|
+
a second declaration of the token further down here, which would silently
|
|
2010
|
+
win over whoever edited section [0].
|
|
2011
|
+
|
|
2012
|
+
R3.6 User menu shadow. Line 1183, inside block [8]:
|
|
2013
|
+
box-shadow: 0 3px 5px rgba(0, 0, 0, .2) !important;
|
|
2014
|
+
replaced by [16b]'s four layers.
|
|
2015
|
+
|
|
2016
|
+
R3.7 User menu displacement and corner. Lines 1200 to 1206 (the two
|
|
2017
|
+
current [8b] rules): replaced by [16a]'s pair, which adds the X axis and
|
|
2018
|
+
the corners. It has to be the WHOLE pair: `translate` is a single property,
|
|
2019
|
+
so writing the X axis in one rule and the Y in another would erase the first.
|
|
2020
|
+
========================================================================== */
|
|
2021
|
+
|
|
2022
|
+
/* R3.1, live part: the content loses the track, not the scrolling.
|
|
2023
|
+
Wheel, trackpad, keyboard and touch drag keep working: only the
|
|
2024
|
+
artwork disappears. ACCEPTED COST, and it's real: the permanent hint that there's more
|
|
2025
|
+
content below disappears, and so does the DRAG target, which is the only scrolling path
|
|
2026
|
+
for someone using a mouse with no wheel. No WCAG criterion requires a visible bar, and the
|
|
2027
|
+
<main> already wasn't keyboard-focusable in Safari before this rule (that's
|
|
2028
|
+
pre-existing, doesn't originate here).
|
|
2029
|
+
MEASURED SIDE EFFECT: `scrollbar-color` IS INHERITED, so line 1035
|
|
2030
|
+
used to tint, by inheritance, every scrollable area INSIDE main (overflow-x-auto table,
|
|
2031
|
+
code block, textarea). Without it, those go back to the browser's default gray,
|
|
2032
|
+
which is similar to the previous #a7aaad.
|
|
2033
|
+
The content widens by 12px, because the 12px ::-webkit-scrollbar turned
|
|
2034
|
+
macOS's overlay bar into a classic bar, which took up space. */
|
|
2035
|
+
/* round 4: GLOBAL RULE. Thin (11px) and discreet across every scrollable area of the panel, except the
|
|
2036
|
+
side bar (hidden on line 349). No ::-webkit-scrollbar anywhere: it's the one
|
|
2037
|
+
that kills macOS's overlay bar. "Only shows up while scrolling" is NOT CSS: it's the
|
|
2038
|
+
system preference (Appearance > Show scroll bars > When scrolling); with it set to
|
|
2039
|
+
"Always", any bar takes up space. Color #787c82 = 3.68:1 on the canvas, passes AA. */
|
|
2040
|
+
html[data-mode] {
|
|
2041
|
+
scrollbar-color: #787c82 transparent;
|
|
2042
|
+
}
|
|
2043
|
+
html[data-mode] main,
|
|
2044
|
+
html[data-mode] main *:not(:where(.no-scrollbar, .kumo-tabs-list)),
|
|
2045
|
+
html[data-mode] :is(aside, nav)[data-side="right"] div.overflow-y-auto {
|
|
2046
|
+
scrollbar-width: thin;
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
/* kumo still draws its OWN bar inside the menu, which isn't the native one: it's
|
|
2050
|
+
a Base UI ScrollArea.Scrollbar (sidebar.tsx:804-813), `w-1.5 opacity-0`
|
|
2051
|
+
that rises to 1 on data-[scrolling] and data-[hovering]. Lines 349 to 357
|
|
2052
|
+
hide only the native one, so it used to reappear when hovering over the menu.
|
|
2053
|
+
ANCHOR: Base UI converts every unmapped state into data-<key>, and
|
|
2054
|
+
`orientation` isn't in scrollAreaStateAttributesMapping, so the Scrollbar
|
|
2055
|
+
comes out with data-orientation="vertical" and the Viewport doesn't (its state doesn't have
|
|
2056
|
+
that key). No :nth-child.
|
|
2057
|
+
Hiding it does NOT unmount or lock the component: Base UI's `shouldRender` reads
|
|
2058
|
+
getHiddenState(viewportEl), from the VIEWPORT, not the bar, and its
|
|
2059
|
+
getBoundingClientRect reads only run on onPointerDown/onPointerMove, which don't
|
|
2060
|
+
fire on an element with display none. The menu's fade mask stays
|
|
2061
|
+
alive, because it's fed by variables written on the Viewport. */
|
|
2062
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="content"] > [data-orientation] {
|
|
2063
|
+
display: none !important;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
/* --------------------------------------------------------------------------
|
|
2067
|
+
R3.8 THE THEME TOGGLE COMES OUT OF THE BAR
|
|
2068
|
+
|
|
2069
|
+
The panel has no real dark mode and never will: wp-admin doesn't have one,
|
|
2070
|
+
EmDash's classic theme doesn't either (measured, the string "data-theme=classic"
|
|
2071
|
+
appears ONCE in the entire styles.css, inside
|
|
2072
|
+
[data-theme=classic]:not([data-mode=dark]), with 28 tokens for light and ZERO for
|
|
2073
|
+
dark), and line 26 of this sheet already forces `color-scheme: light`, which
|
|
2074
|
+
neutralizes kumo's 130 light-dark() calls at once. Keeping the button means keeping a
|
|
2075
|
+
control that promises one thing and delivers another, whose only real effect today
|
|
2076
|
+
is silently degrading 14 tokens (see R3.9).
|
|
2077
|
+
|
|
2078
|
+
DOUBLE ANCHOR, on purpose. The positional one alone (a[href][target] + button) would be
|
|
2079
|
+
dangerous: if an EmDash bump removes or moves the ThemeToggle, it would start matching
|
|
2080
|
+
the NEXT BUTTON, which is the user menu (Sair, Configuracoes). A client
|
|
2081
|
+
with no logout button is far worse than a client with a useless theme button. The
|
|
2082
|
+
data-base-ui-tooltip-trigger is stamped by Base UI on the Tooltip's trigger, and
|
|
2083
|
+
ThemeToggle is the only header control with a `title` prop (kumo's Button
|
|
2084
|
+
only wraps in a Tooltip when there's a title): the user menu and the Sidebar.Trigger
|
|
2085
|
+
have no title, so they never match. With both requirements together, an
|
|
2086
|
+
upgrade's failure becomes "the sun reappears", visible right away, and never "the wrong
|
|
2087
|
+
button disappeared".
|
|
2088
|
+
UPGRADE WATCH: if the button comes back, check whether EmDash removed the `title`
|
|
2089
|
+
(lost the tooltip) or reordered the header.
|
|
2090
|
+
The rule on line 231 (32px width on that same button) stays inert, and doesn't
|
|
2091
|
+
get in the way: if EmDash puts another button with a tooltip in the bar, it still
|
|
2092
|
+
gives it the right width.
|
|
2093
|
+
-------------------------------------------------------------------------- */
|
|
2094
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > div > header
|
|
2095
|
+
a[href][target] + button[data-base-ui-tooltip-trigger] {
|
|
2096
|
+
display: none !important;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
/* --------------------------------------------------------------------------
|
|
2100
|
+
R3.9 THE 14 TOKENS THAT EVAPORATE WHEN data-mode=dark
|
|
2101
|
+
|
|
2102
|
+
EmDash's block is [data-theme=classic]:not([data-mode=dark]). When
|
|
2103
|
+
data-mode=dark enters the <html>, the whole block stops matching and its 28 tokens
|
|
2104
|
+
fall back to kumo's `:root,:host` light-dark() (it falls there, not to
|
|
2105
|
+
:root[data-mode=dark], because :root,:host is UNLAYERED and the dark block lives
|
|
2106
|
+
inside @layer base: a layer loses to what has no layer).
|
|
2107
|
+
|
|
2108
|
+
And data-mode=dark IS REACHABLE TODAY, with nobody clicking anything: the
|
|
2109
|
+
ThemeProvider uses defaultTheme "system" and reads prefers-color-scheme, and
|
|
2110
|
+
admin.astro has a BLOCKING is:inline script in the head that stamps data-mode
|
|
2111
|
+
before this sheet's <link>. It only takes the client's system being in dark mode.
|
|
2112
|
+
|
|
2113
|
+
Of those 28, the skin already redeclares 18. The 14 below were inherited, and in
|
|
2114
|
+
dark mode they'd change value on their own, leaving WordPress's palette for Tailwind's
|
|
2115
|
+
light ramp (line 26's `color-scheme: light` forces light-dark's light branch,
|
|
2116
|
+
so it doesn't go dark: it goes off-brand).
|
|
2117
|
+
THE MOST IMPORTANT ONE IS HOVER: in wp-admin the fill DARKENS from #dcdcde to
|
|
2118
|
+
#c3c4c7; without this, in dark mode it LIGHTENS (oklch(0.922) to oklch(0.965)) and the
|
|
2119
|
+
button gets lighter on hover, the opposite of the rest of the
|
|
2120
|
+
panel.
|
|
2121
|
+
|
|
2122
|
+
Values copied literally from the classic block, checked one by one: in light mode
|
|
2123
|
+
nothing changes (it's additive), in dark mode the panel now looks the same as light.
|
|
2124
|
+
Specificity (0,2,1) beats classic (0,2,0) and loses to the skin's scoped
|
|
2125
|
+
blocks (0,3,3) for the menu, the bar and the popover, which is exactly what's
|
|
2126
|
+
wanted. None of these 14 has an !important declaration inside @layer utilities
|
|
2127
|
+
(zero occurrences in the bundle), so the unlayered declaration truly wins.
|
|
2128
|
+
Measured accessibility gain: with the fallback, info text #2271b1 over Tailwind's
|
|
2129
|
+
blue-100 gave 4.24:1 and failed AA; fixing info-tint to
|
|
2130
|
+
#e7f4fe raises that same text to 4.62:1 and it passes, without touching text color.
|
|
2131
|
+
REGRESSION TO WATCH ON EVERY EMDASH BUMP: if classic changes, these 14 don't
|
|
2132
|
+
change with it (that's the point), but the other 18 remain a valid
|
|
2133
|
+
comparison against the new styles.css.
|
|
2134
|
+
-------------------------------------------------------------------------- */
|
|
2135
|
+
html:root[data-mode] {
|
|
2136
|
+
--color-kumo-fill: #dcdcde;
|
|
2137
|
+
--color-kumo-fill-hover: #c3c4c7; /* hover DARKENS, like in wp-admin */
|
|
2138
|
+
--color-kumo-contrast: #1d2327;
|
|
2139
|
+
--color-kumo-interact: #c3c4c7;
|
|
2140
|
+
--text-color-kumo-strong: #50575e;
|
|
2141
|
+
--text-color-kumo-inactive: #a7aaad;
|
|
2142
|
+
--color-kumo-info: #72aee6;
|
|
2143
|
+
--color-kumo-info-tint: #e7f4fe;
|
|
2144
|
+
--color-kumo-success: #00a32a;
|
|
2145
|
+
--color-kumo-success-tint: #edfaef;
|
|
2146
|
+
--color-kumo-warning: #dba617;
|
|
2147
|
+
--color-kumo-warning-tint: #fcf9e8;
|
|
2148
|
+
--color-kumo-danger: #d63638;
|
|
2149
|
+
--color-kumo-danger-tint: #fcf0f1;
|
|
2150
|
+
}
|
|
2151
|
+
/* note: these 14 are status BACKGROUND. The 4 status TEXT tokens keep
|
|
2152
|
+
our own AA values from lines 30 to 33 (#007017, #b32d2e, #996800, #2271b1),
|
|
2153
|
+
deliberately different from classic. Do not overwrite.
|
|
2154
|
+
Measured contrast of the resulting pairs: 5.86:1 on success, 5.67:1 on danger,
|
|
2155
|
+
4.58:1 on warning, 4.62:1 on info. All pass AA. */
|
|
2156
|
+
|
|
2157
|
+
|
|
2158
|
+
/* ==========================================================================
|
|
2159
|
+
[14] EMPTY-STATE ACTION ON THE BLOCK AXIS
|
|
2160
|
+
|
|
2161
|
+
CAUSE MEASURED IN THE BUNDLE, not from a screenshot. kumo's <Button> is born with
|
|
2162
|
+
`group flex w-max shrink-0 items-center` (buttonVariants, button.tsx:134), which
|
|
2163
|
+
styles.css compiles to display:flex plus width:max-content: a BLOCK BOX
|
|
2164
|
+
with a shrunk width. The empty-state card is `border rounded-lg p-12
|
|
2165
|
+
text-center` (MenuList.tsx:187), and text-align centers text and icon in
|
|
2166
|
+
line, but does NOT shift a block box: it stays flush against the start border.
|
|
2167
|
+
MEASURED IN PRODUCTION (/_emdash/admin/menus): the "Criar menu" button sits 49px from
|
|
2168
|
+
the start border and 1290px from the end. With this rule: 669.5px on both sides,
|
|
2169
|
+
and the horizontal-scroll gate stays clean.
|
|
2170
|
+
justify-content on the parent doesn't help, because the parent isn't flex: what centers
|
|
2171
|
+
a shrunk block box is the AUTO MARGIN.
|
|
2172
|
+
|
|
2173
|
+
ANCHOR: `data-kumo-component` is an attribute kumo writes by hand on the
|
|
2174
|
+
element itself (button.tsx:322 for Button, 406 for LinkButton), so it survives
|
|
2175
|
+
a utility swap in an upgrade. The third line covers EmDash's RouterLinkButton,
|
|
2176
|
+
which builds the same look by calling buttonVariants but does NOT emit the
|
|
2177
|
+
attribute. `w-max` only exists in buttonVariants (the only occurrence in kumo/src, and
|
|
2178
|
+
the admin only uses `min-w-max`), so `a.flex.w-max` doesn't reach a regular link.
|
|
2179
|
+
WATCH: `.text-center` is a utility, not a contract. If EmDash swaps
|
|
2180
|
+
these blocks for its own EmptyState component, the rule stops matching and the
|
|
2181
|
+
button goes back to the left (visual failure, never breaks).
|
|
2182
|
+
|
|
2183
|
+
MEASURED INVENTORY (every Button/LinkButton/RouterLinkButton in the admin, with the parent
|
|
2184
|
+
resolved by indentation). 16 cases, none gets worse:
|
|
2185
|
+
CHANGE (12, all empty-state, error, or "load more" actions):
|
|
2186
|
+
MenuList:187 (Menus), Sections:255 (Secoes), ContentTypeEditor:625,
|
|
2187
|
+
RepeaterField:164 and PortableTextEditor:2226 (repeater),
|
|
2188
|
+
MarketplaceBrowse:131 and ThemeMarketplaceBrowse:110 (marketplace errors),
|
|
2189
|
+
DeviceAuthorizePage:196, ContentPickerModal:216, MediaLibrary:1244 (<td>),
|
|
2190
|
+
byline-schema:267 (the same "Repetir" block [11] cites, <td>) and
|
|
2191
|
+
router:2524 (the app's error screen).
|
|
2192
|
+
MATCH AND CHANGE NOTHING (4, verified in the browser): LoginPage:117, which arrives
|
|
2193
|
+
with `w-full` (twMerge erases the w-max, width 100%, margin resolves to
|
|
2194
|
+
zero) and MediaPickerModal:687, BylineCreditsEditor:468 and
|
|
2195
|
+
MediaUploadDialog:421, which are `flex flex-col items-center` and were already
|
|
2196
|
+
centered.
|
|
2197
|
+
DON'T MATCH, checked one by one: an action inside a grouping wrapper (not a direct child), the
|
|
2198
|
+
<span> with buttonVariants inside a <label> on the importer's two dropzones,
|
|
2199
|
+
the `inline-block` link on the plugin detail error, animate-pulse skeleton
|
|
2200
|
+
(none has text-center), kumo's dropdown menu, popover, toast and pagination (the
|
|
2201
|
+
7 text-center in kumo/src have no button inside), and the sidebar, the header
|
|
2202
|
+
and the shell (zero text-center).
|
|
2203
|
+
The only `.text-center` that is a ROW flex across the whole admin
|
|
2204
|
+
(BylineCreditsEditor:540) has no button inside. WATCH: if an upgrade puts
|
|
2205
|
+
a button there, the auto margin pushes the siblings to the edges. That's the only
|
|
2206
|
+
way this rule could misalign something.
|
|
2207
|
+
|
|
2208
|
+
NOT SCOPED TO `main` ON PURPOSE: this way it also applies in the content-picker
|
|
2209
|
+
modal, the device authorization screen and the app's error screen, which live
|
|
2210
|
+
outside <main>. `!important` isn't needed: an unlayered declaration beats a
|
|
2211
|
+
utility inside @layer utilities (`.mx-auto` lives there) and none of these
|
|
2212
|
+
buttons carries mx-*. `margin-inline` is logical, so it follows RTL.
|
|
2213
|
+
========================================================================== */
|
|
2214
|
+
html[data-mode] .text-center > :is([data-kumo-component="Button"],
|
|
2215
|
+
[data-kumo-component="LinkButton"],
|
|
2216
|
+
a.flex.w-max) {
|
|
2217
|
+
margin-inline: auto;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
|
|
2221
|
+
/* ==========================================================================
|
|
2222
|
+
[15a] WHAT'S BORN SQUARE STAYS SQUARE
|
|
2223
|
+
|
|
2224
|
+
MEASURED CAUSE: the badge on the Tipos de conteudo row is
|
|
2225
|
+
`flex h-8 w-8 items-center justify-center rounded-lg` (ContentTypeList.tsx:290)
|
|
2226
|
+
inside `div.flex.items-center.space-x-3`, WITHOUT shrink-0. A flex item without
|
|
2227
|
+
shrink-0 doesn't stop at its declared width: its automatic minimum size is the
|
|
2228
|
+
SMALLER of the specified width (32px) and the content's minimum, which here is the
|
|
2229
|
+
16px svg. The box drops from 32 to 16 and the height stays at h-8's 32. Since the
|
|
2230
|
+
text sibling has neither min-w-0 nor truncate, the badge is always the one that yields, and only
|
|
2231
|
+
on the ROW with the longest text: hence two sizes in the same column.
|
|
2232
|
+
|
|
2233
|
+
MEASURED ON THE RUNNING PANEL (Chrome via CDP, route /content-types): at 320px and
|
|
2234
|
+
390px six badges shrink to 16x32 (one of them 16.42x32) and at 1440px one shrinks to
|
|
2235
|
+
22.13x32 while the others stay 32x32. With this rule all six go to 32x32.
|
|
2236
|
+
Across the eight measured routes (root, Tipos de conteudo, Usuarios, Midia, Ajustes,
|
|
2237
|
+
Conteudo, Comentarios, Menus), at 320, 390 and 1440, and with the user popover
|
|
2238
|
+
OPEN, no other element changed size, the document's and main's scrollWidth stayed
|
|
2239
|
+
the same and the popup's transform/position stayed intact.
|
|
2240
|
+
|
|
2241
|
+
CENSUS IN THE SHIPPED CODE: the admin bundle has 498 class strings with a square
|
|
2242
|
+
pair (h-N together with w-N, or size-N), 42 with shrink-0 or flex-none and 456 without;
|
|
2243
|
+
kumo adds 35, 7 of them with shrink-0.
|
|
2244
|
+
|
|
2245
|
+
WHY IT DOESN'T CATCH WHAT SHOULD STRETCH, in four safeguards:
|
|
2246
|
+
1. `flex: 0 0 auto` only has an effect on a FLEX ITEM. On any other element the
|
|
2247
|
+
property is ignored: Media's thumbnail, which today is a block inside
|
|
2248
|
+
<td>, doesn't move a pixel, and the same goes for anything in position absolute.
|
|
2249
|
+
2. `aspect-ratio` only kicks in when ONE of the two dimensions is automatic. Since the selector
|
|
2250
|
+
requires the PAIR, these elements declare both width AND height, so the line is
|
|
2251
|
+
a no-op today: it's the safety net for the day an upgrade removes one of the two.
|
|
2252
|
+
Checked: of the 100 squares that are flex items on the swept routes, the 94
|
|
2253
|
+
healthy ones didn't move a pixel.
|
|
2254
|
+
3. An element meant to grow never declares a fixed width: in the admin and in kumo,
|
|
2255
|
+
ZERO occurrences combine a square pair with grow, flex-1, flex-auto, basis-*,
|
|
2256
|
+
w-full or h-full. The :not()s come for free, as an upgrade guarantee.
|
|
2257
|
+
4. Scoped to main and to the Base UI portals. The side menu is left out on
|
|
2258
|
+
purpose (its icons already have their own geometry in section [4], `flex: 0 0
|
|
2259
|
+
var(--wp-icon)`, and two rules fighting over the same element is the start of the
|
|
2260
|
+
next pitfall). The top bar is also left out, because the Header
|
|
2261
|
+
lives OUTSIDE <main> (Shell.tsx:105-116): its avatar isn't reached, and
|
|
2262
|
+
today it doesn't need to be, since the button wrapping it is w-max.
|
|
2263
|
+
|
|
2264
|
+
CASCADE: styles.css doesn't put !important on any utility involved (the
|
|
2265
|
+
file's only !important is in @layer properties), so this unlayered sheet
|
|
2266
|
+
wins. The measurement confirms it: the badge changes from 16 to 32 in the live DOM.
|
|
2267
|
+
|
|
2268
|
+
UPGRADE WATCH: the size ladder is a Tailwind utility, not a
|
|
2269
|
+
contract. Swap `h-8 w-8` for a new size, and that screen's badge goes back to
|
|
2270
|
+
shrinking (visual failure, doesn't break) and the fix is adding the pair to the :is().
|
|
2271
|
+
The ladder below is TODAY's measurement, counted in the admin bundle PLUS kumo's.
|
|
2272
|
+
Several (size-9 and size-10 on the icon-button, size-8.5 on the menu item) are already born
|
|
2273
|
+
with shrink-0 and are therefore no-ops right now: they're in the ladder so it doesn't lie, and for
|
|
2274
|
+
the day an upgrade removes shrink-0 from them.
|
|
2275
|
+
========================================================================== */
|
|
2276
|
+
html[data-mode] :is(main, [data-base-ui-portal]) :is(.flex, .inline-flex) > :is(
|
|
2277
|
+
.h-2.w-2, .h-3.w-3, .h-3\.5.w-3\.5, .h-4.w-4, .h-5.w-5, .h-6.w-6,
|
|
2278
|
+
.h-7.w-7, .h-8.w-8, .h-9.w-9, .h-10.w-10, .h-12.w-12, .h-16.w-16,
|
|
2279
|
+
.size-2, .size-3, .size-3\.5, .size-4, .size-4\.5, .size-5, .size-6,
|
|
2280
|
+
.size-6\.5, .size-7, .size-8\.5, .size-9, .size-10, .size-14
|
|
2281
|
+
):not(.grow):not(.flex-1):not(.flex-auto) {
|
|
2282
|
+
flex: 0 0 auto;
|
|
2283
|
+
aspect-ratio: 1 / 1;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
/* [15b] AN ICON THAT COMES IN THROUGH THE `size` PROP, not through a class.
|
|
2287
|
+
kumo's Phosphor and Loader turn the prop into a width/height ATTRIBUTE on the
|
|
2288
|
+
<svg>, with no class at all, so [15a], which finds it by class, doesn't reach it.
|
|
2289
|
+
|
|
2290
|
+
CENSUS, counted in the 0.36 bundle itself: 285 svgs are DIRECT children of a flex row
|
|
2291
|
+
inside main/portal. 251 already carry h-N/w-N/size-N and fall under [15a]. 34 are left,
|
|
2292
|
+
and this rule exists for them:
|
|
2293
|
+
28x kumo's Loader, on the "loading" rows of Ajustes, WordPress import,
|
|
2294
|
+
user list and media picker. It sets width/height in an INLINE
|
|
2295
|
+
style, which does NOT stop flex from squeezing it: an inline style doesn't
|
|
2296
|
+
touch flex-shrink.
|
|
2297
|
+
2x WarningCircle on the Redirecionamentos loop warning.
|
|
2298
|
+
1x Hero illustration for Media's empty state, 1x Plus, 1x UserCircleIcon,
|
|
2299
|
+
1x FileKindIcon, all in a fixed-size box: there the rule is just a safety net.
|
|
2300
|
+
Real squeeze measured in only one spot: the 14px WarningCircle inside
|
|
2301
|
+
`div.w-20.flex.items-center.justify-end.gap-1`.
|
|
2302
|
+
|
|
2303
|
+
WHAT THIS RULE DOESN'T COVER, on purpose: in Redirecionamentos the
|
|
2304
|
+
`<ArrowNext size={14} />` lives in `div.w-8.text-center` and the empty-state
|
|
2305
|
+
icon lives in `div.py-12.text-center`. Neither is a flex child, so
|
|
2306
|
+
neither is a flex item and neither shrinks; what shrinks in that row is the
|
|
2307
|
+
`div.w-8` itself. Worth remembering that a shrunk svg doesn't distort either: with the
|
|
2308
|
+
default preserveAspectRatio (xMidYMid meet, which Phosphor doesn't override) the
|
|
2309
|
+
artwork just gets smaller and stays centered.
|
|
2310
|
+
|
|
2311
|
+
Covering every svg that's a direct flex child is safe: the bundle sweep found no
|
|
2312
|
+
svg with w-full, h-full, size-full, flex-1, grow or basis-* in that position. The
|
|
2313
|
+
kumo svgs this rule also catches stay inert since they already have fixed width and
|
|
2314
|
+
height: the Popover's and Tooltip's arrow (20x10), the Breadcrumbs separator and
|
|
2315
|
+
the Link's external icon. The arrow is positioned by Floating UI on the PARENT element,
|
|
2316
|
+
and here nothing writes transform, position, inset or overflow.
|
|
2317
|
+
|
|
2318
|
+
`flex-shrink` and not the `flex` shortcut: the shortcut also zeroes grow and rewrites
|
|
2319
|
+
basis, and since this rule is unlayered it would silently override a `grow` or
|
|
2320
|
+
`basis-*` that kumo might one day put on an svg.
|
|
2321
|
+
No companion aspect-ratio rule: Phosphor's <svg> always carries both width AND
|
|
2322
|
+
height (IconBase emits both), so aspect-ratio would never come into play, not today,
|
|
2323
|
+
not in the imagined upgrade. It would be dead code. */
|
|
2324
|
+
html[data-mode] :is(main, [data-base-ui-portal]) :is(.flex, .inline-flex) > svg {
|
|
2325
|
+
flex-shrink: 0;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
/* --------------------------------------------------------------------------
|
|
2329
|
+
[15c] OPTIONAL, AND SHIPPED DISABLED ON PURPOSE.
|
|
2330
|
+
|
|
2331
|
+
The fixed-width columns on the TWO Redirecionamentos lists (the rules
|
|
2332
|
+
list and the 404s panel) are flex items without shrink-0. MEASURED: `.flex-1{flex:1}`
|
|
2333
|
+
is grow 1, shrink 1, basis 0%, and the shrinkage is distributed by the factor
|
|
2334
|
+
scaled by the BASIS, so basis 0 has weight 0: the truncate columns give up
|
|
2335
|
+
nothing and the whole squeeze falls on the fixed columns from the very first pixel of
|
|
2336
|
+
deficit. Each one stops at its own min-content, which in the HEADER is smaller than in the
|
|
2337
|
+
ROW, and the result is columns out of alignment.
|
|
2338
|
+
|
|
2339
|
+
WHY IT'S LEFT COMMENTED OUT: locking the shrinkage without containing the overflow trades
|
|
2340
|
+
"squeezed columns" for LEAKAGE, because the container arrives without overflow-x-auto
|
|
2341
|
+
and block [12] paints the background and border shadow ON IT. That's why the fix comes
|
|
2342
|
+
bundled with `overflow-x: auto`, and that means accepting HORIZONTAL SCROLLING inside
|
|
2343
|
+
the card at phone width (the row's minimum goes up to about 664px).
|
|
2344
|
+
`bun run gate` only runs the 5 public routes and NEVER opens /_emdash/admin, so
|
|
2345
|
+
it's no safety net here.
|
|
2346
|
+
TO ENABLE: check it by eye, at 320, 390 and 768, inside the panel, and only then
|
|
2347
|
+
uncomment the whole block. If the studio doesn't want a horizontal bar inside the
|
|
2348
|
+
panel, the right call is to DISCARD the item: the original defect is a cosmetic
|
|
2349
|
+
drift in the header at narrow widths.
|
|
2350
|
+
|
|
2351
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50.text-kumo-subtle.flex.items-center.gap-4) {
|
|
2352
|
+
overflow-x: auto;
|
|
2353
|
+
}
|
|
2354
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50.text-kumo-subtle.flex.items-center.gap-4)
|
|
2355
|
+
.flex.items-center.gap-4 {
|
|
2356
|
+
min-width: min-content;
|
|
2357
|
+
}
|
|
2358
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50.text-kumo-subtle.flex.items-center.gap-4)
|
|
2359
|
+
.flex.items-center.gap-4 > :is(.w-8, .w-14, .w-16, .w-20, .w-32) {
|
|
2360
|
+
flex: 0 0 auto;
|
|
2361
|
+
}
|
|
2362
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50.text-kumo-subtle.flex.items-center.gap-4)
|
|
2363
|
+
.flex.items-center.gap-4 > .flex-1 {
|
|
2364
|
+
min-width: 7rem;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
The anchor is narrower than block [12]'s on purpose: just
|
|
2368
|
+
`:has(> .border-b.bg-kumo-tint/50)` would reach THREE containers, and the
|
|
2369
|
+
Taxonomias one has a different set of columns (w-16 / w-24). Requiring the band
|
|
2370
|
+
itself to be the `flex items-center gap-4` AND `text-kumo-subtle` row leaves
|
|
2371
|
+
exactly the two redirect lists. The 404 panel's `.w-32`
|
|
2372
|
+
MUST be in the set: leaving it out would make it the only elastic column,
|
|
2373
|
+
absorbing 100% of the deficit, which would be WORSE than today. The 7rem floor exists
|
|
2374
|
+
because, with basis 0 and negative free space, the URL columns would end up at exactly
|
|
2375
|
+
0px (a defect that already happens today, below 440px of usable width).
|
|
2376
|
+
-------------------------------------------------------------------------- */
|
|
2377
|
+
|
|
2378
|
+
|
|
2379
|
+
/* ==========================================================================
|
|
2380
|
+
[16] USER MENU (replaces parts of block [8])
|
|
2381
|
+
========================================================================== */
|
|
2382
|
+
|
|
2383
|
+
/* [16a] REPLACES the two [8b] rules on lines 1200 to 1206. Paste the WHOLE
|
|
2384
|
+
pair: `translate` is a single property, so you can't write the X axis
|
|
2385
|
+
in one rule and the Y in another, the second would erase the first.
|
|
2386
|
+
|
|
2387
|
+
Y AXIS, already known: kumo asks for `sideOffset: 8` (popover.tsx:189).
|
|
2388
|
+
X AXIS, new, and it gives back the 5px that Base UI's `shift()` steals. kumo doesn't
|
|
2389
|
+
pass `collisionPadding`, so the Positioner falls back to the default of 5
|
|
2390
|
+
(vendor-base-ui:4423); with side="bottom" that default becomes
|
|
2391
|
+
{top:6, right:5, bottom:5, left:5} and shift runs with mainAxis on. Since the
|
|
2392
|
+
skin already zeroes the header's padding (line 181) and pins it to left/right 0 on
|
|
2393
|
+
desktop (262-266), the popup's right border is born exactly on the clipping
|
|
2394
|
+
border: detectOverflow.right = 5 > 0, and Floating UI subtracts 5px from X. The
|
|
2395
|
+
limitShift would only kick in above 15px, so the 5px pass through whole.
|
|
2396
|
+
MEASURED by simulating the real middleware chain across three viewports (1440x900,
|
|
2397
|
+
a short window forcing a flip, and 320px): shift = {x:-5} in all three, a 5px gap on
|
|
2398
|
+
both sides. The value is the same on bottom and on top because the padding
|
|
2399
|
+
calculation comes from the `side` PROP, not from the side flip resolved to.
|
|
2400
|
+
|
|
2401
|
+
Nothing here writes transform: `translate` is independent, composes BEFORE it, doesn't
|
|
2402
|
+
enter the popup's `transition-[transform,scale,opacity]` (applies instantly) and
|
|
2403
|
+
Floating UI measures the POSITIONER, the parent, whose box doesn't change when the child
|
|
2404
|
+
translates. The popup also doesn't use its own transform: Tailwind v4 compiles
|
|
2405
|
+
`scale-90` into the standalone `scale` property.
|
|
2406
|
+
|
|
2407
|
+
CORNER: which one touches the bar depends on the side. In `bottom` (the normal case) it touches the
|
|
2408
|
+
TOP border; in `top` (flip due to lack of space) it touches the BOTTOM one.
|
|
2409
|
+
`!important` IS MANDATORY, not decoration: line 1182 declares
|
|
2410
|
+
`border-radius: var(--wp-radius) !important` and IMPORTANCE is evaluated BEFORE
|
|
2411
|
+
specificity. Without it these declarations lose to 1182 even at
|
|
2412
|
+
(0,5,2) against (0,4,2), and the rule becomes a no-op (measured: 4px on all four
|
|
2413
|
+
corners). The 1182 shorthand still applies as a fallback for lateral data-side
|
|
2414
|
+
(left/right), which IS reachable: Base UI uses fallbackAxisSide 'end', so in a
|
|
2415
|
+
window too short the menu goes sideways, and there no border touches the
|
|
2416
|
+
bar, so all four corners are the right call.
|
|
2417
|
+
|
|
2418
|
+
WATCH: if a kumo upgrade passes an explicit `collisionPadding`, only the
|
|
2419
|
+
fallback number changes. In an RTL interface align=end flips to the left and the sign
|
|
2420
|
+
inverts (--wp-popover-inset: -5px); the panel is pt-BR, so it applies today. Safari
|
|
2421
|
+
without `translate` support falls back to the 8px-plus-5px gap: clean degradation. */
|
|
2422
|
+
html[data-mode] [role="dialog"].kumo-popover-popup[data-side="bottom"]:has(a[href*="/settings/security"]) {
|
|
2423
|
+
translate: var(--wp-popover-inset, 5px) calc(var(--wp-popover-offset, 8px) * -1);
|
|
2424
|
+
border-radius: 0 0 var(--wp-radius) var(--wp-radius) !important;
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
html[data-mode] [role="dialog"].kumo-popover-popup[data-side="top"]:has(a[href*="/settings/security"]) {
|
|
2428
|
+
translate: var(--wp-popover-inset, 5px) var(--wp-popover-offset, 8px);
|
|
2429
|
+
border-radius: var(--wp-radius) var(--wp-radius) 0 0 !important;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
/* [16b] REPLACES line 1183 (`box-shadow: 0 3px 5px rgba(0,0,0,.2)`).
|
|
2433
|
+
That one was a literal copy from wp-admin: a single, short layer, made for a
|
|
2434
|
+
LIGHT card over a light gray. With a #1d2327 popup over the #f0f0f1 canvas it barely
|
|
2435
|
+
shows up, because the first shadow pixels land right next to an element that's already
|
|
2436
|
+
dark: what makes the menu float here is REACH, not opacity.
|
|
2437
|
+
Four layers, from near to far:
|
|
2438
|
+
1. contact seals the border on the light background, stops a hard clipping edge
|
|
2439
|
+
2. penumbra gives thickness to the card
|
|
2440
|
+
3. ambient the mass that separates the menu from the page
|
|
2441
|
+
4. diffuse the long reach that gives height, low alpha because the radius is large
|
|
2442
|
+
The negative spread on the last two SHRINKS the lateral leak and pushes the mass
|
|
2443
|
+
downward (doesn't eliminate it: they still leak 5px and 6px on each side).
|
|
2444
|
+
Reach upward is practically nil (layer 2: 0px; 3: -7px; 4: -22px), so
|
|
2445
|
+
nothing dirties the dark bar above the flush menu.
|
|
2446
|
+
DON'T ADD BACK A 1px LAYER: this popup's border is `outline outline-kumo-fill`
|
|
2447
|
+
(popover.tsx:212), a standalone property, not a ring inside box-shadow like
|
|
2448
|
+
the content popups in block [8e]. It survives this declaration, and its
|
|
2449
|
+
color already comes from line 1181.
|
|
2450
|
+
The ink is rgba(29,35,39,...), --wp-ink itself: a shadow tinted with the surface's
|
|
2451
|
+
color sits better on the page's gray than pure black. The shadow isn't in
|
|
2452
|
+
the popup's `transition-[transform,scale,opacity]`, so it doesn't animate.
|
|
2453
|
+
If it gets too heavy, lower the alphas on layers 3 and 4 (that's where the sense of height
|
|
2454
|
+
lives); if it gets too weak, raise layer 4's RADIUS, never layer 1's alpha. */
|
|
2455
|
+
html[data-mode] [role="dialog"].kumo-popover-popup:has(a[href*="/settings/security"]) {
|
|
2456
|
+
box-shadow:
|
|
2457
|
+
0 1px 2px rgba(29, 35, 39, .16),
|
|
2458
|
+
0 4px 8px rgba(29, 35, 39, .12),
|
|
2459
|
+
0 12px 22px -6px rgba(29, 35, 39, .14),
|
|
2460
|
+
0 28px 44px -16px rgba(29, 35, 39, .20) !important;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
|
|
2464
|
+
/* ==========================================================================
|
|
2465
|
+
[17] SIDE BAR WITH NO MOTION
|
|
2466
|
+
The owner asked for two things that coexist: MORE hover interpolation in the content
|
|
2467
|
+
(block [19]) and ZERO animation on the bar. Everything here is scoped to the menu, and
|
|
2468
|
+
`transition-property` isn't inherited, so zeroing a parent never reaches the
|
|
2469
|
+
content.
|
|
2470
|
+
========================================================================== */
|
|
2471
|
+
|
|
2472
|
+
/* [17a] THE ICON DOESN'T SHIFT ON HOVER, it only changes color.
|
|
2473
|
+
The movement was OURS (translateX(1px), lines 569-570), fed by the
|
|
2474
|
+
transition on lines 555-556. There's no competitor on kumo's side: sidebar.tsx
|
|
2475
|
+
only has `group-hover/menu-button:opacity-100` on the chevron (pure opacity), and its
|
|
2476
|
+
`translate-x-[-3px]` on line 1136 is for the COLLAPSED state, already neutralized
|
|
2477
|
+
by lines 383-388.
|
|
2478
|
+
IDEAL: edit it in place (R3.4). These two rules do the same on top: the same
|
|
2479
|
+
selector declared later wins, no !important needed.
|
|
2480
|
+
MAINTENANCE: the transition list now only has `color`. Anyone wanting to animate the
|
|
2481
|
+
icon again needs to re-add the property to the list, otherwise the transform
|
|
2482
|
+
shows up abrupt. */
|
|
2483
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] svg {
|
|
2484
|
+
-webkit-transition: color var(--wp-fast) ease;
|
|
2485
|
+
transition: color var(--wp-fast) ease;
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"]:hover svg {
|
|
2489
|
+
color: var(--wp-accent-soft);
|
|
2490
|
+
-webkit-transform: none;
|
|
2491
|
+
transform: none;
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
/* [17b] COLLAPSE AND EXPAND WITH NO INTERPOLATION.
|
|
2495
|
+
kumo does ALL the menu's transitions with
|
|
2496
|
+
`duration-(--sidebar-animation-duration)` and writes that variable INLINE on the
|
|
2497
|
+
wrapper, at 250ms (sidebar.tsx:376, SIDEBAR_ANIMATION_DURATION_MS = 250, curve
|
|
2498
|
+
cubic-bezier(0.77,0,0.175,1)). !important in an author sheet with NO layer beats the
|
|
2499
|
+
style attribute: measured in Chromium, the wrapper resolves to 0ms with the inline saying
|
|
2500
|
+
250ms, and the utility .duration-(--sidebar-animation-duration), which lives in
|
|
2501
|
+
@layer utilities with no !important, computes transition-duration 0s and
|
|
2502
|
+
transition-property none.
|
|
2503
|
+
|
|
2504
|
+
One line zeroes all 13 consumers: aside width (659), content-container width
|
|
2505
|
+
(694), footer width and padding (847), viewport padding (794), the item's
|
|
2506
|
+
inner block translate (1136), the trigger icon's little stroke (1512), header
|
|
2507
|
+
padding and the title's grid-cols-[1fr] to [0fr] collapse (admin/Sidebar.tsx
|
|
2508
|
+
373 and 384), PLUS the mobile drawer (nav with transition-transform, 606) and the mobile
|
|
2509
|
+
backdrop's fade (575). DECLARED CONSEQUENCE: on mobile the menu now opens
|
|
2510
|
+
without sliding.
|
|
2511
|
+
<main> has no transition of its own (Shell.tsx: `flex-1 overflow-y-auto`), so it
|
|
2512
|
+
follows the aside and becomes instant along with it. The collapsed item's tooltip exits through a
|
|
2513
|
+
portal in body, OUTSIDE the wrapper, and keeps whatever section [18] dictates.
|
|
2514
|
+
Doesn't leave a stuck state: there's no transitionend listener in sidebar.tsx, and
|
|
2515
|
+
`resizable` and `peekable` are false by default and the Shell doesn't pass either
|
|
2516
|
+
one. The panel also doesn't use MenuSub, Collapsible or SlidingViews, so
|
|
2517
|
+
zeroing the variable doesn't kill any accordion that exists today. If one ever exists, the
|
|
2518
|
+
way out is to give the duration back only on that node.
|
|
2519
|
+
This selector already exists on line 142 for the same reason (EmDash writes it inline
|
|
2520
|
+
on the wrapper): if editing by hand, merge the declaration there. */
|
|
2521
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) {
|
|
2522
|
+
--sidebar-animation-duration: 0ms !important;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
/* Safety net, in case an upgrade swaps the variable for a hand-written value. Lists
|
|
2526
|
+
ONLY the four nodes whose geometry actually moves and that this sheet
|
|
2527
|
+
doesn't already immobilize some other way: aside and content-container animate width,
|
|
2528
|
+
the footer animates width (its padding is already 0 !important on 491) and the
|
|
2529
|
+
trigger's little stroke moves from translate-x-px to translate-x-[10.5px].
|
|
2530
|
+
LEFT OUT on purpose:
|
|
2531
|
+
. [data-sidebar="header"]: padding already pinned at 0 8px !important (293).
|
|
2532
|
+
. [data-sidebar="menu-button"] > div: translate and transform already pinned at
|
|
2533
|
+
none !important (385-387).
|
|
2534
|
+
. [data-sidebar="content"] > div: padding already pinned at 0 !important (348), and
|
|
2535
|
+
this selector would also catch Base UI's Scrollbar, which is the SECOND div child
|
|
2536
|
+
of ScrollArea.Root and has its own 150ms fade that doesn't take part in the
|
|
2537
|
+
collapse.
|
|
2538
|
+
. [data-sidebar="menu-button"] and [data-sidebar="trigger"] itself: both have
|
|
2539
|
+
OUR color transition on hover (379-380 and 510-511), which the owner wants kept. */
|
|
2540
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) > :is(aside,nav).emdash-sidebar,
|
|
2541
|
+
html[data-mode] :is(aside,nav).emdash-sidebar > [data-sidebar="content-container"],
|
|
2542
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"],
|
|
2543
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"] svg path {
|
|
2544
|
+
-webkit-transition-property: none !important;
|
|
2545
|
+
transition-property: none !important;
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
/* [17c] COLLAPSED: THE ICON DOESN'T MOVE, AND THE BAR SHRINKS FROM 57px TO 36px.
|
|
2549
|
+
|
|
2550
|
+
OPEN-STATE MEASUREMENT: the scroll viewport has zeroed padding (347-351), and
|
|
2551
|
+
neither group, menu, nor menu-item puts padding in kumo (`p-0`, only `gap-y-px`).
|
|
2552
|
+
All the breathing room comes from the item, on line 372. So the icon's left border sits
|
|
2553
|
+
8px from the menu's border.
|
|
2554
|
+
COLLAPSED TODAY: a 57px bar (SIDEBAR_WIDTH_ICON, sidebar.tsx:93, written
|
|
2555
|
+
inline on the wrapper at 375) with the item centered, so the icon lands at
|
|
2556
|
+
(57 - 20) / 2 = 18.5px. A 10.5px jump on every click.
|
|
2557
|
+
NEW MATH: 8px padding + 20px icon (--wp-icon) + 8px on the other side =
|
|
2558
|
+
36px, which is wp-admin's collapsed #adminmenu. With the item aligned left, the
|
|
2559
|
+
jump becomes ZERO.
|
|
2560
|
+
|
|
2561
|
+
The new token goes into html:root[data-mode] and not html[data-mode]: (0,1,1)
|
|
2562
|
+
would lose to block [0] on line 20 and the declaration would become dead letter. In
|
|
2563
|
+
calc() from --wp-icon so the bar keeps up on its own if the icon changes.
|
|
2564
|
+
IDEAL: move the token line to block [0], next to line 47, which is where
|
|
2565
|
+
per-client changes happen.
|
|
2566
|
+
|
|
2567
|
+
DOESN'T LEAK INTO THE EDITOR: the right panel is a SECOND Sidebar.Provider
|
|
2568
|
+
(ContentEditor.tsx:667) and is immune for three independent reasons: its wrapper
|
|
2569
|
+
doesn't match :has(.emdash-sidebar), it writes its own --sidebar-width-icon
|
|
2570
|
+
inline (a declaration on the element beats inheritance) and it's collapsible="offcanvas", which
|
|
2571
|
+
doesn't even use the variable.
|
|
2572
|
+
TOUCH TARGET: the collapsed item is 36 by 36, above the gate's 24px. The label
|
|
2573
|
+
stays accessible via the tooltip, because `peekable` is false by default and
|
|
2574
|
+
kumo's condition is `state === "collapsed" && !peekable`.
|
|
2575
|
+
The `border-r` kumo puts on the footer when collapsing doesn't need to go: measured,
|
|
2576
|
+
a right border doesn't move an icon anchored to the left (a 36px footer with
|
|
2577
|
+
border-box, a 35px content box, the icon still sits at 8px and
|
|
2578
|
+
8 + 20 = 28 < 35, doesn't even clip). */
|
|
2579
|
+
html:root[data-mode] {
|
|
2580
|
+
--wp-menu-w-icon: calc(8px + var(--wp-icon) + 8px); /* 36px today */
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar) {
|
|
2584
|
+
--sidebar-width-icon: var(--wp-menu-w-icon) !important;
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
/* padding-LEFT still comes from line 372, which applies in both states: that's what
|
|
2588
|
+
stops the icon from moving. Only padding-RIGHT is readjusted here, because the
|
|
2589
|
+
16px in [17d] exist to keep the label away from the active item's little arrow, and in the
|
|
2590
|
+
collapsed state there's no label or arrow (line 438 hides the ::after). */
|
|
2591
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar)[data-state="collapsed"] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] {
|
|
2592
|
+
justify-content: flex-start !important;
|
|
2593
|
+
padding-right: 8px !important;
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
html[data-mode] [data-sidebar-wrapper]:has(.emdash-sidebar)[data-state="collapsed"] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] > div {
|
|
2597
|
+
flex: 0 0 auto !important;
|
|
2598
|
+
justify-content: flex-start !important;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
/* the "Recolher menu" button follows the same ruler. Anchored on aria-expanded, which is
|
|
2602
|
+
boolean (sidebar.tsx:1544) and doesn't fall into the third "peeking" state, same as the
|
|
2603
|
+
criterion already adopted in section [4B]. The padding comes from line 500. The icon here is the
|
|
2604
|
+
::before with a mask from [4B] (kumo's <svg> is hidden on 1718), also at
|
|
2605
|
+
20px: same geometry, same math. */
|
|
2606
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="footer"] [data-sidebar="trigger"][aria-expanded="false"] {
|
|
2607
|
+
justify-content: flex-start !important;
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
/* [17d] BREATHING ROOM ON THE ITEM'S RIGHT.
|
|
2611
|
+
Today padding-right is 8px and the active item's little arrow occupies exactly those
|
|
2612
|
+
8px: the ::after on line 410 is a 16x16 box (border 8px) anchored to
|
|
2613
|
+
right: 0, and its visible part is the triangle from the rightmost 8px. The text
|
|
2614
|
+
ends right at the tip, zero slack. With 16px, 8px is left between the end of the text and
|
|
2615
|
+
the arrow's tip, and the same breathing room against the border on items with no arrow.
|
|
2616
|
+
THE ARROW DOESN'T MOVE: `right: 0` anchors to the padding-box, and padding doesn't shift
|
|
2617
|
+
the padding-box.
|
|
2618
|
+
ACCEPTED COST: the label drops from 186px to 178px with the menu at 230px, about
|
|
2619
|
+
one character at 14px. The compensation (menu at 240px) is left out because this
|
|
2620
|
+
round asked for a narrower bar, and the note is in R3.5.
|
|
2621
|
+
The clickable area doesn't shrink: padding is part of the button's area. The submenu item's
|
|
2622
|
+
chevron (`ml-auto`) and the MenuBadge get the same breathing room, which is an improvement.
|
|
2623
|
+
IDEAL: edit line 372 (R3.5). This rule does the same on top. */
|
|
2624
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="menu-button"] {
|
|
2625
|
+
padding: 0 16px 0 8px !important;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
|
|
2629
|
+
/* ==========================================================================
|
|
2630
|
+
[18] COLLAPSED MENU: THE LABEL BECOMES A TAB STUCK TO THE ICON (wp-admin)
|
|
2631
|
+
|
|
2632
|
+
The label is a kumo Tooltip (Base UI) PORTALED into <body>, so it's not a
|
|
2633
|
+
descendant of the menu and no ancestor reaches it. The anchor has two halves:
|
|
2634
|
+
:has([data-sidebar="menu-button"][data-popup-open])
|
|
2635
|
+
whoever opened the popup is a menu item. Without this guard the rule would catch
|
|
2636
|
+
ANY popup with data-side="right", and Base UI produces that value on
|
|
2637
|
+
its own: its default is fallbackAxisSideDirection "end"
|
|
2638
|
+
(internals/useAnchorPositioning.js:183), so a side="bottom" tooltip from the
|
|
2639
|
+
editor bar with no room above or below tries the perpendicular axis
|
|
2640
|
+
and lands on "right". The guard doesn't flicker on close: with no animation, Base UI
|
|
2641
|
+
does a flushSync unmount before paint (useAnimationsFinished.js:70-85).
|
|
2642
|
+
.kumo-tooltip-popup[data-side="right"]
|
|
2643
|
+
a class kumo writes by hand on the Popup plus the attribute Base UI
|
|
2644
|
+
emits (TooltipPopup.js puts side and align in the state). The second line of each
|
|
2645
|
+
pair targets the same thing via the Positioner, in case the library moves data-side up a level.
|
|
2646
|
+
`side="right"` is hardcoded in Sidebar.MenuButton (sidebar.tsx:1225) and
|
|
2647
|
+
EmDash only passes `tooltip` when `state === "collapsed"`, with peekable false:
|
|
2648
|
+
there's no empty popup with the menu open.
|
|
2649
|
+
|
|
2650
|
+
Positioning is still Floating UI's job: no rule here writes
|
|
2651
|
+
transform, position, inset or overflow. The only displacement is `translate`, and
|
|
2652
|
+
it exists to undo kumo's `sideOffset: 10` (Base UI applies
|
|
2653
|
+
offset({mainAxis}) without adding the arrow), which sticks the tab to the rail's border.
|
|
2654
|
+
|
|
2655
|
+
MEASURED IN THE BROWSER with styles.css and this sheet loaded: Positioner at
|
|
2656
|
+
x=57, popup painted at x=47, 36px height same as the item's, text 9.4px from the top
|
|
2657
|
+
and 10.1px from the bottom. Contrast 5.78:1 on normal (#90b5d8 on #303539) and 5.17:1
|
|
2658
|
+
on active (#ffffff on #2271b1), both AA.
|
|
2659
|
+
|
|
2660
|
+
MEASURED PITFALL, and it's the reason for the `flex-direction` line: kumo's Popup
|
|
2661
|
+
carries `flex-col` together with `flex`. Without undoing that, `align-items: center`
|
|
2662
|
+
centers on the HORIZONTAL axis and the label sticks to the top, with 19px left over
|
|
2663
|
+
at the bottom. The tab would ship looking fixed and come out crooked.
|
|
2664
|
+
|
|
2665
|
+
WHAT HAS NO CSS FIX, and is stated here: the 600ms delay on the first
|
|
2666
|
+
pass (Base UI's OPEN_DELAY, and neither kumo nor EmDash passes `delay`; inside
|
|
2667
|
+
the TooltipProvider group the following items open instantly), and the tab not
|
|
2668
|
+
being clickable, because it's a popup, not a link.
|
|
2669
|
+
WATCH: the -10px is tied to kumo's sideOffset. If the library changes the value,
|
|
2670
|
+
a gap or an overlap comes back (visible, doesn't break). If the tooltip needs to
|
|
2671
|
+
flip sides for lack of room, data-side stops being "right" and the skin
|
|
2672
|
+
simply stops applying, falling back to kumo's balloon: graceful failure.
|
|
2673
|
+
========================================================================== */
|
|
2674
|
+
|
|
2675
|
+
/* [18a] THE TRANSITION DIES OUTSIDE THE GUARD, AND THIS POSITION IS THE FIX.
|
|
2676
|
+
MEASURED DEFECT (round 3, three tabs stuck on screen while sweeping down the column): the
|
|
2677
|
+
`transition: none` line lived INSIDE the block guarded by
|
|
2678
|
+
:has([data-sidebar="menu-button"][data-popup-open]). A global guard means
|
|
2679
|
+
it TURNS ITSELF ON AND OFF, and Base UI unmounts the popup by waiting for
|
|
2680
|
+
its own `getAnimations()` (popupStoreUtils.js:448 calls
|
|
2681
|
+
useOpenChangeComplete with ref = popupRef, which lands in useAnimationsFinished.js:85,
|
|
2682
|
+
`Promise.all(getAnimations().map(a => a.finished))`).
|
|
2683
|
+
The sequence that gets stuck, timed in the browser with getAnimations instrumented:
|
|
2684
|
+
1. item A closes, Base UI removes data-popup-open from trigger A;
|
|
2685
|
+
2. at that instant NO trigger has data-popup-open, the guard drops, this sheet
|
|
2686
|
+
stops applying to popup A and kumo's transition comes back to life;
|
|
2687
|
+
3. Base UI looks at popup A and sees TWO transitions running (measured: opacity and
|
|
2688
|
+
scale, computed transition "transform .15s, scale .15s", not "none"),
|
|
2689
|
+
so it waits for both `finished` promises;
|
|
2690
|
+
4. item B opens 30ms later, data-popup-open reappears, the guard rises again
|
|
2691
|
+
and reimposes `transition: none` on popup A too, because the selector is global;
|
|
2692
|
+
5. `transition: none` CANCELS both transitions, `finished` rejects, and
|
|
2693
|
+
popup A never unmounts. Every icon in the sweep leaves one more stuck.
|
|
2694
|
+
COUNT: a sweep of 5 icons, 900ms pause on each, 1.5s idle at the end.
|
|
2695
|
+
Before: 3 to 4 popups stuck with data-closed and data-ending-style. With this line
|
|
2696
|
+
outside the guard, ZERO.
|
|
2697
|
+
The native escape path EXISTS and is the right one: with no animation at all,
|
|
2698
|
+
`Promise.all([])` resolves instantly and the unmount happens via flushSync before paint. What
|
|
2699
|
+
was missing was guaranteeing the popup is ALWAYS animation-free, not only
|
|
2700
|
+
while the guard is standing. That's why the line doesn't go back to being a 1ms
|
|
2701
|
+
transition: the problem was never the duration, it was the toggling.
|
|
2702
|
+
SCOPE: only the tooltip popup with data-side "right", the same set the skin
|
|
2703
|
+
already paints. The declared cost is that a tooltip from another component that lands on
|
|
2704
|
+
"right" for lack of space loses the 150ms fade. Trading animation for nothing is the
|
|
2705
|
+
direction of this round, so the cost is aligned with the request. */
|
|
2706
|
+
html[data-mode] [data-base-ui-portal] .kumo-tooltip-popup[data-side="right"],
|
|
2707
|
+
html[data-mode] [data-base-ui-portal] [data-side="right"] > .kumo-tooltip-popup {
|
|
2708
|
+
transition: none !important;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
/* [18b] THE PAINTING STAYS GUARDED, BUT NOW TIED TO THE OPEN POPUP.
|
|
2712
|
+
`[data-open]` on the popup itself is the only bridge that exists: MEASURED in the browser,
|
|
2713
|
+
the popup has no id, no data-anchor, no attribute at all pointing back to the
|
|
2714
|
+
trigger, and the trigger (id="base-ui-_r_l_", data-base-ui-tooltip-trigger) isn't
|
|
2715
|
+
referenced by anything on the portal side. A real bridge between EACH tab and
|
|
2716
|
+
ITS button doesn't exist in CSS.
|
|
2717
|
+
What `[data-open]` solves: Base UI only leaves one popup with data-open at a time,
|
|
2718
|
+
so the dark painting stops leaking onto the tabs that are closing. Without it,
|
|
2719
|
+
the guard is global and paints EVERY tab in the portal, which is exactly the screenshot of
|
|
2720
|
+
three dark tabs stacked up. With [18a] in place there's only ever one tab at a time, and
|
|
2721
|
+
this line is the belt: if one is ever left over, it comes out without the skin instead of becoming
|
|
2722
|
+
a second dark tab on screen. */
|
|
2723
|
+
html[data-mode]:has([data-sidebar="menu-button"][data-popup-open]) [data-base-ui-portal] .kumo-tooltip-popup[data-side="right"][data-open],
|
|
2724
|
+
html[data-mode]:has([data-sidebar="menu-button"][data-popup-open]) [data-base-ui-portal] [data-side="right"] > .kumo-tooltip-popup[data-open] {
|
|
2725
|
+
translate: -10px 0 !important;
|
|
2726
|
+
display: flex !important;
|
|
2727
|
+
flex-direction: row !important; /* undoes kumo's flex-col */
|
|
2728
|
+
align-items: center !important; /* only centers vertically after the row */
|
|
2729
|
+
justify-content: flex-start !important;
|
|
2730
|
+
min-height: 36px !important; /* the same height as the menu item */
|
|
2731
|
+
padding: 0 12px !important;
|
|
2732
|
+
background: var(--wp-ink-hover) !important;
|
|
2733
|
+
color: var(--wp-accent-soft) !important;
|
|
2734
|
+
font-family: var(--wp-font) !important;
|
|
2735
|
+
font-size: 14px !important;
|
|
2736
|
+
font-weight: 400 !important;
|
|
2737
|
+
line-height: 1.3 !important;
|
|
2738
|
+
white-space: nowrap !important;
|
|
2739
|
+
border: 0 !important;
|
|
2740
|
+
border-radius: 0 !important;
|
|
2741
|
+
outline: 0 !important;
|
|
2742
|
+
box-shadow: none !important;
|
|
2743
|
+
/* zero animation, which is the bar's side of the deal this round. The `transition` line
|
|
2744
|
+
that used to be HERE moved to block [18a], outside the guard: inside it, it turned on and
|
|
2745
|
+
off and kept the popup mounted forever (the measurement is up above).
|
|
2746
|
+
kumo's entry `scale-90` compiles into the standalone `scale` property,
|
|
2747
|
+
so it can be killed without touching transform. */
|
|
2748
|
+
scale: 1 !important;
|
|
2749
|
+
opacity: 1 !important;
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
/* the beak doesn't exist in wp-admin. Base UI's Arrow is the aria-hidden child.
|
|
2753
|
+
Same `[data-open]` as [18b]: the beak only disappears on the tab that's open, so the
|
|
2754
|
+
pair of rules doesn't drift apart if a closing popup is ever left on screen. */
|
|
2755
|
+
html[data-mode]:has([data-sidebar="menu-button"][data-popup-open]) [data-base-ui-portal] .kumo-tooltip-popup[data-side="right"][data-open] > [aria-hidden="true"],
|
|
2756
|
+
html[data-mode]:has([data-sidebar="menu-button"][data-popup-open]) [data-base-ui-portal] [data-side="right"] > .kumo-tooltip-popup[data-open] > [aria-hidden="true"] {
|
|
2757
|
+
display: none !important;
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
/* current item: in wp-admin the active item's tab comes out blue, not gray. The popup is in the
|
|
2761
|
+
portal, far from the item, so the bridge is the TRIGGER: Base UI puts data-popup-open
|
|
2762
|
+
on the <a> itself while the label is open, and only one stays open at a time.
|
|
2763
|
+
The popup's `[data-open]` closes the other end of the bridge: without it, an active item with
|
|
2764
|
+
its label opening would paint ANY tab in the portal blue, including ones that
|
|
2765
|
+
were closing. */
|
|
2766
|
+
html[data-mode]:has([data-sidebar="menu-button"][data-active="true"][data-popup-open]) [data-base-ui-portal] .kumo-tooltip-popup[data-side="right"][data-open],
|
|
2767
|
+
html[data-mode]:has([data-sidebar="menu-button"][data-active="true"][data-popup-open]) [data-base-ui-portal] [data-side="right"] > .kumo-tooltip-popup[data-open] {
|
|
2768
|
+
background: var(--wp-accent) !important;
|
|
2769
|
+
color: var(--wp-accent-contrast) !important;
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
|
|
2773
|
+
/* ==========================================================================
|
|
2774
|
+
[19] HOVER INTERPOLATION IN THE CONTENT
|
|
2775
|
+
The other side of this round's request. Everything scoped to `main`, which is a SIBLING of
|
|
2776
|
+
the side bar in the Shell (Sidebar.Provider > [SidebarNav, div.flex-1 > [Header,
|
|
2777
|
+
main]]): disjoint subtrees, so nothing here reaches the menu.
|
|
2778
|
+
No rule uses `transition: all`: an unenumerated property is a property
|
|
2779
|
+
that a kumo upgrade could start animating without anyone deciding to.
|
|
2780
|
+
========================================================================== */
|
|
2781
|
+
|
|
2782
|
+
/* [19a] WHERE NOBODY DECLARED ONE.
|
|
2783
|
+
MEASURED in the bundle: kumo 2.6.0 is inconsistent. Of the six Button variants, only
|
|
2784
|
+
"outline" carries `transition-colors`; the DEFAULT one ("secondary": `bg-kumo-base ...
|
|
2785
|
+
not-disabled:hover:bg-kumo-tint`) and "ghost" change color on hover with no
|
|
2786
|
+
transition at all. The same goes for SettingsNavRow (index.js:35998), which
|
|
2787
|
+
arrives with `hover:bg-kumo-tint` and zero transition class. A className string
|
|
2788
|
+
sweep: 112 carry `hover:bg|text|border-kumo`, and 100 of them have no
|
|
2789
|
+
transition whatsoever.
|
|
2790
|
+
|
|
2791
|
+
ANCHOR = the very CLASS that declares the hover, not the component. Tailwind v4
|
|
2792
|
+
keeps the literal name in the attribute, so `hover:bg-kumo` as a substring matches
|
|
2793
|
+
exactly whoever changes color on hover, and it also catches `group-hover:`,
|
|
2794
|
+
`not-disabled:hover:` and `aria-selected:hover:`, which are the same case.
|
|
2795
|
+
|
|
2796
|
+
THREE GUARDS:
|
|
2797
|
+
1. `:not([class*="transition"])` = only where Tailwind hasn't already declared one.
|
|
2798
|
+
Avoids replacing Base UI's `transition-[transform,scale,opacity]` and the
|
|
2799
|
+
outline variant's `transition-colors`.
|
|
2800
|
+
2. `:not(.emdash-sidebar *)` = an upgrade safety net, since the `main` scope alone
|
|
2801
|
+
already makes it impossible to reach the bar.
|
|
2802
|
+
3. `:where()` on the WHOLE match, which counts as zero specificity. Without it the
|
|
2803
|
+
rule would weigh (0,4,2) and would RUN OVER the transitions this skin already declares,
|
|
2804
|
+
because `transition` is a shorthand: whichever one wins REPLACES the whole list, doesn't add to it.
|
|
2805
|
+
The victims were real and measured: lines 610, 629 and 1649 (buttons next to the
|
|
2806
|
+
h1, which animate background-color, border-color, color, box-shadow and transform,
|
|
2807
|
+
and are default-variant Buttons, meaning they match `hover:bg-kumo`), 737
|
|
2808
|
+
(card), 728 (links) and 811 (table row). Without :where(), the "Novo"
|
|
2809
|
+
button would lose the list's box-shadow and transform and, since :hover keeps
|
|
2810
|
+
applying translateY(-1px), it would start SNAP-JUMPING 1px. The opposite of what was asked.
|
|
2811
|
+
With :where() the rule weighs (0,1,2), loses to all of them and only paints what
|
|
2812
|
+
nobody else declared.
|
|
2813
|
+
|
|
2814
|
+
REACH, stated plainly: NO POPUP GETS IN. The user menu is a Popover with a
|
|
2815
|
+
portal in body, so outside `main`, and stays without interpolation, along with the toast and
|
|
2816
|
+
the side menu's popup. This is deliberate: widening the scope to body would touch
|
|
2817
|
+
the popups' entry and exit animation.
|
|
2818
|
+
KNOWN RESIDUE: at (0,1,2) rule 728 (`main a`) still wins on
|
|
2819
|
+
links, and it lists only color and background-color. So an <a> with
|
|
2820
|
+
`hover:border-kumo-*` (the editor's index) keeps a jerky border. Fixing that means
|
|
2821
|
+
editing 728, which is a different rule and deserves its own round. */
|
|
2822
|
+
html[data-mode] main :where(
|
|
2823
|
+
[class*="hover:bg-kumo"],
|
|
2824
|
+
[class*="hover:text-kumo"],
|
|
2825
|
+
[class*="hover:border-kumo"]
|
|
2826
|
+
):where(:not([class*="transition"])):where(:not(.emdash-sidebar *)) {
|
|
2827
|
+
-webkit-transition:
|
|
2828
|
+
background-color var(--wp-fast) ease,
|
|
2829
|
+
color var(--wp-fast) ease,
|
|
2830
|
+
border-color var(--wp-fast) ease;
|
|
2831
|
+
transition:
|
|
2832
|
+
background-color var(--wp-fast) ease,
|
|
2833
|
+
color var(--wp-fast) ease,
|
|
2834
|
+
border-color var(--wp-fast) ease;
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
/* [19b] THE SHADOW FOR THE OTHER TWO FAMILIES IN BLOCK [7].
|
|
2838
|
+
The hover on line 884 lifts the shadow on THREE families, but only the first (the
|
|
2839
|
+
panel card) has a transition, on 740. The table frame (7 listings) and the
|
|
2840
|
+
settings block (10 Ajustes screens) used to swap all four box-shadow layers at
|
|
2841
|
+
once: a snap, not a lift. That's what the owner complained about.
|
|
2842
|
+
DURATION: --wp-normal with --wp-ease, identical to line 740, so the listing rises
|
|
2843
|
+
at the SAME pace as the widget he already approved. Elevation is a bigger visual delta
|
|
2844
|
+
than a background tone, so it can take the slower curve without feeling sluggish.
|
|
2845
|
+
|
|
2846
|
+
The first four guards are a faithful copy of lines 885-886 and aren't decoration:
|
|
2847
|
+
they exclude the marketplace skeleton, the user detail skeleton, the editor's
|
|
2848
|
+
floating bar and the plugin consent dialog.
|
|
2849
|
+
FIFTH GUARD, `:not([class*="transition"])`: `transition` is a SHORTHAND and resets
|
|
2850
|
+
transition-property. Tailwind's transition-* utilities live in
|
|
2851
|
+
@layer utilities WITHOUT !important, and a normal declaration OUTSIDE a layer beats a normal
|
|
2852
|
+
one INSIDE a layer. Without this guard the rule was ERASING the own transition of nine
|
|
2853
|
+
elements, among them the editor's HTML block (HtmlBlockNode.tsx:101) and plugin block
|
|
2854
|
+
(PluginBlockNode.tsx:281), which animate the selected state's background with
|
|
2855
|
+
`transition-colors`: it would trade a shadow snap in the listing for a color snap
|
|
2856
|
+
in the editor. The guard fails safe (whoever already has their own transition keeps it
|
|
2857
|
+
as is) and loses no target: the 7 frames and the 10 settings blocks
|
|
2858
|
+
don't carry "transition" in their class string.
|
|
2859
|
+
No `transform`: these two boxes don't lift, only the shadow changes (line 895
|
|
2860
|
+
is only for the card). */
|
|
2861
|
+
html[data-mode] main .overflow-x-auto.border:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]):not([class*="transition"]),
|
|
2862
|
+
html[data-mode] main div.border.bg-kumo-base:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]):not([class*="transition"]) {
|
|
2863
|
+
-webkit-transition: box-shadow var(--wp-normal) var(--wp-ease);
|
|
2864
|
+
transition: box-shadow var(--wp-normal) var(--wp-ease);
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
/* [19c] SAME FIX FOR THE <div>-BASED LISTING IN BLOCK [12] (Taxonomias and the
|
|
2868
|
+
two Redirecionamentos ones). Without this they'd end up being the only listings
|
|
2869
|
+
that still snap after the fix, and the divergence would just move somewhere else. Same
|
|
2870
|
+
pace as [19b]. The only value that changes on line 1567's :hover is box-shadow,
|
|
2871
|
+
so the list is exact, no leftovers.
|
|
2872
|
+
None of the three containers carries a transition-* class, and there's no
|
|
2873
|
+
`!transition*` utility in styles.css: a normal, unlayered declaration wins on its own,
|
|
2874
|
+
with no !important. */
|
|
2875
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50) {
|
|
2876
|
+
-webkit-transition: box-shadow var(--wp-normal) var(--wp-ease);
|
|
2877
|
+
transition: box-shadow var(--wp-normal) var(--wp-ease);
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
/* [19d] EDITOR'S COLLAPSIBLE HEADER (Sumario, Revisoes).
|
|
2881
|
+
Matches exactly TWO buttons: the DocumentOutline's and RevisionHistory's
|
|
2882
|
+
Collapsible.Trigger, the only two sections created with `disclosure: true`
|
|
2883
|
+
(index.js:10385 and 10400). The drag handle on the same section does NOT get in: dnd-kit
|
|
2884
|
+
only emits aria-describedby, aria-disabled, aria-pressed and
|
|
2885
|
+
aria-roledescription, never aria-expanded. The other six sections come out with
|
|
2886
|
+
data-disclosure="false".
|
|
2887
|
+
WHY ITS OWN RULE AND NOT the generic [19a]: the button IS inside `main` and
|
|
2888
|
+
kumo's ghost variant already puts `hover:bg-kumo-tint` on it, so the generic rule could
|
|
2889
|
+
reach it. This rule exists to win by specificity and LOCK the property
|
|
2890
|
+
list. The hover that applies here is line 1448's, and the base on 1424 has no
|
|
2891
|
+
transition at all.
|
|
2892
|
+
`border-color` is included because the band has a border-bottom and an upgrade that starts
|
|
2893
|
+
swapping it on hover would already arrive interpolated. NO `width` at all: line 1425 writes
|
|
2894
|
+
`width: calc(100% + 32px) !important` to beat EmDash's inline style, and animating
|
|
2895
|
+
that would wobble the whole band. Also no transform or position: dnd-kit
|
|
2896
|
+
writes an inline transform on the SECTION during the drag.
|
|
2897
|
+
No !important on purpose: a normal, unlayered rule beats a normal one in a layer, and the
|
|
2898
|
+
only `transition` with !important in styles.css lives in `.transition-none!`,
|
|
2899
|
+
`.t-text-swap` and `[data-sidebar-popup]`, none of which this button carries. */
|
|
2900
|
+
html[data-mode] section[data-disclosure="true"] button[aria-expanded] {
|
|
2901
|
+
-webkit-transition: background-color var(--wp-fast) ease, border-color var(--wp-fast) ease;
|
|
2902
|
+
transition: background-color var(--wp-fast) ease, border-color var(--wp-fast) ease;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
/* [19e] WHOEVER TURNED OFF SYSTEM ANIMATIONS.
|
|
2906
|
+
A COLOR change isn't motion and stays: it's what makes hover legible. What
|
|
2907
|
+
bothers someone who asked for less animation is the four-layer shadow at .28s, so
|
|
2908
|
+
that one (and any transform) becomes instant, and only that one.
|
|
2909
|
+
WHY THE WHOLE LIST AND NOT `transition: none`: `none` is a blanket switch and
|
|
2910
|
+
would also erase the color transition. Measured: excluding the shadow-lg
|
|
2911
|
+
and animate-pulse guards, [7]'s selectors match 41 class strings and 8 already carry
|
|
2912
|
+
EmDash's OWN transition (transition-colors on the theme card, the plugin
|
|
2913
|
+
card and the dropzone that gets a ring on isOver; transition-all and
|
|
2914
|
+
transition-opacity on the Media cards). They live in @layer utilities without
|
|
2915
|
+
!important, so the erasure would actually happen.
|
|
2916
|
+
ORDER MATTERS: the selectors repeat [19b]'s and [19c]'s, so this block has
|
|
2917
|
+
to stay AFTER them, as it does. The `:hover` is here because, if the
|
|
2918
|
+
transition is ever also declared on the hover rule, the base-only version would cover the
|
|
2919
|
+
exit and not the entry.
|
|
2920
|
+
The block on line 1009 still applies to the entry animations and to the
|
|
2921
|
+
card's transform. */
|
|
2922
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2923
|
+
html[data-mode] main .overflow-x-auto.border:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]),
|
|
2924
|
+
html[data-mode] main .overflow-x-auto.border:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]):hover,
|
|
2925
|
+
html[data-mode] main div.border.bg-kumo-base:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]),
|
|
2926
|
+
html[data-mode] main div.border.bg-kumo-base:not([class*="shadow-lg"]):not([class*="animate-pulse"]):not(.animate-pulse *):not([data-emdash-inline-bubble-menu]):hover,
|
|
2927
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50),
|
|
2928
|
+
html[data-mode] main div.border.rounded-lg:has(> .border-b.bg-kumo-tint\/50):hover {
|
|
2929
|
+
-webkit-transition:
|
|
2930
|
+
background-color var(--wp-fast) ease,
|
|
2931
|
+
border-color var(--wp-fast) ease,
|
|
2932
|
+
color var(--wp-fast) ease,
|
|
2933
|
+
opacity var(--wp-fast) ease,
|
|
2934
|
+
box-shadow 0s,
|
|
2935
|
+
-webkit-transform 0s;
|
|
2936
|
+
transition:
|
|
2937
|
+
background-color var(--wp-fast) ease,
|
|
2938
|
+
border-color var(--wp-fast) ease,
|
|
2939
|
+
color var(--wp-fast) ease,
|
|
2940
|
+
opacity var(--wp-fast) ease,
|
|
2941
|
+
box-shadow 0s,
|
|
2942
|
+
transform 0s;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
/* ==========================================================================
|
|
2947
|
+
[20] THE SITE LOGO DOESN'T MOVE WHEN THE BAR COLLAPSES
|
|
2948
|
+
========================================================================== */
|
|
2949
|
+
|
|
2950
|
+
/* MEASURED in dev at 1400px, the <img> inside [data-sidebar="header"]:
|
|
2951
|
+
expanded left = 8px (MENU icon right below: 8px)
|
|
2952
|
+
collapsed left = 5px (MENU icon right below: 8px)
|
|
2953
|
+
In other words, round 3 stopped the menu icon in both states and only the
|
|
2954
|
+
header was left out of square, 3px to the left.
|
|
2955
|
+
|
|
2956
|
+
CULPRIT, and it's not ours: kumo puts the utility
|
|
2957
|
+
`group-data-[state=collapsed]/sidebar:-translate-x-[3px]` on the header's <a> (plus its
|
|
2958
|
+
sibling `rtl:group-data-[state=collapsed]/sidebar:translate-x-[3px]`). In Tailwind v4
|
|
2959
|
+
that utility writes the `translate` property, not `transform`: the computed value
|
|
2960
|
+
is `translate: -3px` when collapsed and `none` when expanded. The 3px is exactly the
|
|
2961
|
+
measured difference, and that's why the <a> already appears shifted without any ancestor
|
|
2962
|
+
having changed its box.
|
|
2963
|
+
|
|
2964
|
+
PADDING DOESN'T TAKE PART, checked element by element all the way to the <aside>:
|
|
2965
|
+
. [data-sidebar="header"]: 8px in both states, pinned on line 293 on top
|
|
2966
|
+
of kumo's px-[11px] / px-3.5 (which does change with the state).
|
|
2967
|
+
. the <a> itself: 0, pinned on line 303 on top of ps-2.5.
|
|
2968
|
+
. content-container and aside: no padding-left.
|
|
2969
|
+
Sanity check: if the header's padding had been left out of round 3,
|
|
2970
|
+
the jump would have been 3 + 3 = 6px, not 3px.
|
|
2971
|
+
|
|
2972
|
+
FIX IN THE SPIRIT OF ROUND 3: a SINGLE value that applies in BOTH states. On
|
|
2973
|
+
purpose there is NO [data-state="collapsed"] selector here with the other side's value,
|
|
2974
|
+
nor a more specific rule redeclaring padding: a pair of per-state values is exactly
|
|
2975
|
+
what diverges again on the next upgrade. The logo stays put, it's the bar that shrinks.
|
|
2976
|
+
`transform: none` comes along as a safety net, exactly like the rule
|
|
2977
|
+
on lines 386-391 already does on the menu item's inner block, in case a Tailwind
|
|
2978
|
+
upgrade goes back to compiling translate-x into the transform matrix.
|
|
2979
|
+
|
|
2980
|
+
DOESN'T BREAK THE GOLDEN RULE: the header's <a> isn't a popup and there's no Floating UI
|
|
2981
|
+
on it; what's written here ZEROES a displacement from kumo instead of creating one
|
|
2982
|
+
of our own.
|
|
2983
|
+
|
|
2984
|
+
THE SITE NAME LABEL NEXT TO IT DOESN'T PUSH THE LOGO, and isn't being touched
|
|
2985
|
+
here. It doesn't even disappear when the bar collapses: line 320 pins
|
|
2986
|
+
`grid-template-columns: 1fr` on the grid <span>, canceling out kumo's
|
|
2987
|
+
`grid-cols-[1fr]` -> `grid-cols-[0fr]` collapse, because the header here is the
|
|
2988
|
+
fixed toolbar's left corner (lines 271-279), in the spirit of wp-admin, not
|
|
2989
|
+
a column row that shrinks. A prior decision, kept. Even if kumo's collapse is
|
|
2990
|
+
ever wanted back, it wouldn't touch the logo: the label comes AFTER
|
|
2991
|
+
the <img> in the flex order and the track closes from right to left.
|
|
2992
|
+
|
|
2993
|
+
IDEAL: merge these three declarations into the rule on line 299, which is their
|
|
2994
|
+
natural home. Left for the consolidation round, alongside the other debts
|
|
2995
|
+
already noted in R3.5, because touching the middle of the file now would shift more
|
|
2996
|
+
than forty file-line pointers in this very sheet. */
|
|
2997
|
+
html[data-mode] :is(aside,nav).emdash-sidebar [data-sidebar="header"] a {
|
|
2998
|
+
translate: none !important;
|
|
2999
|
+
-webkit-transform: none !important;
|
|
3000
|
+
transform: none !important;
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
|
|
3004
|
+
/* ==========================================================================
|
|
3005
|
+
[20] TWO FIXES WITH NO HOME IN THE BLOCKS ABOVE
|
|
3006
|
+
(round 5, 2026-09-08)
|
|
3007
|
+
========================================================================== */
|
|
3008
|
+
|
|
3009
|
+
/* [20a] THE SEARCH FIELD FILLS ITS OWN WRAPPER.
|
|
3010
|
+
|
|
3011
|
+
UPSTREAM BUG, measured in the fixture: EmDash wraps every panel search in
|
|
3012
|
+
`<div class="relative flex-1 max-w-md">` with the icon at `absolute start-3`, but
|
|
3013
|
+
kumo's `<Input>` does NOT carry `w-full` (inputVariants, input-f2ct7obgdzypjmp2.js: only
|
|
3014
|
+
`h-9 gap-1.5 rounded-lg px-3`). The field ends up with the native <input>'s intrinsic width
|
|
3015
|
+
while the wrapper still reserves `max-w-md`'s 448px. Measured on the Secoes page:
|
|
3016
|
+
wrapper 448px, field 197px, meaning a 250px hole between the search and the
|
|
3017
|
+
"Todas as origens" filter, which was the owner's complaint (it appeared floating loose in the middle of the row).
|
|
3018
|
+
|
|
3019
|
+
One rule fixes all NINE searches in the panel, which all share the same design (Secoes,
|
|
3020
|
+
ContentPicker, MediaPicker, SectionPicker, Marketplace, Registry, ThemeMarketplace,
|
|
3021
|
+
404): with the field filling the wrapper, the filter next to it sits flush against the container's own
|
|
3022
|
+
16px gap, which is the requested "stuck to the section search" look.
|
|
3023
|
+
SCOPE: `> input` direct child, so as not to reach a button or card that also uses
|
|
3024
|
+
`relative ... flex-1` (MediaFolderCard). */
|
|
3025
|
+
html[data-mode] main div.relative[class*="flex-1"] > input {
|
|
3026
|
+
width: 100% !important;
|
|
3027
|
+
}
|
|
3028
|
+
|
|
3029
|
+
/* [20b] ANTI-CLS ON THE PANEL'S METRIC CARDS.
|
|
3030
|
+
|
|
3031
|
+
Measured in the fixture: loaded card 89px, loading card 68px. The 21px jump comes
|
|
3032
|
+
entirely from the body (LayerCard.Primary), which holds the number at a 28.8px line height and
|
|
3033
|
+
the skeleton at 8px. The header no longer jumps, because line ~760 pins min-height 36px
|
|
3034
|
+
on it.
|
|
3035
|
+
|
|
3036
|
+
ANCHOR: `.text-3xl` is the class EmDash puts on the body in BOTH states (SummaryMetrics
|
|
3037
|
+
passes `text-3xl font-semibold leading-none` on both the `loading` branch and the loaded one),
|
|
3038
|
+
so the minimum height applies right from the first paint.
|
|
3039
|
+
THE NUMBER: 1.2em of 24px = 28.8px line height, the same the `.text-3xl` on line ~795
|
|
3040
|
+
defines, plus the body's 24px padding. If one of the two ever changes, change this one along with it. */
|
|
3041
|
+
html[data-mode] main .bg-kumo-elevated.ring-kumo-hairline > .ring-kumo-fill.text-3xl {
|
|
3042
|
+
min-height: calc(1.2em + 24px);
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
/* [20c] CHECKABLE LISTS (taxonomies, bylines, create taxonomy): only the corner.
|
|
3046
|
+
|
|
3047
|
+
Measured in the admin bundle: `CategoryCheckboxTree`, `BylineFilter` and `CreateTaxonomyDialog`
|
|
3048
|
+
draw each row as `rounded px-2 hover:bg-kumo-tint/50`, the same inset highlight with a
|
|
3049
|
+
round corner as the other menus. Here ONLY the corner dies, not the inset: these three lists
|
|
3050
|
+
live inside a popover and a dialog whose wrapper hasn't been measured yet in the logged-in DOM, and
|
|
3051
|
+
zeroing a container's padding without measuring is the kind of guess that breaks silently. It's
|
|
3052
|
+
noted as a debt: with the panel open, measure the wrapper and complete the edge-to-edge treatment.
|
|
3053
|
+
ANCHORED by attribute, because the Tailwind class carries `/` and `:`. */
|
|
3054
|
+
html[data-mode] [class*="hover:bg-kumo-tint/50"][class*="rounded"] {
|
|
3055
|
+
border-radius: 0 !important;
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
/* [20d] THE CREDITS BLOCK'S "+" BUTTON FITS INSIDE THE BAND (round 5.1).
|
|
3059
|
+
|
|
3060
|
+
Measured in the bundle: EmDash positions that trigger ABSOLUTE, `className="absolute end-14
|
|
3061
|
+
top-2"`, counting on the section's original layout (with no header band). Our
|
|
3062
|
+
skin turns the title into a 37px band, and a 36px button starting at 8px
|
|
3063
|
+
overflows by 7px: that's why its focus ring appeared clipped, on top of the band's
|
|
3064
|
+
border, and not aligned with the title.
|
|
3065
|
+
NUMBERS: 37px band, 32px button, 2.5px left over on each side; `top: 2px` stays within
|
|
3066
|
+
half-a-pixel rounding error and needs no transform (forbidden on an element dnd-kit drags).
|
|
3067
|
+
The band's `padding-inline-end` goes from 40px (just the handle) to 92px (handle + button + slack)
|
|
3068
|
+
ONLY on the section that has the button, so the title never runs under it. */
|
|
3069
|
+
html[data-mode] section[data-sorting] .absolute.end-14.top-2 {
|
|
3070
|
+
top: 2px !important;
|
|
3071
|
+
width: 32px !important;
|
|
3072
|
+
height: 32px !important;
|
|
3073
|
+
min-height: 0 !important;
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
html[data-mode] section[data-sorting]:has(.absolute.end-14.top-2) > div > div.flex:has(> h3),
|
|
3077
|
+
html[data-mode] section[data-sorting]:has(.absolute.end-14.top-2) > div > h3,
|
|
3078
|
+
html[data-mode] section[data-sorting]:has(.absolute.end-14.top-2) > div > div > h3 {
|
|
3079
|
+
padding-inline-end: 92px !important;
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
|
|
3083
|
+
/* ==========================================================================
|
|
3084
|
+
[21] THE RIGHT PANEL'S HEADER BAND: TITLE ON THE LEFT, EVERYTHING ELSE ON
|
|
3085
|
+
THE RIGHT, ALL THE SAME SIZE AND WITH THE SAME HOVER
|
|
3086
|
+
(round 5.2, 2026-09-08, measured on the logged-in panel via CDP)
|
|
3087
|
+
========================================================================== */
|
|
3088
|
+
|
|
3089
|
+
/* WHAT WAS MEASURED, in the editor's "Creditos" block (36.6px band, x from 1361 to 1728):
|
|
3090
|
+
h3 "Creditos" x=1377 (16px from the border, correct)
|
|
3091
|
+
button (i) "Why are bylines..." x=1437.6 14x14, radius 2px <- stuck to the title
|
|
3092
|
+
button "+" (Popover.Trigger) absolute end-14 top-2, 36x36 <- was overflowing
|
|
3093
|
+
button handle [data-sortable-handle] x=1694 28x28, radius 0
|
|
3094
|
+
Three sibling buttons with related roles, three sizes, three positions and three different hovers.
|
|
3095
|
+
|
|
3096
|
+
THE TARGET: title on the left; (i), "+" and the handle flush right, 28x28, 4px between them,
|
|
3097
|
+
6px from the border. The band's breathing-room math comes from that: 6 + 28 + 4 = 38px without the "+",
|
|
3098
|
+
6 + 28 + 4 + 28 + 4 = 70px with it.
|
|
3099
|
+
WHY `margin-inline-start: auto` on the (i): it's a flex child; the other two are
|
|
3100
|
+
`absolute`, positioned by EmDash, so it's the only piece that needs to MOVE.
|
|
3101
|
+
DEGRADATION: a browser without :has() only loses the extra breathing room; the buttons stay the same
|
|
3102
|
+
size and with the same hover. */
|
|
3103
|
+
|
|
3104
|
+
/* SINGLE HEIGHT: the band is now 36px on every block, with content centered. Without this the
|
|
3105
|
+
28px button was pushing the header itself (measured: the Creditos band went to 45px and the
|
|
3106
|
+
handle, anchored at `top: 4px`, was 4px off center). The h3 INSIDE the flex band needs to
|
|
3107
|
+
go back to being plain text: the height rule matches it too, and then the band would add up to 36 + 8. */
|
|
3108
|
+
html[data-mode] section[data-sorting] > div > h3,
|
|
3109
|
+
html[data-mode] section[data-sorting] > div > div > h3,
|
|
3110
|
+
html[data-mode] section[data-sorting] > div > div.flex:has(> h3) {
|
|
3111
|
+
display: flex !important;
|
|
3112
|
+
align-items: center !important;
|
|
3113
|
+
min-height: 36px !important;
|
|
3114
|
+
padding-block: 4px !important;
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
html[data-mode] section[data-sorting] > div > div.flex:has(> h3) > h3 {
|
|
3118
|
+
display: block !important;
|
|
3119
|
+
min-height: 0 !important;
|
|
3120
|
+
padding-block: 0 !important;
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
/* band's breathing room: 38px reserves the handle, 70px reserves handle + "+" */
|
|
3124
|
+
html[data-mode] section[data-sorting] > div > h3,
|
|
3125
|
+
html[data-mode] section[data-sorting] > div > div > h3,
|
|
3126
|
+
html[data-mode] section[data-sorting] > div > div.flex:has(> h3) {
|
|
3127
|
+
padding-inline-end: 38px !important;
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
html[data-mode] section[data-sorting]:has(.absolute.end-14.top-2) > div > h3,
|
|
3131
|
+
html[data-mode] section[data-sorting]:has(.absolute.end-14.top-2) > div > div > h3,
|
|
3132
|
+
html[data-mode] section[data-sorting]:has(.absolute.end-14.top-2) > div > div.flex:has(> h3) {
|
|
3133
|
+
padding-inline-end: 70px !important;
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
/* the help button goes to the end of the row */
|
|
3137
|
+
html[data-mode] section[data-sorting] > div > div.flex:has(> h3) > button {
|
|
3138
|
+
margin-inline-start: auto !important;
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
/* the "+" stops overflowing: 28px centered in the band, flush to the left of the handle */
|
|
3142
|
+
html[data-mode] section[data-sorting] .absolute.end-14.top-2 {
|
|
3143
|
+
top: 4px !important;
|
|
3144
|
+
inset-inline-end: 38px !important;
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
/* all three, same box and same hover */
|
|
3148
|
+
html[data-mode] section[data-sorting] > div > div.flex:has(> h3) > button,
|
|
3149
|
+
html[data-mode] section[data-sorting] .absolute.end-14.top-2,
|
|
3150
|
+
html[data-mode] section[data-sorting="false"] > button[data-sortable-handle] {
|
|
3151
|
+
display: grid !important;
|
|
3152
|
+
place-items: center !important;
|
|
3153
|
+
width: 28px !important;
|
|
3154
|
+
height: 28px !important;
|
|
3155
|
+
min-height: 0 !important;
|
|
3156
|
+
padding: 0 !important;
|
|
3157
|
+
border-radius: 3px !important;
|
|
3158
|
+
background: transparent !important;
|
|
3159
|
+
color: var(--wp-muted) !important;
|
|
3160
|
+
-webkit-transition: background-color var(--wp-fast) ease, color var(--wp-fast) ease;
|
|
3161
|
+
transition: background-color var(--wp-fast) ease, color var(--wp-fast) ease;
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
html[data-mode] section[data-sorting] > div > div.flex:has(> h3) > button:hover,
|
|
3165
|
+
html[data-mode] section[data-sorting] .absolute.end-14.top-2:hover,
|
|
3166
|
+
html[data-mode] section[data-sorting="false"] > button[data-sortable-handle]:hover {
|
|
3167
|
+
background: var(--wp-line-soft) !important;
|
|
3168
|
+
color: var(--wp-text) !important;
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
|
|
3172
|
+
/* ==========================================================================
|
|
3173
|
+
[22] (EMPTY) The block that used to hide the legacy migration fields lived here.
|
|
3174
|
+
|
|
3175
|
+
On 2026-09-08, in the same session it was born, the five fields were REALLY DROPPED:
|
|
3176
|
+
`category`, `tags`, `author`, `image` and `image_alt` on `posts`, and `image` on
|
|
3177
|
+
`areas`. What was blocking the cleanup was the risk of publishing a historical revision after the
|
|
3178
|
+
DROP ("no such column", content.ts:1931). Solved at the root, with a backup taken first:
|
|
3179
|
+
UPDATE revisions SET data = json_remove(data, '$.category','$.tags','$.author',
|
|
3180
|
+
'$.image','$.image_alt') WHERE collection IN ('posts','areas'); -- 9 rows
|
|
3181
|
+
Zero revisions with a legacy key after that, so EmDash's field DELETE ran without
|
|
3182
|
+
leaving an armed bomb behind. Do not resurrect this block: if one of these fields reappears in the
|
|
3183
|
+
form, it's because it came back to the schema, and the fix belongs in the schema.
|
|
3184
|
+
========================================================================== */
|