PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
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 trunk All 48 releases
fluent-cart / app / Services / Theme / ColorPalette.php

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

351 lines 13.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 'secondary_btn_bg_color' => [
182 'var' => '--fct-secondary-btn-bg-color',
183 'label' => __('Secondary button background', 'fluent-cart'),
184 'note' => __('Add to Cart and the other outlined buttons.', 'fluent-cart'),
185 'group' => 'button',
186 // Not plain 'surface': the outline always keeps the page
187 // surface, but while the theme states its button pair the
188 // LABEL borrows the pair's readable half (see
189 // ThemePalette::resolve()).
190 'role' => 'secondary_button_bg',
191 ],
192 'secondary_btn_text_color' => [
193 'var' => '--fct-secondary-btn-text-color',
194 'label' => __('Secondary button text', 'fluent-cart'),
195 'note' => '',
196 'group' => 'button',
197 'role' => 'secondary_button_text',
198 ],
199 'secondary_btn_border_color' => [
200 'var' => '--fct-secondary-btn-border-color',
201 'label' => __('Secondary button border', 'fluent-cart'),
202 'note' => '',
203 'group' => 'button',
204 'role' => 'border',
205 ],
206 'secondary_btn_hover_bg_color' => [
207 'var' => '--fct-secondary-btn-hover-bg-color',
208 'label' => __('Secondary button hover', 'fluent-cart'),
209 'note' => '',
210 'group' => 'button',
211 'role' => 'surface_mute',
212 ],
213
214 /* ------------------------------------------------ Form inputs */
215 'input_bg_color' => [
216 'var' => '--fct-input-bg-color',
217 'label' => __('Input background', 'fluent-cart'),
218 'note' => '',
219 'group' => 'input',
220 'role' => 'surface',
221 ],
222 'input_text_color' => [
223 'var' => '--fct-input-text-color',
224 'label' => __('Input text', 'fluent-cart'),
225 'note' => '',
226 'group' => 'input',
227 'role' => 'text',
228 ],
229 'input_placeholder_text_color' => [
230 'var' => '--fct-input-placeholder-text-color',
231 'label' => __('Placeholder text', 'fluent-cart'),
232 'note' => '',
233 'group' => 'input',
234 'role' => 'text_placeholder',
235 ],
236 'input_disabled_bg_color' => [
237 'var' => '--fct-input-disabled-bg-color',
238 'aliases' => ['--fct-input-disabled-bg'],
239 'label' => __('Disabled input background', 'fluent-cart'),
240 'note' => '',
241 'group' => 'input',
242 'role' => 'surface_mute',
243 ],
244 ];
245
246 /**
247 * Filter the global storefront colours a store owner can set.
248 *
249 * Anything added here becomes settable in the admin, sanitised on save
250 * and written to the page — the three consumers read this one list.
251 *
252 * @param array $globals Settings key => definition.
253 */
254 $globals = apply_filters('fluent_cart/theme/color_globals', $globals);
255
256 foreach ($globals as $key => $definition) {
257 $globals[$key] = wp_parse_args($definition, [
258 'var' => '',
259 'aliases' => [],
260 'label' => $key,
261 'note' => '',
262 'group' => 'surface',
263 'role' => '',
264 ]);
265 }
266
267 self::$cachedGlobals = $globals;
268
269 return self::$cachedGlobals;
270 }
271
272 /**
273 * Drop the request-level registry cache.
274 *
275 * The registry runs its entries through a filter and wp_parse_args on the
276 * first read, so it is built once per request. A filter registered after
277 * that first read would otherwise never be seen.
278 *
279 * @return void
280 */
281 public static function clearCache(): void
282 {
283 self::$cachedGlobals = null;
284 }
285
286 /**
287 * The globals belonging to one group, in registry order.
288 *
289 * @param string $group
290 * @return array
291 */
292 public static function globalsFor(string $group): array
293 {
294 $matched = [];
295
296 foreach (self::globals() as $key => $definition) {
297 if (Arr::get($definition, 'group') === $group) {
298 $matched[$key] = $definition;
299 }
300 }
301
302 return $matched;
303 }
304
305 /**
306 * The semantic roles theme inheritance resolves.
307 *
308 * The first four are anchors read from the theme's palette; the rest are
309 * derived from those so that a theme offering two colours still produces a
310 * coherent store.
311 *
312 * @return array Role key => label.
313 */
314 public static function roles(): array
315 {
316 return [
317 'surface' => __('Surface', 'fluent-cart'),
318 'text' => __('Body text', 'fluent-cart'),
319 'accent' => __('Accent', 'fluent-cart'),
320 'button_bg' => __('Button', 'fluent-cart'),
321 'surface_alt' => __('Alternate surface', 'fluent-cart'),
322 'surface_mute' => __('Muted surface', 'fluent-cart'),
323 'border' => __('Border', 'fluent-cart'),
324 'divider' => __('Divider', 'fluent-cart'),
325 'text_muted' => __('Muted text', 'fluent-cart'),
326 'text_placeholder' => __('Placeholder text', 'fluent-cart'),
327 'button_text' => __('Button text', 'fluent-cart'),
328 'secondary_button_bg' => __('Secondary button', 'fluent-cart'),
329 'secondary_button_text' => __('Secondary button text', 'fluent-cart'),
330 ];
331 }
332
333 /**
334 * Every custom property one settings key writes, primary name first.
335 *
336 * @param array $definition
337 * @return array
338 */
339 public static function varsOf(array $definition): array
340 {
341 $vars = [Arr::get($definition, 'var', '')];
342 $aliases = Arr::get($definition, 'aliases', []);
343
344 if (is_array($aliases)) {
345 $vars = array_merge($vars, $aliases);
346 }
347
348 return array_values(array_filter($vars));
349 }
350 }
351