Navigation

NotificationBell

A bell with the unread count on its corner: a link, or a button that opens any panel.

#navigation#notification#button#icon#no-js

Demo

Preview
2 notifications
Ana replied to your comment.
The build finished.
This one opens a popover by its id.
2 unread; click the bell to read them.

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

Example

The code of the preview above.

demos/notification_bell.qwe
component NotificationBellDemoLive() {    state unread = 2    <div.demo-row>        <NotificationBell id="notification-bell-demo-live" count={unread} on_click={() => { unread = 0 }}/>        <Button size="sm" variant="outline" id="notification-bell-demo-more" on_click={() => { unread += 1 }}>New notification</Button>        <span.demo-note>{unread == 0 ? "All read (the bell was clicked)." : `${unread} unread; click the bell to read them.`}</span>    </div>}view NotificationBellDemo() =    <div.demo-stack>        <div.demo-row style="gap: 20px">            <NotificationBell id="notification-bell-demo" count={3} href="#notification_bell"/>            <NotificationBell count={0} href="#notification_bell"/>            <NotificationBell id="notification-bell-demo-many" count={120}/>            <NotificationBell id="notification-bell-demo-dot" count={1} dot={true}/>            <NotificationBell count={7} size="sm"/>            <NotificationBell count={7} size="lg"/>        </div>        <div.demo-row>            <NotificationBell id="notification-bell-demo-panel" count={2} target="notification-bell-demo-pop"/>            <div id="notification-bell-demo-pop" popover="auto" style="margin: auto; padding: 14px 16px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); color: var(--ui-fg); box-shadow: var(--ui-shadow-lg); font: 14px/1.5 var(--ui-font)">                <b>2 notifications</b><br/>Ana replied to your comment.<br/>The build finished.            </div>            <span.demo-note>This one opens a popover by its id.</span>        </div>        <NotificationBellDemoLive/>    </div>

Source

ui/notification_bell.qwe
// NotificationBell: a bell with the number of unread notifications on its corner, for// a navbar. It opens nothing itself: it is a link to the notifications page, or a// button that opens any panel (a popover by its id, or your own component's state).////   <NotificationBell count={3} href="/notifications"/>//   <NotificationBell count={12} target="alerts-panel"/>   opens <div id="alerts-panel" popover>…</div>//   <NotificationBell count={n} on_click={() => { open = !open }}/>//// count: how many are unread; 0 shows no badge. limit: past it the badge says "99+"// (99). dot: a dot instead of the number, for when the count is unknown. href: makes it// a link. target: the id of a popover it opens (popovertarget). label: its name// ("Notifications"); screen readers hear "Notifications, 3 unread". size: "sm", "md"// (default) or "lg". ring: the bell swings once when the count goes up (CSS, on the// badge appearing; still for people who ask for less motion). on_click(): inside one// of your components, what a click does.import ui/theme.qwe use ui_clslet notification_bell_css = css`.ui-bell {  position: relative; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; padding: 0;  border: 0; border-radius: var(--ui-radius-sm); background: transparent; color: var(--ui-fg); cursor: pointer;  text-decoration: none; transition: background .12s;}.ui-bell:hover { background: var(--ui-subtle); }.ui-bell:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-bell svg { width: 22px; height: 22px; transform-origin: 50% 4px; }.ui-bell-ring:has(.ui-bell-count) svg { animation: ui-bell-swing .8s ease-in-out 1; }@keyframes ui-bell-swing { 20% { transform: rotate(14deg); } 40% { transform: rotate(-12deg); } 60% { transform: rotate(8deg); } 80% { transform: rotate(-5deg); } }.ui-bell-count {  position: absolute; top: 4px; left: 50%; margin-left: 2px; min-width: 17px; height: 17px; padding: 0 4px; border-radius: 999px;  border: 2px solid var(--ui-bg); background: var(--ui-danger); color: #fff; font: 700 10px/13px var(--ui-font); text-align: center;  font-variant-numeric: tabular-nums;}.ui-bell-dot { min-width: 10px; height: 10px; padding: 0; margin-left: 4px; top: 6px; }.ui-bell-sm { width: 30px; height: 30px; }.ui-bell-sm svg { width: 18px; height: 18px; }.ui-bell-sm .ui-bell-count { top: 1px; margin-left: 0; }.ui-bell-lg { width: 46px; height: 46px; }.ui-bell-lg svg { width: 26px; height: 26px; }.ui-bell-lg .ui-bell-count { top: 6px; margin-left: 4px; }@media (prefers-reduced-motion: reduce) { .ui-bell { transition: none; } .ui-bell-ring:has(.ui-bell-count) svg { animation: none; } }`view NotificationBell(count: int = 0, limit: int = 99, dot: bool = false, href: str? = null, target: str? = null,                      label: str = "Notifications", size: str = "md", ring: bool = true, id: str? = null, on_click = null,                      class: str? = null) {    style global notification_bell_css    let cls = ui_cls(["ui-bell", size != "md" && `ui-bell-${size}`, ring && "ui-bell-ring", class])    let said = count > 0 ? `${label}, ${count} unread` : label    let body = [        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"             aria-hidden="true"><path d="M6 16V11a6 6 0 0 1 12 0v5l2 2H4zM10 21h4"/></svg>,        count > 0 && <span class={ui_cls(["ui-bell-count", dot && "ui-bell-dot"])} aria-hidden="true">{dot ? "" : count > limit ? `${limit}+` : count}</span>,    ]    href != null        ? <a class={cls} id={id} href={href} aria-label={said} title={label}>{body}</a>        : <button class={cls} id={id} type="button" aria-label={said} title={label} popovertarget={target}                  @click={() => { on_click && on_click() }}>{body}</button>}
Thread
58Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.