astro-component-kit 0.3.0 → 0.4.0

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 (115) hide show
  1. package/README.md +159 -176
  2. package/package.json +2 -2
  3. package/src/components/index.ts +16 -202
  4. package/src/components/lib/buttons/NeonBorderButton.astro +162 -0
  5. package/src/components/lib/buttons/RippleButton.astro +199 -0
  6. package/src/components/lib/buttons/SlideRevealButton.astro +159 -0
  7. package/src/components/lib/buttons/index.ts +4 -0
  8. package/src/components/lib/cards/GithubRepoCard.astro +176 -0
  9. package/src/components/lib/cards/PricingCompareMiniCard.astro +196 -0
  10. package/src/components/lib/cards/StatsTrendCard.astro +149 -0
  11. package/src/components/lib/cards/index.ts +3 -0
  12. package/src/components/lib/charts/ActivityRings.astro +105 -0
  13. package/src/components/lib/charts/BoxPlotChart.astro +109 -0
  14. package/src/components/lib/charts/BubbleChart.astro +101 -0
  15. package/src/components/lib/charts/BulletChart.astro +160 -0
  16. package/src/components/lib/charts/BumpChart.astro +92 -0
  17. package/src/components/lib/charts/CandlestickChart.astro +105 -0
  18. package/src/components/lib/charts/ChartFrame.astro +373 -0
  19. package/src/components/lib/charts/DivergingBarChart.astro +98 -0
  20. package/src/components/lib/charts/DumbbellChart.astro +92 -0
  21. package/src/components/lib/charts/ForecastLineChart.astro +125 -0
  22. package/src/components/lib/charts/FunnelChart.astro +122 -0
  23. package/src/components/lib/charts/GaugeChart.astro +111 -0
  24. package/src/components/lib/charts/GoalProgressChart.astro +97 -0
  25. package/src/components/lib/charts/GroupedBarChart.astro +125 -0
  26. package/src/components/lib/charts/HeatmapChart.astro +131 -0
  27. package/src/components/lib/charts/HistogramChart.astro +111 -0
  28. package/src/components/lib/charts/HorizontalBarChart.astro +155 -0
  29. package/src/components/lib/charts/KpiStatTile.astro +116 -0
  30. package/src/components/lib/charts/KpiTrendGrid.astro +60 -0
  31. package/src/components/lib/charts/LineChart.astro +160 -0
  32. package/src/components/lib/charts/LinearProgressGauge.astro +136 -0
  33. package/src/components/lib/charts/LollipopChart.astro +100 -0
  34. package/src/components/lib/charts/MeterGauge.astro +122 -0
  35. package/src/components/lib/charts/MetricDeltaTable.astro +125 -0
  36. package/src/components/lib/charts/PieChart.astro +135 -0
  37. package/src/components/lib/charts/PolarAreaChart.astro +85 -0
  38. package/src/components/lib/charts/PopulationPyramid.astro +122 -0
  39. package/src/components/lib/charts/PunchCardChart.astro +89 -0
  40. package/src/components/lib/charts/RadarChart.astro +94 -0
  41. package/src/components/lib/charts/RadarMetricChart.astro +168 -0
  42. package/src/components/lib/charts/RadialBarChart.astro +124 -0
  43. package/src/components/lib/charts/RangeAreaChart.astro +113 -0
  44. package/src/components/lib/charts/RetentionCohortTable.astro +126 -0
  45. package/src/components/lib/charts/SankeyChart.astro +128 -0
  46. package/src/components/lib/charts/ScatterPlot.astro +139 -0
  47. package/src/components/lib/charts/SegmentedBarChart.astro +119 -0
  48. package/src/components/lib/charts/SlopeChart.astro +105 -0
  49. package/src/components/lib/charts/SparklineChart.astro +87 -0
  50. package/src/components/lib/charts/StackedAreaChart.astro +130 -0
  51. package/src/components/lib/charts/StackedBarChart.astro +136 -0
  52. package/src/components/lib/charts/SunburstChart.astro +103 -0
  53. package/src/components/lib/charts/TreemapChart.astro +94 -0
  54. package/src/components/lib/charts/WaffleChart.astro +121 -0
  55. package/src/components/lib/charts/WaterfallChart.astro +118 -0
  56. package/src/components/lib/charts/chart-tooltip.ts +75 -0
  57. package/src/components/lib/charts/chart-utils.ts +271 -0
  58. package/src/components/lib/charts/index.ts +46 -0
  59. package/src/components/lib/data/ActivityHeatmapGrid.astro +157 -0
  60. package/src/components/lib/data/MetricComparisonRow.astro +109 -0
  61. package/src/components/lib/data/StatusTimelineFeed.astro +168 -0
  62. package/src/components/lib/data/index.ts +3 -0
  63. package/src/components/lib/feedback/ProgressRingCounter.astro +118 -0
  64. package/src/components/lib/feedback/ToastNotificationGroup.astro +173 -0
  65. package/src/components/lib/feedback/index.ts +2 -0
  66. package/src/components/lib/forms/GlassInlineToggleGroup.astro +161 -0
  67. package/src/components/lib/forms/PasswordStrengthInput.astro +242 -0
  68. package/src/components/lib/forms/PhoneCountryInput.astro +145 -0
  69. package/src/components/lib/forms/TagsAutocompleteInput.astro +249 -0
  70. package/src/components/lib/forms/index.ts +5 -0
  71. package/src/components/lib/layout/DiagonalSectionDivider.astro +69 -0
  72. package/src/components/lib/layout/HolyGrailGrid.astro +125 -0
  73. package/src/components/lib/layout/index.ts +3 -1
  74. package/src/components/lib/navigation/QuickJumpDock.astro +125 -0
  75. package/src/components/lib/navigation/StepBreadcrumbBar.astro +151 -0
  76. package/src/components/lib/navigation/SubNavbarPills.astro +102 -0
  77. package/src/components/lib/navigation/index.ts +3 -0
  78. package/src/components/lib/overlays/CookiePreferencesDrawer.astro +226 -0
  79. package/src/components/lib/overlays/FloatingFeedbackWidget.astro +203 -0
  80. package/src/components/lib/overlays/ImageCompareSlider.astro +172 -0
  81. package/src/components/lib/overlays/index.ts +3 -0
  82. package/src/components/lib/sections/BlogGridSection.astro +131 -0
  83. package/src/components/lib/sections/CareersOpenings.astro +139 -0
  84. package/src/components/lib/sections/ChangelogSection.astro +144 -0
  85. package/src/components/lib/sections/ContactSplitMap.astro +139 -0
  86. package/src/components/lib/sections/CtaNewsletterSection.astro +163 -0
  87. package/src/components/lib/sections/CtaSplitImage.astro +147 -0
  88. package/src/components/lib/sections/FaqTwoColumn.astro +119 -0
  89. package/src/components/lib/sections/FeatureAlternatingRows.astro +190 -0
  90. package/src/components/lib/sections/FeatureIconGrid.astro +155 -0
  91. package/src/components/lib/sections/FeatureTabsShowcase.astro +180 -0
  92. package/src/components/lib/sections/FooterMegaSection.astro +155 -0
  93. package/src/components/lib/sections/HeroDashboardPreview.astro +180 -0
  94. package/src/components/lib/sections/HeroMinimalCentered.astro +175 -0
  95. package/src/components/lib/sections/HeroShowcaseSplit.astro +304 -0
  96. package/src/components/lib/sections/HeroTerminal.astro +251 -0
  97. package/src/components/lib/sections/HeroWaitlist.astro +229 -0
  98. package/src/components/lib/sections/HowItWorksSteps.astro +178 -0
  99. package/src/components/lib/sections/IntegrationsGrid.astro +167 -0
  100. package/src/components/lib/sections/LogoCloudSection.astro +96 -0
  101. package/src/components/lib/sections/LogoWallGrid.astro +91 -0
  102. package/src/components/lib/sections/PricingSinglePlan.astro +157 -0
  103. package/src/components/lib/sections/PricingToggleCards.astro +171 -0
  104. package/src/components/lib/sections/StatsCounterBand.astro +122 -0
  105. package/src/components/lib/sections/StatsDashboardSection.astro +83 -0
  106. package/src/components/lib/sections/TestimonialCarousel.astro +165 -0
  107. package/src/components/lib/sections/TestimonialFeatured.astro +120 -0
  108. package/src/components/lib/sections/TestimonialGridSection.astro +188 -0
  109. package/src/components/lib/sections/index.ts +29 -0
  110. package/src/components/lib/typography/AnimatedCounterText.astro +102 -0
  111. package/src/components/lib/typography/CurvedPathHeading.astro +86 -0
  112. package/src/components/lib/typography/GradientTextMarquee.astro +105 -0
  113. package/src/components/lib/typography/HackerDecodeText.astro +85 -0
  114. package/src/components/lib/typography/index.ts +4 -0
  115. package/src/styles/global.css +18 -1
