Social

UserBadge

A small chip with a person's avatar and name, plus a role label or a verified tick; a link when given an href.

#social#user#profile#no-js

Demo

Preview
Ana SilvaBen OkaforAdminLee ChenAuthorMia Rossi(online)
Sam PatelReviewerPlato

Assigned to Ana Silva yesterday.

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

Example

The code of the preview above.

demos/user_badge.qwe
view UserBadgeDemo() =    <div.demo-stack>        <div.demo-row>            <UserBadge id="user-badge-demo-ana" name="Ana Silva" src="https://i.pravatar.cc/80?img=47" verified={true}/>            <UserBadge id="user-badge-demo-ben" name="Ben Okafor" role="Admin" href="#user_badge"/>            <UserBadge name="Lee Chen" tone="accent" role="Author"/>            <UserBadge name="Mia Rossi" status="online"/>        </div>        <div.demo-row>            <UserBadge id="user-badge-demo-sm" name="Sam Patel" size="sm" role="Reviewer"/>            <UserBadge name="Plato" size="sm" tone="accent"/>            <p.demo-note style="margin: 0">Assigned to <UserBadge id="user-badge-demo-plain" name="Ana Silva" size="sm" tone="plain"/> yesterday.</p>        </div>    </div>

Source

ui/user_badge.qwe
// UserBadge: a small chip naming a person: their avatar (or initials) and name, with// a role after it or a verified tick, for an author line, an assignee, a mention.////   <UserBadge name="Ana Silva" src="/img/ana.jpg" verified={true}/>//   <UserBadge name="Ben Okafor" role="Admin" href="/people/ben"/>//   <UserBadge name="Lee Chen" size="sm" tone="accent"/>//// name: whose it is; src: their picture (else their initials, on a colour of the// name's own). role: a small label after the name ("Admin", "Author"). verified: a tick// after the name, told to screen readers. href makes it a link. size: "sm" or "md"// (default). tone: "neutral" (default), "accent" or "plain" (no border, in a line of// text). status: "online", "away", "busy" or "offline", a dot on the avatar.import ui/theme.qwe use ui_clslet user_badge_css = css`.ui-user-badge {  display: inline-flex; align-items: center; gap: 7px; max-width: 100%; height: 30px; padding: 0 10px 0 3px; border: 1px solid var(--ui-border);  border-radius: 999px; background: var(--ui-surface); color: var(--ui-fg); font: 500 13px/1 var(--ui-font); text-decoration: none;  vertical-align: middle; white-space: nowrap; --s: 24px; --h: #6e56cf;}a.ui-user-badge:hover { border-color: var(--ui-strong); background: var(--ui-subtle); }a.ui-user-badge:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-user-badge-accent { border-color: transparent; background: var(--ui-accent-soft); color: var(--ui-accent); }.ui-user-badge-plain { height: auto; padding: 0; border: 0; background: none; }.ui-user-badge-avatar {  position: relative; display: inline-grid; place-items: center; flex: none; width: var(--s); height: var(--s); border-radius: 50%;  background: color-mix(in srgb, var(--h) 24%, var(--ui-bg)); color: color-mix(in srgb, var(--h) 70%, var(--ui-fg));  font: 600 calc(var(--s) * .4)/1 var(--ui-font); user-select: none;}.ui-user-badge-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }.ui-user-badge-c0 { --h: #e5484d; } .ui-user-badge-c1 { --h: #f76b15; } .ui-user-badge-c2 { --h: #ad7f00; }.ui-user-badge-c3 { --h: #30a46c; } .ui-user-badge-c4 { --h: #12a594; } .ui-user-badge-c5 { --h: #0090ff; }.ui-user-badge-c6 { --h: #6e56cf; } .ui-user-badge-c7 { --h: #d6409f; }.ui-user-badge-dot { position: absolute; right: -1px; bottom: -1px; width: 9px; height: 9px; border-radius: 50%; border: 2px solid var(--ui-surface); background: var(--ui-strong); }.ui-user-badge-online { background: var(--ui-success); }.ui-user-badge-away { background: var(--ui-warning); }.ui-user-badge-busy { background: var(--ui-danger); }.ui-user-badge-name { overflow: hidden; text-overflow: ellipsis; }.ui-user-badge-role { padding: 2px 6px; border-radius: 999px; background: var(--ui-subtle); color: var(--ui-muted); font-size: 11px; font-weight: 600; }.ui-user-badge-accent .ui-user-badge-role { background: color-mix(in srgb, var(--ui-accent) 15%, transparent); color: var(--ui-accent); }.ui-user-badge-tick { flex: none; width: 14px; height: 14px; color: var(--ui-info); }.ui-user-badge-sm { height: 24px; padding: 0 8px 0 2px; gap: 5px; font-size: 12px; --s: 20px; }.ui-user-badge-sm .ui-user-badge-role { padding: 1px 5px; font-size: 10px; }.ui-user-badge-sm .ui-user-badge-tick { width: 12px; height: 12px; }.ui-user-badge-plain.ui-user-badge-sm { height: auto; padding: 0; }`// The initials of a name: "Ada Lovelace" is "AL", "Plato" is "PL", "" is "?".fn user_badge_initials(name: str) -> str {    let words = split(trim(name), " ") | filter((w) => w != "")    let letters = len(words) == 0 ? "?" : (len(words) == 1 ? slice(words[0], 0, 2) : words[0][0] + words[-1][0])    upper(letters)}// One of eight colours, chosen from the name's letters, so a name always gets the same.fn user_badge_hue(name: str) -> int {    let chars = "abcdefghijklmnopqrstuvwxyz0123456789"    let codes = split(lower(name), "") | map((c) => (index_of(chars, c) ?? 36) + 1)    floor(reduce(codes, 5381, (h, c) => (h * 33 + c) % 1000003) / 13) % 8}view UserBadge(name: str, src: str? = null, role: str? = null, verified: bool = false, href: str? = null,               size: str = "md", tone: str = "neutral", status: str? = null, id: str? = null, class: str? = null) {    style global user_badge_css    let cls = ui_cls(["ui-user-badge", size == "sm" && "ui-user-badge-sm", tone != "neutral" && `ui-user-badge-${tone}`, class])    let body = [        <span class={ui_cls(["ui-user-badge-avatar", `ui-user-badge-c${user_badge_hue(name)}`])} aria-hidden="true">            {user_badge_initials(name)}            {src != null && <img src={src} alt="" loading="lazy"/>}            {status != null && <span class={`ui-user-badge-dot ui-user-badge-${status}`}></span>}        </span>,        <span.ui-user-badge-name>{name}</span>,        verified && <svg.ui-user-badge-tick viewBox="0 0 24 24" fill="currentColor" role="img" aria-label="Verified"><path d="M12 2l2.4 2.1 3.2-.4 1 3 2.9 1.5-.6 3.2L22 14l-2.1 2.4.4 3.2-3 1L15.8 23.5l-3.2-.6L9.6 23.5l-1.5-2.9-3-1 .4-3.2L3.4 14l1.1-2.6-.6-3.2L6.8 6.7l1-3 3.2.4z"/><path d="M8.5 12.5l2.5 2.5 5-5" fill="none" stroke="var(--ui-surface)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>,        role != null && <span.ui-user-badge-role>{role}</span>,        status != null && <span.ui-sr>({status})</span>,    ]    href != null ? <a class={cls} id={id} href={href}>{body}</a> : <span class={cls} id={id}>{body}</span>}
Thread
65Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.