BookmarkButton
A save-for-later toggle whose bookmark fills when pressed, with or without its label.
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/bookmark_button.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/bookmark_button.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/bookmark_button.qwe
component BookmarkButtonDemoLive() { state kept = false <div.demo-row> <BookmarkButton on_change={(on) => { kept = on }}/> <span.demo-note>{kept ? "Saved for later." : "Not saved."}</span> </div>}view BookmarkButtonDemo() = <div.demo-stack> <BookmarkButtonDemoLive/> <div.demo-row> <BookmarkButton saved={true} name="saved"/> <BookmarkButton icon_only={true}/> <BookmarkButton icon_only={true} saved={true} variant="ghost"/> <BookmarkButton variant="soft" size="sm" label="Bookmark" saved_label="Bookmarked"/> <BookmarkButton size="lg"/> <BookmarkButton disabled={true}/> </div> </div>Source
ui/bookmark_button.qwe
// BookmarkButton: saves something for later: a bookmark that fills when pressed, with// "Save" turning into "Saved" (or the icon alone). It is a toggle (aria-pressed), and// in a form it sends `name` while saved.//// <BookmarkButton/>// <BookmarkButton saved={true} icon_only={true} name="saved" on_change={(on) => { keep(id, on) }}/>//// saved: pressed to start with. label ("Save") and saved_label ("Saved"): its text;// icon_only: the icon alone, the label its name and tooltip. name and value ("1"):// sent as name=value while saved, in a <form>. variant: "outline" (default), "ghost"// or "soft"; size: "sm", "md" (default) or "lg".// on_change(saved): inside one of your components, given true or false.import ui/theme.qwe use ui_clslet bookmark_button_css = css`.ui-bookmark-button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 34px; padding: 0 12px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); background: transparent; color: var(--ui-fg); font: 500 14px/1 var(--ui-font); white-space: nowrap; cursor: pointer; transition: background .15s, color .15s, border-color .15s;}.ui-bookmark-button:hover { background: var(--ui-subtle); border-color: var(--ui-strong); }.ui-bookmark-button:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-bookmark-button:disabled { opacity: .55; cursor: not-allowed; }.ui-bookmark-button-ghost { border-color: transparent; }.ui-bookmark-button-soft { border-color: transparent; background: var(--ui-subtle); }.ui-bookmark-button-on, .ui-bookmark-button-on:hover { color: var(--ui-accent); border-color: color-mix(in srgb, var(--ui-accent) 45%, var(--ui-border)); background: var(--ui-accent-soft); }.ui-bookmark-button-ghost.ui-bookmark-button-on { border-color: transparent; }.ui-bookmark-button svg { flex: none; width: 18px; height: 18px; fill: none; transition: fill .15s; }.ui-bookmark-button-on svg { fill: currentColor; }.ui-bookmark-button-only { width: 34px; padding: 0; }.ui-bookmark-button-sm { height: 28px; padding: 0 9px; font-size: 13px; }.ui-bookmark-button-sm svg { width: 16px; height: 16px; }.ui-bookmark-button-sm.ui-bookmark-button-only { width: 28px; padding: 0; }.ui-bookmark-button-lg { height: 42px; padding: 0 16px; font-size: 15px; }.ui-bookmark-button-lg svg { width: 22px; height: 22px; }.ui-bookmark-button-lg.ui-bookmark-button-only { width: 42px; padding: 0; }@media (prefers-reduced-motion: reduce) { .ui-bookmark-button, .ui-bookmark-button svg { transition: none; } }`component BookmarkButton(saved: bool = false, label: str = "Save", saved_label: str = "Saved", icon_only: bool = false, name: str? = null, value: str = "1", variant: str = "outline", size: str = "md", disabled: bool = false, on_change = null, class: str? = null) { style global bookmark_button_css state on = saved let says = on ? saved_label : label <button type="button" class={ui_cls(["ui-bookmark-button", variant != "outline" && `ui-bookmark-button-${variant}`, size != "md" && `ui-bookmark-button-${size}`, icon_only && "ui-bookmark-button-only", on && "ui-bookmark-button-on", class])} aria-pressed={on ? "true" : "false"} aria-label={icon_only ? label : null} title={icon_only ? says : null} 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="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2Z"/> </svg> {!icon_only && <span>{says}</span>} {name != null && <input type="hidden" name={name} value={value} disabled={!on}/>} </button>}Related
FollowButtonFollow becomes Following once pressed, and says Unfollow on hover, as the social sites do.5 views · 7 downloadsLikeButtonA heart that toggles with a count that follows it, a real toggle for screen readers and forms.5 views · 4 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 BookmarkButton: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.