@@ -0,0 +1,157 @@
1
+ ---
2
+ /**
3
+ * ActivityHeatmapGrid — GitHub-style activity contribution matrix with intensity color levels and month labels.
4
+ *
5
+ * @param {string} title - Section header label. Default is 'Commit Activity'.
6
+ * @param {number} totalCount - Total activity events recorded. Default is 1428.
7
+ */
8
+ interface Props {
9
+ title?: string;
10
+ totalCount?: number;
11
+ }
12
+
13
+ const {
14
+ title = 'Contribution Activity',
15
+ totalCount = 1428,
16
+ } = Astro.props;
17
+
18
+ // Mock 16 weeks of 7 days
19
+ const weeks = Array.from({ length: 16 }, (_, w) =>
20
+ Array.from({ length: 7 }, (_, d) => {
21
+ // Generate pseudo-random intensity 0 to 4
22
+ const seed = (w * 7 + d * 13 + 5) % 17;
23
+ if (seed < 4) return 0;
24
+ if (seed < 8) return 1;
25
+ if (seed < 12) return 2;
26
+ if (seed < 15) return 3;
27
+ return 4;
28
+ })
29
+ );
30
+
31
+ const months = ['Jul', 'Aug', 'Sep', 'Oct'];
32
+ ---
33
+
34
+ <div class="heatmap-card">
35
+ <div class="heatmap-card__head">
36
+ <div class="heatmap-card__title-wrap">
37
+ <h5 class="heatmap-card__title">{title}</h5>
38
+ <span class="heatmap-card__total">{totalCount.toLocaleString()} events in the last 4 months</span>
39
+ </div>
40
+
41
+ <div class="heatmap-card__legend" aria-hidden="true">
42
+ <span class="legend-text">Less</span>
43
+ <span class="cell cell--0"></span>
44
+ <span class="cell cell--1"></span>
45
+ <span class="cell cell--2"></span>
46
+ <span class="cell cell--3"></span>
47
+ <span class="cell cell--4"></span>
48
+ <span class="legend-text">More</span>
49
+ </div>
50
+ </div>
51
+
52
+ <div class="heatmap-card__matrix">
53
+ <div class="heatmap-card__months">
54
+ {months.map((m) => (
55
+ <span class="month-label">{m}</span>
56
+ ))}
57
+ </div>
58
+
59
+ <div class="heatmap-card__grid">
60
+ {weeks.map((week) => (
61
+ <div class="heatmap-col">
62
+ {week.map((level) => (
63
+ <span class:list={['cell', `cell--${level}`]}></span>
64
+ ))}
65
+ </div>
66
+ ))}
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <style>
72
+ .heatmap-card {
73
+ display: flex;
74
+ flex-direction: column;
75
+ gap: 12px;
76
+ padding: var(--sp-6, 1.5rem);
77
+ background: rgba(255, 255, 255, 0.02);
78
+ backdrop-filter: blur(16px);
79
+ border: 1px solid rgba(255, 255, 255, 0.08);
80
+ border-radius: var(--r-xl, 20px);
81
+ width: 100%;
82
+ max-width: 480px;
83
+ font-family: var(--font-sans, system-ui, sans-serif);
84
+ }
85
+
86
+ .heatmap-card__head {
87
+ display: flex;
88
+ justify-content: space-between;
89
+ align-items: flex-start;
90
+ gap: 12px;
91
+ }
92
+
93
+ .heatmap-card__title {
94
+ font-size: 0.95rem;
95
+ font-weight: 700;
96
+ color: var(--c-text-1, #ffffff);
97
+ margin: 0 0 2px 0;
98
+ }
99
+
100
+ .heatmap-card__total {
101
+ font-size: 0.75rem;
102
+ color: var(--c-text-3, #737373);
103
+ }
104
+
105
+ .heatmap-card__legend {
106
+ display: flex;
107
+ align-items: center;
108
+ gap: 4px;
109
+ font-size: 0.7rem;
110
+ color: var(--c-text-3, #737373);
111
+ }
112
+
113
+ .heatmap-card__matrix {
114
+ display: flex;
115
+ flex-direction: column;
116
+ gap: 6px;
117
+ overflow-x: auto;
118
+ padding-bottom: 4px;
119
+ }
120
+
121
+ .heatmap-card__months {
122
+ display: flex;
123
+ justify-content: space-between;
124
+ padding: 0 4px;
125
+ font-size: 0.68rem;
126
+ color: var(--c-text-3, #737373);
127
+ font-weight: 600;
128
+ }
129
+
130
+ .heatmap-card__grid {
131
+ display: flex;
132
+ gap: 4px;
133
+ }
134
+
135
+ .heatmap-col {
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: 4px;
139
+ }
140
+
141
+ .cell {
142
+ width: 12px;
143
+ height: 12px;
144
+ border-radius: 3px;
145
+ transition: transform 0.15s ease;
146
+ }
147
+
148
+ .heatmap-card__grid .cell:hover {
149
+ transform: scale(1.3);
150
+ }
151
+
152
+ .cell--0 { background: rgba(255, 255, 255, 0.05); }
153
+ .cell--1 { background: rgba(184, 134, 11, 0.25); }
154
+ .cell--2 { background: rgba(184, 134, 11, 0.55); }
155
+ .cell--3 { background: rgba(212, 160, 23, 0.85); }
156
+ .cell--4 { background: #fef3c7; box-shadow: 0 0 6px rgba(254, 243, 199, 0.6); }
157
+ </style>
@@ -0,0 +1,109 @@
1
+ ---
2
+ /**
3
+ * MetricComparisonRow — Horizontal gauge comparing actual performance vs target benchmark with percentage badge.
4
+ *
5
+ * @param {string} title - Benchmark description (e.g. 'Monthly Revenue Target').
6
+ * @param {number} current - Current numeric value figure.
7
+ * @param {number} target - Goal target value.
8
+ * @param {string} unit - Unit prefix or suffix (e.g. '$', 'ms', 'users'). Default is '$'.
9
+ * @param {string} color - Progress bar accent color. Default is '#d4a017'.
10
+ */
11
+ interface Props {
12
+ title: string;
13
+ current: number;
14
+ target: number;
15
+ unit?: string;
16
+ color?: string;
17
+ }
18
+
19
+ const {
20
+ title = 'Quarterly Revenue Target',
21
+ current = 8450,
22
+ target = 10000,
23
+ unit = '$',
24
+ color = '#d4a017',
25
+ } = Astro.props;
26
+
27
+ const safeCurrent = current ?? 0;
28
+ const safeTarget = (target && target > 0) ? target : 1;
29
+ const percent = Math.min(100, Math.round((safeCurrent / safeTarget) * 100));
30
+ ---
31
+
32
+ <div class="metric-row" style={`--metric-accent: ${color};`}>
33
+ <div class="metric-row__header">
34
+ <div class="metric-row__title-wrap">
35
+ <h5 class="metric-row__title">{title}</h5>
36
+ <span class="metric-row__ratio">{unit}{safeCurrent.toLocaleString()} of {unit}{safeTarget.toLocaleString()}</span>
37
+ </div>
38
+ <span class="metric-row__badge">{percent}%</span>
39
+ </div>
40
+
41
+ <div class="metric-row__track" role="progressbar" aria-valuenow={safeCurrent} aria-valuemax={safeTarget} aria-valuemin="0">
42
+ <div class="metric-row__fill" style={`width: ${percent}%;`}></div>
43
+ </div>
44
+ </div>
45
+
46
+ <style>
47
+ .metric-row {
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: 8px;
51
+ width: 100%;
52
+ max-width: 440px;
53
+ padding: var(--sp-4, 1rem);
54
+ background: rgba(255, 255, 255, 0.02);
55
+ backdrop-filter: blur(12px);
56
+ border: 1px solid rgba(255, 255, 255, 0.06);
57
+ border-radius: var(--r-md, 14px);
58
+ font-family: var(--font-sans, system-ui, sans-serif);
59
+ }
60
+
61
+ .metric-row__header {
62
+ display: flex;
63
+ justify-content: space-between;
64
+ align-items: flex-end;
65
+ }
66
+
67
+ .metric-row__title-wrap {
68
+ display: flex;
69
+ flex-direction: column;
70
+ gap: 2px;
71
+ }
72
+
73
+ .metric-row__title {
74
+ font-size: 0.88rem;
75
+ font-weight: 700;
76
+ color: var(--c-text-1, #ffffff);
77
+ margin: 0;
78
+ }
79
+
80
+ .metric-row__ratio {
81
+ font-size: 0.75rem;
82
+ color: var(--c-text-3, #737373);
83
+ }
84
+
85
+ .metric-row__badge {
86
+ font-size: 0.82rem;
87
+ font-weight: 800;
88
+ color: var(--metric-accent);
89
+ background: rgba(184, 134, 11, 0.12);
90
+ border: 1px solid rgba(184, 134, 11, 0.3);
91
+ padding: 2px 8px;
92
+ border-radius: var(--r-full, 9999px);
93
+ }
94
+
95
+ .metric-row__track {
96
+ height: 8px;
97
+ background: rgba(255, 255, 255, 0.06);
98
+ border-radius: var(--r-full, 9999px);
99
+ overflow: hidden;
100
+ }
101
+
102
+ .metric-row__fill {
103
+ height: 100%;
104
+ background: linear-gradient(90deg, #b8860b, var(--metric-accent));
105
+ border-radius: inherit;
106
+ box-shadow: 0 0 10px rgba(212, 160, 23, 0.4);
107
+ transition: width 0.6s cubic-bezier(0.16, 1, 0.3, 1);
108
+ }
109
+ </style>
@@ -0,0 +1,168 @@
1
+ ---
2
+ /**
3
+ * StatusTimelineFeed — Chronological event audit feed with status icons, timestamps, and detail lines.
4
+ *
5
+ * @param {Array<{title: string, desc: string, time: string, status: 'done' | 'active' | 'pending'}>} events - Timeline events.
6
+ */
7
+ interface Props {
8
+ events?: Array<{
9
+ title: string;
10
+ desc: string;
11
+ time: string;
12
+ status: 'done' | 'active' | 'pending';
13
+ }>;
14
+ }
15
+
16
+ const {
17
+ events = [
18
+ {
19
+ title: 'Production Deployment #104',
20
+ desc: 'Container vtzmu4t7 synchronized with latest Git commit.',
21
+ time: 'Just now',
22
+ status: 'done',
23
+ },
24
+ {
25
+ title: 'Automated Playwright Audits',
26
+ desc: 'All 214 pages verified with 0 visual regressions.',
27
+ time: '12 mins ago',
28
+ status: 'done',
29
+ },
30
+ {
31
+ title: 'Edge Cache Invalidation',
32
+ desc: 'Purging stale Cloudflare and Traefik edge routes.',
33
+ time: 'In progress',
34
+ status: 'active',
35
+ },
36
+ {
37
+ title: 'Global CDN Replication',
38
+ desc: 'Waiting for European and US west points of presence.',
39
+ time: 'Queued',
40
+ status: 'pending',
41
+ },
42
+ ],
43
+ } = Astro.props;
44
+ ---
45
+
46
+ <div class="status-feed">
47
+ <div class="status-feed__line" aria-hidden="true"></div>
48
+ {events.map((evt) => (
49
+ <div class:list={['status-feed__item', `status-feed__item--${evt.status}`]}>
50
+ <div class="status-feed__bullet">
51
+ {evt.status === 'done' && '✓'}
52
+ {evt.status === 'active' && '●'}
53
+ {evt.status === 'pending' && '○'}
54
+ </div>
55
+ <div class="status-feed__content">
56
+ <div class="status-feed__header">
57
+ <h5 class="status-feed__title">{evt.title}</h5>
58
+ <span class="status-feed__time">{evt.time}</span>
59
+ </div>
60
+ <p class="status-feed__desc">{evt.desc}</p>
61
+ </div>
62
+ </div>
63
+ ))}
64
+ </div>
65
+
66
+ <style>
67
+ .status-feed {
68
+ position: relative;
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: 20px;
72
+ width: 100%;
73
+ max-width: 440px;
74
+ font-family: var(--font-sans, system-ui, sans-serif);
75
+ padding: 4px 0;
76
+ }
77
+
78
+ .status-feed__line {
79
+ position: absolute;
80
+ top: 14px;
81
+ bottom: 14px;
82
+ left: 12px;
83
+ width: 2px;
84
+ background: rgba(255, 255, 255, 0.08);
85
+ pointer-events: none;
86
+ }
87
+
88
+ .status-feed__item {
89
+ position: relative;
90
+ display: flex;
91
+ align-items: flex-start;
92
+ gap: 16px;
93
+ }
94
+
95
+ .status-feed__bullet {
96
+ position: relative;
97
+ z-index: 2;
98
+ display: grid;
99
+ place-items: center;
100
+ width: 26px;
101
+ height: 26px;
102
+ border-radius: 50%;
103
+ background: #0a0a0e;
104
+ border: 1px solid rgba(255, 255, 255, 0.15);
105
+ font-size: 0.75rem;
106
+ font-weight: 900;
107
+ flex-shrink: 0;
108
+ color: var(--c-text-3, #737373);
109
+ }
110
+
111
+ .status-feed__item--done .status-feed__bullet {
112
+ background: rgba(16, 185, 129, 0.15);
113
+ border-color: #10b981;
114
+ color: #10b981;
115
+ }
116
+
117
+ .status-feed__item--active .status-feed__bullet {
118
+ background: rgba(212, 160, 23, 0.2);
119
+ border-color: var(--c-primary-light, #d4a017);
120
+ color: var(--c-primary-light, #d4a017);
121
+ box-shadow: 0 0 10px rgba(212, 160, 23, 0.5);
122
+ animation: status-pulse 2s infinite ease-in-out;
123
+ }
124
+
125
+ .status-feed__content {
126
+ flex: 1;
127
+ padding: 0.65rem 1rem;
128
+ background: rgba(255, 255, 255, 0.02);
129
+ border: 1px solid rgba(255, 255, 255, 0.06);
130
+ border-radius: var(--r-md, 12px);
131
+ transition: border-color 0.2s ease;
132
+ }
133
+
134
+ .status-feed__content:hover {
135
+ border-color: rgba(184, 134, 11, 0.3);
136
+ }
137
+
138
+ .status-feed__header {
139
+ display: flex;
140
+ justify-content: space-between;
141
+ align-items: center;
142
+ margin-bottom: 2px;
143
+ }
144
+
145
+ .status-feed__title {
146
+ font-size: 0.85rem;
147
+ font-weight: 700;
148
+ color: var(--c-text-1, #ffffff);
149
+ margin: 0;
150
+ }
151
+
152
+ .status-feed__time {
153
+ font-size: 0.7rem;
154
+ color: var(--c-text-3, #737373);
155
+ }
156
+
157
+ .status-feed__desc {
158
+ font-size: 0.78rem;
159
+ color: var(--c-text-2, #a3a3a3);
160
+ margin: 0;
161
+ line-height: 1.45;
162
+ }
163
+
164
+ @keyframes status-pulse {
165
+ 0%, 100% { transform: scale(1); }
166
+ 50% { transform: scale(1.1); }
167
+ }
168
+ </style>
@@ -21,3 +21,6 @@ export { default as TimelineHorizSteps } from './TimelineHorizSteps.astro';
21
21
  export { default as TreeItemInteractive } from './TreeItemInteractive.astro';
22
22
  export { default as UserDirectoryItem } from './UserDirectoryItem.astro';
23
23
  export { default as VerticalTimeline } from './VerticalTimeline.astro';
24
+ export { default as MetricComparisonRow } from './MetricComparisonRow.astro';
25
+ export { default as ActivityHeatmapGrid } from './ActivityHeatmapGrid.astro';
26
+ export { default as StatusTimelineFeed } from './StatusTimelineFeed.astro';
@@ -0,0 +1,118 @@
1
+ ---
2
+ /**
3
+ * ProgressRingCounter — Circular SVG progress ring with centered countdown or metric value.
4
+ *
5
+ * @param {number} value - Current counter or progress value. Default is 75.
6
+ * @param {number} max - Maximum value threshold. Default is 100.
7
+ * @param {number} size - Pixel diameter of the ring. Default is 96.
8
+ * @param {number} strokeWidth - Thickness of the ring stroke. Default is 6.
9
+ * @param {string} color - Primary gauge fill color. Default is '#d4a017'.
10
+ * @param {string} label - Subtitle label beneath figure. Default is 'COMPLETED'.
11
+ */
12
+ interface Props {
13
+ value?: number;
14
+ max?: number;
15
+ size?: number;
16
+ strokeWidth?: number;
17
+ color?: string;
18
+ label?: string;
19
+ }
20
+
21
+ const {
22
+ value = 75,
23
+ max = 100,
24
+ size = 96,
25
+ strokeWidth = 6,
26
+ color = '#d4a017',
27
+ label = 'COMPLETED',
28
+ } = Astro.props;
29
+
30
+ const radius = (size - strokeWidth) / 2;
31
+ const circumference = 2 * Math.PI * radius;
32
+ const percent = Math.min(100, Math.max(0, (value / max) * 100));
33
+ const strokeDashoffset = circumference - (percent / 100) * circumference;
34
+ ---
35
+
36
+ <div class="ring-counter" style={`--ring-size: ${size}px; --ring-color: ${color};`}>
37
+ <svg width={size} height={size} class="ring-counter__svg">
38
+ <circle
39
+ class="ring-counter__bg"
40
+ cx={size / 2}
41
+ cy={size / 2}
42
+ r={radius}
43
+ stroke-width={strokeWidth}
44
+ />
45
+ <circle
46
+ class="ring-counter__meter"
47
+ cx={size / 2}
48
+ cy={size / 2}
49
+ r={radius}
50
+ stroke-width={strokeWidth}
51
+ stroke-dasharray={circumference}
52
+ stroke-dashoffset={strokeDashoffset}
53
+ />
54
+ </svg>
55
+
56
+ <div class="ring-counter__center">
57
+ <span class="val">{Math.round(percent)}%</span>
58
+ {label && <span class="lbl">{label}</span>}
59
+ </div>
60
+ </div>
61
+
62
+ <style>
63
+ .ring-counter {
64
+ position: relative;
65
+ width: var(--ring-size);
66
+ height: var(--ring-size);
67
+ display: inline-flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ font-family: var(--font-sans, system-ui, sans-serif);
71
+ user-select: none;
72
+ }
73
+
74
+ .ring-counter__svg {
75
+ transform: rotate(-90deg);
76
+ width: 100%;
77
+ height: 100%;
78
+ }
79
+
80
+ .ring-counter__bg {
81
+ fill: none;
82
+ stroke: rgba(255, 255, 255, 0.08);
83
+ }
84
+
85
+ .ring-counter__meter {
86
+ fill: none;
87
+ stroke: var(--ring-color);
88
+ stroke-linecap: round;
89
+ transition: stroke-dashoffset 0.8s cubic-bezier(0.16, 1, 0.3, 1);
90
+ filter: drop-shadow(0 0 6px rgba(212, 160, 23, 0.35));
91
+ }
92
+
93
+ .ring-counter__center {
94
+ position: absolute;
95
+ inset: 0;
96
+ display: flex;
97
+ flex-direction: column;
98
+ align-items: center;
99
+ justify-content: center;
100
+ text-align: center;
101
+ }
102
+
103
+ .ring-counter__center .val {
104
+ font-size: 1.25rem;
105
+ font-weight: 800;
106
+ color: var(--c-text-1, #ffffff);
107
+ line-height: 1;
108
+ }
109
+
110
+ .ring-counter__center .lbl {
111
+ font-size: 0.55rem;
112
+ font-weight: 700;
113
+ letter-spacing: 0.08em;
114
+ color: var(--c-text-3, #737373);
115
+ margin-top: 4px;
116
+ text-transform: uppercase;
117
+ }
118
+ </style>