@rathnasgala/theme 0.0.17 → 0.0.18
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
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"themePackage": {
|
|
4
4
|
"name": "@rathnasgala/theme",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.18",
|
|
6
6
|
"availableDesignThemes": [
|
|
7
7
|
"editorial"
|
|
8
8
|
],
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"lib/render-markdown.js": "40e6d6743b76de6c16d138475f188a4263967530c370d8b26a363f4df58c2caf",
|
|
22
22
|
"lib/seo.js": "107de8804931945b8e73894877fc394d6b535a9d0d969a4ba5b68f2e136e0a17",
|
|
23
23
|
"lib/share-targets.js": "458aff86fab2b62a364442cd8c612565c6e0b53eef2f20cb6237cc562b1e0731",
|
|
24
|
-
"lib/site-config.js": "
|
|
24
|
+
"lib/site-config.js": "da7d2dffab62c7346c9930494b85fc6ea735d4f50565bbd0454a04393f8cad96",
|
|
25
25
|
"package-lock.json": "c97a6a2f794aef083a39a8d2628eccc4a3c484e1c6225899b77ec1fb7c466d9e",
|
|
26
26
|
"package.json": "4c60bea06f39aec755ac68caf8d8841c1d59d0f6121fad7f4396231ce14712dc",
|
|
27
27
|
"src/404.njk": "be0d44370ac5402156aaf3d32ea581057bc61ec94c1a5f92bb809733af7a8a20",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"src/_data/languages.js": "1358748f7fa2f74406d3295a030827d6229a53e94da72cf712e719d7a356aeac",
|
|
32
32
|
"src/_data/site.js": "d9c001f4cc12a34027dd92303530b11b5402d47613459bb997e22c4bcbea326d",
|
|
33
33
|
"src/_includes/components/ui.njk": "23eb647d8658868449ec6bb809e965a85302124a97639f94d5d28af72df56e4c",
|
|
34
|
-
"src/_includes/layouts/base.njk": "
|
|
34
|
+
"src/_includes/layouts/base.njk": "6914da799eb31fea3fc230ab196252129ccca90f27ccb08bbcee81741a91ced4",
|
|
35
35
|
"src/_includes/layouts/post.njk": "1accd2832064ad19ee7bf18daf5fa1e60569f32c9b7a547934a6361f747d7eb7",
|
|
36
36
|
"src/assets/embed-codepen.svg": "e9def697f38c466fb2b6652535c4c814e8cbbbefc6e052a69f335aeb27e8356b",
|
|
37
37
|
"src/assets/embed-gist.svg": "fcd9bc2bf29a5c118492b853c0a01390b7539c38ddb985da3c2a6e2f246a39f7",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"src/assets/interactions.js": "dd4e343505e528d89596ca67db679b52c989e022f93399193e4377db01fc8a08",
|
|
43
43
|
"src/assets/preferences.js": "789127bee013ed6a1bbf8cefd7b9109291c16220e7a6e09314e3c93c54731d1d",
|
|
44
44
|
"src/assets/search.js": "df425f0239277792ad39f2138bc89cb66cc4ecda5ed59042d46abc2f05372088",
|
|
45
|
-
"src/assets/theme-mode.js": "
|
|
46
|
-
"src/assets/theme.css": "
|
|
45
|
+
"src/assets/theme-mode.js": "944b29db11a577961a60ddcfa52f07ef2411f3f3631bea85bd79be16428c08be",
|
|
46
|
+
"src/assets/theme.css": "2a2367f2c875121cc95bc789a0d5889960813e660ca7722b328b39b4ff5068a8",
|
|
47
47
|
"src/assets/vendor/hooks.js": "ae6ec58592683ca64fa4d65b20d40b103df62a673bcc4712515cf165950b07f3",
|
|
48
48
|
"src/assets/vendor/htm.js": "e4dac9bc40a6f2ec590413d787ae30bbcfdfcde4f28971ee65d9578271b7aad4",
|
|
49
49
|
"src/assets/vendor/preact.js": "712517dd2a42c3c22b5ab0264ad7e9a2b1e8be6cf97fa00dbedd98203617e616",
|
|
@@ -7,19 +7,39 @@ const BUDGET_KEYS = Object.freeze([
|
|
|
7
7
|
'managedCssBytes',
|
|
8
8
|
'ordinaryHtmlBytes'
|
|
9
9
|
]);
|
|
10
|
+
/*
|
|
11
|
+
* Only what the theme actually implements. A value here is a promise that choosing it changes how
|
|
12
|
+
* a publication looks; anything the CSS does not answer to has no business being offered.
|
|
13
|
+
*/
|
|
10
14
|
const DESIGN_VALUES = Object.freeze({
|
|
11
15
|
layout: Object.freeze(['article-first', 'portfolio']),
|
|
12
|
-
palette: Object.freeze(['default', 'ocean'])
|
|
16
|
+
palette: Object.freeze(['default', 'ocean']),
|
|
17
|
+
typography: Object.freeze(['system', 'editorial', 'humanist', 'mono']),
|
|
18
|
+
density: Object.freeze(['compact', 'comfortable', 'spacious']),
|
|
19
|
+
spacing: Object.freeze(['compact', 'comfortable', 'spacious']),
|
|
20
|
+
radius: Object.freeze(['sharp', 'soft', 'round']),
|
|
21
|
+
motion: Object.freeze(['none', 'subtle', 'expressive']),
|
|
22
|
+
componentStyle: Object.freeze(['quiet', 'outlined', 'raised'])
|
|
13
23
|
});
|
|
14
24
|
const CONTACT_KEYS = Object.freeze([
|
|
15
25
|
'enabled', 'websiteEnabled', 'phoneEnabled'
|
|
16
26
|
]);
|
|
17
27
|
|
|
28
|
+
/*
|
|
29
|
+
* `layout` and `palette` have always been required. The rest were added later and are optional:
|
|
30
|
+
* a publication written before they existed is still a valid publication, and the theme falls
|
|
31
|
+
* back to its own defaults for anything absent. Present-but-unknown is still an error, because
|
|
32
|
+
* that is a writer expecting something the CSS will not do.
|
|
33
|
+
*/
|
|
34
|
+
const REQUIRED_DESIGN = Object.freeze(['layout', 'palette']);
|
|
35
|
+
|
|
18
36
|
function validateImplementedDesign(config) {
|
|
19
37
|
if (config?.design == null) return;
|
|
20
38
|
for (const [field, allowed] of Object.entries(DESIGN_VALUES)) {
|
|
21
|
-
|
|
22
|
-
|
|
39
|
+
const value = config.design[field];
|
|
40
|
+
if (value === undefined && !REQUIRED_DESIGN.includes(field)) continue;
|
|
41
|
+
if (!allowed.includes(value)) {
|
|
42
|
+
throw new TypeError(`Unsupported design.${field}: ${value}`);
|
|
23
43
|
}
|
|
24
44
|
}
|
|
25
45
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
|
-
<html lang="{{ post.language or site.site.defaultLanguage }}" data-theme="{{ site.design.theme }}" data-layout="{{ site.design.layout }}" data-palette="{{ site.design.palette }}" data-mode="system">
|
|
2
|
+
<html lang="{{ post.language or site.site.defaultLanguage }}" data-theme="{{ site.design.theme }}" data-layout="{{ site.design.layout }}" data-palette="{{ site.design.palette }}" data-typography="{{ site.design.typography }}" data-density="{{ site.design.density }}" data-spacing="{{ site.design.spacing }}" data-radius="{{ site.design.radius }}" data-motion="{{ site.design.motion }}" data-component-style="{{ site.design.componentStyle }}" data-mode="{{ site.design.colorMode | default('system') }}">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
const MODES = ['system', 'light', 'dark'];
|
|
2
2
|
const STORAGE_KEY = 'gala-color-mode';
|
|
3
3
|
|
|
4
|
+
/*
|
|
5
|
+
* The reader's own choice always wins. Absent one, the publication's `design.colorMode` decides
|
|
6
|
+
* what they see first — it is already on the element, server-rendered. This used to fall back to
|
|
7
|
+
* `system` unconditionally, which meant a writer could set a colour mode and no reader ever saw
|
|
8
|
+
* it: the setting existed everywhere except in the page.
|
|
9
|
+
*/
|
|
10
|
+
function publicationDefault() {
|
|
11
|
+
const declared = document.documentElement.dataset.mode;
|
|
12
|
+
return MODES.includes(declared) ? declared : 'system';
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
function storedMode() {
|
|
5
16
|
try {
|
|
6
17
|
const mode = localStorage.getItem(STORAGE_KEY);
|
|
7
|
-
|
|
18
|
+
if (MODES.includes(mode)) return mode;
|
|
8
19
|
} catch {
|
|
9
|
-
return
|
|
20
|
+
return publicationDefault();
|
|
10
21
|
}
|
|
22
|
+
return publicationDefault();
|
|
11
23
|
}
|
|
12
24
|
|
|
13
25
|
function applyMode(mode) {
|
|
@@ -1,57 +1,181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A publication should read like a well-set page, not like a product.
|
|
3
|
+
*
|
|
4
|
+
* The chrome is quiet, the type carries the hierarchy, and depth comes from surface and space
|
|
5
|
+
* rather than from an outline around everything. Every value here is a token so that the ten
|
|
6
|
+
* `design:` choices in site.config.yml can move the whole page by moving a handful of variables,
|
|
7
|
+
* instead of each component inventing its own spacing and its own grey.
|
|
8
|
+
*/
|
|
1
9
|
:root {
|
|
2
10
|
color-scheme: light dark;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
--gala-
|
|
6
|
-
--gala-
|
|
7
|
-
--gala-color-
|
|
8
|
-
--gala-color-
|
|
9
|
-
--gala-color-
|
|
10
|
-
--gala-
|
|
11
|
-
--gala-
|
|
11
|
+
|
|
12
|
+
/* Neutrals carry a slight warmth. A pure grey reads as unconsidered; these were picked. */
|
|
13
|
+
--gala-color-background: light-dark(#fdfcfb, #131211);
|
|
14
|
+
--gala-color-surface: light-dark(#f6f4f1, #1c1a18);
|
|
15
|
+
--gala-color-raised: light-dark(#fffffe, #24211f);
|
|
16
|
+
--gala-color-text: light-dark(#1a1817, #f2efec);
|
|
17
|
+
--gala-color-muted: light-dark(#6b645e, #a8a099);
|
|
18
|
+
--gala-color-border: light-dark(#968e86, #6b645e);
|
|
19
|
+
--gala-color-hairline: light-dark(#e8e3dd, #2a2724);
|
|
20
|
+
--gala-color-border-strong: light-dark(#c9c2ba, #46413d);
|
|
21
|
+
--gala-color-accent: light-dark(#8a4b2a, #e8a87c);
|
|
22
|
+
|
|
23
|
+
/* Type. One scale, used everywhere, fluid between a phone and a desk. */
|
|
24
|
+
--gala-font-body: ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
|
|
25
|
+
--gala-font-display: var(--gala-font-body);
|
|
26
|
+
--gala-font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;
|
|
27
|
+
--gala-text-xs: 0.78rem;
|
|
28
|
+
--gala-text-sm: 0.875rem;
|
|
29
|
+
--gala-text-base: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
|
|
30
|
+
--gala-text-lg: clamp(1.15rem, 1.09rem + 0.3vw, 1.3rem);
|
|
31
|
+
--gala-text-xl: clamp(1.4rem, 1.28rem + 0.6vw, 1.75rem);
|
|
32
|
+
--gala-text-2xl: clamp(1.75rem, 1.5rem + 1.2vw, 2.4rem);
|
|
33
|
+
--gala-text-3xl: clamp(2.2rem, 1.7rem + 2.4vw, 3.4rem);
|
|
34
|
+
--gala-leading-tight: 1.15;
|
|
35
|
+
--gala-leading-body: 1.65;
|
|
36
|
+
--gala-tracking-tight: -0.018em;
|
|
37
|
+
--gala-tracking-wide: 0.08em;
|
|
38
|
+
--gala-weight-display: 640;
|
|
39
|
+
|
|
40
|
+
/* Space, radius, depth, motion. */
|
|
41
|
+
--gala-step: 1rem;
|
|
42
|
+
--gala-space-2xs: calc(var(--gala-step) * 0.25);
|
|
43
|
+
--gala-space-xs: calc(var(--gala-step) * 0.5);
|
|
44
|
+
--gala-space-sm: calc(var(--gala-step) * 0.75);
|
|
45
|
+
--gala-space-md: var(--gala-step);
|
|
46
|
+
--gala-space-lg: calc(var(--gala-step) * 1.75);
|
|
47
|
+
--gala-space-xl: calc(var(--gala-step) * 3);
|
|
48
|
+
--gala-space-page: clamp(1.25rem, 5vw, 4.5rem);
|
|
49
|
+
--gala-radius-control: 0.625rem;
|
|
50
|
+
--gala-radius-surface: 1rem;
|
|
51
|
+
--gala-shadow-low: 0 1px 2px light-dark(rgb(26 24 23 / 6%), rgb(0 0 0 / 40%));
|
|
52
|
+
--gala-shadow-high:
|
|
53
|
+
0 1px 2px light-dark(rgb(26 24 23 / 5%), rgb(0 0 0 / 35%)),
|
|
54
|
+
0 12px 28px -12px light-dark(rgb(26 24 23 / 18%), rgb(0 0 0 / 60%));
|
|
55
|
+
--gala-motion-fast: 120ms;
|
|
56
|
+
--gala-motion-base: 220ms;
|
|
57
|
+
--gala-ease: cubic-bezier(0.2, 0, 0, 1);
|
|
58
|
+
|
|
59
|
+
--gala-content-width: 72rem;
|
|
60
|
+
--gala-measure: 68ch;
|
|
12
61
|
--gala-widget-min-block-size: 8rem;
|
|
13
62
|
}
|
|
14
63
|
|
|
64
|
+
/* An explicit choice must beat the operating system in both directions, so every colour is
|
|
65
|
+
restated rather than left to `light-dark()`. */
|
|
15
66
|
:root[data-mode='light'] {
|
|
16
67
|
color-scheme: light;
|
|
17
|
-
--gala-color-background: #
|
|
18
|
-
--gala-color-
|
|
19
|
-
--gala-color-
|
|
20
|
-
--gala-color-
|
|
21
|
-
--gala-color-
|
|
68
|
+
--gala-color-background: #fdfcfb;
|
|
69
|
+
--gala-color-surface: #f6f4f1;
|
|
70
|
+
--gala-color-raised: #fffffe;
|
|
71
|
+
--gala-color-text: #1a1817;
|
|
72
|
+
--gala-color-muted: #6b645e;
|
|
73
|
+
--gala-color-border: #968e86;
|
|
74
|
+
--gala-color-hairline: #e8e3dd;
|
|
75
|
+
--gala-color-border-strong: #c9c2ba;
|
|
76
|
+
--gala-color-accent: #8a4b2a;
|
|
22
77
|
}
|
|
23
|
-
|
|
24
78
|
:root[data-mode='dark'] {
|
|
25
79
|
color-scheme: dark;
|
|
26
|
-
--gala-color-background: #
|
|
27
|
-
--gala-color-
|
|
28
|
-
--gala-color-
|
|
29
|
-
--gala-color-
|
|
30
|
-
--gala-color-
|
|
80
|
+
--gala-color-background: #131211;
|
|
81
|
+
--gala-color-surface: #1c1a18;
|
|
82
|
+
--gala-color-raised: #24211f;
|
|
83
|
+
--gala-color-text: #f2efec;
|
|
84
|
+
--gala-color-muted: #a8a099;
|
|
85
|
+
--gala-color-border: #6b645e;
|
|
86
|
+
--gala-color-hairline: #2a2724;
|
|
87
|
+
--gala-color-border-strong: #46413d;
|
|
88
|
+
--gala-color-accent: #e8a87c;
|
|
31
89
|
}
|
|
32
90
|
|
|
91
|
+
/* ---------------------------------------------------------------- palettes */
|
|
33
92
|
:root[data-palette='ocean'] {
|
|
34
|
-
--gala-color-background: light-dark(#
|
|
35
|
-
--gala-color-
|
|
36
|
-
--gala-color-
|
|
37
|
-
--gala-color-
|
|
38
|
-
--gala-color-
|
|
93
|
+
--gala-color-background: light-dark(#f8fbfd, #0b1a24);
|
|
94
|
+
--gala-color-surface: light-dark(#eef4f9, #12252f);
|
|
95
|
+
--gala-color-raised: light-dark(#ffffff, #172d39);
|
|
96
|
+
--gala-color-text: light-dark(#12242f, #eaf3f8);
|
|
97
|
+
--gala-color-muted: light-dark(#5a6f7d, #98aebb);
|
|
98
|
+
--gala-color-border: light-dark(#7d8d99, #5f7d8c);
|
|
99
|
+
--gala-color-hairline: light-dark(#dde7ee, #1d3644);
|
|
100
|
+
--gala-color-border-strong: light-dark(#bdccd7, #33505f);
|
|
101
|
+
--gala-color-accent: light-dark(#0f5f7a, #7fd0e8);
|
|
39
102
|
}
|
|
40
|
-
|
|
41
103
|
:root[data-palette='ocean'][data-mode='light'] {
|
|
42
|
-
|
|
43
|
-
--gala-color-
|
|
44
|
-
--gala-color-surface: #
|
|
45
|
-
--gala-color-
|
|
46
|
-
--gala-color-
|
|
104
|
+
color-scheme: light;
|
|
105
|
+
--gala-color-background: #f8fbfd;
|
|
106
|
+
--gala-color-surface: #eef4f9;
|
|
107
|
+
--gala-color-raised: #ffffff;
|
|
108
|
+
--gala-color-text: #12242f;
|
|
109
|
+
--gala-color-muted: #5a6f7d;
|
|
110
|
+
--gala-color-border: #7d8d99;
|
|
111
|
+
--gala-color-hairline: #dde7ee;
|
|
112
|
+
--gala-color-border-strong: #bdccd7;
|
|
113
|
+
--gala-color-accent: #0f5f7a;
|
|
47
114
|
}
|
|
48
|
-
|
|
49
115
|
:root[data-palette='ocean'][data-mode='dark'] {
|
|
50
|
-
|
|
51
|
-
--gala-color-
|
|
52
|
-
--gala-color-surface: #
|
|
53
|
-
--gala-color-
|
|
54
|
-
--gala-color-
|
|
116
|
+
color-scheme: dark;
|
|
117
|
+
--gala-color-background: #0b1a24;
|
|
118
|
+
--gala-color-surface: #12252f;
|
|
119
|
+
--gala-color-raised: #172d39;
|
|
120
|
+
--gala-color-text: #eaf3f8;
|
|
121
|
+
--gala-color-muted: #98aebb;
|
|
122
|
+
--gala-color-border: #5f7d8c;
|
|
123
|
+
--gala-color-hairline: #1d3644;
|
|
124
|
+
--gala-color-border-strong: #33505f;
|
|
125
|
+
--gala-color-accent: #7fd0e8;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ---------------------------------------------------------------- typography
|
|
129
|
+
System stacks rather than a downloaded face: a publication must build and render with no call
|
|
130
|
+
to anyone, and a webfont is bytes on every reader's first paint. These are the good faces the
|
|
131
|
+
reader's machine already has. */
|
|
132
|
+
:root[data-typography='editorial'] {
|
|
133
|
+
--gala-font-body: ui-serif, "Iowan Old Style", Charter, "Source Serif 4", Georgia, serif;
|
|
134
|
+
--gala-font-display: var(--gala-font-body);
|
|
135
|
+
--gala-weight-display: 600;
|
|
136
|
+
}
|
|
137
|
+
:root[data-typography='humanist'] {
|
|
138
|
+
--gala-font-body: Optima, Candara, "Gill Sans", "Trebuchet MS", sans-serif;
|
|
139
|
+
--gala-font-display: var(--gala-font-body);
|
|
140
|
+
}
|
|
141
|
+
:root[data-typography='mono'] {
|
|
142
|
+
--gala-font-display: var(--gala-font-mono);
|
|
143
|
+
--gala-weight-display: 560;
|
|
144
|
+
--gala-tracking-tight: -0.005em;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* ---------------------------------------------------------------- density */
|
|
148
|
+
:root[data-density='compact'], :root[data-spacing='compact'] {
|
|
149
|
+
--gala-step: 0.8rem;
|
|
150
|
+
--gala-leading-body: 1.5;
|
|
151
|
+
}
|
|
152
|
+
:root[data-density='spacious'], :root[data-spacing='spacious'] {
|
|
153
|
+
--gala-step: 1.25rem;
|
|
154
|
+
--gala-leading-body: 1.75;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* ---------------------------------------------------------------- radius */
|
|
158
|
+
:root[data-radius='sharp'] { --gala-radius-control: 0; --gala-radius-surface: 0; }
|
|
159
|
+
:root[data-radius='round'] { --gala-radius-control: 999px; --gala-radius-surface: 1.5rem; }
|
|
160
|
+
|
|
161
|
+
/* ---------------------------------------------------------------- motion
|
|
162
|
+
`none` is honoured for everyone; a reader who asks the operating system for less motion is
|
|
163
|
+
honoured regardless of what the publication chose. */
|
|
164
|
+
:root[data-motion='none'] { --gala-motion-fast: 0ms; --gala-motion-base: 0ms; }
|
|
165
|
+
:root[data-motion='expressive'] { --gala-motion-base: 340ms; }
|
|
166
|
+
@media (prefers-reduced-motion: reduce) {
|
|
167
|
+
:root { --gala-motion-fast: 0ms; --gala-motion-base: 0ms; }
|
|
168
|
+
*, *::before, *::after { animation-duration: 1ms !important; transition-duration: 1ms !important; }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* ---------------------------------------------------------------- surfaces
|
|
172
|
+
`quiet` sits on the page, `outlined` draws its edge, `raised` lifts. One variable decides,
|
|
173
|
+
so every card, dialog and chip agrees. */
|
|
174
|
+
:root { --gala-surface-border: var(--gala-color-hairline); --gala-surface-shadow: var(--gala-shadow-low); }
|
|
175
|
+
:root[data-component-style='outlined'] { --gala-surface-border: var(--gala-color-border-strong); }
|
|
176
|
+
:root[data-component-style='raised'] {
|
|
177
|
+
--gala-surface-border: transparent;
|
|
178
|
+
--gala-surface-shadow: var(--gala-shadow-high);
|
|
55
179
|
}
|
|
56
180
|
|
|
57
181
|
:root[data-layout='portfolio'] { --gala-content-width: 82rem; }
|
|
@@ -62,6 +186,53 @@ body {
|
|
|
62
186
|
color: var(--gala-color-text);
|
|
63
187
|
background: var(--gala-color-background);
|
|
64
188
|
font-family: var(--gala-font-body);
|
|
189
|
+
font-size: var(--gala-text-base);
|
|
190
|
+
line-height: var(--gala-leading-body);
|
|
191
|
+
-webkit-font-smoothing: antialiased;
|
|
192
|
+
text-rendering: optimizeLegibility;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Headings carry the hierarchy, so they get the display face, a tighter measure and tighter
|
|
196
|
+
tracking — large type set at body tracking is the single clearest sign of an unconsidered page. */
|
|
197
|
+
:where(h1, h2, h3, h4) {
|
|
198
|
+
font-family: var(--gala-font-display);
|
|
199
|
+
font-weight: var(--gala-weight-display);
|
|
200
|
+
line-height: var(--gala-leading-tight);
|
|
201
|
+
letter-spacing: var(--gala-tracking-tight);
|
|
202
|
+
text-wrap: balance;
|
|
203
|
+
margin-block: 0 var(--gala-space-sm);
|
|
204
|
+
}
|
|
205
|
+
:where(h1) { font-size: var(--gala-text-3xl); }
|
|
206
|
+
:where(h2) { font-size: var(--gala-text-2xl); }
|
|
207
|
+
:where(h3) { font-size: var(--gala-text-xl); }
|
|
208
|
+
:where(h4) { font-size: var(--gala-text-lg); }
|
|
209
|
+
:where(p, ul, ol, blockquote) { margin-block: 0 var(--gala-space-md); }
|
|
210
|
+
:where(p) { text-wrap: pretty; }
|
|
211
|
+
|
|
212
|
+
blockquote {
|
|
213
|
+
margin-inline: 0;
|
|
214
|
+
padding-inline-start: var(--gala-space-md);
|
|
215
|
+
border-inline-start: 2px solid var(--gala-color-accent);
|
|
216
|
+
color: var(--gala-color-muted);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
:where(code, kbd, samp) { font-family: var(--gala-font-mono); font-size: 0.9em; }
|
|
220
|
+
:where(pre) {
|
|
221
|
+
padding: var(--gala-space-md);
|
|
222
|
+
overflow-x: auto;
|
|
223
|
+
background: var(--gala-color-surface);
|
|
224
|
+
border-radius: var(--gala-radius-control);
|
|
225
|
+
}
|
|
226
|
+
:where(hr) { border: 0; border-block-start: 1px solid var(--gala-color-hairline); margin-block: var(--gala-space-xl); }
|
|
227
|
+
:where(table) { border-collapse: collapse; inline-size: 100%; }
|
|
228
|
+
:where(th, td) { padding: var(--gala-space-xs) var(--gala-space-sm); border-block-end: 1px solid var(--gala-color-hairline); text-align: start; }
|
|
229
|
+
|
|
230
|
+
/* An uppercase label needs air between the letters or it sets as a solid block. */
|
|
231
|
+
.gala-eyebrow, .gala-comment__meta time, .gala-comment__edited {
|
|
232
|
+
font-size: var(--gala-text-xs);
|
|
233
|
+
letter-spacing: var(--gala-tracking-wide);
|
|
234
|
+
text-transform: uppercase;
|
|
235
|
+
color: var(--gala-color-muted);
|
|
65
236
|
}
|
|
66
237
|
|
|
67
238
|
.gala-site-header {
|
|
@@ -85,7 +256,12 @@ body {
|
|
|
85
256
|
border: 0.125rem solid transparent;
|
|
86
257
|
border-radius: 50%;
|
|
87
258
|
}
|
|
88
|
-
.gala-icon-control
|
|
259
|
+
.gala-icon-control {
|
|
260
|
+
transition: background var(--gala-motion-fast) var(--gala-ease),
|
|
261
|
+
color var(--gala-motion-fast) var(--gala-ease);
|
|
262
|
+
color: var(--gala-color-muted);
|
|
263
|
+
}
|
|
264
|
+
.gala-icon-control:hover { background: var(--gala-color-surface); color: var(--gala-color-text); }
|
|
89
265
|
.gala-icon-control:focus-visible { outline: 0.1875rem solid currentcolor; outline-offset: 0.1875rem; }
|
|
90
266
|
/* Stroked, not filled: the icons are drawn on one 24px grid at a single weight, so they read as
|
|
91
267
|
a set rather than as six unrelated silhouettes. */
|
|
@@ -98,8 +274,9 @@ body {
|
|
|
98
274
|
padding: 1.5rem;
|
|
99
275
|
color: var(--gala-color-text);
|
|
100
276
|
background: var(--gala-color-background);
|
|
101
|
-
border:
|
|
102
|
-
border-radius: var(--gala-radius-
|
|
277
|
+
border: 1px solid var(--gala-surface-border);
|
|
278
|
+
border-radius: var(--gala-radius-surface);
|
|
279
|
+
box-shadow: var(--gala-shadow-high);
|
|
103
280
|
}
|
|
104
281
|
.gala-dialog::backdrop { background: rgb(0 0 0 / 65%); }
|
|
105
282
|
.gala-dialog__close { float: inline-end; }
|
|
@@ -115,14 +292,19 @@ a { color: var(--gala-color-accent); }
|
|
|
115
292
|
|
|
116
293
|
button {
|
|
117
294
|
min-block-size: 2.75rem;
|
|
118
|
-
padding-inline:
|
|
295
|
+
padding-inline: var(--gala-space-md);
|
|
119
296
|
color: inherit;
|
|
120
|
-
background:
|
|
121
|
-
border:
|
|
297
|
+
background: var(--gala-color-surface);
|
|
298
|
+
border: 1px solid var(--gala-color-border);
|
|
122
299
|
border-radius: var(--gala-radius-control);
|
|
123
300
|
font: inherit;
|
|
301
|
+
font-size: var(--gala-text-sm);
|
|
124
302
|
cursor: pointer;
|
|
303
|
+
transition: background var(--gala-motion-fast) var(--gala-ease),
|
|
304
|
+
border-color var(--gala-motion-fast) var(--gala-ease);
|
|
125
305
|
}
|
|
306
|
+
button:hover { border-color: var(--gala-color-border-strong); background: var(--gala-color-raised); }
|
|
307
|
+
button[disabled] { opacity: 0.55; cursor: not-allowed; }
|
|
126
308
|
|
|
127
309
|
button:focus-visible {
|
|
128
310
|
outline: 0.1875rem solid currentcolor;
|
|
@@ -135,8 +317,9 @@ button:focus-visible {
|
|
|
135
317
|
display: inline-flex;
|
|
136
318
|
align-items: center;
|
|
137
319
|
min-block-size: 2.75rem;
|
|
138
|
-
border:
|
|
320
|
+
border: 1px solid var(--gala-color-border);
|
|
139
321
|
border-radius: var(--gala-radius-control);
|
|
322
|
+
font-size: var(--gala-text-sm);
|
|
140
323
|
}
|
|
141
324
|
|
|
142
325
|
.gala-badge,
|
|
@@ -146,7 +329,7 @@ button:focus-visible {
|
|
|
146
329
|
.gala-section-heading > :where(h2, p) { margin-block: 0 0.5rem; }
|
|
147
330
|
.gala-hero { display: grid; gap: 1.5rem; align-items: center; }
|
|
148
331
|
.gala-hero__image { inline-size: 100%; block-size: auto; border-radius: var(--gala-radius-control); }
|
|
149
|
-
.gala-page-content { max-inline-size:
|
|
332
|
+
.gala-page-content { max-inline-size: var(--gala-measure); }
|
|
150
333
|
.gala-page-content :where(img, video) { max-inline-size: 100%; block-size: auto; }
|
|
151
334
|
.gala-author-profile { display: flex; gap: 1rem; padding: 1rem; background: var(--gala-color-surface); border-radius: var(--gala-radius-control); }
|
|
152
335
|
.gala-author-profile img { border-radius: 50%; object-fit: cover; }
|
|
@@ -156,7 +339,25 @@ button:focus-visible {
|
|
|
156
339
|
.gala-card-index { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: 1rem; padding: 0; list-style: none; }
|
|
157
340
|
[data-layout='portfolio'] .gala-card-index { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); gap: 1.5rem; }
|
|
158
341
|
[data-layout='portfolio'] .gala-card { padding: clamp(1.25rem, 3vw, 2rem); }
|
|
159
|
-
|
|
342
|
+
/* The surfaces are where `componentStyle` becomes visible: quiet sits on the page, outlined draws
|
|
343
|
+
its edge, raised lifts. One variable decides, so cards and dialogs cannot drift apart. */
|
|
344
|
+
.gala-card {
|
|
345
|
+
block-size: 100%;
|
|
346
|
+
box-sizing: border-box;
|
|
347
|
+
padding: var(--gala-space-lg);
|
|
348
|
+
background: var(--gala-color-surface);
|
|
349
|
+
border: 1px solid var(--gala-surface-border);
|
|
350
|
+
border-radius: var(--gala-radius-surface);
|
|
351
|
+
box-shadow: var(--gala-surface-shadow);
|
|
352
|
+
}
|
|
353
|
+
[data-component-style='raised'] .gala-card { background: var(--gala-color-raised); }
|
|
354
|
+
/* The one place the page moves of its own accord, and so the one place `motion` is legible:
|
|
355
|
+
`subtle` is a short settle, `expressive` a longer one, `none` no movement at all. */
|
|
356
|
+
.gala-card { transition: transform var(--gala-motion-base) var(--gala-ease),
|
|
357
|
+
box-shadow var(--gala-motion-base) var(--gala-ease); }
|
|
358
|
+
.gala-card-index li:hover .gala-card { transform: translateY(-2px); box-shadow: var(--gala-shadow-high); }
|
|
359
|
+
[data-motion='none'] .gala-card { transition: none; }
|
|
360
|
+
[data-motion='none'] .gala-card-index li:hover .gala-card { transform: none; }
|
|
160
361
|
.gala-stats-graph { min-block-size: var(--gala-widget-min-block-size); margin-inline: 0; }
|
|
161
362
|
.gala-stats-graph svg { inline-size: 100%; block-size: 8rem; }
|
|
162
363
|
.gala-stats-graph polyline { fill: none; stroke: var(--gala-color-accent); stroke-width: 2; }
|
|
@@ -249,3 +450,75 @@ button:focus-visible {
|
|
|
249
450
|
@media (prefers-reduced-motion: no-preference) {
|
|
250
451
|
@view-transition { navigation: auto; }
|
|
251
452
|
}
|
|
453
|
+
|
|
454
|
+
/* ---------------------------------------------------------------- the conversation
|
|
455
|
+
A thread is a hierarchy, and the indent is the only thing that says so. It is drawn with a
|
|
456
|
+
rule rather than padding alone, so five levels deep still reads as five levels and not as an
|
|
457
|
+
accident of margin. */
|
|
458
|
+
.gala-comments-island { margin-block-start: var(--gala-space-xl); }
|
|
459
|
+
.gala-comments__heading {
|
|
460
|
+
font-size: var(--gala-text-lg);
|
|
461
|
+
margin-block-end: var(--gala-space-md);
|
|
462
|
+
}
|
|
463
|
+
.gala-comments, .gala-comment-replies { list-style: none; margin: 0; padding: 0; }
|
|
464
|
+
.gala-comment-replies {
|
|
465
|
+
margin-block-start: var(--gala-space-sm);
|
|
466
|
+
margin-inline-start: var(--gala-space-xs);
|
|
467
|
+
padding-inline-start: var(--gala-space-md);
|
|
468
|
+
border-inline-start: 1px solid var(--gala-color-hairline);
|
|
469
|
+
}
|
|
470
|
+
.gala-comment { padding-block: var(--gala-space-sm); }
|
|
471
|
+
.gala-comment + .gala-comment { border-block-start: 1px solid var(--gala-color-hairline); }
|
|
472
|
+
.gala-comment-replies .gala-comment + .gala-comment { border-block-start: 0; }
|
|
473
|
+
.gala-comment__meta {
|
|
474
|
+
display: flex;
|
|
475
|
+
align-items: baseline;
|
|
476
|
+
gap: var(--gala-space-xs);
|
|
477
|
+
margin-block-end: var(--gala-space-2xs);
|
|
478
|
+
font-size: var(--gala-text-sm);
|
|
479
|
+
}
|
|
480
|
+
.gala-comment__meta strong { font-weight: 600; }
|
|
481
|
+
.gala-comment__pending { font-size: var(--gala-text-xs); color: var(--gala-color-muted); }
|
|
482
|
+
.gala-comment__body { margin-block: 0; overflow-wrap: anywhere; }
|
|
483
|
+
|
|
484
|
+
/* The controls stay out of the way until the comment is worth acting on. */
|
|
485
|
+
.gala-comment-actions {
|
|
486
|
+
display: flex;
|
|
487
|
+
gap: var(--gala-space-2xs);
|
|
488
|
+
margin-block-start: var(--gala-space-2xs);
|
|
489
|
+
opacity: 0.72;
|
|
490
|
+
transition: opacity var(--gala-motion-fast) var(--gala-ease);
|
|
491
|
+
}
|
|
492
|
+
.gala-comment:hover > .gala-comment-actions,
|
|
493
|
+
.gala-comment:focus-within > .gala-comment-actions { opacity: 1; }
|
|
494
|
+
.gala-comment-actions button {
|
|
495
|
+
min-block-size: 2rem;
|
|
496
|
+
padding-inline: var(--gala-space-xs);
|
|
497
|
+
font-size: var(--gala-text-xs);
|
|
498
|
+
background: transparent;
|
|
499
|
+
border-color: transparent;
|
|
500
|
+
color: var(--gala-color-muted);
|
|
501
|
+
}
|
|
502
|
+
.gala-comment-actions button:hover { color: var(--gala-color-text); background: var(--gala-color-surface); }
|
|
503
|
+
|
|
504
|
+
.gala-comment__form { display: grid; gap: var(--gala-space-xs); margin-block: var(--gala-space-sm); }
|
|
505
|
+
.gala-comment__form textarea {
|
|
506
|
+
inline-size: 100%;
|
|
507
|
+
box-sizing: border-box;
|
|
508
|
+
padding: var(--gala-space-sm);
|
|
509
|
+
color: inherit;
|
|
510
|
+
background: var(--gala-color-raised);
|
|
511
|
+
border: 1px solid var(--gala-color-border);
|
|
512
|
+
border-radius: var(--gala-radius-control);
|
|
513
|
+
font: inherit;
|
|
514
|
+
font-size: var(--gala-text-sm);
|
|
515
|
+
line-height: var(--gala-leading-body);
|
|
516
|
+
resize: vertical;
|
|
517
|
+
}
|
|
518
|
+
.gala-comment__form textarea:focus-visible { outline: 2px solid var(--gala-color-accent); outline-offset: 1px; }
|
|
519
|
+
.gala-comment__controls { display: flex; gap: var(--gala-space-xs); }
|
|
520
|
+
.gala-comments__status, .gala-comments__empty, .gala-comments__prompt {
|
|
521
|
+
color: var(--gala-color-muted);
|
|
522
|
+
font-size: var(--gala-text-sm);
|
|
523
|
+
}
|
|
524
|
+
.gala-comments__more { margin-block-start: var(--gala-space-md); }
|