5 KiB
Solid.js + Vite (Open Free Books)
Reference for client-side work in this Zola repo. Read this when editing frontend/, bundle.js, or adding interactivity.
Stack
| Piece | Location |
|---|---|
| Source | frontend/src/ (functional Solid components) |
| Entry | frontend/src/main.tsx |
| Build | Vite + vite-plugin-solid |
| Output | themes/openfreebooks/static/js/bundle.js |
| Loaded in | themes/openfreebooks/templates/base.html (defer) |
Package manager: Bun (bun install, bun run build:js).
Config bridge (Zola → Solid)
base.html embeds JSON:
<script id="site-config" type="application/json">…</script>
main.tsx reads it via readConfig() for brand, homeUrl, browseUrl, aboutUrl, githubUrl. Add new global URLs in zola.toml [extra] and mirror in base.html + readConfig() types.
Mount points
| ID / target | Component | Role |
|---|---|---|
#site-header |
SiteHeader |
Nav + theme toggle |
#marquee |
Marquee |
Homepage quote strip |
document.body |
ScrollRevealBootstrap |
[data-scroll-reveal-card] |
document.body |
ContributorsDialogBootstrap |
<dialog> for contributors |
#footer-year |
mountFooterYear() |
Copyright year |
Prefer bootstrap components that querySelector existing markup over re-rendering large HTML regions in Solid.
Conventions
- Functional components only; no class components.
- No mocks in Vitest unless the user asks for tests.
- Keep bundles small: mount only what’s needed.
- Styles stay in Sass (
themes/openfreebooks/sass/); useclassNamewith existing classes, not inline Tailwind. - Scroll reveal: stagger per section (
[data-scroll-reveal]group), not global index; reveal in-viewport items immediately on load (scroll-reveal.tsx).
Vite config
outDir:themes/openfreebooks/staticemptyOutDir: false— do not wipe theme static (only addjs/bundle.js)- Entry:
frontend/src/main.tsx→js/bundle.js - Lazy chunks: e.g.
import("./catalog-mermaid-view")→js/catalog-mermaid-view-[hash].js
Workflow
- Edit
frontend/src/…. - Run
bun run build:js(orbun run dev:jswhilezola serveruns). - Hard-refresh browser.
- For production:
bun run buildthenwrangler deploy.
Catalog (Solid + Mermaid)
Mounted from catalog.html via catalog-app.tsx (in main bundle). Map view lazy-loads catalog-mermaid-view.tsx.
| Concern | Module |
|---|---|
| Diagram source | lib/catalog-to-mermaid.ts |
| Mermaid init / hex theme | lib/catalog-mermaid-theme.ts → mermaidInitializeOptions(mode) |
| Strand title band overlay | lib/catalog-mermaid-strand-headers.ts |
| Light/dark for map | lib/catalog-canvas-theme.ts |
| Compare tab HTML | catalog-compare-doc.tsx reads #catalog-compare-template |
Mermaid theming: use theme: "base" and hex themeVariables only; CSS cannot override Mermaid’s inline cluster fills. See zola-catalog.md.
Subject shared assets (chapters)
For subjects with multiple chapters (e.g. mathematics), colocate shared code under content/{subject}/:
| File | Build output | Loaded by |
|---|---|---|
subject.scss |
static/css/subjects/{subject}.css |
chapter.html when subjects-meta.json has hasCss |
subject.ts |
chunk via Vite | main.tsx → generated/subject-modules.ts → initSubject(article) |
Run bun run sync:chapters to compile CSS and regenerate subject-modules.ts + data/_generated/subjects-meta.json. Chapter-only styles stay in content/{subject}/{slug}/chapter.scss.
Math conventions: .agents/skills/ofb/math-chapter-patterns.md.
KaTeX (math)
| Use case | API | Where |
|---|---|---|
Chapter HTML with \( … \) / \[ … \] |
renderBookMath() |
frontend/src/lib/katex.ts — runs on load via initBookMath() in main.tsx |
| Widget labels / dynamic formulas | renderLatex() + innerHTML |
import { renderLatex } from "@ofb/katex" in content/**/widgets/*.tsx |
| Captions with delimiter math | renderMathInContainer(el) |
Same module; do not duplicate per chapter |
- CSS:
js/katex.cssis linked frombase.htmlon every page. - Do not add local
function renderLatexorimport katex from "katex"in widgets — use@ofb/katexonly.
Adding a feature
- Add mount root in Tera template (empty
<div id="…">or use existing). - Create
frontend/src/components/your-feature.tsx. - Import and
render()inmain.tsx(or extend a bootstrap). - Add Sass if new UI.
Dialogs
Contributor modals use native <dialog> + showModal() in contributors-dialog.tsx. Markup lives in partials/contributors-block.html.
Do not
- Commit generated
main.cssorindex.htmlunderthemes/openfreebooks/static/. - Use
emptyOutDir: truein Vite without restoring allowed theme static files.
Verify
bun run build:js
zola build
Check themes/openfreebooks/static/js/bundle.js updated; hard-refresh if behavior looks stale.