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.
- package/README.md +133 -36
- package/package.json +17 -4
- package/src/components/index.ts +18 -202
- package/src/components/lib/Galaxy3D.astro +249 -137
- package/src/components/lib/app/AppSidebar.astro +249 -0
- package/src/components/lib/app/AppTopbar.astro +284 -0
- package/src/components/lib/app/ArrivalsBoard.astro +176 -0
- package/src/components/lib/app/AuthCard.astro +213 -0
- package/src/components/lib/app/DataTable.astro +346 -0
- package/src/components/lib/app/GuestProfileCard.astro +133 -0
- package/src/components/lib/app/HousekeepingBoard.astro +189 -0
- package/src/components/lib/app/PageHeader.astro +157 -0
- package/src/components/lib/app/PlanUsageCard.astro +112 -0
- package/src/components/lib/app/RoomRack.astro +207 -0
- package/src/components/lib/app/SettingsSection.astro +96 -0
- package/src/components/lib/app/SwitchRow.astro +71 -0
- package/src/components/lib/app/TeamMembers.astro +190 -0
- package/src/components/lib/app/app-icons.ts +52 -0
- package/src/components/lib/app/index.ts +19 -0
- package/src/components/lib/buttons/NeonBorderButton.astro +162 -0
- package/src/components/lib/buttons/RippleButton.astro +199 -0
- package/src/components/lib/buttons/SlideRevealButton.astro +159 -0
- package/src/components/lib/buttons/index.ts +4 -0
- package/src/components/lib/cards/GithubRepoCard.astro +176 -0
- package/src/components/lib/cards/PricingCompareMiniCard.astro +196 -0
- package/src/components/lib/cards/StatsTrendCard.astro +149 -0
- package/src/components/lib/cards/index.ts +3 -0
- package/src/components/lib/charts/ActivityRings.astro +105 -0
- package/src/components/lib/charts/AreaChart.astro +4 -4
- package/src/components/lib/charts/BarChart.astro +3 -3
- package/src/components/lib/charts/BoxPlotChart.astro +109 -0
- package/src/components/lib/charts/BubbleChart.astro +101 -0
- package/src/components/lib/charts/BulletChart.astro +160 -0
- package/src/components/lib/charts/BumpChart.astro +92 -0
- package/src/components/lib/charts/CandlestickChart.astro +105 -0
- package/src/components/lib/charts/ChartFrame.astro +373 -0
- package/src/components/lib/charts/DivergingBarChart.astro +98 -0
- package/src/components/lib/charts/DonutChart.astro +2 -2
- package/src/components/lib/charts/DumbbellChart.astro +92 -0
- package/src/components/lib/charts/ForecastLineChart.astro +125 -0
- package/src/components/lib/charts/FunnelChart.astro +122 -0
- package/src/components/lib/charts/GaugeChart.astro +111 -0
- package/src/components/lib/charts/GoalProgressChart.astro +97 -0
- package/src/components/lib/charts/GroupedBarChart.astro +125 -0
- package/src/components/lib/charts/HeatmapChart.astro +131 -0
- package/src/components/lib/charts/HistogramChart.astro +111 -0
- package/src/components/lib/charts/HorizontalBarChart.astro +155 -0
- package/src/components/lib/charts/KpiStatTile.astro +116 -0
- package/src/components/lib/charts/KpiTrendGrid.astro +60 -0
- package/src/components/lib/charts/LineChart.astro +160 -0
- package/src/components/lib/charts/LinearProgressGauge.astro +136 -0
- package/src/components/lib/charts/LollipopChart.astro +100 -0
- package/src/components/lib/charts/MeterGauge.astro +122 -0
- package/src/components/lib/charts/MetricDeltaTable.astro +125 -0
- package/src/components/lib/charts/PieChart.astro +135 -0
- package/src/components/lib/charts/PolarAreaChart.astro +85 -0
- package/src/components/lib/charts/PopulationPyramid.astro +122 -0
- package/src/components/lib/charts/PunchCardChart.astro +89 -0
- package/src/components/lib/charts/RadarChart.astro +94 -0
- package/src/components/lib/charts/RadarMetricChart.astro +168 -0
- package/src/components/lib/charts/RadialBarChart.astro +124 -0
- package/src/components/lib/charts/RangeAreaChart.astro +113 -0
- package/src/components/lib/charts/RetentionCohortTable.astro +126 -0
- package/src/components/lib/charts/SankeyChart.astro +128 -0
- package/src/components/lib/charts/ScatterPlot.astro +139 -0
- package/src/components/lib/charts/SegmentedBarChart.astro +119 -0
- package/src/components/lib/charts/SlopeChart.astro +105 -0
- package/src/components/lib/charts/SparklineChart.astro +87 -0
- package/src/components/lib/charts/StackedAreaChart.astro +130 -0
- package/src/components/lib/charts/StackedBarChart.astro +136 -0
- package/src/components/lib/charts/SunburstChart.astro +103 -0
- package/src/components/lib/charts/TreemapChart.astro +94 -0
- package/src/components/lib/charts/WaffleChart.astro +121 -0
- package/src/components/lib/charts/WaterfallChart.astro +118 -0
- package/src/components/lib/charts/chart-tooltip.ts +75 -0
- package/src/components/lib/charts/chart-utils.ts +271 -0
- package/src/components/lib/charts/index.ts +46 -0
- package/src/components/lib/data/ActivityHeatmapGrid.astro +157 -0
- package/src/components/lib/data/MetricComparisonRow.astro +109 -0
- package/src/components/lib/data/StatusTimelineFeed.astro +168 -0
- package/src/components/lib/data/index.ts +3 -0
- package/src/components/lib/ecommerce/CartDrawer.astro +483 -0
- package/src/components/lib/ecommerce/CartPageList.astro +290 -0
- package/src/components/lib/ecommerce/CartToast.astro +276 -0
- package/src/components/lib/ecommerce/CategoryTiles.astro +143 -0
- package/src/components/lib/ecommerce/CheckoutForm.astro +423 -0
- package/src/components/lib/ecommerce/CountdownBanner.astro +230 -0
- package/src/components/lib/ecommerce/FilterBar.astro +278 -0
- package/src/components/lib/ecommerce/FilterSidebar.astro +405 -0
- package/src/components/lib/ecommerce/LiveActivityToast.astro +213 -0
- package/src/components/lib/ecommerce/OrderSummary.astro +295 -0
- package/src/components/lib/ecommerce/ProductBuyBox.astro +247 -0
- package/src/components/lib/ecommerce/ProductCardMinimal.astro +229 -0
- package/src/components/lib/ecommerce/ProductGallery.astro +145 -0
- package/src/components/lib/ecommerce/ProductGrid.astro +118 -0
- package/src/components/lib/ecommerce/ProductMedia.astro +113 -0
- package/src/components/lib/ecommerce/ProductRating.astro +78 -0
- package/src/components/lib/ecommerce/ProductToolbar.astro +93 -0
- package/src/components/lib/ecommerce/PromoBar.astro +145 -0
- package/src/components/lib/ecommerce/StockIndicator.astro +127 -0
- package/src/components/lib/ecommerce/StoreHeader.astro +446 -0
- package/src/components/lib/ecommerce/StoreHero.astro +260 -0
- package/src/components/lib/ecommerce/StoreProductCard.astro +307 -0
- package/src/components/lib/ecommerce/TrustBadges.astro +101 -0
- package/src/components/lib/ecommerce/VariantPicker.astro +145 -0
- package/src/components/lib/ecommerce/cart-store.ts +151 -0
- package/src/components/lib/ecommerce/catalog-filter.ts +62 -0
- package/src/components/lib/ecommerce/index.ts +34 -0
- package/src/components/lib/ecommerce/product-icons.ts +28 -0
- package/src/components/lib/ecommerce/types.ts +70 -0
- package/src/components/lib/feedback/ProgressRingCounter.astro +118 -0
- package/src/components/lib/feedback/ToastNotificationGroup.astro +173 -0
- package/src/components/lib/feedback/index.ts +2 -0
- package/src/components/lib/forms/AnimatedInput.astro +4 -2
- package/src/components/lib/forms/CustomRadioGroup.astro +3 -1
- package/src/components/lib/forms/GlassInlineToggleGroup.astro +161 -0
- package/src/components/lib/forms/PasswordStrengthInput.astro +242 -0
- package/src/components/lib/forms/PhoneCountryInput.astro +145 -0
- package/src/components/lib/forms/TagsAutocompleteInput.astro +249 -0
- package/src/components/lib/forms/index.ts +5 -0
- package/src/components/lib/index.ts +2 -0
- package/src/components/lib/layout/BentoGrid3.astro +6 -4
- package/src/components/lib/layout/CenterFixedBox.astro +2 -2
- package/src/components/lib/layout/CenteredContainer.astro +4 -2
- package/src/components/lib/layout/ClusterHorizontal.astro +2 -2
- package/src/components/lib/layout/DiagonalSectionDivider.astro +69 -0
- package/src/components/lib/layout/FeatureHero.astro +4 -2
- package/src/components/lib/layout/FooterCenteredMinimal.astro +4 -2
- package/src/components/lib/layout/FullBleedSection.astro +2 -2
- package/src/components/lib/layout/GalleryGrid4.astro +2 -2
- package/src/components/lib/layout/HolyGrailGrid.astro +127 -0
- package/src/components/lib/layout/LandingSectionSplit.astro +5 -3
- package/src/components/lib/layout/MasonryGrid.astro +2 -2
- package/src/components/lib/layout/MinimalFooterGrid.astro +1 -1
- package/src/components/lib/layout/ScrollSnapSection.astro +1 -1
- package/src/components/lib/layout/SidebarLayoutWrapper.astro +4 -2
- package/src/components/lib/layout/SplitScreenLayout.astro +3 -3
- package/src/components/lib/layout/StepLayoutVertical.astro +1 -1
- package/src/components/lib/layout/StickySidebarWrapper.astro +4 -2
- package/src/components/lib/layout/index.ts +3 -1
- package/src/components/lib/navigation/QuickJumpDock.astro +125 -0
- package/src/components/lib/navigation/StepBreadcrumbBar.astro +151 -0
- package/src/components/lib/navigation/SubNavbarPills.astro +102 -0
- package/src/components/lib/navigation/index.ts +3 -0
- package/src/components/lib/overlays/CookiePreferencesDrawer.astro +226 -0
- package/src/components/lib/overlays/FloatingFeedbackWidget.astro +203 -0
- package/src/components/lib/overlays/ImageCompareSlider.astro +172 -0
- package/src/components/lib/overlays/index.ts +3 -0
- package/src/components/lib/sections/AgencyContactBrief.astro +194 -0
- package/src/components/lib/sections/AgencyFooterCta.astro +94 -0
- package/src/components/lib/sections/AgencyHeader.astro +161 -0
- package/src/components/lib/sections/AgencyHeroKinetic.astro +172 -0
- package/src/components/lib/sections/AgencyServicesList.astro +113 -0
- package/src/components/lib/sections/AgencyTeamGrid.astro +111 -0
- package/src/components/lib/sections/AgencyWorkGrid.astro +147 -0
- package/src/components/lib/sections/BlogGridSection.astro +133 -0
- package/src/components/lib/sections/ButtonsShowcase.astro +20 -18
- package/src/components/lib/sections/CardsShowcase.astro +28 -26
- package/src/components/lib/sections/CareersOpenings.astro +141 -0
- package/src/components/lib/sections/ChangelogSection.astro +151 -0
- package/src/components/lib/sections/ContactGlassForm.astro +10 -8
- package/src/components/lib/sections/ContactSplitMap.astro +141 -0
- package/src/components/lib/sections/CtaAppDownload.astro +13 -11
- package/src/components/lib/sections/CtaGlowBanner.astro +12 -10
- package/src/components/lib/sections/CtaNewsletterSection.astro +165 -0
- package/src/components/lib/sections/CtaSplitImage.astro +149 -0
- package/src/components/lib/sections/DataShowcase.astro +23 -21
- package/src/components/lib/sections/FaqAccordionSection.astro +8 -8
- package/src/components/lib/sections/FaqTwoColumn.astro +121 -0
- package/src/components/lib/sections/FeatureAlternatingRows.astro +192 -0
- package/src/components/lib/sections/FeatureBentoGrid.astro +13 -11
- package/src/components/lib/sections/FeatureIconGrid.astro +155 -0
- package/src/components/lib/sections/FeatureTabsShowcase.astro +182 -0
- package/src/components/lib/sections/FeatureTimelineFlow.astro +8 -6
- package/src/components/lib/sections/FeedbackShowcase.astro +15 -13
- package/src/components/lib/sections/FooterMegaSection.astro +157 -0
- package/src/components/lib/sections/FormsShowcase.astro +22 -20
- package/src/components/lib/sections/HeroDashboardPreview.astro +182 -0
- package/src/components/lib/sections/HeroGlass.astro +22 -20
- package/src/components/lib/sections/HeroGradient.astro +15 -13
- package/src/components/lib/sections/HeroMinimalCentered.astro +175 -0
- package/src/components/lib/sections/HeroShowcaseSplit.astro +306 -0
- package/src/components/lib/sections/HeroSplit.astro +13 -11
- package/src/components/lib/sections/HeroTerminal.astro +253 -0
- package/src/components/lib/sections/HeroWaitlist.astro +231 -0
- package/src/components/lib/sections/HowItWorksSteps.astro +180 -0
- package/src/components/lib/sections/IntegrationsGrid.astro +167 -0
- package/src/components/lib/sections/LayoutShowcase.astro +21 -19
- package/src/components/lib/sections/LogoCloudSection.astro +96 -0
- package/src/components/lib/sections/LogoWallGrid.astro +93 -0
- package/src/components/lib/sections/NavigationShowcase.astro +15 -13
- package/src/components/lib/sections/OverlaysShowcase.astro +21 -19
- package/src/components/lib/sections/PricingComparisonMatrix.astro +15 -13
- package/src/components/lib/sections/PricingSinglePlan.astro +159 -0
- package/src/components/lib/sections/PricingTierCards.astro +11 -9
- package/src/components/lib/sections/PricingToggleCards.astro +171 -0
- package/src/components/lib/sections/ShopCategoryHero.astro +103 -0
- package/src/components/lib/sections/ShopCollectionMosaic.astro +131 -0
- package/src/components/lib/sections/ShopFooterWordmark.astro +100 -0
- package/src/components/lib/sections/ShopHeaderEditorial.astro +396 -0
- package/src/components/lib/sections/ShopHeroEditorial.astro +151 -0
- package/src/components/lib/sections/ShopLookbook.astro +250 -0
- package/src/components/lib/sections/ShopNewsletterEditorial.astro +128 -0
- package/src/components/lib/sections/ShopProductRail.astro +150 -0
- package/src/components/lib/sections/ShopReviewsEditorial.astro +133 -0
- package/src/components/lib/sections/ShopValuesMarquee.astro +91 -0
- package/src/components/lib/sections/StatsCounterBand.astro +124 -0
- package/src/components/lib/sections/StatsDashboardSection.astro +85 -0
- package/src/components/lib/sections/StatsMetricShowcase.astro +9 -7
- package/src/components/lib/sections/TeamGridSection.astro +12 -10
- package/src/components/lib/sections/TestimonialCarousel.astro +167 -0
- package/src/components/lib/sections/TestimonialFeatured.astro +122 -0
- package/src/components/lib/sections/TestimonialGridSection.astro +188 -0
- package/src/components/lib/sections/TestimonialWallGlass.astro +11 -9
- package/src/components/lib/sections/TypographyShowcase.astro +10 -8
- package/src/components/lib/sections/index.ts +39 -0
- package/src/components/lib/templates/ecommerce/StoreCategoryPage.astro +160 -0
- package/src/components/lib/templates/ecommerce/StoreCheckoutPage.astro +98 -0
- package/src/components/lib/templates/ecommerce/StoreHomePage.astro +191 -0
- package/src/components/lib/templates/ecommerce/StoreLayout.astro +169 -0
- package/src/components/lib/templates/ecommerce/StoreProductPage.astro +165 -0
- package/src/components/lib/templates/ecommerce/data.ts +187 -0
- package/src/components/lib/templates/ecommerce/index.ts +14 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialBagPage.astro +52 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialCheckoutPage.astro +123 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialHomePage.astro +102 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialLayout.astro +216 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialProductPage.astro +205 -0
- package/src/components/lib/templates/ecommerce-editorial/EditorialShopPage.astro +112 -0
- package/src/components/lib/templates/ecommerce-editorial/data.ts +125 -0
- package/src/components/lib/templates/ecommerce-editorial/index.ts +16 -0
- package/src/components/lib/templates/saas-hotel/AppLayout.astro +83 -0
- package/src/components/lib/templates/saas-hotel/DashboardPage.astro +66 -0
- package/src/components/lib/templates/saas-hotel/GuestsPage.astro +65 -0
- package/src/components/lib/templates/saas-hotel/LandingPage.astro +213 -0
- package/src/components/lib/templates/saas-hotel/ReportsPage.astro +63 -0
- package/src/components/lib/templates/saas-hotel/ReservationsPage.astro +49 -0
- package/src/components/lib/templates/saas-hotel/RoomsPage.astro +48 -0
- package/src/components/lib/templates/saas-hotel/SettingsPage.astro +115 -0
- package/src/components/lib/templates/saas-hotel/SignInPage.astro +50 -0
- package/src/components/lib/templates/saas-hotel/data.ts +229 -0
- package/src/components/lib/templates/saas-hotel/index.ts +19 -0
- package/src/components/lib/templates/saas-hotel/lobby-theme.css +90 -0
- package/src/components/lib/typography/AnimatedCounterText.astro +102 -0
- package/src/components/lib/typography/CurvedPathHeading.astro +86 -0
- package/src/components/lib/typography/GradientTextMarquee.astro +105 -0
- package/src/components/lib/typography/HackerDecodeText.astro +85 -0
- package/src/components/lib/typography/index.ts +4 -0
- package/src/styles/global.css +113 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* TestimonialFeatured — A single large customer quote paired with the company's
|
|
4
|
+
* headline results (case-study style), plus a link to the full story.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} quote - The testimonial text.
|
|
7
|
+
* @param {string} name - Author name.
|
|
8
|
+
* @param {string} role - Author role and company.
|
|
9
|
+
* @param {string} company - Company wordmark text.
|
|
10
|
+
* @param {Array<{value: string, label: string}>} metrics - Outcome stats.
|
|
11
|
+
* @param {{label: string, href: string}} link - Case study link.
|
|
12
|
+
* @param {string} variant - Layout style (split|centered|card).
|
|
13
|
+
* @param {string} size - Vertical rhythm (sm|md|lg).
|
|
14
|
+
* @param {string} className - Extra CSS class.
|
|
15
|
+
*/
|
|
16
|
+
interface Props {
|
|
17
|
+
quote?: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
role?: string;
|
|
20
|
+
company?: string;
|
|
21
|
+
metrics?: Array<{ value: string; label: string }>;
|
|
22
|
+
link?: { label: string; href: string };
|
|
23
|
+
variant?: 'split' | 'centered' | 'card';
|
|
24
|
+
size?: 'sm' | 'md' | 'lg';
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
quote = 'We replaced three tools and a chart library with this kit. Our product pages load twice as fast and our conversion rate has never been higher.',
|
|
30
|
+
name = 'Elena Vasquez',
|
|
31
|
+
role = 'VP of Marketing, Orbitly',
|
|
32
|
+
company = 'Orbitly',
|
|
33
|
+
metrics = [
|
|
34
|
+
{ value: '2.1×', label: 'faster page loads' },
|
|
35
|
+
{ value: '+38%', label: 'trial conversion' },
|
|
36
|
+
{ value: '−64%', label: 'front-end bundle size' },
|
|
37
|
+
],
|
|
38
|
+
link = { label: 'Read the case study', href: '#' },
|
|
39
|
+
variant = 'split',
|
|
40
|
+
size = 'md',
|
|
41
|
+
className = '',
|
|
42
|
+
} = Astro.props;
|
|
43
|
+
|
|
44
|
+
const initials = name.split(' ').map((p) => p[0]).slice(0, 2).join('');
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
<div class="cq-testimonial-featured-cq">
|
|
48
|
+
<section class={`tf tf--${variant} tf--${size} ${className}`} aria-label={`Customer story: ${company}`}>
|
|
49
|
+
<div class="tf__inner">
|
|
50
|
+
<figure class="tf__figure">
|
|
51
|
+
<p class="tf__company" aria-hidden="true">
|
|
52
|
+
<svg width="22" height="22" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2" /><circle cx="12" cy="12" r="4" fill="currentColor" /></svg>
|
|
53
|
+
{company}
|
|
54
|
+
</p>
|
|
55
|
+
<svg class="tf__mark" width="48" height="36" viewBox="0 0 48 36" aria-hidden="true"><path d="M0 36V22C0 9 7 2 20 0l2 5C14 7 11 12 11 18h9v18H0zm28 0V22C28 9 35 2 48 0l2 5c-8 2-11 7-11 13h9v18H28z" fill="currentColor" /></svg>
|
|
56
|
+
<blockquote class="tf__quote"><p>{quote}</p></blockquote>
|
|
57
|
+
<figcaption class="tf__author">
|
|
58
|
+
<span class="tf__avatar" aria-hidden="true">{initials}</span>
|
|
59
|
+
<span><strong>{name}</strong><span class="tf__role">{role}</span></span>
|
|
60
|
+
</figcaption>
|
|
61
|
+
</figure>
|
|
62
|
+
|
|
63
|
+
<div class="tf__side">
|
|
64
|
+
<dl class="tf__metrics">
|
|
65
|
+
{metrics.map((mt) => (
|
|
66
|
+
<div class="tf__metric">
|
|
67
|
+
<dt>{mt.label}</dt>
|
|
68
|
+
<dd>{mt.value}</dd>
|
|
69
|
+
</div>
|
|
70
|
+
))}
|
|
71
|
+
</dl>
|
|
72
|
+
{link && (
|
|
73
|
+
<a class="tf__link" href={link.href}>
|
|
74
|
+
{link.label}
|
|
75
|
+
<svg width="16" height="16" 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>
|
|
76
|
+
</a>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</section>
|
|
81
|
+
</div>
|
|
82
|
+
<style>
|
|
83
|
+
.cq-testimonial-featured-cq { container-type: inline-size; }
|
|
84
|
+
.tf {
|
|
85
|
+
--tf-py: 5.5rem;
|
|
86
|
+
padding: var(--tf-py) var(--sp-6, 1.5rem);
|
|
87
|
+
background: var(--c-bg, #050505);
|
|
88
|
+
}
|
|
89
|
+
.tf--sm { --tf-py: 3.5rem; }
|
|
90
|
+
.tf--lg { --tf-py: 7.5rem; }
|
|
91
|
+
.tf__inner { max-width: 1120px; margin: 0 auto; display: grid; grid-template-columns: 1.6fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
|
|
92
|
+
.tf--card .tf__inner {
|
|
93
|
+
padding: clamp(2rem, 5vw, 3.5rem);
|
|
94
|
+
border-radius: var(--r-xl, 28px);
|
|
95
|
+
background: radial-gradient(circle at 0% 0%, var(--c-primary-glow, color-mix(in srgb, var(--c-primary, #b8860b) 14%, transparent)), transparent 55%), rgb(var(--ink-rgb, 255 255 255) / 0.025);
|
|
96
|
+
border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.08));
|
|
97
|
+
}
|
|
98
|
+
.tf--centered .tf__inner { grid-template-columns: 1fr; text-align: center; max-width: 860px; }
|
|
99
|
+
.tf--centered .tf__author, .tf--centered .tf__company { justify-content: center; }
|
|
100
|
+
.tf--centered .tf__mark { margin-inline: auto; }
|
|
101
|
+
.tf__figure { margin: 0; }
|
|
102
|
+
.tf__company { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 2rem; font-size: 1.125rem; font-weight: 750; letter-spacing: -0.02em; color: var(--c-text-1, #fff); }
|
|
103
|
+
.tf__mark { display: block; color: var(--c-primary-light, #d4a017); opacity: 0.8; margin-bottom: 1.25rem; }
|
|
104
|
+
.tf__quote { margin: 0 0 2rem; }
|
|
105
|
+
.tf__quote p { margin: 0; font-size: clamp(1.35rem, 2.6vw, 1.875rem); font-weight: 550; line-height: 1.4; letter-spacing: -0.015em; color: var(--c-text-1, #fff); text-wrap: pretty; }
|
|
106
|
+
.tf__author { display: flex; align-items: center; gap: 0.85rem; }
|
|
107
|
+
.tf__author strong { display: block; color: var(--c-text-1, #fff); }
|
|
108
|
+
.tf__role { display: block; font-size: 0.875rem; color: var(--c-text-2, #a3a3a3); }
|
|
109
|
+
.tf__avatar { display: grid; place-items: center; width: 48px; height: 48px; border-radius: 50%; background: linear-gradient(135deg, var(--chart-7, #9085e9), var(--chart-1, #3987e5)); color: #fff; font-weight: 700; }
|
|
110
|
+
.tf__side { display: grid; gap: 1.5rem; }
|
|
111
|
+
.tf__metrics { margin: 0; display: grid; gap: 1px; border-radius: var(--r-lg, 20px); overflow: hidden; background: rgb(var(--ink-rgb, 255 255 255) / 0.07); }
|
|
112
|
+
.tf--centered .tf__metrics { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
|
|
113
|
+
.tf__metric { display: flex; flex-direction: column-reverse; gap: 0.25rem; padding: 1.25rem 1.5rem; background: var(--c-bg-1, #0a0a0a); }
|
|
114
|
+
.tf__metric dt { font-size: 0.875rem; color: var(--c-text-2, #a3a3a3); }
|
|
115
|
+
.tf__metric dd { margin: 0; font-size: 2rem; font-weight: 800; letter-spacing: -0.03em; color: var(--c-text-1, #fff); font-variant-numeric: tabular-nums; }
|
|
116
|
+
.tf__link { display: inline-flex; align-items: center; gap: 0.45rem; min-height: 44px; font-weight: 650; color: var(--c-primary-light, #d4a017); text-decoration: none; }
|
|
117
|
+
.tf--centered .tf__link { justify-self: center; }
|
|
118
|
+
.tf__link svg { transition: transform 0.2s ease; }
|
|
119
|
+
.tf__link:hover svg { transform: translateX(3px); }
|
|
120
|
+
.tf__link:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 4px; }
|
|
121
|
+
@container (max-width: 820px) { .tf__inner { grid-template-columns: 1fr; } }
|
|
122
|
+
</style>
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* TestimonialGridSection — Bento grid showcase of verified customer reviews with dark glass styling and star ratings.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} eyebrow - Small uppercase category lead. Default is 'WALL OF LOVE'.
|
|
6
|
+
* @param {string} title - Section main headline. Default is 'Trusted by innovative engineering teams'.
|
|
7
|
+
* @param {string} subtitle - Subtitle description.
|
|
8
|
+
*/
|
|
9
|
+
interface Props {
|
|
10
|
+
eyebrow?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
eyebrow = 'WALL OF LOVE',
|
|
17
|
+
title = 'Trusted by thousands of Astro builders',
|
|
18
|
+
subtitle = 'Discover why developers, indie hackers, and creative agencies rely on Astro Components Kit for production websites.',
|
|
19
|
+
} = Astro.props;
|
|
20
|
+
|
|
21
|
+
const testimonials = [
|
|
22
|
+
{
|
|
23
|
+
name: 'Elena Rostova',
|
|
24
|
+
role: 'Principal Architect @ Veloce Labs',
|
|
25
|
+
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80',
|
|
26
|
+
quote: 'The pure zero-JS philosophy with Astro 5 is game-changing. Our Core Web Vitals score hit 100 on every audited route without tweaking hydration.',
|
|
27
|
+
stars: 5,
|
|
28
|
+
highlight: true,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'Marcus Vance',
|
|
32
|
+
role: 'Founder @ HyperSync',
|
|
33
|
+
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&auto=format&fit=crop&q=80',
|
|
34
|
+
quote: 'Copying and pasting self-contained Astro components directly shaved two full sprints off our SaaS launch timeline.',
|
|
35
|
+
stars: 5,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Sofia Chen',
|
|
39
|
+
role: 'Senior UI/UX Designer',
|
|
40
|
+
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&auto=format&fit=crop&q=80',
|
|
41
|
+
quote: 'The glassmorphic dark palette with gold accents feels extraordinarily premium right out of the box. Highly recommended.',
|
|
42
|
+
stars: 5,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
<section class="testi-section">
|
|
48
|
+
<div class="testi-section__header">
|
|
49
|
+
<span class="testi-section__eyebrow">{eyebrow}</span>
|
|
50
|
+
<h2 class="testi-section__title">{title}</h2>
|
|
51
|
+
<p class="testi-section__subtitle">{subtitle}</p>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="testi-section__grid">
|
|
55
|
+
{testimonials.map((t) => (
|
|
56
|
+
<article class:list={['testi-card', { 'testi-card--highlight': t.highlight }]}>
|
|
57
|
+
<div class="testi-card__stars" aria-label={`${t.stars} out of 5 stars`}>
|
|
58
|
+
{Array.from({ length: t.stars }).map(() => (
|
|
59
|
+
<span>★</span>
|
|
60
|
+
))}
|
|
61
|
+
</div>
|
|
62
|
+
<blockquote class="testi-card__quote">
|
|
63
|
+
"{t.quote}"
|
|
64
|
+
</blockquote>
|
|
65
|
+
<div class="testi-card__author">
|
|
66
|
+
<img src={t.avatar} alt={t.name} class="testi-card__avatar" loading="lazy" />
|
|
67
|
+
<div class="testi-card__meta">
|
|
68
|
+
<h5 class="testi-card__name">{t.name}</h5>
|
|
69
|
+
<span class="testi-card__role">{t.role}</span>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</article>
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
</section>
|
|
76
|
+
|
|
77
|
+
<style>
|
|
78
|
+
.testi-section {
|
|
79
|
+
padding: var(--sp-12, 3rem) var(--sp-6, 1.5rem);
|
|
80
|
+
max-width: 1200px;
|
|
81
|
+
margin: 0 auto;
|
|
82
|
+
font-family: var(--font-sans, system-ui, sans-serif);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.testi-section__header {
|
|
86
|
+
text-align: center;
|
|
87
|
+
max-width: 640px;
|
|
88
|
+
margin: 0 auto 3rem auto;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.testi-section__eyebrow {
|
|
92
|
+
display: inline-block;
|
|
93
|
+
font-size: 0.75rem;
|
|
94
|
+
font-weight: 800;
|
|
95
|
+
letter-spacing: 0.12em;
|
|
96
|
+
color: var(--c-primary-light, #d4a017);
|
|
97
|
+
padding: 0.35rem 0.8rem;
|
|
98
|
+
border: 1px solid color-mix(in srgb, var(--c-primary, #b8860b) 30%, transparent);
|
|
99
|
+
border-radius: var(--r-sm, 8px);
|
|
100
|
+
margin-bottom: 1rem;
|
|
101
|
+
text-transform: uppercase;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.testi-section__title {
|
|
105
|
+
font-size: clamp(1.8rem, 4vw, 2.8rem);
|
|
106
|
+
font-weight: 900;
|
|
107
|
+
color: var(--c-text-1, #ffffff);
|
|
108
|
+
letter-spacing: -0.03em;
|
|
109
|
+
margin: 0 0 1rem 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.testi-section__subtitle {
|
|
113
|
+
font-size: 0.95rem;
|
|
114
|
+
color: var(--c-text-2, #a3a3a3);
|
|
115
|
+
line-height: 1.6;
|
|
116
|
+
margin: 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.testi-section__grid {
|
|
120
|
+
display: grid;
|
|
121
|
+
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
122
|
+
gap: 1.5rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.testi-card {
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-direction: column;
|
|
128
|
+
padding: var(--sp-8, 2rem);
|
|
129
|
+
background: rgb(var(--ink-rgb, 255 255 255) / 0.02);
|
|
130
|
+
backdrop-filter: blur(16px);
|
|
131
|
+
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
|
|
132
|
+
border-radius: var(--r-xl, 24px);
|
|
133
|
+
transition: transform 0.3s ease, border-color 0.3s ease;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.testi-card:hover {
|
|
137
|
+
transform: translateY(-4px);
|
|
138
|
+
border-color: color-mix(in srgb, var(--c-primary, #b8860b) 30%, transparent);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.testi-card--highlight {
|
|
142
|
+
background: radial-gradient(circle at top left, color-mix(in srgb, var(--c-primary, #b8860b) 10%, transparent), rgb(var(--ink-rgb, 255 255 255) / 0.02) 70%);
|
|
143
|
+
border-color: color-mix(in srgb, var(--c-primary, #b8860b) 40%, transparent);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.testi-card__stars {
|
|
147
|
+
color: #fbbf24;
|
|
148
|
+
font-size: 1rem;
|
|
149
|
+
letter-spacing: 2px;
|
|
150
|
+
margin-bottom: 1rem;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.testi-card__quote {
|
|
154
|
+
font-size: 0.95rem;
|
|
155
|
+
color: var(--c-text-1, #ffffff);
|
|
156
|
+
line-height: 1.65;
|
|
157
|
+
margin: 0 0 1.5rem 0;
|
|
158
|
+
flex: 1;
|
|
159
|
+
font-style: italic;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.testi-card__author {
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
gap: 12px;
|
|
166
|
+
margin-top: auto;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.testi-card__avatar {
|
|
170
|
+
width: 44px;
|
|
171
|
+
height: 44px;
|
|
172
|
+
border-radius: 50%;
|
|
173
|
+
object-fit: cover;
|
|
174
|
+
border: 2px solid color-mix(in srgb, var(--c-primary, #b8860b) 40%, transparent);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.testi-card__name {
|
|
178
|
+
font-size: 0.92rem;
|
|
179
|
+
font-weight: 700;
|
|
180
|
+
color: var(--c-text-1, #ffffff);
|
|
181
|
+
margin: 0 0 2px 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.testi-card__role {
|
|
185
|
+
font-size: 0.75rem;
|
|
186
|
+
color: var(--c-text-3, #737373);
|
|
187
|
+
}
|
|
188
|
+
</style>
|
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
} = Astro.props;
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
+
<div class="cq-testimonial-wall-glass-cq">
|
|
24
25
|
<section class={`testimonials-section ${className}`} aria-labelledby="testimonials-title">
|
|
25
26
|
<div class="testimonials-bg" aria-hidden="true">
|
|
26
27
|
<div class="test-glow test-glow--1"></div>
|
|
@@ -151,8 +152,9 @@ const {
|
|
|
151
152
|
</div>
|
|
152
153
|
</div>
|
|
153
154
|
</section>
|
|
154
|
-
|
|
155
|
+
</div>
|
|
155
156
|
<style>
|
|
157
|
+
.cq-testimonial-wall-glass-cq { container-type: inline-size; }
|
|
156
158
|
.testimonials-section {
|
|
157
159
|
position: relative;
|
|
158
160
|
padding: var(--sp-20, 5rem) var(--sp-6, 1.5rem);
|
|
@@ -211,7 +213,7 @@ const {
|
|
|
211
213
|
font-weight: 700;
|
|
212
214
|
letter-spacing: 0.06em;
|
|
213
215
|
text-transform: uppercase;
|
|
214
|
-
background:
|
|
216
|
+
background: color-mix(in srgb, var(--c-primary, #b8860b) 12%, transparent);
|
|
215
217
|
color: #eab308;
|
|
216
218
|
border: 1px solid rgba(234, 179, 8, 0.25);
|
|
217
219
|
margin-bottom: var(--sp-4, 1rem);
|
|
@@ -241,7 +243,7 @@ const {
|
|
|
241
243
|
gap: 2rem;
|
|
242
244
|
padding: 1.25rem 2rem;
|
|
243
245
|
background: rgba(15, 23, 42, 0.7);
|
|
244
|
-
border: 1px solid
|
|
246
|
+
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
|
|
245
247
|
border-radius: 16px;
|
|
246
248
|
backdrop-filter: blur(12px);
|
|
247
249
|
max-width: 750px;
|
|
@@ -284,7 +286,7 @@ const {
|
|
|
284
286
|
.trust-sep {
|
|
285
287
|
width: 1px;
|
|
286
288
|
height: 40px;
|
|
287
|
-
background:
|
|
289
|
+
background: rgb(var(--ink-rgb, 255 255 255) / 0.1);
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
/* Wall Grid */
|
|
@@ -299,7 +301,7 @@ const {
|
|
|
299
301
|
background: rgba(15, 23, 42, 0.55);
|
|
300
302
|
backdrop-filter: blur(20px);
|
|
301
303
|
-webkit-backdrop-filter: blur(20px);
|
|
302
|
-
border: 1px solid
|
|
304
|
+
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
|
|
303
305
|
border-radius: var(--r-xl, 18px);
|
|
304
306
|
padding: 1.75rem;
|
|
305
307
|
display: flex;
|
|
@@ -310,7 +312,7 @@ const {
|
|
|
310
312
|
|
|
311
313
|
.test-card:hover {
|
|
312
314
|
transform: translateY(-4px);
|
|
313
|
-
border-color:
|
|
315
|
+
border-color: rgb(var(--ink-rgb, 255 255 255) / 0.2);
|
|
314
316
|
}
|
|
315
317
|
|
|
316
318
|
.test-card--featured {
|
|
@@ -352,7 +354,7 @@ const {
|
|
|
352
354
|
align-items: center;
|
|
353
355
|
gap: 0.85rem;
|
|
354
356
|
padding-top: 1rem;
|
|
355
|
-
border-top: 1px solid
|
|
357
|
+
border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.06);
|
|
356
358
|
}
|
|
357
359
|
|
|
358
360
|
.author-avatar {
|
|
@@ -364,7 +366,7 @@ const {
|
|
|
364
366
|
font-weight: 800;
|
|
365
367
|
font-size: 0.85rem;
|
|
366
368
|
color: #fff;
|
|
367
|
-
border: 2px solid
|
|
369
|
+
border: 2px solid rgb(var(--ink-rgb, 255 255 255) / 0.15);
|
|
368
370
|
}
|
|
369
371
|
|
|
370
372
|
.author-avatar--1 { background: linear-gradient(135deg, #6366f1, #a855f7); }
|
|
@@ -390,7 +392,7 @@ const {
|
|
|
390
392
|
color: #64748b;
|
|
391
393
|
}
|
|
392
394
|
|
|
393
|
-
@
|
|
395
|
+
@container (max-width: 960px) {
|
|
394
396
|
.trust-bar {
|
|
395
397
|
flex-direction: column;
|
|
396
398
|
gap: 1rem;
|
|
@@ -8,6 +8,7 @@ interface Props {
|
|
|
8
8
|
const { title = "Typography", subtitle = "Headings and text patterns." } = Astro.props;
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
<div class="cq-typography-showcase-cq">
|
|
11
12
|
<section class="typography-showcase">
|
|
12
13
|
<div class="container">
|
|
13
14
|
<div class="showcase-header">
|
|
@@ -54,8 +55,9 @@ const { title = "Typography", subtitle = "Headings and text patterns." } = Astro
|
|
|
54
55
|
</div>
|
|
55
56
|
</div>
|
|
56
57
|
</section>
|
|
57
|
-
|
|
58
|
+
</div>
|
|
58
59
|
<style>
|
|
60
|
+
.cq-typography-showcase-cq { container-type: inline-size; }
|
|
59
61
|
.typography-showcase {
|
|
60
62
|
padding: var(--sp-16) 0;
|
|
61
63
|
position: relative;
|
|
@@ -72,8 +74,8 @@ const { title = "Typography", subtitle = "Headings and text patterns." } = Astro
|
|
|
72
74
|
text-transform: uppercase;
|
|
73
75
|
letter-spacing: 0.1em;
|
|
74
76
|
color: var(--c-primary-light);
|
|
75
|
-
background:
|
|
76
|
-
border: 1px solid
|
|
77
|
+
background: color-mix(in srgb, var(--c-primary, #b8860b) 10%, transparent);
|
|
78
|
+
border: 1px solid color-mix(in srgb, var(--c-primary, #b8860b) 25%, transparent);
|
|
77
79
|
border-radius: var(--r-full);
|
|
78
80
|
margin-bottom: var(--sp-4);
|
|
79
81
|
}
|
|
@@ -95,7 +97,7 @@ const { title = "Typography", subtitle = "Headings and text patterns." } = Astro
|
|
|
95
97
|
padding: var(--sp-12);
|
|
96
98
|
background: rgba(10, 10, 10, 0.6);
|
|
97
99
|
backdrop-filter: blur(20px);
|
|
98
|
-
border: 1px solid
|
|
100
|
+
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
|
|
99
101
|
border-radius: var(--r-xl);
|
|
100
102
|
margin-bottom: var(--sp-6);
|
|
101
103
|
box-shadow: 0 20px 60px rgba(0,0,0,0.4);
|
|
@@ -117,9 +119,9 @@ const { title = "Typography", subtitle = "Headings and text patterns." } = Astro
|
|
|
117
119
|
/* Typography Mockups */
|
|
118
120
|
.typo-mockup {
|
|
119
121
|
padding: var(--sp-6);
|
|
120
|
-
background:
|
|
122
|
+
background: rgb(var(--ink-rgb, 255 255 255) / 0.02);
|
|
121
123
|
border-radius: var(--r-md);
|
|
122
|
-
border: 1px solid
|
|
124
|
+
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.05);
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
/* Gradient Heading */
|
|
@@ -221,7 +223,7 @@ const { title = "Typography", subtitle = "Headings and text patterns." } = Astro
|
|
|
221
223
|
.usage-code {
|
|
222
224
|
text-align: center;
|
|
223
225
|
padding: var(--sp-4);
|
|
224
|
-
background:
|
|
226
|
+
background: rgb(var(--ink-rgb, 255 255 255) / 0.02);
|
|
225
227
|
border-radius: var(--r-md);
|
|
226
228
|
}
|
|
227
229
|
.usage-code code {
|
|
@@ -230,7 +232,7 @@ const { title = "Typography", subtitle = "Headings and text patterns." } = Astro
|
|
|
230
232
|
color: var(--c-primary-light);
|
|
231
233
|
}
|
|
232
234
|
|
|
233
|
-
@
|
|
235
|
+
@container (max-width: 640px) {
|
|
234
236
|
.typo-stack { gap: var(--sp-5); }
|
|
235
237
|
.typo-mockup { padding: var(--sp-4); }
|
|
236
238
|
.preview-stage { padding: var(--sp-8) var(--sp-4); }
|
|
@@ -26,3 +26,42 @@ export { default as FaqAccordionSection } from './FaqAccordionSection.astro';
|
|
|
26
26
|
export { default as StatsMetricShowcase } from './StatsMetricShowcase.astro';
|
|
27
27
|
export { default as TeamGridSection } from './TeamGridSection.astro';
|
|
28
28
|
export { default as ContactGlassForm } from './ContactGlassForm.astro';
|
|
29
|
+
export { default as TestimonialGridSection } from './TestimonialGridSection.astro';
|
|
30
|
+
export { default as LogoCloudSection } from './LogoCloudSection.astro';
|
|
31
|
+
export { default as HeroShowcaseSplit } from './HeroShowcaseSplit.astro';
|
|
32
|
+
|
|
33
|
+
// Additional sections (v0.4)
|
|
34
|
+
export { default as HeroMinimalCentered } from './HeroMinimalCentered.astro';
|
|
35
|
+
export { default as HeroTerminal } from './HeroTerminal.astro';
|
|
36
|
+
export { default as HeroWaitlist } from './HeroWaitlist.astro';
|
|
37
|
+
export { default as HeroDashboardPreview } from './HeroDashboardPreview.astro';
|
|
38
|
+
export { default as FeatureIconGrid } from './FeatureIconGrid.astro';
|
|
39
|
+
export { default as FeatureAlternatingRows } from './FeatureAlternatingRows.astro';
|
|
40
|
+
export { default as FeatureTabsShowcase } from './FeatureTabsShowcase.astro';
|
|
41
|
+
export { default as HowItWorksSteps } from './HowItWorksSteps.astro';
|
|
42
|
+
export { default as IntegrationsGrid } from './IntegrationsGrid.astro';
|
|
43
|
+
export { default as PricingToggleCards } from './PricingToggleCards.astro';
|
|
44
|
+
export { default as PricingSinglePlan } from './PricingSinglePlan.astro';
|
|
45
|
+
export { default as TestimonialCarousel } from './TestimonialCarousel.astro';
|
|
46
|
+
export { default as TestimonialFeatured } from './TestimonialFeatured.astro';
|
|
47
|
+
export { default as CtaNewsletterSection } from './CtaNewsletterSection.astro';
|
|
48
|
+
export { default as CtaSplitImage } from './CtaSplitImage.astro';
|
|
49
|
+
export { default as StatsCounterBand } from './StatsCounterBand.astro';
|
|
50
|
+
export { default as StatsDashboardSection } from './StatsDashboardSection.astro';
|
|
51
|
+
export { default as CareersOpenings } from './CareersOpenings.astro';
|
|
52
|
+
export { default as FaqTwoColumn } from './FaqTwoColumn.astro';
|
|
53
|
+
export { default as ContactSplitMap } from './ContactSplitMap.astro';
|
|
54
|
+
export { default as LogoWallGrid } from './LogoWallGrid.astro';
|
|
55
|
+
export { default as BlogGridSection } from './BlogGridSection.astro';
|
|
56
|
+
export { default as ChangelogSection } from './ChangelogSection.astro';
|
|
57
|
+
export { default as FooterMegaSection } from './FooterMegaSection.astro';
|
|
58
|
+
export { default as ShopHeaderEditorial } from './ShopHeaderEditorial.astro';
|
|
59
|
+
export { default as ShopHeroEditorial } from './ShopHeroEditorial.astro';
|
|
60
|
+
export { default as ShopCollectionMosaic } from './ShopCollectionMosaic.astro';
|
|
61
|
+
export { default as ShopProductRail } from './ShopProductRail.astro';
|
|
62
|
+
export { default as ShopLookbook } from './ShopLookbook.astro';
|
|
63
|
+
export { default as ShopValuesMarquee } from './ShopValuesMarquee.astro';
|
|
64
|
+
export { default as ShopReviewsEditorial } from './ShopReviewsEditorial.astro';
|
|
65
|
+
export { default as ShopNewsletterEditorial } from './ShopNewsletterEditorial.astro';
|
|
66
|
+
export { default as ShopFooterWordmark } from './ShopFooterWordmark.astro';
|
|
67
|
+
export { default as ShopCategoryHero } from './ShopCategoryHero.astro';
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* StoreCategoryPage — E-commerce template catalogue: breadcrumb, category chips, toolbar
|
|
4
|
+
* with live count + sort, FilterSidebar and ProductGrid (instant client-side filtering).
|
|
5
|
+
*
|
|
6
|
+
* Usage (src/pages/shop.astro):
|
|
7
|
+
* import { StoreCategoryPage } from 'astro-component-kit/templates/ecommerce';
|
|
8
|
+
* <StoreCategoryPage />
|
|
9
|
+
* Category links use `?category=<id>`; pass `category` to build one static page per category.
|
|
10
|
+
*
|
|
11
|
+
* @param {StoreConfig} store - Catalogue and settings. Default: demo store.
|
|
12
|
+
* @param {StoreRoutes} routes - Page URLs.
|
|
13
|
+
* @param {string} category - Pre-selected category id (optional).
|
|
14
|
+
* @param {boolean} seedCart - Pre-fill the cart with demo items. Default false.
|
|
15
|
+
* @param {string} theme - Initial theme (dark|light|auto). Default 'dark'.
|
|
16
|
+
* @param {boolean} noindex - Add robots noindex. Default false.
|
|
17
|
+
*/
|
|
18
|
+
import StoreLayout from './StoreLayout.astro';
|
|
19
|
+
import ProductToolbar from '../../ecommerce/ProductToolbar.astro';
|
|
20
|
+
import FilterSidebar from '../../ecommerce/FilterSidebar.astro';
|
|
21
|
+
import ProductGrid from '../../ecommerce/ProductGrid.astro';
|
|
22
|
+
import TrustBadges from '../../ecommerce/TrustBadges.astro';
|
|
23
|
+
import { demoStore, defaultRoutes, categoryHref, categoryNames, type StoreConfig, type StoreRoutes } from './data';
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
store?: StoreConfig;
|
|
27
|
+
routes?: StoreRoutes;
|
|
28
|
+
category?: string;
|
|
29
|
+
seedCart?: boolean;
|
|
30
|
+
noindex?: boolean;
|
|
31
|
+
theme?: 'dark' | 'light' | 'auto';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { store = demoStore, routes = defaultRoutes, category, seedCart = false, noindex = false, theme = 'dark' } = Astro.props;
|
|
35
|
+
|
|
36
|
+
const active = store.categories.find((c) => c.id === category);
|
|
37
|
+
const names = categoryNames(store);
|
|
38
|
+
const maxPrice = Math.ceil(Math.max(...store.products.map((p) => p.price)) / 50) * 50;
|
|
39
|
+
const colors = Array.from(new Map(store.products.flatMap((p) => p.colors ?? []).map((c) => [c.name, c])).values());
|
|
40
|
+
const filterCats = store.categories.map((c) => ({ id: c.id, name: c.name, count: store.products.filter((p) => p.category === c.id).length }));
|
|
41
|
+
const title = active ? active.name : 'All products';
|
|
42
|
+
const description = active?.description ?? 'Everything we make, in one place. Filter by category, price, colour and rating.';
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
<StoreLayout theme={theme} title={title} description={description} store={store} routes={routes} currentHref={routes.shop} seedCart={seedCart} noindex={noindex}>
|
|
46
|
+
<div class="store-container shop">
|
|
47
|
+
<nav class="crumbs" aria-label="Breadcrumb">
|
|
48
|
+
<ol>
|
|
49
|
+
<li><a href={routes.home}>Home</a></li>
|
|
50
|
+
<li><a href={routes.shop} aria-current={active ? undefined : 'page'}>Shop</a></li>
|
|
51
|
+
{active && <li><span aria-current="page">{active.name}</span></li>}
|
|
52
|
+
</ol>
|
|
53
|
+
</nav>
|
|
54
|
+
|
|
55
|
+
<ProductToolbar title={title} description={description} total={store.products.length} target="shop-grid" filtersId="shop-filters" />
|
|
56
|
+
|
|
57
|
+
<ul class="chips" aria-label="Categories">
|
|
58
|
+
<li><a class="chip" href={routes.shop} aria-current={!active ? 'page' : undefined}>All</a></li>
|
|
59
|
+
{store.categories.map((c) => (
|
|
60
|
+
<li><a class="chip" href={categoryHref(routes, c.id)} aria-current={active?.id === c.id ? 'page' : undefined} data-chip={c.id}>{c.name}</a></li>
|
|
61
|
+
))}
|
|
62
|
+
</ul>
|
|
63
|
+
|
|
64
|
+
<div class="shop__layout">
|
|
65
|
+
<div class="shop__filters">
|
|
66
|
+
<FilterSidebar
|
|
67
|
+
target="shop-grid"
|
|
68
|
+
id="shop-filters"
|
|
69
|
+
categories={filterCats}
|
|
70
|
+
selected={active ? [active.id] : []}
|
|
71
|
+
maxPrice={maxPrice}
|
|
72
|
+
colors={colors}
|
|
73
|
+
currency={store.currency}
|
|
74
|
+
locale={store.locale}
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
<ProductGrid
|
|
78
|
+
id="shop-grid"
|
|
79
|
+
products={store.products}
|
|
80
|
+
productBase={routes.product}
|
|
81
|
+
categoryNames={names}
|
|
82
|
+
columns={3}
|
|
83
|
+
currency={store.currency}
|
|
84
|
+
locale={store.locale}
|
|
85
|
+
cartKey={store.cartKey}
|
|
86
|
+
openCart={false}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="shop__trust">
|
|
91
|
+
<TrustBadges variant="divided" size="sm" />
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</StoreLayout>
|
|
95
|
+
|
|
96
|
+
<script>
|
|
97
|
+
// Category chips filter in place (no reload) and keep FilterSidebar in sync.
|
|
98
|
+
document.querySelectorAll<HTMLAnchorElement>('.chips a').forEach((chip) => {
|
|
99
|
+
chip.addEventListener('click', (e) => {
|
|
100
|
+
const form = document.getElementById('shop-filters') as HTMLFormElement | null;
|
|
101
|
+
if (!form) return;
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
const id = chip.dataset.chip;
|
|
104
|
+
form.querySelectorAll<HTMLInputElement>('input[name="category"]').forEach((i) => { i.checked = id ? i.value === id : false; });
|
|
105
|
+
form.dispatchEvent(new Event('change', { bubbles: true }));
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
const syncChips = () => {
|
|
109
|
+
const form = document.getElementById('shop-filters') as HTMLFormElement | null;
|
|
110
|
+
if (!form) return;
|
|
111
|
+
const checked = Array.from(form.querySelectorAll<HTMLInputElement>('input[name="category"]:checked')).map((i) => i.value);
|
|
112
|
+
document.querySelectorAll<HTMLAnchorElement>('.chips a').forEach((chip) => {
|
|
113
|
+
const id = chip.dataset.chip;
|
|
114
|
+
const on = id ? checked.length === 1 && checked[0] === id : checked.length === 0;
|
|
115
|
+
if (on) chip.setAttribute('aria-current', 'page');
|
|
116
|
+
else chip.removeAttribute('aria-current');
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
document.getElementById('shop-filters')?.addEventListener('change', syncChips);
|
|
120
|
+
document.getElementById('shop-filters')?.addEventListener('reset', () => window.setTimeout(syncChips, 0));
|
|
121
|
+
syncChips();
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<style>
|
|
125
|
+
.shop { padding-block: var(--sp-6) clamp(3rem, 7vw, 5rem); }
|
|
126
|
+
|
|
127
|
+
.crumbs ol { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0 0 var(--sp-6); padding: 0; list-style: none; font-size: 0.85rem; }
|
|
128
|
+
.crumbs li { display: flex; align-items: center; gap: var(--sp-2); color: var(--c-text-2); }
|
|
129
|
+
.crumbs li + li::before { content: '/'; opacity: 0.5; }
|
|
130
|
+
.crumbs a { color: var(--c-text-2); }
|
|
131
|
+
.crumbs a:hover { color: var(--c-text-1); }
|
|
132
|
+
.crumbs [aria-current='page'] { color: var(--c-text-1); font-weight: 600; }
|
|
133
|
+
|
|
134
|
+
.chips { display: flex; gap: var(--sp-2); margin: 0 0 var(--sp-6); padding: 0 0 4px; list-style: none; overflow-x: auto; scrollbar-width: none; }
|
|
135
|
+
.chip {
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
min-height: 40px;
|
|
139
|
+
padding: 0 var(--sp-4);
|
|
140
|
+
border-radius: var(--r-full);
|
|
141
|
+
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.1);
|
|
142
|
+
color: var(--c-text-2);
|
|
143
|
+
font-size: 0.88rem;
|
|
144
|
+
font-weight: 600;
|
|
145
|
+
white-space: nowrap;
|
|
146
|
+
transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
|
147
|
+
}
|
|
148
|
+
.chip:hover { color: var(--c-text-1); border-color: rgb(var(--ink-rgb, 255 255 255) / 0.25); }
|
|
149
|
+
.chip[aria-current='page'] { background: var(--c-text-1); color: var(--c-bg); border-color: var(--c-text-1); }
|
|
150
|
+
.chip:focus-visible { outline: 2px solid var(--c-primary-light); outline-offset: 2px; }
|
|
151
|
+
|
|
152
|
+
.shop__layout { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: var(--sp-8); align-items: start; }
|
|
153
|
+
.shop__filters { position: sticky; top: 88px; }
|
|
154
|
+
.shop__trust { margin-top: clamp(3rem, 6vw, 4.5rem); }
|
|
155
|
+
|
|
156
|
+
@media (max-width: 900px) {
|
|
157
|
+
.shop__layout { grid-template-columns: 1fr; gap: var(--sp-4); }
|
|
158
|
+
.shop__filters { position: static; }
|
|
159
|
+
}
|
|
160
|
+
</style>
|