@treeseed/core 0.8.7 → 0.8.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.
Files changed (73) hide show
  1. package/dist/components/content/ContentStatusLegend.astro +4 -4
  2. package/dist/components/docs/BookFontControls.astro +9 -9
  3. package/dist/components/docs/DesktopSidebarToggle.astro +8 -8
  4. package/dist/components/docs/Footer.astro +6 -6
  5. package/dist/components/docs/PageTitle.astro +1 -1
  6. package/dist/components/docs/ThemeSelect.astro +3 -1
  7. package/dist/components/forms/ContactForm.astro +21 -21
  8. package/dist/components/forms/FooterSubscribeForm.astro +9 -9
  9. package/dist/components/site/BookList.astro +7 -7
  10. package/dist/components/site/CTASection.astro +4 -4
  11. package/dist/components/site/ChronicleList.astro +6 -6
  12. package/dist/components/site/Hero.astro +3 -3
  13. package/dist/components/site/PathCard.astro +5 -5
  14. package/dist/components/site/ProfileList.astro +5 -5
  15. package/dist/components/site/RouteNotFound.astro +5 -5
  16. package/dist/components/site/SectionIntro.astro +3 -3
  17. package/dist/components/site/StageBanner.astro +2 -2
  18. package/dist/components/site/TrustCallout.astro +3 -3
  19. package/dist/components/ui/data/ActionList.astro +51 -0
  20. package/dist/components/ui/data/Badge.astro +19 -0
  21. package/dist/components/ui/data/DataTable.astro +51 -0
  22. package/dist/components/ui/data/KeyValueList.astro +28 -0
  23. package/dist/components/ui/data/MetricCard.astro +25 -0
  24. package/dist/components/ui/data/MetricGrid.astro +27 -0
  25. package/dist/components/ui/data/StatusPill.astro +20 -0
  26. package/dist/components/ui/forms/Button.astro +52 -0
  27. package/dist/components/ui/forms/Field.astro +39 -0
  28. package/dist/components/ui/forms/FormActions.astro +12 -0
  29. package/dist/components/ui/forms/PasswordMeter.astro +80 -0
  30. package/dist/components/ui/forms/RadioGroup.astro +55 -0
  31. package/dist/components/ui/forms/Select.astro +44 -0
  32. package/dist/components/ui/forms/TextInput.astro +58 -0
  33. package/dist/components/ui/forms/Textarea.astro +45 -0
  34. package/dist/components/ui/layout/PageHeader.astro +45 -0
  35. package/dist/components/ui/shell/AppShell.astro +110 -0
  36. package/dist/components/ui/shell/BottomNav.astro +35 -0
  37. package/dist/components/ui/shell/ProjectHeader.astro +66 -0
  38. package/dist/components/ui/shell/PublicShell.astro +108 -0
  39. package/dist/components/ui/shell/RailNav.astro +35 -0
  40. package/dist/components/ui/shell/TopBar.astro +52 -0
  41. package/dist/components/ui/surface/Card.astro +46 -0
  42. package/dist/components/ui/surface/EmptyState.astro +45 -0
  43. package/dist/components/ui/surface/Panel.astro +54 -0
  44. package/dist/components/ui/theme/ThemeMenu.astro +32 -0
  45. package/dist/components/ui/theme/ThemePreviewSwatch.astro +18 -0
  46. package/dist/components/ui/theme/ThemeScript.astro +105 -0
  47. package/dist/components/ui/theme/ThemeSelector.astro +202 -0
  48. package/dist/components/ui/types.js +0 -0
  49. package/dist/dev.js +14 -2
  50. package/dist/layouts/AuthoredEntryLayout.astro +27 -27
  51. package/dist/layouts/BookLayout.astro +10 -10
  52. package/dist/layouts/ContentLayout.astro +4 -4
  53. package/dist/layouts/MainLayout.astro +27 -25
  54. package/dist/layouts/NoteLayout.astro +6 -6
  55. package/dist/layouts/ProfileLayout.astro +17 -17
  56. package/dist/pages/404.astro +6 -6
  57. package/dist/pages/contact.astro +4 -4
  58. package/dist/pages/docs-runtime/[...slug].astro +12 -12
  59. package/dist/pages/docs-runtime/index.astro +13 -13
  60. package/dist/pages/index.astro +28 -28
  61. package/dist/pages/ui/index.astro +216 -0
  62. package/dist/site.js +3 -2
  63. package/dist/styles/app-shell.css +398 -0
  64. package/dist/styles/forms.css +258 -0
  65. package/dist/styles/global.css +119 -119
  66. package/dist/styles/prose.css +11 -11
  67. package/dist/styles/theme.css +177 -0
  68. package/dist/styles/tokens.css +62 -22
  69. package/dist/styles/ui.css +551 -0
  70. package/dist/utils/content-status.js +5 -5
  71. package/dist/utils/site-config.js +2 -2
  72. package/dist/utils/theme.js +352 -40
  73. package/package.json +35 -2
