@abreen/tada 1.15.13 → 1.16.1
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/README.md +4 -0
- package/assets/material-symbols/celebration_24px.svg +1 -0
- package/assets/material-symbols/close_24px.svg +1 -0
- package/assets/material-symbols/contrast_24px.svg +1 -0
- package/assets/material-symbols/contrast_rtl_off_24px.svg +1 -0
- package/assets/material-symbols/info_24px.svg +1 -0
- package/assets/material-symbols/menu_24px.svg +1 -0
- package/assets/material-symbols/north_east_24px.svg +1 -0
- package/assets/material-symbols/search_24px.svg +1 -0
- package/assets/material-symbols/south_east_24px.svg +1 -0
- package/assets/material-symbols/tag_24px.svg +1 -0
- package/assets/material-symbols/warning_24px.svg +1 -0
- package/bin/validators.ts +2 -0
- package/build/bundle.ts +7 -12
- package/build/generate-fonts.ts +17 -0
- package/build/material-symbols.ts +107 -0
- package/build/site-variables.ts +2 -0
- package/build/types.d.ts +2 -0
- package/build/utils/markdown.ts +11 -43
- package/build/utils/render.ts +24 -8
- package/build/watch/assets.ts +3 -3
- package/fonts/libertinus-mono/LICENSE.txt +99 -0
- package/fonts/libertinus-mono/woff2/LibertinusMono-Regular.woff2 +0 -0
- package/fonts/source-serif-4/LICENSE.txt +91 -0
- package/fonts/source-serif-4/woff2/SourceSerif4-Italic-VariableFont_opsz,wght.woff2 +0 -0
- package/fonts/source-serif-4/woff2/SourceSerif4-VariableFont_opsz,wght.woff2 +0 -0
- package/package.json +2 -1
- package/schema/site.schema.json +10 -0
- package/src/_alerts.scss +1 -1
- package/src/_base.scss +2 -16
- package/src/anchor/index.ts +14 -10
- package/src/anchor/style.scss +10 -10
- package/src/appearance-picker/index.ts +214 -0
- package/src/appearance-picker/style.scss +130 -0
- package/src/code.scss +1 -0
- package/src/header/_base.scss +34 -6
- package/src/header/style.scss +8 -0
- package/src/index.ts +2 -0
- package/src/material-symbols.scss +11 -0
- package/src/navigate/lifecycle.ts +2 -0
- package/src/search/style.scss +0 -1
- package/src/slides/style.scss +1 -0
- package/src/style.scss +30 -28
- package/src/toc/style.scss +3 -1
- package/src/trace/style.scss +1 -0
- package/templates/_appearance-picker.html +18 -0
- package/templates/_theme.scss +43 -13
- package/templates/_top.html +38 -2
- package/templates/code.html +1 -0
- package/templates/default.html +1 -0
- package/templates/literate.html +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abreen/tada",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A static site generator",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"build/",
|
|
18
18
|
"src/",
|
|
19
19
|
"templates/",
|
|
20
|
+
"assets/",
|
|
20
21
|
"fonts/",
|
|
21
22
|
"init/",
|
|
22
23
|
"schema/",
|
package/schema/site.schema.json
CHANGED
|
@@ -48,6 +48,16 @@
|
|
|
48
48
|
"default": 700
|
|
49
49
|
},
|
|
50
50
|
"defaultTimeZone": { "type": "string" },
|
|
51
|
+
"defaultFont": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"enum": ["sans", "serif"],
|
|
54
|
+
"default": "sans"
|
|
55
|
+
},
|
|
56
|
+
"defaultContrast": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"enum": ["standard", "high"],
|
|
59
|
+
"default": "standard"
|
|
60
|
+
},
|
|
51
61
|
"vars": { "type": "object" },
|
|
52
62
|
"extensionToShikiLanguage": {
|
|
53
63
|
"type": "object",
|
package/src/_alerts.scss
CHANGED
package/src/_base.scss
CHANGED
|
@@ -33,18 +33,7 @@ body {
|
|
|
33
33
|
font-family: var(--font);
|
|
34
34
|
font-size: var(--font-size);
|
|
35
35
|
font-weight: var(--font-weight);
|
|
36
|
-
|
|
37
|
-
/*
|
|
38
|
-
* Enable Inter's features:
|
|
39
|
-
* liga - standard ligatures
|
|
40
|
-
* calt - contextual alternates
|
|
41
|
-
* ss07 - square punctuation
|
|
42
|
-
* ss08 - square quotes
|
|
43
|
-
* cv01 - alternate 1
|
|
44
|
-
* cv08 - uppercase I with serif
|
|
45
|
-
* cv10 - capital G with spur
|
|
46
|
-
*/
|
|
47
|
-
font-feature-settings: 'liga', 'calt', 'ss07', 'ss08', 'cv01', 'cv08', 'cv10';
|
|
36
|
+
font-feature-settings: var(--font-feature-settings);
|
|
48
37
|
line-height: var(--line-height);
|
|
49
38
|
color: var(--fg-color);
|
|
50
39
|
background-color: var(--bg-color);
|
|
@@ -94,6 +83,7 @@ pre,
|
|
|
94
83
|
tt,
|
|
95
84
|
code {
|
|
96
85
|
font-family: var(--mono-font);
|
|
86
|
+
font-feature-settings: var(--mono-font-feature-settings);
|
|
97
87
|
}
|
|
98
88
|
|
|
99
89
|
tt,
|
|
@@ -182,10 +172,6 @@ input[type='search'] {
|
|
|
182
172
|
min-width: 0;
|
|
183
173
|
padding-left: 2rem;
|
|
184
174
|
background-color: var(--bg-color);
|
|
185
|
-
background-image: var(--icon-search);
|
|
186
|
-
background-repeat: no-repeat;
|
|
187
|
-
background-position: left 6px center;
|
|
188
|
-
background-size: 16px 16px;
|
|
189
175
|
border-radius: 18px;
|
|
190
176
|
|
|
191
177
|
&:disabled {
|
package/src/anchor/index.ts
CHANGED
|
@@ -2,7 +2,14 @@ function getElements(parent: HTMLElement): HTMLHeadingElement[] {
|
|
|
2
2
|
return Array.from(parent.querySelectorAll('h1, h2, h3, h4, h5, h6'));
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
function
|
|
5
|
+
function createHeadingAnchorIcon(window: Window): HTMLSpanElement {
|
|
6
|
+
const icon = window.document.createElement('span');
|
|
7
|
+
icon.className = 'material-symbol-icon material-symbol-icon-heading-anchor';
|
|
8
|
+
icon.setAttribute('aria-hidden', 'true');
|
|
9
|
+
return icon;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createPresentIcon(window: Window): SVGSVGElement {
|
|
6
13
|
const svg = window.document.createElementNS(
|
|
7
14
|
'http://www.w3.org/2000/svg',
|
|
8
15
|
'svg',
|
|
@@ -17,13 +24,10 @@ function createIcon(window: Window, type: 'hash' | 'present'): SVGSVGElement {
|
|
|
17
24
|
svg.setAttribute('stroke-linecap', 'round');
|
|
18
25
|
svg.setAttribute('stroke-linejoin', 'round');
|
|
19
26
|
|
|
20
|
-
const paths =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
'M5 5h14a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z',
|
|
25
|
-
'M9 20h6',
|
|
26
|
-
];
|
|
27
|
+
const paths = [
|
|
28
|
+
'M5 5h14a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z',
|
|
29
|
+
'M9 20h6',
|
|
30
|
+
];
|
|
27
31
|
|
|
28
32
|
for (const d of paths) {
|
|
29
33
|
const path = window.document.createElementNS(
|
|
@@ -82,7 +86,7 @@ export default (window: Window) => {
|
|
|
82
86
|
while (el.firstChild) {
|
|
83
87
|
link.appendChild(el.firstChild);
|
|
84
88
|
}
|
|
85
|
-
link.appendChild(
|
|
89
|
+
link.appendChild(createHeadingAnchorIcon(window));
|
|
86
90
|
el.appendChild(link);
|
|
87
91
|
|
|
88
92
|
const handleClick = () => {
|
|
@@ -103,7 +107,7 @@ export default (window: Window) => {
|
|
|
103
107
|
button.className = 'heading-present-button';
|
|
104
108
|
button.setAttribute('aria-label', 'Present from this slide');
|
|
105
109
|
button.title = 'Present from this slide';
|
|
106
|
-
button.appendChild(
|
|
110
|
+
button.appendChild(createPresentIcon(window));
|
|
107
111
|
el.appendChild(button);
|
|
108
112
|
|
|
109
113
|
const slide = el.parentElement;
|
package/src/anchor/style.scss
CHANGED
|
@@ -7,7 +7,7 @@ a.heading-anchor {
|
|
|
7
7
|
@include subdued-link;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
.heading-anchor
|
|
10
|
+
.heading-anchor .material-symbol-icon,
|
|
11
11
|
.heading-present-button svg {
|
|
12
12
|
display: inline-block;
|
|
13
13
|
width: 1em;
|
|
@@ -15,7 +15,7 @@ a.heading-anchor {
|
|
|
15
15
|
vertical-align: middle;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
.heading-anchor
|
|
18
|
+
.heading-anchor .material-symbol-icon {
|
|
19
19
|
margin-left: 4px;
|
|
20
20
|
vertical-align: middle;
|
|
21
21
|
opacity: 0;
|
|
@@ -39,7 +39,7 @@ a.heading-anchor {
|
|
|
39
39
|
opacity: 0;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
a.heading-anchor:focus-visible
|
|
42
|
+
a.heading-anchor:focus-visible .material-symbol-icon,
|
|
43
43
|
.heading-present-button:focus {
|
|
44
44
|
opacity: 1;
|
|
45
45
|
}
|
|
@@ -65,12 +65,12 @@ body.is-presenting .heading-present-button {
|
|
|
65
65
|
background: transparent;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
h1:hover a.heading-anchor
|
|
69
|
-
h2:hover a.heading-anchor
|
|
70
|
-
h3:hover a.heading-anchor
|
|
71
|
-
h4:hover a.heading-anchor
|
|
72
|
-
h5:hover a.heading-anchor
|
|
73
|
-
h6:hover a.heading-anchor
|
|
68
|
+
h1:hover a.heading-anchor .material-symbol-icon,
|
|
69
|
+
h2:hover a.heading-anchor .material-symbol-icon,
|
|
70
|
+
h3:hover a.heading-anchor .material-symbol-icon,
|
|
71
|
+
h4:hover a.heading-anchor .material-symbol-icon,
|
|
72
|
+
h5:hover a.heading-anchor .material-symbol-icon,
|
|
73
|
+
h6:hover a.heading-anchor .material-symbol-icon,
|
|
74
74
|
h1:hover .heading-present-button,
|
|
75
75
|
h2:hover .heading-present-button,
|
|
76
76
|
h3:hover .heading-present-button,
|
|
@@ -88,7 +88,7 @@ body.is-presenting .heading-present-button {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
@media print {
|
|
91
|
-
.heading-anchor
|
|
91
|
+
.heading-anchor .material-symbol-icon,
|
|
92
92
|
.heading-present-button {
|
|
93
93
|
display: none;
|
|
94
94
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
export type FontPreference = 'sans' | 'serif';
|
|
2
|
+
export type ContrastPreference = 'standard' | 'high';
|
|
3
|
+
|
|
4
|
+
const FONT_STORAGE_KEY = 'fontPreference';
|
|
5
|
+
const CONTRAST_STORAGE_KEY = 'contrastPreference';
|
|
6
|
+
|
|
7
|
+
function getStorage(window: Window): Storage | null {
|
|
8
|
+
try {
|
|
9
|
+
return window.localStorage;
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getStoredPreference<T extends string>(
|
|
16
|
+
storage: Storage | null,
|
|
17
|
+
key: string,
|
|
18
|
+
storedValues: readonly T[],
|
|
19
|
+
defaultValue: T,
|
|
20
|
+
): T {
|
|
21
|
+
try {
|
|
22
|
+
const storedValue = storage?.getItem(key);
|
|
23
|
+
return storedValues.includes(storedValue as T)
|
|
24
|
+
? (storedValue as T)
|
|
25
|
+
: defaultValue;
|
|
26
|
+
} catch {
|
|
27
|
+
return defaultValue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function saveStoredPreference<T extends string>(
|
|
32
|
+
storage: Storage | null,
|
|
33
|
+
key: string,
|
|
34
|
+
preference: T,
|
|
35
|
+
defaultValue: T,
|
|
36
|
+
): void {
|
|
37
|
+
try {
|
|
38
|
+
if (preference === defaultValue) {
|
|
39
|
+
storage?.removeItem(key);
|
|
40
|
+
} else {
|
|
41
|
+
storage?.setItem(key, preference);
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
// Storage can be unavailable in privacy-restricted contexts.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getFontPreference(
|
|
49
|
+
storage: Storage | null,
|
|
50
|
+
defaultValue: FontPreference = 'sans',
|
|
51
|
+
): FontPreference {
|
|
52
|
+
return getStoredPreference(
|
|
53
|
+
storage,
|
|
54
|
+
FONT_STORAGE_KEY,
|
|
55
|
+
['sans', 'serif'],
|
|
56
|
+
defaultValue,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getContrastPreference(
|
|
61
|
+
storage: Storage | null,
|
|
62
|
+
defaultValue: ContrastPreference = 'standard',
|
|
63
|
+
): ContrastPreference {
|
|
64
|
+
return getStoredPreference(
|
|
65
|
+
storage,
|
|
66
|
+
CONTRAST_STORAGE_KEY,
|
|
67
|
+
['standard', 'high'],
|
|
68
|
+
defaultValue,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function saveFontPreference(
|
|
73
|
+
storage: Storage | null,
|
|
74
|
+
preference: FontPreference,
|
|
75
|
+
defaultValue: FontPreference = 'sans',
|
|
76
|
+
): void {
|
|
77
|
+
saveStoredPreference(storage, FONT_STORAGE_KEY, preference, defaultValue);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function saveContrastPreference(
|
|
81
|
+
storage: Storage | null,
|
|
82
|
+
preference: ContrastPreference,
|
|
83
|
+
defaultValue: ContrastPreference = 'standard',
|
|
84
|
+
): void {
|
|
85
|
+
saveStoredPreference(storage, CONTRAST_STORAGE_KEY, preference, defaultValue);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getDefaultFontPreference(document: Document): FontPreference {
|
|
89
|
+
return document.documentElement.dataset.defaultFontPreference === 'serif'
|
|
90
|
+
? 'serif'
|
|
91
|
+
: 'sans';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getDefaultContrastPreference(document: Document): ContrastPreference {
|
|
95
|
+
return document.documentElement.dataset.defaultContrastPreference === 'high'
|
|
96
|
+
? 'high'
|
|
97
|
+
: 'standard';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function applyFontPreference(
|
|
101
|
+
document: Document,
|
|
102
|
+
preference: FontPreference,
|
|
103
|
+
): void {
|
|
104
|
+
if (preference === 'serif') {
|
|
105
|
+
if (document.documentElement.dataset.fontPreference !== preference) {
|
|
106
|
+
document.documentElement.dataset.fontPreference = preference;
|
|
107
|
+
}
|
|
108
|
+
} else if (document.documentElement.dataset.fontPreference !== undefined) {
|
|
109
|
+
delete document.documentElement.dataset.fontPreference;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function applyContrastPreference(
|
|
114
|
+
document: Document,
|
|
115
|
+
preference: ContrastPreference,
|
|
116
|
+
): void {
|
|
117
|
+
if (preference === 'high') {
|
|
118
|
+
if (document.documentElement.dataset.contrastPreference !== preference) {
|
|
119
|
+
document.documentElement.dataset.contrastPreference = preference;
|
|
120
|
+
}
|
|
121
|
+
} else if (
|
|
122
|
+
document.documentElement.dataset.contrastPreference !== undefined
|
|
123
|
+
) {
|
|
124
|
+
delete document.documentElement.dataset.contrastPreference;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function syncControls(
|
|
129
|
+
document: Document,
|
|
130
|
+
selector: string,
|
|
131
|
+
preference: string,
|
|
132
|
+
): void {
|
|
133
|
+
document.querySelectorAll<HTMLButtonElement>(selector).forEach(button => {
|
|
134
|
+
button.disabled = false;
|
|
135
|
+
button.setAttribute(
|
|
136
|
+
'aria-pressed',
|
|
137
|
+
String(
|
|
138
|
+
button.dataset.fontPreferenceValue === preference ||
|
|
139
|
+
button.dataset.contrastPreferenceValue === preference,
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function syncAppearance(
|
|
146
|
+
document: Document,
|
|
147
|
+
fontPreference: FontPreference,
|
|
148
|
+
contrastPreference: ContrastPreference,
|
|
149
|
+
): void {
|
|
150
|
+
syncControls(document, '[data-font-preference-value]', fontPreference);
|
|
151
|
+
syncControls(
|
|
152
|
+
document,
|
|
153
|
+
'[data-contrast-preference-value]',
|
|
154
|
+
contrastPreference,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export default function mountAppearancePicker(window: Window): () => void {
|
|
159
|
+
const { document } = window;
|
|
160
|
+
const container = document.querySelector<HTMLElement>('.appearance-pickers');
|
|
161
|
+
if (!container) {
|
|
162
|
+
return () => {};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const buttons = Array.from(
|
|
166
|
+
container.querySelectorAll<HTMLButtonElement>('button'),
|
|
167
|
+
);
|
|
168
|
+
const storage = getStorage(window);
|
|
169
|
+
const defaultFontPreference = getDefaultFontPreference(document);
|
|
170
|
+
const defaultContrastPreference = getDefaultContrastPreference(document);
|
|
171
|
+
let fontPreference = getFontPreference(storage, defaultFontPreference);
|
|
172
|
+
let contrastPreference = getContrastPreference(
|
|
173
|
+
storage,
|
|
174
|
+
defaultContrastPreference,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
applyFontPreference(document, fontPreference);
|
|
178
|
+
applyContrastPreference(document, contrastPreference);
|
|
179
|
+
syncAppearance(document, fontPreference, contrastPreference);
|
|
180
|
+
|
|
181
|
+
const handleClick = (event: Event) => {
|
|
182
|
+
const button = event.currentTarget as HTMLButtonElement;
|
|
183
|
+
const fontValue = button.dataset.fontPreferenceValue;
|
|
184
|
+
const contrastValue = button.dataset.contrastPreferenceValue;
|
|
185
|
+
|
|
186
|
+
if (fontValue === 'sans' || fontValue === 'serif') {
|
|
187
|
+
fontPreference = fontValue;
|
|
188
|
+
saveFontPreference(storage, fontPreference, defaultFontPreference);
|
|
189
|
+
applyFontPreference(document, fontPreference);
|
|
190
|
+
} else if (contrastValue === 'standard' || contrastValue === 'high') {
|
|
191
|
+
contrastPreference = contrastValue;
|
|
192
|
+
saveContrastPreference(
|
|
193
|
+
storage,
|
|
194
|
+
contrastPreference,
|
|
195
|
+
defaultContrastPreference,
|
|
196
|
+
);
|
|
197
|
+
applyContrastPreference(document, contrastPreference);
|
|
198
|
+
} else {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
syncAppearance(document, fontPreference, contrastPreference);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
for (const button of buttons) {
|
|
206
|
+
button.addEventListener('click', handleClick);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return () => {
|
|
210
|
+
for (const button of buttons) {
|
|
211
|
+
button.removeEventListener('click', handleClick);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
@use '../mixins' as *;
|
|
2
|
+
|
|
3
|
+
.appearance-pickers {
|
|
4
|
+
display: flex;
|
|
5
|
+
gap: 0.5rem;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
margin: 2rem auto 1rem;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.preference-picker {
|
|
11
|
+
--preference-picker-option-width: 2.5rem;
|
|
12
|
+
--preference-picker-gap: 0.5rem;
|
|
13
|
+
--preference-picker-padding: 6px;
|
|
14
|
+
|
|
15
|
+
@include user-select(none);
|
|
16
|
+
|
|
17
|
+
position: relative;
|
|
18
|
+
display: grid;
|
|
19
|
+
grid-template-columns: repeat(2, var(--preference-picker-option-width));
|
|
20
|
+
gap: var(--preference-picker-gap);
|
|
21
|
+
width: fit-content;
|
|
22
|
+
padding: var(--preference-picker-padding);
|
|
23
|
+
background: var(--bg2-color);
|
|
24
|
+
border-radius: calc(var(--border-radius) + 4px);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.preference-picker::before {
|
|
28
|
+
position: absolute;
|
|
29
|
+
top: var(--preference-picker-padding);
|
|
30
|
+
bottom: var(--preference-picker-padding);
|
|
31
|
+
left: var(--preference-picker-padding);
|
|
32
|
+
width: var(--preference-picker-option-width);
|
|
33
|
+
content: '';
|
|
34
|
+
background: var(--bg-color);
|
|
35
|
+
border-radius: var(--border-radius);
|
|
36
|
+
box-shadow: 0 0 0 var(--control-border-width) var(--fg2-color);
|
|
37
|
+
transform: translateX(0);
|
|
38
|
+
|
|
39
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
40
|
+
transition: transform 0.2s ease;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
:root[data-font-preference='serif'] .font-picker::before,
|
|
45
|
+
:root[data-contrast-preference='high'] .contrast-picker::before {
|
|
46
|
+
transform: translateX(calc(100% + var(--preference-picker-gap)));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.preference-picker:has(
|
|
50
|
+
.preference-picker-option[aria-pressed='true']:is(:hover, :focus)
|
|
51
|
+
)::before {
|
|
52
|
+
background: var(--bg2-color);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.preference-picker:has(
|
|
56
|
+
.preference-picker-option[aria-pressed='true']:active:not(:disabled)
|
|
57
|
+
)::before {
|
|
58
|
+
background: var(--link-active-color);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.preference-picker-option {
|
|
62
|
+
position: relative;
|
|
63
|
+
z-index: 1;
|
|
64
|
+
display: inline-flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
width: var(--preference-picker-option-width);
|
|
68
|
+
height: 2rem;
|
|
69
|
+
padding: 0;
|
|
70
|
+
font-size: var(--font-size-smaller);
|
|
71
|
+
color: var(--fg2-color);
|
|
72
|
+
background: transparent;
|
|
73
|
+
border: 0;
|
|
74
|
+
border-radius: var(--border-radius);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.font-picker-option-sans {
|
|
78
|
+
font-family:
|
|
79
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
|
|
80
|
+
sans-serif;
|
|
81
|
+
font-feature-settings: normal;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.font-picker-option-serif {
|
|
85
|
+
// Avoid requesting the bundled serif face just to render the inactive
|
|
86
|
+
// preview on a default sans page.
|
|
87
|
+
font-family: var(--serif-preview-font);
|
|
88
|
+
font-feature-settings: var(--serif-font-feature-settings);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:root:not([data-font-preference='serif']) .font-picker-option-sans {
|
|
92
|
+
font-family: var(--sans-font);
|
|
93
|
+
font-feature-settings: var(--sans-font-feature-settings);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
:root[data-font-preference='serif'] .font-picker-option-serif {
|
|
97
|
+
font-family: var(--serif-font);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.material-symbol-icon {
|
|
101
|
+
width: 1.25rem;
|
|
102
|
+
height: 1.25rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.material-symbol-icon-contrast-standard {
|
|
106
|
+
--material-symbol-icon: var(--icon-contrast-standard);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.material-symbol-icon-contrast-high {
|
|
110
|
+
--material-symbol-icon: var(--icon-contrast-high);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.preference-picker-option[aria-pressed='true'] {
|
|
114
|
+
color: var(--fg-color);
|
|
115
|
+
background: transparent;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.preference-picker-option[aria-pressed='true']:is(:hover, :focus) {
|
|
119
|
+
background: transparent;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.preference-picker-option[aria-pressed='true']:active:not(:disabled) {
|
|
123
|
+
background: transparent !important;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@media print {
|
|
127
|
+
.appearance-pickers {
|
|
128
|
+
display: none;
|
|
129
|
+
}
|
|
130
|
+
}
|
package/src/code.scss
CHANGED
package/src/header/_base.scss
CHANGED
|
@@ -16,6 +16,10 @@ header {
|
|
|
16
16
|
view-transition-name: site-header;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
:root[data-contrast-preference='high'] header {
|
|
20
|
+
background: var(--bg-color);
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
header details summary {
|
|
20
24
|
position: relative;
|
|
21
25
|
display: flex;
|
|
@@ -56,15 +60,20 @@ header details summary::marker {
|
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
header summary::after {
|
|
63
|
+
flex: 0 0 24px;
|
|
59
64
|
order: -1;
|
|
65
|
+
width: 24px;
|
|
60
66
|
height: 24px;
|
|
61
67
|
margin-right: 8px;
|
|
62
68
|
margin-left: var(--gap);
|
|
63
|
-
content:
|
|
69
|
+
content: '';
|
|
70
|
+
|
|
71
|
+
@include contained-mask(var(--icon-header-menu));
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
header details[open] summary::after {
|
|
67
|
-
|
|
75
|
+
-webkit-mask-image: var(--icon-header-close);
|
|
76
|
+
mask-image: var(--icon-header-close);
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
header .content {
|
|
@@ -131,16 +140,35 @@ header .results-container {
|
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
header .search-controls {
|
|
143
|
+
position: relative;
|
|
134
144
|
display: flex;
|
|
135
145
|
align-items: center;
|
|
136
146
|
height: var(--header-collapsed-height);
|
|
137
147
|
|
|
148
|
+
@media (width <= 400px) {
|
|
149
|
+
display: none;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
&::before {
|
|
153
|
+
position: absolute;
|
|
154
|
+
top: 50%;
|
|
155
|
+
left: 7px;
|
|
156
|
+
z-index: 1;
|
|
157
|
+
width: 22px;
|
|
158
|
+
height: 22px;
|
|
159
|
+
pointer-events: none;
|
|
160
|
+
content: '';
|
|
161
|
+
transform: translateY(-50%);
|
|
162
|
+
|
|
163
|
+
@include contained-mask(var(--icon-search));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
&:has(input:disabled)::before {
|
|
167
|
+
opacity: 0.4;
|
|
168
|
+
}
|
|
169
|
+
|
|
138
170
|
input.search {
|
|
139
171
|
width: 180px;
|
|
140
|
-
|
|
141
|
-
@media (width <= 400px) {
|
|
142
|
-
display: none;
|
|
143
|
-
}
|
|
144
172
|
}
|
|
145
173
|
}
|
|
146
174
|
|
package/src/header/style.scss
CHANGED
|
@@ -50,10 +50,18 @@ header nav div > p {
|
|
|
50
50
|
@include user-select(none);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
:root[data-contrast-preference='high'] header:has(details[open]) {
|
|
54
|
+
background: var(--bg2-color);
|
|
55
|
+
}
|
|
56
|
+
|
|
53
57
|
@media (hover: hover) {
|
|
54
58
|
header:has(details[open]):hover {
|
|
55
59
|
background: var(--bg-color-translucent);
|
|
56
60
|
}
|
|
61
|
+
|
|
62
|
+
:root[data-contrast-preference='high'] header:hover {
|
|
63
|
+
background: var(--bg2-color);
|
|
64
|
+
}
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
header .results-container {
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import './style.scss';
|
|
2
2
|
|
|
3
|
+
import './material-symbols.scss';
|
|
3
4
|
import './anchor/style.scss';
|
|
4
5
|
import './code.scss';
|
|
5
6
|
import './code/style.scss';
|
|
@@ -12,6 +13,7 @@ import './slides/style.scss';
|
|
|
12
13
|
import './timezone/style.scss';
|
|
13
14
|
import './toc/style.scss';
|
|
14
15
|
import './trace/style.scss';
|
|
16
|
+
import './appearance-picker/style.scss';
|
|
15
17
|
import './navigate/style.scss';
|
|
16
18
|
import './page-update/style.scss';
|
|
17
19
|
|
|
@@ -6,6 +6,7 @@ import mountCode from '../code';
|
|
|
6
6
|
import mountTrace from '../trace';
|
|
7
7
|
import mountPrint from '../print';
|
|
8
8
|
import mountSlides from '../slides';
|
|
9
|
+
import mountAppearancePicker from '../appearance-picker';
|
|
9
10
|
import { scheduleTask } from '../util';
|
|
10
11
|
|
|
11
12
|
const PER_PAGE_COMPONENTS: Record<
|
|
@@ -20,6 +21,7 @@ const PER_PAGE_COMPONENTS: Record<
|
|
|
20
21
|
trace: mountTrace,
|
|
21
22
|
slides: mountSlides,
|
|
22
23
|
print: mountPrint,
|
|
24
|
+
appearancePicker: mountAppearancePicker,
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
let cleanups: (() => void)[] = [];
|
package/src/search/style.scss
CHANGED
package/src/slides/style.scss
CHANGED
|
@@ -94,6 +94,7 @@ body.is-presenting header,
|
|
|
94
94
|
body.is-presenting .title-and-info,
|
|
95
95
|
body.is-presenting nav.toc,
|
|
96
96
|
body.is-presenting footer,
|
|
97
|
+
body.is-presenting .appearance-pickers,
|
|
97
98
|
body.is-presenting .slides-header,
|
|
98
99
|
body.is-presenting .trace-toolbar {
|
|
99
100
|
display: none;
|