# astro-better-nav-bar

Configurable top navigation bar with dropdowns, theme selector, search, login link, and CTA button.

> For the index of this section of the site, see [llms.txt](https://lambdalatitudinarians.org/llms.txt)

# astro-better-nav-bar

A configurable top navigation bar for Astro sites. Supports dropdowns, a dark/light theme selector, a search button, login link, and CTA button. Designed for both docs and content sites.

Current version: **0.1.1** --[npm](https://www.npmjs.com/package/astro-better-nav-bar)

## Installation

```shell-session
$ npm install astro-better-nav-bar
```

Import the CSS in your global stylesheet:

```css
@import "astro-better-nav-bar/style.css";
```

## Basic usage

```jsx
---
import { NavBar } from 'astro-better-nav-bar';
---
<NavBar
  sectionTitle="My Site"
  sectionTitleHref="/"
  showThemeSelector
  showSearch
  loginHref="https://example.com/login"
  ctaHref="/contact"
  ctaLabel="Get started"
>
  <img slot="logo" src="/logo.svg" alt="My Site" class="h-8 w-8" />
</NavBar>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `logoHref` | `string` | `/` | URL the logo links to |
| `logoAriaLabel` | `string` | `Home` | aria-label for logo link |
| `sectionTitle` | `string` | — | Section label displayed next to logo |
| `sectionTitleHref` | `string` | — | URL the section title links to |
| `dropdowns` | `Dropdown[]` | `[]` | Dropdown menus (see below) |
| `showThemeSelector` | `boolean` | `false` | Show the light/dark theme toggle |
| `showSearch` | `boolean` | `false` | Show a search button (opens native browser search) |
| `loginHref` | `string` | — | URL for the “Log in” link |
| `loginLabel` | `string` | `Log in` | Text for the login link |
| `ctaHref` | `string` | — | URL for the CTA button |
| `ctaLabel` | `string` | — | Text for the CTA button |
| `breadcrumbs` | `{title, href}[]` | `[]` | Breadcrumb trail displayed below the main bar |

## Slots

| Slot | Description |
| --- | --- |
| `logo` | Logo element (image, SVG, or Astro component) |
| `sidebar-toggle` | Renders at the left of the mobile bar; use for a docs sidebar hamburger |
| `extra-actions` | Additional items in the desktop and mobile action area |

## Sidebar toggle

The `sidebar-toggle` slot is designed for a CSS-only docs sidebar hamburger. Place a `<label>` that controls a hidden checkbox; CSS `:has()` then shows and hides the sidebar without any JavaScript.

```jsx
<!-- hidden checkbox drives sidebar state -->
<input type="checkbox" id="nav-sidebar-open" class="nav-sidebar-checkbox" aria-hidden="true">

<NavBar ...>
  <label slot="sidebar-toggle" for="nav-sidebar-open" class="nav-sidebar-toggle" aria-label="Open navigation">
    <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
      <path d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 010 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 010 1.5H1.75A.75.75 0 011 7.75zm0 5a.75.75 0 01.75-.75h12.5a.75.75 0 010 1.5H1.75a.75.75 0 01-.75-.75z"/>
    </svg>
  </label>
</NavBar>

<!-- clicking backdrop unchecks the checkbox, closing the sidebar -->
<label class="sidebar-backdrop" for="nav-sidebar-open" aria-hidden="true"></label>
```

Then wire up the sidebar in CSS:

```css
.nav-sidebar-checkbox { position: fixed; opacity: 0; pointer-events: none; width: 0; height: 0; }

.nav-sidebar-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 1.75rem; width: 1.75rem;
  border-radius: 0.25rem;
  color: var(--nav-text, #fff);
  cursor: pointer;
}
/* hide when the sidebar is already visible inline */
@media (min-width: 769px) { .nav-sidebar-toggle { display: none; } }

.sidebar-backdrop {
  display: none;
  position: fixed; inset: 0; z-index: 39;
  background: rgba(0, 0, 0, 0.4); cursor: pointer;
}

@media (max-width: 768px) {
  .doc-sidebar {
    position: fixed; left: 0; top: 3.75rem; bottom: 0;
    z-index: 40; transform: translateX(-100%);
    transition: transform 0.2s ease;
  }
  body:has(.nav-sidebar-checkbox:checked) .doc-sidebar { transform: translateX(0); }
  body:has(.nav-sidebar-checkbox:checked) .sidebar-backdrop { display: block; }
}
```

## Dropdowns

Each dropdown takes a `label` (the button text) and an `items` array:

```typescript
type DropdownItem = { name: string; href: string };
type Dropdown = { label: string; items: DropdownItem[] };
```

```jsx
<NavBar
  dropdowns={[
    {
      label: 'Resources',
      items: [
        { name: 'Docs', href: '/docs' },
        { name: 'Blog', href: '/blog' },
        { name: 'API', href: '/api' },
      ],
    },
  ]}
  ...
/>
```

## Theme selector

When `showThemeSelector` is true, a theme toggle appears in the nav. It reads and writes `localStorage.getItem('theme')` and toggles the `.dark` class on `document.documentElement`.

The desktop nav shows a moon-icon dropdown with three options: Light, Dark, and System. Mobile shows a single toggle button.

## CSS customization

Override these CSS custom properties in your stylesheet:

```css
:root {
  --nav-bg:           #5E2750;   /* nav bar background */
  --nav-border:       #4a1f40;   /* nav bottom border */
  --nav-text:         #f5e6f0;   /* nav text color */
  --nav-text-muted:   #d4a8c8;   /* muted text (e.g. section title) */
  --nav-hover:        rgba(255,255,255,0.12); /* hover background */
  --nav-cta-bg:       #D24D15;   /* CTA button background */
  --nav-cta-hover-bg: #b83d0e;   /* CTA button hover */
  --nav-max-width:    1400px;    /* max content width */
}
```

Dark mode

The nav bar always uses `--nav-*` variables regardless of dark mode, since the nav typically has its own distinct background color that does not follow the page’s light/dark toggle.