CreditScore
A credit score on a gauge of its bands from poor to excellent, the band it falls in, the change since the last report and the factors that move it.
Demo
up +14 pointssince last month
- Poor 300–579
- Fair 580–669
- Good, your score 670–739
- Very good 740–799
- Excellent 800–850
What moves your score
- Payment history, helping 100% on time high impact
- Credit card use, hurting Keeping it under 30% helps. 38% of limits high impact
- Age of credit, neither helping nor hurting 6 yrs 4 mos medium impact
- Hard inquiries, hurting Each one counts for two years. 2 this year low impact
- Total accounts, helping 9 low impact
ClearView Credit, updated Oct 1, 2026
down -23 pointssince last quarter
- Poor, your score 300–579
- Fair 580–669
- Good 670–739
- Very good 740–799
- Excellent 800–850
unchanged 0 pointssince last report
- Poor 0–438
- Fair 439–720
- Good 721–960
- Excellent, your score 961–999
Meridian Bureau, updated 12 Sept 2026
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/credit_score.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/credit_score.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/credit_score.qwe
view CreditScoreDemo() { <div.demo-stack> <CreditScore id="credit-score-demo-good" score={712} change={14} period="since last month" updated="2026-10-01" source="ClearView Credit" factors={[ { label: "Payment history", value: "100% on time", effect: "good", impact: "high" }, { label: "Credit card use", value: "38% of limits", effect: "bad", impact: "high", note: "Keeping it under 30% helps." }, { label: "Age of credit", value: "6 yrs 4 mos", effect: "neutral", impact: "medium" }, { label: "Hard inquiries", value: "2 this year", effect: "bad", impact: "low", note: "Each one counts for two years." }, { label: "Total accounts", value: "9", effect: "good", impact: "low" }, ]}/> <div.demo-row style="align-items: stretch"> <div style="flex: 1 1 300px; max-width: 420px"> <CreditScore id="credit-score-demo-poor" score={548} change={-23} period="since last quarter" label="Your credit score"/> </div> <div style="flex: 1 1 300px; max-width: 420px"> <CreditScore id="credit-score-demo-scale" score={961} low={0} high={999} change={0} source="Meridian Bureau" updated="2026-09-12" locale="en-GB" bands={[{ label: "Poor", from: 0 }, { label: "Fair", from: 439 }, { label: "Good", from: 721 }, { label: "Excellent", from: 961 }]}/> </div> </div> </div>}Source
ui/credit_score.qwe
// CreditScore: a credit score on a half-circle gauge of its bands, from poor to// excellent, with a marker at the score, the score and its band's name in the// middle, the change since the last report, the bands' ranges as a legend (the// score's band marked), and the factors that move it, each with how much it// weighs and whether it helps or hurts.//// <CreditScore score={712} change={14} updated="2026-10-01" source="ClearView Credit" factors={[// { label: "Payment history", value: "100% on time", effect: "good", impact: "high" },// { label: "Credit card use", value: "38% of limits", effect: "bad", impact: "high", note: "Under 30% helps" }]}/>// <CreditScore score={540} low={0} high={999} bands={[{ label: "Poor", from: 0 }, { label: "Fair", from: 439 },// { label: "Good", from: 721 }, { label: "Excellent", from: 961 }]}/>//// score: the score (an int). low, high: the scale's ends (300 and 850). bands: a// list of { label, from } from the lowest up, each running to the next one's from// (the last to high); by default Poor 300, Fair 580, Good 670, Very good 740 and// Excellent 800. They are coloured red to green whatever their number.// change: the points it moved since the last report (↑ green, ↓ red); period:// what that is since ("since last month"). updated: the report's date (ISO), and// source who made it, in a line at the bottom. factors: a list of { label, value?,// effect: "good", "bad" or "neutral", impact: "high", "medium" or "low", note? }.// locale: how dates are written (the program's; en-US). label: what screen// readers call the gauge ("Credit score"). id, class.import ui/theme.qwe use ui_clslet credit_score_css = css`.ui-credit-score { container-type: inline-size; display: grid; gap: 18px; min-width: 0; padding: 18px 20px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); color: var(--ui-fg); font: 14px/1.45 var(--ui-font); --ui-credit-score-c0: var(--ui-danger); --ui-credit-score-c1: var(--ui-warning); --ui-credit-score-c2: color-mix(in srgb, var(--ui-success) 55%, #c9b400); --ui-credit-score-c3: var(--ui-success); --ui-credit-score-c4: color-mix(in srgb, var(--ui-success) 70%, var(--ui-info)); }.ui-credit-score-main { display: grid; gap: 18px; align-items: center; }@container (min-width: 560px) { .ui-credit-score-main { grid-template-columns: minmax(220px, 300px) 1fr; } }.ui-credit-score-dial { position: relative; justify-self: center; width: 100%; max-width: 300px; }.ui-credit-score-dial svg { display: block; width: 100%; height: auto; overflow: visible; }.ui-credit-score-arc { fill: none; stroke-width: 16; stroke-linecap: butt; opacity: .28; }.ui-credit-score-arc[data-current] { opacity: 1; }.ui-credit-score-mark { fill: var(--ui-surface); stroke: var(--ui-fg); stroke-width: 3; }.ui-credit-score-ends { fill: var(--ui-muted); font: 10px var(--ui-font); }.ui-credit-score-read { position: absolute; left: 0; right: 0; bottom: 0; display: grid; justify-items: center; gap: 0; text-align: center; }.ui-credit-score-num { font-size: 40px; font-weight: 750; letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums; }.ui-credit-score-band { margin-top: 4px; font-size: 14px; font-weight: 650; color: var(--ui-credit-score-tone); }.ui-credit-score-change { display: inline-flex; align-items: center; gap: 3px; margin-top: 4px; padding: 1px 8px; border-radius: 999px; font-size: 12px; font-weight: 600; background: var(--ui-subtle); color: var(--ui-muted); font-variant-numeric: tabular-nums; }.ui-credit-score-up { background: var(--ui-success-soft); color: var(--ui-success); }.ui-credit-score-down { background: var(--ui-danger-soft); color: var(--ui-danger); }.ui-credit-score-period { color: var(--ui-muted); font-size: 12px; }.ui-credit-score-legend { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; font-size: 13px; }.ui-credit-score-legend li { display: grid; grid-template-columns: 10px 1fr auto; align-items: center; gap: 10px; padding: 5px 10px; border-radius: var(--ui-radius-sm); }.ui-credit-score-legend li::before { content: ""; width: 10px; height: 10px; border-radius: 3px; background: var(--ui-credit-score-tone); }.ui-credit-score-legend li[data-current] { background: var(--ui-subtle); font-weight: 650; }.ui-credit-score-legend span:last-child { color: var(--ui-muted); font-variant-numeric: tabular-nums; }.ui-credit-score-factors h4 { margin: 0 0 8px; font-size: 13px; font-weight: 650; }.ui-credit-score-factors ul { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--ui-border); }.ui-credit-score-factor { display: grid; grid-template-columns: 24px 1fr auto; align-items: center; gap: 2px 12px; padding: 10px 2px; border-bottom: 1px solid var(--ui-border); }.ui-credit-score-effect { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: var(--ui-subtle); color: var(--ui-muted); font-size: 13px; font-weight: 700; }.ui-credit-score-good .ui-credit-score-effect { background: var(--ui-success-soft); color: var(--ui-success); }.ui-credit-score-bad .ui-credit-score-effect { background: var(--ui-danger-soft); color: var(--ui-danger); }.ui-credit-score-what b { display: block; font-weight: 600; }.ui-credit-score-what small { display: block; color: var(--ui-muted); font-size: 12px; }.ui-credit-score-value { font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }.ui-credit-score-impact { display: block; color: var(--ui-muted); font-size: 11px; font-weight: 500; letter-spacing: .03em; text-transform: uppercase; }.ui-credit-score-foot { margin: 0; color: var(--ui-muted); font-size: 12px; }.ui-credit-score-t0 { --ui-credit-score-tone: var(--ui-credit-score-c0); }.ui-credit-score-t1 { --ui-credit-score-tone: var(--ui-credit-score-c1); }.ui-credit-score-t2 { --ui-credit-score-tone: var(--ui-credit-score-c2); }.ui-credit-score-t3 { --ui-credit-score-tone: var(--ui-credit-score-c3); }.ui-credit-score-t4 { --ui-credit-score-tone: var(--ui-credit-score-c4); }.ui-credit-score-arc { stroke: var(--ui-credit-score-tone); }`let credit_score_bands = [ { label: "Poor", from: 300 }, { label: "Fair", from: 580 }, { label: "Good", from: 670 }, { label: "Very good", from: 740 }, { label: "Excellent", from: 800 },]// A number as SVG writes it, to two decimals.fn credit_score_at(n: num) -> str { let r = round(n * 100) / 100 r == floor(r) ? str(int(r)) : str(r)}// The point on the dial (centre 100,100, radius 80) a fraction f of the way round.fn credit_score_point(f: num) -> str { let a = math.pi * (1 - f) `${credit_score_at(100 + 80 * math.cos(a))},${credit_score_at(100 - 80 * math.sin(a))}`}view CreditScore(score: int, low: int = 300, high: int = 850, bands: list = credit_score_bands, change: int? = null, period: str = "since last report", updated: str? = null, source: str? = null, factors: list = [], locale: str? = null, label: str = "Credit score", id: str? = null, class: str? = null) { style global credit_score_css let span = high > low ? high - low : 1 let kept = score < low ? low : (score > high ? high : score) let frac = (v: num) => (v - low) / span let n = len(bands) // Band i's colour, 0 (red) to 4 (green), spread over however many bands there are. let tone = (i: int) => n <= 1 ? 3 : int(round(i * 4 / (n - 1))) let ranges = for [i, b] in enumerate(bands) => { label: b.label, from: b.from, to: i + 1 < n ? bands[i + 1].from - 1 : high, tone: tone(i) } let here = find(ranges, (r) => kept >= r.from && kept <= r.to) ?? ranges[0] let gap = 0.006 <section class={ui_cls(["ui-credit-score", class])} id={id} aria-label={label}> <div.ui-credit-score-main> <div class={`ui-credit-score-dial ui-credit-score-t${here.tone}`} role="meter" aria-label={label} aria-valuemin={low} aria-valuemax={high} aria-valuenow={score} aria-valuetext={`${score} out of ${high}, ${here.label}`}> <svg viewBox="0 0 200 112" aria-hidden="true"> {for [i, r] in enumerate(ranges) { let a = frac(r.from) + (i > 0 ? gap : 0) let b = frac(i + 1 < n ? r.to + 1 : high) - (i + 1 < n ? gap : 0) <path class={`ui-credit-score-arc ui-credit-score-t${r.tone}`} key={r.label} data-current={r.label == here.label ? "" : null} d={`M${credit_score_point(a)} A80,80 0 0 1 ${credit_score_point(b)}`}/> }} <circle.ui-credit-score-mark cx={split(credit_score_point(frac(kept)), ",")[0]} cy={split(credit_score_point(frac(kept)), ",")[1]} r="8"/> <text.ui-credit-score-ends x="20" y="112" text-anchor="middle">{low}</text> <text.ui-credit-score-ends x="180" y="112" text-anchor="middle">{high}</text> </svg> <div.ui-credit-score-read aria-hidden="true"> <span.ui-credit-score-num data-key="score">{score}</span> <span.ui-credit-score-band data-key="band">{here.label}</span> </div> </div> <div style="display: grid; gap: 12px"> {change != null && <p style="margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px"> <span class={ui_cls(["ui-credit-score-change", change > 0 && "ui-credit-score-up", change < 0 && "ui-credit-score-down"])} data-key="change"> <span aria-hidden="true">{change > 0 ? "↑" : (change < 0 ? "↓" : "→")}</span> <span.ui-sr>{change > 0 ? "up " : (change < 0 ? "down " : "unchanged ")}</span>{change > 0 ? `+${change}` : str(change)} {abs(change) == 1 ? "point" : "points"} </span> <span.ui-credit-score-period>{period}</span> </p>} <ul.ui-credit-score-legend aria-label="Score bands"> {for r in ranges => <li class={`ui-credit-score-t${r.tone}`} key={r.label} data-current={r.label == here.label ? "" : null}> <span>{r.label}{r.label == here.label && <span.ui-sr>, your score</span>}</span>{" "} <span>{r.from}–{r.to}</span> </li>} </ul> </div> </div> {len(factors) > 0 && <div.ui-credit-score-factors> <h4>What moves your score</h4> <ul> {for f in factors { let effect = f.effect ?? "neutral" <li class={`ui-credit-score-factor ui-credit-score-${effect}`} key={f.label} data-effect={effect}> <span.ui-credit-score-effect aria-hidden="true">{effect == "good" ? "↑" : (effect == "bad" ? "↓" : "–")}</span>{" "} <span.ui-credit-score-what> <b>{f.label}<span.ui-sr>{effect == "good" ? ", helping" : (effect == "bad" ? ", hurting" : ", neither helping nor hurting")}</span></b>{" "} {f.note != null && <small>{f.note}</small>} </span>{" "} <span.ui-credit-score-value>{f.value}{" "} {f.impact != null && <span.ui-credit-score-impact>{f.impact} impact</span>} </span> </li> }} </ul> </div>} {(updated != null || source != null) && <p.ui-credit-score-foot> {source != null ? source : "Report"}{updated != null ? `, updated ${date.format(updated, { locale: locale ?? "en-US", style: "medium" })}` : ""} </p>} </section>}Related
CurrencyStrengthThe relative strength of the major currencies as bars, strongest first, given or worked out from pairs' changes.7 views · 7 downloadsLoanCommitteeQueueLoan applications before the credit committee: member, amount, purpose, the votes so far, where each stands, and approve or decline per row.7 views · 10 downloadsPaymentHistoryTwenty-four months of payments as a grid of years and months, on time, 30, 60 or 90 days late, or nothing due, as credit reports show them, with a legend.7 views · 7 downloadsAccountCardA bank account at a glance: type, nickname, masked number, current and available balance, what is on hold, and quick actions.8 views · 7 downloadsAmortizationTableA loan's payment schedule worked out from its amount, rate and term: each month's principal, interest and balance, grouped by year with the year's totals.7 views · 7 downloadsCollateralListWhat secures a loan: each item's valuation and documents, and the loan-to-value ratio as a meter against the lender's limit.7 views · 5 downloads
Thread
Thread 0 posts
On CreditScore: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.