mdeck 3.6.2 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/assets/extensions/palettes/cobalt/extension.toml +2 -0
- package/assets/extensions/palettes/greyscale/extension.toml +4 -12
- package/assets/extensions/palettes/lagoon/extension.toml +2 -0
- package/assets/extensions/palettes/nordic/extension.toml +2 -0
- package/assets/extensions/palettes/paper/extension.toml +2 -0
- package/assets/extensions/palettes/swiss/extension.toml +2 -0
- package/assets/extensions/themes/academic/styles.css +2 -4
- package/assets/extensions/themes/glass/styles.css +2 -6
- package/assets/extensions/themes/minimal/styles.css +2 -5
- package/assets/extensions/themes/neue/styles.css +2 -5
- package/assets/extensions/themes/plain/styles.css +1 -2
- package/assets/extensions/themes/work/styles.css +2 -5
- package/docs/reference/authoring.md +1 -1
- package/docs/reference/extensions.md +2 -0
- package/docs/reference/palettes.md +7 -4
- package/docs/site/content/theme-authoring.md +6 -3
- package/package.json +1 -1
- package/skills/write-slides/SKILL.md +1 -1
- package/src/build/check.js +1 -0
- package/src/cli/themes.js +1 -1
- package/src/components/callout.css +11 -21
- package/src/editor/components/PalettePicker.jsx +1 -1
- package/src/editor/extensions.js +2 -2
- package/src/extensions/contrast.js +3 -1
- package/src/extensions/manifest.js +7 -3
- package/src/extensions/packages.js +3 -0
- package/src/extensions/tokens.js +10 -2
- package/src/live/client.js +1 -1
- package/src/runtime/chrome.jsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.7.0 — 2026-10-09
|
|
4
|
+
|
|
5
|
+
- Palettes have a third accent, `--accent-3`, and the three accents mean something: `--accent` is emphasis, `--accent-2` its companion (gradients, a contrast to the accent), `--accent-3` attention, used sparingly. `--accent-3` marks things and need not be readable as text, so it may be a yellow; `mdeck check` asks only that it can be seen (1.5:1 against the background). A palette without `--accent-3` keeps working and gets an amber (`#d99a00`, at night `#f5c542`); `mdeck check` suggests giving it its own. All built-in palettes and those in the theme repository set it (the repository requires it); `mdeck palettes update` brings installed ones up to date.
|
|
6
|
+
- Callouts take their colours from the palette: note, tip and important the accent, warning and caution the third accent. The five callout colours (`--callout-note` … `--callout-caution`) are gone, and with them the green, purple and blue that matched no palette. The built-in themes follow.
|
|
7
|
+
|
|
3
8
|
## 3.6.2 — 2026-10-09
|
|
4
9
|
|
|
5
10
|
- The docs home page and the README start with what mdeck does: drawing from an iPad, polls from phones, citations, themes, running code and sending one file, each with a picture (`npm run feature-images` renders them). The docs' example deck is now a short tour of mdeck, with its own control bar and drawing, and a picker of every built-in theme as a picture, with light or dark.
|
|
@@ -16,6 +16,7 @@ license = "MIT"
|
|
|
16
16
|
"--rule" = "#d5dbf0"
|
|
17
17
|
"--accent" = "#1f3fd1"
|
|
18
18
|
"--accent-2" = "#e8590c"
|
|
19
|
+
"--accent-3" = "#e8a400"
|
|
19
20
|
"--on-accent" = "#ffffff"
|
|
20
21
|
|
|
21
22
|
[dark]
|
|
@@ -27,4 +28,5 @@ license = "MIT"
|
|
|
27
28
|
"--rule" = "#212b5e"
|
|
28
29
|
"--accent" = "#6f8cff"
|
|
29
30
|
"--accent-2" = "#ff922b"
|
|
31
|
+
"--accent-3" = "#ffd43b"
|
|
30
32
|
"--on-accent" = "#0a1030"
|
|
@@ -8,8 +8,8 @@ author = "Tilman Schieber"
|
|
|
8
8
|
license = "MIT"
|
|
9
9
|
|
|
10
10
|
# No colour anywhere: emphasis is black against the softer body text, the
|
|
11
|
-
# second accent a mid grey
|
|
12
|
-
# usual colours.
|
|
11
|
+
# second accent a mid grey, the third (warnings) a lighter grey; code uses
|
|
12
|
+
# greys instead of its usual colours.
|
|
13
13
|
[light]
|
|
14
14
|
"--bg" = "#ffffff"
|
|
15
15
|
"--surface" = "#f0f0f0"
|
|
@@ -19,6 +19,7 @@ license = "MIT"
|
|
|
19
19
|
"--rule" = "#d4d4d4"
|
|
20
20
|
"--accent" = "#000000"
|
|
21
21
|
"--accent-2" = "#6e6e6e"
|
|
22
|
+
"--accent-3" = "#9a9a9a"
|
|
22
23
|
"--on-accent" = "#ffffff"
|
|
23
24
|
"--token-default" = "#141414"
|
|
24
25
|
"--token-comment" = "#6b6b6b"
|
|
@@ -27,11 +28,6 @@ license = "MIT"
|
|
|
27
28
|
"--token-keyword" = "#000000"
|
|
28
29
|
"--token-function" = "#2a2a2a"
|
|
29
30
|
"--token-operator" = "#555555"
|
|
30
|
-
"--callout-note" = "#4a4a4a"
|
|
31
|
-
"--callout-tip" = "#4a4a4a"
|
|
32
|
-
"--callout-important" = "#000000"
|
|
33
|
-
"--callout-warning" = "#7a7a7a"
|
|
34
|
-
"--callout-caution" = "#000000"
|
|
35
31
|
|
|
36
32
|
[dark]
|
|
37
33
|
"--bg" = "#121212"
|
|
@@ -42,6 +38,7 @@ license = "MIT"
|
|
|
42
38
|
"--rule" = "#303030"
|
|
43
39
|
"--accent" = "#ffffff"
|
|
44
40
|
"--accent-2" = "#a6a6a6"
|
|
41
|
+
"--accent-3" = "#6e6e6e"
|
|
45
42
|
"--on-accent" = "#121212"
|
|
46
43
|
"--token-default" = "#f2f2f2"
|
|
47
44
|
"--token-comment" = "#9a9a9a"
|
|
@@ -50,8 +47,3 @@ license = "MIT"
|
|
|
50
47
|
"--token-keyword" = "#ffffff"
|
|
51
48
|
"--token-function" = "#e0e0e0"
|
|
52
49
|
"--token-operator" = "#b0b0b0"
|
|
53
|
-
"--callout-note" = "#b8b8b8"
|
|
54
|
-
"--callout-tip" = "#b8b8b8"
|
|
55
|
-
"--callout-important" = "#ffffff"
|
|
56
|
-
"--callout-warning" = "#8c8c8c"
|
|
57
|
-
"--callout-caution" = "#ffffff"
|
|
@@ -16,6 +16,7 @@ license = "MIT"
|
|
|
16
16
|
"--rule" = "#e0deda"
|
|
17
17
|
"--accent" = "#0d9488"
|
|
18
18
|
"--accent-2" = "#c2410c"
|
|
19
|
+
"--accent-3" = "#ca8a04"
|
|
19
20
|
"--on-accent" = "#ffffff"
|
|
20
21
|
|
|
21
22
|
[dark]
|
|
@@ -27,4 +28,5 @@ license = "MIT"
|
|
|
27
28
|
"--rule" = "#23332f"
|
|
28
29
|
"--accent" = "#2dd4bf"
|
|
29
30
|
"--accent-2" = "#fb923c"
|
|
31
|
+
"--accent-3" = "#facc15"
|
|
30
32
|
"--on-accent" = "#062b26"
|
|
@@ -16,6 +16,7 @@ license = "MIT"
|
|
|
16
16
|
"--rule" = "#cdd8e1"
|
|
17
17
|
"--accent" = "#1f6f9f"
|
|
18
18
|
"--accent-2" = "#7a5c9e"
|
|
19
|
+
"--accent-3" = "#c08a1e"
|
|
19
20
|
"--on-accent" = "#ffffff"
|
|
20
21
|
|
|
21
22
|
[dark]
|
|
@@ -27,4 +28,5 @@ license = "MIT"
|
|
|
27
28
|
"--rule" = "#22343f"
|
|
28
29
|
"--accent" = "#7cc4ea"
|
|
29
30
|
"--accent-2" = "#c3a8e6"
|
|
31
|
+
"--accent-3" = "#e8c170"
|
|
30
32
|
"--on-accent" = "#0e1a24"
|
|
@@ -16,6 +16,7 @@ license = "MIT"
|
|
|
16
16
|
"--rule" = "#e0ddd6"
|
|
17
17
|
"--accent" = "#c2410c"
|
|
18
18
|
"--accent-2" = "#1f2937"
|
|
19
|
+
"--accent-3" = "#c99700"
|
|
19
20
|
"--on-accent" = "#ffffff"
|
|
20
21
|
|
|
21
22
|
[dark]
|
|
@@ -27,4 +28,5 @@ license = "MIT"
|
|
|
27
28
|
"--rule" = "#2e2e2c"
|
|
28
29
|
"--accent" = "#fb8c4a"
|
|
29
30
|
"--accent-2" = "#e7e5e0"
|
|
31
|
+
"--accent-3" = "#f2c94c"
|
|
30
32
|
"--on-accent" = "#151515"
|
|
@@ -16,6 +16,7 @@ license = "MIT"
|
|
|
16
16
|
"--rule" = "#dadada"
|
|
17
17
|
"--accent" = "#e30613"
|
|
18
18
|
"--accent-2" = "#0047bb"
|
|
19
|
+
"--accent-3" = "#f2b700"
|
|
19
20
|
"--on-accent" = "#ffffff"
|
|
20
21
|
|
|
21
22
|
[dark]
|
|
@@ -27,4 +28,5 @@ license = "MIT"
|
|
|
27
28
|
"--rule" = "#2e2e2e"
|
|
28
29
|
"--accent" = "#ff3b30"
|
|
29
30
|
"--accent-2" = "#6f9bff"
|
|
31
|
+
"--accent-3" = "#ffd60a"
|
|
30
32
|
"--on-accent" = "#0d0d0d"
|
|
@@ -476,11 +476,9 @@ strong { color: var(--ink); font-weight: 700; }
|
|
|
476
476
|
.slide-body .callout p { color: var(--ink); }
|
|
477
477
|
.slide-body .callout :is(ul, ol) { gap: 8px; }
|
|
478
478
|
|
|
479
|
-
.slide-body :is(.callout-theorem, .callout-lemma, .callout-corollary, .callout-proposition, .callout-tip, .callout-important) { --block: var(--accent); }
|
|
479
|
+
.slide-body :is(.callout-theorem, .callout-lemma, .callout-corollary, .callout-proposition, .callout-note, .callout-tip, .callout-important) { --block: var(--accent); }
|
|
480
480
|
.slide-body :is(.callout-definition) { --block: var(--accent-2); }
|
|
481
|
-
.slide-body .callout-
|
|
482
|
-
.slide-body .callout-warning { --block: var(--callout-warning); }
|
|
483
|
-
.slide-body .callout-caution { --block: var(--callout-caution); }
|
|
481
|
+
.slide-body :is(.callout-warning, .callout-caution) { --block: var(--accent-3); }
|
|
484
482
|
.slide-body :is(.callout-theorem, .callout-lemma, .callout-corollary, .callout-proposition) > :not(.callout-title) {
|
|
485
483
|
font-family: var(--font-serif);
|
|
486
484
|
font-style: italic;
|
|
@@ -640,13 +640,9 @@ deck-stage[data-deck-static] .slide { animation: none; }
|
|
|
640
640
|
box-shadow: 0 0 10px currentColor;
|
|
641
641
|
}
|
|
642
642
|
.slide-body .callout p { padding-left: 0; margin: 0; color: var(--ink); }
|
|
643
|
-
.slide-body .callout-note { --tint: var(--
|
|
644
|
-
.slide-body .callout-
|
|
645
|
-
.slide-body .callout-important { --tint: var(--callout-important); }
|
|
646
|
-
.slide-body .callout-warning { --tint: var(--callout-warning); }
|
|
647
|
-
.slide-body .callout-caution { --tint: var(--callout-caution); }
|
|
643
|
+
.slide-body :is(.callout-note, .callout-tip, .callout-important) { --tint: var(--accent); }
|
|
644
|
+
.slide-body :is(.callout-warning, .callout-caution) { --tint: var(--accent-3); }
|
|
648
645
|
.slide-body .callout-definition { --tint: var(--accent-2); }
|
|
649
|
-
.slide-body .callout-tip .callout-title { color: color-mix(in oklab, var(--callout-tip) 60%, var(--ink)); }
|
|
650
646
|
.slide-body :is(.callout-theorem, .callout-lemma, .callout-corollary) .callout-title { color: var(--em); }
|
|
651
647
|
.slide-body .callout-definition .callout-title { color: light-dark(color-mix(in oklab, var(--accent-2) 80%, var(--ink)), var(--accent-2)); }
|
|
652
648
|
|
|
@@ -297,11 +297,8 @@ strong { color: var(--ink); font-weight: 600; }
|
|
|
297
297
|
margin: 32px 0 0;
|
|
298
298
|
max-width: 1200px;
|
|
299
299
|
}
|
|
300
|
-
.slide-body .callout-tip { border-left-color: var(--accent); }
|
|
301
|
-
.slide-body .callout-
|
|
302
|
-
.slide-body .callout-important { border-left-color: var(--callout-important); }
|
|
303
|
-
.slide-body .callout-warning { border-left-color: var(--callout-warning); }
|
|
304
|
-
.slide-body .callout-caution { border-left-color: var(--callout-caution); }
|
|
300
|
+
.slide-body :is(.callout-note, .callout-tip, .callout-important) { border-left-color: var(--accent); }
|
|
301
|
+
.slide-body :is(.callout-warning, .callout-caution) { border-left-color: var(--accent-3); }
|
|
305
302
|
.slide-body .callout-title { margin-bottom: 8px; }
|
|
306
303
|
.slide-body .callout p { padding-left: 0; color: var(--ink); font-size: var(--fs-body); }
|
|
307
304
|
/* Lecture blocks: theorems in the accent, definitions in the second */
|
|
@@ -434,12 +434,9 @@ strong { color: var(--ink); font-weight: 600; }
|
|
|
434
434
|
margin: 40px 0 0;
|
|
435
435
|
max-width: 1200px;
|
|
436
436
|
}
|
|
437
|
-
.slide-body .callout-tip { border-top-color: var(--accent); }
|
|
437
|
+
.slide-body :is(.callout-note, .callout-tip, .callout-important) { border-top-color: var(--accent); }
|
|
438
438
|
.slide-body .callout-tip .callout-title { color: var(--accent); }
|
|
439
|
-
.slide-body .callout-
|
|
440
|
-
.slide-body .callout-important { border-top-color: var(--callout-important); }
|
|
441
|
-
.slide-body .callout-warning { border-top-color: var(--callout-warning); }
|
|
442
|
-
.slide-body .callout-caution { border-top-color: var(--callout-caution); }
|
|
439
|
+
.slide-body :is(.callout-warning, .callout-caution) { border-top-color: var(--accent-3); }
|
|
443
440
|
.slide-body .callout-title { letter-spacing: 0.12em; margin-bottom: 10px; }
|
|
444
441
|
.slide-body .callout p { padding-left: 0; color: var(--ink); font-size: var(--fs-body); }
|
|
445
442
|
/* Lecture blocks: theorems under a rule in the accent, definitions in the second */
|
|
@@ -418,8 +418,7 @@ strong { font-weight: 600; color: var(--ink); }
|
|
|
418
418
|
}
|
|
419
419
|
.slide-body .callout p { padding-left: 0; margin: 0; color: var(--ink-soft); }
|
|
420
420
|
.slide-body .callout :is(.callout-title) { color: var(--ink); }
|
|
421
|
-
.slide-body .callout:is(.callout-warning, .callout-caution) { border-left-color: var(--
|
|
422
|
-
.slide-body .callout.callout-caution { border-left-color: var(--callout-caution); }
|
|
421
|
+
.slide-body .callout:is(.callout-warning, .callout-caution) { border-left-color: var(--accent-3); }
|
|
423
422
|
.slide-body .callout.callout-proof { background: none; border: 0; padding: 0; }
|
|
424
423
|
|
|
425
424
|
/* ─────────────────────────────────────────────
|
|
@@ -448,11 +448,8 @@ strong { font-weight: 600; color: var(--ink); }
|
|
|
448
448
|
margin: 0 0 8px 0;
|
|
449
449
|
}
|
|
450
450
|
.slide-body .callout p { padding-left: 0; margin: 0; color: var(--ink); }
|
|
451
|
-
.slide-body .callout-note { --bar: var(--
|
|
452
|
-
.slide-body .callout-
|
|
453
|
-
.slide-body .callout-important { --bar: var(--callout-important); }
|
|
454
|
-
.slide-body .callout-warning { --bar: var(--callout-warning); }
|
|
455
|
-
.slide-body .callout-caution { --bar: var(--callout-caution); }
|
|
451
|
+
.slide-body :is(.callout-note, .callout-tip, .callout-important) { --bar: var(--accent); }
|
|
452
|
+
.slide-body :is(.callout-warning, .callout-caution) { --bar: var(--accent-3); }
|
|
456
453
|
.slide-body .callout-definition { --bar: var(--accent-2); }
|
|
457
454
|
|
|
458
455
|
/* ─────────────────────────────────────────────
|
|
@@ -686,7 +686,7 @@ A local SVG whose source contains `var(--…)` is drawn inline instead of as an
|
|
|
686
686
|
layout's `image` (`title`, `chapter`, `image-text`, `full-bleed-image`, and
|
|
687
687
|
local layouts that render it with `Picture` from `mdeck/layout`). It can use the
|
|
688
688
|
palette's custom properties (`--bg`, `--surface`, `--ink`, `--ink-soft`,
|
|
689
|
-
`--muted`, `--rule`, `--accent`, `--accent-2`, `--on-accent`) and follows the
|
|
689
|
+
`--muted`, `--rule`, `--accent`, `--accent-2`, `--accent-3`, `--on-accent`) and follows the
|
|
690
690
|
theme, palette and appearance. Give each a fallback (`var(--accent, #1f3fd1)`)
|
|
691
691
|
for places that show the file on its own, set `fit: contain` to keep the whole
|
|
692
692
|
drawing, prefix class names in its `<style>` because inline styles apply to the
|
|
@@ -83,6 +83,7 @@ description = "Deep blue with a warm highlight."
|
|
|
83
83
|
"--rule" = "#c9d7e2"
|
|
84
84
|
"--accent" = "#0a6aa8"
|
|
85
85
|
"--accent-2" = "#b45309"
|
|
86
|
+
"--accent-3" = "#c99700"
|
|
86
87
|
"--on-accent" = "#ffffff"
|
|
87
88
|
|
|
88
89
|
[dark]
|
|
@@ -94,6 +95,7 @@ description = "Deep blue with a warm highlight."
|
|
|
94
95
|
"--rule" = "#24384b"
|
|
95
96
|
"--accent" = "#ffbd69"
|
|
96
97
|
"--accent-2" = "#7cc4ff"
|
|
98
|
+
"--accent-3" = "#f2c94c"
|
|
97
99
|
"--on-accent" = "#102030"
|
|
98
100
|
```
|
|
99
101
|
|
|
@@ -23,7 +23,7 @@ The presenter view and the reader view switch palette and light or dark without
|
|
|
23
23
|
| `swiss` | white, black and signal red | black, white and red |
|
|
24
24
|
| `nordic` | ice grey, deep navy and a cool steel blue | polar night with ice blue |
|
|
25
25
|
| `cobalt` | strong ultramarine on white, vivid orange | midnight blue, bright blue and orange |
|
|
26
|
-
| `greyscale` | black, white and greys only, code
|
|
26
|
+
| `greyscale` | black, white and greys only, code too | the same, inverted |
|
|
27
27
|
| `lagoon` | teal on warm stone, orange second accent | deep teal-black, bright teal |
|
|
28
28
|
| `paper` | off-white paper and near-black ink with one orange accent | charcoal with a softer orange |
|
|
29
29
|
|
|
@@ -35,7 +35,7 @@ More palettes are in the [theme repository](https://github.com/tilman-schieber/m
|
|
|
35
35
|
|
|
36
36
|
## Colours
|
|
37
37
|
|
|
38
|
-
Every variant sets all
|
|
38
|
+
Every variant sets all ten (a palette without `--accent-3` gets an amber, and `mdeck check` suggests setting one):
|
|
39
39
|
|
|
40
40
|
| Colour | Role |
|
|
41
41
|
|---|---|
|
|
@@ -45,8 +45,9 @@ Every variant sets all nine:
|
|
|
45
45
|
| `--ink-soft` | Body text |
|
|
46
46
|
| `--muted` | Small labels: headers, footers, captions |
|
|
47
47
|
| `--rule` | Lines and dividers |
|
|
48
|
-
| `--accent` |
|
|
49
|
-
| `--accent-2` |
|
|
48
|
+
| `--accent` | Emphasis: highlighted words, markers, chapter slides; notes, tips and important callouts |
|
|
49
|
+
| `--accent-2` | The accent's companion: gradients, a contrast to the accent, definitions |
|
|
50
|
+
| `--accent-3` | Attention: warnings and cautions, and a colour that pops, used sparingly. It marks things (bars, shapes) and need not be readable as text, so it may be a yellow |
|
|
50
51
|
| `--on-accent` | Text set on the accent, such as a chapter slide's title |
|
|
51
52
|
|
|
52
53
|
Themes may mix them (for example a light tint of the accent), and read the other variant as `--inverse-bg`, `--inverse-ink`, `--inverse-accent` and so on.
|
|
@@ -71,6 +72,7 @@ description = "Sea blue and rust on chalk white."
|
|
|
71
72
|
"--rule" = "#c9d7e2"
|
|
72
73
|
"--accent" = "#0a6aa8"
|
|
73
74
|
"--accent-2" = "#b45309"
|
|
75
|
+
"--accent-3" = "#c99700"
|
|
74
76
|
"--on-accent" = "#ffffff"
|
|
75
77
|
|
|
76
78
|
[dark]
|
|
@@ -82,6 +84,7 @@ description = "Sea blue and rust on chalk white."
|
|
|
82
84
|
"--rule" = "#24384b"
|
|
83
85
|
"--accent" = "#5fb3f0"
|
|
84
86
|
"--accent-2" = "#f59e5b"
|
|
87
|
+
"--accent-3" = "#f2c94c"
|
|
85
88
|
"--on-accent" = "#0d1a24"
|
|
86
89
|
```
|
|
87
90
|
|
|
@@ -48,6 +48,7 @@ title = "Blue notebook"
|
|
|
48
48
|
"--rule" = "#dbe3ee"
|
|
49
49
|
"--accent" = "#2455c7"
|
|
50
50
|
"--accent-2" = "#17625c"
|
|
51
|
+
"--accent-3" = "#c99700"
|
|
51
52
|
"--on-accent" = "#ffffff"
|
|
52
53
|
|
|
53
54
|
[dark]
|
|
@@ -59,6 +60,7 @@ title = "Blue notebook"
|
|
|
59
60
|
"--rule" = "#24354d"
|
|
60
61
|
"--accent" = "#7aa2ff"
|
|
61
62
|
"--accent-2" = "#5ec4b6"
|
|
63
|
+
"--accent-3" = "#f2c94c"
|
|
62
64
|
"--on-accent" = "#0f1a2b"
|
|
63
65
|
```
|
|
64
66
|
|
|
@@ -76,11 +78,12 @@ The folder must be called `notebook` to match `id`. Use it with `palette: notebo
|
|
|
76
78
|
| `--ink-soft` | Body text |
|
|
77
79
|
| `--muted` | Secondary labels |
|
|
78
80
|
| `--rule` | Borders |
|
|
79
|
-
| `--accent` |
|
|
80
|
-
| `--accent-2` |
|
|
81
|
+
| `--accent` | Emphasis; notes, tips and important callouts |
|
|
82
|
+
| `--accent-2` | The accent's companion: gradients, a contrast to the accent |
|
|
83
|
+
| `--accent-3` | Attention: warnings and cautions. Use it sparingly, for bars and shapes, never as text: it may be a yellow |
|
|
81
84
|
| `--on-accent` | Text placed on an accent background |
|
|
82
85
|
|
|
83
|
-
A theme has no colours of its own: it uses these, and mixes of them, so every palette repaints it. For an inverted slide it uses the palette's other version, as `--inverse-bg`, `--inverse-ink`, `--inverse-accent` and so on:
|
|
86
|
+
A theme has no colours of its own: it uses these, and mixes of them, so every palette repaints it. Give each accent the job its name says: a theme that colours callouts colours note, tip and important with `--accent` and warning and caution with `--accent-3`. For an inverted slide it uses the palette's other version, as `--inverse-bg`, `--inverse-ink`, `--inverse-accent` and so on:
|
|
84
87
|
|
|
85
88
|
```css
|
|
86
89
|
.slide--focus {
|
package/package.json
CHANGED
|
@@ -142,7 +142,7 @@ To see slides yourself, if you can read pictures, run `mdeck snapshot <deck>.md
|
|
|
142
142
|
|
|
143
143
|
When the user wants a look the built-in themes and palettes do not give (their institute's colours, a font, the style of a poster or picture), first run `mdeck themes search` or `mdeck palettes search` (with a word or two): someone may have shared it, and `mdeck themes install <theme> <deck>.md` (with its palette) or `mdeck palettes install <palette> <deck>.md` puts it beside the deck, so the slide folder carries it. Otherwise make it as an extension in `extensions/` beside the deck, never as styling on single slides. Read `extensions.md` from the reference folder first; it lists every setting.
|
|
144
144
|
|
|
145
|
-
- **Colours only: a palette.** `extensions/<id>/extension.toml` with `kind = "palette"`, and every colour role (`--bg`, `--surface`, `--ink`, `--ink-soft`, `--muted`, `--rule`, `--accent`, `--accent-2`, `--on-accent`) in both `[light]` and `[dark]
|
|
145
|
+
- **Colours only: a palette.** `extensions/<id>/extension.toml` with `kind = "palette"`, and every colour role (`--bg`, `--surface`, `--ink`, `--ink-soft`, `--muted`, `--rule`, `--accent`, `--accent-2`, `--accent-3`, `--on-accent`) in both `[light]` and `[dark]`: `--accent` is emphasis, `--accent-2` its companion, `--accent-3` attention (warnings; it may be too light for text, such as a yellow). Set all of them. Text colours must stay readable on `--bg` and `--surface` in both variants, because themes use the other variant for inverted slides.
|
|
146
146
|
- **Fonts, sizes, spacing or arrangement: a theme.** Copy the folder of the closest built-in theme (`assets/extensions/themes/<id>/` in the mdeck repository or in `$(npm root -g)/mdeck/`) to `extensions/<new-id>/`, change `id` and `title`, then its tokens and `styles.css`. A theme takes no colours of its own: it names its default `palette`, and the colours come from palettes.
|
|
147
147
|
- **Use it:** set `theme:` and `palette:` in the deck settings. `mdeck list <deck>.md` must show it as local, and `mdeck check <deck>.md` reports any mistake in its `extension.toml`.
|
|
148
148
|
- **Hand over:** tell the user to run `mdeck design <deck>.md` (or `mdeck design <folder>` when there is no deck yet). It shows the look on a sample deck with every kind of slide, light and dark, and lets them fine-tune colours, fonts and sizes there.
|
package/src/build/check.js
CHANGED
|
@@ -26,6 +26,7 @@ export function checkDeck(slidesPath, registry, source = readFileSync(slidesPath
|
|
|
26
26
|
for (const record of Object.values(registry.palettes)) {
|
|
27
27
|
if (record.source !== 'local') continue
|
|
28
28
|
for (const problem of paletteProblems(record.manifest)) diagnostics.push({ severity: 'warning', code: 'palette-contrast', message: `Palette "${record.id}", ${problem}`, line: 1, column: 1 })
|
|
29
|
+
for (const role of record.manifest.fallbacks ?? []) diagnostics.push({ severity: 'warning', code: 'palette-fallback', message: `Palette "${record.id}" sets no ${role}, so it gets mdeck's amber; give it a colour of its own in [light] and [dark]`, line: 1, column: 1 })
|
|
29
30
|
}
|
|
30
31
|
// The deck's drawings file: readable, and only for slides that still exist.
|
|
31
32
|
const inkPath = inkFileFor(resolve(slidesPath))
|
package/src/cli/themes.js
CHANGED
|
@@ -27,7 +27,7 @@ function looksOf(registry, catalogue) {
|
|
|
27
27
|
const palettes = manifestsOf(registry, 'palette')
|
|
28
28
|
// Each theme's icon for the gallery's list (see renderPreviews).
|
|
29
29
|
const icons = new Map(catalogue.themes.map(entry => [entry.id, entry.icon]))
|
|
30
|
-
const colours = tokens => Object.fromEntries(['--bg', '--surface', '--ink', '--accent', '--accent-2'].map(key => [key, tokens?.[key] ?? null]))
|
|
30
|
+
const colours = tokens => Object.fromEntries(['--bg', '--surface', '--ink', '--accent', '--accent-2', '--accent-3'].map(key => [key, tokens?.[key] ?? null]))
|
|
31
31
|
return {
|
|
32
32
|
mdeck: VERSION,
|
|
33
33
|
themes: Object.values(manifestsOf(registry, 'theme')).map(theme => ({ id: theme.id, title: theme.title, description: theme.description ?? '', builtIn: builtIn.has(theme.id + ':t'),
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
--callout-important: #8250df;
|
|
5
|
-
--callout-warning: #eab308;
|
|
6
|
-
--callout-caution: #cf222e;
|
|
7
|
-
}
|
|
8
|
-
|
|
1
|
+
/* Callouts take the palette's accents by what they mean: a note, a tip and
|
|
2
|
+
something important are emphasis, in the accent; a warning and a caution
|
|
3
|
+
call for attention, in the third accent. */
|
|
9
4
|
.callout {
|
|
10
5
|
border-left: 4px solid var(--rule);
|
|
11
6
|
padding: 20px 28px;
|
|
@@ -21,20 +16,15 @@
|
|
|
21
16
|
}
|
|
22
17
|
.callout p { margin: 0; color: var(--ink-soft); padding-left: 28px; }
|
|
23
18
|
|
|
24
|
-
.callout-note
|
|
25
|
-
.callout-
|
|
26
|
-
.callout-important { border-left-color: var(--callout-important); }
|
|
27
|
-
.callout-warning { border-left-color: var(--callout-warning); }
|
|
28
|
-
.callout-caution { border-left-color: var(--callout-caution); }
|
|
19
|
+
.callout:is(.callout-note, .callout-tip, .callout-important) { border-left-color: var(--accent); }
|
|
20
|
+
.callout:is(.callout-warning, .callout-caution) { border-left-color: var(--accent-3); }
|
|
29
21
|
|
|
30
|
-
/* Titles are text: the
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.callout-tip
|
|
35
|
-
.callout-
|
|
36
|
-
.callout-warning .callout-title { color: color-mix(in oklab, var(--callout-warning) 45%, var(--ink)); }
|
|
37
|
-
.callout-caution .callout-title { color: color-mix(in oklab, var(--callout-caution) 65%, var(--ink)); }
|
|
22
|
+
/* Titles are text: the accent mixed with the text colour, darker on light
|
|
23
|
+
slides and lighter on dark ones, so they can be read on both. The third
|
|
24
|
+
accent is a marker colour that need not be readable as text (a yellow, say),
|
|
25
|
+
so it gets more of the text colour. */
|
|
26
|
+
.callout:is(.callout-note, .callout-tip, .callout-important) .callout-title { color: color-mix(in oklab, var(--accent) 65%, var(--ink)); }
|
|
27
|
+
.callout:is(.callout-warning, .callout-caution) .callout-title { color: color-mix(in oklab, var(--accent-3) 45%, var(--ink)); }
|
|
38
28
|
|
|
39
29
|
/* Blocks for lectures, in every theme. Theorems, lemmas and corollaries in
|
|
40
30
|
the accent with the statement in italics, definitions in the second
|
|
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'preact/hooks'
|
|
|
3
3
|
import { Icon } from '../../components/Icon.jsx'
|
|
4
4
|
|
|
5
5
|
export function Swatches({ tokens = {} }) {
|
|
6
|
-
return <span class="swatches">{['--bg', '--surface', '--rule', '--accent', '--accent-2', '--ink'].map(key => tokens[key] ? <i key={key} style={{ background: tokens[key] }} /> : null)}</span>
|
|
6
|
+
return <span class="swatches">{['--bg', '--surface', '--rule', '--accent', '--accent-2', '--accent-3', '--ink'].map(key => tokens[key] ? <i key={key} style={{ background: tokens[key] }} /> : null)}</span>
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
// The palettes a theme offers, each with its colours in the light or dark
|
package/src/editor/extensions.js
CHANGED
|
@@ -7,8 +7,8 @@ export const ID_RE = /^[a-z][a-z0-9-]*$/
|
|
|
7
7
|
export { COLOR_ROLES as CORE_TOKENS } from '../extensions/tokens.js'
|
|
8
8
|
// Readable colours a new palette starts from.
|
|
9
9
|
const STARTER_COLORS = {
|
|
10
|
-
light: { '--bg': '#ffffff', '--surface': '#f2f2f2', '--ink': '#111111', '--ink-soft': '#333333', '--muted': '#666666', '--rule': '#dadada', '--accent': '#0b6bcb', '--accent-2': '#c2410c', '--on-accent': '#ffffff' },
|
|
11
|
-
dark: { '--bg': '#111111', '--surface': '#1e1e1e', '--ink': '#f2f2f2', '--ink-soft': '#cfcfcf', '--muted': '#8f8f8f', '--rule': '#2e2e2e', '--accent': '#6aaeff', '--accent-2': '#fb923c', '--on-accent': '#111111' },
|
|
10
|
+
light: { '--bg': '#ffffff', '--surface': '#f2f2f2', '--ink': '#111111', '--ink-soft': '#333333', '--muted': '#666666', '--rule': '#dadada', '--accent': '#0b6bcb', '--accent-2': '#c2410c', '--accent-3': '#e0a100', '--on-accent': '#ffffff' },
|
|
11
|
+
dark: { '--bg': '#111111', '--surface': '#1e1e1e', '--ink': '#f2f2f2', '--ink-soft': '#cfcfcf', '--muted': '#8f8f8f', '--rule': '#2e2e2e', '--accent': '#6aaeff', '--accent-2': '#fb923c', '--accent-3': '#f5c542', '--on-accent': '#111111' },
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const entries = table => Object.entries(table ?? {}).map(([name, value]) => ({ name, ...(value && typeof value === 'object' && !Array.isArray(value) ? value : { value }) }))
|
|
@@ -35,6 +35,8 @@ const RULES = [
|
|
|
35
35
|
['--muted', '--bg', 4.5, 'muted text'],
|
|
36
36
|
['--accent', '--bg', 3, 'the accent'],
|
|
37
37
|
['--accent-2', '--bg', 3, 'the second accent'],
|
|
38
|
+
// A marker (bars, shapes), not text: it has to be seen, not read.
|
|
39
|
+
['--accent-3', '--bg', 1.5, 'the third accent'],
|
|
38
40
|
['--on-accent', '--accent', 3, 'text on the accent'],
|
|
39
41
|
]
|
|
40
42
|
|
|
@@ -48,7 +50,7 @@ export function paletteProblems(palette) {
|
|
|
48
50
|
for (const appearance of ['light', 'dark']) {
|
|
49
51
|
const tokens = palette[appearance] ?? {}
|
|
50
52
|
for (const [fore, back, minimum, what] of RULES) {
|
|
51
|
-
if (palette.theme && (fore === '--accent' || fore === '--accent-2')) continue
|
|
53
|
+
if (palette.theme && (fore === '--accent' || fore === '--accent-2' || fore === '--accent-3')) continue
|
|
52
54
|
const ratio = contrast(tokens[fore], tokens[back])
|
|
53
55
|
if (ratio != null && ratio < minimum) problems.push(`${appearance}: ${what} (${fore} on ${back}) has a contrast of ${ratio.toFixed(1)}:1; at least ${minimum}:1 is needed`)
|
|
54
56
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { existsSync } from 'node:fs'
|
|
5
5
|
import { resolve, relative, isAbsolute, sep } from 'node:path'
|
|
6
6
|
import { parse as parseToml, TomlError } from 'smol-toml'
|
|
7
|
-
import { TOKEN_NAME_RE, COLOR_ROLES, APPEARANCES } from './tokens.js'
|
|
7
|
+
import { TOKEN_NAME_RE, COLOR_ROLES, COLOR_FALLBACKS, APPEARANCES } from './tokens.js'
|
|
8
8
|
import { propertyErrors } from '../layouts/layoutProps.js'
|
|
9
9
|
|
|
10
10
|
export const SCHEMA_VERSION = 1
|
|
@@ -247,12 +247,16 @@ function validateGuide(table, fail) {
|
|
|
247
247
|
function validatePalette(raw, shared, fail) {
|
|
248
248
|
if (raw.theme != null && (typeof raw.theme !== 'string' || !ID_RE.test(raw.theme))) fail('theme must be the id of the only theme that may use this palette', 'theme')
|
|
249
249
|
const variants = {}
|
|
250
|
+
const fallbacks = new Set()
|
|
250
251
|
for (const appearance of APPEARANCES) {
|
|
251
252
|
if (!isPlainObject(raw[appearance])) fail(`a [${appearance}] table with the ${appearance} colours is required`, appearance)
|
|
252
253
|
const tokens = validateTokens(raw[appearance], appearance, fail, { required: true })
|
|
253
|
-
const missing = COLOR_ROLES.filter(role => !Object.hasOwn(tokens, role))
|
|
254
|
+
const missing = COLOR_ROLES.filter(role => !Object.hasOwn(tokens, role) && !COLOR_FALLBACKS[role])
|
|
254
255
|
if (missing.length) fail(`missing colours: ${missing.join(', ')}`, appearance)
|
|
256
|
+
for (const [role, values] of Object.entries(COLOR_FALLBACKS)) {
|
|
257
|
+
if (!Object.hasOwn(tokens, role)) { tokens[role] = values[appearance]; fallbacks.add(role) }
|
|
258
|
+
}
|
|
255
259
|
variants[appearance] = tokens
|
|
256
260
|
}
|
|
257
|
-
return { ...shared, ...(raw.theme ? { theme: raw.theme } : {}), light: variants.light, dark: variants.dark }
|
|
261
|
+
return { ...shared, ...(raw.theme ? { theme: raw.theme } : {}), light: variants.light, dark: variants.dark, ...(fallbacks.size ? { fallbacks: [...fallbacks] } : {}) }
|
|
258
262
|
}
|
|
@@ -74,6 +74,9 @@ export function checkPackage({ kind, id, files }, { published = false } = {}) {
|
|
|
74
74
|
}
|
|
75
75
|
if (record.kind !== kind) fail(`${id}/extension.toml says kind = "${record.kind}", but it is in the ${kind}s`)
|
|
76
76
|
if (published) for (const key of ['version', 'author', 'license']) if (!record.package?.[key]) fail(`The ${kind} ${id}: ${key} is required in the theme repository`)
|
|
77
|
+
// Palettes made before a colour existed get mdeck's fallback; a new one in
|
|
78
|
+
// the repository sets every colour itself.
|
|
79
|
+
if (published && record.manifest.fallbacks?.length) fail(`The palette ${id}: ${record.manifest.fallbacks.join(', ')} is required in the theme repository`)
|
|
77
80
|
if (kind === 'theme') {
|
|
78
81
|
for (const url of record.manifest.fonts) if (!FONT_HOSTS.some(host => url.startsWith(host))) fail(`The theme ${id} loads fonts from ${url}; allowed are ${FONT_HOSTS.join(', ')}`)
|
|
79
82
|
checkCss(files['styles.css'] ?? '', `The theme ${id}: styles.css`)
|
package/src/extensions/tokens.js
CHANGED
|
@@ -9,8 +9,16 @@ export function tokensToCss(tokens = {}, selector = ':root') {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// The colours every palette variant defines. Themes use only these (and
|
|
12
|
-
// mixes of them), so any palette fits any theme.
|
|
13
|
-
|
|
12
|
+
// mixes of them), so any palette fits any theme. The three accents mean
|
|
13
|
+
// something: --accent is emphasis (and notes, tips, important callouts),
|
|
14
|
+
// --accent-2 its companion (gradients, a contrast to the accent), --accent-3
|
|
15
|
+
// attention (warnings and cautions), used sparingly; it marks things and
|
|
16
|
+
// need not be readable as text.
|
|
17
|
+
export const COLOR_ROLES = ['--bg', '--surface', '--ink', '--ink-soft', '--muted', '--rule', '--accent', '--accent-2', '--accent-3', '--on-accent']
|
|
18
|
+
// Colours a palette may leave out, and what it gets instead: palettes made
|
|
19
|
+
// before the third accent get an amber that calls for attention in light and
|
|
20
|
+
// dark alike (`mdeck check` suggests setting one's own).
|
|
21
|
+
export const COLOR_FALLBACKS = { '--accent-3': { light: '#d99a00', dark: '#f5c542' } }
|
|
14
22
|
export const APPEARANCES = ['light', 'dark']
|
|
15
23
|
|
|
16
24
|
/**
|
package/src/live/client.js
CHANGED
|
@@ -237,7 +237,7 @@ export function latestByDevice(messages) {
|
|
|
237
237
|
// The deck's look as the answer page needs it: theme variables, font
|
|
238
238
|
// stylesheets and the style of a slide heading, read from the page that shows
|
|
239
239
|
// the slides (the presenter's current-slide frame, or the deck itself).
|
|
240
|
-
const LOOK_TOKENS = ['--bg', '--ink', '--ink-soft', '--muted', '--rule', '--surface', '--accent', '--accent-2', '--on-accent', '--font-body', '--font-display']
|
|
240
|
+
const LOOK_TOKENS = ['--bg', '--ink', '--ink-soft', '--muted', '--rule', '--surface', '--accent', '--accent-2', '--accent-3', '--on-accent', '--font-body', '--font-display']
|
|
241
241
|
let lookSource = () => document
|
|
242
242
|
export function setLookSource(source) { lookSource = source }
|
|
243
243
|
export function captureLook(doc = lookSource()) {
|
package/src/runtime/chrome.jsx
CHANGED
|
@@ -43,7 +43,7 @@ export const S = {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function PaletteSwatches({ tokens }) {
|
|
46
|
-
const keys = ['--bg', '--surface', '--rule', '--accent', '--accent-2', '--ink']
|
|
46
|
+
const keys = ['--bg', '--surface', '--rule', '--accent', '--accent-2', '--accent-3', '--ink']
|
|
47
47
|
return (
|
|
48
48
|
<div style={{ display: 'flex', gap: '2px', alignItems: 'center', flexShrink: 0 }}>
|
|
49
49
|
{keys.map(k => tokens[k]
|