Data

NewsList

Headlines in a compact list with source, time and a small picture; numbered for a most-read list.

#list#link#marketing#no-js

Demo

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

Example

The code of the preview above.

demos/news_list.qwe
let news_list_demo_items = [    { title: "Quillo 0.5: components resume where the server left them", href: "#news_list", source: "Blog", time: "2 h ago", datetime: "2026-10-28T08:00:00Z", src: "https://picsum.photos/seed/n1/128/96" },    { title: "The UI kit passes 400 components", href: "#news_list", source: "Blog", time: "Yesterday", datetime: "2026-10-27T10:00:00Z", src: "https://picsum.photos/seed/n2/128/96" },    { title: "Services: one program, split apart when it is time", href: "#news_list", source: "Docs", time: "Oct 20", datetime: "2026-10-20" },    { title: "Decimals, money and why floats cannot bill", href: "#news_list", source: "Guides", time: "Oct 12", datetime: "2026-10-12" },]view NewsListDemo() =    <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px; align-items: start">        <NewsList id="news-list-demo-latest" label="Latest" items={news_list_demo_items}/>        <NewsList id="news-list-demo-top" label="Most read" numbered={true} dense={true}                  items={news_list_demo_items | map((n) => { title: n.title, href: n.href, time: n.time })}/>    </div>

Source

ui/news_list.qwe
// NewsList: headlines in a compact list, each with where it is from and when, and a// small picture if there is one: a news feed, a changelog's latest, "also read".////   <NewsList items={[//       { title: "Quillo 0.5 released", href: "/blog/0-5", source: "Blog", time: "2 h ago" },//       { title: "Components: 400 of them", href: "/blog/400", source: "Blog", time: "Yesterday", src: "/img/400.jpg" },//   ]}/>//// items: a list of { title, href, source?, time?, datetime?, src? }: the title links// to href; source is where it is from; time is when, as it should read ("2 h ago"),// with datetime its ISO form for machines; src a small picture to the right.// numbered: ranks them 1, 2, 3 (a "most read" list). label: the list's name for// screen readers ("News"). dense: tighter rows. id: the list's id.import ui/theme.qwe use ui_clslet news_list_css = css`.ui-news-list { margin: 0; padding: 0; list-style: none; min-width: 0; font: 14px/1.4 var(--ui-font); color: var(--ui-fg); counter-reset: ui-news; }.ui-news-list-item { position: relative; display: flex; align-items: flex-start; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--ui-border); }.ui-news-list-item:last-child { border-bottom: 0; }.ui-news-list-dense .ui-news-list-item { padding: 6px 0; gap: 10px; }.ui-news-list-rank { flex: none; width: 1.6em; color: var(--ui-muted); font-size: 18px; font-weight: 700; line-height: 1.2; font-variant-numeric: tabular-nums; }.ui-news-list-text { flex: 1; min-width: 0; display: grid; gap: 2px; }.ui-news-list-title { margin: 0; font-size: 14px; font-weight: 550; line-height: 1.35; }.ui-news-list-title a { color: inherit; text-decoration: none; }.ui-news-list-title a::after { content: ""; position: absolute; inset: 0; }.ui-news-list-item:hover .ui-news-list-title { color: var(--ui-accent); }.ui-news-list-item:has(a:focus-visible) { box-shadow: var(--ui-focus); border-radius: var(--ui-radius-sm); }.ui-news-list-title a:focus-visible { outline: none; }.ui-news-list-meta { display: flex; flex-wrap: wrap; gap: 4px; color: var(--ui-muted); font-size: 12px; }.ui-news-list-meta span + span::before, .ui-news-list-meta span + time::before { content: "·"; margin-right: 4px; }.ui-news-list-pic { flex: none; width: 64px; height: 48px; border-radius: var(--ui-radius-sm); object-fit: cover; background: var(--ui-subtle); }.ui-news-list-dense .ui-news-list-pic { width: 48px; height: 36px; }`view NewsList(items: list = [], numbered: bool = false, dense: bool = false, label: str = "News", id: str? = null, class: str? = null) {    style global news_list_css    <ol class={ui_cls(["ui-news-list", dense && "ui-news-list-dense", class])} id={id} aria-label={label}>        {for [i, it] in enumerate(items) => <li.ui-news-list-item key={it.href ?? it.title}>            {numbered && <span.ui-news-list-rank aria-hidden="true">{i + 1}</span>}            <div.ui-news-list-text>                <h4.ui-news-list-title>{it.href != null ? <a href={it.href}>{it.title}</a> : it.title}</h4>                {(it.source != null || it.time != null) && <div.ui-news-list-meta>                    {it.source != null && <span>{it.source}</span>}                    {it.time != null && <time datetime={it.datetime}>{it.time}</time>}                </div>}            </div>            {it.src != null && <img.ui-news-list-pic src={it.src} alt="" loading="lazy"/>}        </li>}    </ol>}
Thread
87Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.