{ "$schema": "../schemas/code-crispies-module-schema.json", "id": "html-dialog", "title": "Dialogs", "description": "Create modal dialogs without JavaScript libraries", "mode": "html", "difficulty": "beginner", "lessons": [ { "id": "dialog-basic", "title": "Open Dialog", "description": "The <dialog> element creates a native modal. Add the open attribute to show it.

Use <form method=\"dialog\"> inside to close it when the form submits - no JavaScript needed!", "task": "Create a dialog with:
1. The open attribute to show it
2. An <h2> saying Welcome!
3. A <p> with a greeting message
4. A <form method=\"dialog\"> with a close button", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: #f5f5f5; } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 400px; } dialog::backdrop { background: rgba(0,0,0,0.5); } dialog h2 { margin: 0 0 15px 0; color: #1976d2; } dialog p { color: #666; margin: 0 0 20px 0; } dialog button { background: #1976d2; color: white; border: none; padding: 10px 25px; border-radius: 6px; cursor: pointer; font-size: 16px; } dialog button:hover { background: #1565c0; }", "sandboxCSS": "", "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", "value": "dialog", "message": "Add a <dialog> element" }, { "type": "attribute_value", "value": { "selector": "dialog", "attr": "open", "value": true }, "message": "Add the open attribute to show the dialog" }, { "type": "element_exists", "value": "dialog h2", "message": "Add an <h2> heading inside the dialog" }, { "type": "element_exists", "value": "form[method='dialog']", "message": "Add a <form method=\"dialog\"> for closing" }, { "type": "element_exists", "value": "dialog button", "message": "Add a close button inside the form" } ] }, { "id": "dialog-form", "title": "Dialog + Form", "description": "Dialogs can contain full forms. The method=\"dialog\" makes the form close the dialog on submit instead of sending data.

This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.", "task": "Create a confirmation dialog:
1. Add open to show it
2. An <h2> saying Confirm Delete
3. A <p> asking Are you sure?
4. A <form method=\"dialog\"> with Cancel and Delete buttons", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; min-height: 200px; background: linear-gradient(135deg, #fce4ec 0%, #f8bbd9 100%); } dialog { border: none; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); padding: 25px; max-width: 350px; text-align: center; } dialog h2 { margin: 0 0 10px 0; color: #c62828; } dialog p { color: #666; margin: 0 0 20px 0; } dialog form { display: flex; gap: 10px; justify-content: center; } dialog button { padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; border: none; } dialog button:first-child { background: #e0e0e0; color: #333; } dialog button:last-child { background: #c62828; color: white; } dialog button:hover { opacity: 0.9; }", "sandboxCSS": "", "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", "value": "dialog[open]", "message": "Add a <dialog> with the open attribute" }, { "type": "element_exists", "value": "dialog h2", "message": "Add a heading to the dialog" }, { "type": "element_exists", "value": "form[method='dialog']", "message": "Add a <form method=\"dialog\"> for the buttons" }, { "type": "element_count", "value": { "selector": "dialog button", "min": 2 }, "message": "Add at least 2 buttons (Cancel and Confirm)" } ] } ] }