LikeButton
A heart that toggles with a count that follows it, a real toggle for screen readers and forms.
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/like_button.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/like_button.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/like_button.qwe
component LikeButtonDemoLive() { state liked = false <div.demo-row> <LikeButton count={12} on_change={(on) => { liked = on }}/> <span.demo-note>{liked ? "You like this." : "Not liked."}</span> </div>}view LikeButtonDemo() = <div.demo-stack> <LikeButtonDemoLive/> <div.demo-row> <LikeButton count={1280} liked={true} name="like"/> <LikeButton count={45200} variant="outline"/> <LikeButton count={2400000} variant="soft" size="sm"/> <LikeButton count={7} size="lg"/> <LikeButton show_count={false}/> <LikeButton count={3} disabled={true}/> </div> </div>Source
ui/like_button.qwe
// LikeButton: a heart that fills when pressed, with how many have liked beside it// (the count follows the press: 12 becomes 13). It is a toggle (aria-pressed), and// in a form it sends `name` while liked.//// <LikeButton count={12}/>// <LikeButton count={1280} liked={true} name="like" on_change={(on) => { like_post(id, on) }}/>//// count: how many like it, shown as "1.3K" past a thousand (show_count: false hides// it). liked: pressed to start with. label ("Like"): its name for screen readers, with// the count. name and value ("1"): sent as name=value while liked, in a <form>.// size: "sm", "md" (default) or "lg". variant: "ghost" (default), "outline" or "soft".// on_change(liked): inside one of your components, given true or false.import ui/theme.qwe use ui_clslet like_button_css = css`.ui-like-button { display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 10px; border: 1px solid transparent; border-radius: 999px; background: transparent; color: var(--ui-muted); font: 500 14px/1 var(--ui-font); font-variant-numeric: tabular-nums; cursor: pointer; transition: background .15s, color .15s, border-color .15s;}.ui-like-button:hover { background: var(--ui-danger-soft); color: var(--ui-danger); }.ui-like-button:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-like-button:disabled { opacity: .55; cursor: not-allowed; }.ui-like-button-outline { border-color: var(--ui-border); }.ui-like-button-soft { background: var(--ui-subtle); }.ui-like-button-on { color: var(--ui-danger); }.ui-like-button svg { flex: none; width: 20px; height: 20px; fill: none; transition: fill .15s, transform .15s; }.ui-like-button-on svg { fill: currentColor; animation: ui-like-button-pop .3s ease-out; }@keyframes ui-like-button-pop { 30% { transform: scale(1.3); } 60% { transform: scale(.95); } }.ui-like-button-sm { height: 28px; padding: 0 8px; font-size: 13px; }.ui-like-button-sm svg { width: 16px; height: 16px; }.ui-like-button-lg { height: 42px; padding: 0 14px; font-size: 16px; }.ui-like-button-lg svg { width: 24px; height: 24px; }@media (prefers-reduced-motion: reduce) { .ui-like-button-on svg { animation: none; } .ui-like-button, .ui-like-button svg { transition: none; } }`// A count as people read it: 999, 1.3K, 12K, 2.4M.fn like_button_count(n: int) -> str { if n < 1000 { return str(n) } if n < 10000 { return `${round(n / 1000, 1)}K` } if n < 1000000 { return `${floor(n / 1000)}K` } `${round(n / 1000000, 1)}M`}component LikeButton(count: int = 0, liked: bool = false, label: str = "Like", name: str? = null, value: str = "1", show_count: bool = true, size: str = "md", variant: str = "ghost", disabled: bool = false, on_change = null, class: str? = null) { style global like_button_css state on = liked let shown = count + (on && !liked ? 1 : 0) - (!on && liked ? 1 : 0) <button type="button" class={ui_cls(["ui-like-button", variant != "ghost" && `ui-like-button-${variant}`, size != "md" && `ui-like-button-${size}`, on && "ui-like-button-on", class])} aria-pressed={on ? "true" : "false"} aria-label={show_count ? `${label}, ${shown} ${shown == 1 ? "like" : "likes"}` : label} disabled={disabled} @click={() => { on = !on on_change && on_change(on) }}> <svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1.1a5.5 5.5 0 0 0-7.8 7.8l1 1L12 21l7.8-7.6 1-1a5.5 5.5 0 0 0 0-7.8Z"/> </svg> {show_count && <span.ui-like-button-count aria-hidden="true">{like_button_count(shown)}</span>} {name != null && <input type="hidden" name={name} value={value} disabled={!on}/>} </button>}Related
BookmarkButtonA save-for-later toggle whose bookmark fills when pressed, with or without its label.8 views · 8 downloadsFollowButtonFollow becomes Following once pressed, and says Unfollow on hover, as the social sites do.5 views · 7 downloadsShareButtonShares a link through the system's share sheet, or copies it where the browser has none, and says which.6 views · 5 downloadsVoteButtonsUp and down votes around a score that follows them, as on a forum, in a column or a row.5 views · 6 downloadsButtonA button, or a link that looks like one: four variants, three tones, three sizes, loading.7 views · 7 downloadsButtonGroupButtons joined edge to edge into one control, or spaced in a row; across or upright.6 views · 6 downloads
Thread
Thread 0 posts
On LikeButton: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.