|
| 1 | +--- |
| 2 | +name: obsidian-html-docs |
| 3 | +description: Author HTML files for the Obsidian HTML Docs plugin (smcllns/obsidian-plugin-html-docs). Use when creating .html docs intended to render inline in Obsidian: theme tokens, sandbox constraints, asset rules, embed sizing, and things that silently do not work. |
| 4 | +--- |
| 5 | + |
| 6 | +# Obsidian HTML Docs authoring guide |
| 7 | + |
| 8 | +The HTML Docs plugin lets users view `.html` files inside Obsidian tabs, markdown embeds, and Canvas cards. It renders each file in a sandboxed iframe via Blob URL. The iframe is sealed (`sandbox="allow-scripts allow-popups allow-forms"`, no `allow-same-origin`) so it cannot reach into Obsidian or the vault. Everything below works within that envelope. |
| 9 | + |
| 10 | +## Default to Obsidian context unless asked otherwise |
| 11 | + |
| 12 | +Before creating the Blob URL, the plugin appends a `<style data-html-docs-theme>` block to your HTML containing a snapshot of the user's current Obsidian design tokens. The iframe does not reach out for these values. They are written as static CSS into your document at load time, then the sandboxed iframe loads that document. **Security boundary unchanged**: no `allow-same-origin`, no reads from the iframe back into Obsidian. |
| 13 | + |
| 14 | +If the user just wants a styled doc that "fits" their vault, use these as your defaults. If they're asking for a specific aesthetic (brutalist poster, retro terminal, Linear-style, an exact brand palette), design freely — Obsidian context is a *hint*, not a constraint. |
| 15 | + |
| 16 | +Injected as static CSS: |
| 17 | + |
| 18 | +- A `color-scheme: light | dark` declaration matching the user's current Obsidian theme |
| 19 | +- `--obsidian-color-scheme` |
| 20 | +- `--obsidian-bg` |
| 21 | +- `--obsidian-bg-2` |
| 22 | +- `--obsidian-text` |
| 23 | +- `--obsidian-text-muted` |
| 24 | +- `--obsidian-accent` |
| 25 | +- `--obsidian-border` |
| 26 | +- `--obsidian-font` |
| 27 | +- `--obsidian-font-mono` |
| 28 | + |
| 29 | +Use Obsidian tokens if present, fall back if not: |
| 30 | + |
| 31 | +```css |
| 32 | +:root { |
| 33 | + color-scheme: light dark; |
| 34 | + --bg: var(--obsidian-bg, light-dark(#ffffff, #0e1014)); |
| 35 | + --text: var(--obsidian-text, light-dark(#16161a, #e7e9ec)); |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +Open HTML tabs and embeds re-render when the Obsidian theme changes, so the injected snapshot follows theme switches. |
| 40 | + |
| 41 | +## Assets: vault paths do not cross into the iframe |
| 42 | + |
| 43 | +The iframe has no base URL pointing into the vault. Obsidian themes, snippets, and `attachments/...` images don't reach the iframe. |
| 44 | + |
| 45 | +| Pattern | Works? | |
| 46 | +|---|---| |
| 47 | +| `<img src="attachments/foo.png">` | No — fails silently | |
| 48 | +| `<img src="data:image/png;base64,...">` | Yes — fully self-contained | |
| 49 | +| `<img src="https://example.com/foo.png">` | Yes — CORS permitting | |
| 50 | +| Inline `<svg>...</svg>` | Yes — best for icons / diagrams | |
| 51 | +| `<link rel="stylesheet" href="https://cdn...">` | Yes — HTTPS only | |
| 52 | + |
| 53 | +Rules of thumb: |
| 54 | + |
| 55 | +- Small graphics & icons → inline SVG or `data:` URL |
| 56 | +- Photos / large images → upload to a host (R2, CDN) and reference HTTPS |
| 57 | +- Fonts → system stack, or HTTPS CDN |
| 58 | +- Never reference `attachments/` or any vault path — the iframe can't see them |
| 59 | + |
| 60 | +## What works |
| 61 | + |
| 62 | +- HTML / CSS (grid, `light-dark()`, custom properties, animations, gradients, SVG with CSS animations) |
| 63 | +- JavaScript (ES2020+, fetch with CORS, Promises, DOM events, requestAnimationFrame, Canvas 2D) |
| 64 | +- Forms (`allow-forms` is set; intercept `submit` if you don't want navigation) |
| 65 | +- `window.parent.postMessage(msg, '*')` — works even with opaque origin |
| 66 | +- External HTTPS resources (images, fonts on CDNs, fetch APIs that allow CORS) |
| 67 | +- Anchor links (`#section`) and the History API — Blob URL preserves both |
| 68 | + |
| 69 | +## What's blocked |
| 70 | + |
| 71 | +- `localStorage`, `sessionStorage`, `IndexedDB` — `SecurityError` |
| 72 | +- `document.cookie` — throws or silently no-ops |
| 73 | +- Reading `window.parent.*` — cross-origin (postMessage still works) |
| 74 | +- Service workers, geolocation, clipboard, notifications, most permission-gated APIs |
| 75 | +- Top-level navigation from inside the iframe (no `allow-top-navigation`) |
| 76 | +- Vault-relative URLs (see Assets above) |
| 77 | + |
| 78 | +## Linking from markdown to HTML |
| 79 | + |
| 80 | +Wikilinks to `.html` files need the explicit extension: |
| 81 | + |
| 82 | +```markdown |
| 83 | +See: [[my-doc.html]] |
| 84 | +``` |
| 85 | + |
| 86 | +`[[my-doc]]` won't resolve to `.html` in stock Obsidian. |
| 87 | + |
| 88 | +## Embed sizing |
| 89 | + |
| 90 | +```markdown |
| 91 | +![[doc.html|600x400]] |
| 92 | +``` |
| 93 | + |
| 94 | +Sets the embed width and height. Default markdown embed height is about 600px; tab views fill the pane. |
| 95 | + |
| 96 | +## Pitfalls (skip the turn cost) |
| 97 | + |
| 98 | +- **Theme scripts that read `window.parent`** — always throw under this plugin's sandbox. Don't write them. |
| 99 | +- **Images via `attachments/foo.png`** — won't resolve. Inline as data URL or use HTTPS. |
| 100 | +- **`localStorage` / cookies for state** — blocked. Use URL hash or postMessage to parent. |
| 101 | +- **Hard-coding a palette when Obsidian tokens are available** — defeats the contextual fit. Use `var(--obsidian-*)` with `light-dark()` fallbacks. |
| 102 | +- **Assuming `prefers-color-scheme` == Obsidian theme** — use `--obsidian-color-scheme` or injected colors instead. |
0 commit comments