@voidzero-dev/vitepress-theme 2.0.1 → 2.1.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 (79) hide show
  1. package/package.json +2 -1
  2. package/src/aliases.js +14 -0
  3. package/src/vitepress/assets/clients/clickup.svg +5 -0
  4. package/src/vitepress/assets/clients/stripe.svg +3 -0
  5. package/src/vitepress/components/oss/Footer.vue +4 -21
  6. package/src/vitepress/components/oss/Header.vue +82 -180
  7. package/src/vitepress/components/oss/Sponsors.vue +3 -3
  8. package/src/vitepress/components/oss/TopBanner.vue +20 -79
  9. package/src/vitepress/components/oss/TrustedBy.vue +1 -1
  10. package/src/vitepress/components/vite/Community.vue +3 -3
  11. package/src/vitepress/components/vite/FeatureGrid1.vue +63 -0
  12. package/src/vitepress/components/vite/{FeatureGrid.vue → FeatureGrid2.vue} +8 -10
  13. package/src/vitepress/components/vite/Hero.vue +6 -15
  14. package/src/vitepress/components/vitepress-default/VPDocOutlineItem.vue +2 -2
  15. package/src/vitepress/components/vitepress-default/VPFlyout.vue +1 -1
  16. package/src/vitepress/components/vitepress-default/VPMenuLink.vue +1 -1
  17. package/src/vitepress/components/vitepress-default/VPNavBarMenuLink.vue +1 -1
  18. package/src/vitepress/components/vitepress-default/VPSidebarItem.vue +1 -1
  19. package/src/vitepress/components/vitepress-default/VPSocialLink.vue +1 -2
  20. package/src/vitepress/fonts/APK-Protocol-Semi-Bold.woff2 +0 -0
  21. package/src/vitepress/fonts/inter-italic-cyrillic-ext.woff2 +0 -0
  22. package/src/vitepress/fonts/inter-italic-cyrillic.woff2 +0 -0
  23. package/src/vitepress/fonts/inter-italic-greek-ext.woff2 +0 -0
  24. package/src/vitepress/fonts/inter-italic-greek.woff2 +0 -0
  25. package/src/vitepress/fonts/inter-italic-latin-ext.woff2 +0 -0
  26. package/src/vitepress/fonts/inter-italic-latin.woff2 +0 -0
  27. package/src/vitepress/fonts/inter-italic-vietnamese.woff2 +0 -0
  28. package/src/vitepress/fonts/inter-roman-cyrillic-ext.woff2 +0 -0
  29. package/src/vitepress/fonts/inter-roman-cyrillic.woff2 +0 -0
  30. package/src/vitepress/fonts/inter-roman-greek-ext.woff2 +0 -0
  31. package/src/vitepress/fonts/inter-roman-greek.woff2 +0 -0
  32. package/src/vitepress/fonts/inter-roman-latin-ext.woff2 +0 -0
  33. package/src/vitepress/fonts/inter-roman-latin.woff2 +0 -0
  34. package/src/vitepress/fonts/inter-roman-vietnamese.woff2 +0 -0
  35. package/src/vitepress/index.ts +64 -230
  36. package/src/vitepress/layouts/VPLayout.vue +2 -17
  37. package/src/vitepress/styles/tokens.css +194 -10
  38. package/src/vitepress/types/theme-context.ts +33 -0
  39. package/src/vitepress/assets/clients/beehiiv.svg +0 -30
  40. package/src/vitepress/assets/clients/excalidraw.svg +0 -82
  41. package/src/vitepress/assets/clients/get-your-guide.svg +0 -1
  42. package/src/vitepress/assets/clients/posthog.svg +0 -1
  43. package/src/vitepress/assets/clients/ramp.svg +0 -1
  44. package/src/vitepress/assets/clients/shopee.svg +0 -55
  45. package/src/vitepress/components/vite/FeaturePanel1.vue +0 -41
  46. package/src/vitepress/components/vite/FeaturePanel2.vue +0 -37
  47. package/src/vitepress/components/vite/FeaturePanel3.vue +0 -43
  48. package/src/vitepress/components/vite/FeaturePanel4.vue +0 -46
  49. package/src/vitepress/components/voidzero/Footer.vue +0 -65
  50. package/src/vitepress/components/voidzero/Header.vue +0 -560
  51. package/src/vitepress/components/voidzero/Megamenu.vue +0 -190
  52. package/src/vitepress/components/voidzero/about/CareerCTA.vue +0 -56
  53. package/src/vitepress/components/voidzero/about/Hero.vue +0 -206
  54. package/src/vitepress/components/voidzero/about/Investors.vue +0 -112
  55. package/src/vitepress/components/voidzero/about/TeamGrid.vue +0 -161
  56. package/src/vitepress/components/voidzero/about/TeamSectionHeading.vue +0 -13
  57. package/src/vitepress/components/voidzero/blog/BlogArchive.vue +0 -223
  58. package/src/vitepress/components/voidzero/blog/BlogSingleContent.vue +0 -364
  59. package/src/vitepress/components/voidzero/blog/BlogSingleHero.vue +0 -113
  60. package/src/vitepress/components/voidzero/blog/BlogSingleRelated.vue +0 -92
  61. package/src/vitepress/components/voidzero/blog/FeaturedArticles.vue +0 -146
  62. package/src/vitepress/components/voidzero/blog/types.ts +0 -56
  63. package/src/vitepress/components/voidzero/home/CaseStudySlider.vue +0 -235
  64. package/src/vitepress/components/voidzero/home/CustomersSectionHeading.vue +0 -5
  65. package/src/vitepress/components/voidzero/home/GitHubStats.vue +0 -27
  66. package/src/vitepress/components/voidzero/home/Hero.vue +0 -69
  67. package/src/vitepress/components/voidzero/home/Investors.vue +0 -30
  68. package/src/vitepress/components/voidzero/home/NewsletterCTA.vue +0 -23
  69. package/src/vitepress/components/voidzero/home/OpenSourceSectionHeading.vue +0 -6
  70. package/src/vitepress/components/voidzero/home/OpenSourceSectionProjects.vue +0 -419
  71. package/src/vitepress/components/voidzero/home/Resources.vue +0 -144
  72. package/src/vitepress/components/voidzero/home/Statistics.vue +0 -507
  73. package/src/vitepress/components/voidzero/home/StatisticsSectionHeading.vue +0 -5
  74. package/src/vitepress/components/voidzero/home/TeamCTA.vue +0 -17
  75. package/src/vitepress/components/voidzero/home/TrustedBy.vue +0 -248
  76. package/src/vitepress/components/voidzero/home/VitePlusSectionFeatures.vue +0 -55
  77. package/src/vitepress/components/voidzero/home/VitePlusSectionHeading.vue +0 -17
  78. package/src/vitepress/fonts/KHTeka-Medium.woff2 +0 -0
  79. package/src/vitepress/fonts/KHTeka-Regular.woff2 +0 -0
