From 386109733bd48f099fd6ac65824439197580ead6 Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Sun, 11 Jan 2026 14:55:43 +0100 Subject: [PATCH] auto-claude: 5.3 - Add explanations to details/summary, progress/mete --- lessons/23-html-details-summary.json | 12 ++++++++++++ lessons/24-html-progress-meter.json | 12 ++++++++++++ lessons/25-html-datalist.json | 8 ++++++++ lessons/26-html-data-attributes.json | 8 ++++++++ lessons/27-html-dialog.json | 8 ++++++++ lessons/28-html-forms-fieldset.json | 12 ++++++++++++ lessons/29-html-figure.json | 12 ++++++++++++ lessons/30-html-tables.json | 12 ++++++++++++ lessons/31-html-marquee.json | 12 ++++++++++++ lessons/32-html-svg.json | 12 ++++++++++++ 10 files changed, 108 insertions(+) diff --git a/lessons/23-html-details-summary.json b/lessons/23-html-details-summary.json index efa6170..acb72c2 100644 --- a/lessons/23-html-details-summary.json +++ b/lessons/23-html-details-summary.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "
\n Click to reveal\n

This content was hidden!

\n
", "previewContainer": "preview-area", + "concept": { + "explanation": "The details element is a native disclosure widget built into HTML5, meaning the browser handles all the show/hide logic without requiring JavaScript or CSS. When clicked, the browser toggles an internal 'open' state and applies native styling (like the disclosure triangle). This makes it accessible by default—screen readers announce it as an expandable section, and keyboard users can activate it with Enter or Space. The summary acts as a button that controls visibility of all sibling content inside the details element.", + "diagram": "Details/Summary Structure\n\n┌─────────────────────────────┐\n│
│ ← Container\n│ ▸ Question │ ← Clickable toggle\n│ │ (Browser renders ▸/▾)\n│

Answer text...

