@sofidevo/astro-dynamic-header 2.0.1 → 2.0.2

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
@@ -137,13 +137,44 @@ const theme = {
137
137
 
138
138
  #### CustomClassNames
139
139
 
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 |
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.
141
+
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
+
150
+ ##### Advanced Usage Examples
151
+
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.
154
+
155
+ ```astro
156
+ <Header
157
+ classNames={{
158
+ header: "shadow-xl border-b border-black/5 dark:border-white/10 transition-all duration-500",
159
+ container: "top-4 px-6"
160
+ }}
161
+ />
162
+ ```
163
+
164
+ **Custom Typography for Logo & Nav Spacing:**
165
+ Perfect for matching the header with your brand's specific typography and layout requirements.
166
+
167
+ ```astro
168
+ <Header
169
+ classNames={{
170
+ logoText: "tracking-tighter font-black italic uppercase",
171
+ nav: "ml-auto gap-8" /* Moves menu to the right and increases gap */
172
+ }}
173
+ />
174
+ ```
175
+
176
+ > [!TIP]
177
+ > 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.
147
178
 
148
179
  #### LogoConfig
149
180
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sofidevo/astro-dynamic-header",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
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",
@@ -1,9 +1,11 @@
1
1
  ---
2
2
  export interface Props {
3
+ /** Override the default color of the hamburger lines */
3
4
  color?: string;
4
5
  }
5
6
 
6
- const { color = "var(--color-hamburger-lines, #fff)" } = Astro.props;
7
+ const { color = "var(--text-color, var(--color-hamburger-lines, #fff))" } =
8
+ Astro.props;
7
9
  ---
8
10
 
9
11
  <button
@@ -23,14 +25,16 @@ const { color = "var(--color-hamburger-lines, #fff)" } = Astro.props;
23
25
  font: inherit;
24
26
  overflow: visible;
25
27
  margin: 0;
26
- padding: 15px;
28
+ padding: 10px;
27
29
  cursor: pointer;
28
30
  transition-timing-function: linear;
29
31
  transition-duration: 0.15s;
30
- transition-property: opacity, filter;
32
+ transition-property: opacity, filter, border-color;
31
33
  text-transform: none;
32
34
  color: inherit;
33
- border: 0;
35
+ border: 1px solid
36
+ color-mix(in srgb, var(--text-color, #fff) 25%, transparent);
37
+ border-radius: 8px;
34
38
  background-color: transparent;
35
39
 
36
40
  @media screen and (max-width: 768px) {
@@ -61,7 +65,10 @@ const { color = "var(--color-hamburger-lines, #fff)" } = Astro.props;
61
65
  .hamburger-inner,
62
66
  .hamburger-inner::before,
63
67
  .hamburger-inner::after {
64
- background-color: var(--hamburger-color, var(--color-hamburger-lines, #fff));
68
+ background-color: var(
69
+ --hamburger-color,
70
+ var(--color-hamburger-lines, #fff)
71
+ );
65
72
  position: absolute;
66
73
  width: 40px;
67
74
  height: 4px;
@@ -88,12 +95,16 @@ const { color = "var(--color-hamburger-lines, #fff)" } = Astro.props;
88
95
  }
89
96
 
90
97
  .hamburger--collapse .hamburger-inner:before {
91
- transition: top 0.12s cubic-bezier(0.33333, 0.66667, 0.66667, 1) 0.2s, transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19);
98
+ transition:
99
+ top 0.12s cubic-bezier(0.33333, 0.66667, 0.66667, 1) 0.2s,
100
+ transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19);
92
101
  }
93
102
 
94
103
  .hamburger--collapse .hamburger-inner:after {
95
104
  top: -20px;
96
- transition: top 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1) 0.2s, opacity 0.1s linear;
105
+ transition:
106
+ top 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1) 0.2s,
107
+ opacity 0.1s linear;
97
108
  }
98
109
 
99
110
  /* Animation */
@@ -105,13 +116,17 @@ const { color = "var(--color-hamburger-lines, #fff)" } = Astro.props;
105
116
 
106
117
  .hamburger--collapse.is-active .hamburger-inner:before {
107
118
  top: 0;
108
- transition: top 0.1s cubic-bezier(0.33333, 0, 0.66667, 0.33333) 0.16s, transform 0.13s cubic-bezier(0.215, 0.61, 0.355, 1) 0.25s;
119
+ transition:
120
+ top 0.1s cubic-bezier(0.33333, 0, 0.66667, 0.33333) 0.16s,
121
+ transform 0.13s cubic-bezier(0.215, 0.61, 0.355, 1) 0.25s;
109
122
  transform: rotate(-90deg);
110
123
  }
111
124
 
112
125
  .hamburger--collapse.is-active .hamburger-inner:after {
113
126
  top: 0;
114
- transition: top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333), opacity 0.1s linear 0.22s;
127
+ transition:
128
+ top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333),
129
+ opacity 0.1s linear 0.22s;
115
130
  opacity: 0;
116
131
  }
117
132
  </style>
package/src/Header.astro CHANGED
@@ -11,11 +11,31 @@ import type {
11
11
  } from "./index.js";
12
12
 
13
13
  export interface Props {
14
+ /**
15
+ * Layout style.
16
+ * - "floating": Centered with max-width and rounded corners.
17
+ * - "fullscreen": Full width with no border radius.
18
+ * @default "floating"
19
+ */
14
20
  headerType?: "floating" | "fullscreen";
21
+ /**
22
+ * Theme behavior.
23
+ * - "light": Force light mode.
24
+ * - "dark": Force dark mode.
25
+ * - "auto": Detects .dark class on the root element.
26
+ * @default "auto"
27
+ */
15
28
  preset?: "light" | "dark" | "auto";
29
+ /** Logo configuration object (src, alt, text, etc.) */
16
30
  logo?: LogoConfig;
31
+ /** Navigation links and structure. */
17
32
  navigation?: NavConfig;
33
+ /** Custom theme overrides for light and dark modes. */
18
34
  theme?: DualThemeConfig;
35
+ /**
36
+ * Custom CSS classes for specific internal elements.
37
+ * @example { header: "shadow-lg", nav: "ml-auto" }
38
+ */
19
39
  classNames?: CustomClassNames;
20
40
  }
21
41
 
@@ -2,9 +2,13 @@
2
2
  import type { MenuItem, SecondaryMenuItem, TertiaryMenuItem } from "./index.js";
3
3
 
4
4
  export interface Props {
5
+ /** Header type for styling: "floating" or "fullscreen" */
5
6
  type?: "floating" | "fullscreen";
7
+ /** Array of menu items to display in the mobile menu */
6
8
  menuItems?: MenuItem[];
9
+ /** Whether to show the home link (defaults to true) */
7
10
  showHomeLink?: boolean;
11
+ /** Text for the home link (defaults to "Home") */
8
12
  homeText?: string;
9
13
  }
10
14
 
@@ -38,64 +42,73 @@ const pagePathname = Astro.url.pathname;
38
42
  )
