astro-component-kit 0.3.1 → 0.4.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.
Files changed (249) hide show
  1. package/README.md +133 -36
  2. package/package.json +17 -4
  3. package/src/components/index.ts +18 -202
  4. package/src/components/lib/Galaxy3D.astro +249 -137
  5. package/src/components/lib/app/AppSidebar.astro +249 -0
  6. package/src/components/lib/app/AppTopbar.astro +284 -0
  7. package/src/components/lib/app/ArrivalsBoard.astro +176 -0
  8. package/src/components/lib/app/AuthCard.astro +213 -0
  9. package/src/components/lib/app/DataTable.astro +346 -0
  10. package/src/components/lib/app/GuestProfileCard.astro +133 -0
  11. package/src/components/lib/app/HousekeepingBoard.astro +189 -0
  12. package/src/components/lib/app/PageHeader.astro +157 -0
  13. package/src/components/lib/app/PlanUsageCard.astro +112 -0
  14. package/src/components/lib/app/RoomRack.astro +207 -0
  15. package/src/components/lib/app/SettingsSection.astro +96 -0
  16. package/src/components/lib/app/SwitchRow.astro +71 -0
  17. package/src/components/lib/app/TeamMembers.astro +190 -0
  18. package/src/components/lib/app/app-icons.ts +52 -0
  19. package/src/components/lib/app/index.ts +19 -0
  20. package/src/components/lib/buttons/NeonBorderButton.astro +162 -0
  21. package/src/components/lib/buttons/RippleButton.astro +199 -0
  22. package/src/components/lib/buttons/SlideRevealButton.astro +159 -0
  23. package/src/components/lib/buttons/index.ts +4 -0
  24. package/src/components/lib/cards/GithubRepoCard.astro +176 -0
  25. package/src/components/lib/cards/PricingCompareMiniCard.astro +196 -0
  26. package/src/components/lib/cards/StatsTrendCard.astro +149 -0
  27. package/src/components/lib/cards/index.ts +3 -0
  28. package/src/components/lib/charts/ActivityRings.astro +105 -0
  29. package/src/components/lib/charts/AreaChart.astro +4 -4
  30. package/src/components/lib/charts/BarChart.astro +3 -3
  31. package/src/components/lib/charts/BoxPlotChart.astro +109 -0
  32. package/src/components/lib/charts/BubbleChart.astro +101 -0
  33. package/src/components/lib/charts/BulletChart.astro +160 -0
  34. package/src/components/lib/charts/BumpChart.astro +92 -0
  35. package/src/components/lib/charts/CandlestickChart.astro +105 -0
  36. package/src/components/lib/charts/ChartFrame.astro +373 -0
  37. package/src/components/lib/charts/DivergingBarChart.astro +98 -0
  38. package/src/components/lib/charts/DonutChart.astro +2 -2
  39. package/src/components/lib/charts/DumbbellChart.astro +92 -0
  40. package/src/components/lib/charts/ForecastLineChart.astro +125 -0
  41. package/src/components/lib/charts/FunnelChart.astro +122 -0
  42. package/src/components/lib/charts/GaugeChart.astro +111 -0
  43. package/src/components/lib/charts/GoalProgressChart.astro +97 -0
  44. package/src/components/lib/charts/GroupedBarChart.astro +125 -0
  45. package/src/components/lib/charts/HeatmapChart.astro +131 -0
  46. package/src/components/lib/charts/HistogramChart.astro +111 -0
  47. package/src/components/lib/charts/HorizontalBarChart.astro +155 -0
  48. package/src/components/lib/charts/KpiStatTile.astro +116 -0
  49. package/src/components/lib/charts/KpiTrendGrid.astro +60 -0
  50. package/src/components/lib/charts/LineChart.astro +160 -0
  51. package/src/components/lib/charts/LinearProgressGauge.astro +136 -0
  52. package/src/components/lib/charts/LollipopChart.astro +100 -0
  53. package/src/components/lib/charts/MeterGauge.astro +122 -0
  54. package/src/components/lib/charts/MetricDeltaTable.astro +125 -0
  55. package/src/components/lib/charts/PieChart.astro +135 -0
  56. package/src/components/lib/charts/PolarAreaChart.astro +85 -0
  57. package/src/components/lib/charts/PopulationPyramid.astro +122 -0
  58. package/src/components/lib/charts/PunchCardChart.astro +89 -0
  59. package/src/components/lib/charts/RadarChart.astro +94 -0
  60. package/src/components/lib/charts/RadarMetricChart.astro +168 -0
  61. package/src/components/lib/charts/RadialBarChart.astro +124 -0
  62. package/src/components/lib/charts/RangeAreaChart.astro +113 -0
  63. package/src/components/lib/charts/RetentionCohortTable.astro +126 -0
  64. package/src/components/lib/charts/SankeyChart.astro +128 -0
  65. package/src/components/lib/charts/ScatterPlot.astro +139 -0
  66. package/src/components/lib/charts/SegmentedBarChart.astro +119 -0
  67. package/src/components/lib/charts/SlopeChart.astro +105 -0
  68. package/src/components/lib/charts/SparklineChart.astro +87 -0
  69. package/src/components/lib/charts/StackedAreaChart.astro +130 -0
  70. package/src/components/lib/charts/StackedBarChart.astro +136 -0
  71. package/src/components/lib/charts/SunburstChart.astro +103 -0
  72. package/src/components/lib/charts/TreemapChart.astro +94 -0
  73. package/src/components/lib/charts/WaffleChart.astro +121 -0
  74. package/src/components/lib/charts/WaterfallChart.astro +118 -0
  75. package/src/components/lib/charts/chart-tooltip.ts +75 -0
  76. package/src/components/lib/charts/chart-utils.ts +271 -0
  77. package/src/components/lib/charts/index.ts +46 -0
  78. package/src/components/lib/data/ActivityHeatmapGrid.astro +157 -0
  79. package/src/components/lib/data/MetricComparisonRow.astro +109 -0
  80. package/src/components/lib/data/StatusTimelineFeed.astro +168 -0
  81. package/src/components/lib/data/index.ts +3 -0
  82. package/src/components/lib/ecommerce/CartDrawer.astro +483 -0
  83. package/src/components/lib/ecommerce/CartPageList.astro +290 -0
  84. package/src/components/lib/ecommerce/CartToast.astro +276 -0
  85. package/src/components/lib/ecommerce/CategoryTiles.astro +143 -0
  86. package/src/components/lib/ecommerce/CheckoutForm.astro +423 -0
  87. package/src/components/lib/ecommerce/CountdownBanner.astro +230 -0
  88. package/src/components/lib/ecommerce/FilterBar.astro +278 -0
  89. package/src/components/lib/ecommerce/FilterSidebar.astro +405 -0
  90. package/src/components/lib/ecommerce/LiveActivityToast.astro +213 -0
  91. package/src/components/lib/ecommerce/OrderSummary.astro +295 -0
  92. package/src/components/lib/ecommerce/ProductBuyBox.astro +247 -0
  93. package/src/components/lib/ecommerce/ProductCardMinimal.astro +229 -0
  94. package/src/components/lib/ecommerce/ProductGallery.astro +145 -0
  95. package/src/components/lib/ecommerce/ProductGrid.astro +118 -0
  96. package/src/components/lib/ecommerce/ProductMedia.astro +113 -0
  97. package/src/components/lib/ecommerce/ProductRating.astro +78 -0
  98. package/src/components/lib/ecommerce/ProductToolbar.astro +93 -0
  99. package/src/components/lib/ecommerce/PromoBar.astro +145 -0
  100. package/src/components/lib/ecommerce/StockIndicator.astro +127 -0
  101. package/src/components/lib/ecommerce/StoreHeader.astro +446 -0
  102. package/src/components/lib/ecommerce/StoreHero.astro +260 -0
  103. package/src/components/lib/ecommerce/StoreProductCard.astro +307 -0
  104. package/src/components/lib/ecommerce/TrustBadges.astro +101 -0
  105. package/src/components/lib/ecommerce/VariantPicker.astro +145 -0
  106. package/src/components/lib/ecommerce/cart-store.ts +151 -0
  107. package/src/components/lib/ecommerce/catalog-filter.ts +62 -0
  108. package/src/components/lib/ecommerce/index.ts +34 -0
  109. package/src/components/lib/ecommerce/product-icons.ts +28 -0
  110. package/src/components/lib/ecommerce/types.ts +70 -0
  111. package/src/components/lib/feedback/ProgressRingCounter.astro +118 -0
  112. package/src/components/lib/feedback/ToastNotificationGroup.astro +173 -0
  113. package/src/components/lib/feedback/index.ts +2 -0
  114. package/src/components/lib/forms/AnimatedInput.astro +4 -2
  115. package/src/components/lib/forms/CustomRadioGroup.astro +3 -1
  116. package/src/components/lib/forms/GlassInlineToggleGroup.astro +161 -0
  117. package/src/components/lib/forms/PasswordStrengthInput.astro +242 -0
  118. package/src/components/lib/forms/PhoneCountryInput.astro +145 -0
  119. package/src/components/lib/forms/TagsAutocompleteInput.astro +249 -0
  120. package/src/components/lib/forms/index.ts +5 -0
  121. package/src/components/lib/index.ts +2 -0
  122. package/src/components/lib/layout/BentoGrid3.astro +6 -4
  123. package/src/components/lib/layout/CenterFixedBox.astro +2 -2
  124. package/src/components/lib/layout/CenteredContainer.astro +4 -2
  125. package/src/components/lib/layout/ClusterHorizontal.astro +2 -2
  126. package/src/components/lib/layout/DiagonalSectionDivider.astro +69 -0
  127. package/src/components/lib/layout/FeatureHero.astro +4 -2
  128. package/src/components/lib/layout/FooterCenteredMinimal.astro +4 -2
  129. package/src/components/lib/layout/FullBleedSection.astro +2 -2
  130. package/src/components/lib/layout/GalleryGrid4.astro +2 -2
  131. package/src/components/lib/layout/HolyGrailGrid.astro +127 -0
  132. package/src/components/lib/layout/LandingSectionSplit.astro +5 -3
  133. package/src/components/lib/layout/MasonryGrid.astro +2 -2
  134. package/src/components/lib/layout/MinimalFooterGrid.astro +1 -1
  135. package/src/components/lib/layout/ScrollSnapSection.astro +1 -1
  136. package/src/components/lib/layout/SidebarLayoutWrapper.astro +4 -2
  137. package/src/components/lib/layout/SplitScreenLayout.astro +3 -3
  138. package/src/components/lib/layout/StepLayoutVertical.astro +1 -1
  139. package/src/components/lib/layout/StickySidebarWrapper.astro +4 -2
  140. package/src/components/lib/layout/index.ts +3 -1
  141. package/src/components/lib/navigation/QuickJumpDock.astro +125 -0
  142. package/src/components/lib/navigation/StepBreadcrumbBar.astro +151 -0
  143. package/src/components/lib/navigation/SubNavbarPills.astro +102 -0
  144. package/src/components/lib/navigation/index.ts +3 -0
  145. package/src/components/lib/overlays/CookiePreferencesDrawer.astro +226 -0
  146. package/src/components/lib/overlays/FloatingFeedbackWidget.astro +203 -0
  147. package/src/components/lib/overlays/ImageCompareSlider.astro +172 -0
  148. package/src/components/lib/overlays/index.ts +3 -0
  149. package/src/components/lib/sections/AgencyContactBrief.astro +194 -0
  150. package/src/components/lib/sections/AgencyFooterCta.astro +94 -0
  151. package/src/components/lib/sections/AgencyHeader.astro +161 -0
  152. package/src/components/lib/sections/AgencyHeroKinetic.astro +172 -0
  153. package/src/components/lib/sections/AgencyServicesList.astro +113 -0
  154. package/src/components/lib/sections/AgencyTeamGrid.astro +111 -0
  155. package/src/components/lib/sections/AgencyWorkGrid.astro +147 -0
  156. package/src/components/lib/sections/BlogGridSection.astro +133 -0
  157. package/src/components/lib/sections/ButtonsShowcase.astro +20 -18
  158. package/src/components/lib/sections/CardsShowcase.astro +28 -26
  159. package/src/components/lib/sections/CareersOpenings.astro +141 -0
  160. package/src/components/lib/sections/ChangelogSection.astro +151 -0
  161. package/src/components/lib/sections/ContactGlassForm.astro +10 -8
  162. package/src/components/lib/sections/ContactSplitMap.astro +141 -0
  163. package/src/components/lib/sections/CtaAppDownload.astro +13 -11
  164. package/src/components/lib/sections/CtaGlowBanner.astro +12 -10
  165. package/src/components/lib/sections/CtaNewsletterSection.astro +165 -0
  166. package/src/components/lib/sections/CtaSplitImage.astro +149 -0
  167. package/src/components/lib/sections/DataShowcase.astro +23 -21
  168. package/src/components/lib/sections/FaqAccordionSection.astro +8 -8
  169. package/src/components/lib/sections/FaqTwoColumn.astro +121 -0
  170. package/src/components/lib/sections/FeatureAlternatingRows.astro +192 -0
  171. package/src/components/lib/sections/FeatureBentoGrid.astro +13 -11
  172. package/src/components/lib/sections/FeatureIconGrid.astro +155 -0
  173. package/src/components/lib/sections/FeatureTabsShowcase.astro +182 -0
  174. package/src/components/lib/sections/FeatureTimelineFlow.astro +8 -6
  175. package/src/components/lib/sections/FeedbackShowcase.astro +15 -13
  176. package/src/components/lib/sections/FooterMegaSection.astro +157 -0
  177. package/src/components/lib/sections/FormsShowcase.astro +22 -20
  178. package/src/components/lib/sections/HeroDashboardPreview.astro +182 -0
  179. package/src/components/lib/sections/HeroGlass.astro +22 -20
  180. package/src/components/lib/sections/HeroGradient.astro +15 -13
  181. package/src/components/lib/sections/HeroMinimalCentered.astro +175 -0
  182. package/src/components/lib/sections/HeroShowcaseSplit.astro +306 -0
  183. package/src/components/lib/sections/HeroSplit.astro +13 -11
  184. package/src/components/lib/sections/HeroTerminal.astro +253 -0
  185. package/src/components/lib/sections/HeroWaitlist.astro +231 -0
  186. package/src/components/lib/sections/HowItWorksSteps.astro +180 -0
  187. package/src/components/lib/sections/IntegrationsGrid.astro +167 -0
  188. package/src/components/lib/sections/LayoutShowcase.astro +21 -19
  189. package/src/components/lib/sections/LogoCloudSection.astro +96 -0
  190. package/src/components/lib/sections/LogoWallGrid.astro +93 -0
  191. package/src/components/lib/sections/NavigationShowcase.astro +15 -13
  192. package/src/components/lib/sections/OverlaysShowcase.astro +21 -19
  193. package/src/components/lib/sections/PricingComparisonMatrix.astro +15 -13
  194. package/src/components/lib/sections/PricingSinglePlan.astro +159 -0
  195. package/src/components/lib/sections/PricingTierCards.astro +11 -9
  196. package/src/components/lib/sections/PricingToggleCards.astro +171 -0
  197. package/src/components/lib/sections/ShopCategoryHero.astro +103 -0
  198. package/src/components/lib/sections/ShopCollectionMosaic.astro +131 -0
  199. package/src/components/lib/sections/ShopFooterWordmark.astro +100 -0
  200. package/src/components/lib/sections/ShopHeaderEditorial.astro +396 -0
  201. package/src/components/lib/sections/ShopHeroEditorial.astro +151 -0
  202. package/src/components/lib/sections/ShopLookbook.astro +250 -0
  203. package/src/components/lib/sections/ShopNewsletterEditorial.astro +128 -0
  204. package/src/components/lib/sections/ShopProductRail.astro +150 -0
  205. package/src/components/lib/sections/ShopReviewsEditorial.astro +133 -0
  206. package/src/components/lib/sections/ShopValuesMarquee.astro +91 -0
  207. package/src/components/lib/sections/StatsCounterBand.astro +124 -0
  208. package/src/components/lib/sections/StatsDashboardSection.astro +85 -0
  209. package/src/components/lib/sections/StatsMetricShowcase.astro +9 -7
  210. package/src/components/lib/sections/TeamGridSection.astro +12 -10
  211. package/src/components/lib/sections/TestimonialCarousel.astro +167 -0
  212. package/src/components/lib/sections/TestimonialFeatured.astro +122 -0
  213. package/src/components/lib/sections/TestimonialGridSection.astro +188 -0
  214. package/src/components/lib/sections/TestimonialWallGlass.astro +11 -9
  215. package/src/components/lib/sections/TypographyShowcase.astro +10 -8
  216. package/src/components/lib/sections/index.ts +39 -0
  217. package/src/components/lib/templates/ecommerce/StoreCategoryPage.astro +160 -0
  218. package/src/components/lib/templates/ecommerce/StoreCheckoutPage.astro +98 -0
  219. package/src/components/lib/templates/ecommerce/StoreHomePage.astro +191 -0
  220. package/src/components/lib/templates/ecommerce/StoreLayout.astro +169 -0
  221. package/src/components/lib/templates/ecommerce/StoreProductPage.astro +165 -0
  222. package/src/components/lib/templates/ecommerce/data.ts +187 -0
  223. package/src/components/lib/templates/ecommerce/index.ts +14 -0
  224. package/src/components/lib/templates/ecommerce-editorial/EditorialBagPage.astro +52 -0
  225. package/src/components/lib/templates/ecommerce-editorial/EditorialCheckoutPage.astro +123 -0
  226. package/src/components/lib/templates/ecommerce-editorial/EditorialHomePage.astro +102 -0
  227. package/src/components/lib/templates/ecommerce-editorial/EditorialLayout.astro +216 -0
  228. package/src/components/lib/templates/ecommerce-editorial/EditorialProductPage.astro +205 -0
  229. package/src/components/lib/templates/ecommerce-editorial/EditorialShopPage.astro +112 -0
  230. package/src/components/lib/templates/ecommerce-editorial/data.ts +125 -0
  231. package/src/components/lib/templates/ecommerce-editorial/index.ts +16 -0
  232. package/src/components/lib/templates/saas-hotel/AppLayout.astro +83 -0
  233. package/src/components/lib/templates/saas-hotel/DashboardPage.astro +66 -0
  234. package/src/components/lib/templates/saas-hotel/GuestsPage.astro +65 -0
  235. package/src/components/lib/templates/saas-hotel/LandingPage.astro +213 -0
  236. package/src/components/lib/templates/saas-hotel/ReportsPage.astro +63 -0
  237. package/src/components/lib/templates/saas-hotel/ReservationsPage.astro +49 -0
  238. package/src/components/lib/templates/saas-hotel/RoomsPage.astro +48 -0
  239. package/src/components/lib/templates/saas-hotel/SettingsPage.astro +115 -0
  240. package/src/components/lib/templates/saas-hotel/SignInPage.astro +50 -0
  241. package/src/components/lib/templates/saas-hotel/data.ts +229 -0
  242. package/src/components/lib/templates/saas-hotel/index.ts +19 -0
  243. package/src/components/lib/templates/saas-hotel/lobby-theme.css +90 -0
  244. package/src/components/lib/typography/AnimatedCounterText.astro +102 -0
  245. package/src/components/lib/typography/CurvedPathHeading.astro +86 -0
  246. package/src/components/lib/typography/GradientTextMarquee.astro +105 -0
  247. package/src/components/lib/typography/HackerDecodeText.astro +85 -0
  248. package/src/components/lib/typography/index.ts +4 -0
  249. package/src/styles/global.css +113 -1
