From 8fd837d31fe9ad16cd077a913173563bc7cf727a Mon Sep 17 00:00:00 2001 From: Gaspard Kirira Date: Tue, 30 Jun 2026 09:35:20 +0300 Subject: [PATCH 1/7] feat(home): add benchmark comparison section --- .../src/components/home/ComparisonSection.vue | 566 +++++++++--------- 1 file changed, 291 insertions(+), 275 deletions(-) diff --git a/vix-site/src/components/home/ComparisonSection.vue b/vix-site/src/components/home/ComparisonSection.vue index 8491fd49..37b4efec 100644 --- a/vix-site/src/components/home/ComparisonSection.vue +++ b/vix-site/src/components/home/ComparisonSection.vue @@ -3,62 +3,98 @@
-
-
-
- Project scope - What each tool is mainly responsible for -
-
Vix.cpp
-
Drogon
-
Crow
+
+
+ Command + wrk -t8 -c800 -d30s --latency +
+
+ Endpoint + /bench +
+
+ Machine + HP EliteBook, 8 CPU threads
+
+ +
+
+
+
+ {{ item.rank }} +

{{ item.name }}

+
- +
{{ item.requests }}
+
{{ item.avg }}
+
{{ item.p50 }}
+
{{ item.p90 }}
+
{{ item.p99 }}
+

- Drogon and Crow answer a focused question: - how do I build a C++ web service? - Vix.cpp answers a wider one: - how do I build, run, test, package, extend, and maintain a real - native C++ application? + Drogon is slightly ahead in raw throughput, but Vix.cpp stays very + close while showing the best p99 latency in this test. Compared to + Crow, Vix.cpp is faster and keeps a lower tail latency.

