AIcss
Components/Text Outputs

Inline Citations

Superscript citation markers with a compact source footer.

Component byKevin@kvnkld

Preview

Transformers scale well with data and compute1, though attention is quadratic in sequence length2.

Code

InlineCitations.tsx
type CiteRef = { n: number; label: string; host: string; url: string };

const TEXT =
  "Transformers scale well with data and compute[1], though attention is quadratic in sequence length[2].";
const REFS: CiteRef[] = [
  { n: 1, label: "Vaswani et al.", host: "arxiv.org", url: "https://arxiv.org/abs/1706.03762" },
  { n: 2, label: "Tay et al.", host: "arxiv.org", url: "https://arxiv.org/abs/2009.06732" },
];

function CiteArrow() {
  return (
    <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
      <path d="M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18" />
    </svg>
  );
}

export function InlineCitations({
  text = TEXT,
  refs = REFS,
}: {
  text?: string;
  refs?: CiteRef[];
}) {
  // Markers like [1] in the text become small numbered chips that link to
  // the source and reveal the reference name in a tooltip on hover.
  const parts = text.split(/(\[\d+\])/g);
  return (
    <div className="cite-prose">
      <p>
        {parts.map((part, i) => {
          const m = part.match(/^\[(\d+)\]$/);
          if (!m) return <span key={i}>{part}</span>;
          const r = refs.find((x) => x.n === Number(m[1]));
          return r ? (
            <span key={i} className="cite-tip">
              <a className="cite-mark" href={r.url} target="_blank" rel="noreferrer">{r.n}</a>
              <span className="cite-tip-box" role="tooltip">{r.label}</span>
            </span>
          ) : (
            <span key={i} className="cite-mark">{m[1]}</span>
          );
        })}
      </p>
      <div className="cite-footer">
        {refs.map((r) => (
          <a key={r.n} className="cite-ref" href={r.url} target="_blank" rel="noreferrer">
            <span className="cite-mark">{r.n}</span>
            <span className="cite-ref-label">{r.label}</span>
            <span className="cite-sep">·</span>
            <span className="cite-ref-host">{r.host}</span>
            <span className="cite-arrow" aria-hidden><CiteArrow /></span>
          </a>
        ))}
      </div>
    </div>
  );
}

/* InlineCitations.css */
.cite-prose { font-size: 14px; line-height: 1.5; color: #1a1a1a; }
.cite-prose p { margin: 0; }
.cite-mark { display: inline-flex; align-items: center; justify-content: center; width: 12px; height: 12px; flex: none; border-radius: 4px; background: #f4f5f7; color: #a1a1a1; font-size: 9px; font-weight: 600; line-height: 1; vertical-align: 5.5px; margin: 0 2px; }
a.cite-mark { cursor: pointer; text-decoration: none; transition: color 0.15s, background 0.15s; }
a.cite-mark:hover { color: #1a1a1a; background: #e6e8ec; }
.cite-tip { position: relative; display: inline; }
.cite-tip-box { position: absolute; left: 50%; bottom: calc(100% + 6px); transform: translateX(-50%) translateY(1px); font-size: 10px; line-height: 1; font-weight: 500; color: rgba(255, 255, 255, 0.9); background: rgba(29, 29, 29, 0.6); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); padding: 4px 5px; border-radius: 6px; white-space: nowrap; pointer-events: none; opacity: 0; filter: blur(2px); transition: opacity 0.15s ease, transform 0.15s ease, filter 0.15s ease; z-index: 1000; }
.cite-tip:hover .cite-tip-box, .cite-tip:focus-within .cite-tip-box { opacity: 1; filter: blur(0); transform: translateX(-50%) translateY(0); }
.cite-footer { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; padding-top: 10px; border-top: 1px solid #e6e8ec; }
.cite-ref { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 18px; color: #a1a1a1; min-width: 0; text-decoration: none; cursor: pointer; }
.cite-ref .cite-mark { margin: 0; }
.cite-ref-label { color: #1a1a1a; font-weight: 450; white-space: nowrap; flex: none; }
.cite-sep { color: #a1a1a1; flex: none; }
.cite-ref-host { color: #a1a1a1; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transition: color 0.16s; }
.cite-arrow { display: inline-flex; flex: none; margin-left: -2px; color: #a1a1a1; opacity: 0; transform: rotate(45deg) translate(0, 2px); transition: opacity 0.16s, transform 0.22s; pointer-events: none; }
.cite-ref:hover .cite-arrow { opacity: 1; transform: rotate(45deg) translate(0, 0); }
.cite-ref:hover .cite-ref-host { color: #1a1a1a; }
@media (prefers-color-scheme: dark) {
  .cite-prose { color: #f5f5f5; }
  .cite-mark { background: #424242; }
  a.cite-mark:hover { color: #f5f5f5; background: #525252; }
  .cite-footer { border-top-color: #303030; }
  .cite-ref-label { color: #f5f5f5; }
  .cite-ref:hover .cite-ref-host { color: #f5f5f5; }
}