@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.
Files changed (5) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +71 -2
  3. package/index.tsx +245 -0
  4. package/package.json +116 -4
  5. package/style.css +642 -0
package/README.md CHANGED
@@ -1,3 +1,72 @@
1
- # Temporary Holding Version
1
+ # @kadal/theme-lecture
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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
+ ![Lecture: the cover, a content slide, code and the theme's own layout](./screenshot.jpg)
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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }