|
| 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 — covers the context the plugin provides, sandbox constraints, asset rules, and the things that silently don't work. |
| 4 | +--- |
| 5 | + |
| 6 | +# Obsidian HTML Docs — authoring guide |
| 7 | + |
| 8 | +The plugin enables the user to view html files inside Obsidian, similar to md files. The plugin renders `.html` in a sandboxed iframe via Blob URL. The iframe is sealed (`sandbox="allow-scripts allow-popups allow-forms"`, no `allow-same-origin`) so it can't reach into Obsidian or the vault. Everything below works within that envelope. |
| 9 | + |
| 10 | +## Default to Obsidian's context — unless asked otherwise |
| 11 | + |
| 12 | +Before sealing the iframe, the plugin can prepend a `<style>` block to your HTML containing a snapshot of the user's current Obsidian design tokens. The iframe doesn't reach out for these values — they're written as static CSS into your document at load time, then the sandbox is applied. **Security boundary unchanged** (no `allow-same-origin`, no reads from the iframe back into Obsidian); the iframe just has a few extra CSS variables declared in its own document. |
| 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** (proposed plugin enhancement; today only `color-scheme: light dark` via OS preference works): |
| 17 | + |
| 18 | +- A `color-scheme: light | dark` declaration matching the user's current Obsidian theme |
| 19 | +- A small set of `--obsidian-*` CSS variables holding snapshot values from the user's active Obsidian theme: `--obsidian-bg`, `--obsidian-bg-2`, `--obsidian-text`, `--obsidian-text-muted`, `--obsidian-accent`, `--obsidian-border`, `--obsidian-font`, `--obsidian-font-mono` |
| 20 | + |
| 21 | +**Graceful pattern** — use Obsidian tokens if present, fall back if not: |
| 22 | + |
| 23 | +```css |
| 24 | +:root { |
| 25 | + color-scheme: light dark; |
| 26 | + --bg: var(--obsidian-bg, light-dark(#ffffff, #0e1014)); |
| 27 | + --text: var(--obsidian-text, light-dark(#16161a, #e7e9ec)); |
| 28 | +} |
| 29 | +``` |
| 30 | + |
| 31 | +Today this CSS already follows OS light/dark via `prefers-color-scheme`. Once the plugin ships theme injection, the snapshot reflects the user's exact Obsidian theme — including custom themes and snippets — and your variable fallbacks resolve to those values automatically. No code change needed on the HTML side. |
| 32 | + |
| 33 | +## Assets — vault paths do NOT cross into the iframe |
| 34 | + |
| 35 | +The iframe has no base URL pointing into the vault. Obsidian themes, snippets, and `attachments/...` images don't reach the iframe. |
| 36 | + |
| 37 | +| Pattern | Works? | |
| 38 | +|---|---| |
| 39 | +| `<img src="attachments/foo.png">` | No — fails silently | |
| 40 | +| `<img src="data:image/png;base64,...">` | Yes — fully self-contained | |
| 41 | +| `<img src="https://example.com/foo.png">` | Yes — CORS permitting | |
| 42 | +| Inline `<svg>...</svg>` | Yes — best for icons / diagrams | |
| 43 | +| `<link rel="stylesheet" href="https://cdn...">` | Yes — HTTPS only | |
| 44 | + |
| 45 | +Rules of thumb: |
| 46 | + |
| 47 | +- Small graphics & icons → inline SVG or `data:` URL |
| 48 | +- Photos / large images → upload to a host (R2, CDN) and reference HTTPS |
| 49 | +- Fonts → system stack, or HTTPS CDN |
| 50 | +- Never reference `attachments/` or any vault path — the iframe can't see them |
| 51 | + |
| 52 | +## What works |
| 53 | + |
| 54 | +- HTML / CSS (grid, `light-dark()`, custom properties, animations, gradients, SVG with CSS animations) |
| 55 | +- JavaScript (ES2020+, fetch with CORS, Promises, DOM events, requestAnimationFrame, Canvas 2D) |
| 56 | +- Forms (`allow-forms` is set; intercept `submit` if you don't want navigation) |
| 57 | +- `window.parent.postMessage(msg, '*')` — works even with opaque origin |
| 58 | +- External HTTPS resources (images, fonts on CDNs, fetch APIs that allow CORS) |
| 59 | +- Anchor links (`#section`) and the History API — Blob URL preserves both |
| 60 | + |
| 61 | +## What's blocked |
| 62 | + |
| 63 | +- `localStorage`, `sessionStorage`, `IndexedDB` — `SecurityError` |
| 64 | +- `document.cookie` — throws or silently no-ops |
| 65 | +- Reading `window.parent.*` — cross-origin (postMessage still works) |
| 66 | +- Service workers, geolocation, clipboard, notifications, most permission-gated APIs |
| 67 | +- Top-level navigation from inside the iframe (no `allow-top-navigation`) |
| 68 | +- Vault-relative URLs (see Assets above) |
| 69 | + |
| 70 | +## Linking from markdown to HTML |
| 71 | + |
| 72 | +Wikilinks to `.html` files need the explicit extension: |
| 73 | + |
| 74 | +```markdown |
| 75 | +See: [[my-doc.html]] |
| 76 | +``` |
| 77 | + |
| 78 | +`[[my-doc]]` won't resolve to `.html` in stock Obsidian. |
| 79 | + |
| 80 | +## Embed sizing |
| 81 | + |
| 82 | +```markdown |
| 83 | +![[doc.html|width=600 height=400]] |
| 84 | +``` |
| 85 | + |
| 86 | +Sets `--html-docs-width` / `--html-docs-height` on the embed container. Default embed height is ~600px; tab views fill the pane. |
| 87 | + |
| 88 | +## Pitfalls (skip the turn cost) |
| 89 | + |
| 90 | +- **Theme scripts that read `window.parent`** — always throw under this plugin's sandbox. Don't write them. |
| 91 | +- **Images via `attachments/foo.png`** — won't resolve. Inline as data URL or use HTTPS. |
| 92 | +- **`localStorage` / cookies for state** — blocked. Use URL hash or postMessage to parent. |
| 93 | +- **Hard-coding a palette when Obsidian tokens are available** — defeats the contextual fit. Use `var(--obsidian-*)` with `light-dark()` fallbacks. |
| 94 | +- **Assuming `prefers-color-scheme` == Obsidian theme** — usually true, sometimes not. Once the plugin ships theme injection, this is fully resolved. |
0 commit comments