components
2 days ago
hooks
2 days ago
lib
2 days ago
constants.js
2 days ago
store.js
2 days ago
style.scss
2 days ago
style.scss
277 lines
| 1 | // Shared color tokens for AI suggestion UI. |
| 2 | // Jetpack ThemeProvider doesn't wrap this page, |
| 3 | // so we define fallback values here. |
| 4 | $cg-green: #069e08; |
| 5 | $cg-green-light: rgba(0, 135, 0, 0.08); |
| 6 | $cg-diff-added: #d4edda; |
| 7 | $cg-diff-removed: #f8d7da; |
| 8 | |
| 9 | .jetpack-content-guidelines-ai__header-container { |
| 10 | display: inline-flex; |
| 11 | } |
| 12 | |
| 13 | .jetpack-content-guidelines-ai__suggest-all-button .jetpack-logo { |
| 14 | width: 18px; |
| 15 | height: 18px; |
| 16 | } |
| 17 | |
| 18 | // Empty state banner. |
| 19 | .jetpack-content-guidelines-ai__banner { |
| 20 | position: relative; |
| 21 | background: #003010; |
| 22 | border-radius: 8px; |
| 23 | padding: 32px; |
| 24 | overflow: hidden; |
| 25 | min-height: 220px; |
| 26 | display: flex; |
| 27 | align-items: flex-start; |
| 28 | width: min(680px, 100%); |
| 29 | box-sizing: border-box; |
| 30 | margin: 0 auto 24px; |
| 31 | } |
| 32 | |
| 33 | .jetpack-content-guidelines-ai__banner-content { |
| 34 | position: relative; |
| 35 | z-index: 1; |
| 36 | display: flex; |
| 37 | flex-direction: column; |
| 38 | max-width: 520px; |
| 39 | |
| 40 | h2 { |
| 41 | color: #daffdc; |
| 42 | font-size: 32px; |
| 43 | line-height: 40px; |
| 44 | font-weight: 500; |
| 45 | margin: 0 0 12px; |
| 46 | } |
| 47 | |
| 48 | p { |
| 49 | color: #daffdc; |
| 50 | font-size: 13px; |
| 51 | line-height: 20px; |
| 52 | margin: 0 0 64px; |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | .jetpack-content-guidelines-ai__banner-actions { |
| 57 | display: flex; |
| 58 | gap: 8px; |
| 59 | } |
| 60 | |
| 61 | .jetpack-content-guidelines-ai__banner-cta { |
| 62 | background-color: #48ff50 !important; |
| 63 | color: #003010 !important; |
| 64 | border-color: #48ff50 !important; |
| 65 | |
| 66 | &:hover { |
| 67 | background-color: #3de046 !important; |
| 68 | border-color: #3de046 !important; |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | .jetpack-content-guidelines-ai__banner-close-text { |
| 73 | color: #48ff50 !important; |
| 74 | |
| 75 | &:hover { |
| 76 | color: #3de046 !important; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | .jetpack-content-guidelines-ai__banner-close { |
| 81 | position: absolute; |
| 82 | inset-block-start: 8px; |
| 83 | inset-inline-end: 8px; |
| 84 | z-index: 1; |
| 85 | color: #fff !important; |
| 86 | |
| 87 | &:hover { |
| 88 | color: #daffdc !important; |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | .jetpack-content-guidelines-ai__banner-orb { |
| 93 | position: absolute; |
| 94 | border-radius: 50%; |
| 95 | pointer-events: none; |
| 96 | width: 180px; |
| 97 | height: 180px; |
| 98 | background: linear-gradient(to right, #04c, #48ff50); |
| 99 | // Shared by both orbs so the top glow and the bottom sphere line up |
| 100 | // on the same vertical axis. |
| 101 | inset-inline-end: 56px; |
| 102 | |
| 103 | &--top { |
| 104 | inset-block-start: -96px; |
| 105 | filter: blur(25px); |
| 106 | } |
| 107 | |
| 108 | &--bottom { |
| 109 | inset-block-end: -96px; |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | // Badge in accordion headers. |
| 114 | // Hide badge when the accordion is expanded — the Accept/Dismiss |
| 115 | // buttons inside already communicate the suggestion state. The badge is |
| 116 | // injected inside the CollapsibleCard trigger, which carries aria-expanded. |
| 117 | [aria-expanded="true"]="true""] .jetpack-content-guidelines-ai__badge-container { |
| 118 | display: none; |
| 119 | } |
| 120 | |
| 121 | // Jetpack CSS custom properties are normally set by |
| 122 | // ThemeProvider, which doesn't wrap this page. Define |
| 123 | // the tokens our Badge component needs as fallbacks. |
| 124 | .jetpack-content-guidelines-ai__badge-container { |
| 125 | --jp-green-5: #d0e6b8; |
| 126 | --jp-green-50: #008710; |
| 127 | |
| 128 | display: inline-flex; |
| 129 | align-items: center; |
| 130 | } |
| 131 | |
| 132 | .jetpack-content-guidelines-ai__badge--loading .components-spinner { |
| 133 | margin: 0; |
| 134 | width: 16px; |
| 135 | height: 16px; |
| 136 | } |
| 137 | |
| 138 | // Suggestion preview inside accordion. |
| 139 | // Match the VStack spacing={4} (16px gap) used by the Gutenberg form. |
| 140 | .jetpack-content-guidelines-ai__suggestion { |
| 141 | display: flex; |
| 142 | flex-direction: column; |
| 143 | gap: 16px; |
| 144 | } |
| 145 | |
| 146 | // Word-level diff display. |
| 147 | // Styled to match the Gutenberg textarea — green border indicates |
| 148 | // a suggestion is active. Clicking anywhere accepts the suggestion. |
| 149 | .jetpack-content-guidelines-ai__diff { |
| 150 | padding: 9px 11px; |
| 151 | border: 1px solid var(--jp-green-40, $cg-green); |
| 152 | border-radius: 2px; |
| 153 | background: #fff; |
| 154 | font-size: 13px; |
| 155 | line-height: 20px; |
| 156 | white-space: pre-wrap; |
| 157 | overflow-wrap: break-word; |
| 158 | overflow-y: auto; |
| 159 | resize: vertical; |
| 160 | cursor: text; |
| 161 | box-sizing: border-box; |
| 162 | } |
| 163 | |
| 164 | .jetpack-content-guidelines-ai__diff-added { |
| 165 | background: $cg-diff-added; |
| 166 | text-decoration: none; |
| 167 | } |
| 168 | |
| 169 | .jetpack-content-guidelines-ai__diff-removed { |
| 170 | background: $cg-diff-removed; |
| 171 | text-decoration: line-through; |
| 172 | } |
| 173 | |
| 174 | .jetpack-content-guidelines-ai__suggestion-actions { |
| 175 | display: flex; |
| 176 | gap: 8px; |
| 177 | } |
| 178 | |
| 179 | // Green shimmer overlay on the textarea during AI generation. |
| 180 | // We make the textarea wrapper positioned so the ::after overlay works. |
| 181 | .is-jetpack-loading .components-textarea-control { |
| 182 | position: relative; |
| 183 | |
| 184 | &::after { |
| 185 | content: ""; |
| 186 | position: absolute; |
| 187 | inset: 0; |
| 188 | pointer-events: none; |
| 189 | border-radius: 4px; |
| 190 | background: linear-gradient(90deg, transparent 0%, $cg-green-light 50%, transparent 100%); |
| 191 | background-size: 200% 100%; |
| 192 | animation: jetpack-cg-shimmer 1.5s infinite; |
| 193 | z-index: 1; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | .is-jetpack-loading textarea { |
| 198 | border-color: var(--jp-green-40, $cg-green) !important; |
| 199 | pointer-events: none; |
| 200 | } |
| 201 | |
| 202 | @keyframes jetpack-cg-shimmer { |
| 203 | |
| 204 | 0% { |
| 205 | background-position: -200% 0; |
| 206 | } |
| 207 | |
| 208 | 100% { |
| 209 | background-position: 200% 0; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | // Hide Gutenberg's DataForm and Save/Clear when a suggestion is active. |
| 214 | // Also hide the per-section generate button since Accept/Dismiss replaces it. |
| 215 | .has-jetpack-suggestion:not(.block-guideline-modal) > :first-child > :not(.jetpack-content-guidelines-ai__actions-container) { |
| 216 | display: none; |
| 217 | } |
| 218 | |
| 219 | // Injected containers should not affect layout when their content is empty. |
| 220 | // React root inside means :empty won't work, |
| 221 | // so we use display:contents when not active. |
| 222 | .jetpack-content-guidelines-ai__actions-container, |
| 223 | .jetpack-content-guidelines-ai__section-button-container { |
| 224 | display: contents; |
| 225 | } |
| 226 | |
| 227 | // Per-section generate button. |
| 228 | .jetpack-content-guidelines-ai__section-generate-button { |
| 229 | display: inline-flex; |
| 230 | } |
| 231 | |
| 232 | // The per-section Generate/Improve button is injected as a third control into |
| 233 | // the form's Save/Clear row — a Gutenberg HStack laid out for two buttons and |
| 234 | // set not to wrap. On narrow viewports the extra button overflows and, because |
| 235 | // flex items shrink by default, the button labels overlap. Let that row wrap |
| 236 | // onto multiple lines instead. Scoped through the injected container (a direct |
| 237 | // child of the HStack) so only the rows we add a button to are affected. |
| 238 | .guidelines__list-item form :has(> .jetpack-content-guidelines-ai__section-button-container) { |
| 239 | flex-wrap: wrap; |
| 240 | } |
| 241 | |
| 242 | // Upgrade notice. |
| 243 | .jetpack-content-guidelines-ai__upgrade-notice-container { |
| 244 | display: contents; |
| 245 | } |
| 246 | |
| 247 | .jetpack-content-guidelines-ai__upgrade-notice { |
| 248 | width: min(680px, 100%); |
| 249 | margin: 0 auto 16px; |
| 250 | box-sizing: border-box; |
| 251 | } |
| 252 | |
| 253 | // Block guideline modal — hide only the textarea input |
| 254 | // when suggestion is active, keeping the label visible. |
| 255 | .block-guideline-modal.has-jetpack-suggestion .components-textarea-control__input { |
| 256 | display: none; |
| 257 | } |
| 258 | |
| 259 | // Block modal — shimmer only on the textarea, |
| 260 | // not the label wrapper. Suppress the ::after overlay |
| 261 | // and apply shimmer as a background on the textarea. |
| 262 | .block-guideline-modal.is-jetpack-loading .components-textarea-control::after { |
| 263 | content: none; |
| 264 | } |
| 265 | |
| 266 | .block-guideline-modal.is-jetpack-loading .components-textarea-control__input { |
| 267 | background: linear-gradient(90deg, transparent 0%, $cg-green-light 50%, transparent 100%); |
| 268 | background-size: 200% 100%; |
| 269 | animation: jetpack-cg-shimmer 1.5s infinite; |
| 270 | } |
| 271 | |
| 272 | // Block modal injected containers — collapse when empty. |
| 273 | .jetpack-content-guidelines-ai__block-actions-container, |
| 274 | .jetpack-content-guidelines-ai__block-suggestion-buttons-container { |
| 275 | display: contents; |
| 276 | } |
| 277 |