PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.6
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.6
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
fluent-cart / app / Services / Theme / ColorPalette.php

ColorPalette.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.6, at app/Services/Theme/ColorPalette.php

369 lines 14.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Theme;
4
5 use FluentCart\Framework\Support\Arr;
6
7 /**
8 * The storefront colour registry.
9 *
10 * FluentCart's stylesheets declare roughly seventy scoped custom properties,
11 * but almost every one of them is written as `var(--fct-<global>, <fallback>)`.
12 * The globals listed here are the ones that are referenced but never declared —
13 * they are the intended knobs, and setting them cascades to everything
14 * downstream without touching a single scoped variable.
15 *
16 * This class is the single source of truth for three consumers: the CSS the
17 * storefront prints, the settings schema the admin renders, and the sanitizer
18 * that decides which submitted keys are real.
19 */
20 class ColorPalette
21 {
22 /**
23 * Defaults source: FluentCart's own colours, only overrides are written.
24 */
25 const SOURCE_DEFAULT = 'default';
26
27 /**
28 * Defaults source: rebuild the whole palette from the active theme.
29 */
30 const SOURCE_THEME = 'inherit_from_theme';
31
32 /**
33 * Defaults source: the store owner picks the global colours by hand.
34 */
35 const SOURCE_CUSTOM = 'customize';
36
37 /**
38 * Request-level cache for the built registry.
39 *
40 * @var array|null
41 */
42 protected static $cachedGlobals = null;
43
44 /**
45 * Every accepted value for the source setting.
46 *
47 * @return array
48 */
49 public static function sources(): array
50 {
51 return [self::SOURCE_DEFAULT, self::SOURCE_THEME, self::SOURCE_CUSTOM];
52 }
53
54 /**
55 * Field groups, in the order the settings screen shows them.
56 *
57 * @return array
58 */
59 public static function groups(): array
60 {
61 return [
62 'text' => __('Text', 'fluent-cart'),
63 'surface' => __('Backgrounds and borders', 'fluent-cart'),
64 'button' => __('Buttons', 'fluent-cart'),
65 'input' => __('Form inputs', 'fluent-cart'),
66 ];
67 }
68
69 /**
70 * The global custom properties a store owner can set.
71 *
72 * Keys are the settings keys (snake_case, safe as form state paths); `var`
73 * is the custom property written to the page. `aliases` covers properties
74 * that carry a second spelling in the stylesheets — the modal checkout
75 * reads `--fct-input-disabled-bg` where every other surface reads
76 * `--fct-input-disabled-bg-color`, and a single setting has to drive both
77 * or the modal drifts away from the rest of the store.
78 *
79 * `default` is the effective value FluentCart's own fallbacks resolve to;
80 * it is shown as a hint and is never written to the page on its own.
81 *
82 * @return array
83 */
84 public static function globals(): array
85 {
86 if (self::$cachedGlobals !== null) {
87 return self::$cachedGlobals;
88 }
89
90 $globals = [
91 /* ------------------------------------------------------- Text */
92 'primary_text_color' => [
93 'var' => '--fct-primary-text-color',
94 'label' => __('Primary text', 'fluent-cart'),
95 'note' => __('Product titles, prices and headings.', 'fluent-cart'),
96 'group' => 'text',
97 'role' => 'text',
98 ],
99 'secondary_text_color' => [
100 'var' => '--fct-secondary-text-color',
101 'label' => __('Secondary text', 'fluent-cart'),
102 'note' => __('Descriptions, captions and inactive navigation.', 'fluent-cart'),
103 'group' => 'text',
104 'role' => 'text_muted',
105 ],
106 'primary_active_text_color' => [
107 'var' => '--fct-primary-active-text-color',
108 'label' => __('Active text', 'fluent-cart'),
109 'note' => __('The selected step and active links in the checkout.', 'fluent-cart'),
110 'group' => 'text',
111 'role' => 'accent',
112 ],
113
114 /* -------------------------------------- Backgrounds and borders */
115 'primary_bg_color' => [
116 'var' => '--fct-primary-bg-color',
117 'label' => __('Primary background', 'fluent-cart'),
118 'note' => __('The brand color behind active states and selected controls.', 'fluent-cart'),
119 'group' => 'surface',
120 'role' => 'accent',
121 ],
122 'secondary_bg_color' => [
123 'var' => '--fct-secondary-bg-color',
124 'label' => __('Secondary background', 'fluent-cart'),
125 'note' => __('The tinted panels behind the shop grid and checkout summary.', 'fluent-cart'),
126 'group' => 'surface',
127 'role' => 'surface_alt',
128 ],
129 'border_color' => [
130 'var' => '--fct-border-color',
131 'label' => __('Border', 'fluent-cart'),
132 'note' => __('Card outlines, input borders and hairlines.', 'fluent-cart'),
133 'group' => 'surface',
134 'role' => 'border',
135 ],
136 'active_border_color' => [
137 'var' => '--fct-active-border-color',
138 'label' => __('Active border', 'fluent-cart'),
139 'note' => __('The outline on a selected variant or payment method.', 'fluent-cart'),
140 'group' => 'surface',
141 'role' => 'accent',
142 ],
143 'secondary_active_border_color' => [
144 'var' => '--fct-secondary-active-border-color',
145 'label' => __('Secondary active border', 'fluent-cart'),
146 'note' => __('The softer active outline used inside the modal checkout.', 'fluent-cart'),
147 'group' => 'surface',
148 'role' => 'accent',
149 ],
150 'divider_color' => [
151 'var' => '--fct-divider-color',
152 'label' => __('Divider', 'fluent-cart'),
153 'note' => __('The lighter rules between rows and sections.', 'fluent-cart'),
154 'group' => 'surface',
155 'role' => 'divider',
156 ],
157 'card_bg_color' => [
158 'var' => '--fct-card-bg-color',
159 'label' => __('Card background', 'fluent-cart'),
160 'note' => __('The surface behind product cards and panels.', 'fluent-cart'),
161 'group' => 'surface',
162 'role' => 'surface',
163 ],
164
165
166 /* ---------------------------------------------------- Buttons */
167 'btn_bg_color' => [
168 'var' => '--fct-btn-bg-color',
169 'label' => __('Button background', 'fluent-cart'),
170 'note' => __('Place Order, Buy Now, and every primary action in the store.', 'fluent-cart'),
171 'group' => 'button',
172 'role' => 'button_bg',
173 ],
174 'btn_text_color' => [
175 'var' => '--fct-btn-text-color',
176 'label' => __('Button text', 'fluent-cart'),
177 'note' => '',
178 'group' => 'button',
179 'role' => 'button_text',
180 ],
181 // Left unset, every primary button hovers in its resting colour —
182 // the stylesheets fall back to it — which is how they always hovered.
183 'btn_hover_bg_color' => [
184 'var' => '--fct-btn-hover-bg-color',
185 'label' => __('Button hover background', 'fluent-cart'),
186 'note' => __('What the primary buttons turn to under the pointer.', 'fluent-cart'),
187 'group' => 'button',
188 'role' => 'button_hover_bg',
189 ],
190 'btn_hover_text_color' => [
191 'var' => '--fct-btn-hover-text-color',
192 'label' => __('Button hover text', 'fluent-cart'),
193 'note' => '',
194 'group' => 'button',
195 'role' => 'button_hover_text',
196 ],
197 'secondary_btn_bg_color' => [
198 'var' => '--fct-secondary-btn-bg-color',
199 'label' => __('Secondary button background', 'fluent-cart'),
200 'note' => __('Add to Cart and the other outlined buttons.', 'fluent-cart'),
201 'group' => 'button',
202 // Not plain 'surface': the outline always keeps the page
203 // surface, but while the theme states its button pair the
204 // LABEL borrows the pair's readable half (see
205 // ThemePalette::resolve()).
206 'role' => 'secondary_button_bg',
207 ],
208 'secondary_btn_text_color' => [
209 'var' => '--fct-secondary-btn-text-color',
210 'label' => __('Secondary button text', 'fluent-cart'),
211 'note' => '',
212 'group' => 'button',
213 'role' => 'secondary_button_text',
214 ],
215 'secondary_btn_border_color' => [
216 'var' => '--fct-secondary-btn-border-color',
217 'label' => __('Secondary button border', 'fluent-cart'),
218 'note' => '',
219 'group' => 'button',
220 'role' => 'border',
221 ],
222 'secondary_btn_hover_bg_color' => [
223 'var' => '--fct-secondary-btn-hover-bg-color',
224 'label' => __('Secondary button hover', 'fluent-cart'),
225 'note' => '',
226 'group' => 'button',
227 'role' => 'surface_mute',
228 ],
229
230 /* ------------------------------------------------ Form inputs */
231 'input_bg_color' => [
232 'var' => '--fct-input-bg-color',
233 'label' => __('Input background', 'fluent-cart'),
234 'note' => '',
235 'group' => 'input',
236 'role' => 'surface',
237 ],
238 'input_text_color' => [
239 'var' => '--fct-input-text-color',
240 'label' => __('Input text', 'fluent-cart'),
241 'note' => '',
242 'group' => 'input',
243 'role' => 'text',
244 ],
245 'input_placeholder_text_color' => [
246 'var' => '--fct-input-placeholder-text-color',
247 'label' => __('Placeholder text', 'fluent-cart'),
248 'note' => '',
249 'group' => 'input',
250 'role' => 'text_placeholder',
251 ],
252 'input_disabled_bg_color' => [
253 'var' => '--fct-input-disabled-bg-color',
254 'aliases' => ['--fct-input-disabled-bg'],
255 'label' => __('Disabled input background', 'fluent-cart'),
256 'note' => '',
257 'group' => 'input',
258 'role' => 'surface_mute',
259 ],
260 ];
261
262 /**
263 * Filter the global storefront colours a store owner can set.
264 *
265 * Anything added here becomes settable in the admin, sanitised on save
266 * and written to the page — the three consumers read this one list.
267 *
268 * @param array $globals Settings key => definition.
269 */
270 $globals = apply_filters('fluent_cart/theme/color_globals', $globals);
271
272 foreach ($globals as $key => $definition) {
273 $globals[$key] = wp_parse_args($definition, [
274 'var' => '',
275 'aliases' => [],
276 'label' => $key,
277 'note' => '',
278 'group' => 'surface',
279 'role' => '',
280 ]);
281 }
282
283 self::$cachedGlobals = $globals;
284
285 return self::$cachedGlobals;
286 }
287
288 /**
289 * Drop the request-level registry cache.
290 *
291 * The registry runs its entries through a filter and wp_parse_args on the
292 * first read, so it is built once per request. A filter registered after
293 * that first read would otherwise never be seen.
294 *
295 * @return void
296 */
297 public static function clearCache(): void
298 {
299 self::$cachedGlobals = null;
300 }
301
302 /**
303 * The globals belonging to one group, in registry order.
304 *
305 * @param string $group
306 * @return array
307 */
308 public static function globalsFor(string $group): array
309 {
310 $matched = [];
311
312 foreach (self::globals() as $key => $definition) {
313 if (Arr::get($definition, 'group') === $group) {
314 $matched[$key] = $definition;
315 }
316 }
317
318 return $matched;
319 }
320
321 /**
322 * The semantic roles theme inheritance resolves.
323 *
324 * The first four are anchors read from the theme's palette; the rest are
325 * derived from those so that a theme offering two colours still produces a
326 * coherent store.
327 *
328 * @return array Role key => label.
329 */
330 public static function roles(): array
331 {
332 return [
333 'surface' => __('Surface', 'fluent-cart'),
334 'text' => __('Body text', 'fluent-cart'),
335 'accent' => __('Accent', 'fluent-cart'),
336 'button_bg' => __('Button', 'fluent-cart'),
337 'surface_alt' => __('Alternate surface', 'fluent-cart'),
338 'surface_mute' => __('Muted surface', 'fluent-cart'),
339 'border' => __('Border', 'fluent-cart'),
340 'divider' => __('Divider', 'fluent-cart'),
341 'text_muted' => __('Muted text', 'fluent-cart'),
342 'text_placeholder' => __('Placeholder text', 'fluent-cart'),
343 'button_text' => __('Button text', 'fluent-cart'),
344 'button_hover_bg' => __('Button hover', 'fluent-cart'),
345 'button_hover_text' => __('Button hover text', 'fluent-cart'),
346 'secondary_button_bg' => __('Secondary button', 'fluent-cart'),
347 'secondary_button_text' => __('Secondary button text', 'fluent-cart'),
348 ];
349 }
350
351 /**
352 * Every custom property one settings key writes, primary name first.
353 *
354 * @param array $definition
355 * @return array
356 */
357 public static function varsOf(array $definition): array
358 {
359 $vars = [Arr::get($definition, 'var', '')];
360 $aliases = Arr::get($definition, 'aliases', []);
361
362 if (is_array($aliases)) {
363 $vars = array_merge($vars, $aliases);
364 }
365
366 return array_values(array_filter($vars));
367 }
368 }
369