@@ -0,0 +1,446 @@
1
+ ---
2
+ /**
3
+ * StoreHeader — Shop navigation bar: brand, menu with category dropdowns, search,
4
+ * account link and a cart button whose badge follows the cart store live.
5
+ *
6
+ * @param {string} brand - Store name. Default 'Nova Store'.
7
+ * @param {string} brandHref - Brand link. Default '/'.
8
+ * @param {Array} links - Menu items `{ label, href, children?: { label, href, description? }[] }`.
9
+ * @param {string} searchAction - Search form action URL. Default '#'.
10
+ * @param {string} searchPlaceholder - Search placeholder. Default 'Search products'.
11
+ * @param {string} accountHref - Account link (hidden when empty). Default '#'.
12
+ * @param {string} cartKey - Cart storage key. Default 'ack-cart'.
13
+ * @param {string} cartLabel - Accessible label for the cart button. Default 'Cart'.
14
+ * @param {string} currentHref - Marks the matching menu link with aria-current.
15
+ * @param {boolean} sticky - Stick to the top on scroll. Default true.
16
+ * @param {string} variant - Style (glass|solid|transparent). Default 'glass'.
17
+ * @param {string} size - Height (sm|md|lg). Default 'md'.
18
+ * @param {boolean} themeToggle - Show a light/dark toggle (sets html[data-theme], remembered in localStorage). Default true.
19
+ * @param {string} id - Base id for inner elements (unique per page). Default 'store-header'.
20
+ * @param {string} className - Extra class.
21
+ *
22
+ * Accessibility: dropdowns are disclosure buttons (aria-expanded) closed with Escape or outside click;
23
+ * the mobile menu is a disclosure too; the cart badge count is part of the button label.
24
+ * Clicking the cart button dispatches `ack:cart-open`, which CartDrawer listens to.
25
+ */
26
+ interface ChildLink { label: string; href: string; description?: string }
27
+ interface NavLink { label: string; href: string; children?: ChildLink[] }
28
+
29
+ interface Props {
30
+ brand?: string;
31
+ brandHref?: string;
32
+ links?: NavLink[];
33
+ searchAction?: string;
34
+ searchPlaceholder?: string;
35
+ accountHref?: string;
36
+ cartKey?: string;
37
+ cartLabel?: string;
38
+ currentHref?: string;
39
+ sticky?: boolean;
40
+ variant?: 'glass' | 'solid' | 'transparent';
41
+ size?: 'sm' | 'md' | 'lg';
42
+ themeToggle?: boolean;
43
+ id?: string;
44
+ className?: string;
45
+ }
46
+
47
+ const {
48
+ brand = 'Nova Store',
49
+ brandHref = '/',
50
+ links = [
51
+ {
52
+ label: 'Shop',
53
+ href: '#',
54
+ children: [
55
+ { label: 'Audio', href: '#', description: 'Headphones & speakers' },
56
+ { label: 'Wearables', href: '#', description: 'Watches & bands' },
57
+ { label: 'Bags', href: '#', description: 'Backpacks & totes' },
58
+ { label: 'Home', href: '#', description: 'Lamps, mugs & plants' },
59
+ ],
60
+ },
61
+ { label: 'New in', href: '#' },
62
+ { label: 'Sale', href: '#' },
63
+ { label: 'Journal', href: '#' },
64
+ ],
65
+ searchAction = '#',
66
+ searchPlaceholder = 'Search products',
67
+ accountHref = '#',
68
+ cartKey = 'ack-cart',
69
+ cartLabel = 'Cart',
70
+ currentHref,
71
+ sticky = true,
72
+ variant = 'glass',
73
+ size = 'md',
74
+ themeToggle = true,
75
+ id = 'store-header',
76
+ className = '',
77
+ } = Astro.props;
78
+
79
+ const uid = id;
80
+ ---
81
+
82
+ <header class:list={['store-header', `store-header--${variant}`, `store-header--${size}`, { 'store-header--sticky': sticky }, className]} data-store-header>
83
+ <div class="store-header__inner">
84
+ {links.length > 0 && (
85
+ <button class="store-header__toggle" type="button" aria-expanded="false" aria-controls={`${uid}-nav`} aria-label="Menu" data-menu-toggle>
86
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path class="l1" d="M4 7h16"/><path class="l2" d="M4 12h16"/><path class="l3" d="M4 17h16"/></svg>
87
+ </button>
88
+ )}
89
+
90
+ <a class="store-header__brand" href={brandHref}>
91
+ <span class="store-header__logo" aria-hidden="true">
92
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2 3 7v10l9 5 9-5V7z"/><path d="m3 7 9 5 9-5M12 12v10"/></svg>
93
+ </span>
94
+ <span>{brand}</span>
95
+ </a>
96
+
97
+ {links.length > 0 ? (
98
+ <nav class="store-header__nav" id={`${uid}-nav`} aria-label="Main" data-menu>
99
+ <ul class="store-header__links">
100
+ {links.map((link, i) => (
101
+ <li class="store-header__item">
102
+ {link.children?.length ? (
103
+ <>
104
+ <button class="store-header__link store-header__link--parent" type="button" aria-expanded="false" aria-controls={`${uid}-dd-${i}`} data-dropdown-toggle>
105
+ {link.label}
106
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>
107
+ </button>
108
+ <div class="store-header__dropdown" id={`${uid}-dd-${i}`} hidden data-dropdown>
109
+ <ul>
110
+ {link.children.map((child) => (
111
+ <li>
112
+ <a href={child.href} aria-current={currentHref === child.href ? 'page' : undefined}>
113
+ <strong>{child.label}</strong>
114
+ {child.description && <span>{child.description}</span>}
115
+ </a>
116
+ </li>
117
+ ))}
118
+ <li class="store-header__dropdown-all"><a href={link.href}>View all →</a></li>
119
+ </ul>
120
+ </div>
121
+ </>
122
+ ) : (
123
+ <a class="store-header__link" href={link.href} aria-current={currentHref === link.href ? 'page' : undefined}>{link.label}</a>
124
+ )}
125
+ </li>
126
+ ))}
127
+ </ul>
128
+ </nav>
129
+ ) : <div class="store-header__nav" />}
130
+
131
+ <div class="store-header__actions">
132
+ <form class="store-header__search" role="search" action={searchAction}>
133
+ <label class="sr-only" for={`${uid}-q`}>{searchPlaceholder}</label>
134
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
135
+ <input id={`${uid}-q`} name="q" type="search" placeholder={searchPlaceholder} autocomplete="off" />
136
+ </form>
137
+ {themeToggle && (
138
+ <button class="store-header__icon store-header__theme" type="button" aria-label="Switch to light theme" data-theme-toggle>
139
+ <svg class="i-sun" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
140
+ <svg class="i-moon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
141
+ </button>
142
+ )}
143
+ {accountHref && (
144
+ <a class="store-header__icon" href={accountHref} aria-label="Account">
145
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg>
146
+ </a>
147
+ )}
148
+ <button class="store-header__icon store-header__cart" type="button" data-cart-open data-cart-key={cartKey} data-cart-label={cartLabel} aria-label={cartLabel}>
149
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 7h12l-1 13H7z"/><path d="M9 7V6a3 3 0 0 1 6 0v1"/></svg>
150
+ <span class="store-header__count" data-cart-count hidden aria-hidden="true">0</span>
151
+ </button>
152
+ </div>
153
+ </div>
154
+ </header>
155
+
156
+ <script>
157
+ import { createCart } from './cart-store';
158
+
159
+ document.querySelectorAll<HTMLElement>('[data-store-header]:not([data-bound])').forEach((header) => {
160
+ header.dataset.bound = '';
161
+ const toggle = header.querySelector<HTMLButtonElement>('[data-menu-toggle]');
162
+ const menu = header.querySelector<HTMLElement>('[data-menu]');
163
+ const dropdownToggles = Array.from(header.querySelectorAll<HTMLButtonElement>('[data-dropdown-toggle]'));
164
+
165
+ const closeDropdowns = (except?: HTMLButtonElement) => {
166
+ dropdownToggles.forEach((btn) => {
167
+ if (btn === except) return;
168
+ btn.setAttribute('aria-expanded', 'false');
169
+ const panel = document.getElementById(btn.getAttribute('aria-controls') || '');
170
+ if (panel) panel.hidden = true;
171
+ });
172
+ };
173
+
174
+ dropdownToggles.forEach((btn) => {
175
+ btn.addEventListener('click', () => {
176
+ const open = btn.getAttribute('aria-expanded') !== 'true';
177
+ closeDropdowns(btn);
178
+ btn.setAttribute('aria-expanded', String(open));
179
+ const panel = document.getElementById(btn.getAttribute('aria-controls') || '');
180
+ if (panel) panel.hidden = !open;
181
+ });
182
+ });
183
+
184
+ toggle?.addEventListener('click', () => {
185
+ const open = toggle.getAttribute('aria-expanded') !== 'true';
186
+ toggle.setAttribute('aria-expanded', String(open));
187
+ menu?.classList.toggle('is-open', open);
188
+ });
189
+
190
+ document.addEventListener('click', (e) => {
191
+ if (!header.contains(e.target as Node)) closeDropdowns();
192
+ });
193
+ document.addEventListener('keydown', (e) => {
194
+ if (e.key !== 'Escape') return;
195
+ const openBtn = dropdownToggles.find((b) => b.getAttribute('aria-expanded') === 'true');
196
+ closeDropdowns();
197
+ openBtn?.focus();
198
+ if (toggle?.getAttribute('aria-expanded') === 'true') {
199
+ toggle.setAttribute('aria-expanded', 'false');
200
+ menu?.classList.remove('is-open');
201
+ toggle.focus();
202
+ }
203
+ });
204
+
205
+ const themeBtn = header.querySelector<HTMLButtonElement>('[data-theme-toggle]');
206
+ if (themeBtn) {
207
+ const root = document.documentElement;
208
+ const current = () => (root.dataset.theme === 'light' ? 'light' : 'dark');
209
+ const label = () => themeBtn.setAttribute('aria-label', current() === 'light' ? 'Switch to dark theme' : 'Switch to light theme');
210
+ label();
211
+ themeBtn.addEventListener('click', () => {
212
+ const next = current() === 'light' ? 'dark' : 'light';
213
+ root.dataset.theme = next;
214
+ try { window.localStorage.setItem('ack-theme', next); } catch { /* ignore */ }
215
+ label();
216
+ document.dispatchEvent(new CustomEvent('ack:theme', { detail: { theme: next } }));
217
+ });
218
+ }
219
+
220
+ const cartBtn = header.querySelector<HTMLButtonElement>('[data-cart-open]');
221
+ const countEl = header.querySelector<HTMLElement>('[data-cart-count]');
222
+ if (cartBtn && countEl) {
223
+ const cart = createCart(cartBtn.dataset.cartKey);
224
+ const label = cartBtn.dataset.cartLabel || 'Cart';
225
+ const render = () => {
226
+ const n = cart.count();
227
+ countEl.textContent = n > 99 ? '99+' : String(n);
228
+ countEl.hidden = n === 0;
229
+ cartBtn.setAttribute('aria-label', n ? `${label} (${n})` : label);
230
+ };
231
+ render();
232
+ cart.subscribe((_items) => {
233
+ render();
234
+ countEl.classList.remove('is-bump');
235
+ void countEl.offsetWidth;
236
+ countEl.classList.add('is-bump');
237
+ });
238
+ cartBtn.addEventListener('click', () => {
239
+ document.dispatchEvent(new CustomEvent('ack:cart-open', { detail: { key: cart.key, trigger: cartBtn } }));
240
+ });
241
+ }
242
+ });
243
+ </script>
244
+
245
+ <style>
246
+ .store-header {
247
+ container-type: inline-size;
248
+ --sh-h: 68px;
249
+ position: relative;
250
+ z-index: 50;
251
+ width: 100%;
252
+ color: var(--c-text-1, #fff);
253
+ border-bottom: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.05));
254
+ }
255
+ .store-header--sm { --sh-h: 56px; }
256
+ .store-header--lg { --sh-h: 80px; }
257
+ .store-header--sticky { position: sticky; top: 0; }
258
+ .store-header--glass { background: rgb(var(--paper-rgb, 5 5 5) / 0.72); backdrop-filter: blur(18px) saturate(1.4); }
259
+ .store-header--solid { background: var(--c-bg-1, #0a0a0a); }
260
+ .store-header--transparent { background: transparent; border-bottom-color: transparent; }
261
+
262
+ .store-header__inner {
263
+ display: flex;
264
+ align-items: center;
265
+ gap: var(--sp-6, 1.5rem);
266
+ max-width: 1280px;
267
+ height: var(--sh-h);
268
+ margin: 0 auto;
269
+ padding: 0 var(--sp-6, 1.5rem);
270
+ }
271
+
272
+ .store-header__brand {
273
+ display: inline-flex;
274
+ align-items: center;
275
+ gap: var(--sp-2, 0.5rem);
276
+ font-weight: 800;
277
+ font-size: 1.05rem;
278
+ letter-spacing: -0.02em;
279
+ color: var(--c-text-1, #fff);
280
+ text-decoration: none;
281
+ white-space: nowrap;
282
+ }
283
+ .store-header__logo {
284
+ display: grid;
285
+ place-items: center;
286
+ width: 34px;
287
+ height: 34px;
288
+ border-radius: 10px;
289
+ background: linear-gradient(135deg, var(--c-primary, #b8860b), var(--c-primary-light, #d4a017));
290
+ color: var(--c-on-primary, #050505);
291
+ }
292
+
293
+ .store-header__nav { flex: 1; }
294
+ .store-header__links { display: flex; align-items: center; gap: var(--sp-1, 0.25rem); margin: 0; padding: 0; list-style: none; }
295
+ .store-header__item { position: relative; }
296
+ .store-header__link {
297
+ display: inline-flex;
298
+ align-items: center;
299
+ gap: 0.35rem;
300
+ min-height: 40px;
301
+ padding: 0 var(--sp-3, 0.75rem);
302
+ border: 0;
303
+ border-radius: var(--r-full, 9999px);
304
+ background: transparent;
305
+ color: var(--c-text-2, #a3a3a3);
306
+ font: inherit;
307
+ font-size: 0.9rem;
308
+ font-weight: 600;
309
+ text-decoration: none;
310
+ cursor: pointer;
311
+ transition: color 0.2s ease, background 0.2s ease;
312
+ }
313
+ .store-header__link:hover,
314
+ .store-header__link[aria-expanded='true'],
315
+ .store-header__link[aria-current='page'] { color: var(--c-text-1, #fff); background: rgb(var(--ink-rgb, 255 255 255) / 0.05); }
316
+ .store-header__link svg { transition: transform 0.2s ease; }
317
+ .store-header__link[aria-expanded='true'] svg { transform: rotate(180deg); }
318
+
319
+ .store-header__dropdown {
320
+ position: absolute;
321
+ top: calc(100% + 10px);
322
+ left: 0;
323
+ min-width: 280px;
324
+ padding: var(--sp-2, 0.5rem);
325
+ border-radius: var(--r-md, 12px);
326
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
327
+ background: rgb(var(--paper-rgb, 5 5 5) / 0.96);
328
+ backdrop-filter: blur(18px);
329
+ box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.8);
330
+ animation: sh-pop 0.18s ease-out;
331
+ }
332
+ .store-header__dropdown ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 2px; }
333
+ .store-header__dropdown a {
334
+ display: grid;
335
+ gap: 2px;
336
+ padding: var(--sp-2, 0.5rem) var(--sp-3, 0.75rem);
337
+ border-radius: var(--r-sm, 8px);
338
+ color: var(--c-text-1, #fff);
339
+ text-decoration: none;
340
+ font-size: 0.9rem;
341
+ }
342
+ .store-header__dropdown a span { font-size: 0.78rem; color: var(--c-text-2, #a3a3a3); }
343
+ .store-header__dropdown a:hover { background: rgb(var(--ink-rgb, 255 255 255) / 0.05); }
344
+ .store-header__dropdown-all { border-top: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.05)); margin-top: 4px; padding-top: 4px; }
345
+ .store-header__dropdown-all a { color: var(--c-primary-light, #d4a017); font-weight: 600; }
346
+ @keyframes sh-pop { from { opacity: 0; transform: translateY(-4px); } }
347
+
348
+ .store-header__actions { display: flex; align-items: center; gap: var(--sp-2, 0.5rem); margin-left: auto; }
349
+ .store-header__search {
350
+ display: flex;
351
+ align-items: center;
352
+ gap: var(--sp-2, 0.5rem);
353
+ height: 40px;
354
+ padding: 0 var(--sp-3, 0.75rem);
355
+ border-radius: var(--r-full, 9999px);
356
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
357
+ background: rgb(var(--ink-rgb, 255 255 255) / 0.03);
358
+ color: var(--c-text-2, #a3a3a3);
359
+ transition: border-color 0.2s ease;
360
+ }
361
+ .store-header__search:focus-within { border-color: var(--c-primary-light, #d4a017); }
362
+ .store-header__search input {
363
+ width: 170px;
364
+ border: 0;
365
+ outline: 0;
366
+ background: transparent;
367
+ color: var(--c-text-1, #fff);
368
+ font: inherit;
369
+ font-size: 0.875rem;
370
+ }
371
+ .store-header__search input::placeholder { color: var(--c-text-2, #a3a3a3); }
372
+
373
+ .store-header__icon,
374
+ .store-header__toggle {
375
+ position: relative;
376
+ display: grid;
377
+ place-items: center;
378
+ width: 44px;
379
+ height: 44px;
380
+ border: 0;
381
+ border-radius: var(--r-full, 9999px);
382
+ background: transparent;
383
+ color: var(--c-text-1, #fff);
384
+ cursor: pointer;
385
+ transition: background 0.2s ease;
386
+ }
387
+ .store-header__icon:hover,
388
+ .store-header__toggle:hover { background: rgb(var(--ink-rgb, 255 255 255) / 0.06); color: var(--c-text-1, #fff); }
389
+ .store-header__toggle { display: none; }
390
+ .store-header__theme .i-moon { display: none; }
391
+ :global([data-theme='light']) .store-header__theme .i-sun { display: none; }
392
+ :global([data-theme='light']) .store-header__theme .i-moon { display: block; }
393
+ .store-header :is(a, button, input):focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; }
394
+
395
+ .store-header__count {
396
+ position: absolute;
397
+ top: 4px;
398
+ right: 2px;
399
+ min-width: 18px;
400
+ height: 18px;
401
+ padding: 0 5px;
402
+ border-radius: var(--r-full, 9999px);
403
+ background: var(--c-primary-light, #d4a017);
404
+ color: var(--c-on-primary, #050505);
405
+ font-size: 0.68rem;
406
+ font-weight: 800;
407
+ line-height: 18px;
408
+ text-align: center;
409
+ }
410
+ .store-header__count[hidden] { display: none; }
411
+ .store-header__count.is-bump { animation: sh-bump 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); }
412
+ @keyframes sh-bump { 40% { transform: scale(1.35); } }
413
+
414
+ .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
415
+
416
+ @container (max-width: 960px) {
417
+ .store-header__inner { gap: var(--sp-2, 0.5rem); padding: 0 var(--sp-3, 0.75rem); }
418
+ .store-header__toggle { display: grid; }
419
+ .store-header__search { display: none; }
420
+ .store-header__nav {
421
+ position: absolute;
422
+ top: 100%;
423
+ left: 0;
424
+ right: 0;
425
+ display: none;
426
+ padding: var(--sp-3, 0.75rem);
427
+ background: var(--c-bg-1, #0a0a0a);
428
+ box-shadow: 0 30px 60px -20px rgba(0, 0, 0, 0.9);
429
+ border-bottom: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.05));
430
+ max-height: calc(100dvh - var(--sh-h));
431
+ overflow-y: auto;
432
+ }
433
+ .store-header__nav.is-open { display: block; }
434
+ .store-header__links { flex-direction: column; align-items: stretch; }
435
+ .store-header__link { width: 100%; justify-content: space-between; min-height: 48px; border-radius: var(--r-sm, 8px); }
436
+ .store-header__dropdown { position: static; min-width: 0; box-shadow: none; background: rgb(var(--ink-rgb, 255 255 255) / 0.02); margin: 4px 0; }
437
+ .store-header__toggle[aria-expanded='true'] .l1 { transform: translateY(5px) rotate(45deg); transform-origin: center; }
438
+ .store-header__toggle[aria-expanded='true'] .l2 { opacity: 0; }
439
+ .store-header__toggle[aria-expanded='true'] .l3 { transform: translateY(-5px) rotate(-45deg); transform-origin: center; }
440
+ .store-header__toggle path { transition: transform 0.2s ease, opacity 0.2s ease; transform-box: fill-box; }
441
+ }
442
+
443
+ @media (prefers-reduced-motion: reduce) {
444
+ .store-header__dropdown, .store-header__count.is-bump { animation: none; }
445
+ }
446
+ </style>