@kadal/theme-lecture 0.0.0-stage → 0.4.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/LICENSE +661 -0
- package/README.md +71 -2
- package/index.tsx +245 -0
- package/package.json +116 -4
- package/style.css +642 -0
package/README.md
CHANGED
|
@@ -1,3 +1,72 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @kadal/theme-lecture
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Lecture**, a theme for [Kadal Deck](https://deck.kadal.cc). An academic theme for lectures, seminars and defences. The Source family (Serif 4 for headings, Sans 3 for text, Code Pro for code), navy for structure and crimson for results, numbered sections, a footer on every slide, definition / theorem / proof blocks set the way a textbook sets them, citations, and a references layout. Made to print: white pages, hairlines that survive a laser printer, no shadows, the footer on every page.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Use it
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @kadal/theme-lecture
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
---
|
|
15
|
+
theme: lecture
|
|
16
|
+
themeConfig: # all optional
|
|
17
|
+
footer: true
|
|
18
|
+
sectionNumbers: true
|
|
19
|
+
accent: "#1f4e8c"
|
|
20
|
+
---
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The deck plugin brings the stylesheet and the module in; nothing to import. The prebuilt shell (the hosted builder, published decks) bundles this theme already. Schemes: light and dark. Fonts: Source Serif 4 (headings, serif), Source Sans 3 (text), Source Code Pro (code), loaded through the theme's `config.fonts`. Code colours: Shiki's github-light / github-dark-dimmed. To show it beside other themes in the theme menu (<kbd>T</kbd>), list it: `themes: [lecture, minimal]`.
|
|
24
|
+
|
|
25
|
+
## themeConfig
|
|
26
|
+
|
|
27
|
+
Every key arrives on the deck root as a CSS variable (`accentDark` → `--theme-accent-dark`); the theme reads them with fallbacks, so set only what you change.
|
|
28
|
+
|
|
29
|
+
| key | type | default | what it does |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| `course` | string | — | The course, bold at the footer's left ("MA 201 · Linear Algebra"). |
|
|
32
|
+
| `author` | string | the deck's `author` | Who is speaking, after the course. |
|
|
33
|
+
| `institution` | string | — | After the author. |
|
|
34
|
+
| `footer` | boolean | `true` | `false` turns the footer off; `footer: false` on a slide hides it there. |
|
|
35
|
+
| `sectionNumbers` | boolean | `true` | `false` leaves the numbers off section slides and the footer. |
|
|
36
|
+
| `accent` | colour | `#1f4e8c` | The colour of the structure (headings' rules, numbers, definitions). |
|
|
37
|
+
|
|
38
|
+
## Layouts
|
|
39
|
+
|
|
40
|
+
Every built-in layout is styled (`default`, `center`, `cover`, `intro`, `section`, `end`, `fact`, `quote`, `statement`, `full`, `two-cols`, `two-cols-header`, `image`, `image-left`, `image-right`, `iframe`, `iframe-left`, `iframe-right`), and the theme adds:
|
|
41
|
+
|
|
42
|
+
| layout | what |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `section` | The built-in section, numbered: "Section 2" over the title, counted from the deck's section slides (`n:` overrides). |
|
|
45
|
+
| `definition` | A slide that is one definition, large: `name:` and `n:` label it. |
|
|
46
|
+
| `theorem` | The same for a theorem; `kind: lemma | corollary | proposition`. |
|
|
47
|
+
| `references` | The slide's list as a numbered bibliography, [1] [2] …, in two columns past six entries. |
|
|
48
|
+
|
|
49
|
+
## Components
|
|
50
|
+
|
|
51
|
+
| component | what |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `<Definition>` `<Theorem>` `<Lemma>` `<Corollary>` `<Proposition>` `<Example>` `<Remark>` | A run-in label ("Theorem 2.1 (Pythagoras).") then the body: `n` numbers it, `title` names it. A definition is a tinted box, a theorem (and lemma, corollary, proposition) a frame with the statement in italics. |
|
|
54
|
+
| `<Proof of>` | "Proof." (or "Proof of Theorem 2.1.") then the body, closed with ∎. |
|
|
55
|
+
| `<Cite n page />` | `[3]`, `[3, 7, p. 12]`; with children, author–year: `<Cite>Knuth 1984</Cite>` → (Knuth 1984). |
|
|
56
|
+
|
|
57
|
+
## Preview
|
|
58
|
+
|
|
59
|
+
`preview/deck.mdx` walks through every layout and component (`theme: ../`). In this repository:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
bun run --filter @kadal/theme-lecture dev # the preview deck, live
|
|
63
|
+
node tools/theme-shots.mjs lecture # screenshots in light and dark, contrast and overflow checks
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Accessibility and print
|
|
67
|
+
|
|
68
|
+
Body and muted text meet WCAG AA (4.5:1) on the page and on cards in every scheme the theme draws (checked by `tools/theme-shots.mjs`). Anything that moves stands still under `prefers-reduced-motion` and on a still copy; printed pages and exports drop shadows and glows.
|
|
69
|
+
|
|
70
|
+
## Licence
|
|
71
|
+
|
|
72
|
+
AGPL-3.0-or-later, like the engine (see LICENSE); a commercial licence is available — see [LICENSING](https://github.com/kadalcc/deck/blob/main/LICENSING.md).
|
package/index.tsx
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { type LayerProps, type LayoutProps, useDeck, useSlideOptional } from "@kadal/deck";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
* Lecture's module: the footer, numbered sections, the blocks a textbook sets, citations.
|
|
8
|
+
*
|
|
9
|
+
* the footer a slide-bottom layer on every slide but the cover: the course (bold),
|
|
10
|
+
* the author and the institution; the current section; "page / total".
|
|
11
|
+
* `themeConfig.footer: false` turns it off, `footer: false` on a slide
|
|
12
|
+
* hides it there. It is drawn on printed pages too.
|
|
13
|
+
* layout: section the built-in section, numbered: "Section 2" over the title (counted
|
|
14
|
+
* from the deck's section slides; `n:` overrides, and
|
|
15
|
+
* `themeConfig.sectionNumbers: false` leaves them off)
|
|
16
|
+
* layout: definition a slide that is one definition, large: `name:` and `n:` label it
|
|
17
|
+
* layout: theorem the same for a theorem (`kind: lemma | corollary | proposition`)
|
|
18
|
+
* layout: references the slide's list as a numbered bibliography, [1] [2] …, two columns
|
|
19
|
+
* past six entries
|
|
20
|
+
* <Definition> <Theorem> <Lemma> <Corollary> <Proposition> <Example> <Remark>
|
|
21
|
+
* a run-in label ("Theorem 2.1 (Pythagoras).") then the body; `n` numbers
|
|
22
|
+
* it, `title` names it
|
|
23
|
+
* <Proof> "Proof." … ∎ (`of="Theorem 2.1"` → "Proof of Theorem 2.1.")
|
|
24
|
+
* <Cite n={3} /> [3] (`n="3, 7"`, `page="12"` → [3, 7, p. 12]); with children, the
|
|
25
|
+
* author-year form: <Cite>Knuth 1984</Cite> → (Knuth 1984)
|
|
26
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const DEFAULTS = {
|
|
30
|
+
course: "",
|
|
31
|
+
author: "",
|
|
32
|
+
institution: "",
|
|
33
|
+
footer: true,
|
|
34
|
+
sectionNumbers: true,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const str = (v: unknown) => (typeof v === "string" && v.trim() ? v.trim() : null);
|
|
38
|
+
const label = (v: unknown) => (v == null || v === "" ? null : String(v).trim());
|
|
39
|
+
|
|
40
|
+
/** The theme's settings: its defaults under the deck's `themeConfig`. */
|
|
41
|
+
function useLectureConfig() {
|
|
42
|
+
const deck = useDeck();
|
|
43
|
+
return { ...DEFAULTS, ...deck.config.themeConfig } as typeof DEFAULTS & Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The section a slide is in: its number and title, from the deck's `layout: section` slides. */
|
|
47
|
+
function useSectionOf(index: number): { n: number; title: string | null } | null {
|
|
48
|
+
const deck = useDeck();
|
|
49
|
+
const sections = deck.manifest.slides.filter(
|
|
50
|
+
(s) => s.index <= index && s.frontmatter.layout === "section",
|
|
51
|
+
);
|
|
52
|
+
const last = sections.at(-1);
|
|
53
|
+
if (!last) return null;
|
|
54
|
+
const given = label(last.frontmatter.n);
|
|
55
|
+
return { n: given && /^\d+$/.test(given) ? Number(given) : sections.length, title: last.title };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function Footer({ slide, page, total, layout, frontmatter }: LayerProps) {
|
|
59
|
+
const deck = useDeck();
|
|
60
|
+
const cfg = useLectureConfig();
|
|
61
|
+
const section = useSectionOf(slide?.index ?? 0);
|
|
62
|
+
if (cfg.footer === false || frontmatter.footer === false || layout === "cover") return null;
|
|
63
|
+
const course = str(cfg.course);
|
|
64
|
+
const rest = [str(cfg.author) ?? str(deck.config.author), str(cfg.institution)].filter(Boolean);
|
|
65
|
+
return (
|
|
66
|
+
<footer className="lecture-footer" aria-hidden="true">
|
|
67
|
+
<span>
|
|
68
|
+
{course ? <b>{course}</b> : null}
|
|
69
|
+
{course && rest.length ? " · " : null}
|
|
70
|
+
{rest.join(" · ")}
|
|
71
|
+
</span>
|
|
72
|
+
<span className="lecture-footer-section">
|
|
73
|
+
{section?.title
|
|
74
|
+
? `${cfg.sectionNumbers === false ? "" : `${section.n} `}${section.title}`
|
|
75
|
+
: ""}
|
|
76
|
+
</span>
|
|
77
|
+
<span className="lecture-footer-page">
|
|
78
|
+
{page} / {total}
|
|
79
|
+
</span>
|
|
80
|
+
</footer>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function Section({ children, frontmatter }: LayoutProps) {
|
|
85
|
+
const cfg = useLectureConfig();
|
|
86
|
+
const index = useSlideOptional()?.meta.index ?? 0;
|
|
87
|
+
const section = useSectionOf(index);
|
|
88
|
+
const n = label(frontmatter.n) ?? (section ? String(section.n) : null);
|
|
89
|
+
return (
|
|
90
|
+
<div className="deck-layout deck-layout-section">
|
|
91
|
+
{cfg.sectionNumbers !== false && n ? (
|
|
92
|
+
<div className="lecture-section-number">
|
|
93
|
+
Section <b>{n}</b>
|
|
94
|
+
</div>
|
|
95
|
+
) : null}
|
|
96
|
+
{children}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
type Kind =
|
|
102
|
+
| "definition"
|
|
103
|
+
| "theorem"
|
|
104
|
+
| "lemma"
|
|
105
|
+
| "corollary"
|
|
106
|
+
| "proposition"
|
|
107
|
+
| "proof"
|
|
108
|
+
| "example"
|
|
109
|
+
| "remark";
|
|
110
|
+
|
|
111
|
+
const NAMES: Record<Kind, string> = {
|
|
112
|
+
definition: "Definition",
|
|
113
|
+
theorem: "Theorem",
|
|
114
|
+
lemma: "Lemma",
|
|
115
|
+
corollary: "Corollary",
|
|
116
|
+
proposition: "Proposition",
|
|
117
|
+
proof: "Proof",
|
|
118
|
+
example: "Example",
|
|
119
|
+
remark: "Remark",
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
interface BlockProps {
|
|
123
|
+
n?: string | number;
|
|
124
|
+
title?: string;
|
|
125
|
+
/** Proof only: what it proves ("Theorem 2.1"). */
|
|
126
|
+
of?: string;
|
|
127
|
+
children?: ReactNode;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function Block({ kind, n, title, of, children }: BlockProps & { kind: Kind }) {
|
|
131
|
+
const name = kind === "proof" && of ? `Proof of ${of}` : NAMES[kind];
|
|
132
|
+
const num = label(n);
|
|
133
|
+
return (
|
|
134
|
+
<div className={`lecture-block is-${kind}`} role="note" aria-label={name}>
|
|
135
|
+
<div className="lecture-block-body">
|
|
136
|
+
<span className="lecture-label">
|
|
137
|
+
{name}
|
|
138
|
+
{num ? ` ${num}` : ""}
|
|
139
|
+
{title ? (
|
|
140
|
+
<>
|
|
141
|
+
{" "}
|
|
142
|
+
<i>({title})</i>
|
|
143
|
+
</>
|
|
144
|
+
) : null}
|
|
145
|
+
.{" "}
|
|
146
|
+
</span>
|
|
147
|
+
{children}
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const block = (kind: Kind) =>
|
|
154
|
+
function LectureBlock(props: BlockProps) {
|
|
155
|
+
return <Block kind={kind} {...props} />;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const Definition = block("definition");
|
|
159
|
+
const Theorem = block("theorem");
|
|
160
|
+
const Lemma = block("lemma");
|
|
161
|
+
const Corollary = block("corollary");
|
|
162
|
+
const Proposition = block("proposition");
|
|
163
|
+
const Proof = block("proof");
|
|
164
|
+
const Example = block("example");
|
|
165
|
+
const Remark = block("remark");
|
|
166
|
+
|
|
167
|
+
function DefinitionLayout({ children, frontmatter }: LayoutProps) {
|
|
168
|
+
return (
|
|
169
|
+
<div className="deck-layout deck-layout-definition">
|
|
170
|
+
<Block
|
|
171
|
+
kind="definition"
|
|
172
|
+
n={label(frontmatter.n) ?? undefined}
|
|
173
|
+
title={str(frontmatter.name) ?? undefined}
|
|
174
|
+
>
|
|
175
|
+
{children}
|
|
176
|
+
</Block>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function TheoremLayout({ children, frontmatter }: LayoutProps) {
|
|
182
|
+
const k = str(frontmatter.kind);
|
|
183
|
+
const kind: Kind = k === "lemma" || k === "corollary" || k === "proposition" ? k : "theorem";
|
|
184
|
+
return (
|
|
185
|
+
<div className="deck-layout deck-layout-theorem">
|
|
186
|
+
<Block
|
|
187
|
+
kind={kind}
|
|
188
|
+
n={label(frontmatter.n) ?? undefined}
|
|
189
|
+
title={str(frontmatter.name) ?? undefined}
|
|
190
|
+
>
|
|
191
|
+
{children}
|
|
192
|
+
</Block>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function References({ children }: LayoutProps) {
|
|
198
|
+
return <div className="deck-layout deck-layout-references">{children}</div>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function Cite({
|
|
202
|
+
n,
|
|
203
|
+
page,
|
|
204
|
+
children,
|
|
205
|
+
}: {
|
|
206
|
+
n?: string | number;
|
|
207
|
+
page?: string | number;
|
|
208
|
+
children?: ReactNode;
|
|
209
|
+
}) {
|
|
210
|
+
const p = label(page);
|
|
211
|
+
if (children != null && n == null)
|
|
212
|
+
return (
|
|
213
|
+
<span className="lecture-cite">
|
|
214
|
+
({children}
|
|
215
|
+
{p ? `, p. ${p}` : ""})
|
|
216
|
+
</span>
|
|
217
|
+
);
|
|
218
|
+
return (
|
|
219
|
+
<span className="lecture-cite">
|
|
220
|
+
[{label(n) ?? "?"}
|
|
221
|
+
{p ? `, p. ${p}` : ""}]
|
|
222
|
+
</span>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export default {
|
|
227
|
+
layouts: {
|
|
228
|
+
section: Section,
|
|
229
|
+
definition: DefinitionLayout,
|
|
230
|
+
theorem: TheoremLayout,
|
|
231
|
+
references: References,
|
|
232
|
+
},
|
|
233
|
+
components: {
|
|
234
|
+
Definition,
|
|
235
|
+
Theorem,
|
|
236
|
+
Lemma,
|
|
237
|
+
Corollary,
|
|
238
|
+
Proposition,
|
|
239
|
+
Proof,
|
|
240
|
+
Example,
|
|
241
|
+
Remark,
|
|
242
|
+
Cite,
|
|
243
|
+
},
|
|
244
|
+
layers: { "slide-bottom": Footer },
|
|
245
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,118 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kadal/theme-lecture",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Lecture, a theme for Kadal Deck (@kadal/deck). An academic theme for lectures and seminars: numbered sections, a course footer, theorem, definition and proof blocks, citations, and print-perfect pages.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"kadal-theme",
|
|
7
|
+
"kadal-deck",
|
|
8
|
+
"presentation",
|
|
9
|
+
"slides",
|
|
10
|
+
"theme",
|
|
11
|
+
"academic",
|
|
12
|
+
"lecture",
|
|
13
|
+
"education",
|
|
14
|
+
"math"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://deck.kadal.cc/docs/",
|
|
17
|
+
"funding": {
|
|
18
|
+
"type": "github",
|
|
19
|
+
"url": "https://github.com/sponsors/pmithunish"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/kadalcc/deck.git",
|
|
24
|
+
"directory": "packages/theme-lecture"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/kadalcc/deck/issues"
|
|
28
|
+
},
|
|
29
|
+
"license": "AGPL-3.0-or-later",
|
|
30
|
+
"author": "Mithunish Prabhakaran <pmithunish@gmail.com>",
|
|
31
|
+
"type": "module",
|
|
32
|
+
"sideEffects": [
|
|
33
|
+
"*.css"
|
|
34
|
+
],
|
|
35
|
+
"files": [
|
|
36
|
+
"style.css",
|
|
37
|
+
"index.tsx",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"exports": {
|
|
42
|
+
".": "./index.tsx",
|
|
43
|
+
"./style.css": "./style.css",
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"provenance": true
|
|
49
|
+
},
|
|
50
|
+
"kadal": {
|
|
51
|
+
"theme": {
|
|
52
|
+
"name": "lecture",
|
|
53
|
+
"title": "Lecture",
|
|
54
|
+
"description": "An academic theme for lectures and seminars: numbered sections, a course footer, theorem, definition and proof blocks, citations, and print-perfect pages.",
|
|
55
|
+
"colorScheme": "both",
|
|
56
|
+
"config": {
|
|
57
|
+
"transition": "fade",
|
|
58
|
+
"fonts": {
|
|
59
|
+
"heading": "Source Serif 4",
|
|
60
|
+
"sans": "Source Sans 3",
|
|
61
|
+
"serif": "Source Serif 4",
|
|
62
|
+
"mono": "Source Code Pro",
|
|
63
|
+
"weights": "400,600,700",
|
|
64
|
+
"italic": true
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"themeConfig": {
|
|
68
|
+
"course": "",
|
|
69
|
+
"author": "",
|
|
70
|
+
"institution": "",
|
|
71
|
+
"footer": true,
|
|
72
|
+
"sectionNumbers": true
|
|
73
|
+
},
|
|
74
|
+
"shiki": {
|
|
75
|
+
"light": "github-light",
|
|
76
|
+
"dark": "github-dark-dimmed"
|
|
77
|
+
},
|
|
78
|
+
"defaults": {},
|
|
79
|
+
"layouts": [
|
|
80
|
+
"section",
|
|
81
|
+
"definition",
|
|
82
|
+
"theorem",
|
|
83
|
+
"references"
|
|
84
|
+
],
|
|
85
|
+
"components": [
|
|
86
|
+
"Definition",
|
|
87
|
+
"Theorem",
|
|
88
|
+
"Lemma",
|
|
89
|
+
"Corollary",
|
|
90
|
+
"Proposition",
|
|
91
|
+
"Proof",
|
|
92
|
+
"Example",
|
|
93
|
+
"Remark",
|
|
94
|
+
"Cite"
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"scripts": {
|
|
99
|
+
"dev": "kadal-deck dev -C preview",
|
|
100
|
+
"check": "kadal-deck check -C preview",
|
|
101
|
+
"typecheck": "tsc --noEmit",
|
|
102
|
+
"test": "bun test"
|
|
103
|
+
},
|
|
104
|
+
"peerDependencies": {
|
|
105
|
+
"@kadal/deck": "^0.4.1",
|
|
106
|
+
"react": "^19.2.0"
|
|
107
|
+
},
|
|
108
|
+
"devDependencies": {
|
|
109
|
+
"@kadal/deck": "workspace:*",
|
|
110
|
+
"@types/react": "^19.2.0",
|
|
111
|
+
"@types/react-dom": "^19.2.0",
|
|
112
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
113
|
+
"react": "^19.2.7",
|
|
114
|
+
"react-dom": "^19.2.7",
|
|
115
|
+
"typescript": "^6.0.3",
|
|
116
|
+
"vite": "^8.0.16"
|
|
117
|
+
}
|
|
118
|
+
}
|