block
A responsive navigation bar block that sticks to the top of the page. Renders a full-width container with support for a logo, desktop navigation links, and a theme mode toggle. On smaller screens, the menu collapses into a slide-up drawer triggered by a menu button.
Provide menuItems for the navigation links and place your logo in the logo slot.
---
import Topbar from "../Topbar.astro";
import type { MenuItem } from "@pindoba/astro-navigation";
import { css } from "@pindoba/styled-system/css";
const menuItems: MenuItem[] = [
{ label: "Home", href: "#" },
{ label: "About", href: "#" },
{ label: "Blog", href: "#" },
{ label: "Contact", href: "#" },
];
---
<Topbar menuItems={menuItems}>
<div
slot="logo"
class={css({
display: "flex",
alignItems: "center",
gap: "sm",
fontSize: "lg",
fontWeight: "bold",
})}
>
Pindoba
</div>
</Topbar>Use showThemeModeSelect to hide the built-in theme toggle, menuActiveItem to highlight the current page, and passThrough to override styles on individual slots such as container for layout adjustments.
---
import Topbar from "../Topbar.astro";
import type { MenuItem } from "@pindoba/astro-navigation";
import { css } from "@pindoba/styled-system/css";
import { stack } from "@pindoba/styled-system/patterns";
const menuItems: MenuItem[] = [
{ label: "Home", href: "#" },
{ label: "Docs", href: "#" },
{ label: "Examples", href: "#" },
];
---
<div
class={stack({
gap: "xl",
direction: "column",
width: "100%",
})}
>
<!-- Without theme mode select -->
<div>
<h3>Without Theme Mode Select</h3>
<Topbar menuItems={menuItems} showThemeModeSelect={false}>
<div
slot="logo"
class={css({
fontSize: "lg",
fontWeight: "bold",
color: "primary.text",
})}
>
MyApp
</div>
</Topbar>
</div>
<!-- Active menu item -->
<div>
<h3>With Active Menu Item</h3>
<Topbar menuItems={menuItems} menuActiveItem="Docs">
<div
slot="logo"
class={css({
fontSize: "lg",
fontWeight: "bold",
})}
>
Pindoba
</div>
</Topbar>
</div>
<!-- Custom bottom border via passThrough -->
<div>
<h3>Custom Bottom Border</h3>
<Topbar
menuItems={menuItems}
passThrough={{
root: css.raw({
backgroundColor: "primary.surface.ground",
}),
}}
>
<div
slot="logo"
class={css({
fontSize: "lg",
fontWeight: "bold",
color: "primary.text.contrast.bold",
})}
>
Pindoba
</div>
</Topbar>
</div>
</div>falseMark this as the chosen one in a set (a current nav item, a checked option, a selected card). Where `interactive` says it *can* be clicked, `active` says it *is* the current choice. Use `activeEmphasis` to control how loud the state is. Combined with `interactive`, hover and press restart their stepping from the active baseline rather than falling back to the unchosen ramp. The three values differ only in what drives the state: `true` by the prop (via `data-panel-active`), `"checked"` by a wrapped native input (`:has(input:checked)` — zero JS, nothing to re-render), and `"current"` by an existing `aria-current`.
the panel's `emphasis`How loud the `active` state is. Defaults to the panel's own `emphasis`, so the chosen state lands on the same ramp as the resting look — this prop is the override for when the active item should be louder (or quieter) than the panel itself. `"primary"` fills with the feedback accent ramp and flips text to the contrast scale (~150 RGB units of separation — the option that reads at a glance in a dense list). `"secondary"` tints the feedback surface ramp and `"tertiary"` the neutral ramp; both promote text to `bold` and lift the border to the default line, but move the surface only ~7–18 units.
"div"HTML element to render. Curated to container-like tags so semantic intent stays clear (no html/script/style/etc).
"surface.ground"Surface background forwarded to the **Navigation** inside the bar — not the Topbar's own Panel surface, which is fixed at `surface.peak`.
Border style: `none`, `default`, `bold`, or `muted`.
Border color while the surface is `active` — the explicit form of the ring `activeEmphasis` would otherwise pick. It holds through hover, so a `borderInteract` color can't steal it. `inherit` keeps whatever `activeEmphasis` chose; `none` clears it.
Border style applied on hover / focus interaction.
Props forwarded only to the desktop ThemeModeSelect. Takes precedence over `themeModeSelectProps` for the desktop placement.
Visual emphasis / prominence level.
Extra content placed beside the ThemeModeSelect.
Semantic color tone: `neutral`, `primary`, `success`, `warning`, `danger`, or `inherit`.
Root element id. Seeds the desktop/mobile ThemeModeSelect ids.
Enable interactive (hover / focus / press) affordances and states.
Brand / logo content.
Replaces the default Navigation with custom menu content.
Label of the currently active navigation item — applies the active style.
Navigation items rendered by the internal Navigation on both desktop and mobile.
Props forwarded to the Dialog used as the mobile navigation drawer.
Props forwarded only to the mobile ThemeModeSelect (inside the drawer). Takes precedence over `themeModeSelectProps` for the mobile placement.
Props forwarded to both the desktop and mobile Navigation components.
Inner padding from the spacing scale.
Per-slot Panda CSS style overrides applied to individual topbar slots.
Corner radius override from the spacing scale (or `full` for fully rounded).
Corner radius for the bottom-left and bottom-right corners.
Corner radius for the top-left and bottom-left corners.
Corner radius for the top-right and bottom-right corners.
Corner radius for the top-left and top-right corners.
falseSerialize the panel's resolved surface inputs into a `data-panel-config` attribute on the root, enabling runtime surface changes via `updatePanelElement()` (and, for inheritors like Card, `updateCard()`) without a framework runtime — the primary consumer is Astro / vanilla JS, where props don't re-render. Reactive frameworks (Svelte/React/Vue) don't need this: their prop changes re-run the connect already.
Keep (or drop) the always-1px transparent border the panel reserves so a border appearing or changing never shifts layout. Computed automatically — it's reserved when a resting `border` is visible, or when `interactive` / `borderInteract` / `active` can change the border at runtime. Set `false` only on a panel that must not occupy that 1px (e.g. a borderless housing frame that would otherwise add 2px around a set).
Elevation shadow applied to the surface.
trueRender the built-in ThemeModeSelect toggle in the desktop and mobile extras area.
Props forwarded to both ThemeModeSelect instances.
Apply a frosted-glass effect with backdrop blur over a surface background.
Plus all standard <div> HTML
attributes.