Embeds and graphics

Embed another page safely, and choose between inline SVG, an image and canvas.

16of 22
≈ 12Min read
8Parts

iframe puts another document in yours

An <iframe> loads a complete, separate page inside a rectangle. It has its own document, its own scripts and — if it comes from another origin — its own origin, which the browser keeps sealed off from yours.

That isolation is the point. It is also why an iframe is the right tool for running code you do not control, which is exactly what the practice labs on this site do.

an embed with its guards on
<iframe src="https://example.com/map"
        title="Map of the showroom"
        width="800" height="450"
        loading="lazy"
        referrerpolicy="no-referrer"
        sandbox="allow-scripts allow-same-origin"
        allow="fullscreen">
</iframe>

The attributes that matter

AttributeWhy
titleRequired in practice: it is the accessible name. Without it a screen reader says only "frame".
sandboxRemoves every capability, then adds back only what you list.
allowPermissions policy: camera, microphone, geolocation, fullscreen, autoplay.
loading="lazy"Do not load it until it is near the viewport. A third-party embed is usually the heaviest thing on a page.
referrerpolicyHow much of your URL is sent to the other site.
width / heightReserve the space, as with an image.

sandbox is a deny list turned inside out

sandbox with no value removes everything: scripts, forms, popups, the origin, top-level navigation. Each token you add gives one capability back.

  • allow-scripts — JavaScript may run.
  • allow-forms — forms may be submitted.
  • allow-popups — may open a window.
  • allow-modals — may call alert and confirm.
  • allow-same-origin — keeps its real origin, so it can use cookies and storage, and its DOM can be read by a same-origin parent.
  • allow-top-navigation — may navigate your page. Almost never give this.
allow-scripts with allow-same-origin removes the sandbox

A same-origin frame that can run scripts can simply remove its own sandbox attribute and reload itself. For untrusted code, grant allow-scripts alone — the frame then has an opaque origin and cannot reach anything of yours.

Talking to a frame

Across origins you cannot read the other document. The only channel is postMessage, and both sides must check who they are talking to.

parent
const frame = document.querySelector("iframe");

frame.contentWindow.postMessage({ type: "theme", value: "dark" }, "https://example.com");

addEventListener("message", (e) => {
  if (e.source !== frame.contentWindow) return;   // not from our frame
  if (e.origin !== "https://example.com") return; // not from who we expect
  console.log(e.data);
});

Never trust a message without checking origin — any page can post to any window it has a reference to.

SVG: three ways to use it

  • currentColor makes the icon inherit the colour of its surroundings — one file, every theme.
  • aria-hidden="true" on a decorative icon, so it is not announced twice next to a label.
  • focusable="false" matters for old Edge, which put SVGs in the tab order.
  • An SVG that carries meaning on its own needs role="img" and a <title> as its first child.
an inline icon that follows the text colour
<button class="close" aria-label="Close">
  <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
    <path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="2" fill="none"/>
  </svg>
</button>
HowStyleableCachedUse for
<img src="x.svg">NoYesIllustrations you do not need to restyle.
Inline <svg>…</svg>YesNo (part of the HTML)Icons that must take the text colour, and anything animated.
CSS background-imageNoYesDecoration.

SVG is markup

Inside an inline SVG you are still writing elements with attributes, and CSS can select them. That is what makes SVG the natural choice for diagrams whose parts have to change.

a diagram with a named part
<svg viewBox="0 0 120 60" class="plan" role="img">
  <title>Door swing in a 120 by 60 opening</title>
  <rect x="0" y="0" width="120" height="60" fill="none" stroke="currentColor"/>
  <path class="leaf" d="M10 50 L10 10" stroke="currentColor" stroke-width="3"/>
  <path class="arc" d="M10 10 A40 40 0 0 1 50 50" fill="none" stroke-dasharray="4 4"/>
</svg>

canvas

<canvas> is a bitmap you draw on with JavaScript. It has no DOM inside it, so nothing you draw is selectable, searchable or readable by a screen reader — whatever the canvas shows must also exist as text somewhere, or be described in the fallback content between the tags.

Reach for canvas when there are thousands of things to draw or a per-pixel effect. For a chart with a dozen bars, SVG is easier and accessible by default.

canvas with a text alternative
<canvas id="chart" width="600" height="300" role="img"
        aria-label="Lead times: painted 5 days, veneer 10 days">
  <table>…the same numbers as a table…</table>
</canvas>

object and embed

Both are older, more general ways of embedding external content. In practice: use <iframe> for pages, <img> or inline markup for SVG, <video> and <audio> for media. <object> still has one use — embedding a PDF with fallback content — and <embed> has none you need.

Practice