News

MatchScore

A game's score: both teams with crests or colours, the score and scorers, and where it stands: kick-off, live with the minute, half time, full time or postponed.

#news#sports#card#live#no-js

Demo

Preview
Premier League · Matchday 10 · Emirates StadiumLive 67'
  • Arsenal2

    Saka 23', Ødegaard 51'

  • Chelsea1

    Palmer 40' (pen)

Cup · Quarter-finalFT
  • Real Sociedad(a.e.t.)1
  • Porto1

Porto win 4–3 on penalties

FT
  • Lyon0
  • Nantes3
Premier League
  • Leeds United
  • Everton
Postponed
  • Bristol City
  • Hull

Waterlogged pitch; a new date will be set.

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/match_score.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/match_score.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/match_score.qwe
view MatchScoreDemo() =    <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; align-items: start">        <MatchScore id="match-score-demo-live" competition="Premier League · Matchday 10" venue="Emirates Stadium" status="live" minute="67'"                    href="#ars-che"                    home={{ name: "Arsenal", short: "ARS", score: 2, color: "#db0007", scorers: ["Saka 23'", "Ødegaard 51'"] }}                    away={{ name: "Chelsea", short: "CHE", score: 1, color: "#034694", scorers: ["Palmer 40' (pen)"] }}/>        <MatchScore id="match-score-demo-final" competition="Cup · Quarter-final" status="final" winner="away"                    note="Porto win 4–3 on penalties"                    home={{ name: "Real Sociedad", score: 1, color: "#0067b1", note: "(a.e.t.)" }}                    away={{ name: "Porto", score: 1, color: "#1d3b85" }}/>        <MatchScore id="match-score-demo-ft" status="final" size="sm"                    home={{ name: "Lyon", score: 0, color: "#d6001c" }} away={{ name: "Nantes", score: 3, color: "#fcd405" }}/>        <MatchScore id="match-score-demo-next" competition="Premier League" status="scheduled" kickoff="2026-11-01T15:00:00Z" kickoff_text="Sat 3:00 PM"                    home={{ name: "Leeds United", color: "#1d428a" }} away={{ name: "Everton", color: "#003399" }}/>        <MatchScore id="match-score-demo-off" status="postponed" note="Waterlogged pitch; a new date will be set."                    home={{ name: "Bristol City" }} away={{ name: "Hull" }}/>    </div>

Source

