@sofidevo/astro-dynamic-header 1.0.30 → 1.0.32

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
@@ -4,13 +4,13 @@ A dynamic, responsive header component for Astro projects that can switch betwee
4
4
 
5
5
  ## Features
6
6
 
7
- - 🎨 **Dynamic Styles**: Switch between floating and fullscreen header layouts
8
- - 📱 **Fully Responsive**: Mobile-first design with hamburger menu
9
- - 🎯 **Multi-level Dropdowns**: Support for nested navigation menus
10
- - � **Slot Support**: Customizable slots for desktop header and mobile panel content
11
- - �🚀 **TypeScript Support**: Full type safety and IntelliSense
12
- - 🎨 **Customizable**: Extensive customization options for colors, sizes, and behavior
13
- - ⚡ **Astro Optimized**: Built specifically for Astro framework
7
+ - **Dynamic Styles**: Switch between floating and fullscreen header layouts
8
+ - **Fully Responsive**: Mobile-first design with hamburger menu
9
+ - **Multi-level Dropdowns**: Support for nested navigation menus
10
+ - **Slot Support**: Customizable slots for desktop header and mobile panel content
11
+ - **TypeScript Support**: Full type safety and IntelliSense
12
+ - **Customizable**: Extensive customization options for colors, sizes, and behavior
13
+ - **Astro Optimized**: Built specifically for Astro framework
14
14
 
15
15
 
16
16
  ### Live demo
@@ -22,6 +22,16 @@ A dynamic, responsive header component for Astro projects that can switch betwee
22
22
  npm i @sofidevo/astro-dynamic-header
23
23
  ```
24
24
 
25
+ ## Required Dependencies
26
+
27
+ You need to add the Iconify CDN to the head of your project for the hamburger menu icons to work properly:
28
+
29
+ ```html
30
+ <script src="https://code.iconify.design/iconify-icon/3.0.0/iconify-icon.min.js"></script>
31
+ ```
32
+
33
+ Add this to your main layout or in the `<head>` section of your Astro pages.
34
+
25
35
  ## Quick Start
26
36
 
27
37
  ### Basic Usage
@@ -116,7 +126,10 @@ const menuItems = [
116
126
  | `headerType` | `"floating" \| "fullscreen"` | `"floating"` | Header layout style |
117
127
  | `logoSrc` | `string` | `"/logo.png"` | Logo image source |
118
128
  | `logoAlt` | `string` | `"Logo"` | Logo alt text |
119
- | `logoWidth` | `string` | `"120px"` | Logo width |
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 |
120
133
  | `homeUrl` | `string` | `"/"` | Home page URL |
121
134
  | `menuItems` | `MenuItemType[]` | `[]` | Navigation menu items |
122
135
  | `backgroundColor` | `string` | `"#0d0d0dcc"` | Header background color |
@@ -135,18 +148,24 @@ interface MenuItemType {
135
148
 
136
149
  ## Slots Support
137
150
 
138
- The Header component provides two customizable slots that allow you to add additional content:
151
+ The Header component provides a flexible slot system that allows you to add additional content:
139
152
 
140
153
  ### Available Slots
141
154
 
142
155
  | Slot Name | Location | Visibility | Description |
143
- |-----------|----------|------------|-------------|
144
- | `slot-desktop` | Header (desktop & mobile) | Always visible | Add content to the main header area |
145
- | `slot-panel` | Mobile navigation panel | Mobile only | Add content to the mobile menu panel |
156
+ |-----------|----------|------------|-----------|
157
+ | `actions` | Header & Mobile panel | Responsive visibility | Add action buttons (login, signup, etc.) that appear in both desktop and mobile |
158
+
159
+ ### Actions Slot Behavior
160
+
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
146
165
 
147
166
  ### Slot Examples
148
167
 
149
- #### Using Individual Slots
168
+ #### Using the Actions Slot
150
169
 
151
170
  ```astro
152
171
  ---
@@ -158,32 +177,20 @@ const menuItems = [
158
177
  ];
159
178
  ---
160
179
 
161
- <!-- Adding content only to desktop header -->
162
180
  <Header
163
181
  headerType="floating"
164
182
  logoSrc="/logo.png"
183
+ logoText="My Company"
165
184
  menuItems={menuItems}
166
185
  >
