The 1 file this component needs and nothing else, each fenced with the path it belongs at, followed by what the library's own verification covered and what it did not. No instructions attached — for placing it yourself, or handing it to something that has its own.
Read it first
Install kit: "Credential ledger"
Library page: https://astro.baysixmedia.com/components/credential-ledger/
Files: 1 required, 1 supporting
Write each block below to the path named in its header, relative to the project root. A block marked (required) is one the component does not work without; (supporting) is documentation. Nothing here needs downloading except a block that says so in as many words.
===== FILE (required): src/components/CredentialLedger.astro =====
---
/**
* CredentialLedger (candidate, Step 34)
*
* Verified authority set as a record, not a badge wall: a split header
* (heading on one side; one sentence and an optional note on where the
* details came from on the other) over a full-width ruled ledger. An entry
* with a value is a registry line — a quiet sentence-case label over the
* value set large in tabular figures, with the issuer, a short description
* and its source beside or under it. An entry with a label and no value
* ("Bonded for residential work") is a statement row in the same face a clear step
* below the records, never an empty number slot. A label that already carries its
* identifier ("USDOT 1234567": no value and a run of 3+ digits, or a label
* containing its value) is set once, whole and exactly as supplied, in the
* value style — never split, never printed twice.
*
* Truth: every label, value, issuer and source renders exactly as supplied.
* No default credential, number, issuer, seal, check mark or claim of
* verification. A source that is an http(s) URL becomes a link named by its
* host; any other source is shown as plain text. No entries, no section.
*
* Responsive (container queries on the section's own width): stacked below
* 640px; from 640px the header splits over the full-width ledger; from
* 1100px, 4–6 entries form a two-column register (rows aligned across,
* value over details in each cell), while 2–3 entries stay one column with
* details beside each record (from 600px of the ledger's width). Row spacing
* and value size follow the entry count (2–3 roomy, 4 standard, 5–6 compact).
*
* Hooks (read, never declared): --crl-bg, --crl-text, --crl-text-muted,
* --crl-brand, --crl-brand-text, --crl-border, --crl-family, --crl-display,
* --crl-grid-max, --crl-grid-gutter, --crl-heading-size. No script.
*/
export interface CredentialLedgerEntry {
/** The public line exactly as supplied. Without a value: an identifier row if it holds 3+ digits, else a statement row. */
label: string;
/** The identifier alone, exactly as supplied. */
value?: string;
/** Who issued or holds the record, as supplied. */
issuer?: string;
/** One short line about what the record covers. */
description?: string;
/** Where it can be checked: a registry URL (linked) or plain text, as supplied. */
source?: string;
}
export interface CredentialLedgerProps {
heading: string;
headingLevel?: "h2" | "h3";
eyebrow?: string;
/** One sentence beside the heading. */
intro?: string;
/** A short provenance note under the intro — never a claim of verification. */
note?: string;
entries?: readonly CredentialLedgerEntry[];
fontStylesheet?: string;
class?: string;
id?: string;
}
interface Props extends CredentialLedgerProps {}
const {
heading,
headingLevel = "h2",
eyebrow = "",
intro = "",
note = "",
entries = [],
fontStylesheet = "https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,400;8..60,500&family=Public+Sans:wght@400;500;600&display=swap",
class: className,
id,
} = Astro.props;
const text = (v: unknown) => (typeof v === "string" ? v.trim() : "");
/* A source links only when it is a web address; anything else is shown as text. */
const webHref = (v: string) => (/^https?:\/\/[^\s]+$/i.test(v) ? v : "");
const hostOf = (href: string) => {
try {
return new URL(href).host.replace(/^www\./i, "");
} catch {
return "";
}
};
const rows = (entries ?? [])
.filter((e) => e && text(e.label))
.map((e) => {
const source = text(e.source);
const href = webHref(source);
const host = href ? hostOf(href) : "";
const label = text(e.label);
const value = text(e.value);
/*
* Presentation only — the text is never split, reworded or reformatted.
* "record": a label over its value. "identifier": the label already carries
* the number (no value, 3+ digits in the label; or the label contains the
* value), so the whole label is set once in the value style. "statement":
* no number at all.
*/
const kind = value ? (label.includes(value) ? "identifier" : "record") : /\d{3,}/.test(label) ? "identifier" : "statement";
return {
kind,
label,
value,
issuer: text(e.issuer),
description: text(e.description),
source: href && host ? "" : source,
href: href && host ? href : "",
host,
};
});
const count = rows.length;
const density = count <= 3 ? "roomy" : count >= 5 ? "compact" : "standard";
const Heading = headingLevel;
---
{count > 0 && (
<>
{fontStylesheet && <link rel="stylesheet" href={fontStylesheet} />}
<section class:list={["crl", className]} id={id} data-density={density} data-cols={count >= 4 ? "2" : "1"}>
<div class="crl-inner">
<div class:list={["crl-lead", { "crl-lead--solo": !intro }]}>
<div class="crl-head">
{eyebrow && <p class="crl-eyebrow">{eyebrow}</p>}
<Heading class="crl-title">{heading}</Heading>
</div>
{intro && <p class="crl-intro">{intro}</p>}
{note && <p class="crl-note">{note}</p>}
</div>
<ol class="crl-ledger" role="list">
{rows.map((r) => {
const hasMeta = Boolean(r.issuer || r.description || r.source || r.href);
return (
<li class:list={["crl-entry", { "crl-entry--statement": r.kind === "statement", "crl-entry--identifier": r.kind === "identifier", "crl-entry--solo": !hasMeta }]}>
{r.kind === "record" ? (
<dl class="crl-record">
<dt class="crl-label">{r.label}</dt>
<dd class="crl-value">{r.value}</dd>
</dl>
) : r.kind === "identifier" ? (
<p class="crl-value crl-value--id">{r.label}</p>
) : (
<p class="crl-statement">{r.label}</p>
)}
{hasMeta && (
<div class="crl-meta">
{r.issuer && <p class="crl-issuer">{r.issuer}</p>}
{r.description && <p class="crl-desc">{r.description}</p>}
{r.href ? (
<p class="crl-source">
<span class="crl-source-key">Source</span>
<a class="crl-source-link" href={r.href} target="_blank" rel="noopener noreferrer">
{r.host}
<span class="crl-sr"> (opens in a new tab)</span>
<svg class="crl-arrow" viewBox="0 0 12 12" width="11" height="11" aria-hidden="true" focusable="false"><path d="M3.5 2.5h6v6M9.5 2.5 2.5 9.5" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="square" /></svg>
</a>
</p>
) : r.source ? (
<p class="crl-source">
<span class="crl-source-key">Source</span>
<span class="crl-source-text">{r.source}</span>
</p>
) : null}
</div>
)}
</li>
);
})}
</ol>
</div>
</section>
</>
)}
<style>
.crl {
/* Own default palette: cool near-white ground, deep ink, a cobalt accent strong enough to read on the head rule. */
--crl-surface: var(--crl-bg, #f7f8fa);
--crl-ink: var(--crl-text, #0f1724);
--crl-soft: var(--crl-text-muted, #3d4555);
--crl-accent: var(--crl-brand, #1a47b8);
--crl-accent-ink: var(--crl-brand-text, #ffffff);
--crl-line: var(--crl-border, #d3d8e1);
--crl-font: var(--crl-family, "Public Sans", ui-sans-serif, system-ui, sans-serif);
--crl-font-display: var(--crl-display, "Source Serif 4", ui-serif, Georgia, serif);
/* Density: set by the entry count. cqi resolves against the ledger, where these are used. */
--crl-row-pad: 30px;
--crl-value-size: clamp(30px, 6.4cqi, 44px);
/* Statements sit a clear step below the records: same face, ~55% of the value size. */
--crl-statement-size: max(19px, calc(var(--crl-value-size) * 0.55));
container-type: inline-size;
container-name: crl;
background: var(--crl-surface);
color: var(--crl-ink);
font-family: var(--crl-font);
}
.crl[data-density="roomy"] {
--crl-row-pad: 38px;
--crl-value-size: clamp(32px, 7.4cqi, 52px);
}
.crl[data-density="compact"] {
--crl-row-pad: 22px;
--crl-value-size: clamp(28px, 5.4cqi, 38px);
}
.crl *,
.crl *::before,
.crl *::after {
box-sizing: border-box;
}
.crl-inner {
display: grid;
gap: 40px;
max-width: var(--crl-grid-max, 1232px);
margin: 0 auto;
padding: 64px 20px 72px;
}
.crl-lead {
min-width: 0;
}
.crl-head {
min-width: 0;
}
/* Sentence case throughout; no tracked capitals anywhere. */
.crl-eyebrow {
margin: 0 0 16px;
color: var(--crl-accent);
font: 600 15px/1.35 var(--crl-font);
}
.crl-title {
max-width: 15ch;
margin: 0;
color: var(--crl-ink);
font-family: var(--crl-font-display);
font-size: var(--crl-heading-size, clamp(34px, 5.2cqi, 48px));
font-weight: 400;
line-height: 1.06;
letter-spacing: -0.02em;
text-wrap: balance;
overflow-wrap: anywhere;
}
.crl-intro {
max-width: 38ch;
margin: 20px 0 0;
color: var(--crl-soft);
font: 400 17px/1.6 var(--crl-font);
text-wrap: pretty;
overflow-wrap: anywhere;
}
.crl-note {
max-width: 38ch;
margin: 28px 0 0;
padding-top: 16px;
border-top: 1px solid var(--crl-line);
color: var(--crl-soft);
font: 400 14px/1.55 var(--crl-font);
font-style: italic;
overflow-wrap: anywhere;
}
/* The ledger: a heavy head rule, hairlines between records. */
.crl-ledger {
container-type: inline-size;
container-name: crl-ledger;
position: relative;
min-width: 0;
margin: 0;
padding: 0;
list-style: none;
border-top: 2px solid var(--crl-ink);
}
.crl-ledger::before {
content: "";
position: absolute;
top: -3px;
left: 0;
width: 88px;
height: 4px;
background: var(--crl-accent);
}
.crl-entry {
display: grid;
gap: 14px;
min-width: 0;
padding-block: var(--crl-row-pad);
border-bottom: 1px solid var(--crl-line);
}
.crl-record {
display: grid;
gap: 10px;
min-width: 0;
margin: 0;
}
/* Sentence case, as supplied: a quiet caption over the value, not a caps tier. */
.crl-label {
margin: 0;
color: var(--crl-soft);
font: 500 14.5px/1.4 var(--crl-font);
overflow-wrap: anywhere;
}
.crl-value {
margin: 0;
color: var(--crl-ink);
font-family: var(--crl-font-display);
font-size: var(--crl-value-size);
font-weight: 400;
line-height: 1;
letter-spacing: -0.01em;
font-variant-numeric: tabular-nums lining-nums;
font-feature-settings: "tnum" 1, "lnum" 1;
overflow-wrap: anywhere;
}
/* An identifier row: the whole supplied label in the value style, allowed to wrap cleanly. */
.crl-value--id {
line-height: 1.06;
text-wrap: balance;
}
/*
* A statement row: the label is the record, set flush on the same left edge
* as the values and held by the same hairlines — no marker of its own.
*/
.crl-statement {
max-width: 34ch;
margin: 0;
color: var(--crl-ink);
font-family: var(--crl-font-display);
font-size: var(--crl-statement-size);
font-weight: 400;
line-height: 1.2;
letter-spacing: -0.01em;
text-wrap: balance;
overflow-wrap: anywhere;
}
.crl-meta {
display: grid;
gap: 6px;
min-width: 0;
max-width: 46ch;
}
.crl-issuer {
margin: 0;
color: var(--crl-ink);
font: 600 15px/1.45 var(--crl-font);
overflow-wrap: anywhere;
}
.crl-desc {
margin: 0;
color: var(--crl-soft);
font: 400 15px/1.55 var(--crl-font);
text-wrap: pretty;
overflow-wrap: anywhere;
}
.crl-source {
display: flex;
flex-wrap: wrap;
align-items: center;
column-gap: 10px;
margin: 2px 0 0;
color: var(--crl-soft);
font: 400 13.5px/1.45 var(--crl-font);
overflow-wrap: anywhere;
}
.crl-source-key {
font-style: italic;
}
.crl-source-text {
min-width: 0;
}
/* A 44px target from padding cancelled by margin: the row keeps the same line rhythm as a plain-text source. */
.crl-source-link {
display: inline-flex;
align-items: center;
gap: 5px;
min-width: 0;
padding-block: 12px;
margin-block: -12px;
color: var(--crl-accent);
font-weight: 500;
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 3px;
text-decoration-color: color-mix(in srgb, var(--crl-accent) 45%, transparent);
overflow-wrap: anywhere;
transition: text-decoration-color 160ms ease;
}
.crl-source-link:hover {
text-decoration-color: currentColor;
}
.crl-source-link:focus-visible {
outline: 2px solid var(--crl-accent);
outline-offset: 2px;
border-radius: 2px;
}
.crl-arrow {
flex: none;
}
.crl-sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
/* Ledger wide enough: the record and its details side by side. */
@container crl-ledger (min-width: 600px) {
.crl-entry {
grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
column-gap: 40px;
align-items: start;
}
.crl-entry--solo {
grid-template-columns: minmax(0, 1fr);
}
.crl-meta {
padding-top: 2px;
}
.crl-entry--statement .crl-meta {
padding-top: 4px;
}
}
/*
* From 640px of the section's width: a split header — eyebrow and heading
* left; intro with the note directly under it right, top-aligned — over a
* full-width ledger.
*/
@container crl (min-width: 640px) {
.crl-inner {
gap: 48px;
padding: 80px 40px 88px;
}
.crl-lead {
display: grid;
grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
grid-template-rows: auto 1fr;
column-gap: 40px;
align-items: start;
}
.crl-head {
grid-row: 1 / span 2;
}
.crl-intro {
margin-top: 4px;
}
.crl-note {
margin-top: 20px;
}
/* No intro: nothing to split beside the heading, so the lead stays one column (see .crl-lead--solo). */
.crl-lead.crl-lead--solo {
display: block;
}
}
/*
* No intro: the note sits directly under the heading. Its rule exists to separate it from an intro; with
* none, it would float alone at the top of an empty second column.
*/
.crl-lead--solo .crl-note {
margin-top: 16px;
padding-top: 0;
border-top: 0;
}
@container crl (min-width: 960px) {
.crl-inner {
gap: 56px;
padding: 104px var(--crl-grid-gutter, 48px) 112px;
}
.crl-lead {
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
column-gap: 56px;
}
/* The intro and note start close to the heading and run wider, so no dead gap opens between them. */
.crl-intro {
max-width: 46ch;
margin-top: 8px;
font-size: 18px;
}
.crl-note {
max-width: 46ch;
}
}
/*
* Wide section, 4–6 entries: a two-column register. Rows align across the
* columns (one grid), each record stacks its value over its details, and
* every cell keeps its own hairline. Two or three entries stay one column,
* details beside each record.
*/
@container crl (min-width: 1100px) {
.crl[data-cols="2"] .crl-ledger {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: 64px;
}
.crl[data-cols="2"] .crl-entry,
.crl[data-cols="2"] .crl-entry--solo {
grid-template-columns: minmax(0, 1fr);
align-content: start;
row-gap: 16px;
}
.crl[data-cols="2"] .crl-meta {
padding-top: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.crl-source-link {
transition: none;
}
}
@media (forced-colors: active) {
.crl-ledger::before {
background: CanvasText;
border-color: CanvasText;
}
.crl-source-link {
color: LinkText;
}
}
</style>
===== END src/components/CredentialLedger.astro =====
===== FILE (supporting): src/components/CredentialLedger.md =====
# Credential ledger
Verified authority as a ruled record: a split header (heading; one sentence and a provenance note) over a ledger of the business's own licences and registrations — a sentence-case label over the identifier in large tabular figures, with issuer, a short line and a source (a registry link only when one is supplied). Value-less credentials become statement rows. No seals, badges or icons.
## Integration
- A section heading (h2 by default). No entries, no section; designed for 2–6 entries.
- Every label, value, issuer and source renders exactly as supplied. An entry without a value is an identifier row when its label holds a run of 3+ digits (the whole label set in the value style), otherwise a statement row — never an empty number slot. A label that contains its value is shown once, not twice.
- A source that is an http(s) URL renders as a link named by its host (new tab, announced); any other source renders as plain text.
- Container queries on the section's own width: stacked below 640px; split header over a full-width ledger from 640px; from 1100px, 4–6 entries set as a two-column register, 2–3 entries one column with details beside each record.
- Reads --crl-bg, --crl-text, --crl-text-muted, --crl-brand, --crl-brand-text, --crl-border, --crl-family, --crl-display, --crl-grid-max, --crl-grid-gutter and --crl-heading-size.
- Loads Source Serif 4 and Public Sans from Google Fonts by default. Pass fontStylesheet="" on a site that loads its own fonts.
## Provenance
Staged as candidate `credential-ledger` (Step 28) and admitted through the candidate pipeline. Source type: original. References (direction only): none. Quality history: quality/candidates/records/credential-ledger.json.
===== END src/components/CredentialLedger.md =====
--- VERIFIED IN THE COMPONENT LIBRARY ---
Enrolled for verification but never run.
No verification run has been recorded.
No checks have passed yet.
No usable source fingerprint, so these results cannot be tied to the files below.
--- NOT PROVEN ---
- ClientRouter: not applicable — this component ships no script.
--- WHAT THIS DOES NOT CERTIFY ---
Verification was performed on the unmodified component, in an isolated fixture, in the
library. It says nothing about this project's CSS, routes, layout or data, and nothing
about the component once you adapt it. A verified component is a good starting point,
not a guarantee about the installation you are about to build.
--- TEST IN THIS PROJECT AFTER INSTALLING OR CUSTOMISING ---
- Horizontal overflow at 1440, 1280, 1024, 834, 390 and 320px.
- Keyboard operation end to end, including visible focus.
- The no-JavaScript render.
- prefers-reduced-motion: reduce.
- Two instances on one page, if this project renders more than one.
- If this project uses <ClientRouter />: teardown and reinitialisation across a real navigation.