Esc

comark-embed

Embed

Paste a YouTube, Vimeo, Loom, Spotify, CodePen, StackBlitz, or Figma link and get its player, with the link under it. Nothing is fetched.

Playground
Watch before standup: Vite in 100 Seconds

The plugin reads the player's address from the link alone, so a document renders the same offline, in tests, and at build time. The link stays under every player, so the page still makes sense in a feed reader or where iframes are blocked. In print, each player gives way to its address.

::embed{url="https://youtu.be/KCrXgy8qtjM" title="Watch before standup: Vite in 100 Seconds" start=30}
::
md

Close the block with :: on the next line. The plugin drops anything inside it.

Prop What it does
url Required. An https:// link.
title Names the player for screen readers and is the text of the link under it. Without it the link shows the host.
start Seconds into a YouTube or Vimeo video to start at. A YouTube link's own t= works too.
Provider Links it reads Player
YouTube youtube.com/watch?v=, youtu.be/, /shorts/, /embed/, /live/ YouTube's privacy-enhanced mode, youtube-nocookie.com. Shorts are tall.
Vimeo vimeo.com/<number>, unlisted links with their hash, player.vimeo.com/video/ With dnt=1, so Vimeo doesn't track the session.
Loom loom.com/share/<id> Loom's embed player.
Spotify open.spotify.com/ track, album, playlist, episode, show, and artist links A compact player for a track or an episode, a full one for the rest.
CodePen codepen.io/<user>/pen/<id> The pen's result.
StackBlitz stackblitz.com/edit/<project>, stackblitz.com/github/<owner>/<repo> The project, keeping options such as file and view.
Figma figma.com/design, proto, board, slides, and deck links Figma's embed, keeping node-id.

A StackBlitz project that runs in WebContainers, such as a Node or Vite starter, needs a cross-origin-isolated page and a supported browser. Without them, its preview may fail with "Unable to run Embedded Project". StackBlitz lists the supported browsers and the browser configuration.

A project that runs in the browser alone, such as the typescript starter, doesn't need that isolation. It still needs one of StackBlitz's supported embedding environments.

Any other https:// link renders as a card with its title and host. Cards don't fetch the page's title yet, so give each one a title.

The plugin adds every problem to meta.issues, as Validation describes:

  • A missing url.
  • A url that isn't https://. The block renders nothing.
  • A provider's link it can't read an ID from. The block renders as a card.
  • A card without a title.
  • A start that isn't whole seconds, or that is set on a player that can't seek.
  • Content inside the block.

Each issue names its block and says how to fix it.

The plugin takes no options. In comarkUi(), embed: false turns it off.

comark-ui brings comark-embed and registers it. To use it alone, install it and pass the renderer's components:

pnpm add comark @comark/html comark-embed
import { createHtmlRenderer } from "@comark/html";
import embed, { components } from "comark-embed/html";

const render = createHtmlRenderer({ plugins: [embed()], components });
ts

For Vue, React, or Svelte, import from comark-embed/vue, comark-embed/react, or comark-embed/svelte. The components add the styles when they mount. When you render on a server, import comark-embed/style.css with your own styles so the page doesn't shift. Renderers covers using it with other plugins.

Players sit in a bordered frame with their link under it. Cards follow shadcn/ui's Item, with a globe, the title, and the host. The rules sit in a comark CSS layer, so any plain rule of your own wins.

Players and cards read these shared tokens:

Token Used for
--comark-background Cards
--comark-foreground A card's title, the hovered link under a player, the tint behind a loading player
--comark-muted The link under a player, a card's host and globe
--comark-border The player's frame, cards
--comark-accent The hovered card, the globe's tile
--comark-ring Focus rings
--comark-radius Corners of frames and cards
--comark-font The link under a player, cards

Set them on .ce:

Variable Default Used for
--ce-app-height 30rem The height of CodePen, StackBlitz, and Figma
--ce-short-width 20rem The widest a YouTube Short gets, centered

Videos keep 16:9 and Shorts 9:16 at any width. Spotify draws its own card at fixed heights, so its players have no frame.

A player is a figure.ce with data-provider, such as data-provider="youtube", and data-size: video, short, compact, list, or app. It holds .ce-frame with the iframe, and a figcaption with the link. A card is an a.ce-card holding a globe, the title in strong, and the host in span.

.ce {
  --ce-app-height: 40rem;
  --ce-short-width: 16rem;
}
css