167
- <button slot="slot-desktop" class="cta-button">Get Started</button>
168
- </Header>
169
-
170
- <!-- Adding content only to mobile panel -->
171
- <Header
172
- headerType="fullscreen"
173
- logoSrc="/logo.png"
174
- menuItems={menuItems}
175
- >
176
- <div slot="slot-panel" class="mobile-footer">
177
- <p>© 2024 My Company</p>
178
- <div class="social-links">
179
- <a href="/twitter">Twitter</a>
180
- <a href="/linkedin">LinkedIn</a>
181
- </div>
186
+ <div slot="actions">
187
+ <button class="login-btn">Login</button>
188
+ <button class="signup-btn">Sign Up</button>
182
189
  </div>
183
190
  </Header>
184
191
  ```
185
192
 
186
- #### Using Both Slots Together
193
+ #### Complete Example with All Props
187
194
 
188
195
  ```astro
189
196
  ---
@@ -200,64 +207,64 @@ const menuItems = [
200
207
  headerType="fullscreen"
201
208
  logoSrc="/logo.png"
202
209
  logoAlt="My Company"
203
- logoWidth="150px"
210
+ logoWidth="60px"
211
+ logoText="My Company"
212
+ logoTextSize="1.2em"
213
+ logoTextColor="#ffffff"
204
214
  homeUrl="/"
205
215
  menuItems={menuItems}
206
216
  backgroundColor="#000000dd"
207
217
  backdropBlur="blur(15px)"
208
218
  zIndex={100}
209
219
  >
210
- <!-- Content for desktop header -->
211
- <button slot="slot-desktop" class="cta-button">
212
- Sign Up
213
- </button>
214
-
215
- <!-- Content for mobile panel -->
216
- <div slot="slot-panel" class="mobile-extras">
217
- <button class="mobile-cta">Download App</button>
218
- <div class="mobile-contact">
219
- <p>Call us: +1 (555) 123-4567</p>
220
- <p>Email: info@company.com</p>
221
- </div>
220
+ <div slot="actions" class="header-actions">
221
+ <button class="btn btn-outline">Login</button>
222
+ <button class="btn btn-primary">Get Started</button>
222
223
  </div>
223
224
  </Header>
224
225
  ```
225
226
 
226
- #### Responsive Slot Behavior
227
-
228
- The `slot-desktop` is visible on both desktop and mobile by default. If you want to hide it on mobile, use CSS:
227
+ #### Styling Action Buttons
229
228
 
230
229
  ```css
231
- /* Hide desktop slot on mobile devices */
232
- @media (width < 768px) {
233
- .cta-button {
234
- display: none;
235
- }
230
+ .header-actions {
231
+ display: flex;
232
+ gap: 0.5em;
233
+ align-items: center;
236
234
  }
237
235
 
238
- /* Or create responsive variants */
239
- .desktop-only {
240
- display: block;
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;
241
246
  }
242
247
 
243
- @media (width < 768px) {
244
- .desktop-only {
245
- display: none;
246
- }
248
+ .btn-outline {
249
+ background: transparent;
250
+ color: #ffffff;
251
+ border: 1px solid #ffffff;
247
252
  }
248
- ```
249
253
 
250
- ```astro
251
- <Header menuItems={menuItems}>
252
- <button slot="slot-desktop" class="cta-button desktop-only">
253
- Desktop CTA
254
- </button>
255
- <div slot="slot-panel">
256
- <button class="mobile-cta">Mobile CTA</button>
257
- </div>
258
- </Header>
259
- ```
254
+ .btn-outline:hover {
255
+ background: #ffffff;
256
+ color: #000000;
257
+ }
258
+
259
+ .btn-primary {
260
+ background: #00ffff;
261
+ color: #000000;
262
+ }
260
263
 
264
+ .btn-primary:hover {
265
+ background: #00cccc;
266
+ }
267
+ ```
261
268
  ## Header Types
262
269
 
263
270
  ### Floating Header
@@ -338,10 +345,10 @@ If you encounter import errors, try these solutions:
338
345
 
339
346
  ### Compatibility
340
347
 
341
- - ✅ Astro 4.x and 5.x
342
- - ✅ SSG Projects (Static Site Generation)
343
- - ✅ SSR Projects (Server-Side Rendering)
344
- - ✅ Hybrid Projects (output: 'hybrid')
348
+ - Astro 4.x and 5.x
349
+ - SSG Projects (Static Site Generation)
350
+ - SSR Projects (Server-Side Rendering)
351
+ - Hybrid Projects (output: 'hybrid')
345
352
 
346
353
  ## Live Examples
347
354
 
@@ -391,4 +398,4 @@ MIT License - see the [LICENSE](./LICENSE) file for details.
391
398
 
392
399
  ## Support
393
400
 
394
- If you find this package helpful, please consider giving it a ⭐ on GitHub!
401
+ If you find this package helpful, please consider giving it a star on GitHub!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofidevo/astro-dynamic-header",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
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",
@@ -20,7 +20,8 @@
20
20
  "./HamburgerButton.astro": "./src/HamburgerButton.astro"
21
21
  },
22
22
  "files": [
23
- "src",
23
+ "src/*.ts",
24
+ "src/*.astro",
24
25
  "README.md"
25
26
  ],
26
27
  "scripts": {
@@ -52,10 +53,10 @@
52
53
  "astro": "^4.0.0 || ^5.0.0"
53
54
  },
54
55
  "devDependencies": {
55
- "@astrojs/check": "^0.9.0",
56
+ "@astrojs/check": "^0.9.6",
56
57
  "@types/jsdom": "^21.1.7",
57
58
  "@vitest/coverage-v8": "^3.2.4",
58
- "astro": "^5.12.8",
59
+ "astro": "^5.16.11",
59
60
  "jsdom": "^26.1.0",
60
61
  "typescript": "^5.0.0",
61
62
  "vitest": "^3.2.4"
package/src/Header.astro CHANGED
@@ -5,9 +5,14 @@ import MobileNav from "./MobileNav.astro";
5
5
 
6
6
  export interface Props {
7
7
  headerType?: "floating" | "fullscreen";
8
+ // properties for logo
8
9
  logoSrc?: string;
9
10
  logoAlt?: string;
10
11
  logoWidth?: string;
12
+ logoText?: string;
13
+ logoTextSize?: string;
14
+ logoTextColor?: string;
15
+ // properties for navigation
11
16
  homeUrl?: string;
12
17
  menuItems?: Array<{
13
18
  link: string;
@@ -21,6 +26,7 @@ export interface Props {
21
26
  }>;
22
27
  }>;
23
28
  }>;
29
+ // styles
24
30
  backgroundColor?: string;
25
31
  backdropBlur?: string;
26
32
  zIndex?: number;
@@ -30,7 +36,10 @@ const {
30
36
  headerType = "floating",
31
37
  logoSrc = "/logo.png",
32
38
  logoAlt = "Logo",
33
- logoWidth = "120px",
39
+ logoWidth = "55px",
40
+ logoText = "Your logo text",
41
+ logoTextSize = "1em",
42
+ logoTextColor = "#ffffff",
34
43
  homeUrl = "/",
35
44
  menuItems = [],
36
45
  backgroundColor = "#0d0d0dcc",
@@ -84,20 +93,25 @@ const {
84
93
 
85
94
  <div class={`header__container header__container--${headerType}`} style={`z-index: ${zIndex};`}>
86
95
  <header class={`header header--${headerType}`} style={`background-color: ${backgroundColor}; backdrop-filter: ${backdropBlur}; -webkit-backdrop-filter: ${backdropBlur};`}>
87
- <a href={homeUrl}>
96
+ <a class="logo__container" href={homeUrl}>
88
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>}
89
99
  </a>
90
100
  <NavMenu menuItems={menuItems} />
91
- <slot name="slot-desktop" />
101
+ <div class="actions-desktop">
102
+ <slot name="actions" />
103
+ </div>
92
104
  <HamburgerButton />
93
105
  <MobileNav menuItems={menuItems} type={headerType}>
94
- <slot name="slot-panel" slot="slot-panel" />
106
+ <div class="actions-mobile" slot="slot-panel">
107
+ <slot name="actions" />
108
+ </div>
95
109
  </MobileNav>
96
110
 
97
111
  </header>
98
112
  </div>
99
113
 
100
- <style>
114
+ <style is:inline >
101
115
  /* Base styles for header container */
102
116
  .header__container {
103
117
  width: 100%;
@@ -115,7 +129,7 @@ const {
115
129
 
116
130
  /* Floating header container styles */
117
131
  .header__container--floating {
118
- padding: 1rem;
132
+ padding: .5em;
119
133
  }
120
134
 
121
135
  /* Fullscreen header container styles */
@@ -129,10 +143,11 @@ const {
129
143
  align-items: center;
130
144
  width: 100%;
131
145
  justify-content: space-between;
146
+ padding: 1em 2em;
132
147
 
133
148
  @media (width < 768px) {
134
149
  align-self: flex-end;
135
- padding: 0.4rem;
150
+ padding: 0.4em;
136
151
  margin: 0 auto;
137
152
  }
138
153
  }
@@ -140,8 +155,8 @@ const {
140
155
  /* Floating header styles */
141
156
  .header--floating {
142
157
  max-width: 1100px;
143
- border-radius: 18px;
144
- padding: 0.5rem 1rem;
158
+ border-radius: 20px;
159
+ padding: .7em 1em;
145
160
 
146
161
  @media (width < 768px) {
147
162
  max-width: 95%;
@@ -151,19 +166,41 @@ const {
151
166
  /* Fullscreen header styles */
152
167
  .header--fullscreen {
153
168
  border-radius: 0;
154
- padding: 1rem 2rem;
169
+ padding: 1em 2em;
155
170
 
156
171
  @media (width < 768px) {
157
172
  max-width: 100%;
158
- padding: 0.8rem 1rem;
173
+ padding: 0.8em 1em;
159
174
  }
160
175
  }
161
176
 
177
+ .logo__container {
178
+ display: flex;
179
+ align-items: center;
180
+ text-decoration: none;
181
+ & span{
182
+ font-weight: 600;
183
+ }
184
+ }
162
185
  .header__logo {
163
- margin-right: 1rem;
186
+ margin-right: 1em;
164
187
  object-fit: contain;
165
188
  }
166
189
 
190
+ /* Actions visibility control */
191
+ .actions-desktop {
192
+ @media (width < 768px) {
193
+ display: none;
194
+ }
195
+ }
196
+
197
+ .actions-mobile {
198
+ margin-top: 20px;
199
+ @media (width >= 768px) {
200
+ display: none;
201
+ }
202
+ }
203
+
167
204
  /* Adjust transform for fullscreen header */
168
205
  .header--fullscreen .mobile-header__menu.is-active {
169
206
  transform: translate(0%);
@@ -50,7 +50,7 @@ const pagePathname = Astro.url.pathname;
50
50
  )
51
51
  }
52
52
  {
53
- menuItems.map((item) => (
53
+ menuItems?.map((item: any) => (
54
54
  <li>
55
55
  {item.submenu ? (
56
56
  <details class="mobile-details">
@@ -65,7 +65,7 @@ const pagePathname = Astro.url.pathname;
65
65
  />
66
66
  </summary>
67
67
  <ol class="mobile-submenu">
68
- {item.submenu.map((sub) => (
68
+ {item?.submenu?.map((sub:any) => (
69
69
  <li>
70
70
  {sub.submenu && sub.submenu.length > 0 ? (
71
71
  <details class="mobile-details">
@@ -80,7 +80,7 @@ const pagePathname = Astro.url.pathname;
80
80
  />
81
81
  </summary>
82
82
  <ol class="mobile-subsubmenu">
83
- {sub.submenu.map((subsub) => (
83
+ {sub.submenu.map((subsub:any) => (
84
84
  <li class="mobile-submenu__item secondary">
85
85
  <a
86
86
  class="menu__link mobile-menu__link"
package/src/NavMenu.astro CHANGED
@@ -33,10 +33,10 @@ const pagePathname = Astro.url.pathname;
33
33
 
34
34
  if (rect.right > viewportWidth) {
35
35
  subsubmenu.style.right = "100%";
36
- subsubmenu.style.left = "auto";
36
+ subsubmenu.style.left = "0";
37
37
  } else {
38
38
  subsubmenu.style.left = "100%";
39
- subsubmenu.style.right = "auto";
39
+ subsubmenu.style.right = "0";
40
40
  }
41
41
  });
42
42
  }
@@ -58,12 +58,12 @@ const pagePathname = Astro.url.pathname;
58
58
  (subsubmenu as HTMLElement).style.display = "block";
59
59
  (subsubmenu as HTMLElement).style.visibility = "hidden";
60
60
  (subsubmenu as HTMLElement).style.left = "100%";
61
- (subsubmenu as HTMLElement).style.transform = "";
61
+ (subsubmenu as HTMLElement).style.transform = "auto";
62
62
 
63
63
  const rect = subsubmenu.getBoundingClientRect();
64
64
 
65
65
  if (rect.right > viewportWidth) {
66
- (subsubmenu as HTMLElement).style.left = "-210%";
66
+ (subsubmenu as HTMLElement).style.left = "-100%";
67
67
  (subsubmenu as HTMLElement).style.right = "auto";
68
68
  const leftRect = subsubmenu.getBoundingClientRect();
69
69
 
@@ -86,7 +86,7 @@ const pagePathname = Astro.url.pathname;
86
86
 
87
87
  const isPositionedLeft =
88
88
  (subsubmenu as HTMLElement).style.left === "-100%";
89
- const transformDirection = isPositionedLeft ? "-2rem" : "2rem";
89
+ const transformDirection = isPositionedLeft ? "-32px" : "32px";
90
90
 
91
91
  const currentTransform = (subsubmenu as HTMLElement).style.transform;
92
92
  if (currentTransform && currentTransform.includes("translateX")) {
@@ -182,7 +182,7 @@ const pagePathname = Astro.url.pathname;
182
182
  </ul>
183
183
  </nav>
184
184
 
185
- <style>
185
+ <style is:inline>
186
186
  .header__menu {
187
187
  @media (width < 768px) {
188
188
  display: none;
@@ -202,10 +202,10 @@ const pagePathname = Astro.url.pathname;
202
202
 
203
203
  .menu__link {
204
204
  text-decoration: none;
205
- padding: 0.5rem ;
205
+ padding: 0.5em ;
206
206
  display: flex;
207
207
  align-items: center;
208
- gap: 0.8rem;
208
+ gap: 0.8em;
209
209
  color: color-mix(in srgb, var(--light-spot-color, #00ffff) 30%, white 70%);
210
210
  position: relative;
211
211
  }
@@ -244,9 +244,9 @@ const pagePathname = Astro.url.pathname;
244
244
  border: 0.5px solid #3e1c71;
245
245
  position: absolute;
246
246
  flex-direction: column;
247
- gap: 0.4rem;
248
- padding: 2rem 1rem;
249
- border-radius: 0.6rem;
247
+ gap: 0.4em;
248
+ padding: 1em;
249
+ border-radius: 10px;
250
250
  transition: transform 0.3s ease-in-out;
251
251
  pointer-events: none;
252
252
  opacity: 0;
@@ -256,6 +256,7 @@ const pagePathname = Astro.url.pathname;
256
256
  .menu__item {
257
257
  padding: 6px;
258
258
  position: relative;
259
+ list-style: none;
259
260
  }
260
261
 
261
262
  .submenu a,
@@ -264,8 +265,8 @@ const pagePathname = Astro.url.pathname;
264
265
  justify-content: space-between;
265
266
  align-items: center;
266
267
  width: 100%;
267
- height: 1rem;
268
- border-radius: 0.4rem;
268
+ height: 64px;
269
+ border-radius: 6px;
269
270
  color: #fff;
270
271
  color: var(--color-tertiary, #fff);
271
272
  }
@@ -277,7 +278,7 @@ const pagePathname = Astro.url.pathname;
277
278
 
278
279
  .submenu__item--secondary a,
279
280
  .submenu__item--tertiary a {
280
- padding: 1rem;
281
+ padding: 26px;
281
282
  }
282
283
 
283
284
  .submenu__item--secondary a:hover,
@@ -287,18 +288,18 @@ const pagePathname = Astro.url.pathname;
287
288
  }
288
289
 
289
290
  .submenu {
290
- padding: 0.9rem;
291
- right: -23px;
291
+ padding: 0.9em;
292
+ right: calc(50% - 80px);
292
293
  }
293
294
 
294
295
  .submenu::after {
295
296
  content: "";
296
297
  position: absolute;
297
- top: -4rem;
298
+ top: -4em;
298
299
  width: 100%;
299
300
  z-index: -3;
300
301
  pointer-events: none;
301
- height: 10rem;
302
+ height: 10em;
302
303
  }
303
304
 
304
305
  .subsubmenu {
@@ -311,22 +312,22 @@ const pagePathname = Astro.url.pathname;
311
312
  .subsubmenu::before {
312
313
  content: "";
313
314
  position: absolute;
314
- top: -4rem;
315
+ top: -4em;
315
316
  z-index: -1;
316
317
  pointer-events: none;
317
- height: 10rem;
318
- width: 20rem;
319
- height: 14rem;
318
+ height: 10em;
319
+ width: 20em;
320
+ height: 14em;
320
321
  }
321
322
 
322
323
  .subsubmenu::after {
323
324
  content: "";
324
325
  position: absolute;
325
326
  top: -14px;
326
- right: 3rem;
327
+ right: 3em;
327
328
  width: 100%;
328
329
  z-index: -3;
329
- height: 21rem;
330
+ height: 21em;
330
331
  }
331
332
 
332
333
  .submenu li {
@@ -336,7 +337,7 @@ const pagePathname = Astro.url.pathname;
336
337
  .menu__item:hover > .submenu {
337
338
  opacity: 1;
338
339
  pointer-events: all;
339
- transform: translateY(1.4rem);
340
+ transform: translateY(1.4em);
340
341
  }
341
342
 
342
343
  .menu__item:hover > .submenu::after {