npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@credence/react

v0.6.0

Published

Citation UI for Credence: clickable evidence pills, hover cards, and PDF bbox highlights.

Downloads

518

Readme

@credence/react

The surface a human touches. Everything else in this repo is for the engineer wiring Credence into an agent. This package is for the person on the other end — the analyst, the auditor, the lawyer whose signature the output has to survive. A reader sees (1), clicks it, and lands on the exact region of the exact page the number came from.

That double audience is the whole design brief. An engineer installs it in an afternoon; somebody else has to defend its output in a room. So every component answers the same question the ledger answers — how do you know? — and refuses to render a blank where the honest answer is "nobody looked".

import { ClaimValue, DocumentPage, IntegrityIndicator } from "@credence/react";

<ClaimValue value={410_000} status="verified" citations={citations} />
// → 410000  ✓ verified  (1)          ...an unsourced value renders "⚠ no source"

<ClaimValue value={undefined} status="unknown" />        // → · unknown  (nobody looked)
<ClaimValue value={{ $credence: "absent" }} status="absent" />
                                                          // → ∅ absent  (someone looked)

<DocumentPage src="/page-4.png" page={4} citations={citations} activeCitationId={active?.id} />
// → the page with the bbox highlighted — or an explicit "source could not be loaded"

<IntegrityIndicator />
// → ✗ Trail NOT verified            ...because a missing prop must never render a tick

Components

| Component | What it does | |---|---| | Citations / CitationPill | numbered pills with a hover card (source + locator + stance as a glyph and a word); renders ⚠ no source when a value has no evidence; an unstated stance prints nothing | | ClaimValue | value + epistemic badge + citations + optional attribution, together; ∅ absent for verified absence | | EpistemicBadge | reported / inferred / verified / disputed, as a glyph and a word | | DocumentPage / BboxHighlight | page image with bbox highlights overlaid, positioned in percentages | | MatrixTable | the review matrix; a cell clicks through to the page and bounding box its value came from | | FindingCard / FindingList | a finding with the act that quieted it — author, reason, time — or a call-out when there is none | | AttributionLine | who asserted a value, with which model version, and against which tool receipt | | IntegrityIndicator | whether the case's append-only trail verifies — in five outcomes, never a default tick | | OversightBanner | that the case is stopped, by whom, why, and what else is blocking it | | LoadingBlock / ErrorBlock / EmptyBlock | the three states every component above can be in |

Helpers: bboxToPercent, citationAriaLabel, citationLabel, citationIndex, describeValueState, formatAttribution, formatDismissal, formatIntegrity, formatLocator, BBOX_SCALE, VALUE_STATE_TOKENS, EVIDENCE_STANCE_TOKENS, tokenStyle, v, credenceStyles.

AttributionLine, IntegrityIndicator and OversightBanner render what the ledger's audit spine produces — Claim.assertedBy / Claim.modelVersion plus the chain entry, verifyChain, and oversight_status. Host apps that have not wired those simply do not render these three; nothing degrades.

MatrixTable shows the value AND the rule

A cell's state answers "what does the ledger hold for this field?", which can be impeccable while a rule still objects to it — a verified, evidenced debt of 5M is ok and a blocking risk at once. So a cell needing attention is marked . A table that rendered only the value would tell a reader everything is fine while the Case Index said otherwise.

States

Every component has a loading, an error and an empty state, because a silent blank teaches a reader that "blank" means "fine".

| Component | loading | error | empty | degraded | |---|---|---|---|---| | Citations | ⋯ Loading evidence… | ⚠ Could not load… + retry | ⚠ no source | a citation with no locator still renders, without one | | ClaimValue | LoadingBlock replaces value and citations | ErrorBlock replaces both | — (unknown / absent are values, not emptiness) | no attribution prop ⇒ no attribution row; null ⇒ the gap is shown | | MatrixTable | ⋯ Loading review matrix… | ErrorBlock + retry | No requirements in this view — never a table of blanks | a cell with no resolvable citations says no evidence in its label | | DocumentPage | ⋯ Loading source page… | "Source document could not be loaded" — and the highlights are withdrawn | No evidence recorded on this page | citations on the page with no bbox are counted out loud, never guessed at | | FindingList / FindingCard | ⋯ Loading findings… | ErrorBlock + retry | No findings on this case | a dismissal with no author or reason is called out, not rendered as quiet | | AttributionLine | ⋯ Loading attribution… | (host's) | Not attributed — no actor recorded | actorKind: null renders as its own third state, not as human or machine | | IntegrityIndicator | ⋯ Verifying the trail… | "Trail integrity could not be checked. Treat this case as unverified." | absent prop ⇒ ✗ Trail NOT verified | an ok chain with no witness renders , not | | OversightBanner | ⋯ Loading oversight status… | (host's) | renders nothing when nothing is held and nothing blocks | a declared autonomy budget nobody counted against prints no fraction at all |

An error is an alert and a loading state is a polite status, because one will resolve on its own and the other will not.

Theming

Every component ships complete inline styles, so the package works with zero CSS. Colours resolve through CSS custom properties with inline fallbacks, so theming is one declaration on any ancestor:

