@pie-players/pie-tool-theme 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,683 @@
1
+ <svelte:options
2
+ customElement={{
3
+ tag: 'pie-tool-theme',
4
+ shadow: 'open',
5
+ props: {
6
+ visible: { type: 'Boolean', attribute: 'visible' },
7
+ toolId: { type: 'String', attribute: 'tool-id' },
8
+ schemes: { type: 'String', attribute: 'schemes' },
9
+ schemeCatalog: { type: 'Object', reflect: false }
10
+ }
11
+ }}
12
+ />
13
+
14
+ <!-- ToolColorScheme - WCAG Color Scheme Selection Tool
15
+
16
+ Provides accessible color scheme options for users who need
17
+ high contrast or alternative color combinations.
18
+
19
+ Addresses WCAG 2.1 Level AA criteria:
20
+ - 1.4.1 Use of Color
21
+ - 1.4.3 Contrast (Minimum)
22
+ - 1.4.11 Non-text Contrast
23
+ -->
24
+
25
+ <script lang="ts">
26
+ const browser = typeof window !== "undefined";
27
+
28
+ import {
29
+ connectToolRuntimeContext,
30
+ ZIndexLayer,
31
+ } from '@pie-players/pie-assessment-toolkit';
32
+ import {
33
+ listPieColorSchemes,
34
+ type PieColorSchemeDefinition,
35
+ } from '@pie-players/pie-theme';
36
+ import type {
37
+ AssessmentToolkitRuntimeContext,
38
+ IToolCoordinator,
39
+ } from '@pie-players/pie-assessment-toolkit';
40
+ import { createFocusTrap, safeLocalStorageGet, safeLocalStorageSet } from '@pie-players/pie-players-shared';
41
+ import { onMount } from 'svelte';
42
+
43
+ let {
44
+ visible = false,
45
+ toolId = 'theme',
46
+ schemes = '',
47
+ schemeCatalog = null
48
+ }: {
49
+ visible?: boolean;
50
+ toolId?: string;
51
+ schemes?: string;
52
+ schemeCatalog?: unknown;
53
+ } = $props();
54
+
55
+ let containerEl = $state<HTMLDivElement | undefined>();
56
+ let runtimeContext = $state<AssessmentToolkitRuntimeContext | null>(null);
57
+ const coordinator = $derived(
58
+ runtimeContext?.toolCoordinator as IToolCoordinator | undefined,
59
+ );
60
+
61
+ // Track registration state
62
+ let registered = $state(false);
63
+
64
+ $effect(() => {
65
+ if (!containerEl) return;
66
+ return connectToolRuntimeContext(containerEl, (value: AssessmentToolkitRuntimeContext) => {
67
+ runtimeContext = value;
68
+ });
69
+ });
70
+
71
+ type ToolSchemePreview = {
72
+ bg: string;
73
+ text: string;
74
+ primary: string;
75
+ };
76
+
77
+ type ToolColorSchemeOption = {
78
+ id: string;
79
+ name: string;
80
+ description: string;
81
+ preview: ToolSchemePreview;
82
+ };
83
+
84
+ const DEFAULT_COLOR_SCHEMES: ToolColorSchemeOption[] = [
85
+ {
86
+ id: 'default',
87
+ name: 'Default',
88
+ description: 'Standard PIE colors',
89
+ preview: {
90
+ bg: '#ffffff',
91
+ text: '#000000',
92
+ primary: '#3f51b5'
93
+ }
94
+ },
95
+ {
96
+ id: 'black-on-white',
97
+ name: 'Black on White',
98
+ description: 'High contrast for readability',
99
+ preview: {
100
+ bg: '#ffffff',
101
+ text: '#000000',
102
+ primary: '#0000cc'
103
+ }
104
+ },
105
+ {
106
+ id: 'white-on-black',
107
+ name: 'White on Black',
108
+ description: 'Inverse high contrast',
109
+ preview: {
110
+ bg: '#000000',
111
+ text: '#ffffff',
112
+ primary: '#ffff00'
113
+ }
114
+ },
115
+ {
116
+ id: 'rose-on-green',
117
+ name: 'Rose on Green',
118
+ description: 'Color blind friendly (protanopia/deuteranopia)',
119
+ preview: {
120
+ bg: '#ccffcc',
121
+ text: '#3d0022',
122
+ primary: '#660044'
123
+ }
124
+ },
125
+ {
126
+ id: 'yellow-on-blue',
127
+ name: 'Yellow on Blue',
128
+ description: 'Strong contrast scheme',
129
+ preview: {
130
+ bg: '#000066',
131
+ text: '#ffff00',
132
+ primary: '#ffff66'
133
+ }
134
+ },
135
+ {
136
+ id: 'black-on-rose',
137
+ name: 'Black on Rose',
138
+ description: 'Warm tinted background',
139
+ preview: {
140
+ bg: '#ffccdd',
141
+ text: '#000000',
142
+ primary: '#880044'
143
+ }
144
+ },
145
+ {
146
+ id: 'light-gray-on-dark-gray',
147
+ name: 'Light Gray on Dark Gray',
148
+ description: 'Reduced brightness for light sensitivity',
149
+ preview: {
150
+ bg: '#333333',
151
+ text: '#e0e0e0',
152
+ primary: '#aaaaaa'
153
+ }
154
+ }
155
+ ];
156
+
157
+ function previewFromVariables(
158
+ variables: Record<string, string> | undefined,
159
+ fallback: ToolSchemePreview
160
+ ): ToolSchemePreview {
161
+ return {
162
+ bg: variables?.['--pie-background'] || variables?.['--pie-secondary-background'] || fallback.bg,
163
+ text: variables?.['--pie-text'] || fallback.text,
164
+ primary: variables?.['--pie-primary'] || fallback.primary
165
+ };
166
+ }
167
+
168
+ function normalizeSchemeCatalogEntry(entry: unknown): ToolColorSchemeOption | null {
169
+ if (!entry || typeof entry !== 'object') return null;
170
+ const value = entry as Record<string, unknown>;
171
+ const id = typeof value.id === 'string' ? value.id.trim() : '';
172
+ if (!id) return null;
173
+ const name = typeof value.name === 'string' && value.name.trim() ? value.name.trim() : id;
174
+ const description = typeof value.description === 'string' ? value.description : '';
175
+ const previewValue = value.preview;
176
+ const preview = (
177
+ previewValue &&
178
+ typeof previewValue === 'object' &&
179
+ typeof (previewValue as Record<string, unknown>).bg === 'string' &&
180
+ typeof (previewValue as Record<string, unknown>).text === 'string' &&
181
+ typeof (previewValue as Record<string, unknown>).primary === 'string'
182
+ )
183
+ ? {
184
+ bg: (previewValue as Record<string, string>).bg,
185
+ text: (previewValue as Record<string, string>).text,
186
+ primary: (previewValue as Record<string, string>).primary
187
+ }
188
+ : previewFromVariables(
189
+ typeof value.variables === 'object' && value.variables
190
+ ? (value.variables as Record<string, string>)
191
+ : undefined,
192
+ { bg: '#ffffff', text: '#000000', primary: '#3f51b5' }
193
+ );
194
+ return { id, name, description, preview };
195
+ }
196
+
197
+ function parseSchemesFromAttribute(raw: string): ToolColorSchemeOption[] {
198
+ if (!raw?.trim()) return [];
199
+ try {
200
+ const parsed = JSON.parse(raw) as unknown;
201
+ return Array.isArray(parsed)
202
+ ? parsed
203
+ .map((entry) => normalizeSchemeCatalogEntry(entry))
204
+ .filter((entry): entry is ToolColorSchemeOption => Boolean(entry))
205
+ : [];
206
+ } catch {
207
+ return [];
208
+ }
209
+ }
210
+
211
+ function resolveThemeHost(): HTMLElement | null {
212
+ const localHost = containerEl?.closest('pie-theme') as HTMLElement | null;
213
+ if (localHost) return localHost;
214
+ const scopedDocumentHost = document.querySelector('pie-theme[scope="document"]') as HTMLElement | null;
215
+ if (scopedDocumentHost) return scopedDocumentHost;
216
+ return document.querySelector('pie-theme') as HTMLElement | null;
217
+ }
218
+
219
+ function resolveThemeSchemes(): ToolColorSchemeOption[] {
220
+ const themeSchemes = listPieColorSchemes();
221
+ const fallbackById = new Map(DEFAULT_COLOR_SCHEMES.map((scheme) => [scheme.id, scheme]));
222
+ return themeSchemes.map((scheme: PieColorSchemeDefinition) => {
223
+ const fallback = fallbackById.get(scheme.id) || {
224
+ id: scheme.id,
225
+ name: scheme.name || scheme.id,
226
+ description: scheme.description || '',
227
+ preview: { bg: '#ffffff', text: '#000000', primary: '#3f51b5' }
228
+ };
229
+ return {
230
+ id: scheme.id,
231
+ name: scheme.name || fallback.name,
232
+ description: scheme.description || fallback.description || '',
233
+ preview: scheme.preview || previewFromVariables(scheme.variables, fallback.preview)
234
+ };
235
+ });
236
+ }
237
+
238
+ const availableSchemes = $derived.by(() => {
239
+ if (Array.isArray(schemeCatalog) && schemeCatalog.length > 0) {
240
+ const normalized = schemeCatalog
241
+ .map((entry) => normalizeSchemeCatalogEntry(entry))
242
+ .filter((entry): entry is ToolColorSchemeOption => Boolean(entry));
243
+ if (normalized.length > 0) return normalized;
244
+ }
245
+ const parsedFromAttribute = parseSchemesFromAttribute(schemes);
246
+ if (parsedFromAttribute.length > 0) return parsedFromAttribute;
247
+ const themeSchemes = resolveThemeSchemes();
248
+ if (themeSchemes.length > 0) return themeSchemes;
249
+ return DEFAULT_COLOR_SCHEMES;
250
+ });
251
+
252
+ // Current color scheme
253
+ let currentScheme = $state('default');
254
+
255
+ // Dropdown state
256
+ let dropdownOpen = $state(false);
257
+
258
+ // Focus trap cleanup function (plain variable, not reactive)
259
+ let cleanupFocusTrap: (() => void) | null = null;
260
+
261
+ // Apply color scheme to document
262
+ function applyColorScheme(schemeId: string) {
263
+ if (!browser) return;
264
+
265
+ const themeHost = resolveThemeHost();
266
+ if (themeHost) {
267
+ themeHost.setAttribute('scheme', schemeId || 'default');
268
+ } else {
269
+ const root = document.documentElement;
270
+ if (schemeId === 'default') {
271
+ root.removeAttribute('data-color-scheme');
272
+ } else {
273
+ root.setAttribute('data-color-scheme', schemeId);
274
+ }
275
+ }
276
+
277
+ // Save to localStorage safely
278
+ safeLocalStorageSet('pie-color-scheme', schemeId);
279
+ }
280
+
281
+ // Select scheme and close the tool
282
+ // Automatically closes the modal after selection for better UX
283
+ function selectScheme(schemeId: string) {
284
+ currentScheme = schemeId;
285
+ applyColorScheme(schemeId);
286
+ // Close the entire tool modal and deselect toolbar button
287
+ coordinator?.hideTool(toolId);
288
+ }
289
+
290
+ // Toggle dropdown
291
+ function toggleDropdown() {
292
+ dropdownOpen = !dropdownOpen;
293
+ }
294
+
295
+ // Get current scheme object
296
+ let currentSchemeObj = $derived(
297
+ availableSchemes.find(s => s.id === currentScheme) ||
298
+ availableSchemes[0] ||
299
+ DEFAULT_COLOR_SCHEMES[0]
300
+ );
301
+
302
+ $effect(() => {
303
+ if (!availableSchemes.find((scheme) => scheme.id === currentScheme)) {
304
+ currentScheme = availableSchemes[0]?.id || 'default';
305
+ }
306
+ });
307
+
308
+ function handleClose() {
309
+ dropdownOpen = false;
310
+ coordinator?.hideTool(toolId);
311
+ }
312
+
313
+ // Handle escape key
314
+ function handleKeyDown(e: KeyboardEvent) {
315
+ if (e.key === 'Escape') {
316
+ if (dropdownOpen) {
317
+ // Close dropdown first
318
+ dropdownOpen = false;
319
+ } else {
320
+ // Close the entire tool
321
+ handleClose();
322
+ }
323
+ } else if (dropdownOpen && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
324
+ // Handle arrow key navigation in dropdown
325
+ e.preventDefault();
326
+ const options = containerEl?.querySelectorAll('.pie-tool-color-scheme__option:not([disabled])') as NodeListOf<HTMLButtonElement>;
327
+ if (!options || options.length === 0) return;
328
+
329
+ const currentIndex = Array.from(options).findIndex(opt => opt === document.activeElement);
330
+ let nextIndex: number;
331
+
332
+ if (e.key === 'ArrowDown') {
333
+ nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % options.length;
334
+ } else {
335
+ nextIndex = currentIndex === -1 ? options.length - 1 : (currentIndex - 1 + options.length) % options.length;
336
+ }
337
+
338
+ options[nextIndex]?.focus();
339
+ }
340
+ }
341
+
342
+ // Register with coordinator when it becomes available
343
+ $effect(() => {
344
+ if (coordinator && toolId && !registered) {
345
+ coordinator.registerTool(toolId, 'Theme', undefined, ZIndexLayer.MODAL);
346
+ registered = true;
347
+ }
348
+ });
349
+
350
+ // Update element reference when container becomes available
351
+ $effect(() => {
352
+ if (coordinator && containerEl && toolId) {
353
+ coordinator.updateToolElement(toolId, containerEl);
354
+ }
355
+ });
356
+
357
+ // Handle focus trap cleanup when visibility changes
358
+ $effect(() => {
359
+ if (!visible && cleanupFocusTrap) {
360
+ cleanupFocusTrap();
361
+ cleanupFocusTrap = null;
362
+ } else if (visible && containerEl && !cleanupFocusTrap) {
363
+ cleanupFocusTrap = createFocusTrap(containerEl);
364
+ }
365
+ });
366
+
367
+ onMount(() => {
368
+ // Load saved scheme from localStorage safely
369
+ if (browser) {
370
+ const themeHost = resolveThemeHost();
371
+ const hostScheme = themeHost?.getAttribute('scheme');
372
+ const saved = safeLocalStorageGet('pie-color-scheme') ?? hostScheme ?? 'default';
373
+ currentScheme = saved;
374
+ if (saved) {
375
+ requestAnimationFrame(() => {
376
+ applyColorScheme(saved);
377
+ });
378
+ }
379
+ }
380
+
381
+ // Click outside handler
382
+ function handleClickOutside(e: MouseEvent) {
383
+ if (!dropdownOpen) return;
384
+ const clickPath = e.composedPath();
385
+ if (!containerEl || !clickPath.includes(containerEl)) {
386
+ dropdownOpen = false;
387
+ }
388
+ }
389
+
390
+ document.addEventListener('click', handleClickOutside);
391
+
392
+ return () => {
393
+ document.removeEventListener('click', handleClickOutside);
394
+ if (coordinator && toolId) {
395
+ coordinator.unregisterTool(toolId);
396
+ }
397
+ };
398
+ });
399
+ </script>
400
+
401
+ {#if visible}
402
+ <div bind:this={containerEl} class="pie-tool-color-scheme" role="dialog" tabindex="-1" aria-modal="true" aria-labelledby="color-scheme-title" onkeydown={handleKeyDown}>
403
+ <div class="pie-tool-color-scheme__header">
404
+ <h3 id="color-scheme-title" class="pie-tool-color-scheme__title">Theme</h3>
405
+ <button
406
+ type="button"
407
+ class="pie-tool-color-scheme__close"
408
+ onclick={handleClose}
409
+ aria-label="Close theme selector"
410
+ >
411
+ ×
412
+ </button>
413
+ </div>
414
+
415
+ <div class="pie-tool-color-scheme__content">
416
+ <p class="pie-tool-color-scheme__description">
417
+ Select a theme to improve readability and reduce eye strain.
418
+ </p>
419
+
420
+ <button
421
+ type="button"
422
+ class="pie-tool-color-scheme__dropdown-trigger"
423
+ aria-label="Select theme"
424
+ aria-expanded={dropdownOpen}
425
+ onclick={toggleDropdown}
426
+ >
427
+ <div class="pie-tool-color-scheme__current">
428
+ <div class="pie-tool-color-scheme__preview">
429
+ <div class="pie-tool-color-scheme__preview-bg" style="background-color: {currentSchemeObj.preview.bg}">
430
+ <div class="pie-tool-color-scheme__preview-text" style="color: {currentSchemeObj.preview.text}">A</div>
431
+ <div class="pie-tool-color-scheme__preview-primary" style="background-color: {currentSchemeObj.preview.primary}"></div>
432
+ </div>
433
+ </div>
434
+ <div class="pie-tool-color-scheme__current-info">
435
+ <div class="pie-tool-color-scheme__current-name">{currentSchemeObj.name}</div>
436
+ <div class="pie-tool-color-scheme__current-description">{currentSchemeObj.description}</div>
437
+ </div>
438
+ <svg class="pie-tool-color-scheme__dropdown-arrow" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
439
+ <path d="M7,10L12,15L17,10H7Z"/>
440
+ </svg>
441
+ </div>
442
+ </button>
443
+
444
+ {#if dropdownOpen}
445
+ <div class="pie-tool-color-scheme__dropdown" role="menu">
446
+ {#each availableSchemes as scheme (scheme.id)}
447
+ <button
448
+ type="button"
449
+ class="pie-tool-color-scheme__option"
450
+ class:pie-tool-color-scheme__option--active={currentScheme === scheme.id}
451
+ role="menuitem"
452
+ aria-label={scheme.name}
453
+ aria-current={currentScheme === scheme.id}
454
+ onclick={() => selectScheme(scheme.id)}
455
+ >
456
+ <div class="pie-tool-color-scheme__preview">
457
+ <div class="pie-tool-color-scheme__preview-bg" style="background-color: {scheme.preview.bg}">
458
+ <div class="pie-tool-color-scheme__preview-text" style="color: {scheme.preview.text}">A</div>
459
+ <div class="pie-tool-color-scheme__preview-primary" style="background-color: {scheme.preview.primary}"></div>
460
+ </div>
461
+ </div>
462
+ <div class="pie-tool-color-scheme__info">
463
+ <div class="pie-tool-color-scheme__name">{scheme.name}</div>
464
+ <div class="pie-tool-color-scheme__description">{scheme.description}</div>
465
+ </div>
466
+ {#if currentScheme === scheme.id}
467
+ <svg xmlns="http://www.w3.org/2000/svg" class="pie-tool-color-scheme__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
468
+ <polyline points="20 6 9 17 4 12"/>
469
+ </svg>
470
+ {/if}
471
+ </button>
472
+ {/each}
473
+ </div>
474
+ {/if}
475
+ </div>
476
+ </div>
477
+ {/if}
478
+
479
+ <style>
480
+ .pie-tool-color-scheme {
481
+ position: fixed;
482
+ top: 50%;
483
+ left: 50%;
484
+ transform: translate(-50%, -50%);
485
+ width: 90%;
486
+ max-width: 32rem;
487
+ background-color: var(--pie-background, white);
488
+ border: 2px solid var(--pie-border, #ccc);
489
+ border-radius: 0.5rem;
490
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
491
+ display: flex;
492
+ flex-direction: column;
493
+ }
494
+
495
+ .pie-tool-color-scheme__header {
496
+ display: flex;
497
+ justify-content: space-between;
498
+ align-items: center;
499
+ padding: 1rem;
500
+ border-bottom: 2px solid var(--pie-border, #ccc);
501
+ }
502
+
503
+ .pie-tool-color-scheme__title {
504
+ font-size: 1.25rem;
505
+ font-weight: 700;
506
+ color: var(--pie-text, black);
507
+ margin: 0;
508
+ }
509
+
510
+ .pie-tool-color-scheme__close {
511
+ width: 2rem;
512
+ height: 2rem;
513
+ border: none;
514
+ background: transparent;
515
+ color: var(--pie-text, black);
516
+ font-size: 1.5rem;
517
+ line-height: 1;
518
+ cursor: pointer;
519
+ border-radius: 0.25rem;
520
+ }
521
+
522
+ .pie-tool-color-scheme__close:hover {
523
+ background-color: var(--pie-secondary-background, #f0f0f0);
524
+ }
525
+
526
+ .pie-tool-color-scheme__content {
527
+ padding: 1rem;
528
+ position: relative;
529
+ display: flex;
530
+ flex-direction: column;
531
+ gap: 1rem;
532
+ }
533
+
534
+ .pie-tool-color-scheme__description {
535
+ margin: 0 0 1rem 0;
536
+ color: var(--pie-text, black);
537
+ opacity: 0.7;
538
+ font-size: 0.875rem;
539
+ }
540
+
541
+ .pie-tool-color-scheme__dropdown-trigger {
542
+ width: 100%;
543
+ padding: 0.75rem;
544
+ border: 2px solid var(--pie-border, #ccc);
545
+ border-radius: 0.5rem;
546
+ background-color: var(--pie-background, white);
547
+ color: var(--pie-text, black);
548
+ cursor: pointer;
549
+ transition: all 0.15s ease;
550
+ }
551
+
552
+ .pie-tool-color-scheme__dropdown-trigger:hover {
553
+ background-color: var(--pie-secondary-background, #f0f0f0);
554
+ }
555
+
556
+ .pie-tool-color-scheme__current {
557
+ display: flex;
558
+ align-items: center;
559
+ gap: 0.75rem;
560
+ }
561
+
562
+ .pie-tool-color-scheme__current-info {
563
+ flex: 1;
564
+ text-align: left;
565
+ }
566
+
567
+ .pie-tool-color-scheme__current-name {
568
+ font-size: 0.875rem;
569
+ font-weight: 600;
570
+ color: var(--pie-text, black);
571
+ }
572
+
573
+ .pie-tool-color-scheme__current-description {
574
+ font-size: 0.75rem;
575
+ color: var(--pie-text, black);
576
+ opacity: 0.6;
577
+ }
578
+
579
+ .pie-tool-color-scheme__dropdown-arrow {
580
+ width: 1.5rem;
581
+ height: 1.5rem;
582
+ flex-shrink: 0;
583
+ }
584
+
585
+ .pie-tool-color-scheme__dropdown {
586
+ position: relative;
587
+ width: 100%;
588
+ max-height: 24rem;
589
+ background-color: var(--pie-background, white);
590
+ border: 2px solid var(--pie-border, #ccc);
591
+ border-radius: 0.5rem;
592
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
593
+ overflow-y: auto;
594
+ overflow-x: hidden;
595
+ margin-top: 0.5rem;
596
+ }
597
+
598
+ .pie-tool-color-scheme__option {
599
+ display: flex;
600
+ align-items: center;
601
+ gap: 0.75rem;
602
+ width: 100%;
603
+ padding: 0.75rem 1rem;
604
+ border: none;
605
+ background-color: transparent;
606
+ color: var(--pie-text, black);
607
+ cursor: pointer;
608
+ text-align: left;
609
+ transition: background-color 0.15s ease;
610
+ }
611
+
612
+ .pie-tool-color-scheme__option:hover {
613
+ background-color: var(--pie-secondary-background, rgba(0, 0, 0, 0.05));
614
+ }
615
+
616
+ .pie-tool-color-scheme__option--active {
617
+ background-color: var(--pie-primary-light, rgba(63, 81, 181, 0.1));
618
+ }
619
+
620
+ .pie-tool-color-scheme__preview {
621
+ flex-shrink: 0;
622
+ width: 2.5rem;
623
+ height: 2.5rem;
624
+ border: 2px solid var(--pie-border, #ccc);
625
+ border-radius: 0.25rem;
626
+ overflow: hidden;
627
+ }
628
+
629
+ .pie-tool-color-scheme__preview-bg {
630
+ width: 100%;
631
+ height: 100%;
632
+ position: relative;
633
+ display: flex;
634
+ align-items: center;
635
+ justify-content: center;
636
+ }
637
+
638
+ .pie-tool-color-scheme__preview-text {
639
+ font-weight: 700;
640
+ font-size: 1.25rem;
641
+ }
642
+
643
+ .pie-tool-color-scheme__preview-primary {
644
+ position: absolute;
645
+ bottom: 0;
646
+ right: 0;
647
+ width: 0.75rem;
648
+ height: 0.75rem;
649
+ border-radius: 50%;
650
+ }
651
+
652
+ .pie-tool-color-scheme__info {
653
+ flex: 1;
654
+ }
655
+
656
+ .pie-tool-color-scheme__name {
657
+ font-size: 0.875rem;
658
+ font-weight: 600;
659
+ color: var(--pie-text, black);
660
+ line-height: 1.25;
661
+ }
662
+
663
+ .pie-tool-color-scheme__description {
664
+ font-size: 0.75rem;
665
+ color: var(--pie-text, black);
666
+ opacity: 0.6;
667
+ }
668
+
669
+ .pie-tool-color-scheme__check {
670
+ flex-shrink: 0;
671
+ width: 1rem;
672
+ height: 1rem;
673
+ color: var(--pie-primary, #3f51b5);
674
+ }
675
+
676
+ /* Keyboard focus styling */
677
+ .pie-tool-color-scheme__close:focus-visible,
678
+ .pie-tool-color-scheme__dropdown-trigger:focus-visible,
679
+ .pie-tool-color-scheme__option:focus-visible {
680
+ outline: 2px solid var(--pie-primary, #3f51b5);
681
+ outline-offset: 2px;
682
+ }
683
+ </style>