PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / src / QuickEdit / quick-edit.css

quick-edit.css in Extendify 3.1.5, at src/QuickEdit/quick-edit.css

1,225 lines 47.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 @import "tailwindcss" source(none) important;
2 @import "../tailwind.css" source(none);
3 @source ".";
4
5 :root {
6 color-scheme: only light;
7 }
8
9 @theme {
10 /* Body-level bars/menus render outside WP admin's font context, so they
11 need an explicit system UI stack. */
12 --font-qe:
13 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
14 Cantarell, "Helvetica Neue", sans-serif;
15 }
16
17 /* Once a block is being edited in Quick Edit, the agent's selected-block
18 X-close (rendered as a div[role="button"] inside its portal at the cover's
19 top-center) reads as orphaned — the user moved past the agent's
20 block-selection step and now owns Cancel via the QE toolbar. Suppress it
21 while a QE canvas is mounted.
22
23 Wrapped in @layer utilities so this `!important` rule competes in the same
24 layer as the agent bundle's Tailwind `.extendify-agent .flex {
25 display: flex !important }`. Per the cascade-layer spec, important rules
26 in any declared layer beat important rules outside layers — without this
27 wrapping, the layered .flex !important would always win regardless of
28 selector specificity. */
29 @layer utilities {
30 /* no-prefix */
31 html:has(.extendify-quick-edit-canvas) #extendify-agent-dom-mount
32 div[role="button"] {
33 @apply hidden!;
34 }
35 }
36
37 /* Admin-bar Edit Mode pill. Lives outside our scope wrapper, so
38 each rule carries a `no-prefix` marker. */
39
40 /* no-prefix */
41 #wpadminbar #wp-admin-bar-extendify-quick-edit-toggle > a.ab-item {
42 @apply inline-flex! items-center! gap-[8px]!;
43 }
44
45 /* no-prefix */
46 #wpadminbar
47 #wp-admin-bar-extendify-quick-edit-toggle
48 .extendify-quick-edit-toggle-pill {
49 @apply inline-block! relative! shrink-0! h-[16px]! w-[28px]! rounded-full! bg-[rgba(255,255,255,0.25)]!;
50 transition: background 0.18s ease !important;
51 }
52
53 /* no-prefix */
54 #wpadminbar
55 #wp-admin-bar-extendify-quick-edit-toggle
56 .extendify-quick-edit-toggle-thumb {
57 /* Literal color, not bg-white — the theme vars don't reach
58 #wpadminbar, so var-based utilities silently no-op here. */
59 @apply absolute! top-[2px]! left-[2px]! h-[12px]! w-[12px]! rounded-full! bg-[#fff]!;
60 transition: transform 0.18s ease !important;
61 }
62
63 /* On state — pill turns the AI Agent button's blue (not the partner
64 color), thumb slides right. */
65 /* no-prefix */
66 html.extendify-quick-edit-on #wpadminbar
67 #wp-admin-bar-extendify-quick-edit-toggle
68 .extendify-quick-edit-toggle-pill {
69 @apply bg-[#3858e9]!;
70 }
71
72 /* no-prefix */
73 html.extendify-quick-edit-on #wpadminbar
74 #wp-admin-bar-extendify-quick-edit-toggle
75 .extendify-quick-edit-toggle-thumb {
76 @apply translate-x-[12px]!;
77 }
78
79 /* Above WP admin bar (z 100000). */
80 #extendify-quick-edit-root {
81 @apply relative z-[100001];
82 }
83
84 /* The agent's highlighter-mode CSS sets pointer-events: none on
85 page-wide buttons/links so the selector-tool can capture clicks.
86 Re-enable them inside our UI surfaces. `html` prefix is required
87 to win specificity against the agent's `!important` rule.
88
89 The canvas inclusion is what keeps the LinkControl popover (and
90 any other BlockTools-slotted popover) clickable when the agent
91 sidebar is open — `BlockTools` puts its popover Slot inside the
92 canvas, so the popover renders inside `.wp-site-blocks`, in the
93 highlighter-mode scope. */
94 /* no-prefix */
95 html .extendify-quick-edit-bar,
96 html .extendify-quick-edit-bar *,
97 html .extendify-quick-edit-floating-bar,
98 html .extendify-quick-edit-floating-bar *,
99 html .extendify-quick-edit-image-menu,
100 html .extendify-quick-edit-image-menu *,
101 html .extendify-quick-edit-canvas,
102 html .extendify-quick-edit-canvas * {
103 @apply pointer-events-auto!;
104 }
105
106 /* Restore pointer-events on `.wp-block-button` wrapper divs inside
107 highlighter-mode. The agent ships a secondary rule (in agent.css) that
108 drops pointer-events on `.wp-block-button` whenever the parent
109 `.wp-block-buttons` contains exactly one button:
110 .extendify-agent-highlighter-mode
111 .wp-block-buttons:has(> .wp-block-button):not(:has(>
112 .wp-block-button:nth-of-type(2))) > .wp-block-button
113 That selector's specificity is (0,6,0) once `:has` / `:not` are scored,
114 so a plain `html.extendify-quick-edit-on .extendify-agent-highlighter-mode
115 .wp-block-button` rescue loses the cascade even with !important. Mirror
116 the same `:has` / `:not` chain to outweigh it; the rescue still fires only
117 when the agent's rule fires, which is exactly the bug scope.
118
119 The inner `<a class="wp-block-button__link">` still has pointer-events:none
120 from the broader `.extendify-agent-highlighter-mode a` rule, so link-nav
121 prevention is intact — we only restore the wrapper div so hover/click
122 targeting resolves to the button via QE's findTagged walk-up. */
123 /* no-prefix */
124 html.extendify-quick-edit-on .extendify-agent-highlighter-mode
125 .wp-block-buttons:has(> .wp-block-button):not(
126 :has(> .wp-block-button:nth-of-type(2))
127 )
128 > .wp-block-button {
129 @apply pointer-events-auto!;
130 }
131
132 /* Cursor contract for edit mode. Tagged blocks read as selectable
133 (crosshair); anchors and form controls inside them keep their native
134 cursors so the click rule (lib/click-rule.js) telegraphs which
135 branch a click will hit before the user clicks. Scoped to
136 `html.extendify-quick-edit-on` so cursors don't bleed into normal
137 browsing. */
138 /* no-prefix */
139 html.extendify-quick-edit-on [data-extendify-agent-block-id],
140 html.extendify-quick-edit-on [data-extendify-part-block-id],
141 html.extendify-quick-edit-on [data-extendify-quick-edit-product-id],
142 html.extendify-quick-edit-on [data-extendify-quick-edit-wpform-field-id] {
143 @apply cursor-crosshair;
144 }
145 /* no-prefix */
146 html.extendify-quick-edit-on [data-extendify-agent-block-id] a[href],
147 html.extendify-quick-edit-on [data-extendify-part-block-id] a[href],
148 html.extendify-quick-edit-on [data-extendify-quick-edit-product-id] a[href],
149 html.extendify-quick-edit-on
150 [data-extendify-quick-edit-wpform-field-id]
151 a[href] {
152 @apply cursor-pointer;
153 }
154 /* `revert` restores the UA-default cursor — text on inputs/textarea,
155 default/pointer on select/button — so form controls inside tagged
156 blocks still feel like form controls. */
157 /* no-prefix */
158 html.extendify-quick-edit-on [data-extendify-agent-block-id]
159 :is(input, textarea, select, button),
160 html.extendify-quick-edit-on
161 [data-extendify-part-block-id]
162 :is(input, textarea, select, button),
163 html.extendify-quick-edit-on
164 [data-extendify-quick-edit-product-id]
165 :is(input, textarea, select, button),
166 html.extendify-quick-edit-on
167 [data-extendify-quick-edit-wpform-field-id]
168 :is(input, textarea, select, button) {
169 @apply cursor-[revert];
170 }
171
172 /* keyboard-entry.js adds tabindex="0" to every tagged block so Tab can
173 walk between blocks for accessibility. Side effect: a mouse click on
174 a tagged block focuses it, the browser paints its :focus-visible
175 ring (1px solid blue in Chrome), and the ring lingers on after the
176 QE click-rule / Esc clears the committed selection — because focus
177 stays on the element until something else takes it. The hover bar's
178 showBar (driven by both mouseover AND focusin) already paints the
179 dashed outline as the visual focus indicator, so the browser ring
180 is both redundant and visually conflicting. Suppress it on every
181 tagged-block flavour while edit mode is on. */
182 /* no-prefix */
183 html.extendify-quick-edit-on [data-extendify-agent-block-id]:focus,
184 html.extendify-quick-edit-on [data-extendify-agent-block-id]:focus-visible,
185 html.extendify-quick-edit-on [data-extendify-part-block-id]:focus,
186 html.extendify-quick-edit-on [data-extendify-part-block-id]:focus-visible,
187 html.extendify-quick-edit-on [data-extendify-quick-edit-product-id]:focus,
188 html.extendify-quick-edit-on
189 [data-extendify-quick-edit-product-id]:focus-visible,
190 html.extendify-quick-edit-on [data-extendify-quick-edit-wpform-field-id]:focus,
191 html.extendify-quick-edit-on
192 [data-extendify-quick-edit-wpform-field-id]:focus-visible {
193 @apply outline-none;
194 }
195
196 /* WP's `<InputControl>` (and `<TextControl>` via it) layers a visual
197 `__backdrop` div over the input element to draw the focus border.
198 By WP design it carries `pointer-events: none` so clicks pass through
199 to the input behind. Our broader `.extendify-quick-edit-canvas *`
200 rule above forces every descendant to `pointer-events: auto`, which
201 re-enables the backdrop and makes it eat real-user clicks on the
202 LinkControl URL input (the "click does nothing" symptom). Pin the backdrop back to none — scoped to the LinkControl
203 shell + the QE canvas's WP popovers so we don't touch input-controls
204 in unrelated UI (the AI Agent's chat input, WP admin forms, etc.). */
205 /* no-prefix */
206 html.extendify-quick-edit-on .block-editor-link-control
207 .components-input-control__backdrop,
208 html.extendify-quick-edit-on
209 .extendify-quick-edit-canvas
210 .components-popover
211 .components-input-control__backdrop {
212 @apply pointer-events-none!;
213 }
214
215 /* The tagged-descendant cursor rules above scope by `[data-extendify-*]`
216 ancestor. WP's LinkControl popover (Quick Edit canvas → BlockTools →
217 Popover.Slot) can end up portaled inside a tagged element when the
218 live block's parent is itself a tagged container; the form-control
219 override on `:is(input, ...)` matches the `<input>` itself, but
220 WP wraps the input in `.components-input-control` + similar divs
221 that catch hover events around the input edge and read as crosshair.
222 Pin the cursor on the popover root + every interactive descendant
223 so the LinkControl always feels like a form, regardless of which
224 tagged ancestor it lands under. */
225 /* no-prefix */
226 html.extendify-quick-edit-on .block-editor-link-control,
227 html.extendify-quick-edit-on .block-editor-link-control *,
228 html.extendify-quick-edit-on .extendify-quick-edit-canvas .components-popover,
229 html.extendify-quick-edit-on
230 .extendify-quick-edit-canvas
231 .components-popover
232 * {
233 @apply cursor-[revert];
234 }
235 html.extendify-quick-edit-on .block-editor-link-control input,
236 html.extendify-quick-edit-on .block-editor-link-control textarea,
237 html.extendify-quick-edit-on
238 .extendify-quick-edit-canvas
239 .components-popover
240 input,
241 html.extendify-quick-edit-on
242 .extendify-quick-edit-canvas
243 .components-popover
244 textarea {
245 @apply cursor-text;
246 }
247
248 /* The `cursor: revert` block above reverts buttons inside
249 the LinkControl popover (pencil / unlink / copy) to the UA default,
250 which is the arrow cursor — not pointer. WP's `.components-button`
251 normally sets `cursor: pointer` but our blanket revert wins. Pin
252 pointer on the link-control's `<button>` and `<a>` (URL preview)
253 so every interactive element in the popover feels clickable. */
254 /* no-prefix */
255 html.extendify-quick-edit-on .block-editor-link-control button,
256 html.extendify-quick-edit-on .block-editor-link-control a,
257 html.extendify-quick-edit-on
258 .extendify-quick-edit-canvas
259 .components-popover
260 button,
261 html.extendify-quick-edit-on
262 .extendify-quick-edit-canvas
263 .components-popover
264 a {
265 @apply cursor-pointer;
266 }
267
268 /* The URL preview (`.block-editor-link-control__preview-title`,
269 an `<a>` rendered by `<ExternalLink>`) renders as white text on the
270 popover's white background when the QE canvas hosts a button block —
271 the canvas inherits Extendable's button-text color (white) and the
272 popover anchor picks it up via `color: inherit` somewhere in the
273 ExternalLink chain. `!important` wins the cascade unambiguously;
274 the rule is already QE-scoped to `html.extendify-quick-edit-on` so
275 it can't bleed into other surfaces. */
276 /* no-prefix */
277 html.extendify-quick-edit-on .block-editor-link-control__preview-title,
278 html.extendify-quick-edit-on .block-editor-link-control__preview-title:hover,
279 html.extendify-quick-edit-on .block-editor-link-control__preview-title:focus,
280 html.extendify-quick-edit-on .block-editor-link-control__preview-title *,
281 html.extendify-quick-edit-on .block-editor-link-control__preview-title:hover *,
282 html.extendify-quick-edit-on .block-editor-link-control__preview-title:focus * {
283 /* Literal color — the theme vars don't reach this portaled popover. */
284 @apply text-[#1e1e1e]! opacity-100!;
285 }
286
287 /* Hide core's developer-facing "Additional CSS class(es)" row (hardcoded, no
288 filter to remove it) — the only Advanced setting rendered in a fieldset. */
289 /* no-prefix */
290 html.extendify-quick-edit-on .block-editor-link-control__settings
291 .block-editor-link-control__setting:has(> fieldset) {
292 @apply hidden!;
293 }
294
295 /* Hover bar (plain DOM, see lib/hover-bar.js). Body-scoped so
296 every rule needs `no-prefix`. The ::before bridges the gap between
297 bar and block so the cursor can cross without losing hover. */
298
299 /* The bar carries `pointer-events: auto` so its ::before hover bridge
300 catches the cursor when traversing block → pill (with
301 `none`, cursor passed under the bar and would resolve to whatever
302 tagged ancestor was below, sometimes an unsupported one — the bar
303 then cleared mid-gesture). Wheel events on the bar are forwarded to
304 the page scroller via a JS handler in hover-bar.js#renderBar — pills
305 stay clickable AND the page still scrolls when the cursor is over
306 the bar. */
307 /* no-prefix */
308 .extendify-quick-edit-bar {
309 @apply pointer-events-auto fixed z-high inline-flex max-w-none flex-row flex-nowrap gap-[6px] p-0 whitespace-nowrap;
310 font-family:
311 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
312 Cantarell, "Helvetica Neue", sans-serif;
313 }
314
315 /* Transparent 12px bridge to the block (above or below the bar)
316 so the cursor can cross the gap without losing hover. */
317 /* no-prefix */
318 .extendify-quick-edit-bar::before {
319 @apply absolute left-0 right-0 h-[12px] bg-transparent;
320 content: "";
321 }
322
323 /* no-prefix */
324 .extendify-quick-edit-bar[data-extendify-quick-edit-placement="above"]::before {
325 @apply -bottom-[12px];
326 }
327
328 /* no-prefix */
329 .extendify-quick-edit-bar[data-extendify-quick-edit-placement="below"]::before {
330 @apply -top-[12px];
331 }
332
333 /* When the bar is pinned inside a viewport-filling block (e.g. a
334 hero Cover) it already overlaps the block — no bridge needed. */
335 /* no-prefix */
336 .extendify-quick-edit-bar[data-extendify-quick-edit-placement="inside"]::before {
337 @apply hidden;
338 }
339
340 /* no-prefix */
341 .extendify-quick-edit-pill {
342 @apply inline-flex cursor-pointer items-center gap-[6px] rounded-full border-0 bg-gray-800 px-[12px] py-[6px] text-[12px] font-medium leading-[1.4] text-white;
343 box-shadow:
344 0 4px 12px rgba(15, 23, 42, 0.25),
345 0 0 0 1px rgba(255, 255, 255, 0.15);
346 transition:
347 background 0.15s ease,
348 transform 0.15s ease,
349 box-shadow 0.15s ease;
350 }
351
352 /* no-prefix */
353 .extendify-quick-edit-pill:hover {
354 @apply -translate-y-px bg-gray-900;
355 box-shadow:
356 0 6px 16px rgba(15, 23, 42, 0.35),
357 0 0 0 1px rgba(255, 255, 255, 0.2);
358 }
359
360 /* no-prefix */
361 .extendify-quick-edit-pill:focus-visible {
362 @apply outline-offset-[3px];
363 outline: 2px solid var(--color-design-main);
364 }
365
366 /* no-prefix */
367 .extendify-quick-edit-pill-close {
368 @apply px-[9px] text-[13px] leading-none;
369 }
370
371 /* Hidden rather than removed: the bar would change width on every pin. */
372 /* no-prefix */
373 .extendify-quick-edit-pill-close:disabled {
374 @apply invisible;
375 }
376
377 /* no-prefix */
378 .extendify-quick-edit-pill-ai {
379 background: #3858e9;
380 }
381
382 /* no-prefix */
383 .extendify-quick-edit-pill-ai:hover {
384 background: #2145e6;
385 }
386
387 /* Hover outline — drawn as a body-level `position: fixed` overlay,
388 NOT as a class on the block element itself. See
389 `lib/hover-bar.js#ensureOutline` for the why: putting the outline
390 on the block puts it inside whatever ancestor `overflow: hidden`
391 clip region the theme establishes, so it gets cropped on full-
392 width pattern containers (the user's report: "only see it on the
393 top"). Body-level fixed sidesteps that.
394
395 Visual style mirrors the Agent's DOMHighlighter so hover and
396 selection feel like the same UI:
397 - Theme primary color (--wp--preset--color--primary) instead
398 of a hardcoded blue — matches whatever the partner brand is
399 set to in Site Editor → Styles → Colors.
400 - 4px dashed stroke instead of 2px solid — chunkier, reads
401 more clearly as "this is selectable."
402 - mix-blend-mode: hard-light — keeps the stroke visible
403 against any background (light hero / dark cover / patterned).
404 - Spring-like transition via cubic-bezier with a slight
405 overshoot — approximates framer-motion's spring without the
406 runtime cost of importing framer-motion just for this.
407 Coefficients picked to match the agent's
408 (stiffness: 700, damping: 40, mass: 0.25) feel — quick
409 expand, gentle settle. */
410 /* no-prefix */
411 .extendify-quick-edit-hover-outline {
412 /* z-index 99997: below hover bar (99999) but above page content */
413 @apply pointer-events-none fixed z-[99997] box-border rounded-[4px] opacity-0 outline-offset-0;
414 outline: 4px dashed var(--wp--preset--color--primary, #3b82f6);
415 /* Subtle overshoot — `cubic-bezier(0.34, 1.4, 0.64, 1)` was too
416 bouncy in practice (peak ~140% of final). Tightened to ~108%
417 peak — feels alive without obviously springing. */
418 transition:
419 top 0.2s cubic-bezier(0.34, 1.08, 0.64, 1),
420 left 0.2s cubic-bezier(0.34, 1.08, 0.64, 1),
421 width 0.2s cubic-bezier(0.34, 1.08, 0.64, 1),
422 height 0.2s cubic-bezier(0.34, 1.08, 0.64, 1),
423 opacity 0.1s ease-out;
424 }
425 /* Matches the agent's selected outline (see lib/over-media.js). */
426 /* no-prefix */
427 .extendify-quick-edit-hover-outline.is-over-media {
428 box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.2);
429 }
430 /* no-prefix */
431 .extendify-quick-edit-hover-outline.is-visible {
432 @apply opacity-100;
433 }
434
435 /* wp.media frame: lock to a single tab + hide filters/details so
436 the modal is just "pick an image". Scoped to the QE-only mode class
437 (added to `frame.modal.$el` in InlineEditor.jsx) rather than `body` so
438 frames opened by the AI Agent / other features aren't affected — the
439 mode class is added by QE alone, so it's the discriminator on its own.
440 It lands on wp.media's outer modal wrapper, which carries no
441 `.media-modal` class of its own; the old `.media-modal.*` prefix
442 therefore matched nothing and the chrome leaked back in. */
443 /* no-prefix */
444 .extendify-quick-edit-mode-browse .media-frame-router,
445 .extendify-quick-edit-mode-upload .media-frame-router {
446 @apply hidden!;
447 }
448
449 /* no-prefix */
450 .extendify-quick-edit-mode-browse .media-frame-content
451 .attachments-browser
452 .media-toolbar,
453 .extendify-quick-edit-mode-upload
454 .media-frame-content
455 .attachments-browser
456 .media-toolbar {
457 @apply hidden!;
458 }
459 /* no-prefix */
460 .extendify-quick-edit-mode-browse .media-frame-content .media-sidebar,
461 .extendify-quick-edit-mode-upload .media-frame-content .media-sidebar {
462 @apply hidden!;
463 }
464 /* wp.media still reserves absolute offsets for the chrome hidden above;
465 core's own `hide-router` class is no use — MediaFrame re-toggles it
466 off on every state change. */
467 /* no-prefix */
468 .extendify-quick-edit-mode-browse .media-frame-content,
469 .extendify-quick-edit-mode-upload .media-frame-content {
470 @apply top-[50px]!;
471 }
472 /* no-prefix */
473 .extendify-quick-edit-mode-browse .attachments-browser:not(.has-load-more)
474 .attachments,
475 .extendify-quick-edit-mode-browse
476 .attachments-browser.has-load-more
477 .attachments-wrapper,
478 .extendify-quick-edit-mode-browse .attachments-browser .uploader-inline,
479 .extendify-quick-edit-mode-upload
480 .attachments-browser:not(.has-load-more)
481 .attachments,
482 .extendify-quick-edit-mode-upload
483 .attachments-browser.has-load-more
484 .attachments-wrapper,
485 .extendify-quick-edit-mode-upload .attachments-browser .uploader-inline {
486 /* Arbitrary [0px], not top-0/right-0 — spacing utilities compile to
487 calc(var(--spacing)*0) and the var doesn't reach this body-level
488 modal, so the rule would silently no-op. */
489 @apply top-[0px]! right-[0px]!;
490 }
491
492 /* While an upload is in flight, cover wp.media's content with our own
493 loading state so the library/gallery view doesn't flash mid-upload.
494 Modal-scoped — without that, this overlay painted a white sheet over
495 the AI Agent's media frame too, which read as "screen goes blank"
496 the instant the agent's media library responded to a click. */
497 /* no-prefix */
498 .extendify-quick-edit-media-uploading .media-frame-content::after {
499 @apply absolute inset-[0px] z-[5] bg-[#fff];
500 content: "";
501 }
502 /* no-prefix */
503 .extendify-quick-edit-media-uploading .media-frame-content::before {
504 @apply absolute top-1/2 left-1/2 z-[6] mr-[0px] mb-[0px] -mt-[16px] -ml-[16px] h-[32px] w-[32px] rounded-full border-[3px] border-[#ddd] border-t-[color:var(--ext-design-main,#2271b1)];
505 content: "";
506 animation: extendify-quick-edit-media-uploading-spin 0.8s linear infinite;
507 }
508 /* no-prefix */
509 @keyframes extendify-quick-edit-media-uploading-spin {
510 to {
511 transform: rotate(360deg);
512 }
513 }
514
515 /* Offset the wp.media modal past the Agent sidebar (which has
516 `inert` removed while open). */
517 /* no-prefix */
518 html:has(#extendify-agent-sidebar:not([inert])) .media-modal-backdrop {
519 @apply left-[384px]!;
520 }
521 /* no-prefix */
522 html:has(#extendify-agent-sidebar:not([inert])) .media-modal {
523 @apply left-[calc(384px+30px)]!;
524 }
525
526 /* BlockEditor-based text editor (BlockTextEditor.jsx). */
527
528 /* Hide every Ariakit tooltip portal while a QE canvas is mounted.
529 Many rounds of trying to land both 36px-custom-button
530 tooltips and 48px-BlockToolbar-button tooltips in a clear spot
531 below the bar (pointer-events tricks, top placement, height
532 equalization, Floating-UI-aware transforms) all hit one or the
533 other case. The dev's call: drop the tooltips here and revisit
534 later.
535
536 Scope is `body:has(.extendify-quick-edit-floating-bar)` so this
537 ONLY hides tooltips while a QE canvas is mounted. Outside an
538 active editing session — AI Agent sidebar, WP admin chrome,
539 other plugins' tooltips — is completely untouched. Inside a
540 session, LinkControl + other in-canvas popovers share this
541 portal too and lose their tooltips alongside the bar's, which
542 is fine for the editing-session contract.
543
544 Each trigger keeps its `aria-label` so screen readers still
545 announce. */
546 /* no-prefix */
547 body:has(.extendify-quick-edit-floating-bar) [id^="portal/tooltip"] {
548 @apply hidden;
549 }
550
551 /* no-prefix */
552 .extendify-quick-edit-floating-bar {
553 @apply pointer-events-auto;
554 }
555
556 /* The portaled BlockToolbar ships with extra Gutenberg chrome we
557 don't want — block transform menu, block settings 3-dots,
558 parent-selector, and the format-toolbar's "More" overflow chevron
559 (which surfaces Highlight / Inline code / Strikethrough / etc.).
560 With our custom Text-color + Highlight buttons rendered alongside
561 the toolbar, the More overflow is redundant — and it kept slipping
562 in alongside our buttons making the toolbar look cluttered. Hide
563 it. */
564 /* no-prefix */
565 .extendify-quick-edit-floating-bar-inner .block-editor-block-mover,
566 .extendify-quick-edit-floating-bar-inner .block-editor-block-parent-selector,
567 .extendify-quick-edit-floating-bar-inner
568 .block-editor-block-toolbar__group-collapsed-toolbar,
569 /* Block-level settings 3-dot menu */
570 .extendify-quick-edit-floating-bar-inner .block-editor-block-settings-menu,
571 /* Block transform / switcher button (the "Heading" icon you can click
572 to convert to another block type — out of scope for Quick Edit) */
573 .extendify-quick-edit-floating-bar-inner .block-editor-block-switcher,
574 /* The chevron that surfaces overflow toolbar groups. */
575 .extendify-quick-edit-floating-bar-inner .block-editor-block-toolbar__group-collapsed-button,
576 /* RichText format toolbar's "More" overflow toggle (down arrow). Hide its
577 dropdown wrapper too — left visible, the 0-width wrapper still counts as
578 a flex item and costs one phantom gap after the last format button. */
579 .extendify-quick-edit-floating-bar-inner [aria-label="More"],
580 .extendify-quick-edit-floating-bar-inner
581 .components-dropdown-menu:has(> [aria-label="More"]) {
582 @apply hidden!;
583 }
584
585 /* no-prefix */
586 .extendify-quick-edit-floating-bar-inner
587 .block-editor-block-contextual-toolbar::before {
588 @apply absolute left-0 top-1/2 h-[14px] w-px -translate-y-1/2 bg-gray-300;
589 content: "";
590 }
591 /* BlockToolbar wrapper owns the layout↔format divider on its left, 12px each side. */
592 /* no-prefix */
593 .extendify-quick-edit-floating-bar-inner
594 .block-editor-block-contextual-toolbar {
595 @apply relative mt-0! mr-0! mb-0! ml-[8px]! pt-0! pr-0! pb-0! pl-[12px]!;
596 }
597 /* core/button has no heading/alignment group, so the toolbar opens the
598 bar and its left divider reads as a stray leading `|`. */
599 /* no-prefix */
600 .extendify-quick-edit-floating-bar-inner
601 .block-editor-block-contextual-toolbar:first-child {
602 @apply ml-0! pl-0!;
603 }
604 /* no-prefix */
605 .extendify-quick-edit-floating-bar-inner
606 .block-editor-block-contextual-toolbar:first-child::before {
607 @apply hidden;
608 }
609
610 /* Strip BlockToolbar's own group dividers (border-right on each
611 .components-toolbar-group in Gutenberg). We deliberately don't add
612 our own dividers between BlockToolbar's internal groups: the format
613 toolbar leaves a thin invisible group after B/I/link (wrapping the
614 "More" overflow we hide via display:none on the chevron button) —
615 adding a `:not(:first-of-type)::before` rule for those internal
616 groups draws a stray divider right next to the colors group's own
617 divider. Easier and cleaner to treat the whole BlockToolbar as one
618 visual segment and only divide between BlockToolbar, colors, and
619 actions. */
620 /* no-prefix */
621 .extendify-quick-edit-floating-bar-inner .components-toolbar-group,
622 .extendify-quick-edit-floating-bar-inner .components-toolbar,
623 /* The portaled BlockToolbar's contextual-toolbar wrapper carries a core
624 `0 1px 0 0` bottom shadow (a faux border) that draws a line under our
625 pill. We provide our own pill shadow on the inner — strip the core one. */
626 .extendify-quick-edit-floating-bar-inner .block-editor-block-contextual-toolbar {
627 @apply border-l-0! border-r-0! shadow-none!;
628 }
629 /* Zero Gutenberg's per-group `padding: 0 6px` + -1px margin so the bar's own
630 `gap` owns spacing. Not the contextual-toolbar — it owns the divider above. */
631 /* no-prefix */
632 .extendify-quick-edit-floating-bar-inner .components-toolbar-group,
633 .extendify-quick-edit-floating-bar-inner .components-toolbar {
634 @apply p-0! m-0!;
635 }
636 /* Clamp Gutenberg's 48px toolbar chrome to the button height so the bar
637 hugs its row instead of floating the buttons in dead vertical space. */
638 /* no-prefix */
639 .extendify-quick-edit-floating-bar-inner .block-editor-block-contextual-toolbar,
640 .extendify-quick-edit-floating-bar-inner .block-editor-block-toolbar,
641 .extendify-quick-edit-floating-bar-inner .components-accessible-toolbar,
642 .extendify-quick-edit-floating-bar-inner .components-toolbar-group,
643 .extendify-quick-edit-floating-bar-inner .block-editor-block-toolbar__slot {
644 @apply h-[28px]! min-h-0!;
645 }
646 /* B/I/link get no gap from Gutenberg (group + inner slot), so they rendered
647 edge-to-edge; gap both to match the bar's rhythm. */
648 /* no-prefix */
649 .extendify-quick-edit-floating-bar-inner .components-toolbar-group,
650 .extendify-quick-edit-floating-bar-inner .block-editor-block-toolbar__slot {
651 @apply gap-[4px]!;
652 }
653
654 /* One box model for all buttons — custom heading/align/color + native
655 B/I/link — so Gutenberg's square, dark-pressed defaults match the pill. */
656 /* no-prefix */
657 .extendify-quick-edit-floating-bar-inner .components-button {
658 @apply min-w-[28px]! h-[28px]! px-[4px]! py-0! rounded-[6px]! bg-transparent! text-gray-900! shadow-none!;
659 }
660 /* Drop Gutenberg's inset ::before hover/pressed bar — it fights our
661 full-button rounded states. */
662 /* no-prefix */
663 .extendify-quick-edit-floating-bar-inner .components-button::before {
664 @apply hidden!;
665 }
666 /* no-prefix */
667 .extendify-quick-edit-floating-bar-inner
668 .components-button:hover:not(:disabled) {
669 @apply bg-[rgba(0,0,0,0.05)]!;
670 }
671 /* Active format (bold/italic/link) — a subtle filled state instead of
672 Gutenberg's near-black pressed background. */
673 /* no-prefix */
674 .extendify-quick-edit-floating-bar-inner .components-button.is-pressed,
675 .extendify-quick-edit-floating-bar-inner
676 .components-button.is-pressed:hover:not(:disabled) {
677 @apply bg-[rgba(15,23,42,0.1)]! text-gray-900!;
678 }
679
680 /* no-prefix */
681 .extendify-quick-edit-text-align-popover .components-popover__content {
682 @apply p-[6px] min-w-[auto];
683 }
684
685 /* no-prefix */
686 .extendify-quick-edit-text-align-popover-inner {
687 @apply flex flex-col gap-[2px];
688 }
689
690 /* no-prefix */
691 .extendify-quick-edit-text-align-popover
692 .extendify-quick-edit-text-align-option.components-button {
693 @apply h-[32px] justify-start gap-[8px] px-[10px] py-0 text-[13px] font-medium;
694 }
695
696 /* Box model + hover inherited from the shared .components-button rule above;
697 the heading button only adds its text label's weight/size + a bit more
698 horizontal padding for the "H2" / "H3" labels. */
699 /* no-prefix */
700 .extendify-quick-edit-floating-bar-inner
701 .extendify-quick-edit-heading-level-button.components-button {
702 @apply px-[6px]! py-0! text-[13px] font-semibold;
703 }
704
705 /* no-prefix */
706 .extendify-quick-edit-heading-level-popover .components-popover__content {
707 @apply p-[6px] min-w-[auto];
708 }
709
710 /* no-prefix */
711 .extendify-quick-edit-heading-level-popover-inner {
712 @apply inline-flex gap-[2px] p-[2px];
713 }
714
715 /* no-prefix */
716 .extendify-quick-edit-heading-level-popover
717 .extendify-quick-edit-heading-level-option.components-button {
718 @apply h-[32px] min-w-[36px] px-[8px] py-0 text-[13px] font-semibold;
719 }
720
721 /* Box model + hover inherited from the shared .components-button rule above. */
722
723 /* no-prefix */
724 .extendify-quick-edit-color-popover .components-popover__content {
725 @apply p-[12px] min-w-[220px];
726 }
727
728 /* no-prefix */
729 .extendify-quick-edit-color-popover-inner
730 .components-circular-option-picker__option-wrapper {
731 @apply m-[4px];
732 }
733
734 /* Explicit "Clear" button below the swatches — the ColorPalette's
735 built-in clear is easy to miss. */
736 /* no-prefix */
737 .extendify-quick-edit-color-popover
738 .extendify-quick-edit-color-clear.components-button {
739 @apply block w-full mt-[8px] px-[12px]! py-[8px]! border-t! border-t-gray-300! rounded-none! text-left! text-gray-700! font-medium!;
740 }
741 /* no-prefix */
742 .extendify-quick-edit-color-popover
743 .extendify-quick-edit-color-clear.components-button:hover {
744 @apply bg-gray-50! text-gray-900!;
745 }
746
747 /* no-prefix */
748 .extendify-quick-edit-host {
749 /* The host overlays the live block 1:1. Its position is set by
750 BlockTextEditor's align() effect; CSS just zeros margin so theme
751 blockGap doesn't push us.
752
753 The blue outline lives HERE (not on the inner canvas). The host
754 has its width / min-height set synchronously to match the live
755 block on insert, so its outline paints at full size on the very
756 first frame. The canvas inside is empty until blocks load —
757 putting the outline on the canvas previously meant a 0-height
758 "narrow stripe" between insert and content load.
759
760 `outline-offset: 0` (flush with edge) is deliberate. With +4px,
761 the outline extends outside the host by 4px — for full-width
762 pattern containers at the page edge, those 4px land in
763 `body`/`html`'s `overflow-x: hidden` clip region, and the
764 outline disappears on the bottom + sides ("only see it on the
765 top," as the user reported). Flush with the edge keeps the
766 outline inside the host's bounds and visible everywhere. */
767 @apply m-0! rounded-[4px] outline-offset-0;
768 outline: 2px solid
769 color-mix(
770 in srgb,
771 var(--wp--preset--color--primary, #3b82f6) 70%,
772 transparent
773 );
774 }
775
776 /* no-prefix */
777 .extendify-quick-edit-canvas {
778 @apply pointer-events-auto;
779 }
780
781 /* While the rich-text editor is focused, transition the outline to
782 fully opaque so the active edit state is unambiguous. */
783 /* no-prefix */
784 .extendify-quick-edit-host:focus-within {
785 outline-color: var(--wp--preset--color--primary, #3b82f6);
786 }
787
788 /* Pin link / button-link color in the canvas to the live element's resting
789 value (snapshot into --qe-link-color in BlockTextEditor.jsx). Without this,
790 the theme's :hover on .wp-block-button__link fires as soon as the user's
791 cursor sits over the text they're editing, flipping the button text to
792 the hover color while the background stays non-hover — broken-looking.
793 Falls back to currentColor when no link is in the live block (e.g. a
794 plain heading or paragraph), making the rule a no-op. */
795 /* no-prefix */
796 .extendify-quick-edit-host a:hover,
797 .extendify-quick-edit-host .wp-block-button__link:hover,
798 .extendify-quick-edit-host .wp-block-button:hover .wp-block-button__link {
799 @apply text-[var(--qe-link-color,currentColor)]!;
800 }
801
802 /* BlockEditor's BlockList adds margin-top: 16px to its child blocks
803 (and 40.2px bottom). When our canvas is positioned exactly over
804 the live block, those margins push the rendered block 16px below
805 where the live one sits — the user sees the heading drift down on
806 click. Zero out the margins for the block immediately inside our
807 canvas so the rendered block top lines up with the live element.
808
809 Also zero PADDING on the block list + block wrappers — the editor
810 stylesheet adds horizontal padding (for the inserter chip / block-
811 selector chrome) that narrows the inner text's effective width, so
812 the same heading wraps at a different word boundary than the live
813 element (line-break parity). With padding zeroed and the
814 host's width matching liveRect.width, the text wraps at the same
815 word as the live element. */
816 /* no-prefix */
817 .extendify-quick-edit-canvas .block-editor-block-list__layout,
818 .extendify-quick-edit-canvas .block-editor-block-list__block {
819 @apply mt-0! mb-0! px-0!;
820 }
821
822 /* core/text-color wraps its target text in `<mark>`. Browsers AND many
823 themes give `<mark>` a yellow background by default; combined with our
824 text-color format's inline `background-color: transparent` (set when
825 only text color is picked), users sometimes still saw a yellow band
826 around their colored text — "changing one color changed
827 both". Neutralize the background in our canvas. Inline
828 `background-color: <color>` (set when the user also picks a
829 highlight) wins via specificity and renders normally. */
830 /* no-prefix */
831 .extendify-quick-edit-canvas mark {
832 background: none;
833 }
834
835 /* Hide Gutenberg's own block-level selection / hover indicators
836 * inside our canvas. BlockList draws a faint gray outline (or a
837 * "::before" rect, depending on Gutenberg version) on the selected
838 * block — when stacked inside our blue canvas outline it reads as a
839 * confusing "double box." We want OUR outline to be the one and only
840 * editing-state cue; the contenteditable's blinking caret is enough
841 * to indicate where the cursor is, no extra block highlight needed. */
842 /* no-prefix */
843 .extendify-quick-edit-canvas .block-editor-block-list__block,
844 .extendify-quick-edit-canvas .block-editor-block-list__block.is-selected,
845 .extendify-quick-edit-canvas .block-editor-block-list__block.is-hovered,
846 .extendify-quick-edit-canvas .block-editor-block-list__block:focus,
847 .extendify-quick-edit-canvas .block-editor-rich-text__editable,
848 .extendify-quick-edit-canvas .block-editor-rich-text__editable:focus {
849 @apply outline-none! shadow-none!;
850 }
851 /* no-prefix */
852 .extendify-quick-edit-canvas .block-editor-block-list__block::before,
853 .extendify-quick-edit-canvas .block-editor-block-list__block::after {
854 @apply hidden!;
855 }
856
857 /* The BlockEditor canvas needs writing flow + its standard editor
858 styles to behave naturally (selection, format toolbar tracking).
859 We don't load editor-styles globally — these are minimal restorations
860 so contenteditable inside .block-editor-rich-text__editable works. */
861 /* no-prefix */
862 .extendify-quick-edit-canvas .block-editor-block-list__layout {
863 @apply cursor-text;
864 }
865
866 /* Force visibility of blocks with the Extendable `ext-animate--on`
867 class (and any element using its initial opacity:0 animation
868 pattern) INSIDE the canvas. The theme's animation JS only runs
869 on page-load scroll events; a freshly mounted BlockEditor block
870 never gets animated in, so opacity stays 0 and the user sees an
871 empty canvas. We deliberately keep the class on the block (it's
872 part of attrs.className and round-trips through serialize) and
873 override visibility cosmetically here.
874
875 `transform: none !important` is the matching half: the theme's
876 pre-animation rule is `.ext-animate--on:not(.ext-animate--off) {
877 transform: scale(0.8); opacity: 0; }`, and a sibling rule
878 `.ext-animate { transform: none; opacity: 1; }` flips both when
879 the JS adds `ext-animate` on scroll-into-view. Inside the canvas
880 the BlockEditor renders the parsed markup with `ext-animate--on`
881 but NOT `ext-animate` (that class is JS-added, not saved), so
882 without this override headings/paragraphs render at 80% scale
883 while the live element sits at scale 1 — the user reported this
884 as "the text gets a little smaller when I click Quick Edit." */
885 /* no-prefix */
886 .extendify-quick-edit-canvas .ext-animate--on,
887 .extendify-quick-edit-canvas .block-editor-block-list__block.ext-animate--on,
888 .extendify-quick-edit-canvas .ext-animate--on * {
889 @apply opacity-100! transform-none!;
890 }
891
892 /* Modals portal to <body>, so the no-prefix marker keeps these out of the
893 .extendify-quick-edit scope. The agent sidebar uses z-higher (999999),
894 so the modal overlay sits at 1_000_000 to stay on top when both are open. */
895
896 /* no-prefix */
897 .components-modal__screen-overlay:has(.extendify-quick-edit-modal) {
898 @apply z-[1000000]!;
899 }
900
901 /* When wp.media's frame opens on top of our modal (e.g. SiteIdentityModal's
902 "Change logo" → MediaUpload), our modal would otherwise sit on top
903 (z-index 1000000) and block clicks on the media library grid. Hide our
904 modal while wp.media is open; it reappears when the user picks an image
905 or closes the frame.
906
907 Trigger on `body.modal-open` — wp.media (legacy Backbone code) adds
908 that class directly on open and removes it on close. Round-3 used
909 `:has(.media-modal)`, which stayed true after wp.media closed (wp.media
910 hides via display:none and leaves the element in the DOM).
911
912 Our own QE modals MUST NOT trip this rule. @wordpress/components Modal
913 also adds `body.modal-open` on mount, so every QE modal hid itself on
914 first open. Each QE modal now passes
915 `bodyOpenClassName={QE_MODAL_BODY_OPEN_CLASS}` — see modal-root.js. */
916 /* no-prefix */
917 body.modal-open .components-modal__frame:has(.extendify-quick-edit-modal),
918 body.modal-open
919 .components-modal__screen-overlay:has(.extendify-quick-edit-modal) {
920 @apply invisible!;
921 }
922
923 /* ----------------------------------------------------------------
924 * Modal polish — keep visual language aligned with the auto-launch
925 * page + AI Agent so the user-perceived experience feels like one
926 * product. Agent's tokens:
927 * --ext-design-main primary brand (banner / primary buttons)
928 * --ext-design-secondary, --ext-design-tertiary (accents, less used)
929 *
930 * The agent's sidebar uses rounded-2xl + shadow-lg + bg-white; we
931 * mirror that on the modal frame so the modal feels like a natural
932 * extension of the agent's UI rather than a stock WP component.
933 *
934 * Falls back to neutral defaults when the partner data hasn't set
935 * the design vars (single-color sites, themes that don't import the
936 * Extendable design system). Keeps the modal looking polished even
937 * outside the auto-launch flow.
938 * ---------------------------------------------------------------- */
939
940 /* no-prefix */
941 .components-modal__frame:has(.extendify-quick-edit-modal),
942 .extendify-quick-edit-modal-root .components-modal__frame {
943 @apply overflow-hidden rounded-[16px]!;
944 box-shadow:
945 0 10px 15px -3px rgba(0, 0, 0, 0.12),
946 0 4px 6px -4px rgba(0, 0, 0, 0.08) !important;
947 }
948
949 /* no-prefix */
950 .components-modal__frame:has(.extendify-quick-edit-modal)
951 .components-modal__header {
952 @apply border-b border-b-gray-300 px-[24px] py-[18px];
953 }
954 /* no-prefix */
955 .components-modal__frame:has(.extendify-quick-edit-modal)
956 .components-modal__header-heading {
957 @apply text-[15px] font-semibold tracking-[-0.01em];
958 }
959 /* no-prefix */
960 .components-modal__frame:has(.extendify-quick-edit-modal)
961 .components-modal__content {
962 @apply px-[24px] pt-[18px] pb-[22px];
963 }
964
965 /* Primary save button uses the brand color so the action feels
966 continuous with the agent's "Send" pill / launch CTAs. */
967 /* no-prefix */
968 .extendify-quick-edit-modal-actions .components-button.is-primary {
969 @apply bg-[var(--ext-design-main,#2c39bd)]! shadow-none! rounded-[8px]! px-[14px]! py-[6px]! font-medium!;
970 }
971 /* no-prefix */
972 .extendify-quick-edit-modal-actions
973 .components-button.is-primary:hover:not(:disabled) {
974 @apply bg-[var(--ext-design-main,#2c39bd)]! brightness-[1.08];
975 }
976 /* no-prefix */
977 .extendify-quick-edit-modal-actions
978 .components-button.is-primary:focus:not(:disabled) {
979 box-shadow:
980 0 0 0 2px #fff,
981 0 0 0 4px var(--ext-design-main, #2c39bd) !important;
982 }
983 /* no-prefix */
984 .extendify-quick-edit-modal-actions .components-button.is-tertiary {
985 @apply rounded-[8px]! px-[12px]! py-[6px]! font-medium! text-gray-700!;
986 }
987 /* no-prefix */
988 .extendify-quick-edit-modal-actions
989 .components-button.is-tertiary:hover:not(:disabled) {
990 @apply bg-gray-100! text-gray-800!;
991 }
992
993 /* Tighter, consistent control rhythm inside modals — replaces
994 wp-components' default 24px gaps with 14px so the modal feels
995 compact and intentional, matching the agent's chat density. */
996 /* no-prefix */
997 .extendify-quick-edit-modal .components-base-control {
998 @apply mb-[14px];
999 }
1000 /* no-prefix */
1001 .extendify-quick-edit-modal .components-base-control__label {
1002 /* normal-case: WP defaults to uppercase via inheritance in some themes */
1003 @apply mb-[6px] text-[12px] font-semibold normal-case tracking-[0] text-gray-800;
1004 }
1005 /* no-prefix */
1006 .extendify-quick-edit-modal .components-text-control__input,
1007 .extendify-quick-edit-modal .components-textarea-control__input {
1008 @apply rounded-[8px] border border-gray-300;
1009 transition:
1010 border-color 0.12s ease,
1011 box-shadow 0.12s ease;
1012 }
1013 /* no-prefix */
1014 .extendify-quick-edit-modal .components-text-control__input:focus,
1015 .extendify-quick-edit-modal .components-textarea-control__input:focus {
1016 @apply border-[color:var(--ext-design-main,#2c39bd)]! outline-none!;
1017 box-shadow: 0 0 0 3px
1018 color-mix(in srgb, var(--ext-design-main, #2c39bd) 18%, transparent) !important;
1019 }
1020
1021 /* no-prefix */
1022 .extendify-quick-edit-modal-actions {
1023 @apply flex justify-end gap-[8px] mt-[18px] pt-[14px] border-t border-t-gray-300;
1024 }
1025
1026 /* no-prefix */
1027 .extendify-quick-edit-modal-label {
1028 @apply mt-[12px] mr-0 mb-[6px] ml-0 text-[11px] font-medium uppercase text-gray-700;
1029 }
1030
1031 /* no-prefix */
1032 .extendify-quick-edit-link-current {
1033 @apply mt-[8px] mr-0 mb-[12px] ml-0 rounded-[6px] bg-gray-50 px-[12px] py-[8px] text-[12px] text-gray-700;
1034 }
1035 /* no-prefix */
1036 .extendify-quick-edit-link-current code {
1037 @apply text-gray-900;
1038 }
1039
1040 /* LinkControl + format-toolbar popovers inside the canvas. WP's Popover
1041 writes the anchor's viewport rect into style.top/left and renders as
1042 `position: absolute`, which interprets those coords relative to the
1043 nearest positioned ancestor. In our setup that's
1044 `.extendify-quick-edit-host` (position: absolute, sized to track the
1045 live block), so the popover drifts by host.top/left and its width is
1046 capped by host.width — the user reads this as "edit box too short" +
1047 "hard to click where intended." Forcing position: fixed lets the
1048 coords land in viewport space and frees the popover from the canvas
1049 width. */
1050 /* no-prefix */
1051 .extendify-quick-edit-canvas .components-popover {
1052 @apply fixed!;
1053 }
1054
1055 /* Without a positioned offsetParent, floating-ui's coords ignore the admin
1056 bar's html margin-top and the popover renders 32px above its anchor. */
1057 /* no-prefix */
1058 .extendify-quick-edit-popover-slot {
1059 @apply relative;
1060 }
1061
1062 /* LinkControl in edit mode (forceIsEditingLink) renders its own Apply / Cancel
1063 below the URL input. Our modal already has Save / Cancel for that, and
1064 the user reads two action rows as broken — hide the inner one. The link
1065 value still propagates via the onChange we pass to LinkControl. */
1066 /* no-prefix */
1067 .extendify-quick-edit-modal-nav .block-editor-link-control__search-actions,
1068 .extendify-quick-edit-modal-nav .block-editor-link-control__search-submit,
1069 .extendify-quick-edit-modal-nav
1070 .block-editor-link-control__search-input-actions,
1071 .extendify-quick-edit-modal-nav
1072 .block-editor-link-control
1073 .block-editor-link-control__settings {
1074 @apply hidden!;
1075 }
1076
1077 /* no-prefix */
1078 .extendify-quick-edit-logo-row {
1079 @apply flex items-center gap-[16px] mt-[8px];
1080 }
1081
1082 /* no-prefix */
1083 .extendify-quick-edit-logo-preview {
1084 @apply max-h-[80px] max-w-[120px] rounded-[6px] border border-gray-300 bg-gray-50 object-contain p-[4px];
1085 }
1086
1087 /* no-prefix */
1088 .extendify-quick-edit-logo-buttons {
1089 @apply flex flex-col gap-[6px];
1090 }
1091
1092 /* Product price modal: currency-prefixed regular + sale inputs. */
1093 /* no-prefix */
1094 .extendify-quick-edit-price-fields {
1095 @apply flex flex-col gap-[12px];
1096 }
1097 /* no-prefix */
1098 .extendify-quick-edit-price-row {
1099 @apply flex items-end gap-[8px];
1100 }
1101 /* The modal-rhythm margin (above) would misalign input and prefix under
1102 items-end; the fields' gap handles spacing. */
1103 /* no-prefix */
1104 .extendify-quick-edit-price-row .components-base-control {
1105 @apply mb-0;
1106 }
1107 /* no-prefix */
1108 .extendify-quick-edit-price-prefix {
1109 /* 32px = the WP TextControl input height, so the symbol centers on it. */
1110 @apply flex h-[32px] items-center text-[14px] font-medium text-gray-700;
1111 }
1112
1113 /* AI generation + Unsplash image-picker modals */
1114
1115 /* no-prefix */
1116 .extendify-quick-edit-ai-form {
1117 @apply flex flex-col gap-[16px];
1118 }
1119
1120 /* no-prefix */
1121 .extendify-quick-edit-ai-generating {
1122 @apply inline-flex items-center gap-[8px] rounded-[6px] bg-gray-50 px-[12px] py-[8px] text-[13px] text-gray-700;
1123 }
1124
1125 /* no-prefix */
1126 .extendify-quick-edit-ai-credits {
1127 @apply text-[12px] text-gray-600 text-right;
1128 }
1129
1130 /* no-prefix */
1131 .extendify-quick-edit-ai-preview {
1132 @apply flex max-h-[60vh] items-center justify-center overflow-hidden rounded-[6px] bg-gray-50;
1133 }
1134 /* no-prefix */
1135 .extendify-quick-edit-ai-preview img {
1136 @apply max-h-[60vh] max-w-full object-contain;
1137 }
1138
1139 /* The padding-top:100% trick beats theme/WP button min-height rules
1140 that override aspect-ratio. */
1141 /* no-prefix */
1142 .extendify-quick-edit-image-grid {
1143 @apply grid grid-cols-3 gap-[8px] mt-[12px];
1144 /* No inner scroll — the WP Modal body already scrolls. Two
1145 scrollbars (modal + grid) was confusing and trapped the user
1146 on the inner scroller while the outer modal still had room. */
1147 }
1148 /* no-prefix */
1149 .extendify-quick-edit-image-grid-item-credit {
1150 @apply pointer-events-none absolute bottom-0 left-0 right-0 px-[8px] py-[6px] text-[11px] leading-[1.3] text-white opacity-0;
1151 background: linear-gradient(
1152 to top,
1153 rgba(0, 0, 0, 0.65) 0%,
1154 rgba(0, 0, 0, 0) 100%
1155 );
1156 transition: opacity 0.15s ease;
1157 }
1158 /* no-prefix */
1159 .extendify-quick-edit-image-grid-item:hover
1160 .extendify-quick-edit-image-grid-item-credit,
1161 .extendify-quick-edit-image-grid-item:focus-visible
1162 .extendify-quick-edit-image-grid-item-credit {
1163 @apply opacity-100;
1164 }
1165 /* no-prefix */
1166 .extendify-quick-edit-image-grid-item-credit a {
1167 @apply pointer-events-auto text-inherit underline;
1168 }
1169 /* no-prefix */
1170 .extendify-quick-edit-image-attribution {
1171 @apply mt-[12px] pt-[10px] border-t border-t-gray-300 text-[11px] text-gray-600;
1172 }
1173 /* no-prefix */
1174 .extendify-quick-edit-image-attribution a {
1175 @apply text-inherit underline;
1176 }
1177 /* no-prefix */
1178 .extendify-quick-edit-image-grid-item {
1179 /* min-h-0: defeat WP button-style min-height rules */
1180 @apply relative w-full min-h-0 cursor-pointer overflow-hidden rounded-[6px] border-0 bg-transparent p-0;
1181 transition: transform 0.12s ease;
1182 }
1183 /* no-prefix */
1184 .extendify-quick-edit-image-grid-item::before {
1185 @apply block pt-[100%];
1186 content: "";
1187 }
1188 /* no-prefix */
1189 .extendify-quick-edit-image-grid-item:hover:not(:disabled) {
1190 @apply scale-[1.02];
1191 }
1192 /* no-prefix */
1193 .extendify-quick-edit-image-grid-item:disabled {
1194 @apply cursor-not-allowed;
1195 }
1196 /* no-prefix */
1197 .extendify-quick-edit-image-grid-item img {
1198 @apply absolute inset-0 block h-full w-full object-cover;
1199 }
1200 /* no-prefix */
1201 .extendify-quick-edit-image-grid-item-overlay {
1202 @apply absolute inset-0 flex items-center justify-center bg-[rgba(0,0,0,0.45)];
1203 }
1204
1205 /* Reduced motion (a11y M7). Stop the infinite upload spinner and drop the
1206 hover outline's spring so it tracks instantly. All `!important`: the
1207 surrounding environment ships a global `prefers-reduced-motion` reset
1208 that forces a tiny non-zero duration (~0.01ms) onto everything, so these
1209 must win over it — and over the base rules — for Extendify's own
1210 reduced-motion intent to be authoritative rather than theme-dependent.
1211 Selectors are page-level (the body-level fixed outline, wp.media's
1212 modal), so they carry `no-prefix` like the originals. */
1213 @media (prefers-reduced-motion: reduce) {
1214 /* no-prefix */
1215 .extendify-quick-edit-media-uploading .media-frame-content::before {
1216 @apply animate-none!;
1217 }
1218 /* no-prefix */
1219 .extendify-quick-edit-hover-outline {
1220 /* Shorthand, not transition-none — only the shorthand zeroes the
1221 duration the global reset forces to 0.01ms. */
1222 transition: none !important;
1223 }
1224 }
1225