@sofidevo/astro-dynamic-header 2.0.2 → 3.0.1

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
@@ -3,7 +3,13 @@
3
3
  A dynamic, responsive header component for Astro projects that can switch between floating and fullscreen styles with multi-level dropdown navigation support.
4
4
 
5
5
  > [!WARNING]
6
- > **Breaking Changes**: Version 2.0+ introduces a restructured configuration object. If you are upgrading from an older version, please review the [Component Props](#component-props) and the [Comprehensive Example](#comprehensive-example) to migrate your configuration.
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.
7
13
 
8
14
  ## Features
9
15
 
@@ -12,7 +18,8 @@ A dynamic, responsive header component for Astro projects that can switch betwee
12
18
  - **Multi-level Dropdowns**: Support for nested navigation menus
13
19
  - **Slot Support**: Customizable slots for desktop header and mobile panel content
14
20
  - **TypeScript Support**: Full type safety and IntelliSense
15
- - **Customizable**: Extensive customization options for colors, sizes, and behavior
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
16
23
  - **Astro Optimized**: Built specifically for Astro framework
17
24
 
18
25
  ### Live demo
@@ -45,11 +52,9 @@ By default, the header uses `preset="auto"`, which automatically detects the the
45
52
  ---
46
53
  import Header from '@sofidevo/astro-dynamic-header/Header';
47
54
 
48
-
49
- const = menuItems: [
50
- { link: '/about', text: 'About' },
51
- ]
52
-
55
+ const menuItems = [
56
+ { link: '/about', text: 'About' },
57
+ ];
53
58
  ---
54
59
 
55
60
  <!-- Detects .dark class on root automatically -->
@@ -92,14 +97,13 @@ const theme = {
92
97
 
93
98
  ### Header Component
94
99
 
95
- | Prop | Type | Default | Description |
96
- | ------------ | ----------------------------- | ------------ | ------------------------------------------ |
97
- | `headerType` | `"floating" \| "fullscreen"` | `"floating"` | Header layout style |
98
- | `preset` | `"light" \| "dark" \| "auto"` | `"auto"` | Theme behavior. `auto` follows root class. |
99
- | `logo` | `LogoConfig` | `{}` | Logo configuration object |
100
- | `navigation` | `NavConfig` | `{}` | Navigation configuration object |
101
- | `theme` | `DualThemeConfig` | `{}` | Custom theme overrides for light/dark |
102
- | `classNames` | `CustomClassNames` | `{}` | Custom class names for CSS Modules |
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 |
103
107
 
104
108
  ### Config Objects
105
109
 
@@ -135,64 +139,61 @@ const theme = {
135
139
  }} />
136
140
  ```
137
141
 
138
- #### CustomClassNames
142
+ #### CustomClassNames → HeaderClassNames
143
+
144
+ > [!NOTE]
145
+ > `CustomClassNames` was renamed to `HeaderClassNames` in v2.1. The old name still works as a type alias — no migration required.
139
146
 
140
- The `classNames` prop allows you to inject custom CSS classes (such as Tailwind CSS utility classes) into specific high-level elements of the Header component. This provides a bridge between the component's internal styles and your project's global styling system.
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.
141
148
 
142
- | Property | Target Element | Purpose & Common Use Cases |
143
- | ----------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
144
- | `container` | Outer `div` wrapping the header | **Positioning & Layout**: Use for `top-0`, `z-50`, `fixed`, or adjusting the `max-width` and `mx-auto` logic. |
145
- | `header` | Inner `<header>` element | **Appearance**: The best place for shadows (`shadow-md`), borders (`border-b`), or custom transition durations. |
146
- | `logo` | `<a>` tag surrounding the logo | **Interactions**: Add hover states, custom focus rings, or adjust the flex alignment of the logo group. |
147
- | `logoText` | `<span>` tag containing the logo text | **Typography**: Override font weights, apply text shadows, or use specific tracking/leading classes. |
148
- | `nav` | `div` wrapping the desktop navigation items | **Desktop Layout**: Adjust spacing between the logo and the menu, or add responsive visibility classes (`hidden md:flex`). |
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. |
149
157
 
150
- ##### Advanced Usage Examples
158
+ ##### Usage Examples
151
159
 
152
- **Implementing a Premium Shadow & Border (Tailwind):**
153
- Ideal for creating a modern "glass" effect with a subtle border and shadow that adapts to dark mode.
160
+ **Premium Shadow & Border:**
154
161
 
155
162
  ```astro
156
- <Header
157
- classNames={{
163
+ <Header
164
+ classNames={{
158
165
  header: "shadow-xl border-b border-black/5 dark:border-white/10 transition-all duration-500",
159
- container: "top-4 px-6"
160
- }}
166
+ container: "top-4 px-6",
167
+ mobileNav: "backdrop-blur-md",
168
+ }}
161
169
  />
162
170
  ```
163
171
 
164
- **Custom Typography for Logo & Nav Spacing:**
165
- Perfect for matching the header with your brand's specific typography and layout requirements.
172
+ **Custom Typography & Nav Spacing:**
166
173
 
167
174
  ```astro
168
- <Header
169
- classNames={{
175
+ <Header
176
+ classNames={{
170
177
  logoText: "tracking-tighter font-black italic uppercase",
171
- nav: "ml-auto gap-8" /* Moves menu to the right and increases gap */
172
- }}
178
+ nav: "ml-auto gap-8",
179
+ }}
173
180
  />
174
181
  ```
175
182
 
176
183
  > [!TIP]
177
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.
178
185
 
179
- #### LogoConfig
180
186
 
181
- | Property | Type | Description |
182
- | ----------- | -------- | ------------------------------------------------ |
183
- | `src` | `string` | URL of the logo image |
184
- | `alt` | `string` | Alternative text for the logo image |
185
- | `width` | `string` | Width of the logo (e.g., "50px", "5rem") |
186
- | `text` | `string` | Text to display next to or instead of logo image |
187
- | `textSize` | `string` | Font size for the logo text |
188
- | `textColor` | `string` | Color for the logo text |
189
187
 
190
188
  #### NavConfig
191
189
 
192
- | Property | Type | Description |
193
- | ----------- | ------------ | --------------------------------------- |
194
- | `homeUrl` | `string` | URL for the home link (defaults to `/`) |
195
- | `menuItems` | `MenuItem[]` | Array of navigation menu items |
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 |
196
197
 
197
198
  #### MenuItem
198
199
 
@@ -210,6 +211,7 @@ The Header component provides a flexible slot system that allows you to add addi
210
211
 
211
212
  | Slot Name | Location | Visibility | Description |
212
213
  | --------- | --------------------- | --------------------- | ---------------------------------------- |
214
+ | `logo` | Header Desktop/Mobile | Always | Add your custom logo HTML/Components |
213
215
  | `actions` | Header & Mobile panel | Responsive visibility | Add action buttons (login, signup, etc.) |
214
216
 
215
217
  ### Example with Slots
@@ -260,32 +262,38 @@ const menuItems = [
260
262
  { link: "/contact", text: "Contact" },
261
263
  ];
262
264
 
263
- const theme = {
264
- light: {
265
- accentColor: "#ff0000",
266
- backgroundColor: "rgba(255, 255, 255, 0.8)",
267
- },
268
- dark: {
269
- accentColor: "#00ffff",
270
- backgroundColor: "rgba(20, 20, 20, 0.9)",
271
- },
272
- };
265
+ ];
273
266
  ---
274
267
 
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>
278
+
275
279
  <Header
276
280
  headerType="floating"
277
281
  preset="dark"
278
- logo={{
279
- src: "https://itssofi.dev/img/icons/sofi-icon.webp",
280
- alt: "My Site Logo",
281
- width: "44px",
282
- }}
283
282
  navigation={{
284
283
  homeUrl: "/",
285
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",
286
291
  }}
287
- theme={theme}
288
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>
289
297
  <button slot="actions">Login</button>
290
298
  </Header>
291
299
  ```
@@ -325,9 +333,11 @@ The package provides full TypeScript support. You can import types to ensure you
325
333
  ```astro
326
334
  ---
327
335
  import Header from '@sofidevo/astro-dynamic-header/Header';
336
+ import { defaultThemes } from '@sofidevo/astro-dynamic-header';
328
337
  import type {
329
338
  NavConfig,
330
339
  DualThemeConfig,
340
+ HeaderClassNames,
331
341
  MenuItem,
332
342
  SecondaryMenuItem
333
343
  } from '@sofidevo/astro-dynamic-header';
@@ -342,42 +352,43 @@ const navigation: NavConfig = {
342
352
  { link: '/hardware', text: 'Hardware' }
343
353
  ]
344
354
  }
345
- ]
355
+ ],
356
+ header__menu__class: "flex gap-6",
346
357
  };
347
358
 
359
+ // Prefer CSS custom properties globally rather than passing the `theme` object.
360
+ // But you can still use the theme prop if needed:
348
361
  const theme: DualThemeConfig = {
349
- light: {
350
- accentColor: "#3e1c71",
351
- backgroundColor: "rgba(255, 255, 255, 0.8)",
352
- backgroundColorOpaque: "#ffffff"
353
- },
354
- dark: {
355
- accentColor: "#00ffff",
356
- backgroundColor: "rgba(10, 10, 10, 0.9)",
357
- backgroundColorOpaque: "#0a0a0a"
358
- }
362
+ light: { ...defaultThemes.light, accentColor: "#3e1c71" },
363
+ dark: { ...defaultThemes.dark, accentColor: "#00ffff", backgroundColor: "rgba(10, 10, 10, 0.9)" },
364
+ };
365
+
366
+ const classNames: HeaderClassNames = {
367
+ header: "shadow-xl",
368
+ mobileNav: "backdrop-blur-md",
359
369
  };
360
370
  ---
361
371
 
362
372
  <Header
363
373
  navigation={navigation}
364
- theme={theme}
374
+ classNames={classNames}
365
375
  preset="auto"
366
376
  />
367
377
  ```
368
378
 
369
379
  ### Available Types
370
380
 
371
- | Type | Description |
372
- | ------------------- | ---------------------------------------------- |
373
- | `MenuItem` | Top-level menu item with optional properties |
374
- | `SecondaryMenuItem` | Second-level menu item |
375
- | `TertiaryMenuItem` | Third-level menu item |
376
- | `NavConfig` | Main navigation configuration object |
377
- | `ThemeConfig` | Individual theme settings (colors, blur, etc.) |
378
- | `DualThemeConfig` | Combined settings for light and dark modes |
379
- | `LogoConfig` | Logo image and text configuration |
380
- | `HeaderProps` | Main props for the Header component |
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 |
381
392
 
382
393
  ## Browser Support
383
394
 
@@ -431,3 +442,64 @@ MIT License - see the [LICENSE](./LICENSE) file for details.
431
442
  ## Support
432
443
 
433
444
  If you find this package helpful, please consider giving it a star on GitHub!
445
+
446
+ ---
447
+
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
458
+
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`.
461
+
462
+ ### v2.1 — Style & Customization Refactor
463
+
464
+ #### New features
465
+
466
+ - **`HeaderClassNames`** replaces `CustomClassNames` (alias kept — no migration required).
467
+ New `mobileNav` slot targets the mobile slide-in `<nav>` panel.
468
+
469
+ - **Nested class props** on `LogoConfig`:
470
+ | Prop | Targets |
471
+ |------|---------|
472
+ | `logo__container__class` | Logo `<a>` wrapper |
473
+ | `logo__text__class` | Logo text `<span>` |
474
+
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>` |
481
+
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
+ ```
487
+
488
+ #### Internal improvements
489
+
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).
492
+
493
+ #### Deprecations
494
+
495
+ - `CustomClassNames` — use `HeaderClassNames` instead. The alias will remain until the next major version.
496
+
497
+ ### v2.0 — Object-based Configuration API
498
+
499
+ > [!WARNING]
500
+ > Breaking change: `logo` changed from `string` to `LogoConfig`, `navigation` changed from `MenuItem[]` to `NavConfig`.
501
+
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`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofidevo/astro-dynamic-header",
3
- "version": "2.0.2",
3
+ "version": "3.0.1",
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",
@@ -53,10 +53,10 @@
53
53
  "astro": "^6.0.6"
54
54
  },