@@ -66,141 +102,47 @@ @@ -210,169 +152,225 @@ const rows = [ position: relative; } -.comparison__table { - margin-top: 42px; - overflow: hidden; - border: 1px solid var(--line); - border-radius: var(--radius-lg); - background: var(--bg-ink); - box-shadow: var(--shadow-lg); +.comparison__section-title :deep(h2) { + max-width: 860px; } -.comparison__head, -.comparison__row { +.comparison__meta { display: grid; - grid-template-columns: minmax(280px, 1.55fr) repeat(3, minmax(130px, 0.75fr)); -} -.comparison__head { - background: var(--bg-panel-strong); - border-bottom: 1px solid var(--line); + grid-template-columns: repeat(3, 1fr); + gap: 14px; + margin-top: 40px; } -.comparison__head > div { - padding: 18px 22px; - color: var(--text); - font-size: 0.86rem; - font-weight: 800; +.comparison__meta > div { + padding: 18px 20px; + border: 1px solid var(--line); + border-radius: var(--radius-lg); + background: rgba(255, 255, 255, 0.035); } -.comparison__head > div:not(:first-child) { - display: flex; - align-items: center; - justify-content: center; +.comparison__meta span { + display: block; + margin-bottom: 6px; + color: var(--text-muted); + font-size: 0.78rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; } -.comparison__head strong { - display: block; +.comparison__meta strong { color: var(--text); - font-size: 0.95rem; + font-size: 0.92rem; + line-height: 1.5; } -.comparison__section-title :deep(h2) { - max-width: 820px; + +.comparison__cards { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 18px; + margin-top: 22px; } -.comparison__head span { - display: block; - margin-top: 4px; - color: var(--text-muted); - font-size: 0.78rem; - font-weight: 500; + +.comparison-card { + position: relative; + overflow: hidden; + padding: 24px; + border: 1px solid var(--line); + border-radius: var(--radius-lg); + background: var(--bg-ink); + box-shadow: var(--shadow-md); } -.comparison__group { - padding: 16px 22px; - background: rgba(255, 255, 255, 0.035); - border-bottom: 1px solid var(--line); +.comparison-card--highlight { + border-color: rgba(34, 197, 94, 0.45); + background: + radial-gradient( + circle at top right, + rgba(34, 197, 94, 0.18), + transparent 34% + ), + var(--bg-ink); } -.comparison__group strong { - display: block; - color: var(--text); - font-size: 0.95rem; - font-weight: 800; +.comparison-card__top { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; } -.comparison__group span { - display: block; - margin-top: 4px; +.comparison-card__rank { + display: inline-flex; + margin-bottom: 8px; color: var(--text-muted); font-size: 0.78rem; - font-weight: 500; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; } -.comparison__group > div { - padding: 16px 22px; +.comparison-card h3 { + margin: 0; color: var(--text); - font-size: 0.82rem; + font-size: clamp(1.25rem, 1.8vw, 1.55rem); + letter-spacing: -0.03em; +} + +.comparison-card__badge { + flex: 0 0 auto; + padding: 7px 10px; + border: 1px solid rgba(34, 197, 94, 0.28); + border-radius: 999px; + background: rgba(34, 197, 94, 0.1); + color: #86efac; + font-size: 0.72rem; font-weight: 800; + white-space: nowrap; } -.comparison__group > div:not(:first-child) { - display: flex; - align-items: center; - justify-content: center; +.comparison-card__main { + margin-top: 28px; } -.comparison__group strong { +.comparison-card__main strong { display: block; color: var(--text); - font-size: 0.95rem; + font-size: clamp(2rem, 4vw, 3rem); + font-weight: 900; + line-height: 1; + letter-spacing: -0.06em; } -.comparison__group span { +.comparison-card__main span { display: block; - margin-top: 4px; + margin-top: 8px; color: var(--text-muted); - font-size: 0.78rem; - font-weight: 500; + font-size: 0.9rem; + font-weight: 700; } -.comparison__row { - border-bottom: 1px solid var(--line-soft); +.comparison-card__stats { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px; + margin-top: 24px; } -.comparison__row:last-child { - border-bottom: 0; +.comparison-card__stats div { + padding: 14px; + border: 1px solid var(--line-soft); + border-radius: var(--radius-md); + background: rgba(255, 255, 255, 0.035); } -.comparison__row > div { - min-width: 0; - padding: 18px 22px; +.comparison-card__stats span { + display: block; + color: var(--text-muted); + font-size: 0.75rem; + font-weight: 700; +} + +.comparison-card__stats strong { + display: block; + margin-top: 4px; + color: var(--text); + font-size: 1rem; +} + +.comparison-card p { + margin: 22px 0 0; color: var(--text-soft); font-size: 0.9rem; - line-height: 1.6; + line-height: 1.65; } -.comparison__row > div:not(:first-child) { - display: flex; - align-items: center; - justify-content: center; - text-align: center; +.comparison__table { + margin-top: 24px; + overflow: hidden; + border: 1px solid var(--line); + border-radius: var(--radius-lg); + background: var(--bg-ink); + box-shadow: var(--shadow-lg); } -.comparison__area { +.comparison__head, +.comparison__row { display: grid; - gap: 5px; + grid-template-columns: minmax(210px, 1.45fr) repeat(5, minmax(110px, 0.8fr)); } -.comparison__area strong { +.comparison__head { + background: var(--bg-panel-strong); + border-bottom: 1px solid var(--line); +} + +.comparison__head > div { + padding: 16px 18px; color: var(--text); - font-size: 0.95rem; + font-size: 0.78rem; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 0.08em; } -.comparison__area span { - color: var(--text-muted); - font-size: 0.8rem; - line-height: 1.5; +.comparison__head > div:not(:first-child), +.comparison__row > div:not(:first-child) { + text-align: center; } -:deep(.status-icon) { - width: 28px; - height: 28px; - display: inline-flex; - align-items: center; - justify-content: center; - border-radius: 999px; +.comparison__row { + border-bottom: 1px solid var(--line-soft); } -:deep(.status-icon svg) { - width: 15px; - height: 15px; - fill: currentColor; +.comparison__row:last-child { + border-bottom: 0; } -:deep(.status-icon--core) { - color: #86efac; - background: rgba(34, 197, 94, 0.18); +.comparison__row--highlight { + background: rgba(34, 197, 94, 0.055); } -:deep(.status-icon--partial) { - color: #fbbf24; - background: rgba(251, 191, 36, 0.18); +.comparison__row > div { + min-width: 0; + padding: 18px; + color: var(--text-soft); + font-size: 0.9rem; + line-height: 1.5; } -:deep(.status-icon--external) { - color: #f87171; - background: rgba(248, 113, 113, 0.18); +.comparison__row strong { + display: block; + color: var(--text); + font-size: 0.95rem; +} + +.comparison__row span { + display: block; + margin-top: 4px; + color: var(--text-muted); + font-size: 0.78rem; } .comparison__summary { @@ -387,7 +385,7 @@ const rows = [ rgba(255, 255, 255, 0.025) ); color: var(--text-soft); - font-size: clamp(1rem, 1.25vw, 1.1rem); + font-size: clamp(1rem, 1.25vw, 1.08rem); line-height: 1.75; text-align: center; } @@ -396,19 +394,37 @@ const rows = [ margin: 0; } -.comparison__summary strong { - color: var(--text); -} +@media (max-width: 980px) { + .comparison__meta, + .comparison__cards { + grid-template-columns: 1fr; + } -@media (max-width: 920px) { .comparison__table { overflow-x: auto; } .comparison__head, - .comparison__row, - .comparison__group { - min-width: 880px; + .comparison__row { + min-width: 860px; + } +} + +@media (max-width: 560px) { + .comparison__meta { + margin-top: 30px; + } + + .comparison-card { + padding: 20px; + } + + .comparison-card__top { + flex-direction: column; + } + + .comparison-card__stats { + grid-template-columns: 1fr; } } From 983a1962e5b7436a8ca2e265bf880b0167068333 Mon Sep 17 00:00:00 2001 From: Gaspard Kirira Date: Tue, 30 Jun 2026 10:25:49 +0300 Subject: [PATCH 2/7] feat(home): update performance benchmark section --- .../components/home/PerformanceSection.vue | 954 +++++++++++------- 1 file changed, 569 insertions(+), 385 deletions(-) diff --git a/vix-site/src/components/home/PerformanceSection.vue b/vix-site/src/components/home/PerformanceSection.vue index bf292e13..fbce3f4b 100644 --- a/vix-site/src/components/home/PerformanceSection.vue +++ b/vix-site/src/components/home/PerformanceSection.vue @@ -3,16 +3,35 @@
+
+
+ Build + Release +
+
+ Compiler + GCC 13.3.0 +
+
+ Machine + HP EliteBook, x86_64 +
+
+ CPU + 8 threads +
+
+
-
-
+
+
-

{{ activeTab.kicker }}

+

{{ activeTab.kicker }}

{{ activeTab.title }}

{{ activeTab.subtitle }}
@@ -42,104 +61,67 @@
-
+
-
{{ bar.display }}
+ {{ metric.label }} + {{ metric.value }} + {{ metric.note }} -
+ - - {{ bar.name }} - {{ bar.note }}
-
-
- {{ fact.value }} - {{ fact.label }} +
+
+
Benchmark
+
Median
+
Notes
-
-
-
- -
-
- Run fast - Small C++ files can be edited and executed directly with vix - run. -
- -
- Build smarter - Clean target builds can return through the build-state fast - path. -
-
- Protect baselines - Runtime, executor, router, HTTP, session, and app paths are - benchmarked. +
+
+ {{ row.name }} + {{ row.group }} +
+
{{ row.value }}
+
{{ row.note }}
+
+
- - + + + +

- The vix run numbers show a local single-file edit/run feedback loop. The - build numbers describe a clean no-op target build where the fast path - can skip the full pipeline. The HTTP result comes from a local v2.7 - release benchmark on an HP EliteBook, not a dedicated benchmark server. + These are local benchmark numbers, not universal production claims. Very + small microbenchmarks can produce extremely high ops/sec values, so they + are used mainly as regression guardrails. The broader router, runtime, + HTTP, executor, session, and app groups are the most useful signals for + release tracking.

@@ -150,202 +132,403 @@ import { computed, ref } from "vue"; import CommandLine from "@/components/common/CommandLine.vue"; import SectionTitle from "@/components/common/SectionTitle.vue"; -const activeKey = ref("run"); +const activeKey = ref("http"); const tabs = [ { - key: "run", - label: "vix run", - kicker: "Single-file feedback loop", - title: "Edit a C++ file and run it in about half a second.", + key: "http", + label: "HTTP", + kicker: "Local HTTP benchmark", + title: "Vix.cpp reached 112k requests per second locally.", subtitle: - "A tiny C++ file was edited and executed twice locally with vix run. The full command completed around 0.57s.", - version: "local", - mode: "dev loop", - bars: [ + "A simple /bench endpoint was measured with wrk using 8 threads, 800 connections, and a 30 second run.", + version: "v2.7.0", + mode: "Release", + metrics: [ { - name: "First run", - display: "0.579s", - height: 100, - note: "Hello, world", - logo: true, + label: "Requests/sec", + value: "112,539", + note: "local /bench endpoint", + width: 100, + featured: true, }, { - name: "After edit", - display: "0.569s", - height: 96, - note: "Hellodd, world", - logo: false, + label: "Total requests", + value: "3,386,276", + note: "completed in 30.09s", + width: 86, + featured: false, }, { - name: "Feedback loop", - display: "~0.57s", - height: 82, - note: "edit → run → output", - logo: false, + label: "Average latency", + value: "7.31ms", + note: "800 connections", + width: 62, + featured: false, + }, + { + label: "P99 latency", + value: "12.19ms", + note: "tail latency", + width: 72, + featured: true, }, ], - facts: [ - { value: "0.579s", label: "first local run" }, - { value: "0.569s", label: "after source edit" }, - { value: "1 file", label: "main.cpp" }, - { value: "direct", label: "vix run workflow" }, + rows: [ + { + name: "wrk throughput", + group: "http.bench", + value: "112,539 req/s", + note: "Simple local endpoint under 800 concurrent connections.", + }, + { + name: "Average latency", + group: "http.bench", + value: "7.31ms", + note: "Measured during the same 30 second run.", + }, + { + name: "P90 latency", + group: "http.bench", + value: "8.73ms", + note: "Good mid-tail signal for the current HTTP runtime.", + }, + { + name: "P99 latency", + group: "http.bench", + value: "12.19ms", + note: "The most important latency number from this run.", + }, ], }, { - key: "build", - label: "vix build", - kicker: "Build-state fast path", - title: "No-op target builds can return in hundreds of milliseconds.", + key: "router", + label: "Router", + kicker: "Core router benchmark", + title: "Route matching paths are tracked separately.", subtitle: - "When the project state proves nothing changed, vix build --fast can skip the full build pipeline and return early.", - version: "build graph", - mode: "fast path", - bars: [ + "Static routes, parameterized routes, query strings, wrong methods, and mixed route tables are measured as separate benchmark cases.", + version: "current", + mode: "Release", + metrics: [ + { + label: "Strip query", + value: "40.64M", + note: "ops/sec", + width: 100, + featured: true, + }, { - name: "vix build --fast", - display: "303ms", - height: 100, - note: "clean no-op target", - logo: true, + label: "Static route", + value: "9.43M", + note: "ops/sec", + width: 68, + featured: true, }, { - name: "normal build", - display: "6.10s", - height: 42, - note: "full no-op path", - logo: false, + label: "Param route", + value: "5.62M", + note: "ops/sec", + width: 52, + featured: false, }, { - name: "graph disabled", - display: "6.16s", - height: 40, - note: "compat path", - logo: false, + label: "Many static", + value: "5.96M", + note: "ops/sec", + width: 55, + featured: false, }, ], - facts: [ - { value: "303ms", label: "fast no-op build" }, - { value: "6.10s", label: "normal no-op build" }, - { value: "6.16s", label: "graph disabled" }, - { value: "~20x", label: "faster no-op path" }, + rows: [ + { + name: "router.match/strip_query", + group: "router.match", + value: "40.64M ops/sec", + note: "Fast query stripping path before route matching.", + }, + { + name: "router.match/static_route", + group: "router.match", + value: "9.43M ops/sec", + note: "Simple static route lookup path.", + }, + { + name: "router.match/param_route", + group: "router.match", + value: "5.62M ops/sec", + note: "Parameterized route matching path.", + }, + { + name: "router.match/many_static_routes", + group: "router.match", + value: "5.96M ops/sec", + note: "Route table with many static entries.", + }, ], }, { - key: "http", - label: "HTTP", - kicker: "Local v2.7 benchmark", - title: "HTTP endpoint reached 112k requests per second locally.", + key: "runtime", + label: "Runtime", + kicker: "Core runtime benchmark", + title: "Runtime queues and worker paths are part of the baseline.", subtitle: - "Measured with wrk, 8 threads, 800 connections, and a 30 second run against a local release build.", - version: "v2.7", + "The low-level queue, scheduler, and worker paths are benchmarked because the higher-level HTTP and app layers depend on them.", + version: "current", mode: "Release", - bars: [ + metrics: [ { - name: "Vix HTTP", - display: "112,539", - height: 100, - note: "requests/sec", - logo: true, + label: "Queue push/pop", + value: "55.52M", + note: "ops/sec", + width: 100, + featured: true, }, { - name: "Requests", - display: "3,386,276", - height: 74, - note: "completed in 30.09s", - logo: false, + label: "Queue push/clear", + value: "33.86M", + note: "ops/sec", + width: 76, + featured: false, + }, + { + label: "Worker tasks", + value: "1.29M", + note: "ops/sec", + width: 48, + featured: true, }, { - name: "p99 latency", - display: "12.19ms", - height: 38, - note: "at 800 connections", - logo: false, + label: "Scheduler tasks", + value: "692K", + note: "ops/sec", + width: 38, + featured: false, }, ], - facts: [ - { value: "112,539", label: "requests/sec" }, - { value: "7.31ms", label: "average latency" }, - { value: "12.19ms", label: "p99 latency" }, - { value: "800", label: "connections" }, + rows: [ + { + name: "runtime.queue/push_pop", + group: "runtime.queue", + value: "55.52M ops/sec", + note: "Queue push and pop path.", + }, + { + name: "runtime.queue/push_clear", + group: "runtime.queue", + value: "33.86M ops/sec", + note: "Queue push and clear path.", + }, + { + name: "runtime.worker/submit_complete_tasks", + group: "runtime.worker", + value: "1.29M ops/sec", + note: "Worker submit and completion path.", + }, + { + name: "runtime.scheduler/submit_complete_tasks", + group: "runtime.scheduler", + value: "692K ops/sec", + note: "Scheduler submit and completion path.", + }, ], }, { - key: "router", - label: "Router", - kicker: "Core benchmark baseline", - title: "Route matching costs are visible and tracked.", + key: "objects", + label: "HTTP objects", + kicker: "HTTP object benchmark", + title: "Request and response object paths are measured directly.", subtitle: - "Static, parameterized, nested, and query-string route paths are measured separately.", - version: "v2.6.3", + "These benchmarks track object construction and response helper paths before full network and application costs are added.", + version: "current", mode: "Release", - bars: [ + metrics: [ + { + label: "Response body", + value: "65.92M", + note: "ops/sec", + width: 100, + featured: true, + }, { - name: "Strip query", - display: "41.6M", - height: 100, + label: "Request default", + value: "10.85M", note: "ops/sec", - logo: true, + width: 72, + featured: true, }, { - name: "Static route", - display: "9.37M", - height: 66, + label: "Static target", + value: "1.06M", note: "ops/sec", - logo: false, + width: 46, + featured: false, }, { - name: "Param route", - display: "5.67M", - height: 48, + label: "Query target", + value: "656K", note: "ops/sec", - logo: false, + width: 36, + featured: false, }, ], - facts: [ - { value: "41.6M", label: "strip query ops/sec" }, - { value: "9.37M", label: "static route ops/sec" }, - { value: "5.67M", label: "param route ops/sec" }, - { value: "3.91M", label: "nested param ops/sec" }, + rows: [ + { + name: "http.response/construct_status_body", + group: "http.response", + value: "65.92M ops/sec", + note: "Status + body response construction path.", + }, + { + name: "http.request/default_construct", + group: "http.request", + value: "10.85M ops/sec", + note: "Default request object construction.", + }, + { + name: "http.request/construct_static_target", + group: "http.request", + value: "1.06M ops/sec", + note: "Request object with static target.", + }, + { + name: "http.request/construct_query_target", + group: "http.request", + value: "656K ops/sec", + note: "Request object with query target parsing.", + }, ], }, { - key: "runtime", - label: "Runtime", - kicker: "Core benchmark baseline", - title: "Runtime queues and scheduler paths are benchmarked.", + key: "executor", + label: "Executor", + kicker: "Executor benchmark", + title: "Executor submit, post, and metrics paths are tracked.", subtitle: - "Low-level runtime pieces are measured because higher-level HTTP, sessions, and app APIs depend on them.", - version: "v2.6.3", + "Executor numbers are useful for detecting regressions in task scheduling, completion, and runtime metric reads.", + version: "current", mode: "Release", - bars: [ + metrics: [ { - name: "Queue push/pop", - display: "49.6M", - height: 100, + label: "Idle reads", + value: "107.44M", + note: "ops/sec guardrail", + width: 100, + featured: false, + }, + { + label: "Running reads", + value: "2.72M", + note: "ops/sec", + width: 64, + featured: false, + }, + { + label: "Submit task", + value: "494K", + note: "ops/sec", + width: 44, + featured: true, + }, + { + label: "Post void", + value: "402K", + note: "ops/sec", + width: 40, + featured: true, + }, + ], + rows: [ + { + name: "executor.metrics/idle_reads", + group: "executor.metrics", + value: "107.44M ops/sec", + note: "Very small guardrail benchmark, not a marketing number.", + }, + { + name: "executor.metrics/running_idle_reads", + group: "executor.metrics", + value: "2.72M ops/sec", + note: "Metric reads while the executor is active.", + }, + { + name: "executor.submit/task_complete", + group: "executor.submit", + value: "494K ops/sec", + note: "Submit and complete task path.", + }, + { + name: "executor.post/void_tasks", + group: "executor.post", + value: "402K ops/sec", + note: "Post void task path.", + }, + ], + }, + { + key: "app", + label: "App/session", + kicker: "Application benchmark", + title: "App registration and fake session transport are benchmarked.", + subtitle: + "These numbers are closer to framework-level behavior because they touch app route registration and fake request transport paths.", + version: "current", + mode: "Release", + metrics: [ + { + label: "Session root", + value: "181K", + note: "ops/sec", + width: 100, + featured: true, + }, + { + label: "Session health", + value: "168K", note: "ops/sec", - logo: true, + width: 92, + featured: false, }, { - name: "Queue push/clear", - display: "33.3M", - height: 78, + label: "Route get", + value: "132K", note: "ops/sec", - logo: false, + width: 72, + featured: true, }, { - name: "Worker tasks", - display: "747K", - height: 42, + label: "Group objects", + value: "73K", note: "ops/sec", - logo: false, + width: 48, + featured: false, }, ], - facts: [ - { value: "49.6M", label: "queue push/pop" }, - { value: "33.3M", label: "queue push/clear" }, - { value: "747K", label: "worker tasks" }, - { value: "677K", label: "scheduler tasks" }, + rows: [ + { + name: "session.fake_transport/single_get_root", + group: "session.fake_transport", + value: "181K ops/sec", + note: "Fake transport GET / path.", + }, + { + name: "session.fake_transport/single_get_health", + group: "session.fake_transport", + value: "168K ops/sec", + note: "Fake transport health endpoint.", + }, + { + name: "app.route_registration/get_routes", + group: "app.route_registration", + value: "132K ops/sec", + note: "Route registration lookup path.", + }, + { + name: "app.group_registration/create_group_objects", + group: "app.group_registration", + value: "73K ops/sec", + note: "Group object creation path.", + }, ], }, ]; @@ -369,14 +552,14 @@ const activeTab = computed( background: radial-gradient( circle at 50% 0%, - rgba(34, 197, 94, 0.1), - transparent 24rem + rgba(34, 197, 94, 0.12), + transparent 25rem ), radial-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px); background-size: auto, 22px 22px; - opacity: 0.5; + opacity: 0.55; mask-image: linear-gradient(#000, transparent 88%); } @@ -384,15 +567,47 @@ const activeTab = computed( position: relative; } +.performance__meta { + max-width: 980px; + margin: 40px auto 0; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 14px; +} + +.performance__meta > div { + padding: 17px 18px; + border: 1px solid var(--line); + border-radius: var(--radius-lg); + background: rgba(255, 255, 255, 0.035); +} + +.performance__meta span { + display: block; + margin-bottom: 7px; + color: var(--text-muted); + font-size: 0.72rem; + font-weight: 850; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.performance__meta strong { + display: block; + color: var(--text); + font-size: 0.95rem; + line-height: 1.4; +} + .performance__shell { - max-width: 900px; - margin: 42px auto 0; + max-width: 980px; + margin: 26px auto 0; } .performance__tabs { display: flex; justify-content: center; - gap: 10px; + gap: 8px; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; @@ -408,9 +623,9 @@ const activeTab = computed( border: 0; background: transparent; color: var(--text-soft); - padding: 0.95rem 1.2rem; - font-size: 0.92rem; - font-weight: 750; + padding: 0.95rem 1.05rem; + font-size: 0.88rem; + font-weight: 800; cursor: pointer; } @@ -437,49 +652,51 @@ const activeTab = computed( background: var(--green); } -.performance__chart-card { - position: relative; +.performance__panel { overflow: hidden; border: 1px solid var(--line); - border-radius: var(--radius-lg); + border-top: 0; + border-radius: 0 0 var(--radius-lg) var(--radius-lg); background: linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent), var(--bg-ink); box-shadow: var(--shadow-lg); } -.performance__chart-head { +.performance__panel-head { display: flex; justify-content: space-between; gap: 24px; - padding: 26px 28px; + padding: 28px; border-bottom: 1px solid var(--line-soft); } -.performance__chart-kicker { +.performance__kicker { margin: 0 0 8px; color: var(--green-bright); font-family: var(--font-mono); font-size: 0.72rem; - font-weight: 800; + font-weight: 850; letter-spacing: 0.1em; text-transform: uppercase; } -.performance__chart-head h3 { +.performance__panel-head h3 { + max-width: 680px; margin: 0; color: var(--text); font-size: clamp(1.35rem, 2.2vw, 2rem); - line-height: 1.1; + line-height: 1.12; letter-spacing: -0.04em; } -.performance__chart-head span { +.performance__panel-head span { display: block; - margin-top: 8px; + max-width: 720px; + margin-top: 9px; color: var(--text-soft); font-size: 0.92rem; - line-height: 1.55; + line-height: 1.6; } .performance__badge { @@ -487,8 +704,8 @@ const activeTab = computed( display: grid; place-items: center; align-content: center; - min-width: 92px; - height: 66px; + min-width: 98px; + height: 68px; padding: 0 14px; border: 1px solid rgba(34, 197, 94, 0.25); border-radius: var(--radius-md); @@ -504,230 +721,197 @@ const activeTab = computed( .performance__badge span { margin: 2px 0 0; - color: rgba(187, 247, 208, 0.7); + color: rgba(187, 247, 208, 0.72); font-size: 0.72rem; - font-weight: 750; + font-weight: 800; } -.performance__bars { +.performance__metrics { display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - align-items: end; - gap: 28px; - min-height: 290px; - padding: 34px 42px 30px; + grid-template-columns: repeat(4, 1fr); + gap: 14px; + padding: 24px; } -.performance__bar-item { - display: grid; - justify-items: center; - align-items: end; - min-width: 0; -} - -.performance__bar-value { - margin-bottom: 8px; - color: var(--text); - font-family: var(--font-mono); - font-size: 0.98rem; - font-weight: 850; - letter-spacing: -0.03em; -} - -.performance__bar-track { +.performance-metric { position: relative; - display: flex; - align-items: end; - justify-content: center; - width: min(100%, 104px); - height: 170px; + overflow: hidden; + min-width: 0; + padding: 18px; + border: 1px solid var(--line-soft); + border-radius: var(--radius-md); + background: rgba(255, 255, 255, 0.03); } -.performance__bar-fill { - position: relative; - width: 100%; - min-height: 38px; - border-radius: 12px 12px 0 0; +.performance-metric--featured { + border-color: rgba(34, 197, 94, 0.32); background: - linear-gradient(180deg, rgba(134, 239, 172, 0.95), rgba(34, 197, 94, 0.74)), - var(--green); - box-shadow: 0 16px 38px rgba(34, 197, 94, 0.18); -} - -.performance__bar-item:nth-child(2) .performance__bar-fill { - background: linear-gradient(180deg, #7dd3fc, #64748b); - box-shadow: 0 16px 38px rgba(125, 211, 252, 0.12); + radial-gradient( + circle at top right, + rgba(34, 197, 94, 0.14), + transparent 42% + ), + rgba(255, 255, 255, 0.035); } -.performance__bar-item:nth-child(3) .performance__bar-fill { - background: linear-gradient(180deg, #c4b5fd, #64748b); - box-shadow: 0 16px 38px rgba(196, 181, 253, 0.12); +.performance-metric span { + display: block; + color: var(--text-muted); + font-size: 0.72rem; + font-weight: 850; + letter-spacing: 0.08em; + text-transform: uppercase; } -.performance__bar-logo { - position: absolute; - left: 50%; - bottom: 16px; - width: 44px; - height: 44px; - transform: translateX(-50%); +.performance-metric strong { + display: block; + margin-top: 10px; + color: var(--text); + font-family: var(--font-mono); + font-size: clamp(1.35rem, 2vw, 1.75rem); + font-weight: 900; + letter-spacing: -0.06em; } -.performance__bar-logo svg { - width: 100%; - height: 100%; +.performance-metric small { + display: block; + margin-top: 5px; + color: var(--text-soft); + font-size: 0.78rem; + line-height: 1.4; } -.performance__bar-item strong { - margin-top: 14px; - color: var(--text); - font-size: 0.9rem; - font-weight: 850; - text-align: center; +.performance-metric__track { + margin-top: 16px; + height: 7px; + overflow: hidden; + border-radius: 999px; + background: rgba(255, 255, 255, 0.08); } -.performance__bar-item span { - margin-top: 4px; - color: var(--text-muted); - font-size: 0.76rem; - font-weight: 650; - text-align: center; +.performance-metric__fill { + height: 100%; + border-radius: inherit; + background: linear-gradient(90deg, #22c55e, #86efac); } -.performance__facts { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); +.performance__table { border-top: 1px solid var(--line-soft); - background: rgba(255, 255, 255, 0.025); } -.performance__facts div { - padding: 18px 16px; - border-right: 1px solid var(--line-soft); - text-align: center; +.performance__table-head, +.performance__table-row { + display: grid; + grid-template-columns: minmax(260px, 1.25fr) minmax(150px, 0.65fr) minmax( + 260px, + 1.1fr + ); } -.performance__facts div:last-child { - border-right: 0; +.performance__table-head { + background: rgba(255, 255, 255, 0.035); } -.performance__facts strong { - display: block; +.performance__table-head > div { + padding: 14px 20px; color: var(--text); - font-family: var(--font-mono); - font-size: 0.95rem; - font-weight: 850; -} - -.performance__facts span { - display: block; - margin-top: 6px; - color: var(--text-muted); font-size: 0.72rem; - font-weight: 700; + font-weight: 900; + letter-spacing: 0.08em; text-transform: uppercase; - letter-spacing: 0.06em; } -.performance__trust { - max-width: 900px; - margin: 24px auto 0; - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 12px; +.performance__table-row { + border-top: 1px solid var(--line-soft); } -.performance__trust article { - padding: 18px; - border: 1px solid var(--line); - border-radius: var(--radius-md); - background: rgba(255, 255, 255, 0.025); +.performance__table-row > div { + min-width: 0; + padding: 17px 20px; + color: var(--text-soft); + font-size: 0.88rem; + line-height: 1.55; } -.performance__trust strong { +.performance__table-row strong { display: block; color: var(--text); font-size: 0.9rem; - font-weight: 850; } -.performance__trust span { +.performance__table-row span { display: block; - margin-top: 6px; + margin-top: 4px; color: var(--text-muted); - font-size: 0.8rem; - line-height: 1.55; + font-family: var(--font-mono); + font-size: 0.75rem; +} + +.performance__table-row > div:nth-child(2) { + color: #86efac; + font-family: var(--font-mono); + font-weight: 850; } .performance__commands { - max-width: 900px; + max-width: 980px; margin: 26px auto 0; display: grid; gap: 8px; } .performance__note { - max-width: 860px; + max-width: 900px; margin: 20px auto 0; color: var(--text-muted); font-size: 0.88rem; - line-height: 1.7; + line-height: 1.75; text-align: center; } -@media (max-width: 760px) { - .performance__chart-head { - flex-direction: column; - } - - .performance__badge { - place-items: start; - align-content: center; - text-align: left; +@media (max-width: 980px) { + .performance__meta, + .performance__metrics { + grid-template-columns: repeat(2, 1fr); } - .performance__bars { - gap: 18px; - padding: 28px 20px; + .performance__table { + overflow-x: auto; } - .performance__facts { - grid-template-columns: repeat(2, minmax(0, 1fr)); + .performance__table-head, + .performance__table-row { + min-width: 780px; } +} - .performance__facts div:nth-child(2) { - border-right: 0; +@media (max-width: 720px) { + .performance__panel-head { + flex-direction: column; } - .performance__facts div:nth-child(-n + 2) { - border-bottom: 1px solid var(--line-soft); + .performance__badge { + place-items: start; + text-align: left; } - .performance__trust { + .performance__meta { grid-template-columns: 1fr; } } -@media (max-width: 520px) { - .performance__bars { +@media (max-width: 560px) { + .performance__metrics { grid-template-columns: 1fr; - min-height: auto; - } - - .performance__bar-track { - width: 100%; - height: 78px; - align-items: center; - justify-content: flex-start; + padding: 18px; } - .performance__bar-fill { - height: 34px !important; - border-radius: 999px; + .performance__panel-head { + padding: 22px; } - .performance__bar-logo { - display: none; + .performance__tab { + padding-inline: 0.9rem; } } From 000662983350dbf54ca3aa67775a7367c4e96e38 Mon Sep 17 00:00:00 2001 From: Gaspard Kirira Date: Fri, 3 Jul 2026 18:43:40 +0300 Subject: [PATCH 3/7] feat(site): add workflow sections to homepage --- .../src/components/home/AppModulesSection.vue | 396 ++++++++++++++++ .../src/components/home/InstallSection.vue | 7 +- .../src/components/home/ProductionSection.vue | 406 ++++++++++++++++ .../src/components/home/TemplatesSection.vue | 447 ++++++++++++++++++ .../src/components/home/VixAppSection.vue | 413 ++++++++++++++++ vix-site/src/pages/HomePage.vue | 8 + vix-site/src/pages/InstallPage.vue | 36 +- vix-site/src/pages/SdksPage.vue | 48 +- 8 files changed, 1755 insertions(+), 6 deletions(-) create mode 100644 vix-site/src/components/home/AppModulesSection.vue create mode 100644 vix-site/src/components/home/ProductionSection.vue create mode 100644 vix-site/src/components/home/TemplatesSection.vue create mode 100644 vix-site/src/components/home/VixAppSection.vue diff --git a/vix-site/src/components/home/AppModulesSection.vue b/vix-site/src/components/home/AppModulesSection.vue new file mode 100644 index 00000000..52204f3c --- /dev/null +++ b/vix-site/src/components/home/AppModulesSection.vue @@ -0,0 +1,396 @@ + + + + + diff --git a/vix-site/src/components/home/InstallSection.vue b/vix-site/src/components/home/InstallSection.vue index 367ec56d..7b867f0f 100644 --- a/vix-site/src/components/home/InstallSection.vue +++ b/vix-site/src/components/home/InstallSection.vue @@ -11,7 +11,12 @@
Install guide - + Read the docs
diff --git a/vix-site/src/components/home/ProductionSection.vue b/vix-site/src/components/home/ProductionSection.vue new file mode 100644 index 00000000..ccd73cf5 --- /dev/null +++ b/vix-site/src/components/home/ProductionSection.vue @@ -0,0 +1,406 @@ + + + + + diff --git a/vix-site/src/components/home/TemplatesSection.vue b/vix-site/src/components/home/TemplatesSection.vue new file mode 100644 index 00000000..0abe0519 --- /dev/null +++ b/vix-site/src/components/home/TemplatesSection.vue @@ -0,0 +1,447 @@ + + + + + diff --git a/vix-site/src/components/home/VixAppSection.vue b/vix-site/src/components/home/VixAppSection.vue new file mode 100644 index 00000000..76997581 --- /dev/null +++ b/vix-site/src/components/home/VixAppSection.vue @@ -0,0 +1,413 @@ + + + + + diff --git a/vix-site/src/pages/HomePage.vue b/vix-site/src/pages/HomePage.vue index 04e3f79a..d4d9752e 100644 --- a/vix-site/src/pages/HomePage.vue +++ b/vix-site/src/pages/HomePage.vue @@ -9,6 +9,10 @@ + + + + @@ -56,6 +60,10 @@ import DirectionSection from "@/components/home/DirectionSection.vue"; import SoftadastraSection from "@/components/home/SoftadastraSection.vue"; import ResourcesSection from "@/components/home/ResourcesSection.vue"; +import AppModulesSection from "@/components/home/AppModulesSection.vue"; +import ProductionSection from "@/components/home/ProductionSection.vue"; +import VixAppSection from "@/components/home/VixAppSection.vue"; +import TemplatesSection from "@/components/home/TemplatesSection.vue"; diff --git a/vix-site/src/components/home/RendererSection.vue b/vix-site/src/components/home/RendererSection.vue new file mode 100644 index 00000000..9118523d --- /dev/null +++ b/vix-site/src/components/home/RendererSection.vue @@ -0,0 +1,425 @@ + + + + + diff --git a/vix-site/src/components/home/ReplaySection.vue b/vix-site/src/components/home/ReplaySection.vue new file mode 100644 index 00000000..7b4d5d03 --- /dev/null +++ b/vix-site/src/components/home/ReplaySection.vue @@ -0,0 +1,450 @@ + + + + + diff --git a/vix-site/src/components/home/ReplySection.vue b/vix-site/src/components/home/ReplySection.vue new file mode 100644 index 00000000..68aee695 --- /dev/null +++ b/vix-site/src/components/home/ReplySection.vue @@ -0,0 +1,431 @@ + + + + + diff --git a/vix-site/src/pages/HomePage.vue b/vix-site/src/pages/HomePage.vue index d4d9752e..588e2ffd 100644 --- a/vix-site/src/pages/HomePage.vue +++ b/vix-site/src/pages/HomePage.vue @@ -9,7 +9,11 @@ + + + + @@ -64,6 +68,10 @@ import AppModulesSection from "@/components/home/AppModulesSection.vue"; import ProductionSection from "@/components/home/ProductionSection.vue"; import VixAppSection from "@/components/home/VixAppSection.vue"; import TemplatesSection from "@/components/home/TemplatesSection.vue"; +import ReplySection from "@/components/home/ReplySection.vue"; +import DiagnosticsSection from "@/components/home/DiagnosticsSection.vue"; +import ReplaySection from "@/components/home/ReplaySection.vue"; +import RendererSection from "@/components/home/RendererSection.vue";