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..."
}

Links