55
55
  "devDependencies": {
56
- "@astrojs/check": "^0.9.8",
56
+ "@astrojs/check": "^0.9.9",
57
57
  "@types/jsdom": "^21.1.7",
58
58
  "@vitest/coverage-v8": "^3.2.4",
59
- "astro": "^6.0.6",
59
+ "astro": "^6.3.7",
60
60
  "jsdom": "^26.1.0",
61
61
  "typescript": "^5.0.0",
62
62
  "vitest": "^3.2.4"
@@ -1,18 +1,11 @@
1
1
  ---
2
- export interface Props {
3
- /** Override the default color of the hamburger lines */
4
- color?: string;
5
- }
6
-
7
- const { color = "var(--text-color, var(--color-hamburger-lines, #fff))" } =
8
- Astro.props;
2
+ export interface Props {}
9
3
  ---
10
4
 
11
5
  <button
12
6
  class="hamburger hamburger--collapse"
13
7
  type="button"
14
8
  id="hamburger-btn"
15
- style={`--hamburger-color: ${color};`}
16
9
  >
17
10
  <span class="hamburger-box">
18
11
  <span class="hamburger-inner"></span>
@@ -65,10 +58,7 @@ const { color = "var(--text-color, var(--color-hamburger-lines, #fff))" } =
65
58
  .hamburger-inner,
66
59
  .hamburger-inner::before,
67
60
  .hamburger-inner::after {
68
- background-color: var(
69
- --hamburger-color,
70
- var(--color-hamburger-lines, #fff)
71
- );
61
+ background-color: var(--text-color, inherit);
72
62
  position: absolute;
73
63
  width: 40px;
74
64
  height: 4px;
package/src/Header.astro CHANGED
@@ -4,21 +4,20 @@ import NavMenu from "./NavMenu.astro";
4
4
  import MobileNav from "./MobileNav.astro";
5
5
 
6
6
  import type {
7
- LogoConfig,
8
7
  NavConfig,
9
8
  DualThemeConfig,
10
- CustomClassNames,
9
+ HeaderClassNames,
11
10
  } from "./index.js";
12
11
 
13
12
  export interface Props {
14
- /**
13
+ /**
15
14
  * Layout style.
16
15
  * - "floating": Centered with max-width and rounded corners.
17
16
  * - "fullscreen": Full width with no border radius.
18
17
  * @default "floating"
19
18
  */
20
19
  headerType?: "floating" | "fullscreen";
21
- /**
20
+ /**
22
21
  * Theme behavior.
23
22
  * - "light": Force light mode.
24
23
  * - "dark": Force dark mode.
@@ -26,35 +25,31 @@ export interface Props {
26
25
  * @default "auto"
27
26
  */
28
27
  preset?: "light" | "dark" | "auto";
29
- /** Logo configuration object (src, alt, text, etc.) */
30
- logo?: LogoConfig;
28
+
31
29
  /** Navigation links and structure. */
32
30
  navigation?: NavConfig;
33
31
  /** Custom theme overrides for light and dark modes. */
34
32
  theme?: DualThemeConfig;
35
- /**
36
- * Custom CSS classes for specific internal elements.
37
- * @example { header: "shadow-lg", nav: "ml-auto" }
33
+ /**
34
+ * High-level CSS class overrides for structural wrapper elements.
35
+ * For fine-grained nav/logo element classes, use the nested `xxx__class`
36
+ * props inside `navigation` or `logo` instead.
37
+ * @example { header: "shadow-lg", container: "top-4" }
38
38
  */
39
- classNames?: CustomClassNames;
39
+ classNames?: HeaderClassNames;
40
40
  }
41
41
 
42
42
  const {
43
43
  headerType = "floating",
44
44
  preset = "auto",
45
- logo = {},
45
+
46
46
  navigation = {},
47
47
  theme = {},
48
48
  classNames = {},
49
49
  } = Astro.props;
50
50
 
51
51
  if (import.meta.env.DEV) {
52
- // Check if logo is being passed as a string (legacy)
53
- if (typeof logo === "string") {
54
- console.warn(
55
- "[@sofidevo/astro-dynamic-header] BREAKING CHANGE: The 'logo' prop now expects an object. Please use logo={{ src: '...' }} instead.",
56
- );
57
- }
52
+
58
53
  if (Array.isArray(navigation)) {
59
54
  console.warn(
60
55
  "[@sofidevo/astro-dynamic-header] BREAKING CHANGE: The 'navigation' prop now expects an object with 'menuItems'. Please use navigation={{ menuItems: [...] }} instead.",
@@ -62,42 +57,36 @@ if (import.meta.env.DEV) {
62
57
  }
63
58
  }
64
59
 
65
- // Default theme configuration
66
- const defaultThemes = {
67
- light: {
68
- backgroundColor: "rgba(255, 255, 255, 0.9)",
69
- backgroundColorOpaque: "rgb(255, 255, 255)",
70
- backdropBlur: "blur(20px)",
71
- zIndex: 10,
72
- textColor: "#1a1a1a",
73
- accentColor: "#3e1c71",
74
- },
75
- dark: {
76
- backgroundColor: "#0d0d0dcc",
77
- backgroundColorOpaque: "#0d0d0d",
78
- backdropBlur: "blur(20px)",
79
- zIndex: 10,
80
- textColor: "#ffffff",
81
- accentColor: "#00ffff",
82
- },
83
- };
84
-
85
- // Merge with user overrides
86
- const lightTheme = { ...defaultThemes.light, ...theme.light };
87
- const darkTheme = { ...defaultThemes.dark, ...theme.dark };
60
+ // Map user theme overrides to CSS custom properties
61
+ const inlineStyles: Record<string, string> = {};
88
62
 
89
- // Navigation configuration
90
- const { homeUrl = "/", menuItems = [] } = navigation;
63
+ if (theme.light) {
64
+ if (theme.light.backgroundColor) inlineStyles["--l-bg"] = theme.light.backgroundColor;
65
+ if (theme.light.backgroundColorOpaque) inlineStyles["--l-bg-opaque"] = theme.light.backgroundColorOpaque;
66
+ if (theme.light.textColor) inlineStyles["--l-text"] = theme.light.textColor;
67
+ if (theme.light.accentColor) inlineStyles["--l-accent"] = theme.light.accentColor;
68
+ if (theme.light.backdropBlur) inlineStyles["--l-blur"] = theme.light.backdropBlur;
69
+ }
70
+ if (theme.dark) {
71
+ if (theme.dark.backgroundColor) inlineStyles["--d-bg"] = theme.dark.backgroundColor;
72
+ if (theme.dark.backgroundColorOpaque) inlineStyles["--d-bg-opaque"] = theme.dark.backgroundColorOpaque;
73
+ if (theme.dark.textColor) inlineStyles["--d-text"] = theme.dark.textColor;
74
+ if (theme.dark.accentColor) inlineStyles["--d-accent"] = theme.dark.accentColor;
75
+ if (theme.dark.backdropBlur) inlineStyles["--d-blur"] = theme.dark.backdropBlur;
76
+ }
91
77
 
92
- // Logo configuration
78
+ const zIndex = theme.light?.zIndex ?? 10;
79
+
80
+ // Navigation configuration
93
81
  const {
94
- src: logoSrc = "/logo.png",
95
- alt: logoAlt = "Logo",
96
- width: logoWidth = "55px",
97
- text: logoText = "",
98
- textSize: logoTextSize = "1em",
99
- textColor: logoTextColor = "inherit", // Will inherit from theme text-color
100
- } = logo;
82
+ homeUrl = "/",
83
+ menuItems = [],
84
+ header__menu__class,
85
+ header__item__class,
86
+ menu__link__class,
87
+ } = navigation;
88
+
89
+
101
90
 
102
91
  const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
103
92
  ---
@@ -150,7 +139,7 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
150
139
  `header__container--${headerType}`,
151
140
  classNames.container,
152
141
  ]}
153
- style={{ zIndex: lightTheme.zIndex }}
142
+ style={{ zIndex }}
154
143
  >
155
144
  <header
156
145
  class:list={[
@@ -159,40 +148,17 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
159
148
  forcedClass,
160
149
  classNames.header,
161
150
  ]}
162
- style={{
163
- "--l-bg": lightTheme.backgroundColor,
164
- "--l-bg-opaque": lightTheme.backgroundColorOpaque,
165
- "--l-text": lightTheme.textColor,
166
- "--l-accent": lightTheme.accentColor,
167
- "--l-blur": lightTheme.backdropBlur,
168
- "--d-bg": darkTheme.backgroundColor,
169
- "--d-bg-opaque": darkTheme.backgroundColorOpaque,
170
- "--d-text": darkTheme.textColor,
171
- "--d-accent": darkTheme.accentColor,
172
- "--d-blur": darkTheme.backdropBlur,
173
- }}
151
+ style={Object.keys(inlineStyles).length > 0 ? inlineStyles : undefined}
174
152
  >
175
- <a class:list={["logo__container", classNames.logo]} href={homeUrl}>
176
- <img
177
- class="header__logo"
178
- src={logoSrc}
179
- alt={logoAlt}
180
- style={{ width: logoWidth }}
181
- />
182
- {
183
- logoText && (
184
- <span
185
- class:list={["header__logo-text", classNames.logoText]}
186
- style={{ color: logoTextColor, fontSize: logoTextSize }}
187
- >
188
- {logoText}
189
- </span>
190
- )
191
- }
192
- </a>
153
+ <slot name="logo" />
193
154
 
194
155
  <div class:list={["nav-menu-wrapper", classNames.nav]}>
195
- <NavMenu menuItems={menuItems} />
156
+ <NavMenu
157
+ menuItems={menuItems}
158
+ header__menu__class={header__menu__class}
159
+ header__item__class={header__item__class}
160
+ menu__link__class={menu__link__class}
161
+ />
196
162
  </div>
197
163
 
198
164
  {
@@ -205,7 +171,11 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
205
171
 
206
172
  <HamburgerButton />
207
173
 
208
- <MobileNav menuItems={menuItems} type={headerType}>
174
+ <MobileNav
175
+ menuItems={menuItems}
176
+ type={headerType}
177
+ mobileNav__class={classNames.mobileNav}
178
+ >
209
179
  {
210
180
  Astro.slots.has("actions") && (
211
181
  <div class="actions-mobile" slot="slot-panel">
@@ -219,11 +189,11 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
219
189
 
220
190
  <style is:inline>
221
191
  .header {
222
- --bg-color: var(--l-bg);
223
- --bg-color-opaque: var(--l-bg-opaque);
224
- --text-color: var(--l-text);
225
- --accent-color: var(--l-accent);
226
- --backdrop-blur: var(--l-blur);
192
+ --bg-color: var(--l-bg, rgba(255, 255, 255, 0.9));
193
+ --bg-color-opaque: var(--l-bg-opaque, rgb(255, 255, 255));
194
+ --text-color: var(--l-text, #1a1a1a);
195
+ --accent-color: var(--l-accent, #3e1c71);
196
+ --backdrop-blur: var(--l-blur, blur(20px));
227
197
 
228
198
  display: flex;
229
199
  align-items: center;
@@ -247,28 +217,28 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
247
217
 
248
218
  /* Automatic dark mode detection via root class */
249
219
  :root.dark .header:not(.header--force-light) {
250
- --bg-color: var(--d-bg);
251
- --bg-color-opaque: var(--d-bg-opaque);
252
- --text-color: var(--d-text);
253
- --accent-color: var(--d-accent);
254
- --backdrop-blur: var(--d-blur);
220
+ --bg-color: var(--d-bg, #0d0d0dcc);
221
+ --bg-color-opaque: var(--d-bg-opaque, #0d0d0d);
222
+ --text-color: var(--d-text, #ffffff);
223
+ --accent-color: var(--d-accent, #00ffff);
224
+ --backdrop-blur: var(--d-blur, blur(20px));
255
225
  }
256
226
 
257
227
  /* Forced themes */
258
228
  .header--force-dark {
259
- --bg-color: var(--d-bg) !important;
260
- --bg-color-opaque: var(--d-bg-opaque) !important;
261
- --text-color: var(--d-text) !important;
262
- --accent-color: var(--d-accent) !important;
263
- --backdrop-blur: var(--d-blur) !important;
229
+ --bg-color: var(--d-bg, #0d0d0dcc) !important;
230
+ --bg-color-opaque: var(--d-bg-opaque, #0d0d0d) !important;
231
+ --text-color: var(--d-text, #ffffff) !important;
232
+ --accent-color: var(--d-accent, #00ffff) !important;
233
+ --backdrop-blur: var(--d-blur, blur(20px)) !important;
264
234
  }
265
235
 
266
236
  .header--force-light {
267
- --bg-color: var(--l-bg) !important;
268
- --bg-color-opaque: var(--l-bg-opaque) !important;
269
- --text-color: var(--l-text) !important;
270
- --accent-color: var(--l-accent) !important;
271
- --backdrop-blur: var(--l-blur) !important;
237
+ --bg-color: var(--l-bg, rgba(255, 255, 255, 0.9)) !important;
238
+ --bg-color-opaque: var(--l-bg-opaque, rgb(255, 255, 255)) !important;
239
+ --text-color: var(--l-text, #1a1a1a) !important;
240
+ --accent-color: var(--l-accent, #3e1c71) !important;
241
+ --backdrop-blur: var(--l-blur, blur(20px)) !important;
272
242
  }
273
243
 
274
244
  .header__container {
@@ -313,20 +283,7 @@ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
313
283
  }
314
284
  }
315
285
 
316
- .logo__container {
317
- display: flex;
318
- align-items: center;
319
- text-decoration: none;
320
- color: inherit;
321
- & span {
322
- font-weight: 600;
323
- }
324
- }
325
286
 
326
- .header__logo {
327
- margin-right: 1em;
328
- object-fit: contain;
329
- }
330
287
 
331
288
  .actions-desktop {
332
289
  @media (width < 768px) {
@@ -10,21 +10,26 @@ export interface Props {
10
10
  showHomeLink?: boolean;
11
11
  /** Text for the home link (defaults to "Home") */
12
12
  homeText?: string;
13
+ /** Extra CSS class(es) for the root mobile `<nav>` panel. */
14
+ mobileNav__class?: string;
13
15
  }
14
16
 
15
- const { type } = Astro.props;
16
-
17
17
  const {
18
+ type,
19
+ mobileNav__class,
18
20
  menuItems = [],
19
21
  showHomeLink = true,
20
- homeText = "Home",
22
+ homeText = "Home"
21
23
  } = Astro.props;
22
24
 
23
25
  const pagePathname = Astro.url.pathname;
24
26
  ---
25
27
 
26
28
  <nav
27
- class={`mobile-header__menu mobile-header__menu--${type}`}
29
+ class:list={[
30
+ `mobile-header__menu mobile-header__menu--${type}`,
31
+ mobileNav__class,
32
+ ]}
28
33
  id="mobile-header-menu"
29
34
  >
30
35
  <ol class="mobile-menu">
@@ -47,7 +52,10 @@ const pagePathname = Astro.url.pathname;
47
52
  return (
48
53
  <li>
49
54
  {item.submenu ? (
50
- <details class="mobile-details" open={item.submenu.some(sub => pagePathname === sub.link)}>
55
+ <details
56
+ class="mobile-details"
57
+ open={item.submenu.some((sub) => pagePathname === sub.link)}
58
+ >
51
59
  <summary class:list={["menu__summary", { active: isActive }]}>
52
60
  {item.text}
53
61
  <iconify-icon
@@ -64,8 +72,18 @@ const pagePathname = Astro.url.pathname;
64
72
  return (
65
73
  <li>
66
74
  {sub.submenu && sub.submenu.length > 0 ? (
67
- <details class="mobile-details" open={sub.submenu.some(subsub => pagePathname === subsub.link)}>
68
- <summary class:list={["menu__summary", { active: isSubActive }]}>
75
+ <details
76
+ class="mobile-details"
77
+ open={sub.submenu.some(
78
+ (subsub) => pagePathname === subsub.link,
79
+ )}
80
+ >
81
+ <summary
82
+ class:list={[
83
+ "menu__summary",
84
+ { active: isSubActive },
85
+ ]}
86
+ >
69
87
  {sub.text}
70
88
  <iconify-icon
71
89
  class="iconify-arrow"
@@ -77,11 +95,16 @@ const pagePathname = Astro.url.pathname;
77
95
  </summary>
78
96
  <ol class="mobile-subsubmenu">
79
97
  {sub.submenu.map((subsub: TertiaryMenuItem) => {
80
- const isSubSubActive = pagePathname === subsub.link;
98
+ const isSubSubActive =
99
+ pagePathname === subsub.link;
81
100
  return (
82
101
  <li class="mobile-submenu__item secondary">
83
102
  <a
84
- class:list={["menu__link", "mobile-menu__link", { active: isSubSubActive }]}
103
+ class:list={[
104
+ "menu__link",
105
+ "mobile-menu__link",
106
+ { active: isSubSubActive },
107
+ ]}
85
108
  href={subsub.link}
86
109
  >
87
110
  {subsub.text}
@@ -92,7 +115,14 @@ const pagePathname = Astro.url.pathname;
92
115
  </ol>
93
116
  </details>
94
117
  ) : (
95
- <a class:list={["menu__link", "mobile-menu__link", { active: isSubActive }]} href={sub.link}>
118
+ <a
119
+ class:list={[
120
+ "menu__link",
121
+ "mobile-menu__link",
122
+ { active: isSubActive },
123
+ ]}
124
+ href={sub.link}
125
+ >
96
126
  {sub.text}
97
127
  </a>
98
128
  )}
@@ -102,7 +132,10 @@ const pagePathname = Astro.url.pathname;
102
132
  </ol>
103
133
  </details>
104
134
  ) : (
105
- <a class:list={["mobile-menu__link", { active: isActive }]} href={item.link}>
135
+ <a
136
+ class:list={["mobile-menu__link", { active: isActive }]}
137
+ href={item.link}
138
+ >
106
139
  {item.text}
107
140
  </a>
108
141
  )}
@@ -128,7 +161,9 @@ const pagePathname = Astro.url.pathname;
128
161
  left: 0;
129
162
  background: var(--bg-color-opaque, #151515);
130
163
  z-index: 20;
131
- transition: transform 0.3s ease-in-out, background-color 0.3s ease;
164
+ transition:
165
+ transform 0.3s ease-in-out,
166
+ background-color 0.3s ease;
132
167
  transform: translateX(100%);
133
168
  padding: 10rem 2rem 2rem;
134
169
  color: var(--text-color, #fff);
package/src/NavMenu.astro CHANGED
@@ -18,11 +18,23 @@ export interface Props {
18
18
  * Text for the home link.
19
19
  * @default "Home"
20
20
  */
21
-
22
21
  homeText?: string;
22
+ /** Extra CSS class(es) for the `<nav>` element. */
23
+ header__menu__class?: string;
24
+ /** Extra CSS class(es) for each top-level `<li>` item. */
25
+ header__item__class?: string;
26
+ /** Extra CSS class(es) for each top-level menu `<a>` link. */
27
+ menu__link__class?: string;
23
28
  }
24
29
 
25
- const { menuItems = [], showHomeLink = true, homeText = "Home" } = Astro.props;
30
+ const {
31
+ menuItems = [],
32
+ showHomeLink = true,
33
+ homeText = "Home",
34
+ header__menu__class,
35
+ header__item__class,
36
+ menu__link__class,
37
+ } = Astro.props;
26
38
 
27
39
  const pagePathname = Astro.url.pathname;
28
40
  ---
@@ -129,12 +141,16 @@ const pagePathname = Astro.url.pathname;
129
141
  });
130
142
  </script>
131
143
 
132
- <nav class="header__menu" id="header-menu">
144
+ <nav class:list={["header__menu", header__menu__class]} id="header-menu">
133
145
  <ul class="menu">
134
146
  {
135
147
  showHomeLink && pagePathname !== "/" && (
136
- <li class="header__item">
137
- <a class="menu__link" href="/" data-astro-prefetch="hover">
148
+ <li class:list={["header__item", header__item__class]}>
149
+ <a
150
+ class:list={["menu__link", menu__link__class]}
151
+ href="/"
152
+ data-astro-prefetch="hover"
153
+ >
138
154
  {homeText}
139
155
  </a>
140
156
  </li>
@@ -142,8 +158,8 @@ const pagePathname = Astro.url.pathname;
142
158
  }
143
159
  {
144
160
  menuItems.map((item: MenuItem) => (
145
- <li class="menu__item">
146
- <a class="menu__link" href={item.link}>
161
+ <li class:list={["menu__item", header__item__class]}>
162
+ <a class:list={["menu__link", menu__link__class]} href={item.link}>
147
163
  {item.text}
148
164
  {item.submenu && (
149
165
  <iconify-icon
@@ -0,0 +1,36 @@
1
+ import type { DualThemeConfig } from "./index.js";
2
+
3
+ /**
4
+ * Built-in default theme tokens used by the Header component.
5
+ * These are merged with any user-supplied `theme` overrides.
6
+ *
7
+ * You can import this object if you want to build on top of the defaults
8
+ * rather than replacing them wholesale:
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { defaultThemes } from '@sofidevo/astro-dynamic-header/defaults';
13
+ * const theme = {
14
+ * light: { ...defaultThemes.light, accentColor: "#e11d48" },
15
+ * dark: { ...defaultThemes.dark, accentColor: "#f43f5e" },
16
+ * };
17
+ * ```
18
+ */
19
+ export const defaultThemes: Required<DualThemeConfig> = {
20
+ light: {
21
+ backgroundColor: "rgba(255, 255, 255, 0.9)",
22
+ backgroundColorOpaque: "rgb(255, 255, 255)",
23
+ backdropBlur: "blur(20px)",
24
+ zIndex: 10,
25
+ textColor: "#1a1a1a",
26
+ accentColor: "#3e1c71",
27
+ },
28
+ dark: {
29
+ backgroundColor: "#0d0d0dcc",
30
+ backgroundColorOpaque: "#0d0d0d",
31
+ backdropBlur: "blur(20px)",
32
+ zIndex: 10,
33
+ textColor: "#ffffff",
34
+ accentColor: "#00ffff",
35
+ },
36
+ };
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
-
1
+ export { defaultThemes } from "./defaults.js";
2
2
 
3
3
  /**
4
4
  * Represents a menu item in the navigation.
@@ -46,87 +46,70 @@ export interface MenuItem {
46
46
  submenu?: SecondaryMenuItem[];
47
47
  }
48
48
 
49
- /**
50
- * Configuration for the site logo.
51
- */
52
- export interface LogoConfig {
53
- /**
54
- * The URL of the logo image.
55
- * @example "/assets/logo.png"
56
- * @default "/logo.png"
57
- */
58
- src?: string;
59
- /** Alternative text for the logo image */
60
- alt?: string;
61
- /**
62
- * CSS width for the logo.
63
- * @example "150px" or "4rem"
64
- * @default "55px"
65
- */
66
- width?: string;
67
- /**
68
- * Optional text to display next to the logo image.
69
- * @example "My Awesome Site"
70
- */
71
- text?: string;
72
- /**
73
- * CSS font-size for the logo text.
74
- * @default "1em"
75
- */
76
- textSize?: string;
77
- /**
78
- * CSS color for the logo text.
79
- * If not provided, it will inherit from the theme's text color.
80
- * @default "inherit"
81
- */
82
- textColor?: string;
83
- }
84
-
85
49
  /**
86
50
  * Configuration for the main navigation.
87
51
  */
88
52
  export interface NavConfig {
89
- /**
53
+ /**
90
54
  * The URL for the home link.
91
55
  * @default "/"
92
56
  */
93
57
  homeUrl?: string;
94
- /**
58
+ /**
95
59
  * Array of top-level menu items.
96
60
  * @example [{ link: "/about", text: "About Us" }]
97
61
  */
98
62
  menuItems?: MenuItem[];
63
+ /**
64
+ * Fine-grained class override for the desktop `<nav>` element.
65
+ * Use this when you want the class to live alongside the rest of the
66
+ * navigation configuration rather than in the top-level `classNames` prop.
67
+ * @example "flex gap-4"
68
+ */
69
+ header__menu__class?: string;
70
+ /**
71
+ * Fine-grained class override applied to every top-level `<li>` item
72
+ * in the desktop navigation.
73
+ * @example "px-2 py-1"
74
+ */
75
+ header__item__class?: string;
76
+ /**
77
+ * Fine-grained class override applied to every top-level `<a>` link
78
+ * in the desktop navigation.
79
+ * @example "hover:underline font-medium"
80
+ */
81
+ menu__link__class?: string;
99
82
  }
100
83
 
101
84
  /**
102
85
  * Individual theme settings for a specific state (light/dark).
103
86
  */
104
87
  export interface ThemeConfig {
105
- /**
88
+ /**
106
89
  * Main background color. Supports hex, rgb, rgba, etc.
107
90
  * @example "rgba(255, 255, 255, 0.9)"
108
91
  */
109
92
  backgroundColor?: string;
110
- /**
93
+ /**
111
94
  * Solid background color for submenus and mobile panels to ensure readability.
112
95
  * @example "#ffffff"
113
96
  */
114
97
  backgroundColorOpaque?: string;
115
- /**
98
+ /**
116
99
  * CSS backdrop-filter blur value.
117
100
  * @default "blur(20px)"
118
101
  */
119
102
  backdropBlur?: string;
120
- /**
103
+ /**
121
104
  * CSS z-index for the header container.
122
105
  * @default 10
123
106
  */
124
107
  zIndex?: number;
125
- /**
108
+ /**
126
109
  * Primary text color for navigation and logo.
127
110
  */
128
111
  textColor?: string;
129
- /**
112
+ /**
130
113
  * Color for highlights, active states, underscores, and small borders.
131
114
  */
132
115
  accentColor?: string;
@@ -143,33 +126,50 @@ export interface DualThemeConfig {
143
126
  }
144
127
 
145
128
  /**
146
- * Custom CSS class names for deep customization.
129
+ * Custom CSS class names for high-level layout & appearance customization.
130
+ *
131
+ * These target the structural wrapper elements of the Header. For fine-grained
132
+ * control over individual nav links or the logo internals, use the nested
133
+ * `xxx__class` props inside the `navigation` or `logo` config objects instead.
134
+ *
135
+ * @example
136
+ * ```astro
137
+ * <Header classNames={{ header: "shadow-xl", container: "top-4 px-6" }} />
138
+ * ```
147
139
  */
148
- export interface CustomClassNames {
149
- /** Class for the outermost fixed container */
140
+ export interface HeaderClassNames {
141
+ /** Outermost fixed `<div>` that positions the header on the page. */
150
142
  container?: string;
151
- /** Class for the main header element */
143
+ /** Inner `<header>` element — best place for shadows, borders, transitions. */
152
144
  header?: string;
153
- /** Class for the logo anchor tag */
145
+ /** Logo anchor `<a>` — add hover states or focus rings here. */
154
146
  logo?: string;
155
- /** Class for the logo span text */
147
+ /** Logo text `<span>` — override typography here. */
156
148
  logoText?: string;
157
- /** Class for the desktop navigation wrapper */
149
+ /** Desktop nav wrapper `<div>` — adjust spacing between logo and menu. */
158
150
  nav?: string;
151
+ /** Mobile nav panel `<nav>` — add slide-in overrides or z-index tweaks. */
152
+ mobileNav?: string;
159
153
  }
160
154
 
155
+ /**
156
+ * @deprecated Use {@link HeaderClassNames} instead.
157
+ * Kept as an alias for backwards compatibility.
158
+ */
159
+ export type CustomClassNames = HeaderClassNames;
160
+
161
161
  /**
162
162
  * Main properties for the Header component.
163
163
  */
164
164
  export interface HeaderProps {
165
- /**
165
+ /**
166
166
  * Layout style.
167
167
  * - "floating": Centered with max-width and rounded corners.
168
168
  * - "fullscreen": Full width with no border radius.
169
169
  * @default "floating"
170
170
  */
171
171
  headerType?: "floating" | "fullscreen";
172
- /**
172
+ /**
173
173
  * Theme behavior.
174
174
  * - "light": Force light mode.
175
175
  * - "dark": Force dark mode.
@@ -177,17 +177,18 @@ export interface HeaderProps {
177
177
  * @default "auto"
178
178
  */
179
179
  preset?: "light" | "dark" | "auto";
180
- /** Logo configuration object. */
181
- logo?: LogoConfig;
180
+
182
181
  /** Navigation links and structure. */
183
182
  navigation?: NavConfig;
184
- /** Custom theme overrides. See @interface DualThemeConfig */
183
+ /** Custom theme overrides. See {@link DualThemeConfig} */
185
184
  theme?: DualThemeConfig;
186
- /**
187
- * Custom CSS classes for injecting utility classes (e.g., Tailwind).
188
- * @example { header: "shadow-lg", logoText: "font-bold" }
185
+ /**
186
+ * High-level CSS class overrides for structural wrapper elements.
187
+ * For fine-grained nav/logo element classes, use the nested `xxx__class`
188
+ * props inside `navigation` or `logo` instead.
189
+ * @example { header: "shadow-lg", container: "top-4" }
189
190
  */
190
- classNames?: CustomClassNames;
191
+ classNames?: HeaderClassNames;
191
192
  }
192
193
 
193
194
  export interface NavMenuProps {