LEARNING OBJECTIVES ⌵
- Structure accessible preformatted code blocks using semantic
<figure>,<pre>, and<code>elements. - Implement an asynchronous copy-to-clipboard button using
navigator.clipboard.writeText()with accessible screen reader announcements (aria-live="polite"). - Construct a secure execution boundary using
<iframe sandbox="allow-scripts">to isolate untrusted user scripts from parent cookies, storage, and DOM trees. - Build a zero-backend interactive runner that dynamically compiles HTML/CSS/JS into real-time rendering viewports using
srcdocandBlobobject URLs.
📖 The Mental Model & Story (Intuitive Foundation)
Think of a high-containment chemical laboratory. When scientists test experimental compounds, they don't conduct reactions on an open kitchen counter where fumes can spread through the building. Instead, they work inside a glove box—a sealed, transparent container with specialized air filters and thick rubber gloves. The scientist can see and manipulate the chemicals safely, but any explosion or toxic vapor is completely trapped inside the chamber.
In frontend documentation, an interactive code playground is that glove box.
Developers visiting your documentation need to tweak HTML markup, modify CSS variables, and execute JavaScript right on the page. If you execute arbitrary user code directly in your main page's DOM, malicious code (or accidental loops) could steal authentication tokens, hijack the top-level window location, or crash your site.
By wrapping execution inside a sandboxed <iframe> with strict HTML5 capabilities, you create an impermeable security boundary that lets students run live code safely.
Technical Deep Dive & Specifications
2.1 The Code Playground Architecture
+-----------------------------------------------------------------------------------------+
| MAIN APPLICATION CONTEXT (https://docs.apex.dev) |
| |
| <div class="code-playground" role="region" aria-label="Interactive Code Runner"> |
| +-------------------------------------------------------------------------------+ |
| | Toolbar: [HTML] [CSS] [JS] | [📋 Copy Code] [▶ Run Snippet] [🔄 Reset] | |
| +-------------------------------------------------------------------------------+ |
| | Editable / Syntax-Highlighted Code Editor: | |
| | <textarea id="code-input" aria-label="Live HTML Code Source"> | |
| | <h1>Hello World</h1> | |
| | <button onclick="alert('Live!')">Click Me</button> | |
| | </textarea> | |
| +-------------------------------------------------------------------------------+ |
| | Live Execution Output Frame: | |
| | <iframe | |
| | sandbox="allow-scripts" | |
| | srcdoc="..." | |
| | aria-label="Interactive Code Result Preview"> | |
| | </iframe> | |
| +-------------------------------------------------------------------------------+ |
| </div> |
| |
| SECURITY BOUNDARY (Blocked Capabilities): |
| ❌ No window.top navigation (allow-top-navigation is omitted) |
| ❌ No access to parent cookies / localStorage (allow-same-origin is omitted) |
| ❌ No popup window generation (allow-popups is omitted) |
| ❌ No form submissions to external endpoints (allow-forms is omitted) |
+-----------------------------------------------------------------------------------------+
2.2 Iframe Sandbox Flags & Security Matrix
The sandbox attribute on <iframe> enforces a strict capability restriction model:
| Sandbox Flag | Security State | What It Permits / Prevents |
|---|---|---|
(empty / default sandbox) |
🔒 Maximum Lockdown | Scripts disabled, forms disabled, cross-origin isolated, no popups. |
sandbox="allow-scripts" |
⚠️ Safe Script Execution | Recommended: JavaScript executes inside iframe, but iframe runs with unique origin (null), blocking cookie and storage access to parent. |
sandbox="allow-scripts allow-same-origin" |
🚨 Critical Vulnerability! | DANGEROUS: Combining these two flags allows iframe script to reach into window.parent.document and remove its own sandbox! |
sandbox="allow-popups" |
⚠️ Restricted Popups | Allows window.open() or target _blank links; omit for code runners. |
sandbox="allow-forms" |
⚠️ Form Submission | Allows <form> POST/GET actions inside the frame. |
2.3 Compilation: srcdoc vs. Blob Object URLs
To inject dynamic user code into the iframe:
srcdocAttribute:iframe.srcdoc = `<!DOCTYPE html><html><head><style>${css}</style></head><body>${html}<script>${js}<\/script></body></html>`;- Pros: Instant, synchronous inline DOM parsing, supported across all modern browsers.
- Security: Operates strictly under the host iframe sandbox constraints.
Blob URL via
URL.createObjectURL():const blob = new Blob([compiledSource], { type: 'text/html;charset=utf-8' }); iframe.src = URL.createObjectURL(blob);- Pros: Completely isolated URL (
blob:https://...), ideal for large multi-file packages or worker instantiations. - Note: Always revoke URLs with
URL.revokeObjectURL(oldUrl)to prevent memory leaks.
- Pros: Completely isolated URL (
💻 Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Lines 101–103:
<div class="runner-card" role="region" aria-label="...">creates a clearly identified landmark region for assistive devices. - Lines 105–112: Toolbar contains accessible
<button type="button">triggers with dedicatedaria-labeldescriptions. - Lines 114–128:
<textarea>holds the live editable HTML markup, populated with default runnable markup. - Line 131:
<iframe sandbox="allow-scripts" title="...">establishes the strict security isolation boundary.allow-scriptsenables JavaScript inside the frame while preventing parent window tampering. - Line 135:
<div id="aria-status" class="sr-only" role="status" aria-live="polite">acts as an ARIA live region to announce copy confirmations and run actions to blind users. - Lines 144–148:
executeCode()setsiframe.srcdoc = code;to instantly re-render the frame without network round-trips. - Lines 155–167: Asynchronous Clipboard API (
navigator.clipboard.writeText) copies code with visual and screen-reader state feedback.
Expected Browser Render Output
+------------------------------------------------------------------------------+
| live-example.html [📋 Copy] [▶ Run] |
+------------------------------------------------------------------------------+
| <style> |
| body { font-family: sans-serif; text-align: center; ... } |
| </style> |
| <h1>Interactive HTML5</h1> |
| <button id="test-btn">Click Me!</button> |
+------------------------------------------------------------------------------+
| LIVE OUTPUT |
| +--------------------------------------------------------------------------+ |
| | Interactive HTML5 | |
| | Click the button to test live sandboxed execution: | |
| | [ Click Me! ] | |
| +--------------------------------------------------------------------------+ |
+------------------------------------------------------------------------------+🏋️ Hands-On Exercise
🎯 The Challenge: Capture Console Logs from Sandboxed Iframe
Instructions:
- In production, users often write
console.log('Output data')inside their playground snippets. Because the iframe runs in a sandbox, its console logs normally go only to developer tools. - Modify the playground runner to intercept
console.loginside the sandboxed iframe and forward messages to the parent window usingwindow.parent.postMessage(). - Display the forwarded logs in a dedicated
<pre class="console-output">terminal below the preview frame.
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Combining
allow-scriptsandallow-same-originon User Iframe: This is the #1 security vulnerability in developer documentation runners. When combined, scripts running inside the iframe can manipulate parent DOM nodes, access parent cookies, and programmatically delete thesandboxattribute. NEVER combine them for untrusted content. - Neglecting Fallback for Clipboard API:
navigator.clipboardrequires a Secure Context (HTTPS orlocalhost). In insecure HTTP contexts,navigator.clipboardisundefined. Always wrap calls intry...catchand handle errors gracefully. - Unsanitized HTML in
aria-liveAnnouncers: When announcing copy results or error states, usetextContentrather thaninnerHTMLto prevent script injection vulnerabilities.
💡 Pro Tips
- Zero-Flicker Sandboxing with
loading="lazy"& Debouncing: When implementing real-time typing re-renders, debounce the execution function by300ms(clearTimeout(timeoutId)) so the iframe is not re-created on every single keystroke. - CSP Headers for iframe Sandboxes: Serve documentation with
Content-Security-Policy: frame-src 'self' data: blob:;to strictly govern the sources allowed to render inside iframe contexts.
📌 Key Takeaways
- Live interactive runners must execute code inside
<iframe sandbox="allow-scripts">to enforce strict origin isolation and protect parent credentials. - Never combine
allow-scriptswithallow-same-originon untrusted user code execution environments. - Dynamic rendering can be achieved instantly using
iframe.srcdocwithout requiring backend compilation servers. - The Clipboard API (
navigator.clipboard.writeText) must be accompanied byaria-live="polite"status regions for accessible screen reader feedback. - Cross-boundary telemetry (e.g.
console.logforwarding) can be securely achieved via structuredpostMessagecommunication. - --