6.9 KiB
Blog Post Example Generation Pattern
Overview
We have a reusable pattern for generating single-problem worksheet examples for blog posts. This ensures blog post examples use the exact same rendering code as the live UI preview, maintaining perfect consistency between documentation and the actual tool.
The Pattern
1. Single Source of Truth
Location: src/app/api/create/worksheets/addition/example/route.ts
This API route contains the generateExampleTypst() function that:
- Takes display options (showCarryBoxes, showTenFrames, etc.)
- Takes specific addends (addend1, addend2)
- Generates a single compact problem using the same Typst helpers as full worksheets
- Compiles to SVG
2. Blog Post Generator Scripts
Pattern: Copy the generateExampleTypst() logic into a script that:
- Imports
generateTypstHelpersandgenerateProblemStackFunctionfromtypstHelpers.ts - Defines examples with specific problems and display options
- Generates Typst source for each example
- Compiles to SVG using
typst compile - Saves to
public/blog/[post-name]/
3. Existing Examples
Ten-frames blog post:
- Script:
scripts/generateTenFrameExamples.ts - Output:
public/blog/ten-frame-examples/ - Usage: Shows same problem (47 + 38) with/without ten-frames
- Blog post:
content/blog/ten-frames-for-regrouping.md
Difficulty progression blog post:
- Script:
scripts/generateBlogExamples.ts - Output:
public/blog/difficulty-examples/ - Usage: Shows same regrouping level with different scaffolding
- Blog post:
content/blog/beyond-easy-and-hard.md
Why This Pattern Matters
Benefits
- Consistency: Blog examples use the exact same rendering as the live tool
- Single Source of Truth: One set of Typst helpers for both UI and docs
- Easy Updates: When worksheet rendering changes, re-run scripts to update examples
- Specific Examples: Can choose exact problems that demonstrate specific features
- Version Control: Static SVGs committed to repo, no runtime generation needed
Anti-Pattern (Don't Do This)
❌ Don't manually create example SVGs in a design tool ❌ Don't screenshot the live UI (inconsistent sizing, quality) ❌ Don't duplicate the Typst rendering logic in separate files ❌ Don't use the full worksheet generator for blog examples (creates 2x2 grids)
How to Create New Blog Examples
Step 1: Create Generator Script
// scripts/generateYourFeatureExamples.ts
import fs from "fs";
import path from "path";
import { execSync } from "child_process";
import {
generateTypstHelpers,
generateProblemStackFunction,
} from "../src/app/create/worksheets/addition/typstHelpers";
const outputDir = path.join(
process.cwd(),
"public",
"blog",
"your-feature-examples",
);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
interface ExampleOptions {
showCarryBoxes?: boolean;
showAnswerBoxes?: boolean;
showPlaceValueColors?: boolean;
showTenFrames?: boolean;
showProblemNumbers?: boolean;
fontSize?: number;
addend1: number;
addend2: number;
}
function generateExampleTypst(config: ExampleOptions): string {
const a = config.addend1;
const b = config.addend2;
const fontSize = config.fontSize || 16;
const cellSize = 0.45; // Larger than UI preview (0.35) for blog readability
const showCarries = config.showCarryBoxes ?? false;
const showAnswers = config.showAnswerBoxes ?? false;
const showColors = config.showPlaceValueColors ?? false;
const showNumbers = config.showProblemNumbers ?? false;
const showTenFrames = config.showTenFrames ?? false;
return String.raw`
#set page(width: auto, height: auto, margin: 12pt, fill: white)
#set text(size: ${fontSize}pt, font: "New Computer Modern Math")
#let heavy-stroke = 0.8pt
#let show-ten-frames-for-all = false
${generateTypstHelpers(cellSize)}
${generateProblemStackFunction(cellSize)}
#let a = ${a}
#let b = ${b}
#let aT = calc.floor(calc.rem(a, 100) / 10)
#let aO = calc.rem(a, 10)
#let bT = calc.floor(calc.rem(b, 100) / 10)
#let bO = calc.rem(b, 10)
#align(center + horizon)[
#problem-stack(
a, b, aT, aO, bT, bO,
${showNumbers ? "0" : "none"},
${showCarries},
${showAnswers},
${showColors},
${showTenFrames},
${showNumbers}
)
]
`;
}
const examples = [
{
filename: "example-1.svg",
description: "Your feature demonstrated",
options: {
addend1: 47,
addend2: 38,
showCarryBoxes: true,
showAnswerBoxes: true,
showPlaceValueColors: true,
showTenFrames: true,
showProblemNumbers: true,
},
},
// Add more examples...
] as const;
for (const example of examples) {
const typstSource = generateExampleTypst(example.options);
const svg = execSync("typst compile --format svg - -", {
input: typstSource,
encoding: "utf8",
maxBuffer: 2 * 1024 * 1024,
});
fs.writeFileSync(path.join(outputDir, example.filename), svg, "utf-8");
}
Step 2: Run Generator
npx tsx scripts/generateYourFeatureExamples.ts
Step 3: Use in Blog Post
---
title: "Your Feature Title"
---
## Feature Overview

_Caption explaining what the example demonstrates._
Tips for Good Examples
Problem Selection
- Choose problems that require the feature: For ten-frames, use 7+8=15 (requires regrouping)
- Use simple, clear numbers: 47 + 38 is better than 387 + 694 for demonstrating basics
- Show edge cases when relevant: Double regrouping (57 + 68) shows ten-frames in both columns
Display Options
- Minimize non-essential scaffolding: Turn off unrelated features to focus attention
- Use consistent options across related examples: Same colors, same carry boxes, etc.
- Consider cell size: Blog examples use 0.45in vs UI preview 0.35in for readability
File Organization
- One directory per blog post:
public/blog/[post-slug]/ - Descriptive filenames:
with-ten-frames.svg, notexample1.svg - Keep generator script: Document what examples show and why
Maintenance
When to Regenerate Examples
- ✅ When
generateProblemStackFunction()changes (new rendering logic) - ✅ When
generateTypstHelpers()changes (new visual styling) - ✅ When Typst compiler updates (may affect rendering)
- ❌ When blog post text changes (examples are independent)
Updating All Examples
# Regenerate all blog examples
npx tsx scripts/generateBlogExamples.ts
npx tsx scripts/generateTenFrameExamples.ts
# Add more as needed
Reference Implementation
See scripts/generateTenFrameExamples.ts for a complete, documented example of this pattern.
Key features demonstrated:
- Clear header documentation explaining the pattern
- Reusable
generateExampleTypst()function - Declarative example definitions
- Helpful inline comments explaining problem choices
- Error handling for Typst compilation