devpage 0.2.2

Live documents from a repository: sites, docs and slides from Markdown, and UI designs drawn from code — with comments, link checking and PDF
// The four kinds of document, side by side: a sketch of how each reads, what
// it is made of, and a link to the page that teaches it.
const kinds = [
  {
    name: "Site",
    kind: "site",
    like: "Like a docs website",
    madeOf: "A root page and the pages its Topics sections reach",
    readAs: "Many pages, with a sidebar",
    href: "/docs/boxes/viewer/making-a-site/README.md",
  },
  {
    name: "Doc",
    kind: "doc",
    like: "Like a Word document",
    madeOf: "One or more chapter files, in order",
    readAs: "One long page, with an outline that follows you",
    href: "/docs/boxes/viewer/writing-a-doc/README.md",
  },
  {
    name: "Slides",
    kind: "slides",
    like: "Like a PowerPoint deck",
    madeOf: "One Markdown file per slide, in order",
    readAs: "One slide at a time, 16:9",
    href: "/docs/boxes/viewer/making-slides/README.md",
  },
  {
    name: "Design",
    kind: "design",
    like: "Like a Figma frame and its prototype",
    madeOf: "One .design file, using other designs",
    readAs: "A canvas you can click through",
    href: "/docs/boxes/viewer/designs/README.md",
    // A design has no document file: the .design file is the whole document.
    field: "checkout.design",
  },
];

export const css = `
  .kind-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1.25rem;
    margin: 1.75rem 0 2rem;
  }
  /* On a slide, the four side by side. */
  .slide .kind-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .slide .kind-sketch { height: 7.5rem; }
  .slide .kind-sketch > * { zoom: 0.8; }
  .slide .kind-body { padding-bottom: 1rem; }
  .kind-cards a.kind-card {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: 18px;
    background: var(--background);
    color: inherit;
    text-decoration: none;
    overflow: hidden;
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
  }
  .kind-cards a.kind-card:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
    box-shadow: 0 12px 32px -12px color-mix(in srgb, var(--text) 30%, transparent);
  }

  /* The sketch: a tiny, theme-colored picture of how the kind reads. */
  .kind-sketch {
    height: 9.5rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .kind-sketch .frame {
    background: var(--background);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 6px 18px -10px color-mix(in srgb, var(--text) 40%, transparent);
    transition: transform 0.25s ease;
  }
  .kind-card:hover .kind-sketch .frame { transform: scale(1.04); }
  .kind-sketch .bar { height: 5px; border-radius: 3px; background: var(--border); }
  .kind-sketch .bar.accent { background: var(--accent); opacity: 0.85; }
  .kind-sketch .bar.short { width: 55%; }
  .kind-sketch .bar.mid { width: 75%; }

  .kind-sketch .site { width: 11rem; height: 6.75rem; display: grid; grid-template-columns: 3rem 1fr; overflow: hidden; }
  .kind-sketch .site .side { background: var(--surface); border-right: 1px solid var(--border); padding: 0.5rem 0.4rem; display: grid; align-content: start; gap: 0.35rem; }
  .kind-sketch .site .main { padding: 0.55rem 0.6rem; display: grid; align-content: start; gap: 0.35rem; }

  .kind-sketch .doc { width: 11rem; height: 6.75rem; display: grid; grid-template-columns: 2.6rem 1fr; gap: 0.5rem; padding: 0.55rem 0.6rem; }
  .kind-sketch .doc .outline { display: grid; align-content: start; gap: 0.35rem; padding-right: 0.45rem; border-right: 1px solid var(--border); }
  .kind-sketch .doc .text { display: grid; align-content: start; gap: 0.35rem; }

  .kind-sketch .deck { position: relative; width: 10.5rem; height: 6.4rem; }
  .kind-sketch .deck .frame { position: absolute; width: 9rem; aspect-ratio: 16 / 9; height: auto; }
  .kind-sketch .deck .back2 { left: 1.5rem; top: 0; opacity: 0.45; }
  .kind-sketch .deck .back1 { left: 0.75rem; top: 0.55rem; opacity: 0.75; }
  .kind-sketch .deck .front { left: 0; top: 1.1rem; display: grid; align-content: center; justify-items: center; gap: 0.4rem; }
  .kind-sketch .deck .front .bar { width: 55%; }
  .kind-sketch .deck .front .bar.title { height: 8px; width: 45%; }

  .kind-sketch .design { width: 4.6rem; height: 7.4rem; border-radius: 12px; padding: 0.6rem 0.5rem; display: grid; align-content: start; gap: 0.4rem; }
  .kind-sketch .design .card { height: 1.6rem; border: 1px solid var(--border); border-radius: 5px; }
  .kind-sketch .design .button { height: 0.85rem; margin-top: 0.9rem; border-radius: 4px; background: var(--accent); opacity: 0.85; }

  .kind-body { display: flex; flex-direction: column; gap: 0.35rem; padding: 1.1rem 1.25rem 1.25rem; flex: 1; }
  .kind-title { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
  .kind-title strong { font-size: 1.3rem; font-weight: 650; letter-spacing: -0.01em; }
  .kind-title .arrow { color: var(--accent); opacity: 0; transform: translateX(-4px); transition: opacity 0.2s ease, transform 0.2s ease; }
  .kind-card:hover .kind-title .arrow { opacity: 1; transform: none; }
  .kind-like { color: var(--muted); font-size: 0.95rem; }
  .kind-facts { margin: 0.75rem 0 0; padding-top: 0.75rem; border-top: 1px solid var(--border); display: grid; gap: 0.55rem; font-size: 0.9rem; line-height: 1.4; flex: 1; }
  .kind-facts dt { font-size: 0.7rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
  .kind-facts dd { margin: 0.1rem 0 0; }
  .kind-field { align-self: flex-start; margin-top: 0.9rem; font-family: var(--font-mono); font-size: 0.75rem; color: var(--muted); background: var(--surface); border: 1px solid var(--border); border-radius: 999px; padding: 0.15rem 0.6rem; }
`;

