PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / trunk
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin vtrunk
1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 0.8.6 All 33 releases
desktop-mode / assets / css / announce.css

announce.css in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin trunk, at assets/css/announce.css

384 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*
2 * Announcement dialog — the shell's full-stop moment.
3 *
4 * The look is the first-run welcome dialog's: a blurred Void scrim with
5 * Nebula and mint glows, a near-white card with a holographic hairline,
6 * and a dark shimmering hero at the top of it. That dialog is the one
7 * piece of UI in the product that gets to be loud, and an announcement
8 * that has to explain a rename is the only other thing that earns it.
9 *
10 * Built as plain classed DOM rather than out of `<os-*>` components, on
11 * purpose and for the same reason the welcome dialog is: this is a
12 * poster, not a control panel. `<os-modal>` gives a dialog chrome and a
13 * focus trap, and would then have to be fought for every one of the
14 * gradients below.
15 *
16 * ---------------------------------------------------------------
17 * KNOWN DUPLICATION. `includes/welcome-dialog.php` inlines its own copy
18 * of this design. It has to be self-contained — it renders in *classic*
19 * wp-admin, on `admin_footer`, where none of the shell's stylesheets
20 * are enqueued — so the two cannot share a file without that dialog
21 * taking on a dependency it was deliberately built without. Restyle one
22 * and the other will drift. If they should converge, the move is to
23 * enqueue THIS file in classic admin too and delete the inline block,
24 * not to copy edits between them.
25 * ---------------------------------------------------------------
26 *
27 * ---------------------------------------------------------------
28 * THIS FILE OPTS OUT OF THE PALETTE, AND THAT IS A DEPARTURE.
29 *
30 * Every colour below is a literal. Not one reads a `--os-*` token, so
31 * nothing here answers to `variables.css` or to a desktop theme, which
32 * is exactly what AGENTS.md's "one declaration, one owner" rule tells
33 * you not to do. Read this as a one-off, NOT as the house pattern: a
34 * feature stylesheet that hardcodes its colours is a bug.
35 *
36 * The trade, deliberately made: the card is the product introducing
37 * itself by name, and a theme recolouring that would be the theme
38 * speaking in the product's voice. `<os-modal>` makes the same call for
39 * its own surface. The difference is that `<os-modal>` still routes
40 * through `--os-ui-modal-*` names, so a theme that really wants the
41 * surface can reach it; this file gives a theme nothing, and it is only
42 * defensible because the dialog exists for one release cycle and then
43 * stops firing forever.
44 *
45 * If it outlives that, tokenize it. Note that nothing will tell you to:
46 * `brand-palette.test.ts` only scans `variables.css`, so no test fails
47 * while this drifts from the palette beside it.
48 * ---------------------------------------------------------------
49 */
50
51 .os-announce,
52 .os-announce * {
53 box-sizing: border-box;
54 }
55
56 .os-announce {
57 position: fixed;
58 inset: 0;
59 /*
60 * Above every window (z 100+), the dock, and the Mio layer (190).
61 * Matches the welcome dialog's own z-index so the two can never
62 * argue if a site somehow shows both.
63 */
64 z-index: 100000;
65 display: flex;
66 align-items: center;
67 justify-content: center;
68 padding: 24px;
69 background: radial-gradient(
70 ellipse at 30% 20%,
71 rgba(236, 155, 255, 0.32),
72 transparent 60%
73 ),
74 radial-gradient(
75 ellipse at 70% 80%,
76 rgba(147, 240, 198, 0.26),
77 transparent 55%
78 ),
79 rgba(12, 11, 15, 0.62);
80 backdrop-filter: blur(14px) saturate(140%);
81 -webkit-backdrop-filter: blur(14px) saturate(140%);
82 animation: os-announce-fade 360ms cubic-bezier(0.22, 1, 0.36, 1);
83 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
84 "Helvetica Neue", Arial, sans-serif;
85 -webkit-font-smoothing: antialiased;
86 -moz-osx-font-smoothing: grayscale;
87 }
88
89 @keyframes os-announce-fade {
90 from {
91 opacity: 0;
92 }
93 to {
94 opacity: 1;
95 }
96 }
97
98 @keyframes os-announce-pop {
99 0% {
100 opacity: 0;
101 transform: translateY(14px) scale(0.96);
102 }
103 100% {
104 opacity: 1;
105 transform: translateY(0) scale(1);
106 }
107 }
108
109 @keyframes os-announce-shimmer {
110 0% {
111 background-position: 0% 50%;
112 }
113 100% {
114 background-position: 100% 50%;
115 }
116 }
117
118 .os-announce__card {
119 position: relative;
120 width: 100%;
121 max-width: 620px;
122 border-radius: 22px;
123 overflow: hidden;
124 background: linear-gradient(
125 145deg,
126 rgba(255, 255, 255, 0.98) 0%,
127 rgba(255, 251, 255, 0.98) 100%
128 );
129 box-shadow: 0 1px 0 rgba(255, 255, 255, 0.85) inset,
130 0 30px 60px -20px rgba(26, 23, 33, 0.55),
131 0 18px 36px -18px rgba(242, 82, 252, 0.45);
132 animation: os-announce-pop 460ms cubic-bezier(0.22, 1, 0.36, 1) both;
133 animation-delay: 60ms;
134 }
135
136 /*
137 * Holographic hairline. A padded pseudo-element masked with `xor` so
138 * only the 1px ring paints — a plain border cannot carry a gradient.
139 */
140 .os-announce__card::before {
141 content: "";
142 position: absolute;
143 inset: -1px;
144 border-radius: inherit;
145 padding: 1px;
146 background: linear-gradient(
147 135deg,
148 rgba(242, 82, 252, 0.65),
149 rgba(236, 155, 255, 0.55),
150 rgba(147, 240, 198, 0.55)
151 );
152 -webkit-mask: linear-gradient(#000 0 0) content-box,
153 linear-gradient(#000 0 0);
154 -webkit-mask-composite: xor;
155 mask-composite: exclude;
156 pointer-events: none;
157 }
158
159 .os-announce__hero {
160 position: relative;
161 padding: 32px 36px 26px;
162 background: linear-gradient(135deg, #0c0b0f 0%, #2a2533 45%, #ec9bff 100%);
163 background-size: 200% 200%;
164 animation: os-announce-shimmer 14s ease infinite alternate;
165 color: #fff;
166 overflow: hidden;
167 }
168
169 /* Station grid + glows, faded out at the edges so it reads as depth. */
170 .os-announce__hero::after {
171 content: "";
172 position: absolute;
173 inset: 0;
174 background: radial-gradient(
175 circle at 85% 15%,
176 rgba(236, 155, 255, 0.35),
177 transparent 45%
178 ),
179 radial-gradient(
180 circle at 15% 90%,
181 rgba(147, 240, 198, 0.22),
182 transparent 50%
183 ),
184 linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px) 0 0 /
185 28px 28px,
186 linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px) 0
187 0 / 28px 28px;
188 pointer-events: none;
189 mask-image: radial-gradient(ellipse at 50% 40%, #000 35%, transparent 80%);
190 -webkit-mask-image: radial-gradient(
191 ellipse at 50% 40%,
192 #000 35%,
193 transparent 80%
194 );
195 }
196
197 /*
198 * The eyebrow pill, sitting above the hero's own overlay. Same chip the
199 * welcome dialog wears for "New here", so the two dialogs read as the
200 * same voice saying two different things.
201 */
202 .os-announce__eyebrow {
203 position: relative;
204 z-index: 1;
205 display: inline-flex;
206 align-items: center;
207 gap: 8px;
208 padding: 5px 12px;
209 font-size: 11px;
210 font-weight: 600;
211 letter-spacing: 0.12em;
212 text-transform: uppercase;
213 color: #fff;
214 background: rgba(255, 255, 255, 0.18);
215 border: 1px solid rgba(255, 255, 255, 0.28);
216 border-radius: 999px;
217 backdrop-filter: blur(6px);
218 -webkit-backdrop-filter: blur(6px);
219 }
220
221 .os-announce__eyebrow-dot {
222 width: 6px;
223 height: 6px;
224 border-radius: 50%;
225 background: #ec9bff;
226 box-shadow: 0 0 0 4px rgba(236, 155, 255, 0.28);
227 }
228
229 .os-announce__title {
230 position: relative;
231 z-index: 1;
232 margin: 14px 0 6px;
233 font-size: 26px;
234 line-height: 1.2;
235 font-weight: 700;
236 letter-spacing: -0.01em;
237 color: #fff;
238 }
239
240 .os-announce__subtitle {
241 position: relative;
242 z-index: 1;
243 margin: 0;
244 font-size: 14px;
245 line-height: 1.5;
246 color: rgba(255, 255, 255, 0.85);
247 }
248
249 .os-announce__body {
250 padding: 24px 36px 4px;
251 font-size: 14.5px;
252 line-height: 1.6;
253 color: #33303a;
254 }
255
256 .os-announce__body p {
257 margin: 0 0 12px;
258 }
259
260 .os-announce__body p:last-child {
261 margin-bottom: 0;
262 }
263
264 /*
265 * The reassurance line — quieter than the copy above it. It answers
266 * "did my install change?" for the people who think to ask, without
267 * competing with the explanation.
268 */
269 .os-announce__fine {
270 padding: 14px 16px;
271 background: linear-gradient(
272 135deg,
273 rgba(242, 82, 252, 0.08),
274 rgba(147, 240, 198, 0.08)
275 );
276 border: 1px solid rgba(242, 82, 252, 0.22);
277 border-radius: 12px;
278 font-size: 13px;
279 color: #2a2533;
280 }
281
282 .os-announce__actions {
283 display: flex;
284 align-items: center;
285 justify-content: flex-end;
286 gap: 10px;
287 padding: 22px 36px 28px;
288 }
289
290 .os-announce__btn {
291 display: inline-flex;
292 align-items: center;
293 justify-content: center;
294 gap: 6px;
295 min-height: 38px;
296 padding: 8px 18px;
297 font-size: 14px;
298 font-weight: 600;
299 font-family: inherit;
300 line-height: 1;
301 border-radius: 10px;
302 border: 1px solid transparent;
303 cursor: pointer;
304 text-decoration: none;
305 transition: transform 120ms ease, box-shadow 120ms ease,
306 background 120ms ease, color 120ms ease;
307 }
308
309 .os-announce__btn:focus-visible {
310 outline: 2px solid #f252fc;
311 outline-offset: 2px;
312 }
313
314 .os-announce__btn--primary,
315 .os-announce__btn--primary:hover,
316 .os-announce__btn--primary:focus {
317 color: #fffbff;
318 }
319
320 .os-announce__btn--primary {
321 /*
322 * Ends on Pulse, not Nebula. `background-position` slides this on
323 * hover, so every stop carries the label at some point, and Nebula
324 * is light enough that Starlight text on it falls to ~1.6:1.
325 */
326 background: linear-gradient(135deg, #2a2533, #a12bb0 45%, #f252fc);
327 background-size: 180% 180%;
328 box-shadow: 0 10px 24px -10px rgba(242, 82, 252, 0.75),
329 0 4px 10px -4px rgba(236, 155, 255, 0.55);
330 }
331
332 .os-announce__btn--primary:hover {
333 transform: translateY(-1px);
334 background-position: 100% 50%;
335 box-shadow: 0 14px 30px -12px rgba(242, 82, 252, 0.85),
336 0 6px 14px -4px rgba(236, 155, 255, 0.65);
337 }
338
339 .os-announce__btn--primary:active {
340 transform: translateY(0);
341 }
342
343 @media (max-width: 600px) {
344 .os-announce__hero {
345 padding: 26px 24px 20px;
346 }
347
348 .os-announce__body,
349 .os-announce__actions {
350 padding-left: 24px;
351 padding-right: 24px;
352 }
353
354 .os-announce__title {
355 font-size: 22px;
356 }
357
358 .os-announce__actions {
359 flex-direction: column-reverse;
360 align-items: stretch;
361 }
362
363 .os-announce__btn {
364 width: 100%;
365 }
366 }
367
368 /*
369 * Reduced motion stops the movement, never the paint. The shimmer, the
370 * pop and the scrim fade are decoration; the gradients they animate are
371 * the dialog's identity and stay put at their first frame.
372 */
373 @media (prefers-reduced-motion: reduce) {
374 .os-announce,
375 .os-announce__card,
376 .os-announce__hero {
377 animation: none;
378 }
379
380 .os-announce__btn {
381 transition: none;
382 }
383 }
384