diff --git a/packages/docs/src/components/ComparisonBarChart.astro b/packages/docs/src/components/ComparisonBarChart.astro new file mode 100644 index 00000000..6f46cdb7 --- /dev/null +++ b/packages/docs/src/components/ComparisonBarChart.astro @@ -0,0 +1,346 @@ +--- +import type { ChartDatum } from '../lib/types' + +interface Props { + title: string + data: ChartDatum[] + valueFormat: 'count' | 'mb' +} + +const { title, data, valueFormat } = Astro.props +const chartPayload = JSON.stringify({ data, valueFormat }) +--- + +
+

{title}

+
+ +
+
+
+ + + + diff --git a/packages/docs/src/components/DependencyStats.astro b/packages/docs/src/components/DependencyStats.astro index e0b18652..0c25ce7f 100644 --- a/packages/docs/src/components/DependencyStats.astro +++ b/packages/docs/src/components/DependencyStats.astro @@ -1,13 +1,45 @@ --- import { getCollection } from 'astro:content' -import { formatBytesToMB, formatTimeMs, getFrameworkSlug } from '../lib/utils' +import { + BYTES_PER_MB, + formatBytesToMB, + formatTimeMs, + getFrameworkSlug, +} from '../lib/utils' import '../styles/shared.css' +import ComparisonBarChart from './ComparisonBarChart.astro' const devtimeEntries = await getCollection('devtime') const runtimeEntries = await getCollection('runtime') const starterStats = devtimeEntries.map((entry) => entry.data) const ssrStats = runtimeEntries.map((entry) => entry.data) + +const validForCharts = starterStats.filter( + (f) => + f?.name != null && + Number.isFinite(f.devDependencies) && + Number.isFinite(f.prodDependencies) && + Number.isFinite(f.buildOutputSize) && + Number.isFinite(f.nodeModulesSizeProdOnly), +) + +const depsData = validForCharts.map((f) => ({ + name: f.name, + value: f.devDependencies, +})) +const prodDepsData = validForCharts.map((f) => ({ + name: f.name, + value: f.prodDependencies, +})) +const buildSizeData = validForCharts.map((f) => ({ + name: f.name, + value: f.buildOutputSize / BYTES_PER_MB, +})) +const buildSizeProdData = validForCharts.map((f) => ({ + name: f.name, + value: f.nodeModulesSizeProdOnly / BYTES_PER_MB, +})) ---
@@ -83,6 +115,65 @@ const ssrStats = runtimeEntries.map((entry) => entry.data)
+
+
+ + +
+
+
+ +
+ +
+
+
@@ -121,6 +212,65 @@ const ssrStats = runtimeEntries.map((entry) => entry.data)
+
+
+ + +
+
+
+ +
+ +
+
+

Runtime Performance

SSR Performance

@@ -473,6 +623,125 @@ const ssrStats = runtimeEntries.map((entry) => entry.data) color: var(--ft-muted); } + .section-with-tabs { + margin-top: 16px; + margin-bottom: 2em; + } + + .chart-tabs-container { + margin-top: 1.5em; + padding: 1em 1.25em; + border: 1px solid var(--ft-border); + border-radius: 12px; + background: var(--ft-bg-muted); + } + + .tablist { + display: flex; + gap: 0; + margin-bottom: 0; + border-bottom: 1px solid var(--ft-border); + } + + .chart-tablist { + margin: 0 0 1em 0; + padding: 0; + border-bottom: none; + gap: 8px; + } + + .tab { + padding: 10px 20px; + font-size: 14px; + font-weight: 500; + color: var(--ft-muted); + background: transparent; + border: none; + border-bottom: 2px solid transparent; + margin-bottom: -1px; + cursor: pointer; + font-family: inherit; + transition: + color 0.2s, + border-color 0.2s, + background-color 0.2s; + } + + .chart-tablist .tab { + padding: 8px 16px; + border-radius: 8px; + border-bottom: none; + margin-bottom: 0; + } + + .tab:hover { + color: var(--ft-text); + } + + .tab:focus-visible { + outline: 2px solid var(--ft-accent); + outline-offset: 2px; + } + + .tab.active { + color: var(--ft-accent); + border-bottom-color: currentColor; + } + + .chart-tablist .tab.active { + background: var(--ft-accent); + color: white; + border-bottom: none; + } + + .tabpanel { + margin-top: 0; + } + + .tabpanel[hidden] { + display: none; + } + + .chart-tabpanels { + position: relative; + min-height: 320px; + } + + .chart-tabpanel { + transition: opacity 0.2s ease; + } + + .chart-tabpanel[hidden] { + display: none; + opacity: 0; + } + + .chart-tabpanel:not([hidden]) { + opacity: 1; + } + + .methodology-note { + margin-top: 0.5em; + margin-bottom: 1em; + } + + :global(html.dark) .tab { + color: var(--ft-muted); + } + + :global(html.dark) .tab:hover { + color: var(--ft-text); + } + + :global(html.dark) .tab.active { + color: var(--ft-accent); + } + + :global(html.dark) .chart-tablist .tab.active { + background: var(--ft-accent); + color: var(--ft-bg); + } + @media screen and (max-width: 768px) { main { padding: 20px 16px; @@ -499,5 +768,66 @@ const ssrStats = runtimeEntries.map((entry) => entry.data) th { font-size: 12px; } + + .tab { + padding: 8px 14px; + font-size: 13px; + } } + + diff --git a/packages/docs/src/components/DevTimeChart.astro b/packages/docs/src/components/DevTimeChart.astro index 481217ed..24b5988a 100644 --- a/packages/docs/src/components/DevTimeChart.astro +++ b/packages/docs/src/components/DevTimeChart.astro @@ -113,8 +113,7 @@ const chartData = JSON.stringify([ } .chart-wrapper :global(.grid-line) { - stroke: var(--ft-border); - opacity: 0.5; + stroke: var(--ft-chart-grid, var(--ft-border)); } diff --git a/packages/docs/src/lib/types.ts b/packages/docs/src/lib/types.ts new file mode 100644 index 00000000..19e70790 --- /dev/null +++ b/packages/docs/src/lib/types.ts @@ -0,0 +1,9 @@ +export interface ChartDatum { + name: string + value: number +} + +export interface ComparisonChartPayload { + data: ChartDatum[] + valueFormat: 'count' | 'mb' +} diff --git a/packages/docs/src/lib/utils.ts b/packages/docs/src/lib/utils.ts index 43c653b4..6b1512bf 100644 --- a/packages/docs/src/lib/utils.ts +++ b/packages/docs/src/lib/utils.ts @@ -1,5 +1,5 @@ const BYTES_PER_KB = 1024 -const BYTES_PER_MB = BYTES_PER_KB * BYTES_PER_KB +export const BYTES_PER_MB = BYTES_PER_KB * BYTES_PER_KB /** * Returns the URL slug for a framework details page. diff --git a/packages/docs/src/styles/shared.css b/packages/docs/src/styles/shared.css index 1990ed86..890ac6e8 100644 --- a/packages/docs/src/styles/shared.css +++ b/packages/docs/src/styles/shared.css @@ -11,6 +11,7 @@ --ft-border: #e5e7eb; --ft-bg: #ffffff; --ft-bg-muted: #f9fafb; + --ft-chart-grid: rgba(0, 0, 0, 0.18); } html.dark { @@ -19,4 +20,5 @@ html.dark { --ft-border: #2e2e2e; --ft-bg: #1a1a1a; --ft-bg-muted: #242424; + --ft-chart-grid: rgba(255, 255, 255, 0.22); }