Domphy

tweetCard

A Core block/component from Magic UI — clean-room reimplemented for Domphy (see methodology). Call tweetCard() with no arguments for a working demo, or edit the code below live.

Implementation notes

Independently designed TweetData contract (author/avatar/verified, text, media, linkPreview, quotedTweet capped at 1 nesting level, createdAt) with an injectable fetchTweet for testability (defaults to an in-memory fixture mock — no real network call). Header (avatar/name/verified-badge/handle/platform-glyph), body with @mention/#hashtag/URL entity styling via a regex tokenizer, media grid (1-4 images), external link-preview card, nested quoted-tweet card, footer timestamp, pulsing loading skeleton, and an 'unavailable' fallback on fetch rejection. Supports both the server-rendered path (tweet prop, renders synchronously with zero flicker) and the client-fetch path (tweetId + async phases). theme prop maps to Domphy's own dataTheme attribute override. One real bug found and fixed during testing: the phase-switching reactive root needed distinct _keys per phase (skeleton/error/body) — without them the reconciler positionally patched the old skeleton DOM in place instead of replacing it, leaking stale attributes (e.g. the skeleton's aria-label) onto the loaded body; fixed and covered by a test. Verified-badge and platform-logo glyphs are deliberately original generic icons, not reproductions of any platform's trademarked logo.

Status: ported · Reference: Magic UI original

// magicui "Tweet Card" — clean-room reimplementation from the public
// behavior/visual spec only (no upstream source viewed or copied). A card
// that replicates a single social-post embed: avatar/name/verified-badge
// header, body text with mention/hashtag/link styling, optional media grid
// or link-preview card, an optional nested quoted post, and a footer
// timestamp. Shows a pulsing skeleton while data loads and a graceful
// fallback on fetch failure. The tweet-data contract (author, text, media,
// createdAt, optional quoted tweet) and the injectable `fetchTweet` are an
// independently designed shape to build against, not lifted from any
// existing fetching library.

import type { DomphyElement, ElementNode, Listener } from "@domphy/core";
import { toState } from "@domphy/core";
import { avatar, empty, icon, link, paragraph, skeleton, small, strong } from "@domphy/ui";
import { themeColor, themeDensity, themeSpacing } from "@domphy/theme";

export interface TweetAuthor {
  name: string;
  handle: string;
  avatarUrl?: string;
  verified?: boolean;
}

export interface TweetMedia {
  url: string;
  alt?: string;
}

export interface TweetLinkPreview {
  url: string;
  title: string;
  description?: string;
  thumbnailUrl?: string;
}

export interface TweetData {
  id: string;
  author: TweetAuthor;
  text: string;
  createdAt: string | number | Date;
  media?: TweetMedia[];
  linkPreview?: TweetLinkPreview;
  quotedTweet?: TweetData;
}

export type TweetFetcher = (tweetId: string) => Promise<TweetData>;

export interface TweetCardProps {
  /** Tweet id to resolve via `fetchTweet`. Ignored when `tweet` is provided. */
  tweetId?: string;
  /** Pre-fetched tweet data — renders synchronously with no loading skeleton (the server-rendered path). */
  tweet?: TweetData;
  /** Injectable data source, so the card is testable against static fixtures without a real network call. Defaults to a bundled mock fixture lookup. */
  fetchTweet?: TweetFetcher;
  /** Forces the card's subtree to a specific theme instead of inheriting the ambient page theme. */
  theme?: "light" | "dark";
  showMedia?: boolean;
  showQuotedTweet?: boolean;
}

type TweetPhase = "loading" | "loaded" | "error";

const DEFAULT_TWEET: TweetData = {
  id: "domphy-demo-1",
  author: { name: "Ada Byte", handle: "adabyte", verified: true },
  text: "Shipping a whole design system as plain objects keyed by HTML tag. No JSX, no virtual DOM. @domphy #buildinpublic https://domphy.com",
  createdAt: "2026-06-18T15:32:00Z",
  linkPreview: {
    url: "https://domphy.com",
    title: "Domphy — the AI-friendly UI framework",
    description: "Patch-based UI for native elements, with a runtime built for reactivity and theming.",
  },
};

const MOCK_TWEET_FIXTURES: Record<string, TweetData> = {
  [DEFAULT_TWEET.id]: DEFAULT_TWEET,
};

/**
 * Default injectable fetcher: resolves from an in-memory fixture table
 * (falling back to a synthesized placeholder tweet for unknown ids) after a
 * short simulated network delay. No real network request is made — callers
 * that need live data should pass their own `fetchTweet`.
 */
