Duck Site Header
The top of a content site: identity, a handful of anchors, one action, and a real drawer below lg.
BlockServer safelucide-react
Preview
Installation
One command writes the section and every component it renders.
$
pnpm dlx shadcn@latest add @duck/duck-site-headerThe block lands in components/blocks/duck-site-header.tsx. It builds on holo-button, which the CLI installs alongside it. Already installed items are skipped.
Usage
import { DuckSiteHeader } from "@/components/blocks/duck-site-header"A block is a starting point, not a widget. The props exist so the example renders with real content — once the file is in your project, hard-code what never changes and delete the rest.
Props
Everything not listed here is forwarded to the root element.
| Prop | Type | Default | Description |
|---|---|---|---|
| brand | React.ReactNode | - | Wordmark, logo, or the site name as text. |
| brandHref | string | "/" | Where the brand links. |
| nav | DuckSiteHeaderItem[] | - | label, href, active, external. The block does not guess the current section from the URL. |
| cta | { label: string; href: string } | - | The one action on the right. |
| actions | React.ReactNode | - | Search, theme switcher, language toggle — anything left of the CTA. |
| sticky | boolean | true | Stick to the top with a blurred backdrop. |
| render | (item, className) => React.ReactNode | - | Swap the plain anchors for a framework link, so client navigation works. |
Rules
What keeps this section from turning into noise.
- Links are anchors by default so the block works in any framework. Pass render for next/link or your router equivalent.
- The drawer is a second nav rather than the same one re-laid-out: a menu that only exists at one width should not leave a hidden tab stop at the other.
- Escape closes it, and the toggle owns aria-expanded and aria-controls. Active state is yours to pass — the block has no router.
