KeyPoints
The story in a few lines at the top of an article: points with links, marks or numbers, when they were updated, and the rest folded under Show more.
Demo
What we know
- A magnitude 6.1 earthquake struck at 06:12 local time, 40 km off the northern coast.
- A tsunami warning is in force for the northern islands; people are told to move to higher ground.
- No deaths have been reported; 23 people were hurt, two seriously.
Show 2 moreShow fewer
- Schools on the islands are closed today and ferries are not running.
- Aftershocks of up to 4.8 have followed; more are expected this week.
Written by our editors from the reporting below.
The budget in four lines
- Income tax stays the same; the threshold rises with inflation.
- Fuel duty goes up by 2p a litre from April.
- Free school meals are extended to every primary school.
- Borrowing falls to 3.1% of national income.
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/key_points.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/key_points.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/key_points.qwe
view KeyPointsDemo() = <div.demo-stack style="gap: 20px"> <KeyPoints id="key-points-demo-quake" title="What we know" updated="Updated 14:20 GMT" datetime="2026-10-30T14:20:00Z" visible={3} tone="danger" note="Written by our editors from the reporting below." points={[ "A magnitude 6.1 earthquake struck at 06:12 local time, 40 km off the northern coast.", { text: "A tsunami warning is in force for the northern islands; people are told to move to higher ground.", href: "#warning" }, "No deaths have been reported; 23 people were hurt, two seriously.", "Schools on the islands are closed today and ferries are not running.", { text: "Aftershocks of up to 4.8 have followed; more are expected this week.", href: "#aftershocks" }, ]}/> <KeyPoints id="key-points-demo-budget" title="The budget in four lines" numbered={true} level={3} points={[ "Income tax stays the same; the threshold rises with inflation.", "Fuel duty goes up by 2p a litre from April.", "Free school meals are extended to every primary school.", "Borrowing falls to 3.1% of national income.", ]}/> </div>Source
ui/key_points.qwe
// KeyPoints: the story in a few lines, in a box at the top of an article ("Key// points", "What we know", "The story so far"): each point a sentence, a link when it// has one, numbered or with a mark, when it was last brought up to date, a note on// who wrote it, and past the first few the rest folded under "Show 3 more" (the// browser's <details>, so no script).//// <KeyPoints title="What we know" updated="Updated 14:20 GMT" datetime="2026-10-30T14:20:00Z" visible={3} points={[// "A magnitude 6.1 earthquake struck at 06:12, 40 km off the coast.",// { text: "A tsunami warning is in force for the northern islands.", href: "#warning" },// "No deaths have been reported.",// "Schools on the islands are closed today.",// ]}/>//// points: strings, or maps of { text, href? }. title: the heading, an <h2> ("Key// points"); level={3} makes it an <h3>. updated: when, as it reads; datetime: the same// in ISO form. visible: how many show before the rest fold away (all). numbered:// 1, 2, 3 instead of marks. note: a line under them ("Summary by our editors, not// AI"). tone: the box's accent, "accent" (default), "info", "warning" or "danger".// id: the box's id (and its heading's, with "-title").import ui/theme.qwe use ui_clslet key_points_css = css`.ui-kpts { --t: var(--ui-accent); display: grid; gap: 10px; max-width: 680px; padding: 16px 20px 18px; border-radius: var(--ui-radius); border: 1px solid color-mix(in srgb, var(--t) 30%, var(--ui-border)); border-top: 4px solid var(--t); background: color-mix(in srgb, var(--t) 5%, var(--ui-surface)); color: var(--ui-fg); font: 15px/1.5 var(--ui-font); }.ui-kpts-info { --t: var(--ui-info); }.ui-kpts-warning { --t: var(--ui-warning); }.ui-kpts-danger { --t: var(--ui-danger); }.ui-kpts-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 12px; }.ui-kpts .ui-kpts-title { margin: 0; font-size: 15px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; }.ui-kpts-head time { color: var(--ui-muted); font-size: 13px; }.ui-kpts-list { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; counter-reset: ui-kpts var(--s, 0); }.ui-kpts-list li { position: relative; padding-left: 22px; counter-increment: ui-kpts; }.ui-kpts-list li::before { content: ""; position: absolute; left: 2px; top: .55em; width: 8px; height: 8px; border-radius: 2px; background: var(--t); }.ui-kpts-num .ui-kpts-list li { padding-left: 30px; }.ui-kpts-num .ui-kpts-list li::before { content: counter(ui-kpts); top: .1em; left: 0; width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center; background: var(--t); color: var(--ui-bg); font-size: 12px; font-weight: 700; line-height: 20px; text-align: center; }.ui-kpts-list a { color: inherit; text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--t) 60%, transparent); text-decoration-thickness: 2px; text-underline-offset: 3px; }.ui-kpts-list a:hover { color: var(--t); }.ui-kpts-list a:focus-visible, .ui-kpts-more summary:focus-visible { outline: none; box-shadow: var(--ui-focus); border-radius: 3px; }.ui-kpts-more > .ui-kpts-list { margin-top: 8px; }.ui-kpts-more summary { display: inline-block; color: var(--t); font-size: 14px; font-weight: 650; cursor: pointer; list-style: none; }.ui-kpts-more summary::-webkit-details-marker { display: none; }.ui-kpts-more summary::after { content: " ▾"; }.ui-kpts-more[open] summary::after { content: " ▴"; }.ui-kpts-more[open] .ui-kpts-show, .ui-kpts-more:not([open]) .ui-kpts-hide { display: none; }.ui-kpts-note { margin: 0; color: var(--ui-muted); font-size: 12px; }`view KeyPoints(points: list, title: str = "Key points", level: int = 2, updated: str? = null, datetime: str? = null, visible: int? = null, numbered: bool = false, note: str? = null, tone: str = "accent", id: str? = null, class: str? = null) { style global key_points_css let items = points | map((p) => type_of(p) == "str" ? { text: p } : p) let cut = visible != null && visible > 0 && len(items) > visible ? visible : len(items) let rest = drop(items, cut) let row = (p) => <li>{p.href != null ? <a href={p.href}>{p.text}</a> : p.text}</li> let group = (xs: list, from: int) => numbered ? <ol.ui-kpts-list style={from > 0 ? `--s: ${from}` : null} start={from > 0 ? from + 1 : null}>{for p in xs => row(p)}</ol> : <ul.ui-kpts-list>{for p in xs => row(p)}</ul> let title_id = id != null ? `${id}-title` : null <section class={ui_cls(["ui-kpts", tone != "accent" && `ui-kpts-${tone}`, numbered && "ui-kpts-num", class])} id={id} aria-labelledby={title_id} aria-label={title_id == null ? title : null}> <div.ui-kpts-head> {level == 3 ? <h3.ui-kpts-title id={title_id}>{title}</h3> : <h2.ui-kpts-title id={title_id}>{title}</h2>} {updated != null && <time datetime={datetime}>{updated}</time>} </div> {group(take(items, cut), 0)} {len(rest) > 0 && <details.ui-kpts-more> <summary><span.ui-kpts-show>Show {len(rest)} more</span><span.ui-kpts-hide>Show fewer</span></summary> {group(rest, cut)} </details>} {note != null && <p.ui-kpts-note>{note}</p>} </section>}Related
ArticleHeaderThe top of a story: its label and section, the headline, the dek, every author grouped by role, published and updated times, and the reading time.8 views · 8 downloadsCorrectionNoticeA correction, clarification, update, editor's note or retraction on a story, dated, with a link to the corrections policy; boxed on top or a line at the end.8 views · 7 downloadsExtractiveSummaryKeeps a text's best sentences, scored by word frequency and position, in their order, with a length slider and the chosen ones marked. No AI service.2 views · 6 downloadsFactCheckA fact-check: the claim and who said it, the rating large and on its scale from true to false, what was found, the sources, with ClaimReview markup.8 views · 12 downloadsSectionFrontA section's front: the lead story large with its picture and dek, the next stories beside it, and a column of more headlines; one column where narrow.9 views · 9 downloadsAdSlotAn advertisement's place kept at its size so the story never jumps: standard sizes by name, another on phones, a clear label, a placeholder, sticky in a rail.6 views · 8 downloads
Thread
Thread 0 posts
On KeyPoints: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.