const defaultFetchTweet: TweetFetcher = (tweetId) =>
  new Promise((resolve) => {
    setTimeout(() => {
      resolve(
        MOCK_TWEET_FIXTURES[tweetId] ?? {
          id: tweetId,
          author: { name: "Unknown Author", handle: "unknown" },
          text: "This is placeholder content for a tweet id with no bundled fixture.",
          createdAt: Date.now(),
        },
      );
    }, 400);
  });

function formatTweetDate(createdAt: TweetData["createdAt"]): string {
  const date = createdAt instanceof Date ? createdAt : new Date(createdAt);
  if (Number.isNaN(date.getTime())) return "";
  return date.toLocaleString(undefined, {
    month: "short",
    day: "numeric",
    year: "numeric",
    hour: "numeric",
    minute: "2-digit",
  });
}

/** Small outline checkmark badge shown next to a verified author's name. */
function verifiedBadgeIcon(): DomphyElement<"span"> {
  return {
    span: [
      {
        svg: [
          { circle: null, cx: "12", cy: "12", r: "9" },
          { polyline: null, points: "8,12.5 11,15.5 16,9" },
        ],
        viewBox: "0 0 24 24",
        fill: "none",
        stroke: "currentColor",
        strokeWidth: "2",
        strokeLinecap: "round",
        strokeLinejoin: "round",
        role: "img",
        ariaLabel: "Verified account",
        style: { width: "100%", height: "100%" },
      } as DomphyElement<"svg">,
    ],
    $: [icon({ color: "info" })],
    style: { width: themeSpacing(4), height: themeSpacing(4), flexShrink: "0" },
  };
}

/** Small generic chat-bubble mark standing in for a "platform" logo — a
 * deliberately original, generic glyph, not a reproduction of any specific
 * platform's trademarked logo. */
function platformLogoIcon(): DomphyElement<"span"> {
  return {
    span: [
      {
        svg: [
          {
            path: null,
            d: "M4 5h16a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H9l-4 3v-3H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z",
          },
        ],
        viewBox: "0 0 24 24",
        fill: "none",
        stroke: "currentColor",
        strokeWidth: "1.75",
        strokeLinecap: "round",
        strokeLinejoin: "round",
        role: "img",
        ariaHidden: "true",
        style: { width: "100%", height: "100%" },
      } as DomphyElement<"svg">,
    ],
    ariaHidden: "true",
    $: [icon({ color: "neutral" })],
    style: { width: themeSpacing(4), height: themeSpacing(4), marginInlineStart: "auto", flexShrink: "0" },
  };
}

function tweetHeader(author: TweetAuthor): DomphyElement<"div"> {
  const initials =
    author.name
      .split(/\s+/)
      .map((word) => word[0])
      .slice(0, 2)
      .join("")
      .toUpperCase() || "?";

  const nameRowChildren: DomphyElement[] = [{ strong: author.name, $: [strong()] }];
  if (author.verified) nameRowChildren.push(verifiedBadgeIcon());

  return {
    div: [
      {
        span: author.avatarUrl
          ? [{ img: null, src: author.avatarUrl, alt: author.name, loading: "lazy" as const }]
          : initials,
        $: [avatar({ color: "primary" })],
      },
      {
        div: [
          {
            div: nameRowChildren,
            style: { display: "flex", alignItems: "center", gap: themeSpacing(1) },
          },
          { small: `@${author.handle}`, $: [small()] },
        ],
        style: { display: "flex", flexDirection: "column", minWidth: "0", overflow: "hidden" },
      },
      platformLogoIcon(),
    ],
    style: { display: "flex", alignItems: "flex-start", gap: themeSpacing(3) },
  };
}

