{
"$schema": "../schemas/code-crispies-module-schema.json",
"id": "html-svg",
"title": "HTML SVG",
"description": "Draw scalable vector graphics directly in HTML",
"mode": "html",
"difficulty": "beginner",
"lessons": [
{
"id": "svg-circle",
"title": "Drawing Circles",
"description": "SVG (Scalable Vector Graphics) lets you draw shapes directly in HTML. The <svg> element is the container, with width and height attributes.
Use <circle> with cx, cy (center) and r (radius) to draw circles.",
"task": "Create an SVG with a circle: 1. An <svg> with width=\"200\" and height=\"200\" 2. A <circle> centered at (100,100) with radius 50 3. Add a fill color",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f5f5f5; display: flex; justify-content: center; } svg { background: white; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); }",
"sandboxCSS": "",
"initialCode": "",
"solution": "",
"previewContainer": "preview-area",
"concept": {
"explanation": "SVG (Scalable Vector Graphics) defines graphics mathematically rather than as pixels, meaning shapes stay crisp at any zoom level or screen resolution—unlike raster images (PNG, JPG) that pixelate when scaled. The browser renders SVG by calculating shape geometry at display time: a circle at (100,100) with radius 50 is recomputed for each pixel density (1x, 2x, 3x displays). This makes SVG perfect for icons, logos, charts, and responsive graphics. SVG elements are DOM nodes like HTML elements, so you can style them with CSS (fill, stroke), animate them with CSS animations or JavaScript, and attach event listeners. The coordinate system starts at top-left (0,0), with x increasing rightward and y increasing downward.",
"diagram": "Vector vs Raster Graphics\n\nRaster (PNG/JPG):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nStores pixels:\n100x100 image = 10,000 pixels\n\n1x display: ■■■■ (crisp)\n2x display: ▪▪▪▪ (pixelated)\n\nVector (SVG):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nStores math:\n\n\n1x display: ● (crisp)\n2x display: ● (still crisp!)\n\nSVG Coordinate System:\n(0,0) ─────────→ x\n │\n │ (100,100)\n │ ● ← center\n │\n ↓\n y\n\nCircle Attributes:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\ncx=\"100\" → Center X coordinate\ncy=\"100\" → Center Y coordinate\nr=\"50\" → Radius (pixels)\nfill=\"steelblue\" → Interior color\n\nBenefits:\n✓ Resolution-independent\n✓ Small file size\n✓ CSS styleable\n✓ Animatable\n✓ Accessible (text labels)\n✓ DOM-scriptable"
},
"validations": [
{
"type": "element_exists",
"value": "svg",
"message": "Add an <svg> element"
},
{
"type": "element_exists",
"value": "circle",
"message": "Add a <circle> element inside the SVG"
},
{
"type": "attribute_value",
"value": { "selector": "svg", "attr": "width", "value": "200" },
"message": "Set width=\"200\" on the SVG"
},
{
"type": "attribute_value",
"value": { "selector": "svg", "attr": "height", "value": "200" },
"message": "Set height=\"200\" on the SVG"
},
{
"type": "attribute_value",
"value": { "selector": "circle", "attr": "cx", "value": "100" },
"message": "Set cx=\"100\" for the circle's horizontal center"
},
{
"type": "attribute_value",
"value": { "selector": "circle", "attr": "cy", "value": "100" },
"message": "Set cy=\"100\" for the circle's vertical center"
},
{
"type": "attribute_value",
"value": { "selector": "circle", "attr": "r", "value": "50" },
"message": "Set r=\"50\" for the circle's radius"
}
]
},
{
"id": "svg-rect-line",
"title": "Rectangles & Lines",
"description": "Draw rectangles with <rect> using x, y, width, height.
Draw lines with <line> using x1, y1 (start) and x2, y2 (end). Lines need a stroke color!",
"task": "Create an SVG with: 1. An <svg> (200x150) 2. A <rect> at position (20,20) with size 80x60 3. A <line> from (120,30) to (180,90) with a stroke color",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 200px; display: flex; justify-content: center; align-items: center; } svg { background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); }",
"sandboxCSS": "",
"initialCode": "",
"solution": "",
"previewContainer": "preview-area",
"concept": {
"explanation": "SVG distinguishes between filled shapes (solid interior) and stroked shapes (outline only) through fill and stroke attributes. Rectangles use top-left corner positioning (x, y) plus dimensions (width, height), while lines use start point (x1, y1) and end point (x2, y2) coordinates—no implicit fill for lines since they're one-dimensional. Lines require an explicit stroke to be visible because fill doesn't apply to one-dimensional paths. Stroke-width controls line thickness in user units (typically pixels), and additional stroke properties like stroke-linecap (round, square, butt) and stroke-linejoin (round, bevel, miter) control how stroke endpoints and corners render. SVG's presentation attributes (fill, stroke) can be overridden by CSS for dynamic styling.",
"diagram": "SVG Shape Positioning\n\nRectangle Coordinates:\n(0,0)\n ┌─────────────────→ x\n │ (20,20)\n │ ┌────────┐ ← x=\"20\" y=\"20\"\n │ │ │ width=\"80\"\n │ │ rect │ height=\"60\"\n │ └────────┘\n ↓\n y\n\nLine Coordinates:\n(x1,y1) (x2,y2)\n ●───────────────●\n(120,30) (180,90)\n ↑ start ↑ end\n\nFill vs Stroke:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n ← Solid interior\n\n ↑ interior ↑ outline\n\n ← Only visible\n ← Ignored!\n\nStroke Properties:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nstroke=\"color\"\nstroke-width=\"3\" → Thickness\nstroke-linecap=\"round\" → End shape\nstroke-dasharray=\"5,5\" → Dashed line\nstroke-opacity=\"0.5\" → Transparency\n\nPresentation Attributes:\nHTML: \nCSS: rect { fill: red; }\nCSS wins if both present!"
},
"validations": [
{
"type": "element_exists",
"value": "svg",
"message": "Add an <svg> element"
},
{
"type": "element_exists",
"value": "rect",
"message": "Add a <rect> element"
},
{
"type": "element_exists",
"value": "line",
"message": "Add a <line> element"
},
{
"type": "attribute_value",
"value": { "selector": "svg", "attr": "width", "value": "200" },
"message": "Set width=\"200\" on the SVG"
},
{
"type": "attribute_value",
"value": { "selector": "svg", "attr": "height", "value": "150" },
"message": "Set height=\"150\" on the SVG"
},
{
"type": "attribute_value",
"value": { "selector": "rect", "attr": "x", "value": "20" },
"message": "Set x=\"20\" on the rect"
},
{
"type": "attribute_value",
"value": { "selector": "rect", "attr": "y", "value": "20" },
"message": "Set y=\"20\" on the rect"
},
{
"type": "attribute_value",
"value": { "selector": "rect", "attr": "width", "value": "80" },
"message": "Set width=\"80\" on the rect"
},
{
"type": "attribute_value",
"value": { "selector": "rect", "attr": "height", "value": "60" },
"message": "Set height=\"60\" on the rect"
},
{
"type": "attribute_value",
"value": { "selector": "line", "attr": "x1", "value": "120" },
"message": "Set x1=\"120\" on the line"
},
{
"type": "attribute_value",
"value": { "selector": "line", "attr": "y1", "value": "30" },
"message": "Set y1=\"30\" on the line"
},
{
"type": "attribute_value",
"value": { "selector": "line", "attr": "x2", "value": "180" },
"message": "Set x2=\"180\" on the line"
},
{
"type": "attribute_value",
"value": { "selector": "line", "attr": "y2", "value": "90" },
"message": "Set y2=\"90\" on the line"
},
{
"type": "contains",
"value": "stroke",
"message": "Add a stroke color to the line"
}
]
},
{
"id": "svg-shapes",
"title": "Multiple Shapes",
"description": "Combine shapes to create simple graphics! Add stroke for outlines and stroke-width for thickness.
Use fill=\"none\" for hollow shapes. Shapes stack in order - later elements appear on top.",
"task": "Create a simple face: 1. An <svg> (200x200) 2. A large <circle> for the face 3. Two small <circle> elements for eyes 4. A <line> for the smile",
"previewHTML": "",
"previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); min-height: 250px; display: flex; justify-content: center; align-items: center; } svg { background: white; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); }",
"sandboxCSS": "",
"initialCode": "",
"solution": "",
"previewContainer": "preview-area",
"concept": {
"explanation": "SVG elements stack in source order like HTML—later elements render on top of earlier ones, creating a painter's algorithm where each shape is \"painted\" over previous shapes. This z-order control means shape placement in your markup defines layering: the face circle must come before the eye circles for eyes to appear on top. Unlike CSS z-index (which requires positioning context), SVG stacking is purely document-order based. You can group related shapes with elements for organizational purposes and apply transformations or styles to the entire group. SVG's declarative nature makes it ideal for programmatic generation: you can template SVG markup or use JavaScript to create/manipulate shapes dynamically, and the browser automatically handles rendering updates.",
"diagram": "SVG Stacking Order\n\nSource Order = Paint Order:\n\n\nVisual Result:\n Layer 4 (front)\n │\n Layer 3\n │\n Layer 2\n │\n Layer 1 (back)\n\nSmiley Face Example:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n1. Face (large circle)\n2. Left eye (small circle) ─┐\n3. Right eye (small circle) ├→ On top of face\n4. Smile (line) ─┘\n\nGrouping with :\n\n \n \n\n\nBenefits:\n✓ Apply transform to group\n✓ Style entire group\n✓ Semantic organization\n✓ Easy to show/hide\n\nDynamic SVG:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nJS: circle.setAttribute('r', 60);\n→ Browser re-renders instantly\n\nJS: svg.innerHTML += '';\n→ Add shapes dynamically\n\nvs Canvas:\nSVG = Retained mode (DOM)\nCanvas = Immediate mode (pixels)"
},
"validations": [
{
"type": "element_exists",
"value": "svg",
"message": "Add an <svg> element"
},
{
"type": "element_count",
"value": { "selector": "circle", "min": 3 },
"message": "Add at least 3 circles (1 face + 2 eyes)"
},
{
"type": "element_exists",
"value": "line",
"message": "Add a <line> for the smile"
}
]
}
]
}