Navigation

NavLink

A link marked current when the page's URL is where it goes, or under it; as text, a pill or underlined.

#navigation#link#no-js

Demo

Preview

With no `current`, the page's own path is compared: this gallery is at /.

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

Example

The code of the preview above.

demos/nav_link.qwe
view NavLinkDemo() =    <div.demo-stack>        <div.demo-row id="nav-link-demo-page">            <NavLink href="/" exact={true}>Home</NavLink>            <NavLink href="/docs">Docs</NavLink>            <NavLink href="/blog">Blog</NavLink>        </div>        <p.demo-note>With no `current`, the page's own path is compared: this gallery is at /.</p>        <div.demo-row id="nav-link-demo-pill">            <NavLink href="/inbox" current="/inbox/42" variant="pill" icon="M4 4h16v16H4zM4 13h5l1.5 2h3L15 13h5" badge={3}>Inbox</NavLink>            <NavLink href="/sent" current="/inbox/42" variant="pill" icon="M3 12 21 3l-4 18-5-7zM12 14l9-11">Sent</NavLink>            <NavLink href="/drafts" current="/inbox/42" variant="pill" icon="✎">Drafts</NavLink>        </div>        <div.demo-row id="nav-link-demo-underline" style="gap: 20px">            <NavLink href="/settings" current="/settings" exact={true} variant="underline">General</NavLink>            <NavLink href="/settings/team" current="/settings" variant="underline">Team</NavLink>            <NavLink href="/settings/billing" current="/settings" variant="underline">Billing</NavLink>        </div>    </div>

Source

ui/nav_link.qwe
// NavLink: a link that is marked current (aria-current="page") when the page's URL// is the one it goes to, or starts with it. For a site's own navigation, where the// mark says where the visitor is.////   <NavLink href="/docs">Docs</NavLink>//   <NavLink href="/" exact={true} variant="pill">Home</NavLink>//   <NavLink href="/inbox" current={request.path} badge={3}>Inbox</NavLink>//// href: where it goes. current: the page's path to compare with (the request's path,// from url(), when not given). It is current when the path is `href`, or is under it// ("/docs/start" under "/docs"); exact: only when it is `href` itself. "/" is only// ever exact. variant: "text" (default: coloured when current), "pill" (a soft// background when current) or "underline" (a line under it when current).// icon: a character or an emoji, or an SVG path (24x24, stroked) when it starts with "M".// badge: a count after the text. label: what screen readers hear instead of the text.// on_click(): inside one of your components, what a click does besides following.import ui/theme.qwe use ui_clslet nav_link_css = css`.ui-navlink {  display: inline-flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: var(--ui-radius-sm);  color: var(--ui-muted); font: 500 14px/1.3 var(--ui-font); text-decoration: none; white-space: nowrap;  transition: color .12s, background .12s;}.ui-navlink:hover { color: var(--ui-fg); }.ui-navlink:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-navlink[aria-current="page"] { color: var(--ui-accent); }.ui-navlink-pill:hover { background: var(--ui-subtle); }.ui-navlink-pill[aria-current="page"] { background: var(--ui-accent-soft); }.ui-navlink-underline { border-radius: 0; padding: 8px 2px; box-shadow: inset 0 -2px 0 transparent; }.ui-navlink-underline[aria-current="page"] { color: var(--ui-fg); box-shadow: inset 0 -2px 0 var(--ui-accent); }.ui-navlink-icon { display: inline-grid; place-items: center; flex: none; width: 18px; height: 18px; font-size: 15px; line-height: 1; }.ui-navlink-icon svg { width: 18px; height: 18px; }.ui-navlink-badge {  min-width: 18px; padding: 0 6px; border-radius: 999px; background: var(--ui-accent); color: var(--ui-accent-fg);  font-size: 11px; font-weight: 600; line-height: 18px; text-align: center; font-variant-numeric: tabular-nums;}@media (prefers-reduced-motion: reduce) { .ui-navlink { transition: none; } }`// Whether `here` (a path, with or without a query) is at `href` or under it.fn nav_link_matches(href: str, here: str, exact: bool) -> bool {    let path = re.replace(here, "[?#].*$", "")    let base = len(href) > 1 && ends_with(href, "/") ? slice(href, 0, len(href) - 1) : href    path == base || path == base + "/" || (!exact && base != "/" && starts_with(path, base + "/"))}view NavLink(href: str, current: str? = null, exact: bool = false, variant: str = "text", icon: str? = null,             badge = null, label: str? = null, id: str? = null, on_click = null, class: str? = null, children) {    style global nav_link_css    let here = current ?? url().path    let on = nav_link_matches(href, here, exact)    <a class={ui_cls(["ui-navlink", variant != "text" && `ui-navlink-${variant}`, class])} id={id} href={href}       aria-current={on ? "page" : null} aria-label={label} @click={() => { on_click && on_click() }}>        {icon != null && <span.ui-navlink-icon aria-hidden="true">{starts_with(icon, "M") && len(icon) > 2            ? <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"                   stroke-linejoin="round"><path d={icon}/></svg>            : icon}</span>}        <span>{children}</span>        {badge != null && <span.ui-navlink-badge>{badge}</span>}    </a>}
Thread
56Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.