ui/match_score.qwe
// MatchScore: a game's score as the sports pages show it: the two teams with their// crests (or their colours and initials), the score, who scored and when, and where// the game stands: the kick-off time before it, LIVE and the minute during it (in// red, pulsing), half time, full time with the winner in bold, or postponed.////   <MatchScore competition="Premier League · Matchday 10" venue="Emirates Stadium" status="live" minute="67'"//               home={{ name: "Arsenal", short: "ARS", score: 2, color: "#ef0107", scorers: ["Saka 23'", "Ødegaard 51'"] }}//               away={{ name: "Chelsea", short: "CHE", score: 1, color: "#034694", scorers: ["Palmer 40' (pen)"] }}/>//   <MatchScore status="scheduled" kickoff="2026-11-01T15:00:00Z" kickoff_text="Sat 3:00 PM" home={{ name: "Leeds" }} away={{ name: "Everton" }}/>//// home and away: maps of { name, score?, short?, crest?, color?, scorers?, note? }:// short: three letters, in the crest when there is no picture (else the initials);// crest: a picture's URL; color: the team's colour (the theme's grey without);// scorers: lines under the team ("Saka 23'"); note: a word after the name ("(a.e.t.)").// status: "scheduled" (default), "live", "halftime", "final" or "postponed".// minute: the game's clock while live ("67'", "90+3'"). kickoff: an ISO date-time,// for <time>; kickoff_text: how it reads ("Sat 3:00 PM"). competition and venue: a// line over the teams. note: a line under them ("Arsenal win 4–3 on penalties",// "Agg. 3–2"). winner: "home" or "away", when the score does not say (penalties).// href: the whole card links to the game. size: "md" (default) or "sm" (tighter, for// a strip of scores). id: the card's id.import ui/theme.qwe use ui_clslet match_score_css = css`.ui-mscore { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; min-width: 0; max-width: 380px; padding: 14px 16px; border: 1px solid var(--ui-border);  border-radius: var(--ui-radius); background: var(--ui-surface); color: var(--ui-fg); font: 14px/1.35 var(--ui-font); font-variant-numeric: tabular-nums; }.ui-mscore-sm { gap: 6px; padding: 10px 12px; font-size: 13px; }.ui-mscore-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; color: var(--ui-muted); font-size: 12px; }.ui-mscore-where { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.ui-mscore-status { flex: none; display: inline-flex; align-items: center; gap: 5px; padding: 1px 8px; border-radius: 999px;  background: var(--ui-subtle); color: var(--ui-muted); font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; }.ui-mscore-live .ui-mscore-status { background: var(--ui-danger); color: var(--ui-bg); }.ui-mscore-live .ui-mscore-status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor;  animation: ui-mscore-pulse 1.4s ease-in-out infinite; }@keyframes ui-mscore-pulse { 50% { opacity: .2; } }.ui-mscore-halftime .ui-mscore-status { background: var(--ui-warning-soft); color: var(--ui-warning); }.ui-mscore-postponed .ui-mscore-status { background: var(--ui-danger-soft); color: var(--ui-danger); }.ui-mscore-teams { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }.ui-mscore-team { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 2px 10px; }.ui-mscore-crest { grid-row: span 2; align-self: start; display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%;  background: var(--c, var(--ui-muted)); color: #fff; font-size: 10px; font-weight: 800; letter-spacing: .02em; overflow: hidden; }.ui-mscore-crest img { width: 100%; height: 100%; object-fit: contain; background: var(--ui-surface); }.ui-mscore-sm .ui-mscore-crest { width: 22px; height: 22px; font-size: 8px; }.ui-mscore-name { min-width: 0; font-size: 15px; font-weight: 600; }.ui-mscore-sm .ui-mscore-name { font-size: 14px; }.ui-mscore-name small { margin-left: 4px; color: var(--ui-muted); font-size: 12px; font-weight: 400; }.ui-mscore-goals { font-size: 24px; line-height: 1; font-weight: 800; min-width: 1ch; text-align: right; }.ui-mscore-sm .ui-mscore-goals { font-size: 17px; }.ui-mscore-scorers { grid-column: 2 / -1; margin: 0; color: var(--ui-muted); font-size: 12px; }.ui-mscore-final .ui-mscore-lost .ui-mscore-name, .ui-mscore-final .ui-mscore-lost .ui-mscore-goals { color: var(--ui-muted); font-weight: 500; }.ui-mscore-final .ui-mscore-won .ui-mscore-name { font-weight: 800; }.ui-mscore-kick { font-size: 15px; font-weight: 700; color: var(--ui-fg); }.ui-mscore-note { margin: 0; padding-top: 8px; border-top: 1px solid var(--ui-border); color: var(--ui-muted); font-size: 12px; }.ui-mscore-link { position: absolute; inset: 0; border-radius: inherit; }.ui-mscore-link:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-mscore:has(.ui-mscore-link:hover) { border-color: var(--ui-strong); }@media (prefers-reduced-motion: reduce) { .ui-mscore-live .ui-mscore-status::before { animation: none; } }`// A team's initials for its crest: "Real Madrid" is "RM".fn match_score_initials(name: str) -> str {    let words = split(trim(name), " ") | filter((w) => w != "")    upper(len(words) == 0 ? "?" : (len(words) == 1 ? slice(words[0], 0, 3) : join(take(words, 3) | map((w) => w[0]), "")))}view MatchScore(home, away, status: str = "scheduled", minute: str? = null, kickoff: str? = null, kickoff_text: str? = null,                competition: str? = null, venue: str? = null, note: str? = null, winner: str? = null, href: str? = null,                size: str = "md", id: str? = null, class: str? = null) {    style global match_score_css    let started = contains(["live", "halftime", "final"], status)    let won = winner ?? (status == "final" && home.score != null && away.score != null && home.score != away.score        ? (home.score > away.score ? "home" : "away") : null)    let label = match status {        "live" => minute != null ? `Live ${minute}` : "Live"        "halftime" => "HT"        "final" => "FT"        "postponed" => "Postponed"        _ => null    }    let spoken = match status {        "live" => minute != null ? `live, ${minute}` : "live"        "halftime" => "half time"        "final" => "full time"        "postponed" => "postponed"        _ => kickoff_text != null ? `kick-off ${kickoff_text}` : "not started"    }    let summary = started        ? `${home.name} ${home.score ?? 0}, ${away.name} ${away.score ?? 0}, ${spoken}`        : `${home.name} against ${away.name}, ${spoken}`    let where = join([competition, venue] | filter((x) => x != null), " · ")    let side = (t, which: str) =>        <li class={ui_cls(["ui-mscore-team", won != null && (won == which ? "ui-mscore-won" : "ui-mscore-lost")])} data-side={which}>            <span.ui-mscore-crest style={t.color != null ? `--c: ${t.color}` : null} aria-hidden="true">                {t.crest != null ? <img src={t.crest} alt=""/> : (t.short ?? match_score_initials(t.name))}            </span>            <span.ui-mscore-name>{t.name}{t.note != null && <small>{t.note}</small>}</span>            {started && <span.ui-mscore-goals>{t.score ?? 0}</span>}            {t.scorers != null && len(t.scorers) > 0 && size != "sm" && <p.ui-mscore-scorers>{join(t.scorers, ", ")}</p>}        </li>    <article class={ui_cls(["ui-mscore", `ui-mscore-${status}`, size == "sm" && "ui-mscore-sm", class])} id={id} aria-label={summary}>        <div.ui-mscore-top>            <span.ui-mscore-where title={where != "" ? where : null}>{where}</span>            {label != null                ? <span.ui-mscore-status>{label}</span>                : (kickoff != null || kickoff_text != null ? <time.ui-mscore-kick datetime={kickoff}>{kickoff_text ?? kickoff}</time> : null)}        </div>        <ul.ui-mscore-teams>            {side(home, "home")}            {side(away, "away")}        </ul>        {note != null && <p.ui-mscore-note>{note}</p>}        {href != null && <a.ui-mscore-link href={href} aria-label={summary}></a>}    </article>}
Thread
108Useful? Like it, or add to its thread below.

Thread 0 posts

On MatchScore: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.