@@ -1,507 +0,0 @@
1
- <script setup lang="ts">
2
- import { onMounted, onUnmounted, ref, computed, watch, shallowRef, markRaw, Ref, Component } from 'vue'
3
- import type { Chart as ChartType, ChartData } from 'chart.js'
4
- import moment from 'moment'
5
- import viteIcon from '@assets/icons/vite-dark.svg'
6
- import vitestIcon from '@assets/icons/vitest-dark.svg'
7
- import rolldownIcon from '@assets/icons/rolldown-dark.svg'
8
- import oxcIcon from '@assets/icons/oxc-dark.svg'
9
-
10
- interface GithubStat {
11
- repo: string
12
- stars: number
13
- contributors: number
14
- }
15
-
16
- interface Props {
17
- statsUrl?: string
18
- githubStats?: GithubStat[]
19
- }
20
-
21
- const props = withDefaults(defineProps<Props>(), {
22
- statsUrl: '/stats.json',
23
- githubStats: () => []
24
- })
25
-
26
- type ProjectKey = 'vite' | 'vitest' | 'rolldown' | 'oxlint'
27
-
28
- const projects = [
29
- { key: 'vite' as ProjectKey, name: 'Vite', icon: viteIcon, color: '#6C3BFF' },
30
- { key: 'vitest' as ProjectKey, name: 'Vitest', icon: vitestIcon, color: '#22FF73' },
31
- { key: 'rolldown' as ProjectKey, name: 'Rolldown', icon: rolldownIcon, color: '#FF5500' },
32
- { key: 'oxlint' as ProjectKey, name: 'Oxc', icon: oxcIcon, color: '#32F3E9' }
33
- ]
34
-
35
- // Repo name to project key mapping
36
- const repoMapping: Record<string, ProjectKey> = {
37
- 'vitejs/vite': 'vite',
38
- 'vitest-dev/vitest': 'vitest',
39
- 'rolldown/rolldown': 'rolldown',
40
- 'oxc-project/oxc': 'oxlint'
41
- }
42
-
43
- const selectedProject = ref<ProjectKey>('vite')
44
- const allProjectData = ref<Record<ProjectKey, { x: number; y: number }[]>>({
45
- vite: [],
46
- vitest: [],
47
- rolldown: [],
48
- oxlint: []
49
- })
50
- const allTotalDownloads = ref<Record<ProjectKey, number>>({
51
- vite: 0,
52
- vitest: 0,
53
- rolldown: 0,
54
- oxlint: 0
55
- })
56
- const allGithubStars = ref<Record<ProjectKey, number>>({
57
- vite: 0,
58
- vitest: 0,
59
- rolldown: 0,
60
- oxlint: 0
61
- })
62
- const allContributors = ref<Record<ProjectKey, number>>({
63
- vite: 0,
64
- vitest: 0,
65
- rolldown: 0,
66
- oxlint: 0
67
- })
68
-
69
- const dataPoints = ref<{ x: number; y: number }[]>([])
70
- const stats: Ref<ChartData<'line', { x: number; y: number }[], unknown>> = ref({
71
- datasets: []
72
- })
73
- const statsChart = shallowRef<ChartType | null>(null)
74
-
75
- // Dynamically loaded NumberFlow - use shallowRef for components
76
- const NumberFlow = shallowRef<Component | null>(null)
77
- const continuous = shallowRef<any>(null)
78
-
79
- const totalDownloads = computed(() => {
80
- return allTotalDownloads.value[selectedProject.value]
81
- })
82
-
83
- const weeklyDownloads = computed(() => {
84
- const projectData = allProjectData.value[selectedProject.value]
85
- return (projectData.slice().reverse().find(p => p.y > 0)?.y || 0)
86
- })
87
-
88
- const weeklyDownloadsFormatted = computed(() => {
89
- const value = weeklyDownloads.value
90
- if (value >= 1000000) {
91
- return (value / 1000000).toFixed(1) + 'M'
92
- } else if (value >= 1000) {
93
- return (value / 1000).toFixed(1) + 'K'
94
- }
95
- return value.toString()
96
- })
97
-
98
- const githubStars = computed(() => {
99
- return allGithubStars.value[selectedProject.value]
100
- })
101
-
102
- const githubStarsFormatted = computed(() => {
103
- const value = githubStars.value
104
- if (value >= 1000000) {
105
- return (value / 1000000).toFixed(1) + 'M'
106
- } else if (value >= 1000) {
107
- return (value / 1000).toFixed(1) + 'K'
108
- }
109
- return value.toString()
110
- })
111
-
112
- const contributors = computed(() => {
113
- return allContributors.value[selectedProject.value]
114
- })
115
-
116
- const contributorsFormatted = computed(() => {
117
- const value = contributors.value
118
- if (value >= 1000000) {
119
- return (value / 1000000).toFixed(1) + 'M'
120
- } else if (value >= 1000) {
121
- return (value / 1000).toFixed(1) + 'K'
122
- }
123
- return value.toString()
124
- })
125
-
126
- const selectedProjectData = computed(() => {
127
- return projects.find(p => p.key === selectedProject.value)
128
- })
129
-
130
- const dropdownOpen = ref(false)
131
- const dropdownButtonRef = ref<HTMLButtonElement | null>(null)
132
- const focusedIndex = ref<number>(0)
133
- const announcement = ref<string>('')
134
-
135
- // Helper function to convert hex to rgba
136
- const hexToRgba = (hex: string, alpha: number) => {
137
- const r = parseInt(hex.slice(1, 3), 16)
138
- const g = parseInt(hex.slice(3, 5), 16)
139
- const b = parseInt(hex.slice(5, 7), 16)
140
- return `rgba(${r}, ${g}, ${b}, ${alpha})`
141
- }
142
-
143
- // Close dropdown when clicking outside
144
- const closeDropdown = (e: MouseEvent) => {
145
- const target = e.target as HTMLElement
146
- if (!target.closest('.project-dropdown')) {
147
- dropdownOpen.value = false
148
- }
149
- }
150
-
151
- const handleKeydown = (e: KeyboardEvent) => {
152
- if (!dropdownOpen.value) {
153
- // When closed, open with Enter or Space or ArrowDown
154
- if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
155
- e.preventDefault()
156
- dropdownOpen.value = true
157
- focusedIndex.value = projects.findIndex(p => p.key === selectedProject.value)
158
- }
159
- } else {
160
- // When open, handle navigation
161
- switch (e.key) {
162
- case 'Escape':
163
- e.preventDefault()
164
- dropdownOpen.value = false
165
- dropdownButtonRef.value?.focus()
166
- break
167
- case 'ArrowDown':
168
- e.preventDefault()
169
- focusedIndex.value = (focusedIndex.value + 1) % projects.length
170
- break
171
- case 'ArrowUp':
172
- e.preventDefault()
173
- focusedIndex.value = focusedIndex.value === 0 ? projects.length - 1 : focusedIndex.value - 1
174
- break
175
- case 'Enter':
176
- case ' ':
177
- e.preventDefault()
178
- selectedProject.value = projects[focusedIndex.value].key
179
- dropdownOpen.value = false
180
- dropdownButtonRef.value?.focus()
181
- break
182
- case 'Home':
183
- e.preventDefault()
184
- focusedIndex.value = 0
185
- break
186
- case 'End':
187
- e.preventDefault()
188
- focusedIndex.value = projects.length - 1
189
- break
190
- }
191
- }
192
- }
193
-
194
- const selectProject = (projectKey: ProjectKey) => {
195
- const project = projects.find(p => p.key === projectKey)
196
- selectedProject.value = projectKey
197
- dropdownOpen.value = false
198
- dropdownButtonRef.value?.focus()
199
-
200
- // Announce change to screen readers
201
- if (project) {
202
- announcement.value = `${project.name} selected. Showing ${project.name} statistics.`
203
- setTimeout(() => announcement.value = '', 1000)
204
- }
205
- }
206
-
207
- onMounted(async () => {
208
- // Add click listener for closing dropdown
209
- document.addEventListener('click', closeDropdown)
210
-
211
- // Dynamically import Chart.js and NumberFlow client-side only
212
- const { default: Chart } = await import('chart.js/auto')
213
- await import('chartjs-adapter-moment')
214
-
215
- const numberFlowModule = await import('@number-flow/vue')
216
- NumberFlow.value = markRaw(numberFlowModule.default)
217
- continuous.value = markRaw(numberFlowModule.continuous)
218
-
219
- // Get the data from the stats URL
220
- const response = await fetch(props.statsUrl)
221
- const rawData = await response.json()
222
-
223
- // Extract data for all projects
224
- for (const project of projects) {
225
- const projectData = rawData[project.key] || []
226
- let points = projectData
227
- .map((point: {x: number; y: number}) => ({
228
- x: moment(point['x']).valueOf(),
229
- y: point['y']
230
- }))
231
- .filter(point => point.y > 0) // Remove points with y = 0
232
-
233
- // Remove trailing incomplete data points (last 1-2 weeks of data)
234
- // This ensures we don't show incomplete current week data
235
- if (points.length > 2) {
236
- points = points.slice(0, -2) // Remove last 2 points
237
- }
238
-
239
- allProjectData.value[project.key] = points
240
- allTotalDownloads.value[project.key] = rawData.totalDownloads?.[project.key] || 0
241
- }
242
-
243
- // Parse GitHub stats data
244
- if (props.githubStats && props.githubStats.length > 0) {
245
- props.githubStats.forEach((stat) => {
246
- const projectKey = repoMapping[stat.repo]
247
- if (projectKey) {
248
- allGithubStars.value[projectKey] = stat.stars
249
- allContributors.value[projectKey] = stat.contributors
250
- }
251
- })
252
- }
253
-
254
- // Set initial data points for selected project
255
- dataPoints.value = allProjectData.value[selectedProject.value]
256
-
257
- const canvas = document.getElementById('chart-canvas') as HTMLCanvasElement
258
- const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
259
-
260
- // Get initial project color
261
- const initialColor = projects.find(p => p.key === selectedProject.value)?.color || '#6C3BFF'
262
- const gradient = ctx.createLinearGradient(0, 0, -50, 400)
263
- gradient.addColorStop(0, initialColor)
264
- gradient.addColorStop(1, initialColor)
265
-
266
- stats.value = {
267
- datasets: [
268
- {
269
- data: dataPoints.value,
270
- fill: {
271
- target: 'origin',
272
- above: hexToRgba(initialColor, 0.1)
273
- },
274
- pointStyle: false,
275
- borderWidth: 3,
276
- borderColor: gradient,
277
- stepped: false
278
- }
279
- ]
280
- }
281
-
282
- statsChart.value = new Chart(canvas as HTMLCanvasElement, {
283
- type: 'line',
284
- options: {
285
- scales: {
286
- x: {
287
- type: 'time',
288
- time: {
289
- unit: 'week'
290
- },
291
- display: false,
292
- alignToPixels: true,
293
- ticks: { padding: 0 },
294
- border: {
295
- display: false
296
- }
297
- },
298
- y: {
299
- min: 0,
300
- display: false,
301
- ticks: { padding: 0 },
302
- alignToPixels: true,
303
- border: {
304
- display: false
305
- }
306
- }
307
- },
308
- plugins: {
309
- legend: {
310
- display: false
311
- },
312
- tooltip: {
313
- enabled: false
314
- }
315
- },
316
- layout: {
317
- autoPadding: false,
318
- padding: {
319
- left: 0,
320
- right: 0,
321
- top: 0,
322
- bottom: 80
323
- }
324
- },
325
- aspectRatio: 5/3.5
326
- },
327
- data: {
328
- datasets: stats.value.datasets
329
- }
330
- })
331
- })
332
-
333
- onUnmounted(() => {
334
- if (statsChart.value) {
335
- statsChart.value.destroy()
336
- }
337
- // Remove click listener
338
- document.removeEventListener('click', closeDropdown)
339
- })
340
-
341
- // Watch for project changes and update the chart
342
- watch(selectedProject, (newProject) => {
343
- const newData = allProjectData.value[newProject]
344
-
345
- if (statsChart.value && statsChart.value.data && statsChart.value.data.datasets[0]) {
346
- // Get the dataset
347
- const dataset = statsChart.value.data.datasets[0]
348
-
349
- // Replace the data array entirely (not mutate in place)
350
- dataset.data = newData.slice() // Use slice to create a new array
351
-
352
- // Update the color
353
- const canvas = document.getElementById('chart-canvas') as HTMLCanvasElement
354
- const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
355
- const newColor = projects.find(p => p.key === newProject)?.color || '#6C3BFF'
356
- const newGradient = ctx.createLinearGradient(0, 0, -50, 400)
357
- newGradient.addColorStop(0, newColor)
358
- newGradient.addColorStop(1, newColor)
359
- dataset.borderColor = newGradient
360
-
361
- // Update the fill color
362
- if (dataset.fill && typeof dataset.fill === 'object') {
363
- dataset.fill.above = hexToRgba(newColor, 0.1)
364
- }
365
-
366
- // Call update to re-render the chart
367
- statsChart.value.update()
368
- }
369
- }, { flush: 'post' })
370
- </script>
371
-
372
- <template>
373
- <!-- Screen reader announcements -->
374
- <div aria-live="polite" aria-atomic="true" class="sr-only">
375
- {{ announcement }}
376
- </div>
377
-
378
- <section class="wrapper wrapper--ticks border-t grid grid-cols-1 md:grid-cols-2 divide-y md:divide-y-0 md:divide-x divide-ceramic">
379
- <div class="p-6 md:p-10 flex flex-col justify-between">
380
- <h6 class="flex gap-2 items-center">
381
- <span>Total downloads</span>
382
- </h6>
383
- <h1>
384
- <component v-if="NumberFlow" :is="NumberFlow" :plugins="[continuous]" :value="totalDownloads" />
385
- <span v-else>{{ totalDownloads }}</span>
386
- </h1>
387
- </div>
388
- <div class="relative w-full aspect-[5/3.5]">
389
- <div class="absolute top-4 md:top-6 left-4 md:left-6 z-10 project-dropdown">
390
- <button
391
- ref="dropdownButtonRef"
392
- @click.stop="dropdownOpen = !dropdownOpen"
393
- @keydown="handleKeydown"
394
- :aria-expanded="dropdownOpen"
395
- :aria-label="`Select project: ${selectedProjectData?.name}`"
396
- aria-haspopup="listbox"
397
- class="button cursor-pointer"
398
- >
399
- <img v-if="selectedProjectData" :src="selectedProjectData.icon" class="size-6" :alt="selectedProjectData?.name">
400
- <span>{{ selectedProjectData?.name }} <span class="hidden md:inline-block">Downloads</span></span>
401
- <svg class="size-3 ml-1 fill-primary" :class="{ 'rotate-180': dropdownOpen }" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 7" aria-hidden="true">
402
- <path d="M1.41 0L6 4.58 10.59 0 12 1.42l-6 6-6-6z"/>
403
- </svg>
404
- </button>
405
- <ul
406
- v-if="dropdownOpen"
407
- role="listbox"
408
- :aria-label="'Project selection'"
409
- class="absolute top-full left-0 mt-1 bg-white border border-stroke rounded-lg shadow-lg overflow-hidden min-w-full select-none"
410
- >
411
- <li
412
- v-for="(project, index) in projects"
413
- :key="project.key"
414
- role="option"
415
- :aria-selected="selectedProject === project.key"
416
- >
417
- <button
418
- @click.stop="selectProject(project.key)"
419
- @keydown="handleKeydown"
420
- :class="{
421
- 'bg-gray-50': selectedProject === project.key,
422
- 'bg-gray-100': focusedIndex === index
423
- }"
424
- class="select-none cursor-pointer flex items-center gap-2 px-3 py-2 w-full text-left text-base hover:bg-stroke/20 focus:outline-none focus:bg-stroke/20 hover:text-primary focus:text-primary"
425
- >
426
- <img :src="project.icon" class="size-5" alt="">
427
- <span class="font-medium">{{ project.name }}</span>
428
- <svg v-if="selectedProject === project.key" class="size-4 ml-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
429
- <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
430
- </svg>
431
- </button>
432
- </li>
433
- </ul>
434
- </div>
435
- <canvas id="chart-canvas" width="200" height="200"></canvas>
436
- <div class="flex justify-between absolute bottom-6 md:bottom-10 left-6 md:left-10 right-6 md:right-10">
437
- <p class="font-mono text-sm text-nickel">April 2020</p>
438
- <p class="font-mono text-sm text-nickel">Today</p>
439
- </div>
440
- </div>
441
- </section>
442
- <section class="wrapper wrapper--ticks border-t grid grid-cols-1 md:grid-cols-3 divide-y md:divide-y-0 md:divide-x divide-ceramic">
443
- <div class="flex flex-col gap-2 p-6 md:p-10">
444
- <h2>
445
- <component
446
- v-if="NumberFlow"
447
- :is="NumberFlow"
448
- :plugins="[continuous]"
449
- :value="weeklyDownloads"
450
- :format="{ notation: 'compact', maximumFractionDigits: 1 }"
451
- />
452
- <span v-else>{{ weeklyDownloadsFormatted }}</span>+
453
- </h2>
454
- <p class="lead">Weekly NPM downloads</p>
455
- </div>
456
- <div class="flex flex-col gap-2 p-6 md:p-10">
457
- <h2>
458
- <component
459
- v-if="NumberFlow"
460
- :is="NumberFlow"
461
- :plugins="[continuous]"
462
- :value="githubStars"
463
- :format="{ notation: 'compact', maximumFractionDigits: 1 }"
464
- />
465
- <span v-else>{{ githubStarsFormatted }}</span>+
466
- </h2>
467
- <p class="lead">GitHub Stars</p>
468
- </div>
469
- <div class="flex flex-col gap-2 p-6 md:p-10">
470
- <h2>
471
- <component
472
- v-if="NumberFlow"
473
- :is="NumberFlow"
474
- :plugins="[continuous]"
475
- :value="contributors"
476
- :format="{ notation: 'compact', maximumFractionDigits: 1 }"
477
- />
478
- <span v-else>{{ contributorsFormatted }}</span>+
479
- </h2>
480
- <p class="lead">Contributors</p>
481
- </div>
482
- </section>
483
- </template>
484
-
485
- <style scoped>
486
- #chart-canvas {
487
- margin: 0;
488
- padding: 0;
489
- position: absolute;
490
- inset: 0;
491
- width: 100%;
492
- height: 100%;
493
- pointer-events: none;
494
- }
495
-
496
- .sr-only {
497
- position: absolute;
498
- width: 1px;
499
- height: 1px;
500
- padding: 0;
501
- margin: -1px;
502
- overflow: hidden;
503
- clip: rect(0, 0, 0, 0);
504
- white-space: nowrap;
505
- border-width: 0;
506
- }
507
- </style>
@@ -1,5 +0,0 @@
1
- <template>
2
- <section class="wrapper px-5 md:px-10 h-36 md:h-48 sm:h-80 flex flex-col justify-center gap-5">
3
- <h3 class="text-start max-w-lg text-balance">Trusted by millions of developers around the world</h3>
4
- </section>
5
- </template>
@@ -1,17 +0,0 @@
1
- <script setup lang="ts">
2
-
3
- </script>
4
-
5
- <template>
6
- <section class="wrapper wrapper--ticks border-t px-5 md:px-10 py-10 md:py-40 flex flex-col items-center gap-10 bg-[#FBFAF7]">
7
- <h3 class="max-w-[45rem] text-center text-balance mx-auto">Our mission is to make the next generation of JavaScript developers more productive than ever before.</h3>
8
- <div class="flex flex-row gap-6 items-baseline">
9
- <a href="/about" class="button">Learn more</a>
10
- <!-- <a href="/about" class="text-primary font-medium border-b">Meet our team →</a>-->
11
- </div>
12
- </section>
13
- </template>
14
-
15
- <style scoped>
16
-
17
- </style>