39
43
  }
40
44
  {
41
- menuItems?.map((item: MenuItem) => (
42
- <li>
43
- {item.submenu ? (
44
- <details class="mobile-details">
45
- <summary class="menu__summary">
45
+ menuItems?.map((item: MenuItem) => {
46
+ const isActive = pagePathname === item.link;
47
+ return (
48
+ <li>
49
+ {item.submenu ? (
50
+ <details class="mobile-details" open={item.submenu.some(sub => pagePathname === sub.link)}>
51
+ <summary class:list={["menu__summary", { active: isActive }]}>
52
+ {item.text}
53
+ <iconify-icon
54
+ class="iconify-arrow"
55
+ icon="codicon:triangle-down"
56
+ width="15"
57
+ height="15"
58
+ style="color: var(--accent-color);"
59
+ />
60
+ </summary>
61
+ <ol class="mobile-submenu">
62
+ {item?.submenu?.map((sub: SecondaryMenuItem) => {
63
+ const isSubActive = pagePathname === sub.link;
64
+ return (
65
+ <li>
66
+ {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 }]}>
69
+ {sub.text}
70
+ <iconify-icon
71
+ class="iconify-arrow"
72
+ icon="codicon:triangle-down"
73
+ width="15"
74
+ height="15"
75
+ style="color: var(--accent-color);"
76
+ />
77
+ </summary>
78
+ <ol class="mobile-subsubmenu">
79
+ {sub.submenu.map((subsub: TertiaryMenuItem) => {
80
+ const isSubSubActive = pagePathname === subsub.link;
81
+ return (
82
+ <li class="mobile-submenu__item secondary">
83
+ <a
84
+ class:list={["menu__link", "mobile-menu__link", { active: isSubSubActive }]}
85
+ href={subsub.link}
86
+ >
87
+ {subsub.text}
88
+ </a>
89
+ </li>
90
+ );
91
+ })}
92
+ </ol>
93
+ </details>
94
+ ) : (
95
+ <a class:list={["menu__link", "mobile-menu__link", { active: isSubActive }]} href={sub.link}>
96
+ {sub.text}
97
+ </a>
98
+ )}
99
+ </li>
100
+ );
101
+ })}
102
+ </ol>
103
+ </details>
104
+ ) : (
105
+ <a class:list={["mobile-menu__link", { active: isActive }]} href={item.link}>
46
106
  {item.text}
47
- <iconify-icon
48
- class="iconify-arrow"
49
- icon="codicon:triangle-down"
50
- width="15"
51
- height="15"
52
- style="color: var(--accent-color);"
53
- />
54
- </summary>
55
- <ol class="mobile-submenu">
56
- {item?.submenu?.map((sub: SecondaryMenuItem) => (
57
- <li>
58
- {sub.submenu && sub.submenu.length > 0 ? (
59
- <details class="mobile-details">
60
- <summary class="menu__summary">
61
- {sub.text}
62
- <iconify-icon
63
- class="iconify-arrow"
64
- icon="codicon:triangle-down"
65
- width="15"
66
- height="15"
67
- style="color: var(--accent-color);"
68
- />
69
- </summary>
70
- <ol class="mobile-subsubmenu">
71
- {sub.submenu.map((subsub: TertiaryMenuItem) => (
72
- <li class="mobile-submenu__item secondary">
73
- <a
74
- class="menu__link mobile-menu__link"
75
- href={subsub.link}
76
- >
77
- {subsub.text}
78
- </a>
79
- </li>
80
- ))}
81
- </ol>
82
- </details>
83
- ) : (
84
- <a class="menu__link mobile-menu__link" href={sub.link}>
85
- {sub.text}
86
- </a>
87
- )}
88
- </li>
89
- ))}
90
- </ol>
91
- </details>
92
- ) : (
93
- <a class="mobile-menu__link" href={item.link}>
94
- {item.text}
95
- </a>
96
- )}
97
- </li>
98
- ))
107
+ </a>
108
+ )}
109
+ </li>
110
+ );
111
+ })
99
112
  }
