@sofidevo/astro-dynamic-header 1.1.34 → 2.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
@@ -2,6 +2,9 @@
2
2
 
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
+ > [!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.
7
+
5
8
  ## Features
6
9
 
7
10
  - **Dynamic Styles**: Switch between floating and fullscreen header layouts
@@ -12,9 +15,9 @@ A dynamic, responsive header component for Astro projects that can switch betwee
12
15
  - **Customizable**: Extensive customization options for colors, sizes, and behavior
13
16
  - **Astro Optimized**: Built specifically for Astro framework
14
17
 
15
-
16
18
  ### Live demo
17
- [https://base-astro-psi.vercel.app/fullscreen-demo](https://base-astro-psi.vercel.app/fullscreen-demo)
19
+
20
+ [https://base-astro-psi.vercel.app/fullscreen-demo](https://base-astro-psi.vercel.app/fullscreen-demo)
18
21
 
19
22
  ## Installation
20
23
 
@@ -34,86 +37,54 @@ Add this to your main layout or in the `<head>` section of your Astro pages.
34
37
 
35
38
  ## Quick Start
36
39
 
37
- ### Basic Usage
40
+ ### Basic Usage (Automatic Theme)
41
+
42
+ By default, the header uses `preset="auto"`, which automatically detects the theme based on a `.dark` class on your root element (`html` or `body`).
38
43
 
39
44
  ```astro
40
45
  ---
41
- // Option 1: Import from direct subpath (recommended)
42
46
  import Header from '@sofidevo/astro-dynamic-header/Header';
43
47
 
44
- // Option 2: Import from main entry point with types
45
- import { HeaderProps, type MenuItemType } from '@sofidevo/astro-dynamic-header';
46
48
 
47
- const menuItems = [
48
- { link: '/about', text: 'About' },
49
- { link: '/contact', text: 'Contact' },
50
- ];
49
+ const = menuItems: [
50
+ { link: '/about', text: 'About' },
51
+ ]
52
+
51
53
  ---
52
54
 
53
- <Header
54
- headerType="floating"
55
- logoSrc="/logo.png"
56
- menuItems={menuItems}
57
- />
55
+ <!-- Detects .dark class on root automatically -->
56
+ <Header navigation={{ menuItems }} />
58
57
  ```
59
58
 
60
- ### TypeScript Configuration
61
-
62
- To ensure imports work correctly in your Astro project, make sure your `tsconfig.json` has the appropriate configuration:
59
+ ### Advanced Usage (Dual-Theme Customization)
63
60
 
64
- ```json
65
- {
66
- "compilerOptions": {
67
- "moduleResolution": "bundler",
68
- "allowImportingTsExtensions": true,
69
- "strict": true,
70
- "noEmit": true,
71
- "jsx": "preserve"
72
- },
73
- "extends": "astro/tsconfigs/strict"
74
- }
75
- ```
76
-
77
- ### Advanced Usage
61
+ You can provide custom colors for both light and dark modes simultaneously.
78
62
 
79
63
  ```astro
64
+
80
65
  ---
81
66
  import Header from '@sofidevo/astro-dynamic-header/Header';
82
- import type { MenuItemType } from '@sofidevo/astro-dynamic-header';
83
-
84
- const menuItems = [
85
- {
86
- link: '#',
87
- text: 'Services',
88
- submenu: [
89
- {
90
- link: '#',
91
- text: 'Web Development',
92
- submenu: [
93
- { link: '/web/frontend', text: 'Frontend' },
94
- { link: '/web/backend', text: 'Backend' },
95
- { link: '/web/fullstack', text: 'Full Stack' },
96
- ],
97
- },
98
- { link: '/design', text: 'Design' },
99
- { link: '/consulting', text: 'Consulting' },
100
- ],
67
+ const navigation = {
68
+ menuItems: [
69
+ { link: '/about', text: 'About' },
70
+ ]
71
+ };
72
+ const theme = {
73
+ light: {
74
+ accentColor: "#3e1c71",
75
+ backgroundColor: "rgba(255, 255, 255, 0.8)"
101
76
  },
102
- { link: '/about', text: 'About' },
103
- { link: '/contact', text: 'Contact' },
104
- ];
77
+ dark: {
78
+ accentColor: "#00ffff",
79
+ backgroundColor: "rgba(20, 20, 20, 0.9)"
80
+ }
81
+ };
105
82
  ---
106
83
 
107
84
  <Header
108
- headerType="fullscreen"
109
- logoSrc="/logo.png"
110
- logoAlt="My Company"
111
- logoWidth="150px"
112
- homeUrl="/"
113
- menuItems={menuItems}
114
- backgroundColor="#000000dd"
115
- backdropBlur="blur(15px)"
116
- zIndex={100}
85
+ preset="auto"
86
+ theme={theme}
87
+ navigation={navigation}
117
88
  />
118
89
  ```
119
90
 
@@ -121,165 +92,189 @@ const menuItems = [
121
92
 
122
93
  ### Header Component
123
94
 
124
- | Prop | Type | Default | Description |
125
- |------|------|---------|-------------|
126
- | `headerType` | `"floating" \| "fullscreen"` | `"floating"` | Header layout style |
127
- | `logoSrc` | `string` | `"/logo.png"` | Logo image source |
128
- | `logoAlt` | `string` | `"Logo"` | Logo alt text |
129
- | `logoWidth` | `string` | `"55px"` | Logo width |
130
- | `logoText` | `string` | `"Your logo text"` | Text displayed next to the logo |
131
- | `logoTextSize` | `string` | `"1em"` | Font size for logo text |
132
- | `logoTextColor` | `string` | `"#ffffff"` | Color for logo text |
133
- | `homeUrl` | `string` | `"/"` | Home page URL |
134
- | `menuItems` | `MenuItemType[]` | `[]` | Navigation menu items |
135
- | `backgroundColor` | `string` | `"#0d0d0dcc"` | Header background color |
136
- | `backdropBlur` | `string` | `"blur(20px)"` | Backdrop filter blur |
137
- | `zIndex` | `number` | `10` | CSS z-index value |
138
-
139
- ### Menu Item Structure
140
-
141
- ```typescript
142
- interface MenuItemType {
143
- link: string;
144
- text: string;
145
- submenu?: MenuItemType[];
146
- }
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 |
103
+
104
+ ### Config Objects
105
+
106
+ #### DualThemeConfig
107
+
108
+ | Propery | Type | Description |
109
+ | ------- | ------------- | ---------------------------- |
110
+ | `light` | `ThemeConfig` | Styles applied in light mode |
111
+ | `dark` | `ThemeConfig` | Styles applied in dark mode |
112
+
113
+ #### ThemeConfig
114
+
115
+ | Propery | Type | Default |
116
+ | ----------------------- | -------- | ---------------- |
117
+ | `backgroundColor` | `string` | _Preset default_ |
118
+ | `backgroundColorOpaque` | `string` | _Preset default_ |
119
+ | `backdropBlur` | `string` | `"blur(20px)"` |
120
+ | `zIndex` | `number` | `10` |
121
+ | `textColor` | `string` | _Preset default_ |
122
+ | `accentColor` | `string` | _Preset default_ |
123
+
124
+ > [!IMPORTANT]
125
+ > **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**.
126
+ >
127
+ > If you use a transparent `backgroundColor` (e.g., `rgba`), remember to also provide its solid counterpart in `backgroundColorOpaque`.
128
+
129
+ ```astro
130
+ <Header theme={{
131
+ light: {
132
+ backgroundColor: "rgba(255, 255, 255, 0.7)", // Transparent header body
133
+ backgroundColorOpaque: "#ffffff", // Solid submenus
134
+ }
135
+ }} />
147
136
  ```
148
137
 
149
- ## Slots Support
138
+ #### CustomClassNames
150
139
 
151
- The Header component provides a flexible slot system that allows you to add additional content:
140
+ | Propery | Type | Description |
141
+ | ----------- | -------- | -------------------------------- |
142
+ | `container` | `string` | Main container class |
143
+ | `header` | `string` | Header element class |
144
+ | `logo` | `string` | Logo link container class |
145
+ | `logoText` | `string` | Logo text class |
146
+ | `nav` | `string` | Desktop navigation wrapper class |
152
147
 
153
- ### Available Slots
148
+ #### LogoConfig
149
+
150
+ | Property | Type | Description |
151
+ | ----------- | -------- | ------------------------------------------------ |
152
+ | `src` | `string` | URL of the logo image |
153
+ | `alt` | `string` | Alternative text for the logo image |
154
+ | `width` | `string` | Width of the logo (e.g., "50px", "5rem") |
155
+ | `text` | `string` | Text to display next to or instead of logo image |
156
+ | `textSize` | `string` | Font size for the logo text |
157
+ | `textColor` | `string` | Color for the logo text |
154
158
 
155
- | Slot Name | Location | Visibility | Description |
156
- |-----------|----------|------------|-----------|
157
- | `actions` | Header & Mobile panel | Responsive visibility | Add action buttons (login, signup, etc.) that appear in both desktop and mobile |
159
+ #### NavConfig
158
160
 
159
- ### Actions Slot Behavior
161
+ | Property | Type | Description |
162
+ | ----------- | ------------ | --------------------------------------- |
163
+ | `homeUrl` | `string` | URL for the home link (defaults to `/`) |
164
+ | `menuItems` | `MenuItem[]` | Array of navigation menu items |
160
165
 
161
- The `actions` slot is intelligent and automatically manages visibility:
162
- - **Desktop (≥768px)**: Shows actions in the main header after navigation
163
- - **Mobile (<768px)**: Shows same actions inside the mobile menu panel
164
- - **Automatic hiding**: Desktop version is hidden on mobile, mobile version is hidden on desktop
166
+ #### MenuItem
167
+
168
+ | Property | Type | Description |
169
+ | --------- | --------------------- | ----------------------------------- |
170
+ | `link` | `string` | URL the menu item points to |
171
+ | `text` | `string` | Label text for the menu item |
172
+ | `submenu` | `SecondaryMenuItem[]` | Optional array of nested menu items |
173
+
174
+ ## Slots Support
175
+
176
+ The Header component provides a flexible slot system that allows you to add additional content:
177
+
178
+ ### Available Slots
165
179
 
166
- ### Slot Examples
180
+ | Slot Name | Location | Visibility | Description |
181
+ | --------- | --------------------- | --------------------- | ---------------------------------------- |
182
+ | `actions` | Header & Mobile panel | Responsive visibility | Add action buttons (login, signup, etc.) |
167
183
 
168
- #### Using the Actions Slot
184
+ ### Example with Slots
169
185
 
170
186
  ```astro
171
187
  ---
172
188
  import Header from '@sofidevo/astro-dynamic-header/Header';
173
189
 
174
- const menuItems = [
175
- { link: '/about', text: 'About' },
176
- { link: '/contact', text: 'Contact' },
177
- ];
190
+ const navigation = {
191
+ menuItems: [{ link: '/about', text: 'About' }]
192
+ };
178
193
  ---
179
194
 
180
- <Header
181
- headerType="floating"
182
- logoSrc="/logo.png"
183
- logoText="My Company"
184
- menuItems={menuItems}
185
- >
195
+ <Header navigation={navigation}>
186
196
  <div slot="actions">
187
197
  <button class="login-btn">Login</button>
188
- <button class="signup-btn">Sign Up</button>
189
198
  </div>
190
199
  </Header>
191
200
  ```
192
201
 
193
- #### Complete Example with All Props
202
+ ## Comprehensive Example
203
+
204
+ Below is a complete implementation example showcasing custom logo configuration, navigation with a home URL, and theme overrides.
194
205
 
195
206
  ```astro
196
207
  ---
197
208
  import Header from '@sofidevo/astro-dynamic-header/Header';
198
209
 
199
210
  const menuItems = [
200
- { link: '/about', text: 'About' },
201
- { link: '/services', text: 'Services' },
202
- { link: '/contact', text: 'Contact' },
211
+ {
212
+ link: "#",
213
+ text: "Services",
214
+ submenu: [
215
+ { link: "/design", text: "Design" },
216
+ { link: "/consulting", text: "Consulting" },
217
+ {
218
+ link: "#",
219
+ text: "Web Development",
220
+ submenu: [
221
+ { link: "/web/frontend", text: "Frontend" },
222
+ { link: "/web/backend", text: "Backend" },
223
+ { link: "/web/fullstack", text: "Full Stack" },
224
+ ],
225
+ },
226
+ ],
227
+ },
228
+ { link: "/about", text: "About" },
229
+ { link: "/contact", text: "Contact" },
203
230
  ];
231
+
232
+ const theme = {
233
+ light: {
234
+ accentColor: "#ff0000",
235
+ backgroundColor: "rgba(255, 255, 255, 0.8)",
236
+ },
237
+ dark: {
238
+ accentColor: "#00ffff",
239
+ backgroundColor: "rgba(20, 20, 20, 0.9)",
240
+ },
241
+ };
204
242
  ---
205
243
 
206
244
  <Header
207
- headerType="fullscreen"
208
- logoSrc="/logo.png"
209
- logoAlt="My Company"
210
- logoWidth="60px"
211
- logoText="My Company"
212
- logoTextSize="1.2em"
213
- logoTextColor="#ffffff"
214
- homeUrl="/"
215
- menuItems={menuItems}
216
- backgroundColor="#000000dd"
217
- backdropBlur="blur(15px)"
218
- zIndex={100}
245
+ headerType="floating"
246
+ preset="dark"
247
+ logo={{
248
+ src: "https://itssofi.dev/img/icons/sofi-icon.webp",
249
+ alt: "My Site Logo",
250
+ width: "44px",
251
+ }}
252
+ navigation={{
253
+ homeUrl: "/",
254
+ menuItems: menuItems,
255
+ }}
256
+ theme={theme}
219
257
  >
220
- <div slot="actions" class="header-actions">
221
- <button class="btn btn-outline">Login</button>
222
- <button class="btn btn-primary">Get Started</button>
223
- </div>
258
+ <button slot="actions">Login</button>
224
259
  </Header>
225
260
  ```
226
261
 
227
- #### Styling Action Buttons
228
-
229
- ```css
230
- .header-actions {
231
- display: flex;
232
- gap: 0.5em;
233
- align-items: center;
234
- }
235
-
236
- .btn {
237
- padding: 0.5em 1em;
238
- border: none;
239
- border-radius: 6px;
240
- cursor: pointer;
241
- font-weight: 500;
242
- text-decoration: none;
243
- display: inline-flex;
244
- align-items: center;
245
- transition: all 0.2s ease;
246
- }
247
-
248
- .btn-outline {
249
- background: transparent;
250
- color: #ffffff;
251
- border: 1px solid #ffffff;
252
- }
253
-
254
- .btn-outline:hover {
255
- background: #ffffff;
256
- color: #000000;
257
- }
258
-
259
- .btn-primary {
260
- background: #00ffff;
261
- color: #000000;
262
- }
263
-
264
- .btn-primary:hover {
265
- background: #00cccc;
266
- }
267
- ```
268
262
  ## Header Types
269
263
 
270
264
  ### Floating Header
265
+
271
266
  - Centered with max-width constraint
272
267
  - Rounded corners
273
268
  - Padding around container
274
269
  - Perfect for modern, card-like designs
275
270
 
276
271
  ### Fullscreen Header
272
+
277
273
  - Full viewport width
278
274
  - No border radius
279
275
  - Edge-to-edge design
280
276
  - Ideal for traditional website layouts
281
277
 
282
-
283
278
  ## Styling and Customization
284
279
 
285
280
  The component uses CSS custom properties that you can override:
@@ -294,24 +289,71 @@ The component uses CSS custom properties that you can override:
294
289
 
295
290
  ## TypeScript Support
296
291
 
297
- Full TypeScript support with exported interfaces:
292
+ The package provides full TypeScript support. You can import types to ensure your configuration is correct:
298
293
 
299
- ```typescript
294
+ ```astro
295
+ ---
296
+ import Header from '@sofidevo/astro-dynamic-header/Header';
300
297
  import type {
301
- MenuItemType,
302
- HeaderProps,
303
- NavMenuProps,
304
- MobileNavProps,
305
- HamburgerButtonProps
298
+ NavConfig,
299
+ DualThemeConfig,
300
+ MenuItem,
301
+ SecondaryMenuItem
306
302
  } from '@sofidevo/astro-dynamic-header';
303
+
304
+ const navigation: NavConfig = {
305
+ menuItems: [
306
+ {
307
+ link: '/products',
308
+ text: 'Products',
309
+ submenu: [
310
+ { link: '/software', text: 'Software' },
311
+ { link: '/hardware', text: 'Hardware' }
312
+ ]
313
+ }
314
+ ]
315
+ };
316
+
317
+ const theme: DualThemeConfig = {
318
+ light: {
319
+ accentColor: "#3e1c71",
320
+ backgroundColor: "rgba(255, 255, 255, 0.8)",
321
+ backgroundColorOpaque: "#ffffff"
322
+ },
323
+ dark: {
324
+ accentColor: "#00ffff",
325
+ backgroundColor: "rgba(10, 10, 10, 0.9)",
326
+ backgroundColorOpaque: "#0a0a0a"
327
+ }
328
+ };
329
+ ---
330
+
331
+ <Header
332
+ navigation={navigation}
333
+ theme={theme}
334
+ preset="auto"
335
+ />
307
336
  ```
308
337
 
338
+ ### Available Types
339
+
340
+ | Type | Description |
341
+ | ------------------- | ---------------------------------------------- |
342
+ | `MenuItem` | Top-level menu item with optional properties |
343
+ | `SecondaryMenuItem` | Second-level menu item |
344
+ | `TertiaryMenuItem` | Third-level menu item |
345
+ | `NavConfig` | Main navigation configuration object |
346
+ | `ThemeConfig` | Individual theme settings (colors, blur, etc.) |
347
+ | `DualThemeConfig` | Combined settings for light and dark modes |
348
+ | `LogoConfig` | Logo image and text configuration |
349
+ | `HeaderProps` | Main props for the Header component |
350
+
309
351
  ## Browser Support
310
352
 
311
- - All modern browsers
312
- - Mobile responsive design
313
- - Supports CSS `backdrop-filter`
314
- - Graceful degradation for older browsers
353
+ - All modern browsers (Chrome, Firefox, Safari, Edge)
354
+ - Mobile responsive design with optimized touch targets
355
+ - Supports CSS `backdrop-filter` for glassmorphism
356
+ - Automatic theme switching based on OS or site preference via `.dark` class
315
357
 
316
358
  ## Troubleshooting
317
359
 
@@ -320,29 +362,26 @@ import type {
320
362
  If you encounter import errors, try these solutions:
321
363
 
322
364
  1. **Use direct subpath import:**
365
+
323
366
  ```astro
324
367
  import Header from '@sofidevo/astro-dynamic-header/Header';
325
368
  ```
326
369
 
327
- 2. **Verify TypeScript configuration:**
370
+ 2. **Check relative imports in TS:**
371
+ 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.
372
+
373
+ 3. **Verify TypeScript configuration:**
374
+
328
375
  ```json
329
376
  // tsconfig.json
330
377
  {
331
378
  "compilerOptions": {
332
- "moduleResolution": "bundler", // or "nodenext"
379
+ "moduleResolution": "bundler",
333
380
  "allowImportingTsExtensions": true
334
381
  }
335
382
  }
336
383
  ```
337
384
 
338
- 3. **Import types separately:**
339
- ```astro
340
- ---
341
- import Header from '@sofidevo/astro-dynamic-header/Header';
342
- import type { MenuItemType } from '@sofidevo/astro-dynamic-header';
343
- ---
344
- ```
345
-
346
385
  ### Compatibility
347
386
 
348
387
  - Astro 4.x and 5.x
@@ -354,44 +393,6 @@ If you encounter import errors, try these solutions:
354
393
 
355
394
  Visit our demo website to see the component in action with interactive examples and complete documentation.
356
395
 
357
- ## Testing
358
-
359
- This project includes a comprehensive test suite with 34 tests covering all critical functionality.
360
-
361
- ### Running Tests
362
-
363
- ```bash
364
- # Run all tests
365
- npm test
366
-
367
- # Run tests in watch mode
368
- npm run test:watch
369
-
370
- # Run tests with coverage report
371
- npm run test:coverage
372
- ```
373
-
374
- ### Test Coverage
375
-
376
- The test suite covers:
377
-
378
- #### Component Logic Tests
379
- - **Header Component** (4 tests): Hamburger controller functionality, menu toggle behavior
380
- - **HamburgerButton Component** (10 tests): Button states, responsive behavior, accessibility
381
- - **MobileNav Component** (7 tests): Dropdown structure, nested submenus, conditional rendering
382
- - **NavMenu Component** (6 tests): Dynamic positioning, submenu interactions, viewport adjustments
383
-
384
- #### Integration Tests (7 tests)
385
- - Component interaction flows
386
- - Responsive behavior between mobile/desktop
387
- - Keyboard navigation and accessibility
388
- - Menu state management during navigation
389
-
390
- ### Test Technologies
391
- - **Vitest**: Fast testing framework
392
- - **jsdom**: DOM simulation for component testing
393
- - **TypeScript**: Type-safe test writing
394
-
395
396
  ## License
396
397
 
397
398
  MIT License - see the [LICENSE](./LICENSE) file for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofidevo/astro-dynamic-header",
3
- "version": "1.1.34",
3
+ "version": "2.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",
@@ -50,20 +50,20 @@
50
50
  "author": "SofiDev contact@sofidev.top",
51
51
  "license": "MIT",
52
52
  "peerDependencies": {
53
- "astro": "^6.0.5"
53
+ "astro": "^6.0.6"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@astrojs/check": "^0.9.8",
57
57
  "@types/jsdom": "^21.1.7",
58
58
  "@vitest/coverage-v8": "^3.2.4",
59
- "astro": "^6.0.5",
59
+ "astro": "^6.0.6",
60
60
  "jsdom": "^26.1.0",
61
61
  "typescript": "^5.0.0",
62
62
  "vitest": "^3.2.4"
63
63
  },
64
64
  "repository": {
65
65
  "type": "git",
66
- "url": "https://github.com/SofiDevO/astro-dynamic-header"
66
+ "url": "git+https://github.com/SofiDevO/astro-dynamic-header.git"
67
67
  },
68
68
  "bugs": {
69
69
  "url": "https://github.com/SofiDevO/astro-dynamic-header/issues"
package/src/Header.astro CHANGED
@@ -3,49 +3,83 @@ import HamburgerButton from "./HamburgerButton.astro";
3
3
  import NavMenu from "./NavMenu.astro";
4
4
  import MobileNav from "./MobileNav.astro";
5
5
 
6
+ import type {
7
+ LogoConfig,
8
+ NavConfig,
9
+ DualThemeConfig,
10
+ CustomClassNames,
11
+ } from "./index.js";
12
+
6
13
  export interface Props {
7
14
  headerType?: "floating" | "fullscreen";
8
- // properties for logo
9
- logoSrc?: string;
10
- logoAlt?: string;
11
- logoWidth?: string;
12
- logoText?: string;
13
- logoTextSize?: string;
14
- logoTextColor?: string;
15
- // properties for navigation
16
- homeUrl?: string;
17
- menuItems?: Array<{
18
- link: string;
19
- text: string;
20
- submenu?: Array<{
21
- link: string;
22
- text: string;
23
- submenu?: Array<{
24
- link: string;
25
- text: string;
26
- }>;
27
- }>;
28
- }>;
29
- // styles
30
- backgroundColor?: string;
31
- backdropBlur?: string;
32
- zIndex?: number;
15
+ preset?: "light" | "dark" | "auto";
16
+ logo?: LogoConfig;
17
+ navigation?: NavConfig;
18
+ theme?: DualThemeConfig;
19
+ classNames?: CustomClassNames;
33
20
  }
34
21
 
35
22
  const {
36
23
  headerType = "floating",
37
- logoSrc = "/logo.png",
38
- logoAlt = "Logo",
39
- logoWidth = "55px",
40
- logoText = "",
41
- logoTextSize = "1em",
42
- logoTextColor = "#ffffff",
43
- homeUrl = "/",
44
- menuItems = [],
45
- backgroundColor = "#0d0d0dcc",
46
- backdropBlur = "blur(20px)",
47
- zIndex = 10
24
+ preset = "auto",
25
+ logo = {},
26
+ navigation = {},
27
+ theme = {},
28
+ classNames = {},
48
29
  } = Astro.props;
30
+
31
+ if (import.meta.env.DEV) {
32
+ // Check if logo is being passed as a string (legacy)
33
+ if (typeof logo === "string") {
34
+ console.warn(
35
+ "[@sofidevo/astro-dynamic-header] BREAKING CHANGE: The 'logo' prop now expects an object. Please use logo={{ src: '...' }} instead.",
36
+ );
37
+ }
38
+ if (Array.isArray(navigation)) {
39
+ console.warn(
40
+ "[@sofidevo/astro-dynamic-header] BREAKING CHANGE: The 'navigation' prop now expects an object with 'menuItems'. Please use navigation={{ menuItems: [...] }} instead.",
41
+ );
42
+ }
43
+ }
44
+
45
+ // Default theme configuration
46
+ const defaultThemes = {
47
+ light: {
48
+ backgroundColor: "rgba(255, 255, 255, 0.9)",
49
+ backgroundColorOpaque: "rgb(255, 255, 255)",
50
+ backdropBlur: "blur(20px)",
51
+ zIndex: 10,
52
+ textColor: "#1a1a1a",
53
+ accentColor: "#3e1c71",
54
+ },
55
+ dark: {
56
+ backgroundColor: "#0d0d0dcc",
57
+ backgroundColorOpaque: "#0d0d0d",
58
+ backdropBlur: "blur(20px)",
59
+ zIndex: 10,
60
+ textColor: "#ffffff",
61
+ accentColor: "#00ffff",
62
+ },
63
+ };
64
+
65
+ // Merge with user overrides
66
+ const lightTheme = { ...defaultThemes.light, ...theme.light };
67
+ const darkTheme = { ...defaultThemes.dark, ...theme.dark };
68
+
69
+ // Navigation configuration
70
+ const { homeUrl = "/", menuItems = [] } = navigation;
71
+
72
+ // Logo configuration
73
+ const {
74
+ src: logoSrc = "/logo.png",
75
+ alt: logoAlt = "Logo",
76
+ width: logoWidth = "55px",
77
+ text: logoText = "",
78
+ textSize: logoTextSize = "1em",
79
+ textColor: logoTextColor = "inherit", // Will inherit from theme text-color
80
+ } = logo;
81
+
82
+ const forcedClass = preset !== "auto" ? `header--force-${preset}` : "";
49
83
  ---
50
84
 
51
85
  <script>
@@ -64,7 +98,7 @@ const {
64
98
  private init(): void {
65
99
  if (this.hamburgerBtn && this.mobileMenu) {
66
100
  this.hamburgerBtn.addEventListener("click", () => this.toggleMenu());
67
- this.links.forEach(link => {
101
+ this.links.forEach((link) => {
68
102
  link.addEventListener("click", () => this.closeMenu());
69
103
  });
70
104
  }
@@ -85,38 +119,138 @@ const {
85
119
  }
86
120
  }
87
121
 
88
- // Auto-initialize when DOM is ready
89
- document.addEventListener('DOMContentLoaded', () => {
122
+ document.addEventListener("DOMContentLoaded", () => {
90
123
  new HamburgerController();
91
124
  });
92
125
  </script>
93
126
 
94
- <div class={`header__container header__container--${headerType}`} style={`z-index: ${zIndex};`}>
95
- <header class={`header header--${headerType}`} style={`background-color: ${backgroundColor}; backdrop-filter: ${backdropBlur}; -webkit-backdrop-filter: ${backdropBlur};`}>
96
- <a class="logo__container" href={homeUrl}>
97
- <img class="header__logo" src={logoSrc} alt={logoAlt} style={`width: ${logoWidth};`} />
98
- {logoText && <span class="header__logo-text" style={`color: ${logoTextColor}; font-size: ${logoTextSize};`}>{logoText}</span>}
127
+ <div
128
+ class:list={[
129
+ "header__container",
130
+ `header__container--${headerType}`,
131
+ classNames.container,
132
+ ]}
133
+ style={{ zIndex: lightTheme.zIndex }}
134
+ >
135
+ <header
136
+ class:list={[
137
+ "header",
138
+ `header--${headerType}`,
139
+ forcedClass,
140
+ classNames.header,
141
+ ]}
142
+ style={{
143
+ "--l-bg": lightTheme.backgroundColor,
144
+ "--l-bg-opaque": lightTheme.backgroundColorOpaque,
145
+ "--l-text": lightTheme.textColor,
146
+ "--l-accent": lightTheme.accentColor,
147
+ "--l-blur": lightTheme.backdropBlur,
148
+ "--d-bg": darkTheme.backgroundColor,
149
+ "--d-bg-opaque": darkTheme.backgroundColorOpaque,
150
+ "--d-text": darkTheme.textColor,
151
+ "--d-accent": darkTheme.accentColor,
152
+ "--d-blur": darkTheme.backdropBlur,
153
+ }}
154
+ >
155
+ <a class:list={["logo__container", classNames.logo]} href={homeUrl}>
156
+ <img
157
+ class="header__logo"
158
+ src={logoSrc}
159
+ alt={logoAlt}
160
+ style={{ width: logoWidth }}
161
+ />
162
+ {
163
+ logoText && (
164
+ <span
165
+ class:list={["header__logo-text", classNames.logoText]}
166
+ style={{ color: logoTextColor, fontSize: logoTextSize }}
167
+ >
168
+ {logoText}
169
+ </span>
170
+ )
171
+ }
99
172
  </a>
100
- <NavMenu menuItems={menuItems} />
101
- {Astro.slots.has('actions') && (
102
- <div class="actions-desktop">
103
- <slot name="actions" />
104
- </div>
105
- )}
106
- <HamburgerButton />
107
- <MobileNav menuItems={menuItems} type={headerType}>
108
- {Astro.slots.has('actions') && (
109
- <div class="actions-mobile" slot="slot-panel">
173
+
174
+ <div class:list={["nav-menu-wrapper", classNames.nav]}>
175
+ <NavMenu menuItems={menuItems} />
176
+ </div>
177
+
178
+ {
179
+ Astro.slots.has("actions") && (
180
+ <div class="actions-desktop">
110
181
  <slot name="actions" />
111
182
  </div>
112
- )}
113
- </MobileNav>
183
+ )
184
+ }
185
+
186
+ <HamburgerButton />
114
187
 
188
+ <MobileNav menuItems={menuItems} type={headerType}>
189
+ {
190
+ Astro.slots.has("actions") && (
191
+ <div class="actions-mobile" slot="slot-panel">
192
+ <slot name="actions" />
193
+ </div>
194
+ )
195
+ }
196
+ </MobileNav>
115
197
  </header>
116
198
  </div>
117
199
 
118
- <style is:inline >
119
- /* Base styles for header container */
200
+ <style is:inline>
201
+ .header {
202
+ --bg-color: var(--l-bg);
203
+ --bg-color-opaque: var(--l-bg-opaque);
204
+ --text-color: var(--l-text);
205
+ --accent-color: var(--l-accent);
206
+ --backdrop-blur: var(--l-blur);
207
+
208
+ display: flex;
209
+ align-items: center;
210
+ width: 100%;
211
+ justify-content: space-between;
212
+ padding: 1em 2em;
213
+ background-color: var(--bg-color);
214
+ backdrop-filter: var(--backdrop-blur);
215
+ -webkit-backdrop-filter: var(--backdrop-blur);
216
+ color: var(--text-color);
217
+ transition:
218
+ background-color 0.3s ease,
219
+ color 0.3s ease;
220
+
221
+ @media (width < 768px) {
222
+ align-self: flex-end;
223
+ padding: 0.4em;
224
+ margin: 0 auto;
225
+ }
226
+ }
227
+
228
+ /* Automatic dark mode detection via root class */
229
+ :root.dark .header:not(.header--force-light) {
230
+ --bg-color: var(--d-bg);
231
+ --bg-color-opaque: var(--d-bg-opaque);
232
+ --text-color: var(--d-text);
233
+ --accent-color: var(--d-accent);
234
+ --backdrop-blur: var(--d-blur);
235
+ }
236
+
237
+ /* Forced themes */
238
+ .header--force-dark {
239
+ --bg-color: var(--d-bg) !important;
240
+ --bg-color-opaque: var(--d-bg-opaque) !important;
241
+ --text-color: var(--d-text) !important;
242
+ --accent-color: var(--d-accent) !important;
243
+ --backdrop-blur: var(--d-blur) !important;
244
+ }
245
+
246
+ .header--force-light {
247
+ --bg-color: var(--l-bg) !important;
248
+ --bg-color-opaque: var(--l-bg-opaque) !important;
249
+ --text-color: var(--l-text) !important;
250
+ --accent-color: var(--l-accent) !important;
251
+ --backdrop-blur: var(--l-blur) !important;
252
+ }
253
+
120
254
  .header__container {
121
255
  width: 100%;
122
256
  display: flex;
@@ -131,43 +265,24 @@ const {
131
265
  }
132
266
  }
133
267
 
134
- /* Floating header container styles */
135
268
  .header__container--floating {
136
- padding: .5em;
269
+ padding: 0.5em;
137
270
  }
138
271
 
139
- /* Fullscreen header container styles */
140
272
  .header__container--fullscreen {
141
273
  padding: 0;
142
274
  }
143
275
 
144
- /* Base styles for header */
145
- .header {
146
- display: flex;
147
- align-items: center;
148
- width: 100%;
149
- justify-content: space-between;
150
- padding: 1em 2em;
151
-
152
- @media (width < 768px) {
153
- align-self: flex-end;
154
- padding: 0.4em;
155
- margin: 0 auto;
156
- }
157
- }
158
-
159
- /* Floating header styles */
160
276
  .header--floating {
161
277
  max-width: 1100px;
162
278
  border-radius: 20px;
163
- padding: .7em 1em;
279
+ padding: 0.7em 1em;
164
280
 
165
281
  @media (width < 768px) {
166
282
  max-width: 95%;
167
283
  }
168
284
  }
169
285
 
170
- /* Fullscreen header styles */
171
286
  .header--fullscreen {
172
287
  border-radius: 0;
173
288
  padding: 1em 2em;
@@ -182,16 +297,17 @@ const {
182
297
  display: flex;
183
298
  align-items: center;
184
299
  text-decoration: none;
185
- & span{
300
+ color: inherit;
301
+ & span {
186
302
  font-weight: 600;
187
303
  }
188
304
  }
305
+
189
306
  .header__logo {
190
307
  margin-right: 1em;
191
308
  object-fit: contain;
192
309
  }
193
310
 
194
- /* Actions visibility control */
195
311
  .actions-desktop {
196
312
  @media (width < 768px) {
197
313
  display: none;
@@ -205,7 +321,6 @@ const {
205
321
  }
206
322
  }
207
323
 
208
- /* Adjust transform for fullscreen header */
209
324
  .header--fullscreen .mobile-header__menu.is-active {
210
325
  transform: translate(0%);
211
326
  }
@@ -1,21 +1,11 @@
1
1
  ---
2
+ import type { MenuItem, SecondaryMenuItem, TertiaryMenuItem } from "./index.js";
3
+
2
4
  export interface Props {
3
5
  type?: "floating" | "fullscreen";
4
- menuItems?: Array<{
5
- link: string;
6
- text: string;
7
- submenu?: Array<{
8
- link: string;
9
- text: string;
10
- submenu?: Array<{
11
- link: string;
12
- text: string;
13
- }>;
14
- }>;
15
- }>;
6
+ menuItems?: MenuItem[];
16
7
  showHomeLink?: boolean;
17
8
  homeText?: string;
18
- accentColor?: string;
19
9
  }
20
10
 
21
11
  const { type } = Astro.props;
@@ -24,7 +14,6 @@ const {
24
14
  menuItems = [],
25
15
  showHomeLink = true,
26
16
  homeText = "Home",
27
- accentColor = "var(--light-spot-color, #00ffff)",
28
17
  } = Astro.props;
29
18
 
30
19
  const pagePathname = Astro.url.pathname;
@@ -33,7 +22,6 @@ const pagePathname = Astro.url.pathname;
33
22
  <nav
34
23
  class={`mobile-header__menu mobile-header__menu--${type}`}
35
24
  id="mobile-header-menu"
36
- style={`--mobile-accent-color: ${accentColor};`}
37
25
  >
38
26
  <ol class="mobile-menu">
39
27
  {
@@ -50,7 +38,7 @@ const pagePathname = Astro.url.pathname;
50
38
  )
51
39
  }
52
40
  {
53
- menuItems?.map((item: any) => (
41
+ menuItems?.map((item: MenuItem) => (
54
42
  <li>
55
43
  {item.submenu ? (
56
44
  <details class="mobile-details">
@@ -61,11 +49,11 @@ const pagePathname = Astro.url.pathname;
61
49
  icon="codicon:triangle-down"
62
50
  width="15"
63
51
  height="15"
64
- style={`color: ${accentColor};`}
52
+ style="color: var(--accent-color);"
65
53
  />
66
54
  </summary>
67
55
  <ol class="mobile-submenu">
68
- {item?.submenu?.map((sub:any) => (
56
+ {item?.submenu?.map((sub: SecondaryMenuItem) => (
69
57
  <li>
70
58
  {sub.submenu && sub.submenu.length > 0 ? (
71
59
  <details class="mobile-details">
@@ -76,11 +64,11 @@ const pagePathname = Astro.url.pathname;
76
64
  icon="codicon:triangle-down"
77
65
  width="15"
78
66
  height="15"
79
- style={`color: ${accentColor};`}
67
+ style="color: var(--accent-color);"
80
68
  />
81
69
  </summary>
82
70
  <ol class="mobile-subsubmenu">
83
- {sub.submenu.map((subsub:any) => (
71
+ {sub.submenu.map((subsub: TertiaryMenuItem) => (
84
72
  <li class="mobile-submenu__item secondary">
85
73
  <a
86
74
  class="menu__link mobile-menu__link"
@@ -125,16 +113,16 @@ const pagePathname = Astro.url.pathname;
125
113
  position: fixed;
126
114
  top: -11px;
127
115
  left: 0;
128
- background: #151515;
116
+ background: var(--bg-color-opaque, #151515);
129
117
  z-index: 20;
130
- transition: transform 0.3s ease-in-out;
118
+ transition: transform 0.3s ease-in-out, background-color 0.3s ease;
131
119
  transform: translateX(100%);
132
120
  padding: 10rem 2rem 2rem;
133
- color: var(--mobile-accent-color);
121
+ color: var(--text-color, #fff);
134
122
  }
135
123
 
136
124
  a {
137
- color: var(--mobile-accent-color);
125
+ color: var(--text-color, #fff);
138
126
  }
139
127
  }
140
128
 
@@ -151,7 +139,7 @@ const pagePathname = Astro.url.pathname;
151
139
 
152
140
  .mobile-menu li a {
153
141
  border: solid 1px
154
- color-mix(in srgb, var(--mobile-accent-color) 20%, transparent);
142
+ color-mix(in srgb, var(--accent-color, #00ffff) 20%, transparent);
155
143
  border-radius: 4px;
156
144
  width: 100%;
157
145
  height: 4rem;
@@ -171,7 +159,7 @@ const pagePathname = Astro.url.pathname;
171
159
  .mobile-details summary {
172
160
  background: linear-gradient(
173
161
  to right,
174
- color-mix(in srgb, var(--mobile-accent-color) 20%, transparent),
162
+ color-mix(in srgb, var(--accent-color, #00ffff) 20%, transparent),
175
163
  transparent
176
164
  );
177
165
  border-radius: 4px;
@@ -207,7 +195,7 @@ const pagePathname = Astro.url.pathname;
207
195
 
208
196
  .mobile-submenu,
209
197
  .mobile-subsubmenu {
210
- border-left: dashed 1px var(--mobile-accent-color);
198
+ border-left: dashed 1px var(--accent-color, #00ffff);
211
199
  padding-left: 2rem;
212
200
  margin-left: 1rem;
213
201
  margin-top: 1rem;
@@ -230,7 +218,7 @@ const pagePathname = Astro.url.pathname;
230
218
  width: 100%;
231
219
  font-size: 1.7rem;
232
220
  height: 5rem;
233
- color: #fff;
221
+ color: inherit;
234
222
  display: flex;
235
223
  align-items: center;
236
224
  }
package/src/NavMenu.astro CHANGED
@@ -1,17 +1,8 @@
1
1
  ---
2
+ import type { MenuItem, SecondaryMenuItem, TertiaryMenuItem } from "./index.js";
3
+
2
4
  export interface Props {
3
- menuItems?: Array<{
4
- link: string;
5
- text: string;
6
- submenu?: Array<{
7
- link: string;
8
- text: string;
9
- submenu?: Array<{
10
- link: string;
11
- text: string;
12
- }>;
13
- }>;
14
- }>;
5
+ menuItems?: MenuItem[];
15
6
  showHomeLink?: boolean;
16
7
  homeText?: string;
17
8
  }
@@ -24,7 +15,7 @@ const pagePathname = Astro.url.pathname;
24
15
  <script>
25
16
  function adjustSubsubmenuPositionOnResize() {
26
17
  const subsubmenus = document.querySelectorAll(
27
- ".subsubmenu"
18
+ ".subsubmenu",
28
19
  ) as NodeListOf<HTMLElement>;
29
20
 
30
21
  subsubmenus.forEach((subsubmenu) => {
@@ -91,7 +82,7 @@ const pagePathname = Astro.url.pathname;
91
82
  const currentTransform = (subsubmenu as HTMLElement).style.transform;
92
83
  if (currentTransform && currentTransform.includes("translateX")) {
93
84
  const existingTranslate = currentTransform.match(
94
- /translateX\(([^)]+)\)/
85
+ /translateX\(([^)]+)\)/,
95
86
  );
96
87
  if (existingTranslate) {
97
88
  const existingValue = existingTranslate[1];
@@ -135,7 +126,7 @@ const pagePathname = Astro.url.pathname;
135
126
  )
136
127
  }
137
128
  {
138
- menuItems.map((item) => (
129
+ menuItems.map((item: MenuItem) => (
139
130
  <li class="menu__item">
140
131
  <a class="menu__link" href={item.link}>
141
132
  {item.text}
@@ -150,7 +141,7 @@ const pagePathname = Astro.url.pathname;
150
141
  </a>
151
142
  {item.submenu && (
152
143
  <ul class="submenu">
153
- {item.submenu.map((sub) => (
144
+ {item.submenu.map((sub: SecondaryMenuItem) => (
154
145
  <li class="submenu__item submenu__item--secondary">
155
146
  <a href={sub.link}>
156
147
  {sub.text}
@@ -165,7 +156,7 @@ const pagePathname = Astro.url.pathname;
165
156
  </a>
166
157
  {sub.submenu && (
167
158
  <ul class="subsubmenu">
168
- {sub.submenu.map((subsub) => (
159
+ {sub.submenu.map((subsub: TertiaryMenuItem) => (
169
160
  <li class="submenu__item--tertiary">
170
161
  <a href={subsub.link}>{subsub.text}</a>
171
162
  </li>
@@ -202,11 +193,15 @@ const pagePathname = Astro.url.pathname;
202
193
 
203
194
  .menu__link {
204
195
  text-decoration: none;
205
- padding: 0.5em ;
196
+ padding: 0.5em;
206
197
  display: flex;
207
198
  align-items: center;
208
199
  gap: 0.8em;
209
- color: color-mix(in srgb, var(--light-spot-color, #00ffff) 30%, white 70%);
200
+ color: color-mix(
201
+ in srgb,
202
+ var(--accent-color, #00ffff) 30%,
203
+ var(--text-color, #ffffff) 70%
204
+ );
210
205
  position: relative;
211
206
  }
212
207
 
@@ -221,7 +216,7 @@ const pagePathname = Astro.url.pathname;
221
216
  transform-origin: 1;
222
217
  height: 2px;
223
218
  transition: transform 0.3s ease;
224
- background: var(--light-spot-color, #00ffff);
219
+ background: var(--accent-color, #00ffff);
225
220
  }
226
221
 
227
222
  .menu__link:hover::after {
@@ -239,13 +234,14 @@ const pagePathname = Astro.url.pathname;
239
234
 
240
235
  .submenu,
241
236
  .subsubmenu {
242
- color: color-mix(in srgb, var(--light-spot-color, #00ffff) 20%, white 80%);
243
- background-color: #171717;
244
- border: 0.5px solid #3e1c71;
237
+ color: var(--text-color, #ffffff);
238
+ background-color: var(--bg-color-opaque, #171717);
239
+ border: 0.5px solid
240
+ color-mix(in srgb, var(--accent-color, #3e1c71) 30%, transparent);
245
241
  position: absolute;
246
242
  flex-direction: column;
247
243
  gap: 0.4em;
248
- padding: 1em;
244
+ padding: 1em;
249
245
  border-radius: 10px;
250
246
  transition: transform 0.3s ease-in-out;
251
247
  pointer-events: none;
@@ -267,8 +263,7 @@ const pagePathname = Astro.url.pathname;
267
263
  width: 100%;
268
264
  height: 64px;
269
265
  border-radius: 6px;
270
- color: #fff;
271
- color: var(--color-tertiary, #fff);
266
+ color: var(--text-color, #fff);
272
267
  }
273
268
 
274
269
  .submenu__item--secondary,
@@ -283,8 +278,12 @@ const pagePathname = Astro.url.pathname;
283
278
 
284
279
  .submenu__item--secondary a:hover,
285
280
  .submenu__item--tertiary a:hover {
286
- background: rgb(71, 70, 70);
287
- color: white;
281
+ background: color-mix(
282
+ in srgb,
283
+ var(--accent-color, #00ffff) 15%,
284
+ transparent
285
+ );
286
+ color: var(--text-color, white);
288
287
  }
289
288
 
290
289
  .submenu {
package/src/index.ts CHANGED
@@ -7,16 +7,66 @@ export interface MenuItemType {
7
7
  submenu?: MenuItemType[];
8
8
  }
9
9
 
10
- export interface HeaderProps {
11
- headerType?: "floating" | "fullscreen";
12
- logoSrc?: string;
13
- logoAlt?: string;
14
- logoWidth?: string;
10
+ export interface TertiaryMenuItem {
11
+ link: string;
12
+ text: string;
13
+ }
14
+
15
+ export interface SecondaryMenuItem {
16
+ link: string;
17
+ text: string;
18
+ submenu?: TertiaryMenuItem[];
19
+ }
20
+
21
+ export interface MenuItem {
22
+ link: string;
23
+ text: string;
24
+ submenu?: SecondaryMenuItem[];
25
+ }
26
+
27
+ export interface LogoConfig {
28
+ src?: string;
29
+ alt?: string;
30
+ width?: string;
31
+ text?: string;
32
+ textSize?: string;
33
+ textColor?: string;
34
+ }
35
+
36
+ export interface NavConfig {
15
37
  homeUrl?: string;
16
- menuItems?: MenuItemType[];
38
+ menuItems?: MenuItem[];
39
+ }
40
+
41
+ export interface ThemeConfig {
17
42
  backgroundColor?: string;
43
+ backgroundColorOpaque?: string;
18
44
  backdropBlur?: string;
19
45
  zIndex?: number;
46
+ textColor?: string;
47
+ accentColor?: string;
48
+ }
49
+
50
+ export interface DualThemeConfig {
51
+ light?: ThemeConfig;
52
+ dark?: ThemeConfig;
53
+ }
54
+
55
+ export interface CustomClassNames {
56
+ container?: string;
57
+ header?: string;
58
+ logo?: string;
59
+ logoText?: string;
60
+ nav?: string;
61
+ }
62
+
63
+ export interface HeaderProps {
64
+ headerType?: "floating" | "fullscreen";
65
+ preset?: "light" | "dark" | "auto";
66
+ logo?: LogoConfig;
67
+ navigation?: NavConfig;
68
+ theme?: DualThemeConfig;
69
+ classNames?: CustomClassNames;
20
70
  }
21
71
 
22
72
  export interface NavMenuProps {