# Navigation progress A **navigation progress bar** is the thin line pinned to the very top of the page — the one that trickles forward while a route loads and snaps to full when it arrives. Unlike most components there's nothing static to drop into your prose: the bar is mounted once, then *driven from JavaScript*. You `start()` it, `complete()` it, or `set()` it to any value between 0 and 100, and it animates the rest. The demo below ships as a project snippet (`snippets/NProgressDemo.jsx`) so you have something live to click. It mounts the bar and wires three buttons to the API: **Run** starts the trickle and finishes it after a beat (the way a page navigation looks), while **Start** and **Finish** expose the two ends on their own. Click any of them and watch the line at the very top of this page. Because the snippet is a project component, it's available as a tag with no setup: ```aardvark {% NProgressDemo %} ``` ## How it works The bar is a single `` element — mount it once (the snippet does this for the page), and from then on you move it with the imperative `nprogress` helpers rather than props: - `nprogress.start()` — begin trickling toward the top edge (it eases forward but never reaches 100% on its own). - `nprogress.complete()` — fill to 100% and fade out. - `nprogress.set(value)` — jump to an exact percentage (0–100), e.g. tie it to upload progress. - `nprogress.increment()` / `nprogress.decrement()` — nudge it by a step. - `nprogress.reset()` — clear it back to the start with no animation. That split — one mounted bar, an imperative API to drive it — is what makes it a snippet rather than a one-shot tag: a static `{% tag %}` can't expose `onClick` handlers that call `nprogress.start()`. The snippet owns both halves: it renders `` and the controls that drive it. Customize it (different triggers, tie it to a real fetch, restyle the buttons) by editing `snippets/NProgressDemo.jsx`. ## CSS Selectors Every snippet instance mounts inside a wrapper carrying its island name, so you can target the demo without touching the rest of the page: ```css /* The mounted snippet wrapper (the controls live inside it). */ [data-aardvark-island="NProgressDemo"] { margin-block: 1rem; } ``` The bar itself is a Mantine `Progress`, so it carries that component's stable Styles API parts — target the track with `.mantine-Progress-root` and the moving fill with `.mantine-Progress-section`: ```css /* Recolor the moving fill of the top-of-page bar. */ .mantine-Progress-section { background-color: var(--mantine-color-pink-6); } ``` The bar's *own* layout (its fixed position at the top, the trailing glow) comes from `@mantine/nprogress`'s stylesheet, which is hashed at build time (e.g. `.m_8f2832ae` for the bar root). Those hashes change between Mantine releases, so prefer the stable `.mantine-Progress-*` parts above when you restyle it. ## Injecting Attributes `attr={...}` forwards raw HTML attributes straight onto the snippet's trigger — here the **Run** button, which is where the snippet forwards its ref. Use it to attach an inline handler, a `data-*` hook, or any attribute the component doesn't expose as a prop: The live result — click **Run** to fire the injected `onclick` (it alerts the button's label) *and* run the progress bar:
Source: Markdown ```aardvark {% NProgressDemo attr={'onclick': ''' const value = this.innerText; console.log('attr demo value:', value); alert(value); '''} %} ``` Source: Python ```python component('NProgressDemo', attr={'onclick': ''' const value = this.innerText; console.log('attr demo value:', value); alert(value); '''}) ``` Attributes go on through a separate channel from React props, so they're written verbatim to the DOM node and never collide with what Mantine manages. Avoid `class`/`style` there (the framework owns those) — reach for the component's own props instead.