Tech & AHA-Stack
How the Math Box was built
The Math Box is a demo application for the AHA-Stack and @casoon/fragment-renderer. It demonstrates how modern web applications can be built with minimal JavaScript and maximum performance.
What is the AHA-Stack?
The AHA-Stack combines three lightweight technologies:
Astro
Astro - A modern web framework for content-focused websites with zero-JS default
HTMX
HTMX - Enables dynamic interactions through HTML attributes instead of JavaScript
Alpine.js
Alpine.js - Lightweight JavaScript for reactive UI components
@casoon/fragment-renderer
The Fragment Renderer is a library from casoon that enables server-side rendering of Astro components as HTML fragments. This is ideal for HTMX-based applications.
Features:
- Server-side rendering of Astro components
- HTMX integration with automatic response headers
- Preset for the AHA-Stack
- TypeScript support
Example: Feedback Fragment
This is how feedback is rendered in the Math Box after an answer:
// task-renderer.ts
import { createAstroRuntime } from "@casoon/fragment-renderer";
import { ahaStackPreset } from "@casoon/fragment-renderer/presets/aha-stack";
const runtime = createAstroRuntime({
...ahaStackPreset({ locale, htmxHeaders: true }),
components: [
{ id: "feedback-correct", loader: () => import("./FeedbackCorrect.astro") },
{ id: "feedback-incorrect", loader: () => import("./FeedbackIncorrect.astro") },
],
});
// Render feedback as HTML fragment
const html = await runtime.renderToString({
componentId: result.isCorrect ? "feedback-correct" : "feedback-incorrect",
props: { locale, nextUrl, isComplete },
}); The Task System
The Math Box uses a modular task system with:
- • Unified interface for all task types
- • Self-validating tasks with hints
- • SVG visualizations for geometry and fractions
- • Multiple-choice and drag-and-drop support
- • Adaptive difficulty and error retry
// Unified Task Interface
interface TaskInstance {
id: string;
question: string;
validate(answer: string): ValidationResult;
getHint(): string;
getCorrectAnswer(): string | number;
}
// Self-validating task
const result = task.validate(userAnswer);
if (!result.isCorrect) {
console.log(result.hint); // "Der kleine Zeiger zeigt die Stunde..."
}