92 lines
8.6 KiB
JSON
92 lines
8.6 KiB
JSON
{
|
|
"$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 <kbd><dialog></kbd> element creates a native modal. Add the <kbd>open</kbd> attribute to show it.<br><br>Use <kbd><form method=\"dialog\"></kbd> inside to close it when the form submits - no JavaScript needed!",
|
|
"task": "Create a dialog with:<br>1. The <kbd>open</kbd> attribute to show it<br>2. An <kbd><h2></kbd> saying <code>Welcome!</code><br>3. A <kbd><p></kbd> with a greeting message<br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Welcome!</h2>\n <p>This is a native HTML dialog element.</p>\n <form method=\"dialog\">\n <button>Close</button>\n </form>\n</dialog>",
|
|
"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 │ │ <dialog> │ │\n │ │ Content │ │\n │ └───────────────┘ │\n └─────────────────────┘\n Focus trapped, Esc closes\n\nshow() → Non-modal dialog\n ↓\n ┌───────────────┐\n │ <dialog> │\n │ Content │ ← Floats above\n └───────────────┘\n Can interact with background\n\nForm Method=\"dialog\":\n<form method=\"dialog\">\n <button value=\"cancel\">Cancel</button>\n <button value=\"ok\">OK</button>\n</form>\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 <kbd><dialog></kbd> element"
|
|
},
|
|
{
|
|
"type": "attribute_value",
|
|
"value": { "selector": "dialog", "attr": "open", "value": true },
|
|
"message": "Add the <kbd>open</kbd> attribute to show the dialog"
|
|
},
|
|
{
|
|
"type": "element_exists",
|
|
"value": "dialog h2",
|
|
"message": "Add an <kbd><h2></kbd> heading inside the dialog"
|
|
},
|
|
{
|
|
"type": "element_exists",
|
|
"value": "form[method='dialog']",
|
|
"message": "Add a <kbd><form method=\"dialog\"></kbd> 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 <kbd>method=\"dialog\"</kbd> makes the form close the dialog on submit instead of sending data.<br><br>This pattern is perfect for confirmation dialogs, quick inputs, or settings panels.",
|
|
"task": "Create a confirmation dialog:<br>1. Add <kbd>open</kbd> to show it<br>2. An <kbd><h2></kbd> saying <code>Confirm Delete</code><br>3. A <kbd><p></kbd> asking <code>Are you sure?</code><br>4. A <kbd><form method=\"dialog\"></kbd> 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": "<dialog open>\n <h2>Confirm Delete</h2>\n <p>Are you sure you want to delete this item?</p>\n <form method=\"dialog\">\n <button value=\"cancel\">Cancel</button>\n <button value=\"delete\">Delete</button>\n </form>\n</dialog>",
|
|
"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<dialog id=\"confirm\">\n <form method=\"dialog\">\n <button value=\"cancel\">Cancel</button>\n <button value=\"delete\">Delete</button>\n </form>\n</dialog>\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<dialog> → Non-blocking\n → Styleable\n → Accessible"
|
|
},
|
|
"validations": [
|
|
{
|
|
"type": "element_exists",
|
|
"value": "dialog[open]",
|
|
"message": "Add a <kbd><dialog></kbd> 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 <kbd><form method=\"dialog\"></kbd> for the buttons"
|
|
},
|
|
{
|
|
"type": "element_count",
|
|
"value": { "selector": "dialog button", "min": 2 },
|
|
"message": "Add at least 2 buttons (Cancel and Confirm)"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|