astro-better-nav-bar 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NavBar.astro +163 -0
- package/README.md +180 -0
- package/ThemeSelector.astro +104 -0
- package/index.js +2 -0
- package/package.json +19 -0
- package/style.css +478 -0
package/NavBar.astro
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
import ThemeSelector from './ThemeSelector.astro';
|
|
3
|
+
|
|
4
|
+
interface NavItem { name: string; href: string; }
|
|
5
|
+
interface NavDropdown { label: string; items: NavItem[]; }
|
|
6
|
+
|
|
7
|
+
export interface Props {
|
|
8
|
+
logoHref?: string;
|
|
9
|
+
logoAriaLabel?: string;
|
|
10
|
+
sectionTitle?: string;
|
|
11
|
+
sectionTitleHref?: string;
|
|
12
|
+
dropdowns?: NavDropdown[];
|
|
13
|
+
showThemeSelector?: boolean;
|
|
14
|
+
showSearch?: boolean;
|
|
15
|
+
loginHref?: string;
|
|
16
|
+
loginLabel?: string;
|
|
17
|
+
ctaHref?: string;
|
|
18
|
+
ctaLabel?: string;
|
|
19
|
+
breadcrumbs?: { title: string; href: string }[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
logoHref = '/',
|
|
24
|
+
logoAriaLabel = 'Home',
|
|
25
|
+
sectionTitle,
|
|
26
|
+
sectionTitleHref = '/',
|
|
27
|
+
dropdowns = [],
|
|
28
|
+
showThemeSelector = true,
|
|
29
|
+
showSearch = false,
|
|
30
|
+
loginHref,
|
|
31
|
+
loginLabel = 'Log In',
|
|
32
|
+
ctaHref,
|
|
33
|
+
ctaLabel = 'Get a demo',
|
|
34
|
+
breadcrumbs = [],
|
|
35
|
+
} = Astro.props as Props;
|
|
36
|
+
|
|
37
|
+
const activeDropdowns = dropdowns.filter(d => d.items.length > 0);
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<div id="main-header" class="nav-header">
|
|
41
|
+
|
|
42
|
+
<!-- Desktop bar (lg and above) -->
|
|
43
|
+
<div class="nav-desktop">
|
|
44
|
+
|
|
45
|
+
<div class="nav-brand">
|
|
46
|
+
<a href={logoHref} class="nav-logo-link" aria-label={logoAriaLabel}>
|
|
47
|
+
<slot name="logo" />
|
|
48
|
+
</a>
|
|
49
|
+
{sectionTitle && (
|
|
50
|
+
<>
|
|
51
|
+
<span class="nav-brand-sep" aria-hidden="true">/</span>
|
|
52
|
+
<a href={sectionTitleHref} class="nav-brand-title">{sectionTitle}</a>
|
|
53
|
+
</>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
{activeDropdowns.length > 0 && (
|
|
58
|
+
<nav id="nav-header-nav" class="nav-dropdowns" aria-label="Site navigation">
|
|
59
|
+
{activeDropdowns.map(menu => (
|
|
60
|
+
<details class="nav-dropdown-root">
|
|
61
|
+
<summary class="nav-dropdown-summary">{menu.label}</summary>
|
|
62
|
+
<div class="nav-dropdown-panel">
|
|
63
|
+
<ul class="nav-dropdown-list">
|
|
64
|
+
{menu.items.map(item => (
|
|
65
|
+
<li class="nav-dropdown-item">
|
|
66
|
+
<a href={item.href} class="nav-dropdown-link">{item.name}</a>
|
|
67
|
+
</li>
|
|
68
|
+
))}
|
|
69
|
+
</ul>
|
|
70
|
+
</div>
|
|
71
|
+
</details>
|
|
72
|
+
))}
|
|
73
|
+
</nav>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
<div class="nav-actions">
|
|
77
|
+
<slot name="extra-actions" />
|
|
78
|
+
{showSearch && (
|
|
79
|
+
<button class="nav-icon-btn nav-search-btn" aria-label="Search" data-widget="search-button">
|
|
80
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20" aria-hidden="true">
|
|
81
|
+
<path fill-rule="evenodd" d="M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z" clip-rule="evenodd" />
|
|
82
|
+
</svg>
|
|
83
|
+
</button>
|
|
84
|
+
)}
|
|
85
|
+
{showThemeSelector && <ThemeSelector />}
|
|
86
|
+
{loginHref && (
|
|
87
|
+
<a href={loginHref} class="nav-login-link">{loginLabel}</a>
|
|
88
|
+
)}
|
|
89
|
+
{ctaHref && (
|
|
90
|
+
<a href={ctaHref} class="nav-cta-btn">{ctaLabel}</a>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<!-- Mobile bar (below lg) -->
|
|
97
|
+
<div class="nav-mobile">
|
|
98
|
+
|
|
99
|
+
<slot name="sidebar-toggle" />
|
|
100
|
+
|
|
101
|
+
{activeDropdowns.length > 0 && (
|
|
102
|
+
<div class="nav-mobile-dropdowns">
|
|
103
|
+
{activeDropdowns.map(menu => (
|
|
104
|
+
<details class="nav-mobile-dropdown-root nav-dropdown-root">
|
|
105
|
+
<summary class="nav-mobile-dropdown-summary">{menu.label}</summary>
|
|
106
|
+
<div class="nav-dropdown-panel nav-dropdown-panel--left">
|
|
107
|
+
<ul class="nav-dropdown-list">
|
|
108
|
+
{menu.items.map(item => (
|
|
109
|
+
<li class="nav-dropdown-item">
|
|
110
|
+
<a href={item.href} class="nav-dropdown-link">{item.name}</a>
|
|
111
|
+
</li>
|
|
112
|
+
))}
|
|
113
|
+
</ul>
|
|
114
|
+
</div>
|
|
115
|
+
</details>
|
|
116
|
+
))}
|
|
117
|
+
</div>
|
|
118
|
+
)}
|
|
119
|
+
|
|
120
|
+
{breadcrumbs.length > 0 && (
|
|
121
|
+
<div class="nav-breadcrumbs">
|
|
122
|
+
<nav aria-label="Breadcrumb">
|
|
123
|
+
<ul class="nav-breadcrumb-list">
|
|
124
|
+
{breadcrumbs.map((crumb, idx) => (
|
|
125
|
+
<>
|
|
126
|
+
<li class="nav-breadcrumb-item">
|
|
127
|
+
<a href={crumb.href}>{crumb.title}</a>
|
|
128
|
+
</li>
|
|
129
|
+
{idx < breadcrumbs.length - 1 && (
|
|
130
|
+
<span class="nav-breadcrumb-sep" aria-hidden="true">/</span>
|
|
131
|
+
)}
|
|
132
|
+
</>
|
|
133
|
+
))}
|
|
134
|
+
</ul>
|
|
135
|
+
</nav>
|
|
136
|
+
</div>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
<div class="nav-mobile-actions">
|
|
140
|
+
{showSearch && (
|
|
141
|
+
<button class="nav-icon-btn nav-search-btn" aria-label="Search" data-widget="search-button">
|
|
142
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20" aria-hidden="true">
|
|
143
|
+
<path fill-rule="evenodd" d="M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z" clip-rule="evenodd" />
|
|
144
|
+
</svg>
|
|
145
|
+
</button>
|
|
146
|
+
)}
|
|
147
|
+
{showThemeSelector && <ThemeSelector />}
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<script>
|
|
155
|
+
document.addEventListener('click', (event) => {
|
|
156
|
+
const header = document.getElementById('main-header');
|
|
157
|
+
if (!header) return;
|
|
158
|
+
const clicked = (event.target as Element).closest('details');
|
|
159
|
+
header.querySelectorAll('details[open]').forEach(d => {
|
|
160
|
+
if (d !== clicked) d.removeAttribute('open');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
</script>
|
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# astro-better-nav-bar
|
|
2
|
+
|
|
3
|
+
Configurable sticky navigation bar for Astro. Includes a logo slot, section title, dropdown menus, theme selector, search button, login link, and CTA button. No Tailwind required -- all styles are plain CSS with configurable custom properties.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npm install astro-better-nav-bar
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Import the stylesheet once in your root layout:
|
|
12
|
+
|
|
13
|
+
```astro
|
|
14
|
+
---
|
|
15
|
+
import 'astro-better-nav-bar/style.css';
|
|
16
|
+
---
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Basic usage
|
|
20
|
+
|
|
21
|
+
```astro
|
|
22
|
+
---
|
|
23
|
+
import { NavBar } from 'astro-better-nav-bar';
|
|
24
|
+
import MyLogo from './MyLogo.astro';
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<NavBar
|
|
28
|
+
sectionTitle="Docs"
|
|
29
|
+
sectionTitleHref="/docs"
|
|
30
|
+
dropdowns={[
|
|
31
|
+
{ label: 'Products', items: [
|
|
32
|
+
{ name: 'Overview', href: '/products' },
|
|
33
|
+
{ name: 'Pricing', href: '/pricing' },
|
|
34
|
+
]},
|
|
35
|
+
{ label: 'Resources', items: [
|
|
36
|
+
{ name: 'Blog', href: '/blog' },
|
|
37
|
+
{ name: 'Community', href: '/community' },
|
|
38
|
+
]},
|
|
39
|
+
]}
|
|
40
|
+
loginHref="https://account.example.com/"
|
|
41
|
+
ctaHref="/contact"
|
|
42
|
+
ctaLabel="Get a demo"
|
|
43
|
+
showSearch
|
|
44
|
+
>
|
|
45
|
+
<MyLogo slot="logo" />
|
|
46
|
+
</NavBar>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Props
|
|
50
|
+
|
|
51
|
+
| Prop | Type | Default | Description |
|
|
52
|
+
|------|------|---------|-------------|
|
|
53
|
+
| `logoHref` | `string` | `'/'` | URL the logo links to |
|
|
54
|
+
| `logoAriaLabel` | `string` | `'Home'` | Accessible label for the logo link |
|
|
55
|
+
| `sectionTitle` | `string` | — | Text shown next to the logo (e.g. "Docs", "Blog") |
|
|
56
|
+
| `sectionTitleHref` | `string` | `'/'` | URL the section title links to |
|
|
57
|
+
| `dropdowns` | `NavDropdown[]` | `[]` | Dropdown menus; see shape below |
|
|
58
|
+
| `showThemeSelector` | `boolean` | `true` | Show the built-in Light/Dark/System theme selector |
|
|
59
|
+
| `showSearch` | `boolean` | `false` | Show a search icon button |
|
|
60
|
+
| `loginHref` | `string` | — | If provided, renders a login link |
|
|
61
|
+
| `loginLabel` | `string` | `'Log In'` | Text for the login link |
|
|
62
|
+
| `ctaHref` | `string` | — | If provided, renders a CTA button |
|
|
63
|
+
| `ctaLabel` | `string` | `'Get a demo'` | Text for the CTA button |
|
|
64
|
+
| `breadcrumbs` | `{ title: string; href: string }[]` | `[]` | Mobile breadcrumb trail shown in the mobile bar |
|
|
65
|
+
|
|
66
|
+
### NavDropdown shape
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
interface NavDropdown {
|
|
70
|
+
label: string;
|
|
71
|
+
items: { name: string; href: string }[];
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Dropdowns with zero items are silently omitted.
|
|
76
|
+
|
|
77
|
+
## Slots
|
|
78
|
+
|
|
79
|
+
| Slot | Description |
|
|
80
|
+
|------|-------------|
|
|
81
|
+
| `logo` | Logo SVG or image. Rendered inside a link to `logoHref`. |
|
|
82
|
+
| `sidebar-toggle` | Optional button to open a docs sidebar. Rendered at the left of the mobile bar. |
|
|
83
|
+
| `extra-actions` | Additional items injected before the search/theme/login/CTA cluster in the desktop action area. |
|
|
84
|
+
|
|
85
|
+
## Section title per page
|
|
86
|
+
|
|
87
|
+
Pass `sectionTitle` and `sectionTitleHref` as props from each layout or page to change the label that appears next to the logo:
|
|
88
|
+
|
|
89
|
+
```astro
|
|
90
|
+
<!-- docs layout -->
|
|
91
|
+
<NavBar sectionTitle="Docs" sectionTitleHref="/docs" ...>
|
|
92
|
+
|
|
93
|
+
<!-- blog layout -->
|
|
94
|
+
<NavBar sectionTitle="Blog" sectionTitleHref="/blog" ...>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Search button
|
|
98
|
+
|
|
99
|
+
When `showSearch` is `true`, a search icon button appears in both the desktop and mobile action areas. The button carries `data-widget="search-button"` and dispatches no events itself -- wire up your own search handler:
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
document.querySelectorAll('[data-widget="search-button"]').forEach(btn => {
|
|
103
|
+
btn.addEventListener('click', () => openSearch());
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Theme selector
|
|
108
|
+
|
|
109
|
+
The built-in theme selector stores the chosen theme in `localStorage` under the key `theme` and toggles the `.dark` class on `<html>`. On desktop it renders as a dropdown with Light, Dark, and System options. On mobile it renders as a single toggle button (moon/sun icon).
|
|
110
|
+
|
|
111
|
+
`ThemeSelector` is also exported as a standalone component if you need it outside the nav:
|
|
112
|
+
|
|
113
|
+
```astro
|
|
114
|
+
---
|
|
115
|
+
import { ThemeSelector } from 'astro-better-nav-bar';
|
|
116
|
+
---
|
|
117
|
+
<ThemeSelector />
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Sidebar toggle
|
|
121
|
+
|
|
122
|
+
For docs-style layouts with a collapsible sidebar, pass your toggle button via the `sidebar-toggle` slot:
|
|
123
|
+
|
|
124
|
+
```astro
|
|
125
|
+
<NavBar ...>
|
|
126
|
+
<button
|
|
127
|
+
slot="sidebar-toggle"
|
|
128
|
+
data-widget="visibility-button"
|
|
129
|
+
data-element="side-nav"
|
|
130
|
+
aria-label="Show Menu"
|
|
131
|
+
>
|
|
132
|
+
<!-- hamburger icon -->
|
|
133
|
+
</button>
|
|
134
|
+
</NavBar>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## CSS custom properties
|
|
138
|
+
|
|
139
|
+
All colors and the max-width are overridable. Set these on `:root` or any ancestor of `.nav-header`:
|
|
140
|
+
|
|
141
|
+
| Property | Default | Description |
|
|
142
|
+
|----------|---------|-------------|
|
|
143
|
+
| `--nav-bg` | `#0f172a` | Header background |
|
|
144
|
+
| `--nav-border` | `#475569` | Header bottom border |
|
|
145
|
+
| `--nav-text` | `#ffffff` | Primary nav text |
|
|
146
|
+
| `--nav-text-muted` | `#94a3b8` | Muted text (breadcrumbs, separator) |
|
|
147
|
+
| `--nav-hover` | `#6366f1` | Hover color for links and icons |
|
|
148
|
+
| `--nav-mobile-item-bg` | `#1e293b` | Mobile dropdown pill background |
|
|
149
|
+
| `--nav-mobile-item-border` | `#475569` | Mobile dropdown pill border |
|
|
150
|
+
| `--nav-dropdown-bg` | `#ffffff` | Dropdown panel background |
|
|
151
|
+
| `--nav-dropdown-bg-dark` | `#0f172a` | Dropdown panel background in dark mode |
|
|
152
|
+
| `--nav-dropdown-border` | `#64748b` | Dropdown panel border |
|
|
153
|
+
| `--nav-dropdown-text` | `#374151` | Dropdown item text |
|
|
154
|
+
| `--nav-dropdown-hover-bg` | `#f1f5f9` | Dropdown item hover background |
|
|
155
|
+
| `--nav-dropdown-hover-text` | `#4338ca` | Dropdown item hover text |
|
|
156
|
+
| `--nav-cta-bg` | `#f97316` | CTA button background |
|
|
157
|
+
| `--nav-cta-hover-bg` | `#ea580c` | CTA button hover background |
|
|
158
|
+
| `--nav-cta-text` | `#ffffff` | CTA button text |
|
|
159
|
+
| `--nav-max-width` | `90rem` | Maximum width of the desktop bar content |
|
|
160
|
+
|
|
161
|
+
Example:
|
|
162
|
+
|
|
163
|
+
```css
|
|
164
|
+
:root {
|
|
165
|
+
--nav-hover: #7c3aed;
|
|
166
|
+
--nav-cta-bg: #16a34a;
|
|
167
|
+
--nav-cta-hover-bg: #15803d;
|
|
168
|
+
--nav-max-width: 80rem;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Dark mode
|
|
173
|
+
|
|
174
|
+
The stylesheet uses a `.dark` ancestor class (compatible with Tailwind's `darkMode: 'class'`). The dropdown panels switch background automatically when `.dark` is on `<html>` or any ancestor.
|
|
175
|
+
|
|
176
|
+
No `@media (prefers-color-scheme: dark)` rules are included. The built-in `ThemeSelector` handles setting the `.dark` class based on `localStorage` and OS preference.
|
|
177
|
+
|
|
178
|
+
## Mobile breakpoint
|
|
179
|
+
|
|
180
|
+
The desktop bar is shown at `min-width: 1024px`. Below that, the mobile bar is shown instead. This matches Tailwind's `lg` breakpoint. The breakpoint is not currently configurable via a CSS variable.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
// standalone theme selector
|
|
3
|
+
// mobile: single icon button that flips light/dark
|
|
4
|
+
// desktop: dropdown with Light / Dark / System options
|
|
5
|
+
---
|
|
6
|
+
<nav-theme-selector class="nav-theme-selector">
|
|
7
|
+
|
|
8
|
+
<!-- mobile toggle: shows moon when light, sun when dark -->
|
|
9
|
+
<button type="button" aria-label="Toggle Theme" class="nav-theme-mobile-btn nav-icon-btn">
|
|
10
|
+
<svg class="nav-theme-sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20" aria-hidden="true">
|
|
11
|
+
<path d="M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.166 17.834a.75.75 0 00-1.06 1.06l1.59 1.591a.75.75 0 101.061-1.06l-1.59-1.591zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.166 6.166a.75.75 0 001.06-1.06L5.636 3.515a.75.75 0 10-1.061 1.06l1.59 1.591z" />
|
|
12
|
+
</svg>
|
|
13
|
+
<svg class="nav-theme-moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20" aria-hidden="true">
|
|
14
|
+
<path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z" clip-rule="evenodd" />
|
|
15
|
+
</svg>
|
|
16
|
+
</button>
|
|
17
|
+
|
|
18
|
+
<!-- desktop dropdown: moon icon summary, three options -->
|
|
19
|
+
<details class="nav-theme-details nav-dropdown-root">
|
|
20
|
+
<summary class="nav-icon-btn nav-theme-details-summary" aria-label="Theme Selector">
|
|
21
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20" aria-hidden="true">
|
|
22
|
+
<path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z" clip-rule="evenodd" />
|
|
23
|
+
</svg>
|
|
24
|
+
</summary>
|
|
25
|
+
<div class="nav-dropdown-panel nav-theme-panel">
|
|
26
|
+
<ul class="nav-dropdown-list">
|
|
27
|
+
<li class="nav-dropdown-item">
|
|
28
|
+
<button type="button" class="nav-dropdown-link nav-theme-option" data-theme="light">
|
|
29
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18" aria-hidden="true">
|
|
30
|
+
<path d="M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.166 17.834a.75.75 0 00-1.06 1.06l1.59 1.591a.75.75 0 101.061-1.06l-1.59-1.591zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.166 6.166a.75.75 0 001.06-1.06L5.636 3.515a.75.75 0 10-1.061 1.06l1.59 1.591z" />
|
|
31
|
+
</svg>
|
|
32
|
+
Light
|
|
33
|
+
</button>
|
|
34
|
+
</li>
|
|
35
|
+
<li class="nav-dropdown-item">
|
|
36
|
+
<button type="button" class="nav-dropdown-link nav-theme-option" data-theme="dark">
|
|
37
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18" aria-hidden="true">
|
|
38
|
+
<path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z" clip-rule="evenodd" />
|
|
39
|
+
</svg>
|
|
40
|
+
Dark
|
|
41
|
+
</button>
|
|
42
|
+
</li>
|
|
43
|
+
<li class="nav-dropdown-item">
|
|
44
|
+
<button type="button" class="nav-dropdown-link nav-theme-option" data-theme="system">
|
|
45
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18" aria-hidden="true">
|
|
46
|
+
<path fill-rule="evenodd" d="M2.25 5.25a3 3 0 013-3h13.5a3 3 0 013 3V15a3 3 0 01-3 3h-3v.257c0 .597.237 1.17.659 1.591l.621.622a.75.75 0 01-.53 1.28h-9a.75.75 0 01-.53-1.28l.621-.622a2.25 2.25 0 00.659-1.59V18h-3a3 3 0 01-3-3V5.25zm1.5 0v7.5a1.5 1.5 0 001.5 1.5h13.5a1.5 1.5 0 001.5-1.5v-7.5a1.5 1.5 0 00-1.5-1.5H5.25a1.5 1.5 0 00-1.5 1.5z" clip-rule="evenodd" />
|
|
47
|
+
</svg>
|
|
48
|
+
System
|
|
49
|
+
</button>
|
|
50
|
+
</li>
|
|
51
|
+
</ul>
|
|
52
|
+
</div>
|
|
53
|
+
</details>
|
|
54
|
+
|
|
55
|
+
</nav-theme-selector>
|
|
56
|
+
|
|
57
|
+
<script>
|
|
58
|
+
class NavThemeSelector extends HTMLElement {
|
|
59
|
+
connectedCallback() {
|
|
60
|
+
const mobileBtn = this.querySelector('.nav-theme-mobile-btn');
|
|
61
|
+
const optionBtns = this.querySelectorAll('.nav-theme-option');
|
|
62
|
+
const details = this.querySelector('.nav-theme-details') as HTMLDetailsElement | null;
|
|
63
|
+
|
|
64
|
+
const getTheme = () => localStorage.getItem('theme') || 'system';
|
|
65
|
+
this.applyTheme(getTheme());
|
|
66
|
+
|
|
67
|
+
mobileBtn?.addEventListener('click', () => {
|
|
68
|
+
const isDark = document.documentElement.classList.contains('dark');
|
|
69
|
+
this.applyTheme(isDark ? 'light' : 'dark');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
optionBtns.forEach(btn => {
|
|
73
|
+
btn.addEventListener('click', (e) => {
|
|
74
|
+
this.applyTheme((e.currentTarget as HTMLButtonElement).dataset.theme!);
|
|
75
|
+
details?.removeAttribute('open');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
document.addEventListener('click', (e) => {
|
|
80
|
+
if (!this.contains(e.target as Node)) details?.removeAttribute('open');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
84
|
+
if (getTheme() === 'system') this.applyTheme('system');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
window.addEventListener('storage', (e) => {
|
|
88
|
+
if (e.key === 'theme' && e.newValue) this.applyTheme(e.newValue);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
applyTheme(theme: string) {
|
|
93
|
+
localStorage.setItem('theme', theme);
|
|
94
|
+
const isDark = theme === 'system'
|
|
95
|
+
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
96
|
+
: theme === 'dark';
|
|
97
|
+
document.documentElement.classList.toggle('dark', isDark);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!customElements.get('nav-theme-selector')) {
|
|
102
|
+
customElements.define('nav-theme-selector', NavThemeSelector);
|
|
103
|
+
}
|
|
104
|
+
</script>
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "astro-better-nav-bar",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Configurable sticky navigation bar component for Astro with dropdowns, theme selector, search, and CTA slots",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.js",
|
|
8
|
+
"./style.css": "./style.css",
|
|
9
|
+
"./NavBar.astro": "./NavBar.astro",
|
|
10
|
+
"./ThemeSelector.astro": "./ThemeSelector.astro"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"*.js",
|
|
14
|
+
"*.astro",
|
|
15
|
+
"*.css"
|
|
16
|
+
],
|
|
17
|
+
"keywords": ["astro", "navbar", "navigation", "header", "theme", "dropdown"],
|
|
18
|
+
"license": "MIT"
|
|
19
|
+
}
|
package/style.css
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/*
|
|
2
|
+
astro-better-nav-bar/style.css
|
|
3
|
+
|
|
4
|
+
Import in your project root layout.
|
|
5
|
+
No Tailwind required -- all styles are plain CSS.
|
|
6
|
+
|
|
7
|
+
CSS custom properties (set on :root or .nav-header's ancestors):
|
|
8
|
+
--nav-bg header background default: #0f172a (slate-900)
|
|
9
|
+
--nav-border header bottom border default: #475569 (slate-600)
|
|
10
|
+
--nav-text primary nav text color default: #ffffff
|
|
11
|
+
--nav-text-muted muted text (breadcrumbs, sep) default: #94a3b8 (slate-400)
|
|
12
|
+
--nav-hover interactive hover color default: #6366f1 (indigo-500)
|
|
13
|
+
--nav-mobile-item-bg mobile dropdown pill bg default: #1e293b (slate-800)
|
|
14
|
+
--nav-mobile-item-border mobile dropdown pill border default: #475569 (slate-600)
|
|
15
|
+
--nav-dropdown-bg dropdown panel background default: #ffffff
|
|
16
|
+
--nav-dropdown-bg-dark dropdown panel bg dark mode default: #0f172a
|
|
17
|
+
--nav-dropdown-border dropdown border default: #64748b (slate-500)
|
|
18
|
+
--nav-dropdown-text dropdown item text default: #374151 (gray-700)
|
|
19
|
+
--nav-dropdown-hover-bg hover bg in dropdown default: #f1f5f9 (slate-100)
|
|
20
|
+
--nav-dropdown-hover-text hover text in dropdown default: #4338ca (indigo-600)
|
|
21
|
+
--nav-cta-bg CTA button background default: #f97316 (orange-500)
|
|
22
|
+
--nav-cta-hover-bg CTA button hover background default: #ea580c (orange-600)
|
|
23
|
+
--nav-cta-text CTA button text default: #ffffff
|
|
24
|
+
--nav-max-width desktop bar max width default: 90rem
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/* ---------------------------------------------------------------
|
|
28
|
+
Header shell
|
|
29
|
+
--------------------------------------------------------------- */
|
|
30
|
+
|
|
31
|
+
.nav-header {
|
|
32
|
+
background-color: var(--nav-bg, #0f172a);
|
|
33
|
+
border-bottom: 1px solid var(--nav-border, #475569);
|
|
34
|
+
position: sticky;
|
|
35
|
+
top: 0;
|
|
36
|
+
z-index: 50;
|
|
37
|
+
text-align: left;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ---------------------------------------------------------------
|
|
41
|
+
Desktop bar
|
|
42
|
+
--------------------------------------------------------------- */
|
|
43
|
+
|
|
44
|
+
.nav-desktop {
|
|
45
|
+
display: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@media (min-width: 1024px) {
|
|
49
|
+
.nav-desktop {
|
|
50
|
+
display: flex;
|
|
51
|
+
justify-content: space-between;
|
|
52
|
+
align-items: center;
|
|
53
|
+
max-width: var(--nav-max-width, 90rem);
|
|
54
|
+
margin-left: auto;
|
|
55
|
+
margin-right: auto;
|
|
56
|
+
padding: 0.5rem 1rem;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* brand */
|
|
61
|
+
.nav-brand {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 0;
|
|
65
|
+
flex-shrink: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.nav-logo-link {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
margin-right: 0.25rem;
|
|
72
|
+
opacity: 1;
|
|
73
|
+
transition: opacity 0.15s ease-in;
|
|
74
|
+
text-decoration: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.nav-logo-link:hover {
|
|
78
|
+
opacity: 0.7;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.nav-brand-sep {
|
|
82
|
+
font-weight: 300;
|
|
83
|
+
font-size: 1.5rem;
|
|
84
|
+
color: var(--nav-text-muted, #94a3b8);
|
|
85
|
+
margin: 0 0.25rem;
|
|
86
|
+
line-height: 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.nav-brand-title {
|
|
90
|
+
font-weight: 700;
|
|
91
|
+
font-size: 1.5rem;
|
|
92
|
+
color: var(--nav-text, #ffffff);
|
|
93
|
+
letter-spacing: -0.025em;
|
|
94
|
+
text-decoration: none;
|
|
95
|
+
transition: color 0.15s ease-in;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.nav-brand-title:hover {
|
|
99
|
+
color: var(--nav-text-muted, #94a3b8);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* dropdowns nav */
|
|
103
|
+
.nav-dropdowns {
|
|
104
|
+
display: flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
margin-left: auto;
|
|
107
|
+
padding: 0;
|
|
108
|
+
z-index: 50;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ---------------------------------------------------------------
|
|
112
|
+
Shared dropdown primitives (desktop + mobile)
|
|
113
|
+
--------------------------------------------------------------- */
|
|
114
|
+
|
|
115
|
+
.nav-dropdown-root {
|
|
116
|
+
position: relative;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* chevron arrow via CSS */
|
|
120
|
+
.nav-dropdown-summary,
|
|
121
|
+
.nav-mobile-dropdown-summary {
|
|
122
|
+
list-style: none;
|
|
123
|
+
cursor: pointer;
|
|
124
|
+
user-select: none;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.nav-dropdown-summary::-webkit-details-marker,
|
|
128
|
+
.nav-mobile-dropdown-summary::-webkit-details-marker {
|
|
129
|
+
display: none;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.nav-dropdown-summary::after,
|
|
133
|
+
.nav-mobile-dropdown-summary::after {
|
|
134
|
+
content: '';
|
|
135
|
+
display: inline-block;
|
|
136
|
+
width: 0.375rem;
|
|
137
|
+
height: 0.375rem;
|
|
138
|
+
border-right: 2px solid currentColor;
|
|
139
|
+
border-bottom: 2px solid currentColor;
|
|
140
|
+
margin-left: 0.375rem;
|
|
141
|
+
margin-bottom: 0.125rem;
|
|
142
|
+
transform: rotate(45deg);
|
|
143
|
+
transition: transform 0.2s ease;
|
|
144
|
+
vertical-align: middle;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
details[open] > .nav-dropdown-summary::after,
|
|
148
|
+
details[open] > .nav-mobile-dropdown-summary::after {
|
|
149
|
+
transform: rotate(-135deg);
|
|
150
|
+
margin-bottom: -0.125rem;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* desktop dropdown summary */
|
|
154
|
+
.nav-dropdown-summary {
|
|
155
|
+
display: flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
gap: 0.25rem;
|
|
158
|
+
font-size: 0.875rem;
|
|
159
|
+
font-weight: 500;
|
|
160
|
+
color: var(--nav-text, #ffffff);
|
|
161
|
+
padding: 0.25rem 0.75rem;
|
|
162
|
+
transition: color 0.15s;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.nav-dropdown-summary:hover,
|
|
166
|
+
details[open] > .nav-dropdown-summary {
|
|
167
|
+
color: var(--nav-hover, #6366f1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* dropdown panel */
|
|
171
|
+
.nav-dropdown-panel {
|
|
172
|
+
position: absolute;
|
|
173
|
+
right: 0;
|
|
174
|
+
top: 100%;
|
|
175
|
+
margin-top: 0.75rem;
|
|
176
|
+
width: 12rem;
|
|
177
|
+
background-color: var(--nav-dropdown-bg, #ffffff);
|
|
178
|
+
border: 1px solid var(--nav-dropdown-border, #64748b);
|
|
179
|
+
border-radius: 0.375rem;
|
|
180
|
+
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
|
|
181
|
+
overflow: hidden;
|
|
182
|
+
z-index: 50;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.dark .nav-dropdown-panel {
|
|
186
|
+
background-color: var(--nav-dropdown-bg-dark, #0f172a);
|
|
187
|
+
border-color: #1e293b;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* left-aligned variant for mobile dropdowns */
|
|
191
|
+
.nav-dropdown-panel--left {
|
|
192
|
+
right: auto;
|
|
193
|
+
left: 0;
|
|
194
|
+
z-index: 60;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.nav-dropdown-list {
|
|
198
|
+
list-style: none;
|
|
199
|
+
margin: 0;
|
|
200
|
+
padding: 0;
|
|
201
|
+
display: flex;
|
|
202
|
+
flex-direction: column;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.nav-dropdown-item {
|
|
206
|
+
margin: 0;
|
|
207
|
+
padding: 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* shared styles for <a> links and <button> options in dropdowns */
|
|
211
|
+
.nav-dropdown-link {
|
|
212
|
+
display: flex;
|
|
213
|
+
align-items: center;
|
|
214
|
+
gap: 0.5rem;
|
|
215
|
+
width: 100%;
|
|
216
|
+
padding: 0.5rem 1rem;
|
|
217
|
+
font-size: 0.875rem;
|
|
218
|
+
font-weight: 500;
|
|
219
|
+
color: var(--nav-dropdown-text, #374151);
|
|
220
|
+
background: transparent;
|
|
221
|
+
border: none;
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
text-decoration: none;
|
|
224
|
+
transition: background-color 0.15s, color 0.15s;
|
|
225
|
+
text-align: left;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.dark .nav-dropdown-link {
|
|
229
|
+
color: #cbd5e1;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.nav-dropdown-link:hover {
|
|
233
|
+
background-color: var(--nav-dropdown-hover-bg, #f1f5f9);
|
|
234
|
+
color: var(--nav-dropdown-hover-text, #4338ca);
|
|
235
|
+
text-decoration: none;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.dark .nav-dropdown-link:hover {
|
|
239
|
+
background-color: #1e293b;
|
|
240
|
+
color: #818cf8;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* ---------------------------------------------------------------
|
|
244
|
+
Desktop action buttons
|
|
245
|
+
--------------------------------------------------------------- */
|
|
246
|
+
|
|
247
|
+
.nav-actions {
|
|
248
|
+
display: flex;
|
|
249
|
+
align-items: center;
|
|
250
|
+
gap: 0.5rem;
|
|
251
|
+
margin-left: 0.5rem;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.nav-icon-btn {
|
|
255
|
+
display: flex;
|
|
256
|
+
align-items: center;
|
|
257
|
+
justify-content: center;
|
|
258
|
+
height: 1.75rem;
|
|
259
|
+
width: 1.75rem;
|
|
260
|
+
border-radius: 0.25rem;
|
|
261
|
+
border: none;
|
|
262
|
+
background: transparent;
|
|
263
|
+
color: var(--nav-text, #ffffff);
|
|
264
|
+
cursor: pointer;
|
|
265
|
+
transition: background-color 0.15s, color 0.15s;
|
|
266
|
+
padding: 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.nav-icon-btn:hover {
|
|
270
|
+
background-color: rgba(255,255,255,0.1);
|
|
271
|
+
color: var(--nav-hover, #6366f1);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.nav-login-link {
|
|
275
|
+
font-size: 0.875rem;
|
|
276
|
+
font-weight: 500;
|
|
277
|
+
color: var(--nav-text, #ffffff);
|
|
278
|
+
text-decoration: none;
|
|
279
|
+
padding: 0 0.75rem;
|
|
280
|
+
transition: color 0.15s;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.nav-login-link:hover {
|
|
284
|
+
color: var(--nav-hover, #6366f1);
|
|
285
|
+
text-decoration: none;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.nav-cta-btn {
|
|
289
|
+
display: inline-block;
|
|
290
|
+
background-color: var(--nav-cta-bg, #f97316);
|
|
291
|
+
color: var(--nav-cta-text, #ffffff);
|
|
292
|
+
font-size: 0.75rem;
|
|
293
|
+
font-weight: 800;
|
|
294
|
+
letter-spacing: -0.025em;
|
|
295
|
+
text-transform: uppercase;
|
|
296
|
+
padding: 0.5rem 0.75rem;
|
|
297
|
+
border-radius: 0.25rem;
|
|
298
|
+
text-decoration: none;
|
|
299
|
+
margin-left: 0.75rem;
|
|
300
|
+
transition: background-color 0.15s;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.nav-cta-btn:hover {
|
|
304
|
+
background-color: var(--nav-cta-hover-bg, #ea580c);
|
|
305
|
+
text-decoration: none;
|
|
306
|
+
color: var(--nav-cta-text, #ffffff);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* ---------------------------------------------------------------
|
|
310
|
+
Theme selector
|
|
311
|
+
--------------------------------------------------------------- */
|
|
312
|
+
|
|
313
|
+
.nav-theme-selector {
|
|
314
|
+
position: relative;
|
|
315
|
+
display: block;
|
|
316
|
+
z-index: 10;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* mobile: single toggle, show moon (light mode) or sun (dark mode) */
|
|
320
|
+
.nav-theme-mobile-btn {
|
|
321
|
+
display: flex;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.nav-theme-mobile-btn .nav-theme-sun-icon {
|
|
325
|
+
display: none;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.nav-theme-mobile-btn .nav-theme-moon-icon {
|
|
329
|
+
display: block;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.dark .nav-theme-mobile-btn .nav-theme-sun-icon {
|
|
333
|
+
display: block;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.dark .nav-theme-mobile-btn .nav-theme-moon-icon {
|
|
337
|
+
display: none;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* desktop: full dropdown, always show moon icon in summary */
|
|
341
|
+
.nav-theme-details {
|
|
342
|
+
display: none;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
@media (min-width: 1024px) {
|
|
346
|
+
.nav-theme-mobile-btn {
|
|
347
|
+
display: none;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.nav-theme-details {
|
|
351
|
+
display: block;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.nav-theme-details-summary {
|
|
356
|
+
list-style: none;
|
|
357
|
+
outline: none;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.nav-theme-details-summary::-webkit-details-marker {
|
|
361
|
+
display: none;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* no chevron on the theme summary */
|
|
365
|
+
.nav-theme-details-summary::after {
|
|
366
|
+
display: none;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.nav-theme-panel {
|
|
370
|
+
right: 0;
|
|
371
|
+
width: 9rem;
|
|
372
|
+
margin-top: 0.75rem;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* ---------------------------------------------------------------
|
|
376
|
+
Mobile bar
|
|
377
|
+
--------------------------------------------------------------- */
|
|
378
|
+
|
|
379
|
+
.nav-mobile {
|
|
380
|
+
display: flex;
|
|
381
|
+
align-items: center;
|
|
382
|
+
padding: 0.5rem 1rem;
|
|
383
|
+
width: 100%;
|
|
384
|
+
position: sticky;
|
|
385
|
+
top: 0;
|
|
386
|
+
z-index: 50;
|
|
387
|
+
gap: 0.5rem;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
@media (min-width: 1024px) {
|
|
391
|
+
.nav-mobile {
|
|
392
|
+
display: none;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* mobile dropdown pills */
|
|
397
|
+
.nav-mobile-dropdowns {
|
|
398
|
+
display: flex;
|
|
399
|
+
align-items: center;
|
|
400
|
+
gap: 0.5rem;
|
|
401
|
+
flex-shrink: 0;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.nav-mobile-dropdown-summary {
|
|
405
|
+
display: flex;
|
|
406
|
+
align-items: center;
|
|
407
|
+
padding: 0.375rem 0.5rem;
|
|
408
|
+
font-size: 0.6875rem;
|
|
409
|
+
text-transform: uppercase;
|
|
410
|
+
letter-spacing: 0.05em;
|
|
411
|
+
font-weight: 600;
|
|
412
|
+
color: #cbd5e1;
|
|
413
|
+
background-color: var(--nav-mobile-item-bg, #1e293b);
|
|
414
|
+
border: 1px solid var(--nav-mobile-item-border, #475569);
|
|
415
|
+
border-radius: 0.25rem;
|
|
416
|
+
cursor: pointer;
|
|
417
|
+
white-space: nowrap;
|
|
418
|
+
transition: color 0.15s, background-color 0.15s;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.nav-mobile-dropdown-summary:hover {
|
|
422
|
+
color: var(--nav-text, #ffffff);
|
|
423
|
+
background-color: #334155;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* breadcrumbs */
|
|
427
|
+
.nav-breadcrumbs {
|
|
428
|
+
flex: 1;
|
|
429
|
+
min-width: 0;
|
|
430
|
+
overflow-x: auto;
|
|
431
|
+
scrollbar-width: none;
|
|
432
|
+
-ms-overflow-style: none;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.nav-breadcrumbs::-webkit-scrollbar {
|
|
436
|
+
display: none;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.nav-breadcrumb-list {
|
|
440
|
+
display: flex;
|
|
441
|
+
align-items: center;
|
|
442
|
+
list-style: none;
|
|
443
|
+
margin: 0;
|
|
444
|
+
padding: 0;
|
|
445
|
+
white-space: nowrap;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.nav-breadcrumb-item {
|
|
449
|
+
display: inline-block;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.nav-breadcrumb-item a {
|
|
453
|
+
font-size: 0.875rem;
|
|
454
|
+
font-weight: 500;
|
|
455
|
+
color: var(--nav-text-muted, #94a3b8);
|
|
456
|
+
text-decoration: none;
|
|
457
|
+
text-transform: capitalize;
|
|
458
|
+
transition: color 0.15s;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.nav-breadcrumb-item a:hover {
|
|
462
|
+
color: var(--nav-hover, #6366f1);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.nav-breadcrumb-sep {
|
|
466
|
+
margin: 0 0.5rem;
|
|
467
|
+
color: var(--nav-text-muted, #94a3b8);
|
|
468
|
+
font-size: 0.875rem;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/* mobile action buttons (pushed right) */
|
|
472
|
+
.nav-mobile-actions {
|
|
473
|
+
margin-left: auto;
|
|
474
|
+
display: flex;
|
|
475
|
+
align-items: center;
|
|
476
|
+
gap: 0.5rem;
|
|
477
|
+
flex-shrink: 0;
|
|
478
|
+
}
|