Hashtag
A #tag that links to its page, with an optional count, and HashtagList, the trending tags ranked with their post counts.
Demo
Shipped the new tables today #quillo #SQLite310 #release notes
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/hashtag.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/hashtag.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/hashtag.qwe
view HashtagDemo() = <div.demo-stack> <p.demo-note id="hashtag-demo-line" style="margin: 0; color: var(--ui-fg); font-size: 14px"> Shipped the new tables today <Hashtag tag="quillo"/> <Hashtag tag="SQLite" count={310}/> <Hashtag tag="release notes" tone="neutral" size="sm"/> </p> <div.demo-narrow> <HashtagList title="Trending" base="#hashtag-" tags={[ { tag: "quillo", count: 2400, trend: "up" }, { tag: "sqlite", count: 1200000 }, { tag: "rust", count: 980, trend: "down" }, { tag: "postgres", count: 310, href: "#hashtag" }]}/> </div> </div>Source
ui/hashtag.qwe
// Hashtag: a #tag as a link to its page, and HashtagList, the tags trending now, each// with how many posts.//// <Hashtag tag="quillo"/> #quillo → /tags/quillo// <Hashtag tag="Rust" base="/explore/" count={1200}/> #Rust · 1.2K// <HashtagList title="Trending" tags={[{ tag: "quillo", count: 2400 }, { tag: "sqlite", count: 310 }]}/>//// tag: the word, without the #. base: the start of its link ("/tags/"); href: the whole// link instead. count: how many posts carry it, shown after it (1200 reads 1.2K).// tone: "accent" (default) or "neutral". size: "sm" or "md".// HashtagList: tags is a list of { tag, count?, href?, trend? ("up", "down") }, shown// in order with their rank; title: a heading above them; base as for Hashtag; label:// what screen readers call the list (the title, or "Hashtags").import ui/theme.qwe use ui_clslet hashtag_css = css`.ui-hashtag { display: inline-flex; align-items: baseline; gap: 5px; color: var(--ui-accent); font: 500 14px/1.4 var(--ui-font); text-decoration: none; border-radius: 3px; }.ui-hashtag:hover { text-decoration: underline; }.ui-hashtag:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-hashtag-neutral { color: var(--ui-fg); }.ui-hashtag-count { color: var(--ui-muted); font-size: .85em; font-weight: 400; font-variant-numeric: tabular-nums; }.ui-hashtag-sm { font-size: 13px; }.ui-hashtag-list { display: grid; gap: 4px; min-width: 0; padding: 14px 16px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-hashtag-list h3 { margin: 0 0 6px; font-size: 15px; font-weight: 700; }.ui-hashtag-list ol { margin: 0; padding: 0; list-style: none; counter-reset: ui-hashtag; }.ui-hashtag-item { display: grid; grid-template-columns: 24px minmax(0, 1fr) auto; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid var(--ui-border); counter-increment: ui-hashtag; }.ui-hashtag-item:first-child { border-top: 0; }.ui-hashtag-item::before { content: counter(ui-hashtag); color: var(--ui-muted); font-size: 12px; font-variant-numeric: tabular-nums; }.ui-hashtag-item .ui-hashtag { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.ui-hashtag-posts { color: var(--ui-muted); font-size: 12px; white-space: nowrap; }.ui-hashtag-trend { display: inline-block; width: 12px; height: 12px; margin-right: 3px; vertical-align: -2px; }.ui-hashtag-up { color: var(--ui-success); }.ui-hashtag-down { color: var(--ui-danger); }`// A count as it reads beside a tag: 950 is "950", 1200 is "1.2K", 2500000 is "2.5M".fn hashtag_count(n: int) -> str { if n < 1000 { return str(n) } let unit = n < 1000000 ? 1000 : 1000000 let tenths = floor(n * 10 / unit) `${floor(tenths / 10)}${tenths % 10 == 0 ? "" : `.${tenths % 10}`}${unit == 1000 ? "K" : "M"}`}view Hashtag(tag: str, base: str = "/tags/", href: str? = null, count: int? = null, tone: str = "accent", size: str = "md", class: str? = null) { style global hashtag_css <a class={ui_cls(["ui-hashtag", tone != "accent" && `ui-hashtag-${tone}`, size == "sm" && "ui-hashtag-sm", class])} href={href ?? `${base}${url_encode(tag)}`}> #{tag} {count != null && <span.ui-hashtag-count aria-label={`${count} posts`}>{hashtag_count(count)}</span>} </a>}view HashtagList(tags: list, title: str? = null, base: str = "/tags/", label: str? = null, class: str? = null) { style global hashtag_css <nav class={ui_cls(["ui-hashtag-list", class])} aria-label={label ?? title ?? "Hashtags"}> {title != null && <h3>{title}</h3>} <ol> {for t in tags => <li.ui-hashtag-item> <Hashtag tag={t.tag} base={base} href={t.href}/> {t.count != null && <span.ui-hashtag-posts> {t.trend == "up" && <svg.ui-hashtag-trend.ui-hashtag-up viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="rising"><path d="M2 9l3.5-3.5 2 2L10 4"/><path d="M7 4h3v3"/></svg>} {t.trend == "down" && <svg.ui-hashtag-trend.ui-hashtag-down viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="falling"><path d="M2 3l3.5 3.5 2-2L10 8"/><path d="M7 8h3V5"/></svg>} {hashtag_count(t.count)} posts </span>} </li>} </ol> </nav>}Related
CommentThreadNested comments with Like and Reply, a reply box that opens under the comment answered, and branches that fold.5 views · 5 downloadsStoryRingA row of avatars in rings, bright for stories not yet seen and grey once seen, with your own first to add one.6 views · 6 downloadsAlphabetIndexA to Z links into a list sorted by name; letters with nothing under them stay in place, disabled.4 views · 6 downloadsDirectoryNavA long list of names grouped under sticky letter headers, with a row of letters at the top that jumps to each group.5 views · 5 downloadsLinkListA plain list of links, each with a line saying where it leads, an icon and a short note at the right.5 views · 6 downloadsMenuA vertical list of links and actions in titled sections, with icons, shortcuts, counts, disabled items and the active one marked.6 views · 6 downloads
Thread
Thread 0 posts
On Hashtag: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.