About this snippet

An AI answer with numbered citation pills that open a source preview card when you hover or tab to them. Each card shows the domain, page title, section and a one-line summary of what the source says. Clicking a pill jumps to its row in the Cited sources list, which links each source to the real page in a new tab, and hovering a pill highlights that row. The first card shows until the pointer enters the answer or something in it gets focus, so it never covers the other pills while you use them.

Each citation sits inside an inline span so the surrounding paragraph remains valid HTML. The preview card is revealed with :hover and :focus-within, and an invisible ::before bridge fills the gap between the pill and the card so the pointer can cross into the card without closing it. The bottom list uses :has() on the article to highlight the matching source row whenever its inline pill is hovered or focused.

Every pill is a real anchor linked to its source item and connected to its preview card with aria-describedby. On narrow screens the preview card stretches across the width of the answer so it never clips off the right edge of the viewport. Transitions stop when reduced motion is on, and forced colors mode adds crisp borders to both the pills and the cards.

Size
11.9 KB
Uses
HTML, CSS
Loads
Nothing external
Views
0
<main class="page">
  <article class="answer" aria-labelledby="answer-title">
    <header class="answer-head">
      <div class="answer-meta">
        <span class="answer-badge">
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M10 4Q10 12 18 12Q10 12 10 20Q10 12 2 12Q10 12 10 4Z"></path>
            <path d="M19 2Q19 5.5 22.5 5.5Q19 5.5 19 9Q19 5.5 15.5 5.5Q19 5.5 19 2Z"></path>
          </svg>
          Grounded answer
        </span>
        <span class="answer-verified">
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12l2 2 4-4M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0Z"></path></svg>
          3 sources verified
        </span>
      </div>
      <h1 class="answer-title" id="answer-title">How do browser HTTP caches validate a stale response?</h1>
    </header>
    <p class="answer-p">
      When a cached response passes its <code>max-age</code> window, the browser does not have to download the whole file again
      <span class="cite cite-1">
        <a class="cite-pill" href="#source-1" aria-describedby="card-1"><span class="sr-only">Source </span>1</a>
        <span class="cite-card" id="card-1" role="tooltip">
          <span class="card-top">
            <span class="card-ico" data-site="rfc">R</span>
            <span class="card-domain">rfc-editor.org</span>
            <span class="card-meta">RFC 9111</span>
          </span>
          <strong class="card-title">HTTP Caching: Section 4.3 Validation</strong>
          <span class="card-summary">A cache that cannot serve a stored response, for example because it is stale, can send a conditional request so the next server can confirm or replace it.</span>
        </span>
      </span>.
      Instead, it sends a conditional request with <code>If-None-Match</code> or <code>If-Modified-Since</code>, carrying the stored <code>ETag</code> or <code>Last-Modified</code> date
      <span class="cite cite-2">
        <a class="cite-pill" href="#source-2" aria-describedby="card-2"><span class="sr-only">Source </span>2</a>
        <span class="cite-card cite-card-end" id="card-2" role="tooltip">
          <span class="card-top">
            <span class="card-ico" data-site="mdn">M</span>
            <span class="card-domain">developer.mozilla.org</span>
            <span class="card-meta">HTTP guides</span>
          </span>
          <strong class="card-title">HTTP conditional requests</strong>
          <span class="card-summary">Once a cached response is stale, the client sends its validator in If-None-Match or If-Modified-Since and gets 304 Not Modified if nothing changed.</span>
        </span>
      </span>.
    </p>
    <p class="answer-p">
      When the file has not changed, the origin server returns <code>304 Not Modified</code>
      <span class="cite cite-3">
        <a class="cite-pill" href="#source-3" aria-describedby="card-3"><span class="sr-only">Source </span>3</a>
        <span class="cite-card cite-card-end" id="card-3" role="tooltip">
          <span class="card-top">
            <span class="card-ico" data-site="web">W</span>
            <span class="card-domain">web.dev</span>
            <span class="card-meta">Article</span>
          </span>
          <strong class="card-title">Prevent unnecessary network requests with the HTTP Cache</strong>
          <span class="card-summary">When the validators match, the server answers 304 Not Modified with headers that say how much longer the file can be cached.</span>
        </span>
      </span>
      with no body, and the browser keeps using its cached copy for as long as the new caching headers allow.
    </p>
    <footer class="sources-wrap" aria-labelledby="sources-title">
      <h2 class="sources-title" id="sources-title">Cited sources</h2>
      <ol class="sources">
        <li class="source source-1" id="source-1">
          <span class="source-ico" data-site="rfc">1</span>
          <div class="source-body">
            <a class="source-link" href="https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3" target="_blank" rel="noopener">HTTP Caching: Section 4.3 Validation</a>
            <span class="source-host">rfc-editor.org &middot; RFC 9111</span>
          </div>
        </li>
        <li class="source source-2" id="source-2">
          <span class="source-ico" data-site="mdn">2</span>
          <div class="source-body">
            <a class="source-link" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Conditional_requests" target="_blank" rel="noopener">HTTP conditional requests</a>
            <span class="source-host">developer.mozilla.org &middot; HTTP guides</span>
          </div>
        </li>
        <li class="source source-3" id="source-3">
          <span class="source-ico" data-site="web">3</span>
          <div class="source-body">
            <a class="source-link" href="https://web.dev/articles/http-cache" target="_blank" rel="noopener">Prevent unnecessary network requests with the HTTP Cache</a>
            <span class="source-host">web.dev &middot; Article</span>
          </div>
        </li>
      </ol>
    </footer>
  </article>
</main>
*, *::before, *::after {
  box-sizing: border-box;
}

