@sofidevo/astro-dynamic-header 3.0.1 → 3.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/README.md CHANGED
@@ -1,505 +1,275 @@
1
1
  # @sofidevo/astro-dynamic-header
2
2
 
3
- A dynamic, responsive header component for Astro projects that can switch between floating and fullscreen styles with multi-level dropdown navigation support.
4
-
5
- > [!WARNING]
6
- > **Breaking Changes — v2.2+**: The `logo` prop has been removed in favor of a much more flexible `logo` slot. The `color` prop has also been removed from the `HamburgerButton` component to ensure flawless dark mode synchronization. The `theme` property behavior was also refactored to prioritize native CSS variables, reducing DOM bloat and enabling purely CSS-based customization. See the [Changelog](#changelog) section below.
7
-
8
- > [!WARNING]
9
- > **Breaking Changes — v2.0+**: The `logo` and `navigation` props were restructured from strings/arrays to configuration objects. See the [Component Props](#component-props) and [Comprehensive Example](#comprehensive-example) to migrate.
10
-
11
- > [!NOTE]
12
- > **What's new — v2.1**: `CustomClassNames` has been renamed `HeaderClassNames` (the old name still works as an alias). New nested class props added to `LogoConfig` and `NavConfig`. New `mobileNav` slot in `HeaderClassNames`. `defaultThemes` is now exported for use outside the component. See the [Changelog](#changelog) section below.
3
+ A dynamic, responsive header component for Astro projects. Supports floating and fullscreen layouts, multi-level dropdown navigation, native CSS variable customization, dark mode, and TypeScript — all with zero external icon dependencies.
13
4
 
14
5
  ## Features
15
6
 
16
- - **Dynamic Styles**: Switch between floating and fullscreen header layouts
17
- - **Fully Responsive**: Mobile-first design with hamburger menu
18
- - **Multi-level Dropdowns**: Support for nested navigation menus
19
- - **Slot Support**: Customizable slots for desktop header and mobile panel content
20
- - **TypeScript Support**: Full type safety and IntelliSense
21
- - **Two-layer customization**: High-level `classNames` prop + fine-grained nested class props inside `navigation` and `logo`
22
- - **Exportable defaults**: Import and extend `defaultThemes` from your own code
23
- - **Astro Optimized**: Built specifically for Astro framework
7
+ - **Floating & Fullscreen layouts** — switch layouts with a single prop.
8
+ - **Fully Responsive** — mobile-first accordion dropdowns with optimized hit targets.
9
+ - **3-level Dropdowns** — top, secondary, and tertiary nested navigation items.
10
+ - **Dark Mode Ready** — auto-detects `.dark` on `<html>`, or forces a state with `preset`.
11
+ - **Inline SVG Icons** — no external CDNs, no extra network requests, no flash of missing icons.
12
+ - **Slot Support** — inject your custom logo and header actions directly into slots.
13
+ - **Pure CSS Customization** — customize background, blur, and colors using native CSS variables.
14
+ - **Full TypeScript** — all props and config interfaces are fully typed.
24
15
 
25
- ### Live demo
16
+ ### Live Demo
26
17
 
27
18
  [https://base-astro-psi.vercel.app/fullscreen-demo](https://base-astro-psi.vercel.app/fullscreen-demo)
28
19
 
20
+ ---
21
+
29
22
  ## Installation
30
23
 
31
24
  ```bash
32
- npm i @sofidevo/astro-dynamic-header
25
+ npm i @sofidevo/astro-dynamic-header
33
26
  ```
34
27
 
35
- ## Required Dependencies
28
+ No external CDN scripts or stylesheet additions are required.
36
29
 
37
- You need to add the Iconify CDN to the head of your project for the hamburger menu icons to work properly:
38
-
39
- ```html
40
- <script src="https://code.iconify.design/iconify-icon/3.0.0/iconify-icon.min.js"></script>
41
- ```
42
-
43
- Add this to your main layout or in the `<head>` section of your Astro pages.
30
+ ---
44
31
 
45
32
  ## Quick Start
46
33
 
47
- ### Basic Usage (Automatic Theme)
48
-
49
- By default, the header uses `preset="auto"`, which automatically detects the theme based on a `.dark` class on your root element (`html` or `body`).
50
-
51
34
  ```astro
52
35
  ---
53
36
  import Header from '@sofidevo/astro-dynamic-header/Header';
54
37
 
55
38
  const menuItems = [
56
39
  { link: '/about', text: 'About' },
40
+ { link: '/contact', text: 'Contact' },
57
41
  ];
58
42
  ---
59
43
 
60
- <!-- Detects .dark class on root automatically -->
61
- <Header navigation={{ menuItems }} />
44
+ <Header navigation={{ menuItems }}>
45
+ <a slot="logo" href="/">MyLogo</a>
46
+ </Header>
62
47
  ```
63
48
 
64
- ### Advanced Usage (Dual-Theme Customization)
65
-
66
- You can provide custom colors for both light and dark modes simultaneously.
67
-
68
- ```astro
69
-
70
49
  ---
71
- import Header from '@sofidevo/astro-dynamic-header/Header';
72
- const navigation = {
73
- menuItems: [
74
- { link: '/about', text: 'About' },
75
- ]
76
- };
77
- const theme = {
78
- light: {
79
- accentColor: "#3e1c71",
80
- backgroundColor: "rgba(255, 255, 255, 0.8)"
81
- },
82
- dark: {
83
- accentColor: "#00ffff",
84
- backgroundColor: "rgba(20, 20, 20, 0.9)"
85
- }
86
- };
87
- ---
88
-
89
- <Header
90
- preset="auto"
91
- theme={theme}
92
- navigation={navigation}
93
- />
94
- ```
95
50
 
96
51
  ## Component Props
97
52
 
98
- ### Header Component
53
+ ### `<Header>`
99
54
 
100
- | Prop | Type | Default | Description |
101
- | ------------ | ----------------------------- | ------------ | ---------------------------------------------- |
102
- | `headerType` | `"floating" \| "fullscreen"` | `"floating"` | Header layout style |
103
- | `preset` | `"light" \| "dark" \| "auto"` | `"auto"` | Theme behavior. `auto` follows root class. |
104
- | `navigation` | `NavConfig` | `{}` | Navigation configuration object |
105
- | `theme` | `DualThemeConfig` | `{}` | Custom theme overrides for light/dark |
106
- | `classNames` | `HeaderClassNames` | `{}` | High-level class overrides for layout elements |
55
+ | Prop | Type | Default | Description |
56
+ | ------------ | ----------------------------- | ------------ | ------------------------------------------------- |
57
+ | `headerType` | `"floating" \| "fullscreen"` | `"floating"` | Layout style |
58
+ | `preset` | `"light" \| "dark" \| "auto"` | `"auto"` | Theme mode. `"auto"` follows `.dark` on `<html>`. |
59
+ | `navigation` | `NavConfig` | `{}` | Menu items, home link, and custom CSS classes |
60
+ | `theme` | `DualThemeConfig` | `{}` | Optional theme overrides (prefer CSS variables) |
61
+ | `classNames` | `HeaderClassNames` | `{}` | Inject CSS classes into structural elements |
107
62
 
108
- ### Config Objects
63
+ ---
109
64
 
110
- #### DualThemeConfig
65
+ ## Configuration Objects
111
66
 
112
- | Propery | Type | Description |
113
- | ------- | ------------- | ---------------------------- |
114
- | `light` | `ThemeConfig` | Styles applied in light mode |
115
- | `dark` | `ThemeConfig` | Styles applied in dark mode |
67
+ ### `NavConfig`
116
68
 
117
- #### ThemeConfig
69
+ | Property | Type | Default | Description |
70
+ | --------------------- | ------------ | ------- | ------------------------------------------------------- |
71
+ | `menuItems` | `MenuItem[]` | `[]` | Top-level navigation items |
72
+ | `homeUrl` | `string` | `"/"` | URL for the home link |
73
+ | `header__menu__class` | `string` | — | Extra CSS class(es) for the desktop `<nav>` element |
74
+ | `header__item__class` | `string` | — | Extra CSS class(es) for each top-level `<li>` menu item |
75
+ | `menu__link__class` | `string` | — | Extra CSS class(es) for each top-level `<a>` link |
118
76
 
119
- | Propery | Type | Default |
120
- | ----------------------- | -------- | ---------------- |
121
- | `backgroundColor` | `string` | _Preset default_ |
122
- | `backgroundColorOpaque` | `string` | _Preset default_ |
123
- | `backdropBlur` | `string` | `"blur(20px)"` |
124
- | `zIndex` | `number` | `10` |
125
- | `textColor` | `string` | _Preset default_ |
126
- | `accentColor` | `string` | _Preset default_ |
77
+ ### `MenuItem`
127
78
 
128
- > [!IMPORTANT]
129
- > **Transparency vs Solid Submenus**: To ensure the best UI and avoid rendering bugs with `backdrop-filter` on nested elements, submenus and the mobile navigation panel are **solid/opaque**.
130
- >
131
- > If you use a transparent `backgroundColor` (e.g., `rgba`), remember to also provide its solid counterpart in `backgroundColorOpaque`.
79
+ | Property | Type | Required | Description |
80
+ | --------- | --------------------- | -------- | --------------------------------- |
81
+ | `link` | `string` | ✅ | URL the item points to |
82
+ | `text` | `string` | ✅ | Display label |
83
+ | `submenu` | `SecondaryMenuItem[]` | — | Optional nested items (2nd level) |
132
84
 
133
- ```astro
134
- <Header theme={{
135
- light: {
136
- backgroundColor: "rgba(255, 255, 255, 0.7)", // Transparent header body
137
- backgroundColorOpaque: "#ffffff", // Solid submenus
138
- }
139
- }} />
140
- ```
85
+ ### `SecondaryMenuItem`
141
86
 
142
- #### CustomClassNames → HeaderClassNames
87
+ | Property | Type | Required | Description |
88
+ | --------- | -------------------- | -------- | --------------------------------- |
89
+ | `link` | `string` | ✅ | URL the item points to |
90
+ | `text` | `string` | ✅ | Display label |
91
+ | `submenu` | `TertiaryMenuItem[]` | — | Optional nested items (3rd level) |
143
92
 
144
- > [!NOTE]
145
- > `CustomClassNames` was renamed to `HeaderClassNames` in v2.1. The old name still works as a type alias — no migration required.
93
+ ### `TertiaryMenuItem`
146
94
 
147
- The `classNames` prop targets the **structural wrapper elements** of the Header. For fine-grained control of individual nav links or the logo internals, use the nested `xxx__class` props inside the `navigation` or `logo` config objects instead.
95
+ | Property | Type | Required | Description |
96
+ | -------- | -------- | -------- | ---------------------- |
97
+ | `link` | `string` | ✅ | URL the item points to |
98
+ | `text` | `string` | ✅ | Display label |
148
99
 
149
- | Property | Target Element | Purpose & Common Use Cases |
150
- | ----------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
151
- | `container` | Outer `div` wrapping the header | **Positioning & Layout**: `top-0`, `z-50`, `fixed`, adjusting `max-width` and `mx-auto` logic. |
152
- | `header` | Inner `<header>` element | **Appearance**: Shadows (`shadow-md`), borders (`border-b`), or custom transition durations. |
153
- | `logo` | `<a>` tag surrounding the logo | **Interactions**: Hover states, custom focus rings, or adjusting the flex alignment of the logo group. |
154
- | `logoText` | `<span>` tag containing the logo text | **Typography**: Font weights, text shadows, or specific tracking/leading classes. |
155
- | `nav` | `<div>` wrapping the desktop navigation items | **Desktop Layout**: Spacing between the logo and the menu, or responsive visibility classes (`hidden md:flex`). |
156
- | `mobileNav` | Root `<nav>` of the mobile slide-in panel | **Mobile Panel**: Extra backdrop blur, custom z-index, slide-in overrides. |
100
+ ---
157
101
 
158
- ##### Usage Examples
102
+ ## Custom Class Names
159
103
 
160
- **Premium Shadow & Border:**
104
+ The `classNames` prop lets you inject CSS classes into specific structural elements.
161
105
 
162
- ```astro
163
- <Header
164
- classNames={{
165
- header: "shadow-xl border-b border-black/5 dark:border-white/10 transition-all duration-500",
166
- container: "top-4 px-6",
167
- mobileNav: "backdrop-blur-md",
168
- }}
169
- />
170
- ```
106
+ ### `HeaderClassNames`
171
107
 
172
- **Custom Typography & Nav Spacing:**
108
+ | Property | Target element | Common use cases |
109
+ | ----------- | -------------------------------------- | ------------------------------------------------- |
110
+ | `container` | Outer `<div>` wrapping the header | Positioning, padding, `z-index` |
111
+ | `header` | Inner `<header>` element | Shadows, borders, transitions |
112
+ | `logo` | `<a>` tag surrounding the logo slot | Hover effects, focus rings |
113
+ | `logoText` | `<span>` containing the logo text | Font weight, tracking, text transforms |
114
+ | `nav` | `<div>` wrapping the desktop nav items | Spacing, alignment, responsive visibility |
115
+ | `mobileNav` | Root `<nav>` of the mobile panel | Backdrop blur, custom z-index, slide-in overrides |
173
116
 
174
117
  ```astro
118
+ <!-- Tailwind example -->
175
119
  <Header
176
120
  classNames={{
177
- logoText: "tracking-tighter font-black italic uppercase",
178
- nav: "ml-auto gap-8",
121
+ header: "shadow-xl border-b border-black/5 dark:border-white/10",
122
+ container: "top-4 px-6",
123
+ mobileNav: "backdrop-blur-md",
179
124
  }}
180
125
  />
181
126
  ```
182
127
 
183
- > [!TIP]
184
- > Since these classes are injected using Astro's `class:list`, you can also pass objects or arrays if you need conditional logic for your custom classes.
185
-
186
-
187
-
188
- #### NavConfig
189
-
190
- | Property | Type | Description |
191
- | --------------------- | ------------ | ------------------------------------------------------- |
192
- | `homeUrl` | `string` | URL for the home link (defaults to `/`) |
193
- | `menuItems` | `MenuItem[]` | Array of navigation menu items |
194
- | `header__menu__class` | `string` | Extra CSS class(es) for the desktop `<nav>` element |
195
- | `header__item__class` | `string` | Extra CSS class(es) for each top-level `<li>` menu item |
196
- | `menu__link__class` | `string` | Extra CSS class(es) for each top-level `<a>` link |
197
-
198
- #### MenuItem
199
-
200
- | Property | Type | Description |
201
- | --------- | --------------------- | ----------------------------------- |
202
- | `link` | `string` | URL the menu item points to |
203
- | `text` | `string` | Label text for the menu item |
204
- | `submenu` | `SecondaryMenuItem[]` | Optional array of nested menu items |
205
-
206
- ## Slots Support
207
-
208
- The Header component provides a flexible slot system that allows you to add additional content:
209
-
210
- ### Available Slots
128
+ ---
211
129
 
212
- | Slot Name | Location | Visibility | Description |
213
- | --------- | --------------------- | --------------------- | ---------------------------------------- |
214
- | `logo` | Header Desktop/Mobile | Always | Add your custom logo HTML/Components |
215
- | `actions` | Header & Mobile panel | Responsive visibility | Add action buttons (login, signup, etc.) |
130
+ ## Slots
216
131
 
217
- ### Example with Slots
132
+ | Slot name | Visible on | Description |
133
+ | --------- | ---------------- | ---------------------------------------------------------- |
134
+ | `logo` | Header | Render your logo exactly as you need (native HTML/widgets) |
135
+ | `actions` | Desktop + mobile | Add buttons, links, or utility widgets |
218
136
 
219
137
  ```astro
220
- ---
221
- import Header from '@sofidevo/astro-dynamic-header/Header';
222
-
223
- const navigation = {
224
- menuItems: [{ link: '/about', text: 'About' }]
225
- };
226
- ---
227
-
228
- <Header navigation={navigation}>
138
+ <Header navigation={{ menuItems }}>
139
+ <a slot="logo" href="/" class="logo-link">
140
+ <img src="/logo.svg" alt="Branding" width="40" />
141
+ <span>MyBrand</span>
142
+ </a>
229
143
  <div slot="actions">
230
- <button class="login-btn">Login</button>
144
+ <a href="/login" class="btn">Log in</a>
231
145
  </div>
232
146
  </Header>
233
147
  ```
234
148
 
235
- ## Comprehensive Example
236
-
237
- Below is a complete implementation example showcasing custom logo configuration, navigation with a home URL, and theme overrides.
238
-
239
- ```astro
240
- ---
241
- import Header from '@sofidevo/astro-dynamic-header/Header';
242
-
243
- const menuItems = [
244
- {
245
- link: "#",
246
- text: "Services",
247
- submenu: [
248
- { link: "/design", text: "Design" },
249
- { link: "/consulting", text: "Consulting" },
250
- {
251
- link: "#",
252
- text: "Web Development",
253
- submenu: [
254
- { link: "/web/frontend", text: "Frontend" },
255
- { link: "/web/backend", text: "Backend" },
256
- { link: "/web/fullstack", text: "Full Stack" },
257
- ],
258
- },
259
- ],
260
- },
261
- { link: "/about", text: "About" },
262
- { link: "/contact", text: "Contact" },
263
- ];
264
-
265
- ];
266
149
  ---
267
150
 
268
- <style is:inline>
269
- :root {
270
- /* Pure CSS Theming Configuration */
271
- --l-accent: #ff0000;
272
- --l-bg: rgba(255, 255, 255, 0.8);
273
-
274
- --d-accent: #00ffff;
275
- --d-bg: rgba(20, 20, 20, 0.9);
276
- }
277
- </style>
151
+ ## Customization & Theme Config
278
152
 
279
- <Header
280
- headerType="floating"
281
- preset="dark"
282
- navigation={{
283
- homeUrl: "/",
284
- menuItems: menuItems,
285
- header__menu__class: "flex gap-6",
286
- menu__link__class: "font-medium",
287
- }}
288
- classNames={{
289
- header: "shadow-xl",
290
- mobileNav: "backdrop-blur-md",
291
- }}
292
- >
293
- <a slot="logo" href="/" style="display: flex; align-items: center; gap: 10px; color: inherit; text-decoration: none;">
294
- <img src="https://sofidev.blog/img/branding/logo.webp" alt="My Site Logo" width="44" />
295
- <span style="font-weight: bold; font-size: 1.2rem;">SofiDev</span>
296
- </a>
297
- <button slot="actions">Login</button>
298
- </Header>
299
- ```
300
-
301
- ## Header Types
153
+ You can fully customize the color scheme using **CSS Custom Properties** (recommended) or the `theme` prop.
302
154
 
303
- ### Floating Header
155
+ ### Option 1: Native CSS Variables (Recommended)
304
156
 
305
- - Centered with max-width constraint
306
- - Rounded corners
307
- - Padding around container
308
- - Perfect for modern, card-like designs
157
+ Set CSS variables globally in your `:root` style block:
309
158
 
310
- ### Fullscreen Header
159
+ ```css
160
+ :root {
161
+ /* Light theme overrides */
162
+ --l-accent: #7c3aed;
163
+ --l-bg: rgba(255, 255, 255, 0.85);
164
+ --l-bg-opaque: #ffffff;
165
+ --l-text: #1a1a1a;
166
+ --l-blur: blur(20px);
167
+
168
+ /* Dark theme overrides */
169
+ --d-accent: #a78bfa;
170
+ --d-bg: rgba(10, 10, 10, 0.85);
171
+ --d-bg-opaque: #0a0a0a;
172
+ --d-text: #f5f5f5;
173
+ --d-blur: blur(20px);
174
+
175
+ /* Hamburger lines override */
176
+ --color-hamburger-lines: #7c3aed;
177
+ }
178
+ ```
311
179
 
312
- - Full viewport width
313
- - No border radius
314
- - Edge-to-edge design
315
- - Ideal for traditional website layouts
180
+ ### Option 2: JS Theme Prop
316
181
 
317
- ## Styling and Customization
182
+ ```astro
183
+ ---
184
+ import { defaultThemes } from '@sofidevo/astro-dynamic-header';
318
185
 
319
- The component uses CSS custom properties that you can override:
186
+ const theme = {
187
+ light: {
188
+ ...defaultThemes.light,
189
+ accentColor: "#7c3aed",
190
+ backgroundColor: "rgba(255, 255, 255, 0.85)",
191
+ },
192
+ dark: {
193
+ ...defaultThemes.dark,
194
+ accentColor: "#a78bfa",
195
+ }
196
+ };
197
+ ---
320
198
 
321
- ```css
322
- :root {
323
- --light-spot-color: #00ffff;
324
- --color-tertiary: #ffffff;
325
- --color-hamburger-lines: #ffffff;
326
- }
199
+ <Header theme={theme} />
327
200
  ```
328
201
 
329
- ## TypeScript Support
202
+ > [!IMPORTANT]
203
+ > When using transparent backgrounds, always supply a solid fallback in `backgroundColorOpaque`. Submenus and mobile panels utilize this solid color to prevent visual glitches with nested blur effects.
330
204
 
331
- The package provides full TypeScript support. You can import types to ensure your configuration is correct:
205
+ ---
206
+
207
+ ## TypeScript
332
208
 
333
209
  ```astro
334
210
  ---
335
211
  import Header from '@sofidevo/astro-dynamic-header/Header';
336
- import { defaultThemes } from '@sofidevo/astro-dynamic-header';
337
212
  import type {
213
+ MenuItem,
338
214
  NavConfig,
339
- DualThemeConfig,
340
215
  HeaderClassNames,
341
- MenuItem,
342
- SecondaryMenuItem
216
+ HeaderProps,
343
217
  } from '@sofidevo/astro-dynamic-header';
344
218
 
345
- const navigation: NavConfig = {
346
- menuItems: [
347
- {
348
- link: '/products',
349
- text: 'Products',
350
- submenu: [
351
- { link: '/software', text: 'Software' },
352
- { link: '/hardware', text: 'Hardware' }
353
- ]
354
- }
355
- ],
356
- header__menu__class: "flex gap-6",
357
- };
219
+ const menuItems: MenuItem[] = [
220
+ { link: '/about', text: 'About' }
221
+ ];
358
222
 
359
- // Prefer CSS custom properties globally rather than passing the `theme` object.
360
- // But you can still use the theme prop if needed:
361
- const theme: DualThemeConfig = {
362
- light: { ...defaultThemes.light, accentColor: "#3e1c71" },
363
- dark: { ...defaultThemes.dark, accentColor: "#00ffff", backgroundColor: "rgba(10, 10, 10, 0.9)" },
223
+ const navigation: NavConfig = {
224
+ menuItems,
225
+ homeUrl: "/",
226
+ header__menu__class: "flex gap-4"
364
227
  };
365
228
 
366
229
  const classNames: HeaderClassNames = {
367
- header: "shadow-xl",
368
- mobileNav: "backdrop-blur-md",
230
+ header: "shadow-lg"
369
231
  };
370
232
  ---
371
233
 
372
234
  <Header
373
235
  navigation={navigation}
374
236
  classNames={classNames}
375
- preset="auto"
376
237
  />
377
238
  ```
378
239
 
379
- ### Available Types
380
-
381
- | Type | Description |
382
- | ------------------- | ----------------------------------------------- |
383
- | `MenuItem` | Top-level menu item with optional properties |
384
- | `SecondaryMenuItem` | Second-level menu item |
385
- | `TertiaryMenuItem` | Third-level menu item |
386
- | `NavConfig` | Navigation config (items + nested class props) |
387
- | `ThemeConfig` | Individual theme settings (colors, blur, etc.) |
388
- | `DualThemeConfig` | Combined settings for light and dark modes |
389
- | `HeaderClassNames` | Class overrides for structural wrapper elements |
390
- | `CustomClassNames` | **Deprecated alias** for `HeaderClassNames` |
391
- | `HeaderProps` | Main props for the Header component |
392
-
393
- ## Browser Support
394
-
395
- - All modern browsers (Chrome, Firefox, Safari, Edge)
396
- - Mobile responsive design with optimized touch targets
397
- - Supports CSS `backdrop-filter` for glassmorphism
398
- - Automatic theme switching based on OS or site preference via `.dark` class
399
-
400
- ## Troubleshooting
401
-
402
- ### Import Issues
403
-
404
- If you encounter import errors, try these solutions:
405
-
406
- 1. **Use direct subpath import:**
407
-
408
- ```astro
409
- import Header from '@sofidevo/astro-dynamic-header/Header';
410
- ```
411
-
412
- 2. **Check relative imports in TS:**
413
- In some environments (like `node16`), you might need to use the `.js` extension even for TypeScript files when importing from the package internals, though the main entry point handles this for you.
414
-
415
- 3. **Verify TypeScript configuration:**
416
-
417
- ```json
418
- // tsconfig.json
419
- {
420
- "compilerOptions": {
421
- "moduleResolution": "bundler",
422
- "allowImportingTsExtensions": true
423
- }
424
- }
425
- ```
426
-
427
- ### Compatibility
428
-
429
- - Astro 4.x and 5.x
430
- - SSG Projects (Static Site Generation)
431
- - SSR Projects (Server-Side Rendering)
432
- - Hybrid Projects (output: 'hybrid')
433
-
434
- ## Live Examples
435
-
436
- Visit our demo website to see the component in action with interactive examples and complete documentation.
437
-
438
- ## License
439
-
440
- MIT License - see the [LICENSE](./LICENSE) file for details.
441
-
442
- ## Support
443
-
444
- If you find this package helpful, please consider giving it a star on GitHub!
445
-
446
240
  ---
447
241
 
448
- ## Changelog
449
-
450
- ### v2.2 — Performance & DX Optimization
451
-
452
- #### Breaking changes
453
-
454
- - **Removed `logo` Object Configuration prop**: The `logo` property and `LogoConfig` interface have been removed. You should now use `<slot name="logo" />` to render your logo exactly as you need, passing native HTML or Astro components.
455
- - **Removed `color` prop from `HamburgerButton`**: The `HamburgerButton` now inherits `--text-color` natively via CSS, which fixes a bug where the button would not properly update its color when switching to dark mode. Any direct uses of `<HamburgerButton color="..." />` will fail to compile in TypeScript and should be updated to rely on global CSS variables or context inheritance.
456
-
457
- #### New features and enhancements
242
+ ## Troubleshooting & FAQ
458
243
 
459
- - **Native CSS Variable Theming (Better DX)**: Redesigned how default styles are injected into the DOM. `Header.astro` no longer injects all default properties into `style={...}` attributes on load, solving the "DOM bloat" issue. You can now deeply customize the component purely by setting CSS variables like `--l-bg`, `--d-bg`, `--l-accent`, and `--d-accent` inside your `:root` style tag, removing the necessity to parse massive `theme` JS objects.
460
- - **Dead Code Elimination**: Cleaned up repetitive prop destructuring inside `MobileNav.astro`.
244
+ ### Import issues
461
245
 
462
- ### v2.1 — Style & Customization Refactor
246
+ Import using the direct subpath:
463
247
 
464
- #### New features
465
-
466
- - **`HeaderClassNames`** replaces `CustomClassNames` (alias kept — no migration required).
467
- New `mobileNav` slot targets the mobile slide-in `<nav>` panel.
248
+ ```astro
249
+ import Header from '@sofidevo/astro-dynamic-header/Header';
250
+ ```
468
251
 
469
- - **Nested class props** on `LogoConfig`:
470
- | Prop | Targets |
471
- |------|---------|
472
- | `logo__container__class` | Logo `<a>` wrapper |
473
- | `logo__text__class` | Logo text `<span>` |
252
+ Other components are exported similarly:
474
253
 
475
- - **Nested class props** on `NavConfig`:
476
- | Prop | Targets |
477
- |------|---------|
478
- | `header__menu__class` | Desktop `<nav>` element |
479
- | `header__item__class` | Each top-level `<li>` |
480
- | `menu__link__class` | Each top-level `<a>` |
254
+ ```astro
255
+ import NavMenu from '@sofidevo/astro-dynamic-header/NavMenu';
256
+ import MobileNav from '@sofidevo/astro-dynamic-header/MobileNav';
257
+ import ChevronIcon from '@sofidevo/astro-dynamic-header/ChevronIcon';
258
+ ```
481
259
 
482
- - **`defaultThemes` exported** — import and spread the built-in tokens to extend them:
483
- ```ts
484
- import { defaultThemes } from "@sofidevo/astro-dynamic-header";
485
- const theme = { dark: { ...defaultThemes.dark, accentColor: "#f43f5e" } };
486
- ```
260
+ ### Acordion icons not showing up
487
261
 
488
- #### Internal improvements
262
+ Icons are rendered as inline SVG components. If you are upgrading from `v1.x` or `v2.0` and have the old ChevronIcon CDN `<script>` tag in your layout `<head>`, you can safely remove it.
489
263
 
490
- - Default theme tokens extracted to `src/defaults.ts` — easier to read and maintain.
491
- - `HamburgerButton` color is now explicitly wired from the resolved theme (no visual change).
264
+ ---
492
265
 
493
- #### Deprecations
266
+ ## Compatibility
494
267
 
495
- - `CustomClassNames` — use `HeaderClassNames` instead. The alias will remain until the next major version.
268
+ - **Astro 7.x**
269
+ - SSG, SSR, and Hybrid project outputs.
496
270
 
497
- ### v2.0 — Object-based Configuration API
271
+ ---
498
272
 
499
- > [!WARNING]
500
- > Breaking change: `logo` changed from `string` to `LogoConfig`, `navigation` changed from `MenuItem[]` to `NavConfig`.
273
+ ## License
501
274
 
502
- - `logo` prop restructured to `LogoConfig` object (`src`, `alt`, `width`, `text`, `textSize`, `textColor`).
503
- - `navigation` prop restructured to `NavConfig` object (`homeUrl`, `menuItems`).
504
- - `classNames` prop introduced for CSS class injection (`CustomClassNames`).
505
- - Dual-theme support via `DualThemeConfig` (`light` + `dark`).
275
+ MIT License.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofidevo/astro-dynamic-header",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "description": "A dynamic Astro header component that switches between floating and fullscreen styles",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -14,10 +14,13 @@
14
14
  "./NavMenu": "./src/NavMenu.astro",
15
15
  "./MobileNav": "./src/MobileNav.astro",
16
16
  "./HamburgerButton": "./src/HamburgerButton.astro",
17
+ "./ChevronIcon": "./src/ChevronIcon.astro",
18
+ "./defaults": "./src/defaults.ts",
17
19
  "./Header.astro": "./src/Header.astro",
18
20
  "./NavMenu.astro": "./src/NavMenu.astro",
19
21
  "./MobileNav.astro": "./src/MobileNav.astro",
20
- "./HamburgerButton.astro": "./src/HamburgerButton.astro"
22
+ "./HamburgerButton.astro": "./src/HamburgerButton.astro",
23
+ "./ChevronIcon.astro": "./src/ChevronIcon.astro"
21
24
  },
22
25
  "files": [
23
26
  "src/*.ts",
@@ -50,13 +53,13 @@
50
53
  "author": "SofiDev contact@sofidev.top",
51
54
  "license": "MIT",
52
55
  "peerDependencies": {
53
- "astro": "^6.0.6"
56
+ "astro": "^7.0.0"
54
57
  },
55
58
  "devDependencies": {
56
59
  "@astrojs/check": "^0.9.9",
57
60
  "@types/jsdom": "^21.1.7",
58
61
  "@vitest/coverage-v8": "^3.2.4",
59
- "astro": "^6.3.7",
62
+ "astro": "^7.0.4",
60
63
  "jsdom": "^26.1.0",
61
64
  "typescript": "^5.0.0",
62
65
  "vitest": "^3.2.4"
@@ -0,0 +1,25 @@
1
+ ---
2
+ const { class: className, ...rest } = Astro.props;
3
+
4
+ const viewBox = "0 0 24 24";
5
+ const content = `<path class="hlhawq"/>`;
6
+ ---
7
+
8
+ <svg
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ width="1em"
11
+ height="1em"
12
+ class={className}
13
+ viewBox={viewBox}
14
+ {...rest}
15
+ set:html={content}
16
+ />
17
+
18
+ <style is:inline>
19
+ .hlhawq {
20
+ fill: currentColor;
21
+ d: path(
22
+ "M11.646 15.146L5.854 9.354a.5.5 0 0 1 .353-.854h11.586a.5.5 0 0 1 .353.854l-5.793 5.792a.5.5 0 0 1-.707 0"
23
+ );
24
+ }
25
+ </style>
package/src/Header.astro CHANGED
@@ -155,6 +155,7 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
155
155
  <div class:list={["nav-menu-wrapper", classNames.nav]}>
156
156
  <NavMenu
157
157
  menuItems={menuItems}
158
+ homeUrl={homeUrl}
158
159
  header__menu__class={header__menu__class}
159
160
  header__item__class={header__item__class}
160
161
  menu__link__class={menu__link__class}
@@ -173,6 +174,7 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
173
174
 
174
175
  <MobileNav
175
176
  menuItems={menuItems}
177
+ homeUrl={homeUrl}
176
178
  type={headerType}
177
179
  mobileNav__class={classNames.mobileNav}
178
180
  >
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import type { MenuItem, SecondaryMenuItem, TertiaryMenuItem } from "./index.js";
3
+ import ChevronIcon from "./ChevronIcon.astro";
3
4
 
4
5
  export interface Props {
5
6
  /** Header type for styling: "floating" or "fullscreen" */
@@ -10,6 +11,8 @@ export interface Props {
10
11
  showHomeLink?: boolean;
11
12
  /** Text for the home link (defaults to "Home") */
12
13
  homeText?: string;
14
+ /** The destination URL for the home link. @default "/" */
15
+ homeUrl?: string;
13
16
  /** Extra CSS class(es) for the root mobile `<nav>` panel. */
14
17
  mobileNav__class?: string;
15
18
  }
@@ -19,7 +22,8 @@ const {
19
22
  mobileNav__class,
20
23
  menuItems = [],
21
24
  showHomeLink = true,
22
- homeText = "Home"
25
+ homeText = "Home",
26
+ homeUrl = "/",
23
27
  } = Astro.props;
24
28
 
25
29
  const pagePathname = Astro.url.pathname;
@@ -34,11 +38,11 @@ const pagePathname = Astro.url.pathname;
34
38
  >
35
39
  <ol class="mobile-menu">
36
40
  {
37
- showHomeLink && pagePathname !== "/" && (
41
+ showHomeLink && pagePathname !== homeUrl && (
38
42
  <li class="header__item mobile-header__item">
39
43
  <a
40
44
  class="menu__link mobile-menu__link"
41
- href="/"
45
+ href={homeUrl}
42
46
  data-astro-prefetch="hover"
43
47
  >
44
48
  {homeText}
@@ -58,13 +62,7 @@ const pagePathname = Astro.url.pathname;
58
62
  >
59
63
  <summary class:list={["menu__summary", { active: isActive }]}>
60
64
  {item.text}
61
- <iconify-icon
62
- class="iconify-arrow"
63
- icon="codicon:triangle-down"
64
- width="15"
65
- height="15"
66
- style="color: var(--accent-color);"
67
- />
65
+ <ChevronIcon class="iconify-arrow" width={15} height={15} />
68
66
  </summary>
69
67
  <ol class="mobile-submenu">
70
68
  {item?.submenu?.map((sub: SecondaryMenuItem) => {
@@ -85,12 +83,10 @@ const pagePathname = Astro.url.pathname;
85
83
  ]}
86
84
  >
87
85
  {sub.text}
88
- <iconify-icon
86
+ <ChevronIcon
89
87
  class="iconify-arrow"
90
- icon="codicon:triangle-down"
91
- width="15"
92
- height="15"
93
- style="color: var(--accent-color);"
88
+ width={15}
89
+ height={15}
94
90
  />
95
91
  </summary>
96
92
  <ol class="mobile-subsubmenu">
@@ -240,7 +236,7 @@ const pagePathname = Astro.url.pathname;
240
236
  overflow: clip;
241
237
  }
242
238
 
243
- .mobile-details:open iconify-icon {
239
+ .mobile-details:open svg.iconify-arrow {
244
240
  transform: rotate(180deg);
245
241
  }
246
242
 
@@ -0,0 +1,106 @@
1
+ ---
2
+ const props = Astro.props;
3
+
4
+ const regex = /\sid="(\S+)"/g;
5
+ const counters = new Map<string, number>();
6
+
7
+ function nextID(id: string) {
8
+ id = id.replace(/[0-9]+$/, "") || "a";
9
+ const count = counters.get(id) || 0;
10
+ counters.set(id, count + 1);
11
+ return count ? `${id}${count}` : id;
12
+ }
13
+
14
+ function replaceIDs(body: string) {
15
+ const ids: string[] = [];
16
+ let match;
17
+ while ((match = regex.exec(body))) ids.push(match[1]);
18
+ if (!ids.length) return body;
19
+ const suffix =
20
+ "suffix" + ((Math.random() * 16777216) | Date.now()).toString(16);
21
+ ids.forEach((id) => {
22
+ const newID = nextID(id);
23
+ const escapedID = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24
+ body = body.replace(
25
+ new RegExp('([#;"])(' + escapedID + ')([")]|\\.[a-z])', "g"),
26
+ "$1" + newID + suffix + "$3",
27
+ );
28
+ });
29
+ body = body.replace(new RegExp(suffix, "g"), "");
30
+ return body;
31
+ }
32
+
33
+ const viewBox = "0 0 24 24";
34
+ const content = replaceIDs(
35
+ `<defs><mask id="SVGnw0mAdjE"><path d="M12 2c5.52 0 10 4.48 10 10c0 5.52 -4.48 10 -10 10c-5.52 0 -10 -4.48 -10 -10c0 -5.52 4.48 -10 10 -10Z" class="n1mjuz"><animate fill="freeze" attributeName="d" dur="0.4s" values="M12 6c3.31 0 6 2.69 6 6c0 3.31 -2.69 6 -6 6c-3.31 0 -6 -2.69 -6 -6c0 -3.31 2.69 -6 6 -6Z;M12 2c5.52 0 10 4.48 10 10c0 5.52 -4.48 10 -10 10c-5.52 0 -10 -4.48 -10 -10c0 -5.52 4.48 -10 10 -10Z"/><set fill="freeze" attributeName="opacity" begin="0.4s" to="0"/></path><path d="M18 -4c5.52 0 10 4.48 10 10c0 5.52 -4.48 10 -10 10c-5.52 0 -10 -4.48 -10 -10c0 -5.52 4.48 -10 10 -10Z"><animate fill="freeze" attributeName="d" dur="0.4s" values="M22 -4c3.31 0 6 2.69 6 6c0 3.31 -2.69 6 -6 6c-3.31 0 -6 -2.69 -6 -6c0 -3.31 2.69 -6 6 -6Z;M18 -4c5.52 0 10 4.48 10 10c0 5.52 -4.48 10 -10 10c-5.52 0 -10 -4.48 -10 -10c0 -5.52 4.48 -10 10 -10Z"/><set fill="freeze" attributeName="opacity" begin="0.4s" to="0"/></path></mask></defs><path class="smogax"><animate fill="freeze" attributeName="opacity" dur="0.4s" values="1;0"/></path><g class="cuyn6t"><path mask="url(#SVGnw0mAdjE)" class="t_ev7s"/><path opacity="0" class="izfmla"><set fill="freeze" attributeName="opacity" begin="0.4s" to="1"/></path></g><g stroke-dashoffset="4" class="abf34n"><path class="k7_l3l"><animate attributeName="stroke-dashoffset" begin="0.4s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="hhuniw"><animate attributeName="stroke-dashoffset" begin="1.07s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="z8cgtd"><animate attributeName="stroke-dashoffset" begin="1.74s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="drcfta"><animate attributeName="stroke-dashoffset" begin="2.41s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="yij7vx"><animate attributeName="stroke-dashoffset" begin="3.08s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="emoajy"><animate attributeName="stroke-dashoffset" begin="3.75s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="f1to6s"><animate attributeName="stroke-dashoffset" begin="4.42s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="niixso"><animate attributeName="stroke-dashoffset" begin="5.09s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path><path class="z8cgtd"><animate attributeName="stroke-dashoffset" begin="5.76s" dur="6s" keyTimes="0;0.08;0.22;0.3;1" repeatCount="indefinite" values="4;0;0;4;4"/></path></g>`,
36
+ );
37
+ ---
38
+
39
+ <svg
40
+ xmlns="http://www.w3.org/2000/svg"
41
+ width="25px"
42
+ height="25px"
43
+ viewBox={viewBox}
44
+ {...props}
45
+ set:html={content}
46
+ />
47
+
48
+ <style is:inline>
49
+ .n1mjuz {
50
+ fill: var(--svg-color--fff, #fff);
51
+ }
52
+ .smogax {
53
+ fill: none;
54
+ stroke: white;
55
+ stroke-linecap: round;
56
+ stroke-linejoin: round;
57
+ stroke-width: var(--svg-stroke-width--2px, 2px);
58
+ d: path(
59
+ "M12 21v1M21 12h1M12 3v-1M3 12h-1M18.5 18.5l0.5 0.5M18.5 5.5l0.5 -0.5M5.5 5.5l-0.5 -0.5M5.5 18.5l-0.5 0.5"
60
+ );
61
+ }
62
+ .t_ev7s {
63
+ d: path("M0 0h24v24H0z");
64
+ }
65
+ .izfmla {
66
+ stroke: white;
67
+ stroke-linecap: round;
68
+ stroke-linejoin: round;
69
+ stroke-width: var(--svg-stroke-width--2px, 2px);
70
+ d: path(
71
+ "M7 6c0 6.08 4.92 11 11 11c0.53 0 1.05 -0.04 1.56 -0.11c-1.61 2.47 -4.39 4.11 -7.56 4.11c-4.97 0 -9 -4.03 -9 -9c0 -3.17 1.64 -5.95 4.11 -7.56c-0.07 0.51 -0.11 1.03 -0.11 1.56Z"
72
+ );
73
+ }
74
+ .abf34n {
75
+ fill: none;
76
+ stroke: white;
77
+ stroke-dasharray: 4;
78
+ stroke-linecap: round;
79
+ stroke-linejoin: round;
80
+ }
81
+ .k7_l3l {
82
+ d: path("M12 5h1.5M12 5h-1.5M12 5v1.5M12 5v-1.5");
83
+ }
84
+ .hhuniw {
85
+ d: path("M17 11h1.5M17 11h-1.5M17 11v1.5M17 11v-1.5");
86
+ }
87
+
88
+ .z8cgtd {
89
+ d: path("M20 5h1.5M20 5h-1.5M20 5v1.5M20 5v-1.5");
90
+ }
91
+ .drcfta {
92
+ d: path("M12 4h1.5M12 4h-1.5M12 4v1.5M12 4v-1.5");
93
+ }
94
+ .yij7vx {
95
+ d: path("M18 12h1.5M18 12h-1.5M18 12v1.5M18 12v-1.5");
96
+ }
97
+ .emoajy {
98
+ d: path("M19 4h1.5M19 4h-1.5M19 4v1.5M19 4v-1.5");
99
+ }
100
+ .f1to6s {
101
+ d: path("M13 4h1.5M13 4h-1.5M13 4v1.5M13 4v-1.5");
102
+ }
103
+ .niixso {
104
+ d: path("M19 11h1.5M19 11h-1.5M19 11v1.5M19 11v-1.5");
105
+ }
106
+ </style>
package/src/NavMenu.astro CHANGED
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import type { MenuItem, SecondaryMenuItem, TertiaryMenuItem } from "./index.js";
3
+ import ChevronIcon from "./ChevronIcon.astro";
3
4
 
4
5
  export interface Props {
5
6
  /**
@@ -19,6 +20,11 @@ export interface Props {
19
20
  * @default "Home"
20
21
  */
21
22
  homeText?: string;
23
+ /**
24
+ * The destination URL for the home link.
25
+ * @default "/"
26
+ */
27
+ homeUrl?: string;
22
28
  /** Extra CSS class(es) for the `<nav>` element. */
23
29
  header__menu__class?: string;
24
30
  /** Extra CSS class(es) for each top-level `<li>` item. */
@@ -31,6 +37,7 @@ const {
31
37
  menuItems = [],
32
38
  showHomeLink = true,
33
39
  homeText = "Home",
40
+ homeUrl = "/",
34
41
  header__menu__class,
35
42
  header__item__class,
36
43
  menu__link__class,
@@ -144,11 +151,11 @@ const pagePathname = Astro.url.pathname;
144
151
  <nav class:list={["header__menu", header__menu__class]} id="header-menu">
145
152
  <ul class="menu">
146
153
  {
147
- showHomeLink && pagePathname !== "/" && (
154
+ showHomeLink && pagePathname !== homeUrl && (
148
155
  <li class:list={["header__item", header__item__class]}>
149
156
  <a
150
157
  class:list={["menu__link", menu__link__class]}
151
- href="/"
158
+ href={homeUrl}
152
159
  data-astro-prefetch="hover"
153
160
  >
154
161
  {homeText}
@@ -162,12 +169,7 @@ const pagePathname = Astro.url.pathname;
162
169
  <a class:list={["menu__link", menu__link__class]} href={item.link}>
163
170
  {item.text}
164
171
  {item.submenu && (
165
- <iconify-icon
166
- class="iconify-arrow"
167
- icon="codicon:triangle-down"
168
- width="15"
169
- height="15"
170
- />
172
+ <ChevronIcon class="arrow-down" width={15} height={15} />
171
173
  )}
172
174
  </a>
173
175
  {item.submenu && (
@@ -177,11 +179,10 @@ const pagePathname = Astro.url.pathname;
177
179
  <a href={sub.link}>
178
180
  {sub.text}
179
181
  {sub.submenu && sub.submenu.length > 0 && (
180
- <iconify-icon
181
- class="iconify-arrow"
182
- icon="codicon:triangle-down"
183
- width="15"
184
- height="15"
182
+ <ChevronIcon
183
+ class="ChevronIcon-arrow"
184
+ width={15}
185
+ height={15}
185
186
  />
186
187
  )}
187
188
  </a>
@@ -255,11 +256,11 @@ const pagePathname = Astro.url.pathname;
255
256
  }
256
257
  }
257
258
 
258
- .iconify-arrow {
259
+ .ChevronIcon-arrow {
259
260
  transition: transform 0.3s linear;
260
261
  }
261
262
 
262
- .menu__link:hover > .iconify-arrow {
263
+ .menu__link:hover > .ChevronIcon-arrow {
263
264
  transform: rotate(180deg);
264
265
  }
265
266
 
@@ -325,11 +326,12 @@ const pagePathname = Astro.url.pathname;
325
326
  .submenu::after {
326
327
  content: "";
327
328
  position: absolute;
328
- top: -4em;
329
+ top: -1.1em;
329
330
  width: 100%;
330
331
  z-index: -3;
331
332
  pointer-events: none;
332
- height: 10em;
333
+ height: 7em;
334
+ right: 0;
333
335
  }
334
336
 
335
337
  .subsubmenu {
@@ -0,0 +1,34 @@
1
+ ---
2
+ const props = Astro.props;
3
+
4
+ const viewBox = "0 0 24 24";
5
+ const content = `<g class="gutruu"><path fill-opacity="0" class="xkoryc"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.4s" values="34;0"/><animate fill="freeze" attributeName="fill-opacity" begin="0.5s" dur="0.4s" to="1"/></path><g class="ft5dvn"><path d="M12 21v1M21 12h1M12 3v-1M3 12h-1" opacity="0"><animateTransform attributeName="transform" dur="30s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/><set fill="freeze" attributeName="opacity" begin="1s" to="1"/><animate fill="freeze" attributeName="d" begin="1s" dur="0.2s" values="M12 19v1M19 12h1M12 5v-1M5 12h-1;M12 21v1M21 12h1M12 3v-1M3 12h-1"/></path><path d="M18.5 18.5l0.5 0.5M18.5 5.5l0.5 -0.5M5.5 5.5l-0.5 -0.5M5.5 18.5l-0.5 0.5" opacity="0"><animateTransform attributeName="transform" dur="30s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/><set fill="freeze" attributeName="opacity" begin="1.2s" to="1"/><animate fill="freeze" attributeName="d" begin="1.2s" dur="0.2s" values="M17 17l0.5 0.5M17 7l0.5 -0.5M7 7l-0.5 -0.5M7 17l-0.5 0.5;M18.5 18.5l0.5 0.5M18.5 5.5l0.5 -0.5M5.5 5.5l-0.5 -0.5M5.5 18.5l-0.5 0.5"/></path></g></g>`;
6
+ ---
7
+
8
+ <svg
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ width="25px"
11
+ height="25px"
12
+ viewBox={viewBox}
13
+ {...props}
14
+ set:html={content}
15
+ />
16
+
17
+ <style is:inline>
18
+ .gutruu {
19
+ stroke: currentColor;
20
+ stroke-linecap: round;
21
+ stroke-linejoin: round;
22
+ stroke-width: var(--svg-stroke-width--2px, 2px);
23
+ }
24
+ .xkoryc {
25
+ fill: currentColor;
26
+ stroke-dasharray: 34;
27
+ d: path(
28
+ "M12 7c2.76 0 5 2.24 5 5c0 2.76 -2.24 5 -5 5c-2.76 0 -5 -2.24 -5 -5c0 -2.76 2.24 -5 5 -5Z"
29
+ );
30
+ }
31
+ .ft5dvn {
32
+ fill: none;
33
+ }
34
+ </style>
package/src/index.ts CHANGED
@@ -192,18 +192,25 @@ export interface HeaderProps {
192
192
  }
193
193
 
194
194
  export interface NavMenuProps {
195
- menuItems?: MenuItemType[];
195
+ type?: "floating" | "fullscreen";
196
+ menuItems?: MenuItem[];
196
197
  showHomeLink?: boolean;
197
198
  homeText?: string;
199
+ header__menu__class?: string;
200
+ header__item__class?: string;
201
+ menu__link__class?: string;
198
202
  }
199
203
 
200
204
  export interface MobileNavProps {
201
- menuItems?: MenuItemType[];
205
+ type?: "floating" | "fullscreen";
206
+ menuItems?: MenuItem[];
202
207
  showHomeLink?: boolean;
203
208
  homeText?: string;
209
+ mobileNav__class?: string;
204
210
  accentColor?: string;
205
211
  }
206
212
 
207
213
  export interface HamburgerButtonProps {
208
214
  color?: string;
209
215
  }
216
+