:root {
  --credence-verified-fg: #047857;
  --credence-verified-bg: rgba(4, 120, 87, 0.12);
}

The optional stylesheet documents every knob in one place and adds focus rings, prefers-reduced-motion handling and print rules:

import { credenceStyles } from "@credence/react/styles";

<style dangerouslySetInnerHTML={{ __html: credenceStyles }} />

It is wrapped in @layer credence. An unlayered author rule beats a layered one regardless of specificity, so your .credence-badge { … } wins over ours without !important and without counting selectors. Stable credence-* class names and data-* state attributes (data-status, data-integrity, data-autonomy, data-actor-kind, data-held, data-stance) are the styling hooks.

Accessibility

Not a follow-up; asserted in tests.

  • One tab stop per matrix. MatrixTable implements the WAI-ARIA roving tabindex: arrow keys move between cells, Home / End jump to the corners. A 12×8 matrix with a tab stop per cell would put 96 stops between a keyboard user and the next control on the page.
  • A cell with nothing to open is aria-disabled, not disabled — it keeps its place in the grid, so the cursor is the only thing that changes.
  • Escape dismisses the hover card while focus stays on the pill, and the card opens on focus as well as hover.
  • Every state carries a glyph and a word, never only a colour. Pills are real <button>s with full-sentence aria-labels ("Citation 1: balance-2025.pdf — p. 4 · region"); the card is a role="tooltip" linked by aria-describedby.
  • Politeness matches urgency. A hold and a broken chain are assertive alerts; loading and a passing chain are polite statuses.
  • Server-rendering is a test, not a hope. Every exported component is rendered with react-dom/server in a node environment, so nothing touches the DOM at import or render time.

Honesty

The design bar for this package is that a three-state thing never renders as two, and absence is visible.

  • unknown vs absent. Nobody looked, versus somebody looked and there is verifiably nothing there. Different glyphs, different words, different borders.
  • ⚠ no source. A value with no evidence says so rather than rendering nothing. A failed load says something different again — collapsing the two would make this surface assert what it does not know.
  • A source page that will not load withdraws its highlights. Boxes floating over a broken image still look like located evidence, and looking like evidence while being none is worse than showing nothing.
  • Trail integrity never defaults to a checkmark. No prop means ✗ Trail NOT verified; indeterminate is its own answer, neither a tick nor an accusation; and an ok chain checked against nothing but itself renders differently from one checked against a witness.
  • An uninstrumented autonomy budget is not a comfortable zero. 0/8 iterations is the most reassuring line that could appear on a banner, and it is exactly what a ceiling nobody counted against produces — so unreported prints no fraction.
  • A machine stop is not human oversight. A case an agent halted has not been looked at by anybody.
  • Greyscale survives. Audit packs get printed, and roughly one reader in twelve cannot tell the green from the red.
  • A citation that does not support is not rendered as one. Stance is a glyph and a word on the hover card (supports / contradicts / cited). Unstated prints nothing — not a placeholder, not a default — because a mark on every historical pill would teach readers to skip the column.

Reviewed through a committed gallery, not Storybook

pnpm --filter @credence/react gallery renders every component in every state to gallery/gallery.golden.html, which is committed and asserted byte-for-byte by test/gallery.test.ts. Open the file — no dev server, no port, no install — or read the diff in a pull request, where a regression in the honesty vocabulary shows up as text rather than as something a human has to remember about a screenshot.

Design decisions

  • No @credence/core dependency, including types. Core pulls Drizzle and PGlite, which have no business in a browser bundle. The types here are local and structurally compatible, so values read from the ledger drop straight in; parity is asserted from both sides, and test/boundary.test.ts fails on any import of a sibling package — by module specifier over comment-stripped source, because import type is erased and would never show up in a bundle.
  • React is the only peer. No CSS framework, no component library, no runtime deps.
  • Percentages, not pixels. Highlights stay glued to the content through zoom, resize and responsive layout.
  • Refs are handed in, never assembled here. The credence:// scheme is configurable in core, so a ref built in this package would be wrong for any deployment that rebranded it.

Non-goals — this is a component library, not an application

@credence/react renders props. It does not fetch, does not hold session state, does not know what a tenant is, and never will. Everything below is deliberately out of scope:

| Not here | Where it belongs | |---|---| | Auth, SSO, RBAC — who may see which case | Cloud | | Data fetching, caching, resolve_ref round-trips, chain verification | the host app; this package takes the results as props | | Routing, navigation, a case list, a portfolio dashboard | Cloud | | PDF rendering — turning a PDF into page images | the host (pdf.js, a render service); DocumentPage takes an image src | | Writes of any kind — dismissing a finding, releasing a hold, recording a decision | the host calls @credence/kit; components emit onSelect* / onOpenRef callbacks and nothing more | | Retention, redaction, PII masking on locators | Cloud, per docs/ROADMAP.md → Later | | Real-time updates, websockets, optimistic state | the host |

The line is not stylistic. A component that fetches is a component that needs a client, a base URL, an auth token and an error policy — and a component library with an auth token in it cannot be dropped into someone else's app, which is the entire reason this package exists.