function Bars({ count, accent }: { count: number; accent?: boolean }) {
  const widths = ["", "mid", "", "short", "mid", ""];
  return (
    <>
      {accent && <div className="bar accent short" />}
      {Array.from({ length: count }, (_, i) => <div className={`bar ${widths[i % widths.length]}`} />)}
    </>
  );
}

function Sketch({ kind }: { kind: string }) {
  if (kind === "site") {
    return (
      <div className="frame site">
        <div className="side"><div className="bar accent" /><Bars count={4} /></div>
        <div className="main"><Bars count={6} accent /></div>
      </div>
    );
  }
  if (kind === "doc") {
    return (
      <div className="frame doc">
        <div className="outline"><div className="bar accent" /><Bars count={3} /></div>
        <div className="text"><Bars count={7} accent /></div>
      </div>
    );
  }
  if (kind === "design") {
    return (
      <div className="frame design">
        <div className="bar accent mid" />
        <div className="card" />
        <div className="bar short" />
        <div className="button" />
      </div>
    );
  }
  return (
    <div className="deck">
      <div className="frame back2" />
      <div className="frame back1" />
      <div className="frame front"><div className="bar accent title" /><div className="bar" /><div className="bar short" /></div>
    </div>
  );
}

export default function KindCards() {
  return (
    <div className="kind-cards">
      {kinds.map((kind) => (
        <a className="kind-card" href={kind.href}>
          <div className="kind-sketch" aria-hidden="true"><Sketch kind={kind.kind} /></div>
          <div className="kind-body">
            <div className="kind-title">
              <strong>{kind.name}</strong>
              <span className="arrow">→</span>
            </div>
            <span className="kind-like">{kind.like}</span>
            <dl className="kind-facts">
              <div><dt>Made of</dt><dd>{kind.madeOf}</dd></div>
              <div><dt>Read as</dt><dd>{kind.readAs}</dd></div>
            </dl>
            <code className="kind-field">{kind.field ?? `kind: ${kind.kind}`}</code>
          </div>
        </a>
      ))}
    </div>
  );
}