|
15 | 15 | */ |
16 | 16 |
|
17 | 17 | :where(.hamelp-ai-overview) { |
| 18 | + // Fill the available width in every layout context. WordPress flex Group |
| 19 | + // blocks default their children to size-to-content (align-items is not |
| 20 | + // `stretch`), which otherwise collapses the block to its intrinsic width when |
| 21 | + // placed alongside e.g. a disclaimer paragraph. width:100% keeps it full-width |
| 22 | + // in flex/grid parents while remaining a no-op in normal block flow. |
| 23 | + width: 100%; |
| 24 | + box-sizing: border-box; |
18 | 25 | // Theme-aware tokens (override these from your theme to re-skin the block). |
19 | 26 | --hamelp-accent: var(--wp--preset--color--primary, #1571da); |
20 | 27 | --hamelp-on-accent: var(--wp--preset--color--base, #fff); |
|
25 | 32 | // input + button look identical across both blocks). |
26 | 33 | --hamelp-radius: 3px; |
27 | 34 | --hamelp-font-weight: 600; |
| 35 | + // Send button size, fully fluid with NO @media breakpoint (avoids colliding |
| 36 | + // with the theme's breakpoints). Inverse clamp (negative vw coefficient) so |
| 37 | + // the touch target GROWS as the viewport shrinks: 44px on phones (≤360px) |
| 38 | + // easing down to 40px on wide screens (≥640px). Everything else (textarea |
| 39 | + // min-height, right padding, bottom padding) derives from this token so the |
| 40 | + // ChatGPT-style inset and single-line text position stay consistent at every |
| 41 | + // width without special-casing. |
| 42 | + --hamelp-btn-size: clamp(40px, calc(49.14px - 1.43vw), 44px); |
28 | 43 | --hamelp-button-bg: var(--hamelp-accent); |
29 | 44 | // Hover shade derived from the button color; the plain background |
30 | 45 | // declaration below wins on engines without color-mix() support. |
|
41 | 56 | } |
42 | 57 |
|
43 | 58 | :where(.hamelp-ai-overview__input-row) { |
| 59 | + // Positioning context for the send button, which is taken out of flow and |
| 60 | + // floated inside the field's bottom-right corner (ChatGPT-style inset). The |
| 61 | + // field's own border/background/radius stay driven by the textarea's theme |
| 62 | + // variables — this container adds no matte of its own. |
| 63 | + position: relative; |
| 64 | + // Still a flex row so the textarea fills the full width. |
44 | 65 | display: flex; |
45 | | - gap: 0.5rem; |
46 | 66 | } |
47 | 67 |
|
48 | 68 | // "Continue the previous conversation" toggle (conversation mode only). |
|
57 | 77 |
|
58 | 78 | :where(.hamelp-ai-overview__input) { |
59 | 79 | flex: 1; |
60 | | - padding: 0.75rem; |
| 80 | + box-sizing: border-box; |
| 81 | + // Reserve room on the right so typed text never runs under the inset send |
| 82 | + // button (right = btn size + 1rem to clear the button plus a small gap). The |
| 83 | + // vertical padding is SYMMETRIC (top/bottom both 0.75rem) so that, combined |
| 84 | + // with align-content: center below, the single line centers cleanly in the |
| 85 | + // content box regardless of the button size or the theme's line-height. |
| 86 | + padding: 0.75rem calc(var(--hamelp-btn-size) + 1rem) 0.75rem 0.75rem; |
61 | 87 | border: 1px solid var(--hamelp-border); |
62 | 88 | border-radius: var(--hamelp-radius, 3px); |
63 | 89 | background: var(--hamelp-surface); |
|
68 | 94 | font-family: inherit; |
69 | 95 | line-height: 1.5; |
70 | 96 | field-sizing: content; |
71 | | - min-height: 1lh; |
| 97 | + // Minimum height sized so the inset send button clears equal ~8px gaps on |
| 98 | + // every side in the single-line state: button height + 2 × the button's |
| 99 | + // corner inset (0.5rem each). field-sizing still grows the field past this |
| 100 | + // as text wraps, at which point the top gap widens. |
| 101 | + min-height: calc(var(--hamelp-btn-size) + 1rem); |
72 | 102 | max-height: 8lh; |
73 | 103 | resize: none; |
| 104 | + // Vertically CENTER the single line of text within the inflated min-height. |
| 105 | + // With symmetric vertical padding above, centering is metric-robust: the line |
| 106 | + // stays visually centered independent of the theme's font/line-height (a |
| 107 | + // bottom-aligned approach drifts too low under some themes). Once the content |
| 108 | + // exceeds min-height (multi-line), align-content has no effect, so wrapped |
| 109 | + // text still grows top-anchored as expected (no text under the inset button). |
| 110 | + align-content: center; |
74 | 111 | } |
75 | 112 |
|
76 | 113 | // Focus ring matches the button and the search box input (outline, 2px offset). |
|
79 | 116 | outline-offset: 2px; |
80 | 117 | } |
81 | 118 |
|
| 119 | +// Small muted helper text directly under the input (e.g. "Shift + Enter で改行"). |
| 120 | +// Muted via opacity so it inherits and adapts to any theme's text color. |
| 121 | +:where(.hamelp-ai-overview__hint) { |
| 122 | + margin: 0; |
| 123 | + font-size: 12px; |
| 124 | + line-height: 1.4; |
| 125 | + text-align: right; |
| 126 | + opacity: 0.6; |
| 127 | +} |
| 128 | + |
| 129 | +// Hide the hint on touch devices, where Shift+Enter is meaningless without a |
| 130 | +// physical keyboard. Device-CAPABILITY query (coarse pointer + no hover), |
| 131 | +// deliberately NOT a width breakpoint, to avoid colliding with theme breakpoints. |
| 132 | +@media (hover: none) and (pointer: coarse) { |
| 133 | + :where(.hamelp-ai-overview__hint) { |
| 134 | + display: none; |
| 135 | + } |
| 136 | +} |
| 137 | + |
82 | 138 | :where(.hamelp-ai-overview__button) { |
83 | | - padding: 0.75rem 1.5rem; |
| 139 | + // Fixed-size circular icon button (ChatGPT-style), floated inside the field's |
| 140 | + // bottom-right corner. The constant bottom offset keeps it pinned to the |
| 141 | + // bottom as the textarea grows, instead of riding up with the content. |
| 142 | + position: absolute; |
| 143 | + right: 0.5rem; |
| 144 | + bottom: 0.5rem; |
| 145 | + display: flex; |
| 146 | + align-items: center; |
| 147 | + justify-content: center; |
| 148 | + width: var(--hamelp-btn-size); |
| 149 | + height: var(--hamelp-btn-size); |
| 150 | + padding: 0; |
84 | 151 | border: 1px solid transparent; |
85 | | - border-radius: var(--hamelp-radius, 3px); |
| 152 | + border-radius: 50%; |
86 | 153 | background: var(--hamelp-button-bg, #1571da); |
| 154 | + background: linear-gradient(135deg, var(--hamelp-button-bg, #1571da), var(--hamelp-accent-hover)); |
87 | 155 | color: var(--hamelp-on-accent); |
88 | | - font-size: 1rem; |
89 | | - font-weight: var(--hamelp-font-weight, 600); |
90 | 156 | cursor: pointer; |
91 | | - transition: background 0.2s ease; |
| 157 | + transition: opacity 0.2s ease; |
| 158 | +} |
| 159 | + |
| 160 | +:where(.hamelp-ai-overview__button-icon) { |
| 161 | + width: 20px; |
| 162 | + height: 20px; |
92 | 163 | } |
93 | 164 |
|
94 | 165 | :where(.hamelp-ai-overview__button:hover) { |
95 | | - background: var(--hamelp-accent-hover); |
| 166 | + opacity: 0.9; |
96 | 167 | } |
97 | 168 |
|
98 | 169 | :where(.hamelp-ai-overview__button:focus-visible) { |
|
105 | 176 | cursor: wait; |
106 | 177 | } |
107 | 178 |
|
| 179 | +// WordPress-standard visually-hidden label: the button shows only the ↑ icon, |
| 180 | +// while the configurable button text stays available to screen readers. |
| 181 | +:where(.hamelp-ai-overview .screen-reader-text) { |
| 182 | + position: absolute; |
| 183 | + width: 1px; |
| 184 | + height: 1px; |
| 185 | + padding: 0; |
| 186 | + margin: -1px; |
| 187 | + overflow: hidden; |
| 188 | + clip: rect(1px, 1px, 1px, 1px); |
| 189 | + clip-path: inset(50%); |
| 190 | + border: 0; |
| 191 | + word-wrap: normal !important; |
| 192 | +} |
| 193 | + |
108 | 194 | // Conversation thread: question/answer turns stack from top to bottom, |
109 | 195 | // with the form (added in the markup after the thread) staying below. |
110 | 196 | :where(.hamelp-ai-overview__thread) { |
|
204 | 290 | text-decoration: underline; |
205 | 291 | } |
206 | 292 |
|
207 | | -// Editor preview styles. |
| 293 | +// Editor preview styles. Mirrors the front-end inset so the block preview shows |
| 294 | +// the send button floated inside the field's bottom-right corner. |
208 | 295 | :where(.hamelp-ai-overview__preview) { |
| 296 | + position: relative; |
209 | 297 | display: flex; |
210 | | - gap: 0.5rem; |
211 | 298 | } |
212 | 299 |
|
213 | 300 | :where(.hamelp-ai-overview__preview textarea), |
214 | 301 | :where(.hamelp-ai-overview__preview button) { |
215 | 302 | opacity: 0.7; |
216 | 303 | } |
217 | 304 |
|
218 | | -// Keep the editor preview textarea a single line (non-interactive preview). |
| 305 | +// Keep the editor preview textarea a single line (non-interactive preview) and |
| 306 | +// reserve the same right padding so the inset button never overlaps text. |
219 | 307 | :where(.hamelp-ai-overview__preview textarea) { |
| 308 | + flex: 1; |
| 309 | + box-sizing: border-box; |
| 310 | + // Mirror the front-end textarea padding (symmetric vertical padding) so the |
| 311 | + // inset button and the centered single line stay in the same place (see that |
| 312 | + // rule). |
| 313 | + padding: 0.75rem calc(var(--hamelp-btn-size) + 1rem) 0.75rem 0.75rem; |
220 | 314 | resize: none; |
221 | 315 | field-sizing: content; |
222 | | - min-height: 1lh; |
| 316 | + // Match the front-end min-height so the inset send button clears equal gaps |
| 317 | + // on every side in the single-line state (see the front-end textarea rule). |
| 318 | + min-height: calc(var(--hamelp-btn-size) + 1rem); |
| 319 | + // Match the front-end vertically-centered single line of text. |
| 320 | + align-content: center; |
223 | 321 | } |
224 | 322 |
|
225 | 323 | :where(.hamelp-ai-overview__note) { |
|
0 commit comments