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,155 @@
1
+ ---
2
+ /**
3
+ * FeatureIconGrid — Classic 3-column feature grid with SVG icons, titles,
4
+ * descriptions and optional "learn more" links. Cards light up on hover/focus.
5
+ *
6
+ * @param {string} eyebrow - Small label above the heading.
7
+ * @param {string} title - Section heading.
8
+ * @param {string} subtitle - Section description.
9
+ * @param {Array<{icon: string, title: string, text: string, href?: string}>} features - Feature cards.
10
+ * `icon` is one of: bolt, shield, layers, chart, globe, code, sparkles, lock, users.
11
+ * @param {number} columns - Max columns on desktop (2|3|4).
12
+ * @param {string} variant - Card style (card|plain|bordered).
13
+ * @param {string} size - Vertical rhythm (sm|md|lg).
14
+ * @param {string} className - Extra CSS class.
15
+ */
16
+ interface Feature { icon: string; title: string; text: string; href?: string }
17
+ interface Props {
18
+ eyebrow?: string;
19
+ title?: string;
20
+ subtitle?: string;
21
+ features?: Feature[];
22
+ columns?: 2 | 3 | 4;
23
+ variant?: 'card' | 'plain' | 'bordered';
24
+ size?: 'sm' | 'md' | 'lg';
25
+ className?: string;
26
+ }
27
+
28
+ const icons: Record<string, string> = {
29
+ bolt: '<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>',
30
+ shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/>',
31
+ layers: '<path d="m12 2 10 5-10 5L2 7l10-5z"/><path d="m2 17 10 5 10-5"/><path d="m2 12 10 5 10-5"/>',
32
+ chart: '<path d="M3 3v18h18"/><path d="m19 9-5 5-4-4-3 3"/>',
33
+ globe: '<circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z"/>',
34
+ code: '<path d="m16 18 6-6-6-6M8 6l-6 6 6 6"/>',
35
+ sparkles: '<path d="M12 3l1.9 5.8L20 10l-6.1 1.2L12 17l-1.9-5.8L4 10l6.1-1.2z"/><path d="M19 17v4M17 19h4"/>',
36
+ lock: '<rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
37
+ users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/>',
38
+ };
39
+
40
+ const {
41
+ eyebrow = 'Features',
42
+ title = 'Everything you need, nothing you don’t',
43
+ subtitle = 'A focused toolkit that keeps your pages fast, accessible and on-brand.',
44
+ features = [
45
+ { icon: 'bolt', title: 'Zero runtime JS', text: 'Components render to static HTML and CSS. Interactivity is progressive and tiny.', href: '#' },
46
+ { icon: 'shield', title: 'Accessible by default', text: 'Semantic markup, focus rings, ARIA where it matters and 4.5:1 contrast everywhere.', href: '#' },
47
+ { icon: 'layers', title: 'Design tokens', text: 'Colors, spacing and radii come from CSS custom properties you can re-theme in one file.' },
48
+ { icon: 'chart', title: 'SVG charts', text: '45 dependency-free charts with tooltips, legends and data-table fallbacks.' },
49
+ { icon: 'globe', title: 'i18n ready', text: 'Every string is a prop. Ship English, Spanish and Russian docs out of the box.' },
50
+ { icon: 'code', title: 'Copy & paste', text: 'No package lock-in. Grab the source from the docs and own every line.' },
51
+ ],
52
+ columns = 3,
53
+ variant = 'card',
54
+ size = 'md',
55
+ className = '',
56
+ } = Astro.props;
57
+
58
+ const id = `fig-${Math.random().toString(36).slice(2, 8)}`;
59
+ ---
60
+
61
+ <section class={`fig fig--${variant} fig--${size} ${className}`} aria-labelledby={id} style={`--fig-cols:${columns}`}>
62
+ <div class="fig__inner">
63
+ <header class="fig__head">
64
+ <p class="fig__eyebrow">{eyebrow}</p>
65
+ <h2 id={id} class="fig__title">{title}</h2>
66
+ <p class="fig__subtitle">{subtitle}</p>
67
+ </header>
68
+ <ul class="fig__grid">
69
+ {features.map((f) => (
70
+ <li class="fig__card">
71
+ <span class="fig__icon" aria-hidden="true">
72
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" set:html={icons[f.icon] ?? icons.sparkles}></svg>
73
+ </span>
74
+ <h3 class="fig__card-title">{f.title}</h3>
75
+ <p class="fig__text">{f.text}</p>
76
+ {f.href && (
77
+ <a class="fig__link" href={f.href}>
78
+ Learn more<span class="fig__sr"> about {f.title}</span>
79
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
80
+ </a>
81
+ )}
82
+ </li>
83
+ ))}
84
+ </ul>
85
+ </div>
86
+ </section>
87
+
88
+ <style>
89
+ .fig {
90
+ --fig-py: 5.5rem;
91
+ padding: var(--fig-py) var(--sp-6, 1.5rem);
92
+ background: var(--c-bg, #050505);
93
+ }
94
+ .fig--sm { --fig-py: 3.5rem; }
95
+ .fig--lg { --fig-py: 7.5rem; }
96
+ .fig__inner { max-width: 1180px; margin: 0 auto; }
97
+ .fig__head { max-width: 640px; margin: 0 auto 3.5rem; text-align: center; }
98
+ .fig__eyebrow { margin: 0 0 0.75rem; font-size: 0.8125rem; font-weight: 650; letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-primary-light, #d4a017); }
99
+ .fig__title { margin: 0 0 1rem; font-size: clamp(1.9rem, 4vw, 2.75rem); font-weight: 800; letter-spacing: -0.03em; line-height: 1.12; color: var(--c-text-1, #fff); text-wrap: balance; }
100
+ .fig__subtitle { margin: 0; font-size: 1.0625rem; line-height: 1.6; color: var(--c-text-2, #a3a3a3); }
101
+ .fig__grid {
102
+ list-style: none;
103
+ margin: 0;
104
+ padding: 0;
105
+ display: grid;
106
+ grid-template-columns: repeat(auto-fill, minmax(min(100%, calc((100% - (var(--fig-cols) - 1) * 1.25rem) / var(--fig-cols))), 1fr));
107
+ gap: 1.25rem;
108
+ }
109
+ .fig__card {
110
+ position: relative;
111
+ padding: 1.75rem;
112
+ border-radius: var(--r-lg, 20px);
113
+ transition: border-color 0.25s ease, background 0.25s ease, transform 0.25s ease;
114
+ }
115
+ .fig--card .fig__card {
116
+ background: linear-gradient(160deg, rgb(var(--ink-rgb, 255 255 255) / 0.04), rgb(var(--ink-rgb, 255 255 255) / 0.01));
117
+ border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.07));
118
+ }
119
+ .fig--bordered .fig__card { border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.1); }
120
+ .fig--plain .fig__card { padding: 0.5rem 0; }
121
+ .fig--card .fig__card:hover, .fig--card .fig__card:focus-within,
122
+ .fig--bordered .fig__card:hover, .fig--bordered .fig__card:focus-within {
123
+ border-color: color-mix(in srgb, var(--c-primary-light, #d4a017) 35%, transparent);
124
+ transform: translateY(-3px);
125
+ }
126
+ .fig__icon {
127
+ display: grid;
128
+ place-items: center;
129
+ width: 46px;
130
+ height: 46px;
131
+ margin-bottom: 1.25rem;
132
+ border-radius: var(--r-md, 12px);
133
+ background: var(--c-primary-glow, color-mix(in srgb, var(--c-primary, #b8860b) 12%, transparent));
134
+ border: 1px solid color-mix(in srgb, var(--c-primary-light, #d4a017) 25%, transparent);
135
+ color: var(--c-primary-light, #d4a017);
136
+ }
137
+ .fig__card-title { margin: 0 0 0.5rem; font-size: 1.125rem; font-weight: 650; color: var(--c-text-1, #fff); letter-spacing: -0.01em; }
138
+ .fig__text { margin: 0; font-size: 0.9375rem; line-height: 1.6; color: var(--c-text-2, #a3a3a3); }
139
+ .fig__link {
140
+ display: inline-flex;
141
+ align-items: center;
142
+ gap: 0.35rem;
143
+ min-height: 44px;
144
+ margin-top: 0.5rem;
145
+ font-size: 0.875rem;
146
+ font-weight: 600;
147
+ color: var(--c-primary-light, #d4a017);
148
+ text-decoration: none;
149
+ }
150
+ .fig__link svg { transition: transform 0.2s ease; }
151
+ .fig__link:hover svg { transform: translateX(3px); }
152
+ .fig__link:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 4px; }
153
+ .fig__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
154
+ @media (prefers-reduced-motion: reduce) { .fig__card, .fig__link svg { transition: none; } }
155
+ </style>
@@ -0,0 +1,182 @@
1
+ ---
2
+ /**
3
+ * FeatureTabsShowcase — Interactive feature section using the WAI-ARIA tabs
4
+ * pattern (arrow keys, Home/End) to switch between product capabilities.
5
+ *
6
+ * @param {string} title - Section heading.
7
+ * @param {string} subtitle - Section description.
8
+ * @param {Array<{label: string, title: string, text: string, points: string[], stat: string, statLabel: string}>} tabs - Tab panels.
9
+ * @param {string} variant - Tab style (pills|underline|cards).
10
+ * @param {string} size - Vertical rhythm (sm|md|lg).
11
+ * @param {string} className - Extra CSS class.
12
+ */
13
+ interface Tab { label: string; title: string; text: string; points: string[]; stat: string; statLabel: string }
14
+ interface Props {
15
+ title?: string;
16
+ subtitle?: string;
17
+ tabs?: Tab[];
18
+ variant?: 'pills' | 'underline' | 'cards';
19
+ size?: 'sm' | 'md' | 'lg';
20
+ className?: string;
21
+ }
22
+
23
+ const {
24
+ title = 'One platform, every workflow',
25
+ subtitle = 'Switch between capabilities to see how teams use the kit.',
26
+ tabs = [
27
+ { label: 'Marketing', title: 'Launch pages in an afternoon', text: 'Assemble hero, features, pricing and FAQ sections into conversion-ready landing pages.', points: ['24 new section blocks', 'SEO-friendly markup', 'Instant static builds'], stat: '3.4×', statLabel: 'faster page launches' },
28
+ { label: 'Product', title: 'In-app analytics without a chart library', text: 'Render KPI tiles, funnels and cohort tables straight from your data at build time.', points: ['45 SVG charts', 'Tooltips & table view', 'Zero client JS'], stat: '0 kB', statLabel: 'chart runtime shipped' },
29
+ { label: 'Engineering', title: 'Accessible components you can trust', text: 'Every component is keyboard navigable and screen-reader tested, with visible focus states.', points: ['WCAG 2.2 AA', 'Typed props', 'Reduced-motion aware'], stat: '100', statLabel: 'Lighthouse accessibility' },
30
+ ],
31
+ variant = 'pills',
32
+ size = 'md',
33
+ className = '',
34
+ } = Astro.props;
35
+
36
+ const uid = Math.random().toString(36).slice(2, 8);
37
+ const id = `fts-${uid}`;
38
+ ---
39
+
40
+ <div class="cq-feature-tabs-showcase-cq">
41
+ <section class={`fts fts--${variant} fts--${size} ${className}`} aria-labelledby={id}>
42
+ <div class="fts__inner">
43
+ <header class="fts__head">
44
+ <h2 id={id} class="fts__title">{title}</h2>
45
+ <p class="fts__subtitle">{subtitle}</p>
46
+ </header>
47
+
48
+ <div class="fts__tabs" data-fts>
49
+ <div class="fts__list" role="tablist" aria-label={title}>
50
+ {tabs.map((t, i) => (
51
+ <button
52
+ type="button"
53
+ role="tab"
54
+ class="fts__tab"
55
+ id={`${id}-tab-${i}`}
56
+ aria-controls={`${id}-panel-${i}`}
57
+ aria-selected={i === 0 ? 'true' : 'false'}
58
+ tabindex={i === 0 ? 0 : -1}
59
+ >{t.label}</button>
60
+ ))}
61
+ </div>
62
+
63
+ {tabs.map((t, i) => (
64
+ <div class="fts__panel" role="tabpanel" id={`${id}-panel-${i}`} aria-labelledby={`${id}-tab-${i}`} tabindex="0" hidden={i !== 0}>
65
+ <div class="fts__copy">
66
+ <h3 class="fts__panel-title">{t.title}</h3>
67
+ <p class="fts__text">{t.text}</p>
68
+ <ul class="fts__points">
69
+ {t.points.map((p) => (
70
+ <li><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>{p}</li>
71
+ ))}
72
+ </ul>
73
+ </div>
74
+ <div class="fts__stat">
75
+ <span class="fts__stat-value">{t.stat}</span>
76
+ <span class="fts__stat-label">{t.statLabel}</span>
77
+ </div>
78
+ </div>
79
+ ))}
80
+ </div>
81
+ </div>
82
+ </section>
83
+ </div>
84
+ <script>
85
+ document.querySelectorAll<HTMLElement>('[data-fts]').forEach((root) => {
86
+ const tabs = Array.from(root.querySelectorAll<HTMLButtonElement>('[role="tab"]'));
87
+ const select = (next: HTMLButtonElement, focus = true) => {
88
+ tabs.forEach((t) => {
89
+ const on = t === next;
90
+ t.setAttribute('aria-selected', String(on));
91
+ t.tabIndex = on ? 0 : -1;
92
+ const panel = document.getElementById(t.getAttribute('aria-controls') || '');
93
+ if (panel) panel.hidden = !on;
94
+ });
95
+ if (focus) next.focus();
96
+ };
97
+ tabs.forEach((tab, i) => {
98
+ tab.addEventListener('click', () => select(tab, false));
99
+ tab.addEventListener('keydown', (e) => {
100
+ const map: Record<string, number> = { ArrowRight: i + 1, ArrowLeft: i - 1, Home: 0, End: tabs.length - 1 };
101
+ if (!(e.key in map)) return;
102
+ e.preventDefault();
103
+ select(tabs[(map[e.key] + tabs.length) % tabs.length]);
104
+ });
105
+ });
106
+ });
107
+ </script>
108
+
109
+ <style>
110
+ .cq-feature-tabs-showcase-cq { container-type: inline-size; }
111
+ .fts {
112
+ --fts-py: 5.5rem;
113
+ padding: var(--fts-py) var(--sp-6, 1.5rem);
114
+ background: var(--c-bg, #050505);
115
+ }
116
+ .fts--sm { --fts-py: 3.5rem; }
117
+ .fts--lg { --fts-py: 7.5rem; }
118
+ .fts__inner { max-width: 1080px; margin: 0 auto; }
119
+ .fts__head { text-align: center; max-width: 640px; margin: 0 auto 2.5rem; }
120
+ .fts__title { margin: 0 0 0.75rem; font-size: clamp(1.9rem, 4vw, 2.75rem); font-weight: 800; letter-spacing: -0.03em; color: var(--c-text-1, #fff); }
121
+ .fts__subtitle { margin: 0; font-size: 1.0625rem; color: var(--c-text-2, #a3a3a3); }
122
+ .fts__list { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 2rem; }
123
+ .fts__tab {
124
+ min-height: 44px;
125
+ padding: 0 1.25rem;
126
+ border: 1px solid transparent;
127
+ border-radius: var(--r-full, 999px);
128
+ background: transparent;
129
+ color: var(--c-text-2, #a3a3a3);
130
+ font: inherit;
131
+ font-size: 0.9375rem;
132
+ font-weight: 600;
133
+ cursor: pointer;
134
+ transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
135
+ }
136
+ .fts__tab:hover { color: var(--c-text-1, #fff); }
137
+ .fts__tab:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; }
138
+ .fts--pills .fts__list { width: fit-content; margin-inline: auto; padding: 0.35rem; border-radius: var(--r-full, 999px); background: rgb(var(--ink-rgb, 255 255 255) / 0.04); border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); }
139
+ .fts--pills .fts__tab[aria-selected='true'] { background: var(--c-text-1, #fff); color: var(--c-on-primary, #0a0a0a); }
140
+ .fts--underline .fts__list { border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); gap: 1.5rem; }
141
+ .fts--underline .fts__tab { border-radius: 0; padding: 0 0.25rem; border-bottom: 2px solid transparent; margin-bottom: -1px; }
142
+ .fts--underline .fts__tab[aria-selected='true'] { color: var(--c-text-1, #fff); border-bottom-color: var(--c-primary-light, #d4a017); }
143
+ .fts--cards .fts__tab { border-radius: var(--r-md, 12px); border-color: rgb(var(--ink-rgb, 255 255 255) / 0.08); }
144
+ .fts--cards .fts__tab[aria-selected='true'] { border-color: var(--c-primary-light, #d4a017); color: var(--c-text-1, #fff); background: var(--c-primary-glow, color-mix(in srgb, var(--c-primary, #b8860b) 12%, transparent)); }
145
+
146
+ .fts__panel {
147
+ display: grid;
148
+ grid-template-columns: 1.4fr 1fr;
149
+ gap: 2rem;
150
+ align-items: center;
151
+ padding: clamp(1.5rem, 4vw, 3rem);
152
+ border-radius: var(--r-xl, 24px);
153
+ background: linear-gradient(160deg, rgb(var(--ink-rgb, 255 255 255) / 0.045), rgb(var(--ink-rgb, 255 255 255) / 0.01));
154
+ border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.07));
155
+ animation: fts-in 0.35s ease;
156
+ }
157
+ .fts__panel[hidden] { display: none; }
158
+ .fts__panel:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 3px; }
159
+ @keyframes fts-in { from { opacity: 0; transform: translateY(6px); } }
160
+ .fts__panel-title { margin: 0 0 0.75rem; font-size: clamp(1.4rem, 3vw, 1.875rem); font-weight: 750; letter-spacing: -0.02em; color: var(--c-text-1, #fff); }
161
+ .fts__text { margin: 0 0 1.25rem; line-height: 1.65; color: var(--c-text-2, #a3a3a3); }
162
+ .fts__points { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
163
+ .fts__points li { display: flex; align-items: center; gap: 0.55rem; color: var(--c-text-1, #fff); font-size: 0.9375rem; }
164
+ .fts__points svg { color: var(--chart-pos, #34c38f); }
165
+ .fts__stat {
166
+ display: flex;
167
+ flex-direction: column;
168
+ align-items: center;
169
+ justify-content: center;
170
+ gap: 0.5rem;
171
+ min-height: 200px;
172
+ border-radius: var(--r-lg, 20px);
173
+ background: radial-gradient(circle at 50% 30%, var(--c-primary-glow, color-mix(in srgb, var(--c-primary, #b8860b) 16%, transparent)), transparent 70%), rgb(var(--ink-rgb, 255 255 255) / 0.02);
174
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.06);
175
+ text-align: center;
176
+ padding: 1.5rem;
177
+ }
178
+ .fts__stat-value { font-size: clamp(2.75rem, 6vw, 4rem); font-weight: 800; letter-spacing: -0.04em; color: var(--c-text-1, #fff); font-variant-numeric: tabular-nums; line-height: 1; }
179
+ .fts__stat-label { font-size: 0.875rem; color: var(--c-text-2, #a3a3a3); }
180
+ @container (max-width: 760px) { .fts__panel { grid-template-columns: 1fr; } }
181
+ @media (prefers-reduced-motion: reduce) { .fts__panel { animation: none; } }
182
+ </style>
@@ -48,6 +48,7 @@ const steps = [
48
48
  ];
49
49
  ---
50
50
 
51
+ <div class="cq-feature-timeline-flow-cq">
51
52
  <section class={`timeline-section ${className}`} aria-labelledby="timeline-title">
52
53
  <div class="timeline-bg" aria-hidden="true">
53
54
  <div class="timeline-glow"></div>
@@ -83,8 +84,9 @@ const steps = [
83
84
  </div>
84
85
  </div>
85
86
  </section>
86
-
87
+ </div>
87
88
  <style>
89
+ .cq-feature-timeline-flow-cq { container-type: inline-size; }
88
90
  .timeline-section {
89
91
  position: relative;
90
92
  padding: var(--sp-20, 5rem) var(--sp-6, 1.5rem);
@@ -131,7 +133,7 @@ const steps = [
131
133
  font-weight: 700;
132
134
  letter-spacing: 0.06em;
133
135
  text-transform: uppercase;
134
- background: rgba(184, 134, 11, 0.12);
136
+ background: color-mix(in srgb, var(--c-primary, #b8860b) 12%, transparent);
135
137
  color: #eab308;
136
138
  border: 1px solid rgba(234, 179, 8, 0.25);
137
139
  margin-bottom: var(--sp-4, 1rem);
@@ -199,7 +201,7 @@ const steps = [
199
201
  flex: 1;
200
202
  background: rgba(15, 23, 42, 0.6);
201
203
  backdrop-filter: blur(20px);
202
- border: 1px solid rgba(255, 255, 255, 0.08);
204
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
203
205
  border-radius: 20px;
204
206
  padding: 2rem;
205
207
  transition: transform 0.3s ease, border-color 0.3s ease;
@@ -207,7 +209,7 @@ const steps = [
207
209
 
208
210
  .step-card__body:hover {
209
211
  transform: translateX(4px);
210
- border-color: rgba(255, 255, 255, 0.18);
212
+ border-color: rgb(var(--ink-rgb, 255 255 255) / 0.18);
211
213
  }
212
214
 
213
215
  .step-tag {
@@ -234,7 +236,7 @@ const steps = [
234
236
  .step-code {
235
237
  padding: 0.75rem 1rem;
236
238
  background: rgba(0, 0, 0, 0.5);
237
- border: 1px solid rgba(255, 255, 255, 0.08);
239
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
238
240
  border-radius: 10px;
239
241
  font-family: var(--font-mono, monospace);
240
242
  font-size: 0.85rem;
@@ -242,7 +244,7 @@ const steps = [
242
244
  overflow-x: auto;
243
245
  }
244
246
 
245
- @media (max-width: 640px) {
247
+ @container (max-width: 640px) {
246
248
  .flow-line {
247
249
  display: none;
248
250
  }
@@ -8,6 +8,7 @@ interface Props {
8
8
  const { title = "Feedback", subtitle = "Badges, alerts, and notification components." } = Astro.props;
9
9
  ---
10
10
 
11
+ <div class="cq-feedback-showcase-cq">
11
12
  <section class="feedback-showcase">
12
13
  <div class="container">
13
14
  <div class="showcase-header">
@@ -66,8 +67,9 @@ const { title = "Feedback", subtitle = "Badges, alerts, and notification compone
66
67
  </div>
67
68
  </div>
68
69
  </section>
69
-
70
+ </div>
70
71
  <style>
72
+ .cq-feedback-showcase-cq { container-type: inline-size; }
71
73
  .feedback-showcase {
72
74
  padding: var(--sp-16) 0;
73
75
  position: relative;
@@ -84,8 +86,8 @@ const { title = "Feedback", subtitle = "Badges, alerts, and notification compone
84
86
  text-transform: uppercase;
85
87
  letter-spacing: 0.1em;
86
88
  color: var(--c-primary-light);
87
- background: rgba(184,134,11,0.1);
88
- border: 1px solid rgba(184,134,11,0.25);
89
+ background: color-mix(in srgb, var(--c-primary, #b8860b) 10%, transparent);
90
+ border: 1px solid color-mix(in srgb, var(--c-primary, #b8860b) 25%, transparent);
89
91
  border-radius: var(--r-full);
90
92
  margin-bottom: var(--sp-4);
91
93
  }
@@ -107,7 +109,7 @@ const { title = "Feedback", subtitle = "Badges, alerts, and notification compone
107
109
  padding: var(--sp-12);
108
110
  background: rgba(10, 10, 10, 0.6);
109
111
  backdrop-filter: blur(20px);
110
- border: 1px solid rgba(255,255,255,0.08);
112
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
111
113
  border-radius: var(--r-xl);
112
114
  margin-bottom: var(--sp-6);
113
115
  box-shadow: 0 20px 60px rgba(0,0,0,0.4);
@@ -192,13 +194,13 @@ const { title = "Feedback", subtitle = "Badges, alerts, and notification compone
192
194
  }
193
195
  .badge--new {
194
196
  background: linear-gradient(135deg, var(--c-primary-light), var(--c-primary));
195
- color: #0a0a0a;
196
- box-shadow: 0 2px 10px rgba(184,134,11,0.25);
197
+ color: var(--c-on-primary, #0a0a0a);
198
+ box-shadow: 0 2px 10px color-mix(in srgb, var(--c-primary, #b8860b) 25%, transparent);
197
199
  }
198
200
  .badge--beta {
199
- background: rgba(255,255,255,0.08);
201
+ background: rgb(var(--ink-rgb, 255 255 255) / 0.08);
200
202
  color: var(--c-text-1);
201
- border: 1px solid rgba(255,255,255,0.12);
203
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.12);
202
204
  }
203
205
  .badge--count {
204
206
  background: rgba(239,68,68,0.15);
@@ -215,9 +217,9 @@ const { title = "Feedback", subtitle = "Badges, alerts, and notification compone
215
217
  gap: 0.75rem;
216
218
  width: 100%;
217
219
  padding: 1rem;
218
- background: rgba(255,255,255,0.06);
220
+ background: rgb(var(--ink-rgb, 255 255 255) / 0.06);
219
221
  backdrop-filter: blur(12px);
220
- border: 1px solid rgba(255,255,255,0.1);
222
+ border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.1);
221
223
  border-radius: var(--r-lg);
222
224
  box-shadow: 0 8px 30px rgba(0,0,0,0.3);
223
225
  animation: fadeSlideIn 0.5s ease 0.45s both;
@@ -268,7 +270,7 @@ const { title = "Feedback", subtitle = "Badges, alerts, and notification compone
268
270
  .spinner {
269
271
  width: 1.5rem;
270
272
  height: 1.5rem;
271
- border: 2px solid rgba(184,134,11,0.2);
273
+ border: 2px solid color-mix(in srgb, var(--c-primary, #b8860b) 20%, transparent);
272
274
  border-top-color: var(--c-primary-light);
273
275
  border-radius: 50%;
274
276
  animation: spin 0.8s linear infinite;
@@ -300,7 +302,7 @@ const { title = "Feedback", subtitle = "Badges, alerts, and notification compone
300
302
  .usage-code {
301
303
  text-align: center;
302
304
  padding: var(--sp-4);
303
- background: rgba(255,255,255,0.02);
305
+ background: rgb(var(--ink-rgb, 255 255 255) / 0.02);
304
306
  border-radius: var(--r-md);
305
307
  }
306
308
  .usage-code code {
@@ -309,7 +311,7 @@ const { title = "Feedback", subtitle = "Badges, alerts, and notification compone
309
311
  color: var(--c-primary-light);
310
312
  }
311
313
 
312
- @media (max-width: 640px) {
314
+ @container (max-width: 640px) {
313
315
  .feedback-stack { max-width: 100%; gap: var(--sp-4); }
314
316
  .preview-stage { padding: var(--sp-8) var(--sp-4); }
315
317
  .preview-stage__inner { min-height: auto; }
@@ -0,0 +1,157 @@
1
+ ---
2
+ /**
3
+ * FooterMegaSection — Full site footer: brand blurb, newsletter field, four link
4
+ * columns, social icons, system-status pill and legal row.
5
+ *
6
+ * @param {string} brand - Brand name.
7
+ * @param {string} tagline - Short brand description.
8
+ * @param {Array<{title: string, links: Array<{label: string, href: string}>}>} columns - Link columns.
9
+ * @param {Array<{label: string, href: string, icon: 'github'|'x'|'discord'|'youtube'}>} socials - Social links.
10
+ * @param {string} status - System status text (empty to hide).
11
+ * @param {string} copyright - Copyright line.
12
+ * @param {string} variant - Footer style (default|bordered|minimal).
13
+ * @param {string} size - Vertical rhythm (sm|md|lg).
14
+ * @param {string} className - Extra CSS class.
15
+ */
16
+ interface Column { title: string; links: Array<{ label: string; href: string }> }
17
+ interface Social { label: string; href: string; icon: 'github' | 'x' | 'discord' | 'youtube' }
18
+ interface Props {
19
+ brand?: string;
20
+ tagline?: string;
21
+ columns?: Column[];
22
+ socials?: Social[];
23
+ status?: string;
24
+ copyright?: string;
25
+ variant?: 'default' | 'bordered' | 'minimal';
26
+ size?: 'sm' | 'md' | 'lg';
27
+ className?: string;
28
+ }
29
+
30
+ const {
31
+ brand = 'Astro Kit',
32
+ tagline = 'Accessible, zero-dependency components for building beautiful Astro sites.',
33
+ columns = [
34
+ { title: 'Product', links: [{ label: 'Components', href: '#' }, { label: 'Sections', href: '#' }, { label: 'Charts', href: '#' }, { label: 'Changelog', href: '#' }] },
35
+ { title: 'Resources', links: [{ label: 'Documentation', href: '#' }, { label: 'Installation', href: '#' }, { label: 'Notes', href: '#' }, { label: 'Showcase', href: '#' }] },
36
+ { title: 'Company', links: [{ label: 'About', href: '#' }, { label: 'Careers', href: '#' }, { label: 'Contact', href: '#' }] },
37
+ { title: 'Legal', links: [{ label: 'Privacy', href: '#' }, { label: 'Terms', href: '#' }, { label: 'License (MIT)', href: '#' }] },
38
+ ],
39
+ socials = [
40
+ { label: 'GitHub', href: '#', icon: 'github' },
41
+ { label: 'X (Twitter)', href: '#', icon: 'x' },
42
+ { label: 'Discord', href: '#', icon: 'discord' },
43
+ { label: 'YouTube', href: '#', icon: 'youtube' },
44
+ ],
45
+ status = 'All systems operational',
46
+ copyright = `© ${new Date().getFullYear()} Astro Kit. All rights reserved.`,
47
+ variant = 'default',
48
+ size = 'md',
49
+ className = '',
50
+ } = Astro.props;
51
+
52
+ const icons = {
53
+ github: '<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.9a3.4 3.4 0 0 0-.9-2.6c3.1-.4 6.4-1.5 6.4-7A5.4 5.4 0 0 0 20 4.8 5 5 0 0 0 19.9 1S18.7.6 16 2.5a13.4 13.4 0 0 0-7 0C6.3.6 5.1 1 5.1 1A5 5 0 0 0 5 4.8a5.4 5.4 0 0 0-1.5 3.7c0 5.4 3.3 6.6 6.4 7a3.4 3.4 0 0 0-.9 2.6V22"/>',
54
+ x: '<path d="M4 4l16 16M20 4 4 20"/>',
55
+ discord: '<path d="M8 12h.01M16 12h.01"/><path d="M7.5 7.5c3-1 6-1 9 0M7 16.5c3 1 7 1 10 0"/><path d="M15.5 17l1 3c3-1 5-3 5.5-4.5.5-4-1-8-3-11-1.5-.8-3-1.2-4.5-1.5L14 4.5M8.5 17l-1 3c-3-1-5-3-5.5-4.5-.5-4 1-8 3-11 1.5-.8 3-1.2 4.5-1.5L10 4.5"/>',
56
+ youtube: '<path d="M2.5 17a24 24 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.6 49.6 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24 24 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.6 49.6 0 0 1-16.2 0A2 2 0 0 1 2.5 17"/><path d="m10 15 5-3-5-3z"/>',
57
+ };
58
+ const id = `fms-${Math.random().toString(36).slice(2, 8)}`;
59
+ ---
60
+
61
+ <div class="cq-footer-mega-section-cq">
62
+ <footer class={`fms fms--${variant} fms--${size} ${className}`} aria-labelledby={id}>
63
+ <h2 id={id} class="fms__sr">Site footer</h2>
64
+ <div class="fms__inner">
65
+ <div class="fms__top">
66
+ <div class="fms__brand">
67
+ <a class="fms__logo" href="/">
68
+ <svg width="28" height="28" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2 3 20h4l5-10 5 10h4z" fill="var(--c-primary-light, #d4a017)" /></svg>
69
+ {brand}
70
+ </a>
71
+ <p class="fms__tagline">{tagline}</p>
72
+ <form class="fms__news" action="#" method="post">
73
+ <label for={`${id}-email`} class="fms__news-label">Get product updates</label>
74
+ <div class="fms__news-field">
75
+ <input id={`${id}-email`} type="email" name="email" placeholder="you@company.com" autocomplete="email" required />
76
+ <button type="submit" aria-label="Subscribe to product updates">
77
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
78
+ </button>
79
+ </div>
80
+ </form>
81
+ </div>
82
+ <nav class="fms__cols" aria-label="Footer">
83
+ {columns.map((c) => (
84
+ <div class="fms__col">
85
+ <h3 class="fms__col-title">{c.title}</h3>
86
+ <ul>
87
+ {c.links.map((l) => <li><a href={l.href}>{l.label}</a></li>)}
88
+ </ul>
89
+ </div>
90
+ ))}
91
+ </nav>
92
+ </div>
93
+ <div class="fms__bottom">
94
+ <p class="fms__copy">{copyright}</p>
95
+ {status && (
96
+ <a class="fms__status" href="#">
97
+ <span class="fms__dot" aria-hidden="true"></span>{status}
98
+ </a>
99
+ )}
100
+ <ul class="fms__socials">
101
+ {socials.map((s) => (
102
+ <li>
103
+ <a href={s.href} aria-label={s.label}>
104
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" set:html={icons[s.icon]}></svg>
105
+ </a>
106
+ </li>
107
+ ))}
108
+ </ul>
109
+ </div>
110
+ </div>
111
+ </footer>
112
+ </div>
113
+ <style>
114
+ .cq-footer-mega-section-cq { container-type: inline-size; }
115
+ .fms {
116
+ --fms-py: 4.5rem;
117
+ padding: var(--fms-py) var(--sp-6, 1.5rem) 2rem;
118
+ background: var(--c-bg-1, #0a0a0a);
119
+ border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.06);
120
+ }
121
+ .fms--sm { --fms-py: 3rem; }
122
+ .fms--lg { --fms-py: 6rem; }
123
+ .fms--bordered .fms__inner { padding: clamp(1.5rem, 4vw, 3rem); border-radius: var(--r-xl, 28px); border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.08)); background: rgb(var(--ink-rgb, 255 255 255) / 0.015); }
124
+ .fms--minimal { background: transparent; border-top: 0; }
125
+ .fms--minimal .fms__news { display: none; }
126
+ .fms__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
127
+ .fms__inner { max-width: 1180px; margin: 0 auto; }
128
+ .fms__top { display: grid; grid-template-columns: 1.2fr 2fr; gap: clamp(2rem, 6vw, 5rem); padding-bottom: 3rem; border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); }
129
+ .fms__logo { display: inline-flex; align-items: center; gap: 0.6rem; min-height: 44px; font-size: 1.25rem; font-weight: 800; letter-spacing: -0.02em; color: var(--c-text-1, #fff); text-decoration: none; }
130
+ .fms__tagline { margin: 0.75rem 0 1.75rem; max-width: 320px; font-size: 0.9375rem; line-height: 1.6; color: var(--c-text-2, #a3a3a3); }
131
+ .fms__news-label { display: block; margin-bottom: 0.5rem; font-size: 0.8125rem; font-weight: 600; color: var(--c-text-1, #fff); }
132
+ .fms__news-field { display: flex; max-width: 340px; border-radius: var(--r-md, 12px); border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.12); background: rgba(0, 0, 0, 0.3); overflow: hidden; transition: border-color 0.2s ease; }
133
+ .fms__news-field:focus-within { border-color: var(--c-primary-light, #d4a017); }
134
+ .fms__news-field input { flex: 1; min-width: 0; min-height: 46px; padding: 0 0.9rem; border: 0; background: transparent; color: var(--c-text-1, #fff); font: inherit; font-size: 0.9375rem; }
135
+ .fms__news-field input:focus { outline: none; }
136
+ .fms__news-field input::placeholder { color: var(--c-text-3, #737373); }
137
+ .fms__news-field button { width: 46px; border: 0; background: rgb(var(--ink-rgb, 255 255 255) / 0.06); color: var(--c-text-1, #fff); cursor: pointer; transition: background 0.2s ease; }
138
+ .fms__news-field button:hover { background: var(--c-primary-light, #d4a017); color: var(--c-on-primary, #0a0a0a); }
139
+ .fms__news-field button:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: -2px; }
140
+ .fms__cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 2rem; }
141
+ .fms__col-title { margin: 0 0 1rem; font-size: 0.8125rem; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--c-text-1, #fff); }
142
+ .fms__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
143
+ .fms__col a, .fms__copy, .fms__status { font-size: 0.9rem; color: var(--c-text-2, #a3a3a3); text-decoration: none; }
144
+ .fms__col a { display: inline-flex; align-items: center; min-height: 36px; transition: color 0.2s ease; }
145
+ .fms__col a:hover { color: var(--c-text-1, #fff); }
146
+ .fms a:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 4px; }
147
+ .fms__bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; padding-top: 1.75rem; }
148
+ .fms__copy { margin: 0; font-size: 0.8125rem; }
149
+ .fms__status { display: inline-flex; align-items: center; gap: 0.5rem; min-height: 36px; padding: 0 0.85rem; border-radius: 999px; border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); font-size: 0.8125rem; }
150
+ .fms__status:hover { color: var(--c-text-1, #fff); }
151
+ .fms__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--chart-pos, #34c38f); box-shadow: 0 0 8px var(--chart-pos, #34c38f); }
152
+ .fms__socials { list-style: none; margin: 0; padding: 0; display: flex; gap: 0.25rem; }
153
+ .fms__socials a { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 10px; color: var(--c-text-2, #a3a3a3); transition: color 0.2s ease, background 0.2s ease; }
154
+ .fms__socials a:hover { color: var(--c-text-1, #fff); background: rgb(var(--ink-rgb, 255 255 255) / 0.06); }
155
+ @container (max-width: 860px) { .fms__top { grid-template-columns: 1fr; } }
156
+ @container (max-width: 560px) { .fms__bottom { flex-direction: column; align-items: flex-start; } }
157
+ </style>