@astronox/core 0.2.2 → 0.2.4
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/package.json +1 -1
- package/src/admin/cache.ts +217 -0
- package/src/admin/index.ts +1 -0
- package/src/auth/firebase/client.ts +37 -7
- package/src/client/formSubmission.ts +177 -0
- package/src/client/index.ts +1 -0
- package/src/components/actions/Button/Button.css +1 -1
- package/src/components/actions/CardAction/CardAction.css +3 -3
- package/src/components/actions/ScrollToTop/ScrollToTop.css +11 -0
- package/src/components/actions/ThemeToggle/ThemeToggle.astro +59 -0
- package/src/components/actions/ThemeToggle/ThemeToggle.css +83 -0
- package/src/components/actions/ThemeToggle/themeToggleController.ts +111 -0
- package/src/components/content/CardBody/CardBody.css +6 -2
- package/src/components/content/CardFooter/CardFooter.astro +9 -0
- package/src/components/content/CardFooter/CardFooter.css +45 -3
- package/src/components/content/CardLink/CardLink.astro +12 -3
- package/src/components/content/CardLink/CardLink.css +32 -0
- package/src/components/content/MarkdownContent/MarkdownContent.css +14 -4
- package/src/components/content/MarkdownContent/MarkdownInline.astro +1 -1
- package/src/components/content/MarkdownContent/contracts.ts +1 -1
- package/src/components/content/MarkdownContent/markdownContentRenderer.ts +7 -4
- package/src/components/data-display/TextPair/TextPair.css +8 -2
- package/src/components/feedback/ThemeBootstrap/ThemeBootstrap.astro +31 -0
- package/src/components/forms/ChoiceGroup/ChoiceGroup.css +1 -1
- package/src/components/forms/PreparedFormBootstrap/PreparedFormBootstrap.astro +203 -0
- package/src/components/forms/PreparedFormBootstrap/contracts.ts +15 -0
- package/src/components/forms/Textarea/Textarea.astro +3 -0
- package/src/components/forms/Textarea/Textarea.css +8 -0
- package/src/components/icons/CircleSymbolIcon.css +3 -3
- package/src/components/index.ts +10 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.astro +5 -1
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.css +12 -17
- package/src/components/navigation/DesktopNavigation.css +4 -1
- package/src/components/navigation/Footer/Footer.astro +102 -0
- package/src/components/navigation/Footer/Footer.css +155 -0
- package/src/components/navigation/Footer/contracts.ts +17 -0
- package/src/components/navigation/Header.astro +20 -2
- package/src/components/navigation/MobileNavigation.astro +107 -43
- package/src/components/navigation/MobileNavigation.css +109 -24
- package/src/components/navigation/NavigationBrand.css +1 -1
- package/src/components/navigation/NavigationControl.css +16 -2
- package/src/components/navigation/NavigationItemContent.astro +6 -1
- package/src/components/navigation/mobileNavigationController.ts +0 -16
- package/src/components/navigation/navigationTypes.ts +9 -1
- package/src/composition/Grid/Grid.css +16 -0
- package/src/data/firestore/client/contracts.ts +23 -0
- package/src/data/firestore/client/firebase.ts +45 -1
- package/src/display/contracts.ts +5 -1
- package/src/layouts/BaseLayout.astro +13 -1
- package/src/patterns/Combobox/comboboxController.ts +69 -0
- package/src/patterns/MapViewport/MapViewport.css +3 -3
- package/src/patterns/Table/TableBody.css +3 -3
- package/src/patterns/Table/TableCell.css +5 -1
- package/src/styles/base.css +24 -7
- package/src/styles/style.css +2 -0
- package/src/styles/tokens.css +60 -15
- package/src/theme/contracts.ts +2 -0
- package/src/theme/index.ts +1 -0
- package/src/theme/storage.ts +1 -0
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { getApps, initializeApp } from 'firebase/app';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
collection,
|
|
4
|
+
doc,
|
|
5
|
+
getFirestore,
|
|
6
|
+
serverTimestamp,
|
|
7
|
+
writeBatch,
|
|
8
|
+
} from 'firebase/firestore';
|
|
3
9
|
|
|
4
10
|
import type { FirebaseClientConfig } from '../../../auth/firebase/contracts';
|
|
11
|
+
import type {
|
|
12
|
+
ClientFirestoreBatchOperation,
|
|
13
|
+
ClientFirestoreBatchResult,
|
|
14
|
+
} from './contracts';
|
|
5
15
|
|
|
6
16
|
export function createFirebaseClientFirestore(config: FirebaseClientConfig) {
|
|
7
17
|
const app =
|
|
@@ -10,3 +20,37 @@ export function createFirebaseClientFirestore(config: FirebaseClientConfig) {
|
|
|
10
20
|
|
|
11
21
|
return getFirestore(app);
|
|
12
22
|
}
|
|
23
|
+
|
|
24
|
+
export function createClientFirestoreServerTimestamp() {
|
|
25
|
+
return serverTimestamp();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function commitClientFirestoreBatch(
|
|
29
|
+
database: ReturnType<typeof createFirebaseClientFirestore>,
|
|
30
|
+
operations: readonly ClientFirestoreBatchOperation[],
|
|
31
|
+
): Promise<ClientFirestoreBatchResult> {
|
|
32
|
+
const batch = writeBatch(database);
|
|
33
|
+
const createdIds: string[] = [];
|
|
34
|
+
|
|
35
|
+
for (const operation of operations) {
|
|
36
|
+
const documents = collection(database, operation.collection);
|
|
37
|
+
if (operation.kind === 'create') {
|
|
38
|
+
const reference = operation.id
|
|
39
|
+
? doc(documents, operation.id)
|
|
40
|
+
: doc(documents);
|
|
41
|
+
batch.set(reference, operation.value);
|
|
42
|
+
createdIds.push(reference.id);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const reference = doc(documents, operation.id);
|
|
47
|
+
if (operation.kind === 'update') {
|
|
48
|
+
batch.update(reference, operation.value);
|
|
49
|
+
} else {
|
|
50
|
+
batch.delete(reference);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await batch.commit();
|
|
55
|
+
return { createdIds };
|
|
56
|
+
}
|
package/src/display/contracts.ts
CHANGED
|
@@ -104,10 +104,13 @@ type DisplayCardLinkContent = {
|
|
|
104
104
|
compactContent?: 'text' | 'media';
|
|
105
105
|
compactText?: DisplayTextValue;
|
|
106
106
|
compactVisual?: 'show' | 'hide';
|
|
107
|
+
density?: 'default' | 'relaxed';
|
|
107
108
|
iconTone?: Tone;
|
|
108
|
-
label
|
|
109
|
+
label?: string;
|
|
109
110
|
title: string;
|
|
110
111
|
meta?: DisplayTextValue;
|
|
112
|
+
textLayout?: TextPairLayout;
|
|
113
|
+
textTreatment?: 'default' | 'heading';
|
|
111
114
|
media?: DisplayMedia;
|
|
112
115
|
mediaPosition?: 'start' | 'end';
|
|
113
116
|
mediaSize?: 'default' | 'compact' | 'small';
|
|
@@ -308,6 +311,7 @@ type NavigationItemBase = {
|
|
|
308
311
|
rel?: string;
|
|
309
312
|
target?: '_blank' | '_self';
|
|
310
313
|
hreflang?: string;
|
|
314
|
+
icon?: AstroComponent;
|
|
311
315
|
media?: DisplayMedia;
|
|
312
316
|
mediaFrame?: 'bordered';
|
|
313
317
|
mediaShape?: 'circle' | 'rectangle';
|
|
@@ -9,6 +9,8 @@ import type { Tone } from '../theme/contracts';
|
|
|
9
9
|
import PageSurface from './PageSurface.astro';
|
|
10
10
|
import Breadcrumbs from '../components/navigation/Breadcrumbs/Breadcrumbs.astro';
|
|
11
11
|
import ScrollToTop from '../components/actions/ScrollToTop/ScrollToTop.astro';
|
|
12
|
+
import ThemeBootstrap from '../components/feedback/ThemeBootstrap/ThemeBootstrap.astro';
|
|
13
|
+
import { defaultThemeStorageKey } from '../theme/storage';
|
|
12
14
|
|
|
13
15
|
type Props = HTMLAttributes<'html'> & {
|
|
14
16
|
meta: PageMeta;
|
|
@@ -16,13 +18,16 @@ type Props = HTMLAttributes<'html'> & {
|
|
|
16
18
|
currentPath?: string;
|
|
17
19
|
breadcrumbs?: readonly BreadcrumbItem[];
|
|
18
20
|
breadcrumbLabel: string;
|
|
21
|
+
breadcrumbTone?: Tone;
|
|
19
22
|
scrollToTopLabel?: string;
|
|
20
23
|
structuredData?: readonly JsonLdNode[];
|
|
21
24
|
tone?: Tone;
|
|
25
|
+
themeStorageKey?: string;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
const {
|
|
25
29
|
breadcrumbLabel,
|
|
30
|
+
breadcrumbTone,
|
|
26
31
|
breadcrumbs,
|
|
27
32
|
currentPath = Astro.url.pathname,
|
|
28
33
|
'data-tone': dataTone,
|
|
@@ -31,6 +36,7 @@ const {
|
|
|
31
36
|
site,
|
|
32
37
|
structuredData,
|
|
33
38
|
tone,
|
|
39
|
+
themeStorageKey = defaultThemeStorageKey,
|
|
34
40
|
...htmlAttrs
|
|
35
41
|
} = Astro.props;
|
|
36
42
|
const lang = meta.lang ?? site.defaultMeta.lang;
|
|
@@ -42,6 +48,7 @@ const resolvedTone = tone ?? dataTone;
|
|
|
42
48
|
<head>
|
|
43
49
|
<meta charset="utf-8" />
|
|
44
50
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
51
|
+
<ThemeBootstrap storageKey={themeStorageKey} />
|
|
45
52
|
<SeoHead
|
|
46
53
|
breadcrumbs={breadcrumbs}
|
|
47
54
|
meta={meta}
|
|
@@ -59,7 +66,11 @@ const resolvedTone = tone ?? dataTone;
|
|
|
59
66
|
<div class="site-main__content">
|
|
60
67
|
{
|
|
61
68
|
breadcrumbs?.length ? (
|
|
62
|
-
<Breadcrumbs
|
|
69
|
+
<Breadcrumbs
|
|
70
|
+
items={breadcrumbs}
|
|
71
|
+
label={breadcrumbLabel}
|
|
72
|
+
tone={breadcrumbTone}
|
|
73
|
+
/>
|
|
63
74
|
) : null
|
|
64
75
|
}
|
|
65
76
|
|
|
@@ -68,6 +79,7 @@ const resolvedTone = tone ?? dataTone;
|
|
|
68
79
|
</PageSurface>
|
|
69
80
|
</div>
|
|
70
81
|
</main>
|
|
82
|
+
<slot name="footer" />
|
|
71
83
|
</div>
|
|
72
84
|
{scrollToTopLabel ? <ScrollToTop label={scrollToTopLabel} /> : null}
|
|
73
85
|
</body>
|
|
@@ -17,6 +17,13 @@ export type ComboboxNumberBounds = {
|
|
|
17
17
|
max: number;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
export type DynamicComboboxOption = {
|
|
21
|
+
description?: string;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
label: string;
|
|
24
|
+
value: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
20
27
|
type NumberBounds = {
|
|
21
28
|
min: number;
|
|
22
29
|
max: number;
|
|
@@ -159,6 +166,68 @@ export function clearCombobox(
|
|
|
159
166
|
root.dispatchEvent(new Event('combobox:clear'));
|
|
160
167
|
}
|
|
161
168
|
|
|
169
|
+
export function setComboboxOptions(
|
|
170
|
+
root: HTMLElement,
|
|
171
|
+
options: readonly DynamicComboboxOption[],
|
|
172
|
+
selectedValues: readonly string[] = [],
|
|
173
|
+
): void {
|
|
174
|
+
if (root.dataset.comboboxMode === 'number') return;
|
|
175
|
+
const optionsRoot = root.querySelector<HTMLElement>('[data-combobox-options]');
|
|
176
|
+
const empty = root.querySelector<HTMLElement>('[data-combobox-empty]');
|
|
177
|
+
if (!optionsRoot || !empty) return;
|
|
178
|
+
|
|
179
|
+
optionsRoot.querySelectorAll('[data-combobox-option]').forEach((node) => node.remove());
|
|
180
|
+
root.querySelectorAll('template[data-combobox-tag-template]').forEach((node) => node.remove());
|
|
181
|
+
|
|
182
|
+
options.forEach((option, index) => {
|
|
183
|
+
const button = document.createElement('button');
|
|
184
|
+
button.className = 'combobox__option';
|
|
185
|
+
button.id = `${root.id}-option-${index}`;
|
|
186
|
+
button.type = 'button';
|
|
187
|
+
button.role = 'option';
|
|
188
|
+
button.tabIndex = -1;
|
|
189
|
+
button.disabled = option.disabled === true;
|
|
190
|
+
button.setAttribute('aria-selected', 'false');
|
|
191
|
+
if (option.disabled) button.setAttribute('aria-disabled', 'true');
|
|
192
|
+
button.dataset.comboboxOption = option.value;
|
|
193
|
+
button.dataset.comboboxSearchText = [option.label, option.description ?? ''].join(' ');
|
|
194
|
+
button.dataset.interactionAccent = 'start';
|
|
195
|
+
|
|
196
|
+
const content = document.createElement('span');
|
|
197
|
+
content.className = 'combobox__option-content';
|
|
198
|
+
const label = document.createElement('span');
|
|
199
|
+
label.className = 'combobox__option-label';
|
|
200
|
+
label.textContent = option.label;
|
|
201
|
+
content.append(label);
|
|
202
|
+
if (option.description) {
|
|
203
|
+
const description = document.createElement('span');
|
|
204
|
+
description.className = 'combobox__option-description';
|
|
205
|
+
description.textContent = option.description;
|
|
206
|
+
content.append(description);
|
|
207
|
+
}
|
|
208
|
+
button.append(content);
|
|
209
|
+
optionsRoot.insertBefore(button, empty);
|
|
210
|
+
|
|
211
|
+
const template = document.createElement('template');
|
|
212
|
+
template.dataset.comboboxTagTemplate = option.value;
|
|
213
|
+
const tag = document.createElement('span');
|
|
214
|
+
tag.className = 'tag';
|
|
215
|
+
tag.dataset.widget = 'tag';
|
|
216
|
+
tag.dataset.size = 'sm';
|
|
217
|
+
tag.dataset.comboboxTag = option.value;
|
|
218
|
+
const tagLabel = document.createElement('span');
|
|
219
|
+
tagLabel.className = 'tag__label';
|
|
220
|
+
tagLabel.dataset.tagLabel = '';
|
|
221
|
+
tagLabel.textContent = option.label;
|
|
222
|
+
tag.append(tagLabel);
|
|
223
|
+
template.content.append(tag);
|
|
224
|
+
root.append(template);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
empty.hidden = options.length > 0;
|
|
228
|
+
setComboboxValues(root, selectedValues);
|
|
229
|
+
}
|
|
230
|
+
|
|
162
231
|
export function setComboboxNumberValue(
|
|
163
232
|
root: HTMLElement,
|
|
164
233
|
requestedValue: number,
|
|
@@ -155,12 +155,12 @@
|
|
|
155
155
|
--map-viewport-cell-surface: color-mix(
|
|
156
156
|
in srgb,
|
|
157
157
|
var(--map-viewport-cell-seed) 38%,
|
|
158
|
-
var(--nox-
|
|
158
|
+
var(--nox-theme-surface-base)
|
|
159
159
|
);
|
|
160
160
|
--map-viewport-cell-surface-hover: color-mix(
|
|
161
161
|
in srgb,
|
|
162
162
|
var(--map-viewport-cell-seed) 52%,
|
|
163
|
-
var(--nox-
|
|
163
|
+
var(--nox-theme-surface-base)
|
|
164
164
|
);
|
|
165
165
|
--map-viewport-cell-background: color-mix(
|
|
166
166
|
in srgb,
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
--map-viewport-cell-border: color-mix(
|
|
176
176
|
in srgb,
|
|
177
177
|
var(--map-viewport-cell-seed) 74%,
|
|
178
|
-
var(--nox-
|
|
178
|
+
var(--nox-theme-text-base)
|
|
179
179
|
);
|
|
180
180
|
|
|
181
181
|
fill: var(--map-viewport-cell-background);
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
.table-body__row {
|
|
133
|
-
--table-primary-link-color: var(--nox-
|
|
133
|
+
--table-primary-link-color: var(--nox-content-link-color);
|
|
134
134
|
--table-row-intrinsic-block-size: var(--table-row-min-block-size);
|
|
135
135
|
|
|
136
136
|
display: grid;
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
.table-body__row[data-table-row-href]:focus-within {
|
|
176
|
-
--table-primary-link-color: var(--nox-link-
|
|
176
|
+
--table-primary-link-color: var(--nox-content-link-color);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
.table-body__cell {
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
|
|
254
254
|
@media (hover: hover) and (pointer: fine) {
|
|
255
255
|
.table-body__row[data-table-row-href]:hover {
|
|
256
|
-
--table-primary-link-color: var(--nox-link-
|
|
256
|
+
--table-primary-link-color: var(--nox-content-link-color);
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
|
|
@@ -68,6 +68,10 @@
|
|
|
68
68
|
var(--nox-motion-easing-standard);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
.table-cell__link [data-text-weight='strong'] {
|
|
72
|
+
font-weight: var(--nox-content-link-emphasis-font-weight);
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
.table-cell__empty {
|
|
72
76
|
color: var(--nox-text-muted);
|
|
73
77
|
font-size: var(--nox-font-size-sm);
|
|
@@ -93,7 +97,7 @@
|
|
|
93
97
|
|
|
94
98
|
@media (hover: hover) and (pointer: fine) {
|
|
95
99
|
.table-cell__link:hover {
|
|
96
|
-
color: var(--nox-link-
|
|
100
|
+
color: var(--nox-content-link-color);
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
|
package/src/styles/base.css
CHANGED
|
@@ -70,7 +70,20 @@ body {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
a {
|
|
73
|
-
color:
|
|
73
|
+
color: var(--nox-content-link-color);
|
|
74
|
+
font-weight: var(--nox-content-link-font-weight);
|
|
75
|
+
text-decoration: none;
|
|
76
|
+
text-decoration-thickness: 0.0625rem;
|
|
77
|
+
text-underline-offset: 0.2em;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
a[data-tone] {
|
|
81
|
+
color: var(--nox-tone-color);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
strong a,
|
|
85
|
+
a strong {
|
|
86
|
+
font-weight: var(--nox-content-link-emphasis-font-weight);
|
|
74
87
|
}
|
|
75
88
|
|
|
76
89
|
a:focus-visible,
|
|
@@ -83,6 +96,12 @@ button {
|
|
|
83
96
|
font: inherit;
|
|
84
97
|
}
|
|
85
98
|
|
|
99
|
+
@media (hover: hover) and (pointer: fine) {
|
|
100
|
+
a:where([href]:hover) {
|
|
101
|
+
text-decoration-line: underline;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
86
105
|
img,
|
|
87
106
|
svg,
|
|
88
107
|
video {
|
|
@@ -104,7 +123,6 @@ p {
|
|
|
104
123
|
overflow-y: auto;
|
|
105
124
|
background: var(--nox-page-background);
|
|
106
125
|
overscroll-behavior-y: contain;
|
|
107
|
-
scrollbar-gutter: stable both-edges;
|
|
108
126
|
}
|
|
109
127
|
|
|
110
128
|
.site-main {
|
|
@@ -113,18 +131,19 @@ p {
|
|
|
113
131
|
display: grid;
|
|
114
132
|
width: var(--nox-page-width);
|
|
115
133
|
margin: 0 auto;
|
|
116
|
-
padding-block: var(--site-main-content-gap)
|
|
117
|
-
|
|
134
|
+
padding-block: var(--site-main-content-gap);
|
|
135
|
+
align-content: start;
|
|
118
136
|
}
|
|
119
137
|
|
|
120
138
|
.site-main__content {
|
|
121
139
|
display: grid;
|
|
122
140
|
min-width: 0;
|
|
141
|
+
align-content: start;
|
|
123
142
|
gap: var(--site-main-content-gap);
|
|
124
143
|
}
|
|
125
144
|
|
|
126
145
|
.site-main:has(> .site-main__content > .breadcrumbs) {
|
|
127
|
-
padding-block
|
|
146
|
+
padding-block: 0 var(--site-main-content-gap);
|
|
128
147
|
}
|
|
129
148
|
|
|
130
149
|
.site-main__content:has(> .breadcrumbs) {
|
|
@@ -134,8 +153,6 @@ p {
|
|
|
134
153
|
@media (width < 48rem) {
|
|
135
154
|
.site-main {
|
|
136
155
|
--site-main-content-gap: var(--nox-space-4);
|
|
137
|
-
|
|
138
|
-
padding-block-end: var(--nox-space-6);
|
|
139
156
|
}
|
|
140
157
|
}
|
|
141
158
|
|
package/src/styles/style.css
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
@import '../components/actions/ActionListItem/ActionListItem.css'
|
|
18
18
|
layer(components);
|
|
19
19
|
@import '../components/actions/Button/Button.css' layer(components);
|
|
20
|
+
@import '../components/actions/ThemeToggle/ThemeToggle.css' layer(components);
|
|
20
21
|
@import '../components/actions/CardAction/CardAction.css'
|
|
21
22
|
layer(components);
|
|
22
23
|
@import '../components/actions/ScrollToTop/ScrollToTop.css'
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
@import '../components/navigation/Breadcrumbs/Breadcrumbs.css'
|
|
61
62
|
layer(components);
|
|
62
63
|
@import '../components/navigation/Header.css' layer(components);
|
|
64
|
+
@import '../components/navigation/Footer/Footer.css' layer(components);
|
|
63
65
|
@import '../components/navigation/NavigationBrand.css' layer(components);
|
|
64
66
|
@import '../components/navigation/NavigationControl.css' layer(components);
|
|
65
67
|
@import '../components/navigation/NavigationMenuList.css' layer(components);
|
package/src/styles/tokens.css
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
--nox-source-tone-warning: #d97706;
|
|
10
10
|
--nox-source-tone-danger: #dc2626;
|
|
11
11
|
|
|
12
|
+
/* Shared textual-link recipe. */
|
|
13
|
+
--nox-content-link-color: var(--nox-source-tone-info);
|
|
14
|
+
--nox-content-link-font-weight: var(--nox-font-weight-regular);
|
|
15
|
+
--nox-content-link-emphasis-font-weight: var(--nox-font-weight-medium);
|
|
16
|
+
|
|
12
17
|
/* Public map seeds. Component states derive their full treatment from these. */
|
|
13
18
|
--nox-source-map-available: #00b86b;
|
|
14
19
|
--nox-source-map-unavailable: #f43f5e;
|
|
@@ -22,6 +27,42 @@
|
|
|
22
27
|
--nox-mix-light: #ffffff;
|
|
23
28
|
--nox-mix-dark: #020617;
|
|
24
29
|
--nox-tone-seed: var(--nox-source-tone-neutral);
|
|
30
|
+
|
|
31
|
+
/* Theme recipe anchors. Light mode preserves the original palette. */
|
|
32
|
+
--nox-theme-page-base: var(--nox-mix-light);
|
|
33
|
+
--nox-theme-surface-base: var(--nox-mix-light);
|
|
34
|
+
--nox-theme-text-base: var(--nox-mix-dark);
|
|
35
|
+
--nox-theme-text-disabled-base: var(--nox-mix-light);
|
|
36
|
+
--nox-theme-border-base: var(--nox-mix-light);
|
|
37
|
+
--nox-theme-inverse-base: var(--nox-mix-light);
|
|
38
|
+
--nox-theme-shadow-base: var(--nox-mix-dark);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:root[data-theme='dark'] {
|
|
42
|
+
color-scheme: dark;
|
|
43
|
+
|
|
44
|
+
--nox-source-tone-neutral: #8f9bad;
|
|
45
|
+
--nox-source-tone-primary: #a78bfa;
|
|
46
|
+
--nox-source-tone-info: #73a4ff;
|
|
47
|
+
--nox-source-tone-success: #42d392;
|
|
48
|
+
--nox-source-tone-warning: #f0ad55;
|
|
49
|
+
--nox-source-tone-danger: #f47777;
|
|
50
|
+
|
|
51
|
+
--nox-source-map-available: #42d392;
|
|
52
|
+
--nox-source-map-unavailable: #fb7185;
|
|
53
|
+
--nox-source-map-field: #fbbf24;
|
|
54
|
+
--nox-source-map-grass: #84cc16;
|
|
55
|
+
--nox-source-map-mountain: #94a3b8;
|
|
56
|
+
--nox-source-map-water: #38bdf8;
|
|
57
|
+
--nox-source-map-range: #60a5fa;
|
|
58
|
+
|
|
59
|
+
--nox-theme-page-base: #0f1218;
|
|
60
|
+
--nox-theme-surface-base: #171b23;
|
|
61
|
+
--nox-theme-text-base: #f8fafc;
|
|
62
|
+
--nox-theme-text-disabled-base: #657184;
|
|
63
|
+
--nox-theme-border-base: #252b35;
|
|
64
|
+
--nox-theme-inverse-base: #0f1218;
|
|
65
|
+
--nox-theme-shadow-base: #000000;
|
|
25
66
|
}
|
|
26
67
|
|
|
27
68
|
:root,
|
|
@@ -30,51 +71,55 @@
|
|
|
30
71
|
--nox-page-background: color-mix(
|
|
31
72
|
in srgb,
|
|
32
73
|
var(--nox-tone-seed) 11%,
|
|
33
|
-
var(--nox-
|
|
74
|
+
var(--nox-theme-page-base)
|
|
34
75
|
);
|
|
35
76
|
--nox-surface: color-mix(
|
|
36
77
|
in srgb,
|
|
37
78
|
var(--nox-tone-seed) 2%,
|
|
38
|
-
var(--nox-
|
|
79
|
+
var(--nox-theme-surface-base)
|
|
39
80
|
);
|
|
40
81
|
--nox-surface-muted: color-mix(
|
|
41
82
|
in srgb,
|
|
42
83
|
var(--nox-tone-seed) 8%,
|
|
43
|
-
var(--nox-
|
|
84
|
+
var(--nox-theme-surface-base)
|
|
44
85
|
);
|
|
45
86
|
--nox-surface-strong: color-mix(
|
|
46
87
|
in srgb,
|
|
47
88
|
var(--nox-tone-seed) 14%,
|
|
48
|
-
var(--nox-
|
|
89
|
+
var(--nox-theme-surface-base)
|
|
49
90
|
);
|
|
50
91
|
|
|
51
|
-
--nox-text: color-mix(
|
|
92
|
+
--nox-text: color-mix(
|
|
93
|
+
in srgb,
|
|
94
|
+
var(--nox-tone-seed) 18%,
|
|
95
|
+
var(--nox-theme-text-base)
|
|
96
|
+
);
|
|
52
97
|
--nox-text-muted: color-mix(
|
|
53
98
|
in srgb,
|
|
54
99
|
var(--nox-tone-seed) 52%,
|
|
55
|
-
var(--nox-
|
|
100
|
+
var(--nox-theme-text-base)
|
|
56
101
|
);
|
|
57
102
|
--nox-text-subtle: color-mix(
|
|
58
103
|
in srgb,
|
|
59
104
|
var(--nox-tone-seed) 68%,
|
|
60
|
-
var(--nox-
|
|
105
|
+
var(--nox-theme-text-base)
|
|
61
106
|
);
|
|
62
107
|
--nox-text-disabled: color-mix(
|
|
63
108
|
in srgb,
|
|
64
109
|
var(--nox-tone-seed) 36%,
|
|
65
|
-
var(--nox-
|
|
110
|
+
var(--nox-theme-text-disabled-base)
|
|
66
111
|
);
|
|
67
|
-
--nox-text-inverse: var(--nox-
|
|
112
|
+
--nox-text-inverse: var(--nox-theme-inverse-base);
|
|
68
113
|
|
|
69
114
|
--nox-border: color-mix(
|
|
70
115
|
in srgb,
|
|
71
116
|
var(--nox-tone-seed) 28%,
|
|
72
|
-
var(--nox-
|
|
117
|
+
var(--nox-theme-border-base)
|
|
73
118
|
);
|
|
74
119
|
--nox-border-muted: color-mix(
|
|
75
120
|
in srgb,
|
|
76
121
|
var(--nox-tone-seed) 18%,
|
|
77
|
-
var(--nox-
|
|
122
|
+
var(--nox-theme-border-base)
|
|
78
123
|
);
|
|
79
124
|
|
|
80
125
|
/* Tone interaction slots. */
|
|
@@ -84,12 +129,12 @@
|
|
|
84
129
|
--nox-tone-hover-background: color-mix(
|
|
85
130
|
in srgb,
|
|
86
131
|
var(--nox-tone-seed) 12%,
|
|
87
|
-
var(--nox-
|
|
132
|
+
var(--nox-theme-surface-base)
|
|
88
133
|
);
|
|
89
134
|
--nox-tone-active-background: color-mix(
|
|
90
135
|
in srgb,
|
|
91
136
|
var(--nox-tone-seed) 20%,
|
|
92
|
-
var(--nox-
|
|
137
|
+
var(--nox-theme-surface-base)
|
|
93
138
|
);
|
|
94
139
|
--nox-tone-border: color-mix(
|
|
95
140
|
in srgb,
|
|
@@ -99,13 +144,13 @@
|
|
|
99
144
|
--nox-tone-text: color-mix(
|
|
100
145
|
in srgb,
|
|
101
146
|
var(--nox-tone-seed) 72%,
|
|
102
|
-
var(--nox-
|
|
147
|
+
var(--nox-theme-text-base)
|
|
103
148
|
);
|
|
104
149
|
--nox-tone-focus-ring: var(--nox-tone-seed);
|
|
105
150
|
--nox-tone-shadow: color-mix(
|
|
106
151
|
in srgb,
|
|
107
152
|
var(--nox-tone-seed) 24%,
|
|
108
|
-
var(--nox-
|
|
153
|
+
var(--nox-theme-shadow-base)
|
|
109
154
|
);
|
|
110
155
|
|
|
111
156
|
/* Shared framed-data surface slots. */
|
package/src/theme/contracts.ts
CHANGED
package/src/theme/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const defaultThemeStorageKey = 'astronox:theme';
|