News

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.

#news#list#summary#article#no-js

Demo

Preview

What we know

Show 2 moreShow fewer

Written by our editors from the reporting below.

The budget in four lines

  1. Income tax stays the same; the threshold rises with inflation.
  2. Fuel duty goes up by 2p a litre from April.
  3. Free school meals are extended to every primary school.
  4. 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.qwe
main.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>}
Thread
89Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.