PluginProbe
Extendify / 3.1.0
Extendify v3.1.0
3.2.1 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 All 127 releases
extendify / src / QuickEdit / quick-edit.css

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

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