orz-markdown 1.0.0 → 1.2.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.
@@ -0,0 +1,302 @@
1
+ # Custom Theme Guide — orz-markdown
2
+
3
+ How to write a CSS theme for the parser. A theme is a single CSS file that imports `common.css` for structural rules and then styles every element the parser emits.
4
+
5
+ ---
6
+
7
+ ## Starting Point
8
+
9
+ Option A — start from scratch with shared infrastructure:
10
+ ```css
11
+ @import 'orz-markdown/themes/common.css';
12
+ /* or relative path when living alongside the bundled themes: */
13
+ @import './common.css';
14
+ ```
15
+
16
+ Option B — start from `assets/minimal.css` (all structural rules already included, only visual layer needed). Copy it and add your visual styles on top.
17
+
18
+ ---
19
+
20
+ ## Design Token Pattern
21
+
22
+ All bundled themes use CSS custom properties for maintainability. Define them on `:root`:
23
+
24
+ ```css
25
+ :root {
26
+ /* Surface colors */
27
+ --bg: #ffffff;
28
+ --bg-surface: #f8f8f8;
29
+ --surface: #ffffff;
30
+ --border: #e0e0e0;
31
+ --border-soft: #eeeeee;
32
+
33
+ /* Text colors */
34
+ --text: #222222;
35
+ --text-soft: #555555;
36
+ --text-muted: #888888;
37
+ --heading: #111111;
38
+
39
+ /* Accent / link */
40
+ --accent: #0060cc;
41
+ --link: #0060cc;
42
+ --link-visited: #5050aa;
43
+
44
+ /* Code */
45
+ --code-bg: #f5f5f5;
46
+ --code-border: #dddddd;
47
+ --code-text: #333333;
48
+
49
+ /* Semantic callout colors (4 required sets) */
50
+ --success-bg: #efffed; --success-border: #5ab85a; --success-text: #1a5e2a;
51
+ --info-bg: #e8f4fd; --info-border: #5a9fd4; --info-text: #1a4a70;
52
+ --warning-bg: #fff8e1; --warning-border: #e0a820; --warning-text: #7a5c00;
53
+ --danger-bg: #fff0f0; --danger-border: #d45a5a; --danger-text: #7a1a1a;
54
+
55
+ /* Inline span colors */
56
+ --span-red: #c0392b;
57
+ --span-yellow: #9a7615;
58
+ --span-green: #218763;
59
+ --span-blue: #1d6db0;
60
+
61
+ /* Typography */
62
+ --font-body: system-ui, sans-serif;
63
+ --font-heading: system-ui, sans-serif;
64
+ --font-code: monospace;
65
+ --base-size: 1rem;
66
+ --line-height: 1.7;
67
+
68
+ /* Markdown body container */
69
+ --markdown-body-max-width: 860px;
70
+ --markdown-body-padding: clamp(1.5rem, 5vw, 3rem);
71
+ --markdown-body-border: 1px solid var(--border);
72
+ --markdown-body-shadow: none;
73
+ --markdown-body-radius: 0px;
74
+ }
75
+ ```
76
+
77
+ For dark themes add `color-scheme: dark` so browser chrome adapts:
78
+ ```css
79
+ :root { color-scheme: dark; }
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Recommended File Structure
85
+
86
+ Follow this section order in every theme file for consistency:
87
+
88
+ 1. `@import './common.css'` and metadata comments
89
+ 2. `:root` theme tokens
90
+ 3. Optional modifier classes (`.font-*`, `.size-*`) if implemented
91
+ 4. Reset and document shell (`html`, `body`, `*`)
92
+ 5. `.markdown-body` container: max-width, padding, surface
93
+ 6. Headings `h1`–`h6` and `.header-anchor`
94
+ 7. Body copy: `p`, `strong`, `em`, `s`, `mark`, `ins`, `sub`, `sup`
95
+ 8. Links: `a`, `a:hover`, `a:visited`
96
+ 9. Lists and task lists: `ul`, `ol`, `li`, `.task-list-item`
97
+ 10. Code and math: `code`, `pre`, `.katex`, `.katex-display`
98
+ 11. Structural blocks: `blockquote`, `table`, `img`, `hr`
99
+ 12. Semantic containers: `.success`, `.info`, `.warning`, `.danger`
100
+ 13. Layout utilities: `.left`, `.right`, `.center`
101
+ 14. Spoiler, tabs, and columns: `details.spoil`, `.tabs`, `.cols`
102
+ 15. TOC and footnotes: `nav.toc`, `.footnotes`
103
+ 16. Plugin output: `.youtube-embed`, `.mermaid`, `.smiles-render`, `.qrcode`, span color/badge classes
104
+ 17. Decorative pseudo-elements and theme-only flourishes
105
+ 18. Responsive rules (`@media`)
106
+ 19. Print rules (`@media print`)
107
+
108
+ ---
109
+
110
+ ## Optional Modifier Classes
111
+
112
+ Some bundled themes expose utility classes for runtime font and size switching. If you implement them, follow the same class names for interoperability:
113
+
114
+ ```css
115
+ /* Font family modifiers — applied to body or .markdown-body */
116
+ .font-serif { font-family: var(--font-body-serif, Georgia, serif); }
117
+ .font-sans { font-family: var(--font-body-sans, system-ui, sans-serif); }
118
+ .font-handwritten { font-family: var(--font-body-handwritten, cursive); }
119
+ .font-typewrite { font-family: var(--font-body-mono, monospace); }
120
+
121
+ /* Size modifiers */
122
+ .size-xs { font-size: 0.8rem; }
123
+ .size-sm { font-size: 0.9rem; }
124
+ .size-lg { font-size: 1.15rem; }
125
+ .size-xl { font-size: 1.3rem; }
126
+ ```
127
+
128
+ These are not required by `common.css` — they are opt-in per theme.
129
+
130
+ ---
131
+
132
+ ## Element Checklist
133
+
134
+ Every theme must have CSS rules for all of the following. See `references/css-classes.md` for the exact selectors.
135
+
136
+ ### Container & page
137
+ - [ ] `body` — page background, base font, line-height
138
+ - [ ] `.markdown-body` — outer container: max-width, padding, background, border
139
+
140
+ ### Headings and anchors
141
+ - [ ] `h1` `h2` `h3` `h4` `h5` `h6` — font size, color, margin, font-family
142
+ - [ ] `.header-anchor` — hidden by default, visible on heading hover
143
+
144
+ ### Inline text
145
+ - [ ] `p` `a` `a:visited` `a:hover`
146
+ - [ ] `strong` `em` `s` / `del` `mark` `ins` `sub` `sup`
147
+ - [ ] `code` (inline) — background, border, border-radius, font
148
+ - [ ] `pre` — block container: background, border, overflow
149
+ - [ ] `pre > code` — reset border/background, set font-size and line-height
150
+
151
+ ### Structural blocks
152
+ - [ ] `blockquote` — left border or background, text color
153
+ - [ ] `ul` `ol` `li` — padding, list markers
154
+ - [ ] `.task-list-item` `.task-list-item-checkbox`
155
+ - [ ] `table` `thead` `tbody` `th` `td` — borders, header background, row striping
156
+ - [ ] `hr` — height, color, margin
157
+ - [ ] `img` — margin, optional border/radius
158
+
159
+ ### Plugins and containers
160
+ - [ ] `.footnote-ref` `.footnote-ref a` `.footnotes` `.footnotes-sep` `.footnote-backref`
161
+ - [ ] `.katex` `.katex-display` `.katex-error`
162
+ - [ ] `div.success` `div.info` `div.warning` `div.danger` — background, border, text color
163
+ - [ ] `.left` `.right` `.center` — float/alignment, optional box styling
164
+ - [ ] `details.spoil` `details.spoil > summary` — border, summary background, open/closed marker
165
+ - [ ] `.tabs` `.tabs-bar` `.tabs-bar-btn` `.tabs-bar-btn.active` `.tab` `.tab.active`
166
+ - [ ] `.tabs:not([data-js]) .tab` — no-JS fallback: show all panels
167
+ - [ ] `.cols` `.col`
168
+ - [ ] `nav.toc` `.toc-list` — list reset, link styles
169
+ - [ ] `span.red` `span.yellow` `span.green` `span.blue`
170
+ - [ ] `span.success` `span.info` `span.warning` `span.danger` — inline badge variant
171
+ - [ ] `div.youtube-embed` `div.youtube-embed iframe` — 16:9 responsive wrapper (structural)
172
+ - [ ] `div.mermaid` `div.mermaid svg`
173
+ - [ ] `div.smiles-render` `canvas[data-smiles]`
174
+ - [ ] `span.qrcode` — **must have `background: #fff`** (SVG is always black-on-white)
175
+
176
+ ---
177
+
178
+ ## Design Guidelines
179
+
180
+ ### 1. Scope under `.markdown-body`
181
+
182
+ Prefix all element rules with `.markdown-body` to avoid polluting global page styles. Exception: `body`, `html`, and `::selection` are acceptable at page scope.
183
+
184
+ ```css
185
+ /* Good */
186
+ .markdown-body h2 { ... }
187
+ .markdown-body blockquote { ... }
188
+
189
+ /* Page-scoped (OK) */
190
+ body { background: var(--bg); font-family: var(--font-body); }
191
+ ```
192
+
193
+ ### 2. Semantic callouts must be visibly distinct
194
+
195
+ `div.success/info/warning/danger` must clearly differ from regular content even in neutral palettes. The four semantic meanings (green/blue/amber/red) must be preserved — do not swap, remove, or use identical styles for multiple variants.
196
+
197
+ ### 3. QR codes always need white background
198
+
199
+ The SVG emitted inside `.qrcode` is black-on-transparent. Set an explicit white background on both the inline widget and the overlay so it reads correctly on dark themes:
200
+
201
+ ```css
202
+ span.qrcode { background: #fff; padding: 4px; }
203
+ ```
204
+
205
+ ### 4. Tabs functional CSS is non-negotiable
206
+
207
+ The tabs show/hide behavior depends on these rules being present. They are functional, not decorative:
208
+
209
+ ```css
210
+ .tab { display: none; }
211
+ .tab.active { display: block; }
212
+ .tabs:not([data-js]) .tab { display: block; } /* no-JS fallback */
213
+ .tabs:not([data-js]) .tab + .tab { border-top: 1px solid ...; } /* visual separator */
214
+ ```
215
+
216
+ ### 5. Spoiler open/close marker is functional
217
+
218
+ The `summary::before` pseudo-element communicating open/closed state must be present. Use `+`/`–`, `▶`/`▼`, or any clear pair:
219
+
220
+ ```css
221
+ details.spoil > summary::before { content: '+'; }
222
+ details.spoil[open] > summary::before { content: '–'; }
223
+ ```
224
+
225
+ ### 6. YouTube embed needs the 16:9 hack
226
+
227
+ The responsive iframe technique is structural — do not change the core ratio or remove it:
228
+
229
+ ```css
230
+ div.youtube-embed { position: relative; height: 0; padding-bottom: 56.25%; overflow: hidden; }
231
+ div.youtube-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
232
+ ```
233
+
234
+ ### 7. Float `.left` needs a mobile breakpoint
235
+
236
+ Without this the float causes overflow on narrow screens:
237
+
238
+ ```css
239
+ @media (max-width: 600px) {
240
+ .left { float: none; max-width: 100%; margin-right: 0; }
241
+ }
242
+ ```
243
+
244
+ ### 8. Dark themes: code block foreground vs syntax highlighting
245
+
246
+ Highlight.js injects its own colors into `<code>` tokens. Set `pre code { color: inherit; }` and choose a matching Highlight.js theme (e.g., `atom-one-dark.min.css`) rather than fighting it with heavy specificity.
247
+
248
+ ### 9. Keep the palette small and purposeful
249
+
250
+ Avoid "AI tells": no gradients on every element, no excessive border-radius, no layout that relies on 15+ custom properties for a single component. Two to four accent colors with consistent usage throughout is more human than a rainbow.
251
+
252
+ ### 10. Test with `tests/example.html`
253
+
254
+ After writing your theme, run `npm run render` from the project root to regenerate the example page and visually verify every element type. The example page includes all containers, all custom plugins, math, chemistry, mermaid, code blocks, tables, and footnotes.
255
+
256
+ ### 11. CJK font stacks
257
+
258
+ Include CJK fallback fonts in your font stacks for proper Chinese/Japanese/Korean glyph rendering:
259
+
260
+ - **Serif themes**: include `Noto Serif TC`, `Noto Serif SC` in the serif stack
261
+ - **Sans-serif themes**: include `Noto Sans TC`, `Noto Sans SC` in the sans stack
262
+ - **Handwritten themes**: prefer `Hanzi Pen TC`, `Hanzi Pen SC`
263
+
264
+ ```css
265
+ --font-body: 'Your Primary Font', 'Noto Sans TC', 'Noto Sans SC', system-ui, sans-serif;
266
+ ```
267
+
268
+ ### 12. SMILES rendering: match the page color scheme
269
+
270
+ SmilesDrawer's `draw()` call accepts a theme argument (`'light'` or `'dark'`). Dark-background themes need `'dark'` so bonds and atoms render white instead of black. This is set in the page initialization script (see `assets/template.html`), not in CSS — but coordinate with it:
271
+
272
+ ```js
273
+ // Light themes:
274
+ drawer.draw(tree, canvas, 'light', false);
275
+ // Dark themes:
276
+ drawer.draw(tree, canvas, 'dark', false);
277
+ ```
278
+
279
+ ---
280
+
281
+ ## Theme Compliance Checklist
282
+
283
+ Use this when auditing an existing theme or reviewing a new one:
284
+
285
+ - [ ] Section order follows the 19-section structure above
286
+ - [ ] `@import './common.css'` is the first line
287
+ - [ ] Base font size and line-height are declared as `:root` tokens near the top
288
+ - [ ] `.markdown-body` max-width, padding, border, and shadow are easy to override
289
+ - [ ] All heading levels `h1`–`h6` are styled distinctly
290
+ - [ ] Ordered lists use decimal counters (`1, 2, 3`) — never `01, 02, 03`
291
+ - [ ] Narrow tables shrink to content width instead of always stretching to full width
292
+ - [ ] All four semantic containers (`success/info/warning/danger`) are visually distinct
293
+ - [ ] Badge spans (`span.success/info/warning/danger`) stay single-line — no pseudo-label text injected
294
+ - [ ] QR code: `span.qrcode` has explicit `background: #fff` (SVG is always black-on-white)
295
+ - [ ] `.left` float has a mobile breakpoint (`float: none` below ~600px)
296
+ - [ ] Tabs show/hide CSS is functional (`display: none` default, `block` when `.active`)
297
+ - [ ] `.tabs:not([data-js]) .tab` fallback shows all panels when JS is absent
298
+ - [ ] Spoiler open/close marker (`summary::before`) communicates state clearly
299
+ - [ ] YouTube embed uses the 16:9 padding-bottom trick
300
+ - [ ] SMILES container is visually neutral — no decorative box styling
301
+ - [ ] Print rules strip outer body framing, fit paper width, and repeat `thead` across pages
302
+ - [ ] CJK font fallbacks are present in serif/sans/handwritten font stacks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orz-markdown",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Customized markdown-it parser with official and custom plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,7 +11,8 @@
11
11
  },
12
12
  "files": [
13
13
  "dist",
14
- "themes"
14
+ "themes",
15
+ "orz-markdown-skills"
15
16
  ],
16
17
  "scripts": {
17
18
  "build": "tsc",
@@ -63,6 +64,7 @@
63
64
  "@types/markdown-it-footnote": "^3.0.4",
64
65
  "@types/node": "^25.3.5",
65
66
  "@types/qrcode-svg": "^1.1.5",
67
+ "happy-dom": "^15.11.0",
66
68
  "tsx": "^4.21.0",
67
69
  "typescript": "^5.9.3",
68
70
  "vitest": "^4.0.18"