@@ -0,0 +1,110 @@
1
+ ---
2
+ import '../../../styles/tokens.css';
3
+ import '../../../styles/theme.css';
4
+ import '../../../styles/ui.css';
5
+ import '../../../styles/forms.css';
6
+ import '../../../styles/app-shell.css';
7
+ import ThemeScript from '../theme/ThemeScript.astro';
8
+ import RailNav from './RailNav.astro';
9
+ import BottomNav from './BottomNav.astro';
10
+ import TopBar from './TopBar.astro';
11
+ import Button from '../forms/Button.astro';
12
+ import type { ButtonAction } from '../types.js';
13
+ import type { ThemePreference } from '../../../utils/theme.js';
14
+
15
+ interface Brand {
16
+ name: string;
17
+ tag?: string;
18
+ href: string;
19
+ logoSrc?: string;
20
+ logoAlt?: string;
21
+ mark?: string;
22
+ }
23
+
24
+ interface NavItem {
25
+ label: string;
26
+ href: string;
27
+ ariaLabel?: string;
28
+ }
29
+
30
+ interface Props {
31
+ title: string;
32
+ description: string;
33
+ currentPath: string;
34
+ appearance: ThemePreference;
35
+ brand: Brand;
36
+ navItems: NavItem[];
37
+ quickActions?: ButtonAction[];
38
+ bottomNavItems?: NavItem[];
39
+ }
40
+
41
+ const {
42
+ title,
43
+ description,
44
+ currentPath,
45
+ appearance,
46
+ brand,
47
+ navItems,
48
+ quickActions = [],
49
+ bottomNavItems = navItems.slice(0, 5),
50
+ } = Astro.props as Props;
51
+ ---
52
+
53
+ <!doctype html>
54
+ <html lang="en">
55
+ <head>
56
+ <meta charset="utf-8" />
57
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
58
+ <title>{title}</title>
59
+ <meta name="description" content={description} />
60
+ <ThemeScript defaultScheme={appearance.scheme} defaultMode={appearance.mode} preferDefaultPreference />
61
+ </head>
62
+ <body>
63
+ <a class="ts-skip-link" href="#main-content">Skip to content</a>
64
+ <div class="ts-app-shell">
65
+ <aside class="ts-app-shell__rail">
66
+ <TopBar brand={brand} />
67
+ <div class="ts-app-shell__rail-context">
68
+ <slot name="railContext" />
69
+ </div>
70
+ <RailNav items={navItems} currentPath={currentPath} />
71
+ {quickActions.length > 0 ? (
72
+ <div class="ts-app-shell__quick-actions">
73
+ <p class="ts-app-shell__eyebrow">Quick actions</p>
74
+ <div class="ts-app-shell__quick-list">
75
+ {quickActions.map((action) => (
76
+ <Button
77
+ href={action.href}
78
+ type={action.type}
79
+ variant={action.variant ?? 'secondary'}
80
+ ariaLabel={action.ariaLabel}
81
+ disabled={action.disabled}
82
+ size="sm"
83
+ >
84
+ {action.label}
85
+ </Button>
86
+ ))}
87
+ </div>
88
+ </div>
89
+ ) : null}
90
+ </aside>
91
+ <main class="ts-app-shell__main" id="main-content">
92
+ <TopBar brand={brand} class="ts-app-shell__mobile-top">
93
+ </TopBar>
94
+ <header class="ts-app-shell__header">
95
+ <div class="ts-app-shell__title">
96
+ <h1>{title}</h1>
97
+ <p>{description}</p>
98
+ </div>
99
+ <div class="ts-app-shell__header-actions">
100
+ <slot name="headerAction" />
101
+ </div>
102
+ </header>
103
+ <slot name="projectContext" />
104
+ <slot />
105
+ </main>
106
+ {bottomNavItems.length > 0 ? <BottomNav items={bottomNavItems} currentPath={currentPath} /> : null}
107
+ </div>
108
+ <slot name="sensitiveModal" />
109
+ </body>
110
+ </html>
@@ -0,0 +1,35 @@
1
+ ---
2
+ interface NavItem {
3
+ label: string;
4
+ href: string;
5
+ ariaLabel?: string;
6
+ }
7
+
8
+ interface Props {
9
+ items: NavItem[];
10
+ currentPath: string;
11
+ label?: string;
12
+ class?: string;
13
+ }
14
+
15
+ const {
16
+ items,
17
+ currentPath,
18
+ label = 'Primary',
19
+ class: className,
20
+ } = Astro.props as Props;
21
+
22
+ function isCurrentPath(href: string) {
23
+ if (href === '/') return currentPath === '/';
24
+ if (href.endsWith('/')) return currentPath === href || currentPath.startsWith(href);
25
+ return currentPath === href || currentPath.startsWith(`${href}/`);
26
+ }
27
+ ---
28
+
29
+ <nav class:list={['ts-bottom-nav', className]} aria-label={label}>
30
+ {items.map((item) => (
31
+ <a class="ts-bottom-nav__link" href={item.href} aria-label={item.ariaLabel} aria-current={isCurrentPath(item.href) ? 'page' : undefined}>
32
+ <span>{item.label}</span>
33
+ </a>
34
+ ))}
35
+ </nav>
@@ -0,0 +1,66 @@
1
+ ---
2
+ import Badge from '../data/Badge.astro';
3
+ import Button from '../forms/Button.astro';
4
+ import type { ButtonAction } from '../types.js';
5
+
6
+ interface TabItem {
7
+ label: string;
8
+ href: string;
9
+ }
10
+
11
+ interface Props {
12
+ title: string;
13
+ description?: string;
14
+ badges?: string[];
15
+ actions?: ButtonAction[];
16
+ tabs?: TabItem[];
17
+ currentPath?: string;
18
+ class?: string;
19
+ }
20
+
21
+ const {
22
+ title,
23
+ description,
24
+ badges = [],
25
+ actions = [],
26
+ tabs = [],
27
+ currentPath = '',
28
+ class: className,
29
+ } = Astro.props as Props;
30
+ ---
31
+
32
+ <section class:list={['ts-project-header', className]}>
33
+ <div class="ts-project-header__main">
34
+ {badges.length > 0 ? (
35
+ <div class="ts-project-header__badges">
36
+ {badges.map((badge) => <Badge>{badge}</Badge>)}
37
+ </div>
38
+ ) : null}
39
+ <h2>{title}</h2>
40
+ {description ? <p>{description}</p> : null}
41
+ </div>
42
+ {actions.length > 0 || Astro.slots.has('actions') ? (
43
+ <div class="ts-project-header__actions">
44
+ {actions.map((action) => (
45
+ <Button
46
+ href={action.href}
47
+ type={action.type}
48
+ variant={action.variant ?? 'secondary'}
49
+ ariaLabel={action.ariaLabel}
50
+ disabled={action.disabled}
51
+ size="sm"
52
+ >
53
+ {action.label}
54
+ </Button>
55
+ ))}
56
+ <slot name="actions" />
57
+ </div>
58
+ ) : null}
59
+ {tabs.length > 0 ? (
60
+ <nav class="ts-shell-tabs" aria-label="Project">
61
+ {tabs.map((tab) => (
62
+ <a class="ts-shell-tab" href={tab.href} aria-current={currentPath === tab.href ? 'page' : undefined}>{tab.label}</a>
63
+ ))}
64
+ </nav>
65
+ ) : null}
66
+ </section>
@@ -0,0 +1,108 @@
1
+ ---
2
+ import '../../../styles/tokens.css';
3
+ import '../../../styles/theme.css';
4
+ import '../../../styles/ui.css';
5
+ import '../../../styles/forms.css';
6
+ import '../../../styles/app-shell.css';
7
+ import ThemeScript from '../theme/ThemeScript.astro';
8
+ import ThemeMenu from '../theme/ThemeMenu.astro';
9
+ import TopBar from './TopBar.astro';
10
+ import Button from '../forms/Button.astro';
11
+ import type { ButtonAction } from '../types.js';
12
+ import type { ThemePreference } from '../../../utils/theme.js';
13
+
14
+ interface Brand {
15
+ name: string;
16
+ tag?: string;
17
+ href: string;
18
+ logoSrc?: string;
19
+ logoAlt?: string;
20
+ mark?: string;
21
+ }
22
+
23
+ interface NavItem {
24
+ label: string;
25
+ href: string;
26
+ ariaLabel?: string;
27
+ }
28
+
29
+ interface Props {
30
+ title: string;
31
+ description: string;
32
+ currentPath: string;
33
+ appearance: ThemePreference;
34
+ brand: Brand;
35
+ navItems: NavItem[];
36
+ actions?: ButtonAction[];
37
+ showAppearanceControl?: boolean;
38
+ preferServerAppearance?: boolean;
39
+ }
40
+
41
+ const {
42
+ title,
43
+ description,
44
+ currentPath,
45
+ appearance,
46
+ brand,
47
+ navItems,
48
+ actions = [],
49
+ showAppearanceControl = true,
50
+ preferServerAppearance = false,
51
+ } = Astro.props as Props;
52
+
53
+ function isCurrentPath(href: string) {
54
+ if (href === '/') return currentPath === '/';
55
+ return currentPath === href || currentPath.startsWith(href);
56
+ }
57
+ ---
58
+
59
+ <!doctype html>
60
+ <html lang="en">
61
+ <head>
62
+ <meta charset="utf-8" />
63
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
64
+ <title>{title}</title>
65
+ <meta name="description" content={description} />
66
+ <ThemeScript
67
+ defaultScheme={appearance.scheme}
68
+ defaultMode={appearance.mode}
69
+ preferDefaultPreference={preferServerAppearance}
70
+ />
71
+ </head>
72
+ <body>
73
+ <a class="ts-skip-link" href="#main-content">Skip to content</a>
74
+ <div class="ts-public-shell">
75
+ <header class="ts-public-shell__header">
76
+ <TopBar brand={brand}>
77
+ <Fragment slot="actions">
78
+ {showAppearanceControl ? <ThemeMenu selectedScheme={appearance.scheme} selectedMode={appearance.mode} /> : null}
79
+ {actions.map((action) => (
80
+ <Button
81
+ href={action.href}
82
+ type={action.type}
83
+ variant={action.variant ?? 'secondary'}
84
+ ariaLabel={action.ariaLabel}
85
+ disabled={action.disabled}
86
+ size="sm"
87
+ >
88
+ {action.label}
89
+ </Button>
90
+ ))}
91
+ <slot name="actions" />
92
+ </Fragment>
93
+ </TopBar>
94
+ <nav class="ts-public-shell__nav" aria-label="Primary">
95
+ {navItems.map((item) => (
96
+ <a class="ts-public-shell__link" href={item.href} aria-label={item.ariaLabel} aria-current={isCurrentPath(item.href) ? 'page' : undefined}>
97
+ {item.label}
98
+ </a>
99
+ ))}
100
+ </nav>
101
+ </header>
102
+ <main class="ts-public-shell__main" id="main-content">
103
+ <slot />
104
+ </main>
105
+ <slot name="footer" />
106
+ </div>
107
+ </body>
108
+ </html>
@@ -0,0 +1,35 @@
1
+ ---
2
+ interface NavItem {
3
+ label: string;
4
+ href: string;
5
+ ariaLabel?: string;
6
+ }
7
+
8
+ interface Props {
9
+ items: NavItem[];
10
+ currentPath: string;
11
+ label?: string;
12
+ class?: string;
13
+ }
14
+
15
+ const {
16
+ items,
17
+ currentPath,
18
+ label = 'Application',
19
+ class: className,
20
+ } = Astro.props as Props;
21
+
22
+ function isCurrentPath(href: string) {
23
+ if (href === '/') return currentPath === '/';
24
+ if (href.endsWith('/')) return currentPath === href || currentPath.startsWith(href);
25
+ return currentPath === href || currentPath.startsWith(`${href}/`);
26
+ }
27
+ ---
28
+
29
+ <nav class:list={['ts-rail-nav', className]} aria-label={label}>
30
+ {items.map((item) => (
31
+ <a class="ts-rail-nav__link" href={item.href} aria-label={item.ariaLabel} aria-current={isCurrentPath(item.href) ? 'page' : undefined}>
32
+ <span>{item.label}</span>
33
+ </a>
34
+ ))}
35
+ </nav>
@@ -0,0 +1,52 @@
1
+ ---
2
+ import Button from '../forms/Button.astro';
3
+ import type { ButtonAction } from '../types.js';
4
+
5
+ interface Brand {
6
+ name: string;
7
+ tag?: string;
8
+ href: string;
9
+ logoSrc?: string;
10
+ logoAlt?: string;
11
+ mark?: string;
12
+ }
13
+
14
+ interface Props {
15
+ brand: Brand;
16
+ actions?: ButtonAction[];
17
+ class?: string;
18
+ }
19
+
20
+ const {
21
+ brand,
22
+ actions = [],
23
+ class: className,
24
+ } = Astro.props as Props;
25
+ ---
26
+
27
+ <div class:list={['ts-top-bar', className]}>
28
+ <a class="ts-shell-brand" href={brand.href}>
29
+ <span class="ts-shell-brand__mark">
30
+ {brand.logoSrc ? <img src={brand.logoSrc} alt={brand.logoAlt ?? ''} width="40" height="40" loading="eager" /> : brand.mark}
31
+ </span>
32
+ <span class="ts-shell-brand__text">
33
+ <span class="ts-shell-brand__name">{brand.name}</span>
34
+ {brand.tag ? <span class="ts-shell-brand__tag">{brand.tag}</span> : null}
35
+ </span>
36
+ </a>
37
+ <div class="ts-top-bar__actions">
38
+ <slot name="actions" />
39
+ {actions.map((action) => (
40
+ <Button
41
+ href={action.href}
42
+ type={action.type}
43
+ variant={action.variant ?? 'secondary'}
44
+ ariaLabel={action.ariaLabel}
45
+ disabled={action.disabled}
46
+ size="sm"
47
+ >
48
+ {action.label}
49
+ </Button>
50
+ ))}
51
+ </div>
52
+ </div>
@@ -0,0 +1,46 @@
1
+ ---
2
+ import type { Tone } from '../types.js';
3
+
4
+ interface Props {
5
+ href?: string;
6
+ eyebrow?: string;
7
+ title?: string;
8
+ description?: string;
9
+ tone?: Tone;
10
+ interactive?: boolean;
11
+ class?: string;
12
+ }
13
+
14
+ const {
15
+ href,
16
+ eyebrow,
17
+ title,
18
+ description,
19
+ tone = 'default',
20
+ interactive = false,
21
+ class: className,
22
+ } = Astro.props as Props;
23
+
24
+ const classes = ['ts-card', className].filter(Boolean).join(' ');
25
+ const isInteractive = href || interactive;
26
+ ---
27
+
28
+ {
29
+ href ? (
30
+ <a href={href} class={classes} data-tone={tone} data-interactive="true">
31
+ {eyebrow ? <p class="ts-card__eyebrow">{eyebrow}</p> : null}
32
+ {title ? <h3 class="ts-card__title">{title}</h3> : null}
33
+ {description ? <p class="ts-card__description">{description}</p> : null}
34
+ <div class="ts-card__body"><slot /></div>
35
+ <slot name="footer" />
36
+ </a>
37
+ ) : (
38
+ <article class={classes} data-tone={tone} data-interactive={isInteractive ? 'true' : undefined}>
39
+ {eyebrow ? <p class="ts-card__eyebrow">{eyebrow}</p> : null}
40
+ {title ? <h3 class="ts-card__title">{title}</h3> : null}
41
+ {description ? <p class="ts-card__description">{description}</p> : null}
42
+ <div class="ts-card__body"><slot /></div>
43
+ <slot name="footer" />
44
+ </article>
45
+ )
46
+ }
@@ -0,0 +1,45 @@
1
+ ---
2
+ import Button from '../forms/Button.astro';
3
+ import type { ButtonAction } from '../types.js';
4
+
5
+ interface Props {
6
+ eyebrow?: string;
7
+ title: string;
8
+ description?: string;
9
+ actions?: ButtonAction[];
10
+ class?: string;
11
+ }
12
+
13
+ const {
14
+ eyebrow,
15
+ title,
16
+ description,
17
+ actions = [],
18
+ class: className,
19
+ } = Astro.props as Props;
20
+ ---
21
+
22
+ <section class:list={['ts-empty-state', className]}>
23
+ {eyebrow ? <p class="ts-empty-state__eyebrow">{eyebrow}</p> : null}
24
+ <h2 class="ts-empty-state__title">{title}</h2>
25
+ {description ? <p class="ts-empty-state__description">{description}</p> : null}
26
+ <div class="ts-empty-state__body">
27
+ <slot />
28
+ </div>
29
+ {actions.length > 0 || Astro.slots.has('actions') ? (
30
+ <div class="ts-empty-state__actions">
31
+ {actions.map((action) => (
32
+ <Button
33
+ href={action.href}
34
+ type={action.type}
35
+ variant={action.variant ?? 'secondary'}
36
+ ariaLabel={action.ariaLabel}
37
+ disabled={action.disabled}
38
+ >
39
+ {action.label}
40
+ </Button>
41
+ ))}
42
+ <slot name="actions" />
43
+ </div>
44
+ ) : null}
45
+ </section>
@@ -0,0 +1,54 @@
1
+ ---
2
+ import Button from '../forms/Button.astro';
3
+ import type { ButtonAction, Tone } from '../types.js';
4
+
5
+ interface Props {
6
+ eyebrow?: string;
7
+ title?: string;
8
+ description?: string;
9
+ tone?: Tone;
10
+ actions?: ButtonAction[];
11
+ class?: string;
12
+ }
13
+
14
+ const {
15
+ eyebrow,
16
+ title,
17
+ description,
18
+ tone = 'default',
19
+ actions = [],
20
+ class: className,
21
+ } = Astro.props as Props;
22
+ ---
23
+
24
+ <section class:list={['ts-panel', className]} data-tone={tone}>
25
+ {eyebrow || title || description || actions.length > 0 || Astro.slots.has('actions') ? (
26
+ <header class="ts-panel__header">
27
+ <div class="ts-panel__heading">
28
+ {eyebrow ? <p class="ts-panel__eyebrow">{eyebrow}</p> : null}
29
+ {title ? <h2 class="ts-panel__title">{title}</h2> : null}
30
+ {description ? <p class="ts-panel__description">{description}</p> : null}
31
+ </div>
32
+ {actions.length > 0 || Astro.slots.has('actions') ? (
33
+ <div class="ts-panel__actions">
34
+ {actions.map((action) => (
35
+ <Button
36
+ href={action.href}
37
+ type={action.type}
38
+ variant={action.variant ?? 'secondary'}
39
+ ariaLabel={action.ariaLabel}
40
+ disabled={action.disabled}
41
+ size="sm"
42
+ >
43
+ {action.label}
44
+ </Button>
45
+ ))}
46
+ <slot name="actions" />
47
+ </div>
48
+ ) : null}
49
+ </header>
50
+ ) : null}
51
+ <div class="ts-panel__body">
52
+ <slot />
53
+ </div>
54
+ </section>
@@ -0,0 +1,32 @@
1
+ ---
2
+ import ThemeSelector from './ThemeSelector.astro';
3
+ import type { ThemePreference } from '../../../utils/theme.js';
4
+
5
+ interface Props {
6
+ selectedScheme?: ThemePreference['scheme'];
7
+ selectedMode?: ThemePreference['mode'];
8
+ label?: string;
9
+ }
10
+
11
+ const {
12
+ selectedScheme,
13
+ selectedMode,
14
+ label = 'Appearance',
15
+ } = Astro.props as Props;
16
+ ---
17
+
18
+ <details class="ts-theme-menu" data-ts-theme-menu>
19
+ <summary class="ts-theme-menu__trigger" aria-label={label} title={label}>
20
+ <svg aria-hidden="true" viewBox="0 0 24 24" focusable="false">
21
+ <path d="M12 3.25a8.75 8.75 0 0 0 0 17.5h1.1a2.05 2.05 0 0 0 1.46-3.49 0.55 0.55 0 0 1 .39-.94h1.45a4.35 4.35 0 0 0 4.35-4.35A8.72 8.72 0 0 0 12 3.25Zm-4.1 9.1a1.35 1.35 0 1 1 0-2.7 1.35 1.35 0 0 1 0 2.7Zm2.3-4.1a1.35 1.35 0 1 1 0-2.7 1.35 1.35 0 0 1 0 2.7Zm3.9 0a1.35 1.35 0 1 1 0-2.7 1.35 1.35 0 0 1 0 2.7Zm2.6 4.1a1.35 1.35 0 1 1 0-2.7 1.35 1.35 0 0 1 0 2.7Z" />
22
+ </svg>
23
+ </summary>
24
+ <div class="ts-theme-menu__panel">
25
+ <ThemeSelector
26
+ label={label}
27
+ selectedScheme={selectedScheme}
28
+ selectedMode={selectedMode}
29
+ compact={false}
30
+ />
31
+ </div>
32
+ </details>
@@ -0,0 +1,18 @@
1
+ ---
2
+ interface Props {
3
+ swatches: string[];
4
+ label?: string;
5
+ }
6
+
7
+ const { swatches, label = 'Color scheme preview' } = Astro.props as Props;
8
+ const visibleSwatches = swatches.slice(0, 4);
9
+ const swatchStyle = visibleSwatches
10
+ .map((swatch, index) => `--ts-preview-swatch-${index + 1}: ${swatch}`)
11
+ .join('; ');
12
+ ---
13
+
14
+ <span class="ts-theme-swatch" style={swatchStyle} role="img" aria-label={label}>
15
+ {visibleSwatches.map((_, index) => (
16
+ <span class="ts-theme-swatch__dot" data-swatch-index={index + 1} aria-hidden="true"></span>
17
+ ))}
18
+ </span>