100
113
  </ol>
101
114
  <slot name="slot-panel" />
@@ -122,7 +135,15 @@ const pagePathname = Astro.url.pathname;
122
135
  }
123
136
 
124
137
  a {
125
- color: var(--text-color, #fff);
138
+ color: color-mix(
139
+ in srgb,
140
+ var(--accent-color, #00ffff) 30%,
141
+ var(--text-color, #ffffff) 70%
142
+ );
143
+ }
144
+
145
+ .active {
146
+ color: var(--accent-color, #00ffff) !important;
126
147
  }
127
148
  }
128
149
 
@@ -139,7 +160,7 @@ const pagePathname = Astro.url.pathname;
139
160
 
140
161
  .mobile-menu li a {
141
162
  border: solid 1px
142
- color-mix(in srgb, var(--accent-color, #00ffff) 20%, transparent);
163
+ color-mix(in srgb, var(--accent-color, #00ffff) 15%, transparent);
143
164
  border-radius: 4px;
144
165
  width: 100%;
145
166
  height: 4rem;
package/src/NavMenu.astro CHANGED
@@ -2,8 +2,23 @@
2
2
  import type { MenuItem, SecondaryMenuItem, TertiaryMenuItem } from "./index.js";
3
3
 
4
4
  export interface Props {
5
+ /**
6
+ * Header type for styling.
7
+ * @example "floating"
8
+ */
9
+ type?: "floating" | "fullscreen";
10
+ /** Array of menu items to display. */
5
11
  menuItems?: MenuItem[];
12
+ /**
13
+ * Whether to show the home link in the menu.
14
+ * @default true
15
+ */
6
16
  showHomeLink?: boolean;
17
+ /**
18
+ * Text for the home link.
19
+ * @default "Home"
20
+ */
21
+
7
22
  homeText?: string;
8
23
  }
9
24
 
package/src/index.ts CHANGED
@@ -1,71 +1,192 @@
1
1
 
2
2
 
3
- // Types
3
+ /**
4
+ * Represents a menu item in the navigation.
5
+ */
4
6
  export interface MenuItemType {
7
+ /** The URL path for the link */
5
8
  link: string;
9
+ /** The text label to display */
6
10
  text: string;
11
+ /** Optional nested submenu items */
7
12
  submenu?: MenuItemType[];
8
13
  }
9
14
 
15
+ /**
16
+ * Represents a third-level menu item.
17
+ */
10
18
  export interface TertiaryMenuItem {
19
+ /** The URL path for the link */
11
20
  link: string;
21
+ /** The text label to display */
12
22
  text: string;
13
23
  }
14
24
 
25
+ /**
26
+ * Represents a second-level menu item with optional nested tertiary items.
27
+ */
15
28
  export interface SecondaryMenuItem {
29
+ /** The URL path for the link */
16
30
  link: string;
31
+ /** The text label to display */
17
32
  text: string;
33
+ /** Optional nested tertiary menu items */
18
34
  submenu?: TertiaryMenuItem[];
19
35
  }
20
36
 
37
+ /**
38
+ * Represents a top-level menu item with optional nested secondary items.
39
+ */
21
40
  export interface MenuItem {
41
+ /** The URL path for the link */
22
42
  link: string;
43
+ /** The text label to display */
23
44
  text: string;
45
+ /** Optional nested secondary menu items */
24
46
  submenu?: SecondaryMenuItem[];
25
47
  }
26
48
 
49
+ /**
50
+ * Configuration for the site logo.
51
+ */
27
52
  export interface LogoConfig {
53
+ /**
54
+ * The URL of the logo image.
55
+ * @example "/assets/logo.png"
56
+ * @default "/logo.png"
57
+ */
28
58
  src?: string;
59
+ /** Alternative text for the logo image */
29
60
  alt?: string;
61
+ /**
62
+ * CSS width for the logo.
63
+ * @example "150px" or "4rem"
64
+ * @default "55px"
65
+ */
30
66
  width?: string;
67
+ /**
68
+ * Optional text to display next to the logo image.
69
+ * @example "My Awesome Site"
70
+ */
31
71
  text?: string;
72
+ /**
73
+ * CSS font-size for the logo text.
74
+ * @default "1em"
75
+ */
32
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
+ */
33
82
  textColor?: string;
34
83
  }
35
84
 
85
+ /**
86
+ * Configuration for the main navigation.
87
+ */
36
88
  export interface NavConfig {
89
+ /**
90
+ * The URL for the home link.
91
+ * @default "/"
92
+ */
37
93
  homeUrl?: string;
94
+ /**
95
+ * Array of top-level menu items.
96
+ * @example [{ link: "/about", text: "About Us" }]
97
+ */
38
98
  menuItems?: MenuItem[];
39
99
  }
40
100
 
101
+ /**
102
+ * Individual theme settings for a specific state (light/dark).
103
+ */
41
104
  export interface ThemeConfig {
105
+ /**
106
+ * Main background color. Supports hex, rgb, rgba, etc.
107
+ * @example "rgba(255, 255, 255, 0.9)"
108
+ */
42
109
  backgroundColor?: string;
110
+ /**
111
+ * Solid background color for submenus and mobile panels to ensure readability.
112
+ * @example "#ffffff"
113
+ */
43
114
  backgroundColorOpaque?: string;
115
+ /**
116
+ * CSS backdrop-filter blur value.
117
+ * @default "blur(20px)"
118
+ */
44
119
  backdropBlur?: string;
120
+ /**
121
+ * CSS z-index for the header container.
122
+ * @default 10
123
+ */
45
124
  zIndex?: number;
125
+ /**
126
+ * Primary text color for navigation and logo.
127
+ */
46
128
  textColor?: string;
129
+ /**
130
+ * Color for highlights, active states, underscores, and small borders.
131
+ */
47
132
  accentColor?: string;
48
133
  }
49
134
 
135
+ /**
136
+ * Combined theme configuration for both light and dark modes.
137
+ */
50
138
  export interface DualThemeConfig {
139
+ /** Settings applied when light mode is active. */
51
140
  light?: ThemeConfig;
141
+ /** Settings applied when dark mode is active. */
52
142
  dark?: ThemeConfig;
53
143
  }
54
144
 
145
+ /**
146
+ * Custom CSS class names for deep customization.
147
+ */
55
148
  export interface CustomClassNames {
149
+ /** Class for the outermost fixed container */
56
150
  container?: string;
151
+ /** Class for the main header element */
57
152
  header?: string;
153
+ /** Class for the logo anchor tag */
58
154
  logo?: string;
155
+ /** Class for the logo span text */
59
156
  logoText?: string;
157
+ /** Class for the desktop navigation wrapper */
60
158
  nav?: string;
61
159
  }
62
160
 
161
+ /**
162
+ * Main properties for the Header component.
163
+ */
63
164
  export interface HeaderProps {
165
+ /**
166
+ * Layout style.
167
+ * - "floating": Centered with max-width and rounded corners.
168
+ * - "fullscreen": Full width with no border radius.
169
+ * @default "floating"
170
+ */
64
171
  headerType?: "floating" | "fullscreen";
172
+ /**
173
+ * Theme behavior.
174
+ * - "light": Force light mode.
175
+ * - "dark": Force dark mode.
176
+ * - "auto": Detects .dark class on the root element.
177
+ * @default "auto"
178
+ */
65
179
  preset?: "light" | "dark" | "auto";
180
+ /** Logo configuration object. */
66
181
  logo?: LogoConfig;
182
+ /** Navigation links and structure. */
67
183
  navigation?: NavConfig;
184
+ /** Custom theme overrides. See @interface DualThemeConfig */
68
185
  theme?: DualThemeConfig;
186
+ /**
187
+ * Custom CSS classes for injecting utility classes (e.g., Tailwind).
188
+ * @example { header: "shadow-lg", logoText: "font-bold" }
189
+ */
69
190
  classNames?: CustomClassNames;
70
191
  }
71
192