From 50c4d51523b6db84edd30598533b7ee842d27076 Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Sun, 21 Dec 2025 22:12:00 +0100 Subject: [PATCH] feat: add HTML lessons mode and side-by-side comparison UI - Add HTML mode support with new validation types (element_exists, element_count, attribute_value, element_text, parent_child, sibling) - Create 3 HTML lesson modules: Elements, Forms Basic, Forms Validation - Implement side-by-side preview comparison (Your Output vs Expected) - Add merge animation with "Perfect Match!" overlay on validation success - Render expected output from solutionCode field in lesson JSON - Update schema to support HTML mode and solutionCode - Reorder modules: HTML first, then CSS, then Tailwind - Update tests for new functionality --- CLAUDE.md | 49 ++++++ lessons/20-html-elements.json | 97 +++++++++++ lessons/21-html-forms-basic.json | 102 ++++++++++++ lessons/22-html-forms-validation.json | 112 +++++++++++++ schemas/code-crispies-module-schema.json | 77 ++++++++- src/app.js | 34 +++- src/config/lessons.js | 13 +- src/helpers/renderer.js | 2 +- src/helpers/validator.js | 200 ++++++++++++++++++++++- src/impl/LessonEngine.js | 133 ++++++++++++++- src/index.html | 24 ++- src/main.css | 111 ++++++++++++- tests/unit/lessons.test.js | 40 +++-- tests/unit/renderer.test.js | 61 ++++--- tests/unit/validator.test.js | 147 ++++++++++++++++- 15 files changed, 1136 insertions(+), 66 deletions(-) create mode 100644 CLAUDE.md create mode 100644 lessons/20-html-elements.json create mode 100644 lessons/21-html-forms-basic.json create mode 100644 lessons/22-html-forms-validation.json diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1e0d379 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,49 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Code Crispies is an interactive CSS/Tailwind learning platform built with pure JavaScript (ES Modules) and Vite. Users complete lessons by writing CSS or Tailwind code that passes validation rules. + +## Commands + +```bash +npm start # Start dev server at http://localhost:1312 +npm run build # Production build to dist/ +npm run test # Run tests once +npm run test.watch # Run tests in watch mode +npm run test.coverage # Run tests with coverage report +npm run format # Format source files with Prettier +npm run format.lessons # Format lesson JSON files +``` + +## Architecture + +### Core Components + +- **LessonEngine** (`src/impl/LessonEngine.js`): Single source of truth for lesson state, progress tracking, and code validation. Manages user code application to preview iframes and persists progress to localStorage. + +- **Validator** (`src/helpers/validator.js`): Validates user code against lesson requirements. Supports validation types: `contains`, `contains_class`, `not_contains`, `regex`, `property_value`, `syntax`, `custom`. + +- **Lesson Config** (`src/config/lessons.js`): Loads and validates lesson modules from JSON. Modules are imported statically and stored in `moduleStore`. + +### Data Flow + +1. Lesson JSON files define modules with lessons, each containing `previewHTML`, `validations`, and other metadata +2. `LessonEngine` loads modules and tracks user progress per lesson +3. User code is applied to an iframe preview and validated against lesson rules +4. Progress is persisted to `localStorage` under `codeCrispies.progress` and `codeCrispies.userCode` + +### Lesson Structure + +Lessons are JSON files in `lessons/` following the schema in `schemas/code-crispies-module-schema.json`. Each module has: +- `mode`: "css" or "tailwind" +- `difficulty`: "beginner", "intermediate", or "advanced" +- `lessons[]`: Array of lessons with validations + +For Tailwind mode, user classes are injected via `{{USER_CLASSES}}` placeholder in `previewHTML`. + +### Testing + +Tests use Vitest with jsdom environment. Setup in `tests/setup.js` includes DOM testing library matchers. Test files are in `tests/unit/`. diff --git a/lessons/20-html-elements.json b/lessons/20-html-elements.json new file mode 100644 index 0000000..01263c8 --- /dev/null +++ b/lessons/20-html-elements.json @@ -0,0 +1,97 @@ +{ + "$schema": "../schemas/code-crispies-module-schema.json", + "id": "html-elements", + "title": "HTML Elements: Block vs Inline", + "description": "Understanding the fundamental difference between container (block) and inline elements", + "mode": "html", + "difficulty": "beginner", + "lessons": [ + { + "id": "block-vs-inline-intro", + "title": "Block vs Inline Elements", + "description": "HTML elements fall into two main categories:

Block elements (containers) start on a new line and take full width. Examples: <div>, <p>, <h1>, <section>

Inline elements flow within text and only take needed width. Examples: <span>, <a>, <strong>, <em>", + "task": "Create a paragraph with a <strong> word inside it. Notice how the paragraph is a block element (takes full width) while strong is inline (flows with text).", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui, sans-serif; padding: 20px; } p { background: #e3f2fd; padding: 10px; } strong { background: #ffecb3; }", + "sandboxCSS": "", + "initialCode": "

This is a paragraph with an important word.

", + "solutionCode": "

This is a paragraph with a important word.

", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "p", + "message": "Add a

paragraph element" + }, + { + "type": "parent_child", + "value": { "parent": "p", "child": "strong" }, + "message": "Place a element inside your paragraph" + } + ] + }, + { + "id": "semantic-containers", + "title": "Semantic Container Elements", + "description": "Modern HTML uses semantic containers that describe their content:

<header> - Page or section header
<nav> - Navigation links
<main> - Main content area
<section> - Thematic grouping
<article> - Self-contained content
<footer> - Page or section footer", + "task": "Create a basic page structure with <header>, <main>, and <footer> elements. Add a heading in the header.", + "previewHTML": "", + "previewBaseCSS": "body { font-family: system-ui; margin: 0; } header { background: #1976d2; color: white; padding: 15px; } main { padding: 20px; min-height: 100px; } footer { background: #424242; color: white; padding: 10px; text-align: center; }", + "sandboxCSS": "", + "initialCode": "", + "solutionCode": "

\n

My Website

\n
\n
\n

Welcome to my site!

\n
\n
\n

Copyright 2025

\n
", + "previewContainer": "preview-area", + "validations": [ + { + "type": "element_exists", + "value": "header", + "message": "Add a
element" + }, + { + "type": "element_exists", + "value": "main", + "message": "Add a
element" + }, + { + "type": "element_exists", + "value": "footer", + "message": "Add a