@astronox/core 0.2.2 → 0.2.3
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/ScrollToTop/ScrollToTop.css +11 -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/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/index.ts +8 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.astro +5 -1
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.css +12 -17
- 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/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 +4 -1
- package/src/layouts/BaseLayout.astro +8 -1
- package/src/patterns/Combobox/comboboxController.ts +69 -0
- 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 +1 -0
- package/src/styles/tokens.css +5 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
.site-footer {
|
|
2
|
+
color: var(--nox-text-muted);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.site-footer__content {
|
|
6
|
+
--site-footer-item-gap: var(--nox-space-2);
|
|
7
|
+
--site-footer-marker-gap: 0.0625rem;
|
|
8
|
+
|
|
9
|
+
display: grid;
|
|
10
|
+
width: var(--nox-page-width);
|
|
11
|
+
margin-inline: auto;
|
|
12
|
+
justify-items: center;
|
|
13
|
+
gap: var(--nox-space-1);
|
|
14
|
+
padding-block-end: calc(
|
|
15
|
+
var(--nox-section-compact-block-size) + var(--nox-space-7)
|
|
16
|
+
);
|
|
17
|
+
font-size: var(--nox-font-size-sm);
|
|
18
|
+
line-height: 1.25;
|
|
19
|
+
text-align: center;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.site-footer__group {
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
min-width: 0;
|
|
25
|
+
align-items: center;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
flex-wrap: wrap;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.site-footer__content[data-footer-columns='2'] {
|
|
31
|
+
grid-template-columns: repeat(2, max-content);
|
|
32
|
+
justify-content: center;
|
|
33
|
+
column-gap: var(--nox-space-4);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.site-footer__content[data-footer-columns='2'][
|
|
37
|
+
data-footer-column-separator='divider'
|
|
38
|
+
] {
|
|
39
|
+
column-gap: 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.site-footer__content[data-footer-columns='2'][
|
|
43
|
+
data-footer-column-separator='divider'
|
|
44
|
+
]
|
|
45
|
+
> .site-footer__group:nth-child(even)::before {
|
|
46
|
+
display: inline-grid;
|
|
47
|
+
width: calc(
|
|
48
|
+
var(--site-footer-item-gap) + var(--site-footer-item-gap) + 0.5em
|
|
49
|
+
);
|
|
50
|
+
flex: 0 0 auto;
|
|
51
|
+
place-items: center;
|
|
52
|
+
color: var(--nox-border);
|
|
53
|
+
content: '|';
|
|
54
|
+
line-height: 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.site-footer__content[data-footer-columns='2']
|
|
58
|
+
> .site-footer__group:last-child:nth-child(odd) {
|
|
59
|
+
grid-column: 1 / -1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.site-footer__item {
|
|
63
|
+
display: inline-flex;
|
|
64
|
+
min-width: 0;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.site-footer__separator {
|
|
70
|
+
display: inline-grid;
|
|
71
|
+
width: calc(
|
|
72
|
+
var(--site-footer-item-gap) + var(--site-footer-item-gap) + 0.5em
|
|
73
|
+
);
|
|
74
|
+
flex: 0 0 auto;
|
|
75
|
+
place-items: center;
|
|
76
|
+
color: var(--nox-border);
|
|
77
|
+
line-height: 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.site-footer__spacer {
|
|
81
|
+
width: var(--site-footer-item-gap);
|
|
82
|
+
flex: 0 0 auto;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.site-footer__prefix + :is(a, .site-footer__label) {
|
|
86
|
+
margin-inline-start: var(--nox-space-1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.site-footer a,
|
|
90
|
+
.site-footer__label {
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
min-width: 0;
|
|
93
|
+
align-items: center;
|
|
94
|
+
gap: var(--site-footer-marker-gap);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.site-footer__marker {
|
|
98
|
+
display: block;
|
|
99
|
+
width: 0.625rem;
|
|
100
|
+
height: 0.625rem;
|
|
101
|
+
flex: 0 0 auto;
|
|
102
|
+
color: var(--nox-text-muted);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.site-footer a {
|
|
106
|
+
color: var(--nox-content-link-color);
|
|
107
|
+
font-weight: var(--nox-content-link-emphasis-font-weight);
|
|
108
|
+
text-decoration: none;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.site-footer a[data-tone],
|
|
112
|
+
.site-footer__label[data-tone] {
|
|
113
|
+
color: var(--nox-tone-color);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.site-footer a:focus-visible {
|
|
117
|
+
border-radius: var(--nox-radius-sm);
|
|
118
|
+
outline: var(--nox-focus-ring-width) solid var(--nox-tone-focus-ring);
|
|
119
|
+
outline-offset: var(--nox-focus-ring-offset);
|
|
120
|
+
color: var(--nox-content-link-color);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.site-footer a[data-tone]:focus-visible {
|
|
124
|
+
color: var(--nox-tone-color);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@media (hover: hover) and (pointer: fine) {
|
|
128
|
+
.site-footer a:hover {
|
|
129
|
+
color: var(--nox-content-link-color);
|
|
130
|
+
text-decoration-line: underline;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.site-footer a[data-tone]:hover {
|
|
134
|
+
color: var(--nox-tone-color);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@media (width < 48rem) {
|
|
139
|
+
.site-footer__content[data-footer-columns='2'] {
|
|
140
|
+
grid-template-columns: minmax(0, 1fr);
|
|
141
|
+
column-gap: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.site-footer__content[data-footer-columns='2']
|
|
145
|
+
> .site-footer__group:last-child:nth-child(odd) {
|
|
146
|
+
grid-column: auto;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.site-footer__content[data-footer-columns='2'][
|
|
150
|
+
data-footer-column-separator='divider'
|
|
151
|
+
]
|
|
152
|
+
> .site-footer__group:nth-child(even)::before {
|
|
153
|
+
content: none;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
2
|
+
import type { Tone } from '../../../theme/contracts';
|
|
3
|
+
|
|
4
|
+
export type FooterItem = {
|
|
5
|
+
href?: HTMLAttributes<'a'>['href'];
|
|
6
|
+
label: string;
|
|
7
|
+
marker?: 'arrow';
|
|
8
|
+
prefix?: string;
|
|
9
|
+
rel?: HTMLAttributes<'a'>['rel'];
|
|
10
|
+
separator?: 'divider' | 'none';
|
|
11
|
+
target?: HTMLAttributes<'a'>['target'];
|
|
12
|
+
tone?: Tone;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type FooterGroup = {
|
|
16
|
+
items: readonly FooterItem[];
|
|
17
|
+
};
|
|
@@ -115,6 +115,10 @@
|
|
|
115
115
|
grid-template-columns: 1fr;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
.grid[data-layout='fixed'][data-collapse-at='sm'][data-last-row='fill'] > * {
|
|
119
|
+
grid-column: auto;
|
|
120
|
+
}
|
|
121
|
+
|
|
118
122
|
.grid:is(
|
|
119
123
|
[data-layout='fixed'],
|
|
120
124
|
[data-layout='content-sidebar']
|
|
@@ -163,6 +167,10 @@
|
|
|
163
167
|
grid-template-columns: 1fr;
|
|
164
168
|
}
|
|
165
169
|
|
|
170
|
+
.grid[data-layout='fixed'][data-collapse-at='md'][data-last-row='fill'] > * {
|
|
171
|
+
grid-column: auto;
|
|
172
|
+
}
|
|
173
|
+
|
|
166
174
|
.grid:is(
|
|
167
175
|
[data-layout='fixed'],
|
|
168
176
|
[data-layout='content-sidebar']
|
|
@@ -211,6 +219,10 @@
|
|
|
211
219
|
grid-template-columns: 1fr;
|
|
212
220
|
}
|
|
213
221
|
|
|
222
|
+
.grid[data-layout='fixed'][data-collapse-at='lg'][data-last-row='fill'] > * {
|
|
223
|
+
grid-column: auto;
|
|
224
|
+
}
|
|
225
|
+
|
|
214
226
|
.grid:is(
|
|
215
227
|
[data-layout='fixed'],
|
|
216
228
|
[data-layout='content-sidebar']
|
|
@@ -259,6 +271,10 @@
|
|
|
259
271
|
grid-template-columns: 1fr;
|
|
260
272
|
}
|
|
261
273
|
|
|
274
|
+
.grid[data-layout='fixed'][data-collapse-at='xl'][data-last-row='fill'] > * {
|
|
275
|
+
grid-column: auto;
|
|
276
|
+
}
|
|
277
|
+
|
|
262
278
|
.grid:is(
|
|
263
279
|
[data-layout='fixed'],
|
|
264
280
|
[data-layout='content-sidebar']
|
|
@@ -53,3 +53,26 @@ export type ClientFirestoreRepositoryConfig<
|
|
|
53
53
|
orderBy?: readonly ClientFirestoreOrder[];
|
|
54
54
|
schema: ClientFirestoreSchema<TDocument>;
|
|
55
55
|
};
|
|
56
|
+
|
|
57
|
+
export type ClientFirestoreBatchOperation =
|
|
58
|
+
| {
|
|
59
|
+
collection: string;
|
|
60
|
+
id?: string;
|
|
61
|
+
kind: 'create';
|
|
62
|
+
value: Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
| {
|
|
65
|
+
collection: string;
|
|
66
|
+
id: string;
|
|
67
|
+
kind: 'update';
|
|
68
|
+
value: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
collection: string;
|
|
72
|
+
id: string;
|
|
73
|
+
kind: 'delete';
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type ClientFirestoreBatchResult = {
|
|
77
|
+
createdIds: readonly string[];
|
|
78
|
+
};
|
|
@@ -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';
|
|
@@ -16,6 +16,7 @@ type Props = HTMLAttributes<'html'> & {
|
|
|
16
16
|
currentPath?: string;
|
|
17
17
|
breadcrumbs?: readonly BreadcrumbItem[];
|
|
18
18
|
breadcrumbLabel: string;
|
|
19
|
+
breadcrumbTone?: Tone;
|
|
19
20
|
scrollToTopLabel?: string;
|
|
20
21
|
structuredData?: readonly JsonLdNode[];
|
|
21
22
|
tone?: Tone;
|
|
@@ -23,6 +24,7 @@ type Props = HTMLAttributes<'html'> & {
|
|
|
23
24
|
|
|
24
25
|
const {
|
|
25
26
|
breadcrumbLabel,
|
|
27
|
+
breadcrumbTone,
|
|
26
28
|
breadcrumbs,
|
|
27
29
|
currentPath = Astro.url.pathname,
|
|
28
30
|
'data-tone': dataTone,
|
|
@@ -59,7 +61,11 @@ const resolvedTone = tone ?? dataTone;
|
|
|
59
61
|
<div class="site-main__content">
|
|
60
62
|
{
|
|
61
63
|
breadcrumbs?.length ? (
|
|
62
|
-
<Breadcrumbs
|
|
64
|
+
<Breadcrumbs
|
|
65
|
+
items={breadcrumbs}
|
|
66
|
+
label={breadcrumbLabel}
|
|
67
|
+
tone={breadcrumbTone}
|
|
68
|
+
/>
|
|
63
69
|
) : null
|
|
64
70
|
}
|
|
65
71
|
|
|
@@ -68,6 +74,7 @@ const resolvedTone = tone ?? dataTone;
|
|
|
68
74
|
</PageSurface>
|
|
69
75
|
</div>
|
|
70
76
|
</main>
|
|
77
|
+
<slot name="footer" />
|
|
71
78
|
</div>
|
|
72
79
|
{scrollToTopLabel ? <ScrollToTop label={scrollToTopLabel} /> : null}
|
|
73
80
|
</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,
|
|
@@ -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
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
@import '../components/navigation/Breadcrumbs/Breadcrumbs.css'
|
|
61
61
|
layer(components);
|
|
62
62
|
@import '../components/navigation/Header.css' layer(components);
|
|
63
|
+
@import '../components/navigation/Footer/Footer.css' layer(components);
|
|
63
64
|
@import '../components/navigation/NavigationBrand.css' layer(components);
|
|
64
65
|
@import '../components/navigation/NavigationControl.css' layer(components);
|
|
65
66
|
@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;
|