PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.4.1
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.4.1
1.4.1 1.4.0 1.3.7 1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 All 35 releases
xspeed / assets / theme.css

theme.css in xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN 1.4.1, at assets/theme.css

230 lines 11.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * xSpeed dashboard design tokens + self-hosted fonts (redesign v2).
3 *
4 * Lives in public/ (copied verbatim to assets/ by Vite on build — NOT bundled)
5 * so the @font-face url('./fonts/…') references resolve relative to
6 * assets/theme.css → assets/fonts/…, and so the CSS variables are available to
7 * BOTH the Tailwind utilities in admin.css and every React component. Scoped to
8 * #xspeed-app; dark overrides ride the existing `.dark` class that useTheme
9 * (and PHP's server-side pre-paint) toggles.
10 *
11 * Fonts are Latin-subset woff2 (Inter 400/500/600, Space Grotesk 500/600/700,
12 * JetBrains Mono 400/500) — admin-only, ~168 KB total, font-display: swap.
13 */
14
15 /* ---- Self-hosted fonts ------------------------------------------------- */
16 @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('./fonts/inter-400.woff2') format('woff2'); }
17 @font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('./fonts/inter-500.woff2') format('woff2'); }
18 @font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('./fonts/inter-600.woff2') format('woff2'); }
19 @font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 500; font-display: swap; src: url('./fonts/space-grotesk-500.woff2') format('woff2'); }
20 @font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 600; font-display: swap; src: url('./fonts/space-grotesk-600.woff2') format('woff2'); }
21 @font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 700; font-display: swap; src: url('./fonts/space-grotesk-700.woff2') format('woff2'); }
22 @font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url('./fonts/jetbrains-mono-400.woff2') format('woff2'); }
23 @font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 500; font-display: swap; src: url('./fonts/jetbrains-mono-500.woff2') format('woff2'); }
24
25 /* ---- Design tokens (light) --------------------------------------------- */
26 #xspeed-app {
27 /* Interactive — muted teal */
28 --primary: #2AA7A0;
29 --primary-hover: #1E9089;
30 --primary-tint: #E6F5F3;
31 --on-primary: #FFFFFF;
32 /* Text/heading colour for content sitting ON a primary/info tint. */
33 --on-tint: #1A7770; /* 4.79:1 on --primary-tint, 5.37:1 on --paper — small Pro badge text sits on the tint */
34 --primary-glow: 0 2px 12px rgba(42, 167, 160, .30);
35
36 /* Surfaces */
37 --canvas: #FBFBFC;
38 --paper: #FFFFFF;
39 --panel: #F5F6F8;
40 --line: #E6E8EC;
41 --line-strong: #D8DCE3;
42
43 /* Text */
44 --text: #0E0F13;
45 --text-secondary: #5C6270;
46 /* WCAG AA (#84). Was #8A909D, which measured 3.10 on --canvas, 3.20 on
47 --paper and 2.96 on --panel — the last being the "Off" status pill, the
48 only state indicator on a group-landing card and the smallest type on
49 screen. Fixing the token rather than the call sites covers all 102
50 usages at once — including Pro's panels, which render inside
51 #xspeed-app and ship no tokens of their own.
52
53 The first pass at this (#6B7180) only measured the three neutral
54 surfaces and missed a fourth class: the TINTS. text-muted is never
55 paired with a *-tint class on the same element — the tint sits on an
56 ancestor upsell card — so a source grep finds nothing and only a
57 rendered-DOM measurement catches it. #6B7180 failed on all four
58 (4.35 / 4.43 / 4.49 / 4.28).
59
60 #686D7C is the lightest value on the same hue that clears 4.5:1 on
61 every surface a muted string can land on:
62
63 canvas 4.99 · paper 5.16 · panel 4.77
64 primary+info-tint 4.60 · success-tint 4.68
65 warning-tint 4.74 · danger-tint 4.52
66
67 Still visibly lighter than --text-secondary (5.91 on canvas), so the
68 hint/secondary distinction the palette relies on survives. */
69 --muted: #686D7C;
70 --faint: #A2A8B4;
71
72 /* Semantic (color + tint) */
73 --success: #10B981;
74 --success-tint: #E9F7F1;
75 --warning: #F59E0B;
76 --warning-tint: #FEF4E6;
77 --danger: #EF4444;
78 --danger-tint: #FDECEC;
79 --info: #2AA7A0;
80 --info-tint: #E6F5F3;
81
82 /* Add-on: something sold separately from Pro (DESIGN.md §24.50). Orange so
83 it never reads as Pro's teal or the warning amber. --addon is text: 5.6:1
84 on --addon-tint, 6.4:1 on --paper. */
85 --addon: #9A4A0B;
86 --addon-tint: #FEF1E6;
87
88 /* Deep-sea rail (deliberately not near-black, to separate from WP's menu) */
89 --rail-top: #14211F;
90 --rail-bottom: #0D1615;
91 --rail-text: #9FADAB;
92 --rail-edge: rgba(255, 255, 255, .06);
93
94 /* Type families */
95 --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
96 --font-display: 'Space Grotesk', 'Inter', system-ui, sans-serif;
97 --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
98
99 /* Shape */
100 --radius-card: 12px;
101 --radius-control: 8px;
102 --radius-pill: 999px;
103
104 font-family: var(--font-sans);
105 color: var(--text);
106 }
107
108 /* ---- Design tokens (dark) — rides the existing .dark class -------------- */
109 #xspeed-app.dark {
110 --canvas: #0E0F13;
111 --paper: #16181D;
112 --panel: #1B1E24;
113 --line: #282C34;
114 --line-strong: #333845;
115 --text: #E7E9EE;
116 --text-secondary: #9AA1AE;
117 /* Same tint gap as light mode (#84). #7E8593 measured 4.29 on
118 --primary-tint and 4.32 on --success-tint, and cleared --panel by
119 exactly 4.50 — no headroom, so any future surface tweak would have
120 broken it silently. #868D9B clears every dark surface:
121 canvas 5.74 · paper 5.33 · panel 5.01 · primary+info-tint 4.77
122 success-tint 4.81 · warning-tint 5.03 · danger-tint 5.17
123 Dark was never actually at "0 failures" on tints; this is what makes
124 that true rather than assumed. */
125 --muted: #868D9B;
126 --faint: #6A707D;
127 /* Teal, a touch lighter so it reads on the dark surfaces. */
128 --primary: #34C0B7;
129 --primary-hover: #2AA7A0;
130 --primary-tint: #0E2622;
131 --on-tint: #7FD8CF;
132 --info: #34C0B7;
133 --info-tint: #0E2622;
134 --success-tint: #12251F;
135 --warning-tint: #251C0F;
136 --danger-tint: #2A1516;
137 --addon: #FDBA74;
138 --addon-tint: #2A1A0E;
139 }
140
141 /* ---- Signature motion (used by the shell + later screens) -------------- */
142 @keyframes xspeed-boltpulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .45; transform: scale(.82); } }
143 @keyframes xspeed-sweep { 0% { background-position: -220px 0; } 100% { background-position: 320px 0; } }
144 @keyframes xspeed-ringfill { from { stroke-dashoffset: 326.7; } to { stroke-dashoffset: 26.1; } }
145 @keyframes xspeed-drain { from { transform: scaleX(1); } to { transform: scaleX(0); } }
146 @keyframes xspeed-pop { from { opacity: 0; transform: scale(.6); } to { opacity: 1; transform: none; } }
147
148 /* Signature-motion utility classes (reduced-motion aware via the rule below). */
149 .xspeed-anim-boltpulse { animation: xspeed-boltpulse 1.8s ease-in-out infinite; }
150 .xspeed-anim-sweep { background-size: 220px 100%; animation: xspeed-sweep 1.5s linear infinite; }
151 /* Purge result (§24.38). The resting state is the END state, so the reduced-motion
152 rule below leaves the hairline undrawn and the icon simply present. */
153 .xspeed-anim-drain { transform: scaleX(0); transform-origin: left; animation: xspeed-drain .6s cubic-bezier(.3, .7, .3, 1) both; }
154 .xspeed-anim-pop { animation: xspeed-pop .2s ease-out both; }
155
156 @media (prefers-reduced-motion: reduce) {
157 #xspeed-app [class*="xspeed-anim"] { animation: none !important; }
158 }
159
160 /* ---- Loader bar + live dot (DESIGN.md §24.55) ----------------------------- */
161 /*
162 * A thin indeterminate bar for a wait with no progress to report, and a
163 * pulsing dot beside a status line that keeps updating on its own. Both are
164 * CSS here rather than Tailwind utilities because they need a pseudo-element
165 * and keyframes. Tokens only: --line, --primary, --success, --radius-pill.
166 */
167 @keyframes xspeed-blink { 50% { opacity: .25; } }
168 @keyframes xspeed-slide { 0% { transform: translateX(-100%); } 100% { transform: translateX(300%); } }
169 #xspeed-app .xspeed-loader-bar { height: 3px; border-radius: var(--radius-pill); background: var(--line); overflow: hidden; position: relative; max-width: 320px; }
170 #xspeed-app .xspeed-loader-bar::after { content: ""; position: absolute; inset: 0; width: 35%; border-radius: var(--radius-pill); background: var(--primary); animation: xspeed-slide 1.6s ease-in-out infinite; }
171 #xspeed-app .xspeed-live-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--primary); margin-top: 6px; animation: xspeed-blink 1.6s ease-in-out infinite; }
172 #xspeed-app .xspeed-live-dot.is-done { background: var(--success); animation: none; }
173 @media (prefers-reduced-motion: reduce) {
174 #xspeed-app .xspeed-live-dot, #xspeed-app .xspeed-loader-bar::after { animation: none; }
175 #xspeed-app .xspeed-loader-bar::after { width: 100%; opacity: .4; }
176 }
177
178 /* ---- MCP discovery card (Overview, disconnected) ----------------------- */
179 /*
180 * Three tinted surfaces for the "Control and optimize your site with AI" card.
181 * They live here rather than in a component because Tailwind cannot express an
182 * ALPHA of a var()-backed colour: with `primary: 'var(--primary)'` in the theme,
183 * `border-primary/45` silently emits the colour at full strength. Deriving them
184 * from --primary with color-mix() keeps the card on-brand in both themes with
185 * no new tokens — change --primary and all three follow.
186 *
187 * Every rule declares a neutral fallback FIRST. color-mix() is ~2023-era, and
188 * where it is unsupported the declaration is dropped: without the fallback the
189 * card's border-color would fall back to currentColor, painting a near-black
190 * hairline around a white card. The fallback degrades to the plain --line
191 * border and no wash, which is simply the standard card treatment.
192 */
193 #xspeed-app .xspeed-mcp-card {
194 border-color: var(--line);
195 border-color: color-mix(in srgb, var(--primary) 45%, transparent);
196 background-image: radial-gradient(120% 150% at 100% 0%,
197 color-mix(in srgb, var(--primary) 9%, transparent) 0%, transparent 58%);
198 }
199
200 /* The miniature session sits in a recessed teal well, not a second white
201 plane — white-on-white read as a stray box rather than an inset surface. */
202 #xspeed-app .xspeed-mcp-well {
203 border-color: var(--line);
204 border-color: color-mix(in srgb, var(--primary) 20%, transparent);
205 background-image: linear-gradient(180deg,
206 color-mix(in srgb, var(--primary) 3%, transparent) 0%,
207 color-mix(in srgb, var(--primary) 7%, transparent) 100%);
208 }
209
210 #xspeed-app .xspeed-mcp-glow {
211 background-image: radial-gradient(58% 62% at 62% 46%,
212 color-mix(in srgb, var(--primary) 13%, transparent) 0%, transparent 72%);
213 }
214
215 /* Dark needs more alpha for the same apparent lift: a 3% teal that reads as a
216 tint over #FFFFFF is invisible over #16181D. */
217 #xspeed-app.dark .xspeed-mcp-card {
218 border-color: color-mix(in srgb, var(--primary) 38%, transparent);
219 }
220 #xspeed-app.dark .xspeed-mcp-well {
221 border-color: color-mix(in srgb, var(--primary) 26%, transparent);
222 background-image: linear-gradient(180deg,
223 color-mix(in srgb, var(--primary) 6%, transparent) 0%,
224 color-mix(in srgb, var(--primary) 11%, transparent) 100%);
225 }
226 #xspeed-app.dark .xspeed-mcp-glow {
227 background-image: radial-gradient(58% 62% at 62% 46%,
228 color-mix(in srgb, var(--primary) 16%, transparent) 0%, transparent 72%);
229 }
230