/** Splits the tweet body into plain text runs and clickable @mention / #hashtag / URL entities. */
function tweetTextBody(text: string): DomphyElement<"p"> {
  const tokens = text.split(/(\s+)/);
  const children: (string | DomphyElement<"a">)[] = tokens.map((token, index) => {
    if (/^https?:\/\/\S+/.test(token)) {
      return {
        a: token,
        href: token,
        target: "_blank",
        rel: "noopener noreferrer",
        _key: `entity-${index}`,
        $: [link({ color: "info" })],
      };
    }
    if (/^[@#]\w+/.test(token)) {
      return { a: token, href: "#", _key: `entity-${index}`, $: [link({ color: "info" })] };
    }
    return token;
  });

  return { p: children as DomphyElement<"p">["p"], $: [paragraph()] };
}

function mediaGrid(media: TweetMedia[]): DomphyElement<"div"> {
  const shown = media.slice(0, 4);
  const columns = shown.length === 1 ? 1 : 2;

  return {
    div: shown.map((item, index) => ({
      img: null,
      src: item.url,
      alt: item.alt ?? "",
      loading: "lazy" as const,
      _key: `media-${index}`,
      style: {
        width: "100%",
        height: "100%",
        aspectRatio: shown.length === 1 ? "16 / 9" : "1 / 1",
        objectFit: "cover",
        display: "block",
      },
    })),
    style: {
      display: "grid",
      gridTemplateColumns: `repeat(${columns}, 1fr)`,
      gap: themeSpacing(0.5),
      borderRadius: (listener: Listener) => themeSpacing(themeDensity(listener) * 3),
      overflow: "hidden",
      color: (listener: Listener) => themeColor(listener, "shift-9"),
      outline: (listener: Listener) => `1px solid ${themeColor(listener, "shift-3")}`,
      outlineOffset: "-1px",
    },
  };
}

function linkPreviewCard(preview: TweetLinkPreview): DomphyElement<"a"> {
  let hostname = preview.url;
  try {
    hostname = new URL(preview.url).hostname;
  } catch {
    // Malformed preview URL — fall back to showing the raw string.
  }

  const detailChildren: DomphyElement[] = [{ strong: preview.title, $: [strong()] }];
  if (preview.description) detailChildren.push({ small: preview.description, $: [small()] });
  detailChildren.push({ small: hostname, $: [small()] });

  const cardChildren: DomphyElement[] = [];
  if (preview.thumbnailUrl) {
    cardChildren.push({
      img: null,
      src: preview.thumbnailUrl,
      alt: "",
      loading: "lazy",
      style: { width: "100%", display: "block", objectFit: "cover", aspectRatio: "2 / 1" },
    });
  }
  cardChildren.push({
    div: detailChildren,
    style: {
      display: "flex",
      flexDirection: "column",
      gap: themeSpacing(1),
      padding: (listener: Listener) => themeSpacing(themeDensity(listener) * 3),
    },
  });

  return {
    a: cardChildren,
    href: preview.url,
    target: "_blank",
    rel: "noopener noreferrer",
    style: {
      display: "block",
      textDecoration: () => "none",
      borderRadius: (listener: Listener) => themeSpacing(themeDensity(listener) * 3),
      overflow: "hidden",
      outline: (listener: Listener) => `1px solid ${themeColor(listener, "shift-3")}`,
      outlineOffset: "-1px",
      color: (listener: Listener) => themeColor(listener, "shift-9"),
      backgroundColor: (listener: Listener) => themeColor(listener, "inherit"),
      "&:hover": { backgroundColor: (listener: Listener) => themeColor(listener, "increase-1") },
    },
  };
}

function tweetFooter(createdAt: TweetData["createdAt"]): DomphyElement<"div"> {
  return {
    div: [{ small: formatTweetDate(createdAt), $: [small()] }],
    style: {
      display: "flex",
      color: (listener: Listener) => themeColor(listener, "shift-9"),
      borderTop: (listener: Listener) => `1px solid ${themeColor(listener, "shift-3")}`,
      paddingTop: (listener: Listener) => themeSpacing(themeDensity(listener) * 2),
    },
  };
}

interface TweetBodyOptions {
  showMedia: boolean;
  showQuotedTweet: boolean;
  /** Caps quoted-tweet nesting at one level, so a quote never renders its own quote. */
  depth: number;
}

function tweetBody(data: TweetData, options: TweetBodyOptions): DomphyElement<"div"> {
  const children: DomphyElement[] = [tweetHeader(data.author), tweetTextBody(data.text)];

  if (options.showMedia && data.media?.length) children.push(mediaGrid(data.media));
  if (data.linkPreview) children.push(linkPreviewCard(data.linkPreview));
  if (options.showQuotedTweet && data.quotedTweet && options.depth < 1) {
    children.push({
      div: [
        tweetBody(data.quotedTweet, {
          showMedia: options.showMedia,
          showQuotedTweet: false,
          depth: options.depth + 1,
        }),
      ],
      dataTone: "shift-2",
      style: {
        borderRadius: (listener: Listener) => themeSpacing(themeDensity(listener) * 3),
        padding: (listener: Listener) => themeSpacing(themeDensity(listener) * 3),
        backgroundColor: (listener: Listener) => themeColor(listener, "inherit"),
        color: (listener: Listener) => themeColor(listener, "shift-10"),
        outline: (listener: Listener) => `1px solid ${themeColor(listener, "shift-3")}`,
        outlineOffset: "-1px",
      },
    });
  }
  children.push(tweetFooter(data.createdAt));

  return {
    div: children,
    style: {
      display: "flex",
      flexDirection: "column",
      gap: (listener: Listener) => themeSpacing(themeDensity(listener) * 3),
    },
  };
}

function tweetSkeleton(): DomphyElement<"div"> {
  return {
    div: [
      {
        div: [
          { span: null, $: [skeleton()], style: { width: themeSpacing(9), height: themeSpacing(9), borderRadius: "50%" } },
          {
            div: [
              { span: null, $: [skeleton()], style: { width: "40%" } },
              { span: null, $: [skeleton()], style: { width: "25%" } },
            ],
            style: { display: "flex", flexDirection: "column", gap: themeSpacing(1), flex: "1 1 auto" },
          },
        ],
        style: { display: "flex", gap: themeSpacing(3), alignItems: "center" },
      },
      { span: null, $: [skeleton()], style: { width: "100%" } },
      { span: null, $: [skeleton()], style: { width: "80%" } },
    ],
    ariaLabel: "Loading tweet",
    style: { display: "flex", flexDirection: "column", gap: themeSpacing(3) },
  };
}

function tweetFallback(): DomphyElement<"div"> {
  return {
    div: [{ span: "🚫" }, { p: "This post is unavailable.", $: [paragraph()] }],
    $: [empty()],
  };
}

/**
 * A card replicating a single social-post embed, with header/body/media/
 * link-preview/quoted-post/footer regions, a loading skeleton, and a
 * fallback state on fetch failure. Call with no arguments for a working
 * demo tweet (rendered synchronously — no loading flicker). Pass `tweetId`
 * (with an optional injectable `fetchTweet`) to exercise the async
 * loading/error states instead.
 */
function tweetCard(props: TweetCardProps = {}): DomphyElement<"div"> {
  const fetchTweet = props.fetchTweet ?? defaultFetchTweet;
  const showMedia = props.showMedia ?? true;
  const showQuotedTweet = props.showQuotedTweet ?? true;

  const initialTweet = props.tweet ?? (props.tweetId ? null : DEFAULT_TWEET);
  const phase = toState<TweetPhase>(initialTweet ? "loaded" : "loading");
  const tweetState = toState<TweetData | null>(initialTweet);

  return {
    div: (listener: Listener) => {
      const currentPhase = phase.get(listener);
      // Each phase gets a distinct `_key` so the reconciler replaces the
      // single child outright on a phase transition instead of positionally
      // patching the old (structurally different) DOM subtree in place —
      // without this, stale attributes/nodes from the previous phase can
      // leak through (e.g. the skeleton's `aria-label` surviving onto the
      // loaded body).
      if (currentPhase === "loading") return [{ ...tweetSkeleton(), _key: "skeleton" }];
      const data = tweetState.get(listener);
      if (currentPhase === "error" || !data) return [{ ...tweetFallback(), _key: "error" }];
      return [{ ...tweetBody(data, { showMedia, showQuotedTweet, depth: 0 }), _key: `body-${data.id}` }];
    },
    role: "article",
    dataTheme: props.theme,
    dataTone: "shift-1",
    style: {
      display: "block",
      width: "100%",
      maxWidth: themeSpacing(120),
      borderRadius: (listenerValue: Listener) => themeSpacing(themeDensity(listenerValue) * 3),
      padding: (listenerValue: Listener) => themeSpacing(themeDensity(listenerValue) * 4),
      backgroundColor: (listenerValue: Listener) => themeColor(listenerValue, "inherit"),
      color: (listenerValue: Listener) => themeColor(listenerValue, "shift-10"),
      outline: (listenerValue: Listener) => `1px solid ${themeColor(listenerValue, "shift-3")}`,
      outlineOffset: "-1px",
    },
    _onMount: (node: ElementNode) => {
      if (props.tweet || !props.tweetId) return;
      let cancelled = false;
      fetchTweet(props.tweetId)
        .then((data) => {
          if (cancelled) return;
          tweetState.set(data);
          phase.set("loaded");
        })
        .catch(() => {
          if (cancelled) return;
          phase.set("error");
        });
      node.addHook("Remove", () => {
        cancelled = true;
      });
    },
  };
}

export { tweetCard, defaultFetchTweet };

← Back to Magic UI catalog