@dorsk/tsumikit 0.2.15 → 0.3.0
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.
- package/dist/components/molecules/FileButton.svelte +12 -8
- package/dist/components/molecules/FileButton.svelte.d.ts +6 -3
- package/dist/components/molecules/IconButton.svelte +21 -3
- package/dist/components/molecules/IconButton.svelte.d.ts +4 -1
- package/dist/components/molecules/SelectButton.svelte +21 -4
- package/dist/components/molecules/SelectButton.svelte.d.ts +7 -2
- package/dist/components/molecules/ThemePicker.svelte +11 -2
- package/dist/stores/theme.svelte.d.ts +78 -13
- package/dist/stores/theme.svelte.js +47 -17
- package/dist/styles/variables.css +259 -0
- package/package.json +1 -1
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
// File-picker button. A real <input type="file"> visually hidden inside a
|
|
3
3
|
// <label> styled as a button — so it's keyboard-focusable and works with zero
|
|
4
4
|
// JS to open the dialog. Emits the chosen files via `onfiles`. Dependency-free.
|
|
5
|
-
import Icon
|
|
5
|
+
import Icon from '../atoms/Icon.svelte';
|
|
6
6
|
import type { IconName } from '../atoms/Icon.svelte';
|
|
7
7
|
|
|
8
8
|
let {
|
|
9
9
|
onfiles,
|
|
10
10
|
label = 'Choose file',
|
|
11
|
-
icon
|
|
11
|
+
icon,
|
|
12
|
+
emoji = '📎',
|
|
12
13
|
iconOnly = false,
|
|
13
14
|
accept,
|
|
14
15
|
multiple = false,
|
|
@@ -19,9 +20,12 @@
|
|
|
19
20
|
}: {
|
|
20
21
|
onfiles: (files: File[]) => void;
|
|
21
22
|
label?: string;
|
|
22
|
-
/** A registered glyph name (rendered as SVG)
|
|
23
|
-
*
|
|
24
|
-
icon?: IconName
|
|
23
|
+
/** A registered glyph name (rendered as SVG). Use `emoji` for an
|
|
24
|
+
* off-registry glyph. */
|
|
25
|
+
icon?: IconName;
|
|
26
|
+
/** Off-registry glyph such as an emoji, rendered as text as-is. Defaults to
|
|
27
|
+
* the 📎 paperclip emoji; ignored when `icon` is set. */
|
|
28
|
+
emoji?: string;
|
|
25
29
|
/** Hide the label visually, showing only the icon. The label is kept for
|
|
26
30
|
* assistive tech (and used as the accessible name). */
|
|
27
31
|
iconOnly?: boolean;
|
|
@@ -61,10 +65,10 @@
|
|
|
61
65
|
{disabled}
|
|
62
66
|
onchange={onchange}
|
|
63
67
|
/>
|
|
64
|
-
{#if
|
|
68
|
+
{#if icon}
|
|
65
69
|
<Icon name={icon} />
|
|
66
|
-
{:else}
|
|
67
|
-
<span class="emoji" aria-hidden="true">{
|
|
70
|
+
{:else if emoji}
|
|
71
|
+
<span class="emoji" aria-hidden="true">{emoji}</span>
|
|
68
72
|
{/if}
|
|
69
73
|
<span class:sr-only={iconOnly}>{label}</span>
|
|
70
74
|
</label>
|
|
@@ -2,9 +2,12 @@ import type { IconName } from '../atoms/Icon.svelte';
|
|
|
2
2
|
type $$ComponentProps = {
|
|
3
3
|
onfiles: (files: File[]) => void;
|
|
4
4
|
label?: string;
|
|
5
|
-
/** A registered glyph name (rendered as SVG)
|
|
6
|
-
*
|
|
7
|
-
icon?: IconName
|
|
5
|
+
/** A registered glyph name (rendered as SVG). Use `emoji` for an
|
|
6
|
+
* off-registry glyph. */
|
|
7
|
+
icon?: IconName;
|
|
8
|
+
/** Off-registry glyph such as an emoji, rendered as text as-is. Defaults to
|
|
9
|
+
* the 📎 paperclip emoji; ignored when `icon` is set. */
|
|
10
|
+
emoji?: string;
|
|
8
11
|
/** Hide the label visually, showing only the icon. The label is kept for
|
|
9
12
|
* assistive tech (and used as the accessible name). */
|
|
10
13
|
iconOnly?: boolean;
|
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
import type { IconName } from '../atoms/Icon.svelte';
|
|
7
7
|
|
|
8
8
|
type IconButtonProps = HTMLButtonAttributes & {
|
|
9
|
-
/** Named glyph from the registry
|
|
9
|
+
/** Named glyph from the registry (rendered as SVG). */
|
|
10
10
|
icon?: IconName;
|
|
11
|
+
/** Off-registry glyph such as an emoji, rendered as text as-is. Use instead
|
|
12
|
+
* of `icon` when the glyph isn't in the registry. */
|
|
13
|
+
emoji?: string;
|
|
11
14
|
/** Raw SVG markup (24×24 viewBox) — overrides `icon`. Pass a
|
|
12
15
|
* lucide-svelte component's contents to render any off-registry icon. */
|
|
13
16
|
children?: Snippet;
|
|
@@ -32,6 +35,7 @@
|
|
|
32
35
|
|
|
33
36
|
let {
|
|
34
37
|
icon,
|
|
38
|
+
emoji,
|
|
35
39
|
children,
|
|
36
40
|
label,
|
|
37
41
|
title = label,
|
|
@@ -50,7 +54,7 @@
|
|
|
50
54
|
</script>
|
|
51
55
|
|
|
52
56
|
<!-- Composition: the icon-only button is a Button (canonical control styling)
|
|
53
|
-
in its icon variant, wrapping an Icon. -->
|
|
57
|
+
in its icon variant, wrapping an Icon (or a text emoji glyph). -->
|
|
54
58
|
<Button
|
|
55
59
|
{...rest}
|
|
56
60
|
{variant}
|
|
@@ -68,7 +72,21 @@
|
|
|
68
72
|
>
|
|
69
73
|
{#if children}
|
|
70
74
|
<Icon {size}>{@render children()}</Icon>
|
|
71
|
-
{:else}
|
|
75
|
+
{:else if emoji}
|
|
76
|
+
<span class="emoji" style="font-size: {size * 1.35}px" aria-hidden="true">{emoji}</span>
|
|
77
|
+
{:else if icon}
|
|
72
78
|
<Icon name={icon} {size} />
|
|
73
79
|
{/if}
|
|
74
80
|
</Button>
|
|
81
|
+
|
|
82
|
+
<style>
|
|
83
|
+
/* Off-registry glyph (emoji) rendered as text rather than an SVG. Sized off the
|
|
84
|
+
`size` prop (×1.35, since an emoji reads small next to an SVG glyph of the
|
|
85
|
+
same px) and centered so it shares the button's tap target. */
|
|
86
|
+
.emoji {
|
|
87
|
+
display: inline-flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
line-height: 1;
|
|
91
|
+
}
|
|
92
|
+
</style>
|
|
@@ -2,8 +2,11 @@ import type { Snippet } from 'svelte';
|
|
|
2
2
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
3
3
|
import type { IconName } from '../atoms/Icon.svelte';
|
|
4
4
|
type IconButtonProps = HTMLButtonAttributes & {
|
|
5
|
-
/** Named glyph from the registry
|
|
5
|
+
/** Named glyph from the registry (rendered as SVG). */
|
|
6
6
|
icon?: IconName;
|
|
7
|
+
/** Off-registry glyph such as an emoji, rendered as text as-is. Use instead
|
|
8
|
+
* of `icon` when the glyph isn't in the registry. */
|
|
9
|
+
emoji?: string;
|
|
7
10
|
/** Raw SVG markup (24×24 viewBox) — overrides `icon`. Pass a
|
|
8
11
|
* lucide-svelte component's contents to render any off-registry icon. */
|
|
9
12
|
children?: Snippet;
|
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
import Button from '../atoms/Button.svelte';
|
|
8
8
|
import Select from '../atoms/Select.svelte';
|
|
9
9
|
|
|
10
|
+
type Option = { value: string; label: string };
|
|
11
|
+
|
|
10
12
|
let {
|
|
11
13
|
glyph,
|
|
12
14
|
label,
|
|
13
15
|
title,
|
|
14
16
|
value,
|
|
15
17
|
options,
|
|
18
|
+
groups,
|
|
16
19
|
onchange,
|
|
17
20
|
class: klass = ''
|
|
18
21
|
}: {
|
|
@@ -21,7 +24,11 @@
|
|
|
21
24
|
label: string;
|
|
22
25
|
title?: string;
|
|
23
26
|
value: string;
|
|
24
|
-
|
|
27
|
+
// Flat option list. Ignored when `groups` is given.
|
|
28
|
+
options?: Option[];
|
|
29
|
+
// Sectioned options, rendered as native <optgroup> blocks (TSU-1). Used by
|
|
30
|
+
// the theme picker to split light/dark; falls back to `options` otherwise.
|
|
31
|
+
groups?: { label: string; options: Option[] }[];
|
|
25
32
|
onchange: (value: string) => void;
|
|
26
33
|
class?: string;
|
|
27
34
|
} = $props();
|
|
@@ -38,9 +45,19 @@
|
|
|
38
45
|
{value}
|
|
39
46
|
onchange={(e) => onchange((e.currentTarget as HTMLSelectElement).value)}
|
|
40
47
|
>
|
|
41
|
-
{#
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
{#if groups}
|
|
49
|
+
{#each groups as g (g.label)}
|
|
50
|
+
<optgroup label={g.label}>
|
|
51
|
+
{#each g.options as o (o.value)}
|
|
52
|
+
<option value={o.value}>{o.label}</option>
|
|
53
|
+
{/each}
|
|
54
|
+
</optgroup>
|
|
55
|
+
{/each}
|
|
56
|
+
{:else}
|
|
57
|
+
{#each options ?? [] as o (o.value)}
|
|
58
|
+
<option value={o.value}>{o.label}</option>
|
|
59
|
+
{/each}
|
|
60
|
+
{/if}
|
|
44
61
|
</Select>
|
|
45
62
|
</Button>
|
|
46
63
|
</span>
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
type Option = {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
};
|
|
1
5
|
type $$ComponentProps = {
|
|
2
6
|
glyph: string;
|
|
3
7
|
label: string;
|
|
4
8
|
title?: string;
|
|
5
9
|
value: string;
|
|
6
|
-
options
|
|
7
|
-
|
|
10
|
+
options?: Option[];
|
|
11
|
+
groups?: {
|
|
8
12
|
label: string;
|
|
13
|
+
options: Option[];
|
|
9
14
|
}[];
|
|
10
15
|
onchange: (value: string) => void;
|
|
11
16
|
class?: string;
|
|
@@ -8,7 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
let { class: klass = '' }: { class?: string } = $props();
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// Split the registry into light/dark sections (TSU-1) so the long list is
|
|
12
|
+
// scannable. `<optgroup>` keeps native popup/keyboard behaviour for free.
|
|
13
|
+
const toOption = (t: (typeof THEMES)[number]) => ({
|
|
14
|
+
value: t.id,
|
|
15
|
+
label: `${t.icon} ${t.label}`
|
|
16
|
+
});
|
|
17
|
+
const groups = [
|
|
18
|
+
{ label: '— light', options: THEMES.filter((t) => t.mode === 'light').map(toOption) },
|
|
19
|
+
{ label: '— dark', options: THEMES.filter((t) => t.mode === 'dark').map(toOption) }
|
|
20
|
+
];
|
|
12
21
|
</script>
|
|
13
22
|
|
|
14
23
|
<SelectButton
|
|
@@ -17,6 +26,6 @@
|
|
|
17
26
|
label="Theme"
|
|
18
27
|
title={`Theme: ${theme.label}`}
|
|
19
28
|
value={theme.current}
|
|
20
|
-
{
|
|
29
|
+
{groups}
|
|
21
30
|
onchange={(v) => theme.set(v as (typeof THEMES)[number]['id'])}
|
|
22
31
|
/>
|
|
@@ -1,88 +1,153 @@
|
|
|
1
1
|
export declare const THEMES: readonly [{
|
|
2
|
-
readonly id: "dark";
|
|
3
|
-
readonly label: "Dark";
|
|
4
|
-
readonly icon: "☾";
|
|
5
|
-
readonly themeColor: "#0f1115";
|
|
6
|
-
}, {
|
|
7
2
|
readonly id: "light";
|
|
8
3
|
readonly label: "Light";
|
|
9
4
|
readonly icon: "☀";
|
|
10
5
|
readonly themeColor: "#f6f7f9";
|
|
6
|
+
readonly mode: "light";
|
|
7
|
+
}, {
|
|
8
|
+
readonly id: "highcontrast";
|
|
9
|
+
readonly label: "High Contrast Light";
|
|
10
|
+
readonly icon: "◻";
|
|
11
|
+
readonly themeColor: "#ffffff";
|
|
12
|
+
readonly mode: "light";
|
|
13
|
+
}, {
|
|
14
|
+
readonly id: "gruvboxlight";
|
|
15
|
+
readonly label: "Gruvbox Light";
|
|
16
|
+
readonly icon: "◇";
|
|
17
|
+
readonly themeColor: "#fbf1c7";
|
|
18
|
+
readonly mode: "light";
|
|
19
|
+
}, {
|
|
20
|
+
readonly id: "solarizedlight";
|
|
21
|
+
readonly label: "Solarized Light";
|
|
22
|
+
readonly icon: "◑";
|
|
23
|
+
readonly themeColor: "#fdf6e3";
|
|
24
|
+
readonly mode: "light";
|
|
25
|
+
}, {
|
|
26
|
+
readonly id: "everforestlight";
|
|
27
|
+
readonly label: "Everforest Light";
|
|
28
|
+
readonly icon: "✾";
|
|
29
|
+
readonly themeColor: "#fdf6e3";
|
|
30
|
+
readonly mode: "light";
|
|
31
|
+
}, {
|
|
32
|
+
readonly id: "rosepinedawn";
|
|
33
|
+
readonly label: "Rosé Pine Dawn";
|
|
34
|
+
readonly icon: "✿";
|
|
35
|
+
readonly themeColor: "#faf4ed";
|
|
36
|
+
readonly mode: "light";
|
|
37
|
+
}, {
|
|
38
|
+
readonly id: "latte";
|
|
39
|
+
readonly label: "Catppuccin Latte";
|
|
40
|
+
readonly icon: "L";
|
|
41
|
+
readonly themeColor: "#eff1f5";
|
|
42
|
+
readonly mode: "light";
|
|
43
|
+
}, {
|
|
44
|
+
readonly id: "nordlight";
|
|
45
|
+
readonly label: "Nord Light";
|
|
46
|
+
readonly icon: "n";
|
|
47
|
+
readonly themeColor: "#eceff4";
|
|
48
|
+
readonly mode: "light";
|
|
49
|
+
}, {
|
|
50
|
+
readonly id: "tokyoday";
|
|
51
|
+
readonly label: "Tokyo Night Day";
|
|
52
|
+
readonly icon: "✧";
|
|
53
|
+
readonly themeColor: "#e1e2e7";
|
|
54
|
+
readonly mode: "light";
|
|
55
|
+
}, {
|
|
56
|
+
readonly id: "kanagawalotus";
|
|
57
|
+
readonly label: "Kanagawa Lotus";
|
|
58
|
+
readonly icon: "❁";
|
|
59
|
+
readonly themeColor: "#f2ecbc";
|
|
60
|
+
readonly mode: "light";
|
|
11
61
|
}, {
|
|
12
62
|
readonly id: "sepia";
|
|
13
63
|
readonly label: "Sepia";
|
|
14
64
|
readonly icon: "✶";
|
|
15
65
|
readonly themeColor: "#f4ecd8";
|
|
66
|
+
readonly mode: "light";
|
|
67
|
+
}, {
|
|
68
|
+
readonly id: "dark";
|
|
69
|
+
readonly label: "Dark";
|
|
70
|
+
readonly icon: "☾";
|
|
71
|
+
readonly themeColor: "#0f1115";
|
|
72
|
+
readonly mode: "dark";
|
|
16
73
|
}, {
|
|
17
74
|
readonly id: "colorblind";
|
|
18
75
|
readonly label: "Color-blind safe";
|
|
19
|
-
readonly icon: "
|
|
76
|
+
readonly icon: "◐";
|
|
20
77
|
readonly themeColor: "#16181d";
|
|
78
|
+
readonly mode: "dark";
|
|
21
79
|
}, {
|
|
22
80
|
readonly id: "mocha";
|
|
23
81
|
readonly label: "Catppuccin Mocha";
|
|
24
82
|
readonly icon: "M";
|
|
25
83
|
readonly themeColor: "#1e1e2e";
|
|
84
|
+
readonly mode: "dark";
|
|
26
85
|
}, {
|
|
27
86
|
readonly id: "dracula";
|
|
28
87
|
readonly label: "Dracula";
|
|
29
88
|
readonly icon: "D";
|
|
30
89
|
readonly themeColor: "#282a36";
|
|
90
|
+
readonly mode: "dark";
|
|
31
91
|
}, {
|
|
32
92
|
readonly id: "nord";
|
|
33
93
|
readonly label: "Nord";
|
|
34
94
|
readonly icon: "N";
|
|
35
95
|
readonly themeColor: "#2e3440";
|
|
96
|
+
readonly mode: "dark";
|
|
36
97
|
}, {
|
|
37
98
|
readonly id: "tokyonight";
|
|
38
99
|
readonly label: "Tokyo Night";
|
|
39
100
|
readonly icon: "✦";
|
|
40
101
|
readonly themeColor: "#1a1b26";
|
|
102
|
+
readonly mode: "dark";
|
|
41
103
|
}, {
|
|
42
104
|
readonly id: "gruvbox";
|
|
43
105
|
readonly label: "Gruvbox";
|
|
44
106
|
readonly icon: "◆";
|
|
45
107
|
readonly themeColor: "#282828";
|
|
108
|
+
readonly mode: "dark";
|
|
46
109
|
}, {
|
|
47
110
|
readonly id: "solarized";
|
|
48
111
|
readonly label: "Solarized Dark";
|
|
49
|
-
readonly icon: "
|
|
112
|
+
readonly icon: "◒";
|
|
50
113
|
readonly themeColor: "#002b36";
|
|
114
|
+
readonly mode: "dark";
|
|
51
115
|
}, {
|
|
52
116
|
readonly id: "rosepine";
|
|
53
117
|
readonly label: "Rosé Pine";
|
|
54
118
|
readonly icon: "❀";
|
|
55
119
|
readonly themeColor: "#191724";
|
|
120
|
+
readonly mode: "dark";
|
|
56
121
|
}, {
|
|
57
122
|
readonly id: "onedark";
|
|
58
123
|
readonly label: "One Dark";
|
|
59
124
|
readonly icon: "①";
|
|
60
125
|
readonly themeColor: "#282c34";
|
|
126
|
+
readonly mode: "dark";
|
|
61
127
|
}, {
|
|
62
128
|
readonly id: "everforest";
|
|
63
129
|
readonly label: "Everforest";
|
|
64
130
|
readonly icon: "☘";
|
|
65
131
|
readonly themeColor: "#2d353b";
|
|
132
|
+
readonly mode: "dark";
|
|
66
133
|
}, {
|
|
67
134
|
readonly id: "monokai";
|
|
68
135
|
readonly label: "Monokai";
|
|
69
136
|
readonly icon: "✸";
|
|
70
137
|
readonly themeColor: "#272822";
|
|
138
|
+
readonly mode: "dark";
|
|
71
139
|
}, {
|
|
72
140
|
readonly id: "amoled";
|
|
73
141
|
readonly label: "AMOLED (high contrast)";
|
|
74
142
|
readonly icon: "◼";
|
|
75
143
|
readonly themeColor: "#000000";
|
|
76
|
-
|
|
77
|
-
readonly id: "highcontrast";
|
|
78
|
-
readonly label: "High Contrast Light";
|
|
79
|
-
readonly icon: "◻";
|
|
80
|
-
readonly themeColor: "#ffffff";
|
|
144
|
+
readonly mode: "dark";
|
|
81
145
|
}];
|
|
82
146
|
export type Mode = (typeof THEMES)[number]['id'];
|
|
147
|
+
export type ThemeMode = (typeof THEMES)[number]['mode'];
|
|
83
148
|
type ThemeOption = (typeof THEMES)[number];
|
|
84
149
|
declare class Theme {
|
|
85
|
-
current: "
|
|
150
|
+
current: "light" | "highcontrast" | "gruvboxlight" | "solarizedlight" | "everforestlight" | "rosepinedawn" | "latte" | "nordlight" | "tokyoday" | "kanagawalotus" | "sepia" | "dark" | "colorblind" | "mocha" | "dracula" | "nord" | "tokyonight" | "gruvbox" | "solarized" | "rosepine" | "onedark" | "everforest" | "monokai" | "amoled";
|
|
86
151
|
constructor();
|
|
87
152
|
private apply;
|
|
88
153
|
get option(): ThemeOption;
|
|
@@ -1,25 +1,55 @@
|
|
|
1
1
|
import { browser } from '../env';
|
|
2
2
|
// Theme registry — the single list the picker and the store both read. Adding a
|
|
3
3
|
// theme = one entry here + one [data-theme="id"] block in variables.css. Nothing
|
|
4
|
-
// else changes. `themeColor` drives the mobile browser-chrome <meta theme-color
|
|
4
|
+
// else changes. `themeColor` drives the mobile browser-chrome <meta theme-color>;
|
|
5
|
+
// `mode` groups the theme into the picker's light/dark sections (TSU-1).
|
|
5
6
|
const KEY = 'tsumikit-theme';
|
|
6
7
|
export const THEMES = [
|
|
7
|
-
|
|
8
|
-
{ id: 'light', label: 'Light', icon: '☀', themeColor: '#f6f7f9' },
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{ id: '
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
8
|
+
// ── Light ── bright, paper-white surfaces
|
|
9
|
+
{ id: 'light', label: 'Light', icon: '☀', themeColor: '#f6f7f9', mode: 'light' },
|
|
10
|
+
{
|
|
11
|
+
id: 'highcontrast',
|
|
12
|
+
label: 'High Contrast Light',
|
|
13
|
+
icon: '◻',
|
|
14
|
+
themeColor: '#ffffff',
|
|
15
|
+
mode: 'light',
|
|
16
|
+
},
|
|
17
|
+
// ── Medium-light (TSU-1) ── easy on the eyes, not blinding, distinct bases
|
|
18
|
+
{ id: 'gruvboxlight', label: 'Gruvbox Light', icon: '◇', themeColor: '#fbf1c7', mode: 'light' },
|
|
19
|
+
{
|
|
20
|
+
id: 'solarizedlight',
|
|
21
|
+
label: 'Solarized Light',
|
|
22
|
+
icon: '◑',
|
|
23
|
+
themeColor: '#fdf6e3',
|
|
24
|
+
mode: 'light',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'everforestlight',
|
|
28
|
+
label: 'Everforest Light',
|
|
29
|
+
icon: '✾',
|
|
30
|
+
themeColor: '#fdf6e3',
|
|
31
|
+
mode: 'light',
|
|
32
|
+
},
|
|
33
|
+
{ id: 'rosepinedawn', label: 'Rosé Pine Dawn', icon: '✿', themeColor: '#faf4ed', mode: 'light' },
|
|
34
|
+
{ id: 'latte', label: 'Catppuccin Latte', icon: 'L', themeColor: '#eff1f5', mode: 'light' },
|
|
35
|
+
{ id: 'nordlight', label: 'Nord Light', icon: 'n', themeColor: '#eceff4', mode: 'light' },
|
|
36
|
+
{ id: 'tokyoday', label: 'Tokyo Night Day', icon: '✧', themeColor: '#e1e2e7', mode: 'light' },
|
|
37
|
+
{ id: 'kanagawalotus', label: 'Kanagawa Lotus', icon: '❁', themeColor: '#f2ecbc', mode: 'light' },
|
|
38
|
+
{ id: 'sepia', label: 'Sepia', icon: '✶', themeColor: '#f4ecd8', mode: 'light' },
|
|
39
|
+
// ── Dark ──
|
|
40
|
+
{ id: 'dark', label: 'Dark', icon: '☾', themeColor: '#0f1115', mode: 'dark' },
|
|
41
|
+
{ id: 'colorblind', label: 'Color-blind safe', icon: '◐', themeColor: '#16181d', mode: 'dark' },
|
|
42
|
+
{ id: 'mocha', label: 'Catppuccin Mocha', icon: 'M', themeColor: '#1e1e2e', mode: 'dark' },
|
|
43
|
+
{ id: 'dracula', label: 'Dracula', icon: 'D', themeColor: '#282a36', mode: 'dark' },
|
|
44
|
+
{ id: 'nord', label: 'Nord', icon: 'N', themeColor: '#2e3440', mode: 'dark' },
|
|
45
|
+
{ id: 'tokyonight', label: 'Tokyo Night', icon: '✦', themeColor: '#1a1b26', mode: 'dark' },
|
|
46
|
+
{ id: 'gruvbox', label: 'Gruvbox', icon: '◆', themeColor: '#282828', mode: 'dark' },
|
|
47
|
+
{ id: 'solarized', label: 'Solarized Dark', icon: '◒', themeColor: '#002b36', mode: 'dark' },
|
|
48
|
+
{ id: 'rosepine', label: 'Rosé Pine', icon: '❀', themeColor: '#191724', mode: 'dark' },
|
|
49
|
+
{ id: 'onedark', label: 'One Dark', icon: '①', themeColor: '#282c34', mode: 'dark' },
|
|
50
|
+
{ id: 'everforest', label: 'Everforest', icon: '☘', themeColor: '#2d353b', mode: 'dark' },
|
|
51
|
+
{ id: 'monokai', label: 'Monokai', icon: '✸', themeColor: '#272822', mode: 'dark' },
|
|
52
|
+
{ id: 'amoled', label: 'AMOLED (high contrast)', icon: '◼', themeColor: '#000000', mode: 'dark' },
|
|
23
53
|
];
|
|
24
54
|
const ORDER = THEMES.map((t) => t.id);
|
|
25
55
|
function isMode(value) {
|
|
@@ -649,3 +649,262 @@
|
|
|
649
649
|
--mach-fg-sl: 70% 82%;
|
|
650
650
|
--mach-border-sl: 45% 40%;
|
|
651
651
|
}
|
|
652
|
+
|
|
653
|
+
/* ===================================================================
|
|
654
|
+
Medium-light tier (TSU-1) — light but not blinding. Cream/paper or
|
|
655
|
+
soft-tinted backgrounds (never pure white) with dark, saturated
|
|
656
|
+
accents so body text clears WCAG AA. Each ports a famous palette in a
|
|
657
|
+
distinct color base. They share the light-theme contract: color-scheme
|
|
658
|
+
light, soft shadows, pale-tint/dark-ink machine badges.
|
|
659
|
+
=================================================================== */
|
|
660
|
+
|
|
661
|
+
/* ---- Gruvbox Light (medium) — warm wood/cream, faded orange accent. */
|
|
662
|
+
[data-theme="gruvboxlight"] {
|
|
663
|
+
color-scheme: light;
|
|
664
|
+
--c-bg: #fbf1c7;
|
|
665
|
+
--c-bg-elev: #f9f5d7;
|
|
666
|
+
--c-bg-elev-2: #ebdbb2;
|
|
667
|
+
--c-surface: #f9f5d7;
|
|
668
|
+
--c-border: #d5c4a1;
|
|
669
|
+
--c-border-strong: #bdae93;
|
|
670
|
+
--c-text: #3c3836;
|
|
671
|
+
--c-text-muted: #504945;
|
|
672
|
+
--c-text-faint: #7c6f64;
|
|
673
|
+
--c-accent: #af3a03;
|
|
674
|
+
--c-accent-ink: #fbf1c7;
|
|
675
|
+
--c-accent-dim: #d65d0e;
|
|
676
|
+
--c-blue: #076678;
|
|
677
|
+
--c-amber: #b57614;
|
|
678
|
+
--c-red: #9d0006;
|
|
679
|
+
--c-green: #79740e;
|
|
680
|
+
--c-violet: #8f3f71;
|
|
681
|
+
--c-gold: #b57614;
|
|
682
|
+
--c-teal: #427b58;
|
|
683
|
+
--md-code-bg: color-mix(in srgb, var(--c-blue) 12%, var(--c-bg));
|
|
684
|
+
--shadow-sm: 0 1px 2px rgba(80, 60, 30, 0.1);
|
|
685
|
+
--shadow-md: 0 6px 20px rgba(80, 60, 30, 0.14);
|
|
686
|
+
--shadow-lg: 0 12px 40px rgba(80, 60, 30, 0.2);
|
|
687
|
+
--mach-bg-sl: 45% 86%;
|
|
688
|
+
--mach-fg-sl: 55% 30%;
|
|
689
|
+
--mach-border-sl: 40% 68%;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/* ---- Solarized Light — Ethan Schoonover's base3 paper with 16-color accents. */
|
|
693
|
+
[data-theme="solarizedlight"] {
|
|
694
|
+
color-scheme: light;
|
|
695
|
+
--c-bg: #fdf6e3;
|
|
696
|
+
--c-bg-elev: #fbf3e0;
|
|
697
|
+
--c-bg-elev-2: #eee8d5;
|
|
698
|
+
--c-surface: #fbf3e0;
|
|
699
|
+
--c-border: #ddd6c1;
|
|
700
|
+
--c-border-strong: #cbc4ad;
|
|
701
|
+
--c-text: #073642;
|
|
702
|
+
--c-text-muted: #586e75;
|
|
703
|
+
--c-text-faint: #839496;
|
|
704
|
+
--c-accent: #2aa198;
|
|
705
|
+
--c-accent-ink: #fdf6e3;
|
|
706
|
+
--c-accent-dim: #2aa198;
|
|
707
|
+
--c-blue: #268bd2;
|
|
708
|
+
--c-amber: #b58900;
|
|
709
|
+
--c-red: #dc322f;
|
|
710
|
+
--c-green: #859900;
|
|
711
|
+
--c-violet: #6c71c4;
|
|
712
|
+
--c-gold: #b58900;
|
|
713
|
+
--c-teal: #2aa198;
|
|
714
|
+
--md-code-bg: color-mix(in srgb, var(--c-blue) 12%, var(--c-bg));
|
|
715
|
+
--shadow-sm: 0 1px 2px rgba(88, 110, 117, 0.12);
|
|
716
|
+
--shadow-md: 0 6px 20px rgba(88, 110, 117, 0.16);
|
|
717
|
+
--shadow-lg: 0 12px 40px rgba(88, 110, 117, 0.22);
|
|
718
|
+
--mach-bg-sl: 45% 86%;
|
|
719
|
+
--mach-fg-sl: 50% 30%;
|
|
720
|
+
--mach-border-sl: 40% 68%;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/* ---- Everforest Light (medium) — soft sage/green paper, low-contrast calm. */
|
|
724
|
+
[data-theme="everforestlight"] {
|
|
725
|
+
color-scheme: light;
|
|
726
|
+
--c-bg: #fdf6e3;
|
|
727
|
+
--c-bg-elev: #f4f0d9;
|
|
728
|
+
--c-bg-elev-2: #efebd4;
|
|
729
|
+
--c-surface: #f4f0d9;
|
|
730
|
+
--c-border: #e0dcc7;
|
|
731
|
+
--c-border-strong: #bdc3af;
|
|
732
|
+
--c-text: #5c6a72;
|
|
733
|
+
--c-text-muted: #708089;
|
|
734
|
+
--c-text-faint: #939f91;
|
|
735
|
+
--c-accent: #8da101;
|
|
736
|
+
--c-accent-ink: #fdf6e3;
|
|
737
|
+
--c-accent-dim: #8da101;
|
|
738
|
+
--c-blue: #3a94c5;
|
|
739
|
+
--c-amber: #dfa000;
|
|
740
|
+
--c-red: #f85552;
|
|
741
|
+
--c-green: #8da101;
|
|
742
|
+
--c-violet: #df69ba;
|
|
743
|
+
--c-gold: #dfa000;
|
|
744
|
+
--c-teal: #35a77c;
|
|
745
|
+
--md-code-bg: color-mix(in srgb, var(--c-blue) 12%, var(--c-bg));
|
|
746
|
+
--shadow-sm: 0 1px 2px rgba(92, 106, 114, 0.12);
|
|
747
|
+
--shadow-md: 0 6px 20px rgba(92, 106, 114, 0.16);
|
|
748
|
+
--shadow-lg: 0 12px 40px rgba(92, 106, 114, 0.22);
|
|
749
|
+
--mach-bg-sl: 35% 86%;
|
|
750
|
+
--mach-fg-sl: 45% 32%;
|
|
751
|
+
--mach-border-sl: 30% 68%;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/* ---- Rosé Pine Dawn — official light variant: muted rose, iris, foam. */
|
|
755
|
+
[data-theme="rosepinedawn"] {
|
|
756
|
+
color-scheme: light;
|
|
757
|
+
--c-bg: #faf4ed;
|
|
758
|
+
--c-bg-elev: #fffaf3;
|
|
759
|
+
--c-bg-elev-2: #f2e9e1;
|
|
760
|
+
--c-surface: #fffaf3;
|
|
761
|
+
--c-border: #dfdad9;
|
|
762
|
+
--c-border-strong: #cecacd;
|
|
763
|
+
--c-text: #575279;
|
|
764
|
+
--c-text-muted: #797593;
|
|
765
|
+
--c-text-faint: #9893a5;
|
|
766
|
+
--c-accent: #907aa9;
|
|
767
|
+
--c-accent-ink: #faf4ed;
|
|
768
|
+
--c-accent-dim: #907aa9;
|
|
769
|
+
--c-blue: #286983;
|
|
770
|
+
--c-amber: #ea9d34;
|
|
771
|
+
--c-red: #b4637a;
|
|
772
|
+
--c-green: #56949f;
|
|
773
|
+
--c-violet: #907aa9;
|
|
774
|
+
--c-gold: #ea9d34;
|
|
775
|
+
--c-teal: #56949f;
|
|
776
|
+
--md-code-bg: color-mix(in srgb, var(--c-blue) 12%, var(--c-bg));
|
|
777
|
+
--shadow-sm: 0 1px 2px rgba(87, 82, 121, 0.1);
|
|
778
|
+
--shadow-md: 0 6px 20px rgba(87, 82, 121, 0.14);
|
|
779
|
+
--shadow-lg: 0 12px 40px rgba(87, 82, 121, 0.2);
|
|
780
|
+
--mach-bg-sl: 35% 88%;
|
|
781
|
+
--mach-fg-sl: 30% 38%;
|
|
782
|
+
--mach-border-sl: 28% 72%;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/* ---- Catppuccin Latte — the official light flavour: neutral with mauve accent. */
|
|
786
|
+
[data-theme="latte"] {
|
|
787
|
+
color-scheme: light;
|
|
788
|
+
--c-bg: #eff1f5;
|
|
789
|
+
--c-bg-elev: #e6e9ef;
|
|
790
|
+
--c-bg-elev-2: #dce0e8;
|
|
791
|
+
--c-surface: #e6e9ef;
|
|
792
|
+
--c-border: #ccd0da;
|
|
793
|
+
--c-border-strong: #bcc0cc;
|
|
794
|
+
--c-text: #4c4f69;
|
|
795
|
+
--c-text-muted: #5c5f77;
|
|
796
|
+
--c-text-faint: #8c8fa1;
|
|
797
|
+
--c-accent: #8839ef;
|
|
798
|
+
--c-accent-ink: #eff1f5;
|
|
799
|
+
--c-accent-dim: #8839ef;
|
|
800
|
+
--c-blue: #1e66f5;
|
|
801
|
+
--c-amber: #df8e1d;
|
|
802
|
+
--c-red: #d20f39;
|
|
803
|
+
--c-green: #40a02b;
|
|
804
|
+
--c-violet: #8839ef;
|
|
805
|
+
--c-gold: #df8e1d;
|
|
806
|
+
--c-teal: #179299;
|
|
807
|
+
--md-code-bg: color-mix(in srgb, var(--c-blue) 10%, var(--c-bg));
|
|
808
|
+
--shadow-sm: 0 1px 2px rgba(76, 79, 105, 0.1);
|
|
809
|
+
--shadow-md: 0 6px 20px rgba(76, 79, 105, 0.14);
|
|
810
|
+
--shadow-lg: 0 12px 40px rgba(76, 79, 105, 0.2);
|
|
811
|
+
--mach-bg-sl: 45% 88%;
|
|
812
|
+
--mach-fg-sl: 45% 34%;
|
|
813
|
+
--mach-border-sl: 35% 72%;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/* ---- Nord Light — Snow Storm base, Polar Night ink, darkened Frost/Aurora
|
|
817
|
+
accents so they read against the pale blue-gray paper. */
|
|
818
|
+
[data-theme="nordlight"] {
|
|
819
|
+
color-scheme: light;
|
|
820
|
+
--c-bg: #eceff4;
|
|
821
|
+
--c-bg-elev: #f4f6f9;
|
|
822
|
+
--c-bg-elev-2: #e5e9f0;
|
|
823
|
+
--c-surface: #f4f6f9;
|
|
824
|
+
--c-border: #d8dee9;
|
|
825
|
+
--c-border-strong: #c2cad6;
|
|
826
|
+
--c-text: #2e3440;
|
|
827
|
+
--c-text-muted: #434c5e;
|
|
828
|
+
--c-text-faint: #6b7689;
|
|
829
|
+
--c-accent: #5e81ac;
|
|
830
|
+
--c-accent-ink: #eceff4;
|
|
831
|
+
--c-accent-dim: #5e81ac;
|
|
832
|
+
--c-blue: #5e81ac;
|
|
833
|
+
--c-amber: #9a6f12;
|
|
834
|
+
--c-red: #bf616a;
|
|
835
|
+
--c-green: #5a7444;
|
|
836
|
+
--c-violet: #9a6c93;
|
|
837
|
+
--c-gold: #9a6f12;
|
|
838
|
+
--c-teal: #3a8a87;
|
|
839
|
+
--md-code-bg: color-mix(in srgb, var(--c-blue) 12%, var(--c-bg));
|
|
840
|
+
--shadow-sm: 0 1px 2px rgba(46, 52, 64, 0.1);
|
|
841
|
+
--shadow-md: 0 6px 20px rgba(46, 52, 64, 0.14);
|
|
842
|
+
--shadow-lg: 0 12px 40px rgba(46, 52, 64, 0.2);
|
|
843
|
+
--mach-bg-sl: 30% 88%;
|
|
844
|
+
--mach-fg-sl: 35% 34%;
|
|
845
|
+
--mach-border-sl: 28% 72%;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/* ---- Tokyo Night Day — the official light variant: muted blue paper, the
|
|
849
|
+
signature blue ink, saturated day accents. */
|
|
850
|
+
[data-theme="tokyoday"] {
|
|
851
|
+
color-scheme: light;
|
|
852
|
+
--c-bg: #e1e2e7;
|
|
853
|
+
--c-bg-elev: #eaeaee;
|
|
854
|
+
--c-bg-elev-2: #d5d6db;
|
|
855
|
+
--c-surface: #eaeaee;
|
|
856
|
+
--c-border: #c4c8da;
|
|
857
|
+
--c-border-strong: #a8acc4;
|
|
858
|
+
--c-text: #3760bf;
|
|
859
|
+
--c-text-muted: #6172b0;
|
|
860
|
+
--c-text-faint: #848cb5;
|
|
861
|
+
--c-accent: #2e7de9;
|
|
862
|
+
--c-accent-ink: #ffffff;
|
|
863
|
+
--c-accent-dim: #2e7de9;
|
|
864
|
+
--c-blue: #2e7de9;
|
|
865
|
+
--c-amber: #8c6c3e;
|
|
866
|
+
--c-red: #f52a65;
|
|
867
|
+
--c-green: #587539;
|
|
868
|
+
--c-violet: #9854f1;
|
|
869
|
+
--c-gold: #8c6c3e;
|
|
870
|
+
--c-teal: #118c74;
|
|
871
|
+
--md-code-bg: color-mix(in srgb, var(--c-blue) 12%, var(--c-bg));
|
|
872
|
+
--shadow-sm: 0 1px 2px rgba(55, 96, 191, 0.1);
|
|
873
|
+
--shadow-md: 0 6px 20px rgba(55, 96, 191, 0.14);
|
|
874
|
+
--shadow-lg: 0 12px 40px rgba(55, 96, 191, 0.2);
|
|
875
|
+
--mach-bg-sl: 40% 88%;
|
|
876
|
+
--mach-fg-sl: 45% 34%;
|
|
877
|
+
--mach-border-sl: 32% 72%;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/* ---- Kanagawa Lotus — the official light variant: warm woodblock paper,
|
|
881
|
+
ink-violet accent, muted earthen hues (brownish base). */
|
|
882
|
+
[data-theme="kanagawalotus"] {
|
|
883
|
+
color-scheme: light;
|
|
884
|
+
--c-bg: #f2ecbc;
|
|
885
|
+
--c-bg-elev: #ebe3b0;
|
|
886
|
+
--c-bg-elev-2: #e5ddb0;
|
|
887
|
+
--c-surface: #ebe3b0;
|
|
888
|
+
--c-border: #d5cea3;
|
|
889
|
+
--c-border-strong: #c9c193;
|
|
890
|
+
--c-text: #545464;
|
|
891
|
+
--c-text-muted: #766b90;
|
|
892
|
+
--c-text-faint: #a09cac;
|
|
893
|
+
--c-accent: #624c83;
|
|
894
|
+
--c-accent-ink: #f2ecbc;
|
|
895
|
+
--c-accent-dim: #624c83;
|
|
896
|
+
--c-blue: #4d699b;
|
|
897
|
+
--c-amber: #cc6d00;
|
|
898
|
+
--c-red: #c84053;
|
|
899
|
+
--c-green: #6f894e;
|
|
900
|
+
--c-violet: #624c83;
|
|
901
|
+
--c-gold: #836f4a;
|
|
902
|
+
--c-teal: #597b75;
|
|
903
|
+
--md-code-bg: color-mix(in srgb, var(--c-blue) 12%, var(--c-bg));
|
|
904
|
+
--shadow-sm: 0 1px 2px rgba(84, 84, 100, 0.12);
|
|
905
|
+
--shadow-md: 0 6px 20px rgba(84, 84, 100, 0.16);
|
|
906
|
+
--shadow-lg: 0 12px 40px rgba(84, 84, 100, 0.22);
|
|
907
|
+
--mach-bg-sl: 35% 84%;
|
|
908
|
+
--mach-fg-sl: 35% 34%;
|
|
909
|
+
--mach-border-sl: 30% 66%;
|
|
910
|
+
}
|
package/package.json
CHANGED