:root {
  --page: #f1f5f9;
  --card: #ffffff;
  --text: #0f172a;
  --muted: #64748b;
  --soft: #f1f5f9;
  --accent: #2563eb;
  --accent-soft: #eff6ff;
  --accent-text: #1d4ed8;
  --popup: #0f172a;
  --popup-text: #f8fafc;
  --popup-muted: #94a3b8;
}

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  margin: 0;
  background: var(--page);
  color: var(--text);
}

.page {
  display: grid;
  grid-template-columns: minmax(0, 620px);
  justify-content: center;
  align-content: center;
  min-height: 100vh;
  padding: 20px 16px;
}

.answer {
  position: relative;
  padding: 26px 28px;
  border-radius: 22px;
  background: var(--card);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 18px 40px -14px rgba(15, 23, 42, 0.14);
}

.answer-head {
  margin-bottom: 14px;
}

.answer-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}

.answer-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-text);
  font-size: 0.75rem;
  font-weight: 600;
}

.answer-badge svg {
  width: 14px;
  height: 14px;
  fill: currentColor;
}

.answer-verified {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--muted);
}

.answer-verified svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: #16a34a;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.answer-title {
  margin: 0;
  font-size: 1.15rem;
  letter-spacing: -0.015em;
  line-height: 1.35;
}

.answer-p {
  margin: 0 0 12px;
  line-height: 1.7;
}

.answer-p code {
  padding: 2px 6px;
  border-radius: 6px;
  background: var(--soft);
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.84em;
}

.cite {
  position: relative;
  display: inline-block;
  vertical-align: baseline;
}

.cite-pill {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-text);
  font-size: 0.75rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  text-decoration: none;
  transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
}

.cite:hover .cite-pill,
.cite:focus-within .cite-pill,
.answer:not(:hover):not(:focus-within) .cite-1 .cite-pill {
  background: var(--accent);
  color: #ffffff;
  transform: translateY(-1px);
}

.cite-pill:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.cite-card {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  z-index: 20;
  display: grid;
  gap: 6px;
  width: 304px;
  padding: 14px 16px;
  border-radius: 16px;
  background: var(--popup);
  color: var(--popup-text);
  box-shadow: 0 22px 44px -12px rgba(15, 23, 42, 0.45);
  font-size: 0.8125rem;
  line-height: 1.45;
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  pointer-events: none;
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s ease;
}

.cite-card-end {
  right: 0;
  left: auto;
}

.cite-card::before {
  content: "";
  position: absolute;
  top: -12px;
  right: 0;
  left: 0;
  height: 12px;
}

.cite:hover .cite-card,
.cite:focus-within .cite-card,
.answer:not(:hover):not(:focus-within) .cite-1 .cite-card {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.cite:hover .cite-card,
.cite:focus-within .cite-card {
  pointer-events: auto;
}

.card-top {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.75rem;
}

.card-ico,
.source-ico {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: #2563eb;
  color: #ffffff;
  font-size: 0.6875rem;
  font-weight: 700;
}

.card-ico[data-site="mdn"],
.source-ico[data-site="mdn"] {
  background: #7c3aed;
}

.card-ico[data-site="web"],
.source-ico[data-site="web"] {
  background: #059669;
}

.card-domain {
  font-weight: 600;
  color: #ffffff;
}

.card-meta {
  margin-left: auto;
  padding: 2px 7px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.1);
  color: var(--popup-muted);
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.6875rem;
}

.card-title {
  margin-top: 2px;
  font-size: 0.875rem;
  font-weight: 600;
  color: #ffffff;
}

.card-summary {
  padding-left: 10px;
  border-left: 2px solid #60a5fa;
  color: #cbd5e1;
  font-size: 0.775rem;
}

.sources-wrap {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--soft);
}

.sources-title {
  margin: 0 0 8px;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.sources {
  display: grid;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.source {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 12px;
  background: transparent;
  font-size: 0.8125rem;
  transition: background 0.15s ease;
}

.source-ico {
  width: 22px;
  height: 22px;
  border-radius: 7px;
}

.source-body {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 10px;
  min-width: 0;
  flex: 1;
}

.source-link {
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}

.source-link:hover {
  color: var(--accent-text);
}

.source-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.source-host {
  font-size: 0.75rem;
  color: var(--muted);
}

.source:target,
.answer:has(.cite-1:hover, .cite-1:focus-within) .source-1,
.answer:has(.cite-2:hover, .cite-2:focus-within) .source-2,
.answer:has(.cite-3:hover, .cite-3:focus-within) .source-3,
.answer:not(:hover):not(:focus-within) .source-1 {
  background: var(--accent-soft);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (max-width: 560px) {
  .answer {
    padding: 20px 18px;
  }

  .cite {
    position: static;
  }

  .cite-card,
  .cite-card-end {
    top: auto;
    right: 16px;
    left: 16px;
    width: auto;
    margin-top: 8px;
  }
}

@media (pointer: coarse) {
  .cite-pill {
    position: relative;
  }

  .cite-pill::before {
    content: "";
    position: absolute;
    inset: -12px;
  }

  .source-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --page: #0b1120;
    --card: #151e30;
    --text: #f1f5f9;
    --muted: #94a3b8;
    --soft: #1e293b;
    --accent: #3b82f6;
    --accent-soft: #172554;
    --accent-text: #93c5fd;
    --popup: #090d16;
  }
}

@media (forced-colors: active) {
  .cite-pill,
  .cite-card,
  .source {
    border: 1px solid CanvasText;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cite-pill,
  .cite-card,
  .source {
    transition: none;
  }
}
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save