│\n│
│\n└─────────────────────────────┘\n\nClosed State (default):\n▸ Question\n\nOpen State:\n▾ Question\nAnswer text...\n\nBrowser Responsibilities:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n✓ Click handling\n✓ Keyboard support (Enter/Space)\n✓ Toggle arrow rendering\n✓ Screen reader announcements\n✓ Content show/hide\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + }, "validations": [ { "type": "element_exists", @@ -51,6 +55,10 @@ "initialCode": "
\n FAQ: What is HTML5?\n

HTML5 is the latest version of the HTML standard with new semantic elements and APIs.

\n
", "solution": "
\n FAQ: What is HTML5?\n

HTML5 is the latest version of the HTML standard with new semantic elements and APIs.

\n
", "previewContainer": "preview-area", + "concept": { + "explanation": "Boolean attributes in HTML represent on/off states where presence equals true. The 'open' attribute tells the browser to render the details element in its expanded state on page load, but users can still collapse it by clicking. This is different from CSS display control—removing the attribute doesn't hide the element entirely, it just sets the initial collapsed state. JavaScript can dynamically add/remove this attribute to programmatically control the disclosure state, which fires a 'toggle' event that developers can listen to.", + "diagram": "Boolean Attribute Behavior\n\nHTML Boolean Attributes:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
→ Expanded\n
→ Collapsed\n → Must be filled\n → Optional\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nThe 'open' Attribute:\n\n┌─────────────────────────────┐\n│
│ ← Attribute present\n│ ▾ Summary │ = Show content\n│ Content visible │\n│
│\n└─────────────────────────────┘\n\n┌─────────────────────────────┐\n│
│ ← Attribute absent\n│ ▸ Summary │ = Hide content\n│
│\n└─────────────────────────────┘\n\nDynamic Control:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nJS: element.open = true → Expand\nJS: element.open = false → Collapse\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + }, "validations": [ { "type": "attribute_value", @@ -70,6 +78,10 @@ "initialCode": "", "solution": "

Frequently Asked Questions

\n\n
\n What is HTML5?\n

HTML5 is the latest version of HTML with new semantic elements and APIs.

\n
\n\n
\n Do I need JavaScript?\n

Many interactive features work with pure HTML5, no JavaScript required!

\n
\n\n
\n Is this accessible?\n

Yes! Native HTML elements have built-in keyboard and screen reader support.

\n
", "previewContainer": "preview-area", + "concept": { + "explanation": "Unlike many JavaScript accordion libraries that only allow one open panel at a time, native details elements are independent by default—users can open as many as they want simultaneously. This behavior is more user-friendly because it doesn't force users to lose their place when exploring multiple topics. Each details element maintains its own open/closed state in the DOM, so users can bookmark or reload the page and the browser may restore the state. The lack of mutual exclusivity is a feature, not a bug—if you need exclusive accordion behavior, you must add JavaScript to close siblings when one opens.", + "diagram": "Independent Accordion Pattern\n\nTraditional JS Accordion (Exclusive):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n▸ Question 1\n▾ Question 2 ← Only one can be\n Answer... open at a time\n▸ Question 3\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nNative Details (Independent):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n▾ Question 1 ← Multiple can be\n Answer... open at once\n▾ Question 2 (User choice!)\n Answer...\n▸ Question 3\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nBenefits:\n✓ Compare multiple answers\n✓ Print all expanded content\n✓ No state management complexity\n✓ Browser handles persistence\n\nTo Make Exclusive:\nAdd JS to listen for 'toggle' event\nand close siblings when one opens" + }, "validations": [ { "type": "element_exists", diff --git a/lessons/24-html-progress-meter.json b/lessons/24-html-progress-meter.json index d8c2917..3f4a3e0 100644 --- a/lessons/24-html-progress-meter.json +++ b/lessons/24-html-progress-meter.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "\n70%", "previewContainer": "preview-area", + "concept": { + "explanation": "The progress element represents completion of a task with a known duration or endpoint—the browser calculates the fill percentage by dividing value by max. Browsers render progress bars with native OS styling by default, which means they look different on Windows, macOS, iOS, and Android, giving each platform's users a familiar appearance. Screen readers announce the completion percentage automatically (\"70 percent\"), and the element has an implicit ARIA role of 'progressbar'. JavaScript can update the value attribute dynamically to reflect real-time progress, and the text content inside serves as fallback for browsers that don't support progress.", + "diagram": "Progress Element Calculation\n\nFormula:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nFill % = (value / max) × 100\n\nExample:\n\n\n 70 ÷ 100 = 0.7 → 70% filled\n\n┌─────────────────────────────┐\n│ Download: │\n├─────────────────────────────┤\n│ ████████████████░░░░░░░░░░ │ 70%\n└─────────────────────────────┘\n ← 70 units → ← 30 units →\n\nUpdating Progress:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nJS: element.value = 50 → 50%\nJS: element.value = 100 → 100%\n\nAccessibility:\nScreen reader announces:\n\"Download, progress bar, 70 percent\"\n\nFallback Content:\n\n 70% ← Shown in old browsers\n" + }, "validations": [ { "type": "element_exists", @@ -51,6 +55,10 @@ "initialCode": "", "solution": "

Loading...

\n", "previewContainer": "preview-area", + "concept": { + "explanation": "Indeterminate state communicates \"something is happening, but we don't know when it will finish\"—browsers render this with an animated pattern that moves continuously to show activity without implying a specific completion percentage. The animation style is platform-native: macOS uses a barber-pole stripe pattern, Windows uses a pulsing dot animation, and browsers may customize the appearance. This semantic distinction matters because it sets correct user expectations—a filled bar implies \"almost done\" while an animated loop implies \"still working\". Screen readers announce indeterminate progress as \"progress bar, busy\" rather than announcing a percentage.", + "diagram": "Determinate vs Indeterminate\n\nDeterminate (value present):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n┌─────────────────────────────┐\n│ ████████████████░░░░░░░░░░ │ 70%\n└─────────────────────────────┘\n ↑ Known progress\n\nIndeterminate (no value):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n┌─────────────────────────────┐\n│ ░░▓▓▓░░░░░░░░░░░░░░░░░░░░░ │ Animating\n└─────────────────────────────┘\n ↑ Unknown duration\n\nBrowser Animations:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nmacOS → Barber-pole stripes\nWindows → Pulsing dots\nAndroid → Circular spinner\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nUse Cases:\n✓ Network requests\n✓ File processing\n✓ Background sync\n✓ Unknown wait time\n\nScreen Reader:\n\"Progress bar, busy\"" + }, "validations": [ { "type": "element_exists", @@ -75,6 +83,10 @@ "initialCode": "", "solution": "\n80%", "previewContainer": "preview-area", + "concept": { + "explanation": "Unlike progress (which shows task completion moving toward 100%), meter represents a measurement at a point in time that can be good or bad depending on context. The browser uses low/high/optimum thresholds to automatically color-code the gauge: green when value is near optimum, yellow in the middle range, and red when critically low or high. For example, battery at 80% is green (good), 40% is yellow (warning), and 10% is red (critical). This semantic intelligence means you don't need CSS—the browser applies appropriate colors based on your threshold values and whether higher or lower is better.", + "diagram": "Meter Threshold Logic\n\nAttributes:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nmin=\"0\" → Range start\nmax=\"1\" → Range end\nlow=\"0.2\" → Below this = bad\nhigh=\"0.8\" → Above this = depends\noptimum=\"1\" → Ideal value\nvalue=\"0.8\" → Current measurement\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nAutomatic Color Zones:\n\nBattery (optimum=high):\n┌─────────────────────────────┐\n│ 0.0 ────────────────── 1.0 │\n│ RED YELLOW GREEN │\n│ 0─0.2 0.2─0.8 0.8─1.0 │\n└─────────────────────────────┘\n ↑ value=0.8 (green)\n\nDisk Usage (optimum=low):\n┌─────────────────────────────┐\n│ 0% ─────────────────── 100% │\n│ GREEN YELLOW RED │\n│ 0─20 20─80 80─100 │\n└─────────────────────────────┘\n ↑ 90% (red)\n\nvs Progress:\nmeter → Snapshot measurement\nprogress → Task moving to 100%" + }, "validations": [ { "type": "element_exists", diff --git a/lessons/25-html-datalist.json b/lessons/25-html-datalist.json index 10b71c6..d86a84a 100644 --- a/lessons/25-html-datalist.json +++ b/lessons/25-html-datalist.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "\n\n\n ", "previewContainer": "preview-area", + "concept": { + "explanation": "Datalist provides native autocomplete without requiring JavaScript—the browser handles all the filtering, dropdown rendering, and selection logic. When users type, the browser automatically shows matching options using substring matching (typing \"fir\" shows \"Firefox\"). Unlike a select element that restricts users to predefined choices, datalist is advisory-only: users can ignore all suggestions and enter custom text, making it perfect for \"common values but allow anything\" scenarios. The dropdown styling is platform-native and keyboard-accessible (arrow keys to navigate, Enter to select), with full screen reader support announcing \"combobox with X suggestions\".", + "diagram": "Datalist vs Select\n\nDatalist (Flexible):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\n \n\n┌─────────────────────────────┐\n│ Fir_ │ ← User types\n├─────────────────────────────┤\n│ ▾ Suggestions: │\n│ Firefox │ ← Filtered\n└─────────────────────────────┘\n✓ Can type \"Brave\" (not listed)\n✓ Suggestions are optional\n\nSelect (Restricted):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\n┌─────────────────────────────┐\n│ Chrome ▾ │\n├─────────────────────────────┤\n│ Chrome │\n│ Firefox │\n└─────────────────────────────┘\n✗ Can't type custom value\n✗ Must pick from list\n\nBrowser Handles:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n✓ Substring filtering\n✓ Dropdown rendering\n✓ Keyboard navigation\n✓ Screen reader announcements" + }, "validations": [ { "type": "element_exists", @@ -51,6 +55,10 @@ "initialCode": "", "solution": "\n\n\n ", "previewContainer": "preview-area", + "concept": { + "explanation": "Datalists scale exceptionally well for long lists because filtering happens client-side without network requests—the browser holds all options in memory and filters instantly as the user types. This makes it ideal for common datasets like countries, states, or product names where you have hundreds of options but don't want to overwhelm users with a massive dropdown. Unlike select elements that require scrolling through all options, datalist progressively narrows results: typing \"ger\" in a 200-country list instantly shows only \"Germany\", \"Algeria\", and \"Niger\". This progressive disclosure pattern improves usability while maintaining full keyboard and screen reader accessibility.", + "diagram": "Progressive Filtering\n\nInitial State:\n┌─────────────────────────────┐\n│ Country: _ │\n└─────────────────────────────┘\n(No dropdown shown)\n\nUser types \"G\":\n┌─────────────────────────────┐\n│ Country: G_ │\n├─────────────────────────────┤\n│ ▾ Suggestions: │\n│ Germany │\n│ Georgia │\n│ Ghana │\n│ Greece │\n└─────────────────────────────┘\n\nUser types \"Ge\":\n┌─────────────────────────────┐\n│ Country: Ge_ │\n├─────────────────────────────┤\n│ ▾ Suggestions: │\n│ Germany │ ← Narrowed to 2\n│ Georgia │\n└─────────────────────────────┘\n\nUser types \"Ger\":\n┌─────────────────────────────┐\n│ Country: Ger_ │\n├─────────────────────────────┤\n│ ▾ Suggestions: │\n│ Germany │ ← Only 1 match\n└─────────────────────────────┘\n\nPerformance:\n✓ No network requests\n✓ Instant client-side filtering\n✓ Scales to hundreds of options\n✓ No JavaScript required" + }, "validations": [ { "type": "element_exists", diff --git a/lessons/26-html-data-attributes.json b/lessons/26-html-data-attributes.json index a8e03c2..593e38b 100644 --- a/lessons/26-html-data-attributes.json +++ b/lessons/26-html-data-attributes.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "
\n

Laptop

\n

A powerful laptop for work and play.

\n
\n\n
\n

T-Shirt

\n

A comfortable cotton t-shirt.

\n
", "previewContainer": "preview-area", + "concept": { + "explanation": "Data attributes provide a standards-compliant way to embed custom metadata directly in HTML without inventing non-standard attributes or abusing existing ones like class or id. The browser ignores data-* attributes for rendering but preserves them in the DOM, making them accessible to JavaScript and CSS. Unlike storing data in JavaScript variables or hidden divs, data attributes keep information with the element it describes, improving maintainability. JavaScript can read them via element.dataset (data-category becomes dataset.category), and CSS can select or display them using attribute selectors and attr(). This pattern separates presentation (CSS classes) from data (data attributes), following the principle of separation of concerns.", + "diagram": "Data Attribute Access\n\nHTML:\n
\n\nJavaScript Access:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nconst el = document.querySelector('article');\nel.dataset.category → \"electronics\"\nel.dataset.price → \"299\"\nel.dataset.inStock → \"true\"\n\n(Hyphens become camelCase)\ndata-in-stock → dataset.inStock\n\nCSS Access:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n/* Select by attribute */\narticle[data-category=\"electronics\"] {\n border-color: blue;\n}\n\n/* Display value */\narticle::after {\n content: \"€\" attr(data-price);\n}\n\nvs Other Approaches:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n✗ class=\"electronics price-299\"\n → Mixes presentation & data\n✗
\n → Abuses id attribute\n✓ data-category=\"electronics\"\n → Semantic & maintainable" + }, "validations": [ { "type": "element_count", @@ -46,6 +50,10 @@ "initialCode": "
    \n \n
", "solution": "
    \n
  • Buy groceries
  • \n
  • Finish homework
  • \n
  • Call mom
  • \n
", "previewContainer": "preview-area", + "concept": { + "explanation": "CSS attribute selectors enable state-based styling without adding/removing classes via JavaScript—you just change the attribute value and CSS reactivity handles the rest. The selector [data-status='active'] has the same specificity as a class (0,0,1,0), making it equally powerful but more semantic for data-driven states. This pattern shines in component libraries and SPAs where state changes frequently: updating one attribute triggers CSS transitions and visual changes automatically. Unlike classes that describe presentation (\"button-blue\"), data attributes describe meaning (\"status=active\"), and CSS translates meaning to presentation, keeping your HTML semantic and your styling decoupled from implementation details.", + "diagram": "CSS Attribute Selectors\n\nAttribute Selector Syntax:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n[data-status] → Has attribute\n[data-status=\"active\"] → Exact match\n[data-status^=\"act\"] → Starts with\n[data-status$=\"ed\"] → Ends with\n[data-status*=\"iv\"] → Contains\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nState-Based Styling:\n\n
  • \n ↓ CSS applies\nli[data-status=\"pending\"] {\n background: lightblue;\n}\n\nJS changes state:\nel.dataset.status = \"active\"\n ↓ CSS automatically updates\nli[data-status=\"active\"] {\n background: orange;\n}\n\nvs Class Approach:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\ndata-status=\"active\"\n✓ Semantic (describes state)\n✓ One attribute to update\n✓ Clear data meaning\n\nclass=\"task active pending\"\n✗ Presentational\n✗ Multiple classes to manage\n✗ Unclear which is data\n\nSpecificity:\n[data-status=\"active\"] = .active\nBoth have 0,0,1,0 specificity" + }, "validations": [ { "type": "element_exists", diff --git a/lessons/27-html-dialog.json b/lessons/27-html-dialog.json index f31ddd0..22de0da 100644 --- a/lessons/27-html-dialog.json +++ b/lessons/27-html-dialog.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "\n

    Welcome!

    \n

    This is a native HTML dialog element.

    \n
    \n \n
    \n
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "The dialog element is a native modal/popup that the browser manages entirely—it handles backdrop rendering, focus trapping, Escape key closing, and scroll locking on the body without any JavaScript. When opened with showModal(), the browser creates an ::backdrop pseudo-element (styled by CSS), traps keyboard focus inside the dialog (Tab cycles through dialog elements only), and prevents interaction with background content. The form method=\"dialog\" pattern leverages native form submission to close the dialog: any button inside submits the form, which closes the dialog and returns the button's value via the dialog's returnValue property. This replaces thousands of lines of modal library code with semantic HTML.", + "diagram": "Dialog Mechanics\n\nNative Modal Features:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n✓ Backdrop rendering\n✓ Focus trapping (Tab loops)\n✓ Escape key closes\n✓ Body scroll lock\n✓ Top-layer rendering\n✓ Screen reader isolation\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nDialog Opening Methods:\n\nshowModal() → Modal dialog\n ↓\n ┌─────────────────────┐\n │ [Backdrop overlay] │\n │ ┌───────────────┐ │\n │ │ │ │\n │ │ Content │ │\n │ └───────────────┘ │\n └─────────────────────┘\n Focus trapped, Esc closes\n\nshow() → Non-modal dialog\n ↓\n ┌───────────────┐\n │ │\n │ Content │ ← Floats above\n └───────────────┘\n Can interact with background\n\nForm Method=\"dialog\":\n
    \n \n \n
    \n ↓ Clicking either button\n 1. Submits form\n 2. Closes dialog\n 3. Sets dialog.returnValue" + }, "validations": [ { "type": "element_exists", @@ -56,6 +60,10 @@ "initialCode": "", "solution": "\n

    Confirm Delete

    \n

    Are you sure you want to delete this item?

    \n
    \n \n \n
    \n
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "The combination of dialog + form method=\"dialog\" creates a confirmation pattern where button values become the dialog's return value, letting you distinguish which button was clicked. When a user clicks a button in a method=\"dialog\" form, three things happen atomically: the form submits (triggering submit event), the dialog closes (triggering close event), and dialog.returnValue is set to the clicked button's value attribute. This pattern is perfect for yes/no confirmations or multi-choice prompts where you need to know the user's decision. Unlike window.confirm() which blocks the entire page and looks dated, dialog provides a customizable, non-blocking, accessible alternative that fits modern design systems.", + "diagram": "Dialog Return Values\n\nHTML:\n\n
    \n \n \n
    \n
    \n\nExecution Flow:\n\nUser clicks \"Delete\" button\n ↓\n1. Form submits\n (submit event fires)\n ↓\n2. Dialog closes\n (close event fires)\n ↓\n3. returnValue set\n dialog.returnValue = \"delete\"\n\nJavaScript Usage:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nconst dialog = document.querySelector('#confirm');\ndialog.showModal();\n\ndialog.addEventListener('close', () => {\n if (dialog.returnValue === 'delete') {\n // User confirmed\n deleteItem();\n } else {\n // User cancelled\n }\n});\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nvs window.confirm():\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nwindow.confirm() → Blocks page\n → Ugly native UI\n → No customization\n\n → Non-blocking\n → Styleable\n → Accessible" + }, "validations": [ { "type": "element_exists", diff --git a/lessons/28-html-forms-fieldset.json b/lessons/28-html-forms-fieldset.json index 24d528b..c609240 100644 --- a/lessons/28-html-forms-fieldset.json +++ b/lessons/28-html-forms-fieldset.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "
    \n
    \n Personal Info\n \n \n \n \n
    \n
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "Fieldset creates a semantic grouping that browsers and assistive technology understand as related form controls, not just a visual border. When a screen reader enters a fieldset, it announces the legend before each control (\"Personal Info, Name, edit text\"), providing context without repetition in every label. The browser also establishes a form control context: disabling the fieldset (disabled attribute) automatically disables all inputs inside, and form validation can be scoped to fieldsets. This semantic structure is crucial for complex forms—it transforms a flat list of inputs into a hierarchical document with clear relationships, improving both accessibility and maintainability.", + "diagram": "Fieldset Semantic Structure\n\nHTML:\n
    \n
    \n Personal Info\n \n \n
    \n
    \n\nScreen Reader Experience:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\"Personal Info, group\"\n → Focus first input\n \"Personal Info, Name, edit text\"\n → Tab to next input\n \"Personal Info, Email, edit text\"\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nContext announced once, then reused\n\nDisabled Propagation:\n\n
    \n ← Automatically disabled\n ← Automatically disabled\n
    \n\nvs Individual Disabling:\n\n ← Must repeat\n\nForm Structure:\n\nFlat (no grouping):\n✗ Name\n✗ Email\n✗ Street\n✗ City\n\nGrouped (semantic):\n✓ Personal Info\n ✓ Name\n ✓ Email\n✓ Address\n ✓ Street\n ✓ City" + }, "validations": [ { "type": "element_exists", @@ -56,6 +60,10 @@ "initialCode": "", "solution": "
    \n
    \n Contact Us\n \n \n \n \n \n
    \n
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "The textarea element is designed for multi-line text input, automatically providing scroll bars when content exceeds its dimensions and preserving line breaks and whitespace on submission. Unlike input elements which ignore Enter key (using it for form submission), textarea captures Enter as a newline character, making it suitable for addresses, comments, messages, or any free-form text. The rows and cols attributes set initial dimensions as character counts (rows for lines, cols for width in characters), but CSS width/height override these. Textarea is a container element (not self-closing), so you must use <textarea>content</textarea> syntax—any text between the tags becomes the initial value, preserving formatting.", + "diagram": "Textarea vs Input\n\nInput (single-line):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\n┌─────────────────────────────┐\n│ Hello_ │\n└─────────────────────────────┘\n✗ Enter → Submits form\n✗ No line breaks\n✗ Self-closing tag\n\nTextarea (multi-line):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\n┌─────────────────────────────┐\n│ Hello │\n│ World_ │\n│ │ ← rows=\"3\"\n└─────────────────────────────┘\n✓ Enter → New line\n✓ Preserves line breaks\n✓ Container element\n✓ Auto-scrolls if overflowing\n\nSizing:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nrows=\"4\" → 4 lines tall\ncols=\"40\" → 40 chars wide\nCSS overrides → width/height\n\nValue Syntax:\n\nvs\n" + }, "validations": [ { "type": "element_exists", @@ -95,6 +103,10 @@ "initialCode": "", "solution": "
    \n
    \n Account Info\n \n \n \n \n
    \n
    \n Preferences\n \n \n
    \n \n
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "Multiple fieldsets divide long forms into logical sections, improving cognitive load by chunking related fields together—users process \"fill out personal info\" and \"fill out address\" as distinct mental tasks rather than one overwhelming list. This pattern also enables progressive disclosure: you can hide/show fieldsets as wizard steps, disable future sections until current ones validate, or use CSS to style sections differently based on state. Screen readers announce fieldset boundaries (\"entering Personal Info group\", \"leaving Personal Info group\"), helping users maintain their place in complex forms. The semantic structure also aids form analytics: you can track which sections users struggle with or abandon most frequently.", + "diagram": "Multi-Fieldset Forms\n\nSingle Long Form:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n┌─────────────────────────────┐\n│ Name: [ ] │\n│ Email: [ ] │\n│ Username: [ ] │ Overwhelming\n│ Password: [ ] │ 8 fields at once\n│ Street: [ ] │\n│ City: [ ] │\n│ State: [ ] │\n│ Bio: [ ] │\n│ [Submit] │\n└─────────────────────────────┘\n\nChunked with Fieldsets:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n┌─────────────────────────────┐\n│ Personal Info │ Section 1\n│ Name: [ ] │ (2 fields)\n│ Email: [ ] │\n├─────────────────────────────┤\n│ Account │ Section 2\n│ Username: [ ] │ (2 fields)\n│ Password: [ ] │\n├─────────────────────────────┤\n│ Address │ Section 3\n│ Street: [ ] │ (3 fields)\n│ City: [ ] │\n│ State: [ ] │\n├─────────────────────────────┤\n│ About You │ Section 4\n│ Bio: [ ] │ (1 field)\n├─────────────────────────────┤\n│ [Submit] │\n└─────────────────────────────┘\n\nBenefits:\n✓ Reduced cognitive load\n✓ Clear visual hierarchy\n✓ Section-level validation\n✓ Progressive disclosure\n✓ Better analytics" + }, "validations": [ { "type": "element_count", diff --git a/lessons/29-html-figure.json b/lessons/29-html-figure.json index dcd5cb5..7d5c84e 100644 --- a/lessons/29-html-figure.json +++ b/lessons/29-html-figure.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "
    \n \"A\n
    A beautiful mountain landscape at sunset.
    \n
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "The figure element semantically marks self-contained content that's referenced from the main flow but could be moved elsewhere (like a sidebar or appendix) without losing meaning. Figcaption provides an accessible label that screen readers announce when encountering the figure, establishing a programmatic relationship between image and caption that's stronger than visual proximity alone. Unlike an img followed by a p, figure+figcaption creates an accessibility API relationship: AT announces \"figure\" when entering, reads the caption, then describes the image, giving users complete context. Search engines also parse this relationship, using figcaption content to understand image meaning for image search results and context-aware rankings.", + "diagram": "Figure Semantic Relationship\n\nRegular Image + Text:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\"Mountain\"\n

    A beautiful mountain.

    \n\n✗ No semantic link\n✗ SR: \"Mountain image\" then \"A beautiful mountain\"\n✗ Caption could apply to any nearby content\n\nFigure + Figcaption:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
    \n \"Mountain\"\n
    A beautiful mountain.
    \n
    \n\n✓ Semantic relationship\n✓ SR: \"Figure. Mountain image. A beautiful mountain.\"\n✓ Caption explicitly bound to image\n\nScreen Reader Flow:\n┌─────────────────────────────┐\n│
    │ → \"Entering figure\"\n│ \"Mountain\" │ → \"Mountain, image\"\n│
    │\n│ A beautiful mountain │ → \"A beautiful mountain\"\n│
    │\n│
    │ → \"Leaving figure\"\n└─────────────────────────────┘\n\nSEO Benefits:\n✓ Image-caption binding\n✓ Better image search results\n✓ Context for visually similar images" + }, "validations": [ { "type": "element_exists", @@ -46,6 +50,10 @@ "initialCode": "", "solution": "
    \n
    function greet(name) {\n  return `Hello, ${name}!`;\n}
    \n
    A simple greeting function in JavaScript
    \n
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "Figure isn't limited to images—it's for any self-contained content like code samples, charts, diagrams, poems, or quotes. The semantic meaning is \"referenced content with a caption\", not \"photo with text\". When wrapping code in figure, you establish that this code block is an example being discussed, not inline code to execute. Screen readers announce \"figure\" before the code, signaling to users that this is illustrative content they may want to skip if they're scanning. The figcaption describes what the code does or why it's shown, helping users decide whether to read it in detail—crucial for technical documentation where code blocks can be numerous and lengthy.", + "diagram": "Figure Use Cases\n\nImages:\n
    \n \n
    Sales 2024
    \n
    \n\nCode Samples:\n
    \n
    function add() {}
    \n
    Addition function
    \n
    \n\nQuotes:\n
    \n
    To be or not to be
    \n
    — Shakespeare
    \n
    \n\nPoems:\n
    \n

    Roses are red

    \n

    Violets are blue

    \n
    — Anonymous
    \n
    \n\nDiagrams (SVG):\n
    \n ...\n
    System architecture
    \n
    \n\nCommon Pattern:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n✓ Content that illustrates a point\n✓ Content referenced by main text\n✓ Content with a description/attribution\n✓ Self-contained units\n\n✗ Not for decorative images\n✗ Not for inline content\n✗ Not for UI elements" + }, "validations": [ { "type": "element_exists", @@ -80,6 +88,10 @@ "initialCode": "", "solution": "
    \n \"Photo\n \"Photo\n \"Photo\n \"Photo\n
    My vacation photo gallery
    \n
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "A single figure can contain multiple related elements when they collectively form one logical unit, like a photo gallery, before/after comparison, or multi-angle product shots. The figcaption describes the entire collection rather than individual items, establishing that these pieces should be understood together. This pattern is semantically different from multiple separate figures—it communicates \"these images are facets of one concept\" versus \"here are several independent illustrations\". Screen readers announce one figure containing multiple images, then read the collective caption, helping users understand the grouping. Search engines also interpret this structure, understanding that images within a figure are related for relevance ranking.", + "diagram": "Single vs Multiple Figures\n\nMultiple Separate Figures:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
    \n \n
    Paris
    \n
    \n
    \n \n
    London
    \n
    \n→ Two independent illustrations\n\nSingle Figure Gallery:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
    \n \n \n \n
    European cities
    \n
    \n→ One concept with multiple views\n\nUse Cases:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n✓ Photo gallery (vacation pics)\n✓ Before/after comparison\n✓ Product: front, side, back views\n✓ Step-by-step process images\n✓ Multi-panel comics/diagrams\n\nScreen Reader:\n\"Figure containing 4 images\"\n→ Image 1: \"paris.jpg\"\n→ Image 2: \"london.jpg\"\n→ Image 3: \"rome.jpg\"\n→ Image 4: \"berlin.jpg\"\n\"Caption: European cities\"" + }, "validations": [ { "type": "element_exists", diff --git a/lessons/30-html-tables.json b/lessons/30-html-tables.json index 2d0c6d0..ee7dec8 100644 --- a/lessons/30-html-tables.json +++ b/lessons/30-html-tables.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "\n \n \n \n \n \n \n \n \n \n \n \n \n \n
    Fruit Prices
    FruitPrice
    Apple$1.50
    Banana$0.75
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "HTML tables communicate semantic data relationships through row/column structure, enabling screen readers to navigate two-dimensionally (announcing \"row 2, column 1: Apple\" or \"Fruit column, row 2\") instead of linearly reading cells. The th (header cell) vs td (data cell) distinction creates accessibility associations: screen readers remember headers and announce them when navigating data cells, giving context. Caption provides a programmatic table title that screen readers announce before entering the table structure. The table element has implicit ARIA role=\"table\", and browsers expose table semantics through accessibility APIs, allowing AT users to jump between tables, skip table content, or navigate by row/column.", + "diagram": "Table Semantic Structure\n\n\n \n ← Row 1 (header)\n ← Column 1 header\n ← Column 2 header\n \n ← Row 2 (data)\n ← Data cell\n \n \n
    Fruit Prices
    FruitPrice
    Apple$1.50
    \n\nScreen Reader Navigation:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\"Table, Fruit Prices\"\n\"2 columns, 2 rows\"\n\nEnter table:\n\"Row 1, Column 1: Fruit, header\"\n→ Right arrow\n\"Row 1, Column 2: Price, header\"\n→ Down arrow\n\"Row 2, Column 2: $1.50\"\n(Still remembers \"Price\" header)\n\nHeader Association:\n┌─────────┬─────────┐\n│ Fruit │ Price │ ← th elements\n├─────────┼─────────┤\n│ Apple │ $1.50 │\n└─────────┴─────────┘\n ↑ ↑\n └─────────┘\n When SR focuses on\n \"$1.50\", it announces:\n \"Price: $1.50, row 2\"\n\nvs div Table:\n✗ No semantic structure\n✗ Linear reading only\n✗ No header association" + }, "validations": [ { "type": "element_exists", @@ -51,6 +55,10 @@ "initialCode": "", "solution": "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    Monthly Sales
    MonthRevenue
    January$12,500
    February$14,200
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "The thead/tbody/tfoot elements create logical sections that browsers can optimize for printing (repeating headers on each page), scrolling (sticky headers while tbody scrolls), and accessibility (screen readers announce section boundaries). This grouping also enables CSS to style sections differently without classes—tbody tr:hover works naturally. Some browsers display thead/tfoot with distinct styling by default. Screen readers announce section transitions (\"entering table header\", \"entering table body\") helping users understand where they are in large tables. For very long tables, browsers may keep thead fixed while scrolling tbody, and when printing multi-page tables, browsers repeat thead at the top of each printed page automatically.", + "diagram": "Table Section Structure\n\n\n \n ← Header section\n \n \n ← Data section\n \n \n ...(many rows)\n \n ← Footer section\n \n \n
    Sales Data
    Month
    Jan
    Feb
    Total
    \n\nPrinting Long Tables:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nPage 1:\n┌─────────────┐\n│ Month │ ← thead (repeated)\n├─────────────┤\n│ Jan │\n│ Feb │ tbody continues...\n\nPage 2:\n┌─────────────┐\n│ Month │ ← thead (repeated)\n├─────────────┤\n│ Mar │\n│ Apr │ tbody continues...\n\nScrolling Long Tables:\n┌─────────────────────────────┐\n│ Month │ Revenue │ Fixed │ ← thead sticky\n├───────────┴─────────┴───────┤\n│ Jan │ $10,000 │ ↕\n│ Feb │ $12,000 │ Scrolls\n│ Mar │ $11,500 │ ↕\n│ ... │\n└─────────────────────────────┘\n\nScreen Reader:\n\"Entering table header\"\n→ Reads headers\n\"Entering table body\"\n→ Reads data rows\n\"Entering table footer\"\n→ Reads totals" + }, "validations": [ { "type": "element_exists", @@ -90,6 +98,10 @@ "initialCode": "", "solution": "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    Order Summary
    ItemPrice
    Widget$25.00
    Gadget$35.00
    Total$60.00
    ", "previewContainer": "preview-area", + "concept": { + "explanation": "The tfoot element defines summary or calculation rows that semantically belong at the table's end, even though in HTML source order it can appear before tbody (browsers render it at the bottom regardless). This location flexibility is historical—placing tfoot before tbody in source allows browsers to render footers before receiving all body data, useful for streaming large datasets. Screen readers announce tfoot as \"table footer\" when entering, signaling that this row contains aggregate data rather than individual records. Tfoot is ideal for totals, averages, counts, or any row that summarizes the data above—it gives these special rows semantic meaning that plain tbody rows lack.", + "diagram": "Tfoot Source Order Flexibility\n\nHTML Source Order (optional):\n\n ...\n ... ← Before tbody\n ...\n
    \n\nBrowser Renders:\n┌─────────────────────────────┐\n│ thead (headers) │\n├─────────────────────────────┤\n│ tbody (data rows) │\n│ ... │\n├─────────────────────────────┤\n│ tfoot (totals) │ ← Rendered last\n└─────────────────────────────┘\n\nWhy Allow tfoot Before tbody?\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nStreaming Large Datasets:\n1. Send \n2. Send \n3. Stream (may take time)\n→ Footer renders before all data\n\nSemantic Meaning:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n → Individual records\n → Aggregate summary\n\nScreen Reader:\n\"Table footer, row 1\"\n\"Total, $60.00\"\n\nCommon tfoot Content:\n✓ Totals/Subtotals\n✓ Averages\n✓ Record counts\n✓ Summary calculations\n\n✗ Not for regular data rows\n✗ Not for pagination controls" + }, "validations": [ { "type": "element_exists", diff --git a/lessons/31-html-marquee.json b/lessons/31-html-marquee.json index 29c0d99..f88513f 100644 --- a/lessons/31-html-marquee.json +++ b/lessons/31-html-marquee.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "Welcome to my website!", "previewContainer": "preview-area", + "concept": { + "explanation": "Marquee is a non-standard HTML element introduced by Netscape in the 1990s that created auto-scrolling text without JavaScript—browsers handled animation entirely natively. While deprecated by W3C standards (never officially standardized), browsers still support it for backward compatibility with legacy websites. The element teaches an important web history lesson: browser vendors sometimes implement features unilaterally, and if popular enough, those features persist even after standards bodies reject them. Modern development uses CSS animations (`@keyframes` + `animation`) or JavaScript for scrolling effects, giving developers more control and adhering to web standards, but marquee demonstrates how declarative HTML can embed behavior.", + "diagram": "Marquee: A Web History Lesson\n\nTimeline:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n1995 → Netscape adds \n1996 → IE copies it (vendor wars)\n2000s → W3C: \"This is non-standard\"\n2024 → Still works! (legacy compat)\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nHow It Works:\nText\n ↓\nBrowser sees marquee element\n ↓\nNative animation engine starts\n ↓\nText scrolls without JS/CSS\n\nModern Equivalent:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
    Text
    \n\n.scroll {\n animation: scroll 10s linear infinite;\n}\n\n@keyframes scroll {\n from { transform: translateX(100%); }\n to { transform: translateX(-100%); }\n}\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nWhy Deprecated?\n✗ Non-standard (vendor-specific)\n✗ Accessibility issues (motion)\n✗ Limited control\n✗ No standard API\n\n✓ Use CSS animations instead\n✓ Add prefers-reduced-motion\n✓ Full control over timing/easing" + }, "validations": [ { "type": "element_exists", @@ -36,6 +40,10 @@ "initialCode": "", "solution": "Bounce! Bounce! Bounce!", "previewContainer": "preview-area", + "concept": { + "explanation": "Marquee attributes control animation parameters through HTML rather than CSS or JavaScript—this declarative approach was innovative for the 1990s but inflexible by modern standards. The behavior attribute changes motion physics: 'scroll' creates continuous looping (exits one side, enters opposite), 'slide' stops when reaching the edge (one-time animation), and 'alternate' bounces back and forth (ping-pong effect). Direction controls axis (left/right for horizontal, up/down for vertical), and scrollamount sets pixels moved per frame (higher = faster). These attributes demonstrate early attempts at animation control before CSS animations existed, showing how HTML sometimes blurred the line between structure and presentation.", + "diagram": "Marquee Behaviors\n\nbehavior=\"scroll\" (default):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n→ Text → → → → → (loops)\n\nbehavior=\"slide\":\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n→ Text → → STOP\n(animates once, stops at edge)\n\nbehavior=\"alternate\":\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n→ Text → → ← ← Text ← ←\n(bounces back and forth)\n\nDirection:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\ndirection=\"left\" → Text moves ←\ndirection=\"right\" → Text moves →\ndirection=\"up\" → Text moves ↑\ndirection=\"down\" → Text moves ↓\n\nSpeed Control:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nscrollamount=\"1\" → Slow (1px/frame)\nscrollamount=\"6\" → Default\nscrollamount=\"20\" → Fast (20px/frame)\n\nCombinations:\n\n Bounces horizontally at 10px/frame\n" + }, "validations": [ { "type": "element_exists", @@ -60,6 +68,10 @@ "initialCode": "", "solution": "BREAKING NEWS: Marquee element still works in browsers!", "previewContainer": "preview-area", + "concept": { + "explanation": "Marquee represents a cautionary tale about web standards versus implementation reality: despite being deprecated for over a decade and never formally standardized, browsers maintain support because removing it would break thousands of legacy websites. This teaches the web's core principle of \"don't break the web\"—backward compatibility trumps clean standards. Modern developers should avoid marquee for accessibility reasons (motion can trigger vestibular disorders, and it ignores prefers-reduced-motion), lack of control (can't pause on hover, sync with other animations, or adjust timing curves), and semantic incorrectness (mixing behavior into structure). Instead, CSS animations provide the same visual effects with full control, accessibility hooks, and standards compliance.", + "diagram": "Legacy Compat vs Modern Standards\n\nThe \"Don't Break the Web\" Principle:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n1995 → Site uses \n2024 → Site still online (unchanged)\n → Browser must still support it\n → Can't remove deprecated feature\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nAccessibility Issues:\n✗ Triggers motion sickness\n✗ Ignores prefers-reduced-motion\n✗ Can't pause on hover\n✗ Distracts from content\n✗ Not keyboard-accessible\n\nModern Replacement:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nHTML:\n
    BREAKING NEWS
    \n\nCSS:\n@media (prefers-reduced-motion: no-preference) {\n .ticker {\n animation: scroll 10s linear infinite;\n }\n}\n\n@keyframes scroll {\n from { transform: translateX(100%); }\n to { transform: translateX(-100%); }\n}\n\n.ticker:hover {\n animation-play-state: paused;\n}\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nLesson Learned:\n✓ Standards matter\n✓ Accessibility first\n✓ Use CSS for presentation\n✓ Don't mix behavior into HTML\n✓ Respect user preferences" + }, "validations": [ { "type": "element_exists", diff --git a/lessons/32-html-svg.json b/lessons/32-html-svg.json index 37bd7a6..b888354 100644 --- a/lessons/32-html-svg.json +++ b/lessons/32-html-svg.json @@ -17,6 +17,10 @@ "initialCode": "", "solution": "\n \n", "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", @@ -66,6 +70,10 @@ "initialCode": "", "solution": "\n \n \n", "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", @@ -150,6 +158,10 @@ "initialCode": "", "solution": "\n \n \n \n \n", "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 ← Painted first (back)\n ← Painted second\n ← Painted third\n ← Painted last (front)\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",