Skip to content

Commit 463ea0a

Browse files
ajitpratap0Ajit Pratap Singhclaude
authored
feat(website): comprehensive website revamp — performance, design, UX, new pages (#514)
* fix(ast): replace DescribeStatement stubs with UnsupportedStatement, fix extraction gaps Addresses multiple issues from the v1.14.0 comprehensive project review: P0 — Critical: - Add UnsupportedStatement AST node with Kind and RawSQL fields to replace DescribeStatement misuse for Snowflake stubs (USE, COPY INTO, PUT, GET, LIST, REMOVE, CREATE STAGE/STREAM/TASK/PIPE/etc.) - Add EXTRACT(field FROM source) parser support (was missing entirely) - Fix all 7 extraction gap tests (CASE, CAST, IN, BETWEEN, EXTRACT, recursive CTEs) — previously t.Skip() stubs, now passing P1 — High: - Add AST.HasUnsupportedStatements() for stub detection - Formatter emits "-- UNSUPPORTED: ..." comment for unmodeled statements instead of producing corrupt SQL - Remove stale "CREATE TABLE not implemented" comment from coverage tests - Add TODO(v2-cleanup) markers to 5 overlapping coverage test files P2 — Medium: - Reconcile Validate() empty-input behavior (parser.ValidateBytes now rejects empty input, matching gosqlx.Validate) - Fix ParseBytes string→byte→string round-trip (now threads []byte directly to tokenizer) - Deprecate pkg/sql/monitor in favor of pkg/metrics (v2.0 removal) - Add v2.0 removal timeline to 3 deprecated parser APIs All tests pass with -race across the full project (20 files, +361/-103). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(website): comprehensive website revamp — performance, design, UX, new pages Major overhaul of the GoSQLX website driven by a full audit (Lighthouse, accessibility, competitive analysis, performance traces, mobile UX testing). Performance: - Convert 7 homepage components from client to server components - Replace Framer Motion FadeIn with lightweight CSS IntersectionObserver (FadeInCSS) - Extract GitHubStarButton/GitHubStarCount as tiny client islands - Expected LCP improvement from ~893ms to <400ms Design System: - 12 new semantic design tokens (bg, brand, text, border layers) - Glass cards: increased opacity, inset highlight, deeper shadows - Typography: Space Grotesk (headings) + Inter (body), JetBrains Mono (code) - Prose contrast improved (body opacity 0.7 → 0.8) New Pages & Sections: - /compare/ — Feature matrix + perf bar chart vs pg_query, vitess, sqlparser - DialectShowcase — 8 dialect cards with compliance % and status badges - PerformanceSection — Animated horizontal bar chart (CSS-only, scroll-triggered) - TrustSection — 5 metric cards + 3 integration cards (Claude, VS Code, Cursor) - Hero MiniPlayground — Live WASM-powered SQL parser embedded in hero Docs & UX: - Cmd+K search modal with Fuse.js (SearchModal + search-index) - Code block CopyButton (hover-to-reveal, clipboard feedback) - DocNavigation (prev/next article links) - Reading time estimate on articles - Mobile sidebar scroll lock Accessibility (Lighthouse 96 → targeting 100): - Logo alt text, cursor-pointer on interactive elements - Mobile menu: body scroll lock + full opacity background - Text contrast: zinc-300 → zinc-200 on cards, code font 13→14px Dark/Light Mode: - ThemeToggle with localStorage persistence + system preference detection - Inline script prevents flash of wrong theme - Full light mode CSS token overrides Benchmarks: - Visual bar charts (ParseChart, CompetitorChart) above tables - Tables in collapsible "View raw data" toggles Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(website): comprehensive light mode CSS override layer The light mode was broken because all 300+ component class strings use hardcoded dark-mode Tailwind utilities (text-white, text-zinc-300, bg-white/[0.06], border-white/[0.06]). The CSS custom property overrides were useless since components don't reference them. Fix: add a comprehensive html.light CSS layer that overrides every hardcoded dark-mode class — text colors, backgrounds, borders, glass cards, buttons, navbar (including dynamic rgba bg), footer, search modal, code blocks (kept dark per convention), accent colors, badges, bar charts, and section borders. Also fix Navbar to detect light class via MutationObserver and swap the hardcoded rgba(9,9,11) bg to rgba(250,251,252) in light mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(website): add !important to light mode overrides for Tailwind v4 cascade Tailwind v4 compiles utilities inside @layer which wins the CSS cascade over unlayered selectors. Our html.light overrides were being ignored. Fix: add !important to all light mode color/bg/border overrides. Also preserve dark code block styling inside pre/textarea elements by undoing text color overrides with inherit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(website): complete light mode audit — slate colors, playground, badges Audit of all 21 page/component files found remaining light mode gaps: - Add slate-200 through slate-600 text color overrides (playground tabs) - Add bg-slate-700/800/900/950 background overrides - Add border-slate-500/700/800 border overrides - Add bg-zinc-800/900/950 background overrides - Add bg-surface override for docs sidebar - Keep playground editor dark in light mode (convention) via scoped overrides targeting the h-[calc(100vh-64px)] container - Reduce hero gradient opacity from 0.3 to 0.15 in light mode - Fix ThemeToggle: remove non-functional light:/dark: pseudo-classes, simplify to standard classes that work with CSS overrides Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(website): playground light mode + accent button text-white exception Two fixes: 1. Remove forced-dark overrides on playground — it now responds to the theme toggle like every other page. CodeMirror editor keeps its own oneDark theme regardless. 2. Add CSS exceptions so text-white stays truly white inside accent-colored backgrounds (bg-accent-indigo, bg-accent-green, bg-brand). Fixes the docs "Get Started" button which had dark text on indigo bg in light mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(website): bump text contrast to pass Lighthouse accessibility >= 0.95 Lighthouse CI failed with accessibility 0.93 (threshold: 0.95). Root cause: text-zinc-400/500 on dark backgrounds gives only ~4.3:1 contrast ratio, below WCAG AA minimum of 4.5:1. Changes: - MiniPlayground: inactive tabs zinc-500 → zinc-400, labels zinc-400 → zinc-300 - TrustSection: heading and labels zinc-400 → zinc-300 - DialectShowcase: subtitle zinc-400 → zinc-300 - AnimatedBars: value labels zinc-400 → zinc-300 - PerformanceSection: footnote zinc-500 → zinc-400 All text now passes WCAG AA (4.5:1 minimum contrast on dark backgrounds). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(website): fix Lighthouse a11y failures — code contrast + select label Two specific Lighthouse accessibility failures from the CI report: 1. color-contrast (2.17:1): The <code> inside MiniPlayground's output <pre> had text-zinc-300 which got overridden to dark gray (#4B5563) by the global light mode CSS, but the <pre> bg stays dark (#1E1E2E). Fix: use inline style={{ color: '#D4D4D8' }} on all code-block <pre> and <textarea> elements so the color can't be overridden. 2. select-name: The dialect <select> was wrapped in a <label> with no visible text. Lighthouse couldn't associate them. Fix: add explicit <label> with "Dialect:" text + aria-label on select. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini-2655.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b36550f commit 463ea0a

40 files changed

+2243
-354
lines changed

website/src/app/benchmarks/BenchmarksContent.tsx

Lines changed: 136 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
'use client';
22

3+
import { useState } from 'react';
34
import { FadeIn } from '@/components/ui/FadeIn';
45
import { GlassCard } from '@/components/ui/GlassCard';
56
import { Button } from '@/components/ui/Button';
7+
import { ParseChart } from '@/components/benchmarks/ParseChart';
8+
import { CompetitorChart } from '@/components/benchmarks/CompetitorChart';
69

710
const metrics = [
811
{ label: 'Sustained Ops/sec', value: '1.38M+' },
@@ -26,6 +29,38 @@ const methodology = [
2629
'Results averaged over 5 consecutive runs to reduce variance',
2730
];
2831

32+
function RawDataToggle({ id, children }: { id: string; children: React.ReactNode }) {
33+
const [open, setOpen] = useState(false);
34+
35+
return (
36+
<div className="mt-4">
37+
<button
38+
type="button"
39+
onClick={() => setOpen((prev) => !prev)}
40+
className="text-xs text-zinc-500 hover:text-zinc-300 transition-colors flex items-center gap-1"
41+
aria-expanded={open}
42+
aria-controls={`raw-data-${id}`}
43+
>
44+
<svg
45+
className={`w-3 h-3 transition-transform duration-200 ${open ? 'rotate-90' : ''}`}
46+
fill="none"
47+
viewBox="0 0 24 24"
48+
stroke="currentColor"
49+
strokeWidth={2}
50+
>
51+
<path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
52+
</svg>
53+
{open ? 'Hide' : 'View'} raw data
54+
</button>
55+
{open && (
56+
<div id={`raw-data-${id}`} className="mt-3">
57+
{children}
58+
</div>
59+
)}
60+
</div>
61+
);
62+
}
63+
2964
function renderMethodologyItem(item: string) {
3065
const FLAG = '-benchmem';
3166
const idx = item.indexOf(FLAG);
@@ -75,110 +110,121 @@ export function BenchmarksContent() {
75110
</div>
76111
</section>
77112

78-
{/* Benchmark Table */}
113+
{/* Parse Benchmarks Chart + Table */}
79114
<section className="section-padding pb-16">
80115
<div className="container-width">
81116
<FadeIn>
82117
<h2 className="text-2xl font-bold text-white mb-4">Parse Benchmarks</h2>
83-
<GlassCard className="p-0 overflow-hidden" hover={false}>
84-
<div className="overflow-x-auto">
85-
<table className="w-full text-sm text-left">
86-
<caption className="sr-only">GoSQLX Parse Benchmarks</caption>
87-
<thead>
88-
<tr className="border-b border-white/[0.06]">
89-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Benchmark</th>
90-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Query Type</th>
91-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Apple M4</th>
92-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Baseline (CI)</th>
93-
</tr>
94-
</thead>
95-
<tbody>
96-
{benchmarks.map((b) => (
97-
<tr
98-
key={b.name}
99-
className="border-b border-white/[0.04] hover:bg-white/[0.03] transition-colors"
100-
>
101-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-white font-medium">{b.name}</td>
102-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">{b.query}</td>
103-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-mono">{b.m4}</td>
104-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400 font-mono">{b.baseline}</td>
105-
</tr>
106-
))}
107-
</tbody>
108-
</table>
109-
</div>
118+
<GlassCard className="p-6" hover={false}>
119+
<ParseChart />
110120
</GlassCard>
111-
<p className="text-xs text-zinc-500 mt-2 md:hidden">&larr; Swipe to see all columns &rarr;</p>
121+
122+
<RawDataToggle id="parse">
123+
<GlassCard className="p-0 overflow-hidden" hover={false}>
124+
<div className="overflow-x-auto">
125+
<table className="w-full text-sm text-left">
126+
<caption className="sr-only">GoSQLX Parse Benchmarks</caption>
127+
<thead>
128+
<tr className="border-b border-white/[0.06]">
129+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Benchmark</th>
130+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Query Type</th>
131+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Apple M4</th>
132+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Baseline (CI)</th>
133+
</tr>
134+
</thead>
135+
<tbody>
136+
{benchmarks.map((b) => (
137+
<tr
138+
key={b.name}
139+
className="border-b border-white/[0.04] hover:bg-white/[0.03] transition-colors"
140+
>
141+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-white font-medium">{b.name}</td>
142+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">{b.query}</td>
143+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-mono">{b.m4}</td>
144+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400 font-mono">{b.baseline}</td>
145+
</tr>
146+
))}
147+
</tbody>
148+
</table>
149+
</div>
150+
</GlassCard>
151+
<p className="text-xs text-zinc-500 mt-2 md:hidden">&larr; Swipe to see all columns &rarr;</p>
152+
</RawDataToggle>
112153
</FadeIn>
113154
</div>
114155
</section>
115156

116-
{/* Competitor Comparison */}
157+
{/* Competitor Comparison Chart + Table */}
117158
<section className="section-padding pb-16">
118159
<div className="container-width">
119160
<FadeIn>
120161
<h2 className="text-2xl font-bold text-white mb-6">Competitor Comparison</h2>
121-
<GlassCard className="p-0 overflow-hidden" hover={false}>
122-
<div className="overflow-x-auto">
123-
<table className="w-full text-sm text-left">
124-
<caption className="sr-only">Competitor Library Comparison</caption>
125-
<thead>
126-
<tr className="border-b border-white/[0.06]">
127-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Library</th>
128-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Language</th>
129-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Ops/sec</th>
130-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Memory/op</th>
131-
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Zero-copy</th>
132-
</tr>
133-
</thead>
134-
<tbody>
135-
<tr className="border-b border-white/[0.04] transition-colors bg-indigo-500/10 border-l-2 border-l-indigo-500">
136-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-white font-medium">
137-
GoSQLX{' '}
138-
<span className="ml-2 inline-block rounded-full bg-indigo-500/20 px-2 py-0.5 text-xs font-medium text-indigo-300">
139-
This Library
140-
</span>
141-
</td>
142-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Go</td>
143-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-mono">1.38M+</td>
144-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300">Low</td>
145-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-accent-green font-medium">
146-
<span aria-label="Yes"></span>
147-
</td>
148-
</tr>
149-
<tr className="border-b border-white/[0.04] hover:bg-white/[0.03] transition-colors">
150-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-medium">xwb1989/sqlparser</td>
151-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Go</td>
152-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400 font-mono">~380K</td>
153-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Higher</td>
154-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-500">
155-
<span aria-label="No"></span>
156-
</td>
157-
</tr>
158-
<tr className="border-b border-white/[0.04] hover:bg-white/[0.03] transition-colors">
159-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-medium">pg_query_go</td>
160-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Go</td>
161-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400 font-mono">~220K</td>
162-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Higher (CGo)</td>
163-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-500">
164-
<span aria-label="No"></span>
165-
</td>
166-
</tr>
167-
<tr className="border-b border-white/[0.04] hover:bg-white/[0.03] transition-colors">
168-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-medium">blastrain/sqlparser</td>
169-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Go</td>
170-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400 font-mono">~290K</td>
171-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Medium</td>
172-
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-500">
173-
<span aria-label="No"></span>
174-
</td>
175-
</tr>
176-
</tbody>
177-
</table>
178-
</div>
162+
<GlassCard className="p-6" hover={false}>
163+
<CompetitorChart />
179164
</GlassCard>
180-
<p className="text-xs text-zinc-500 mt-2 md:hidden">&larr; Swipe to see all columns &rarr;</p>
181-
<p className="text-xs text-zinc-500 mt-2">* Competitor figures estimated from published benchmarks on equivalent hardware. Results may vary by query complexity.</p>
165+
166+
<RawDataToggle id="competitor">
167+
<GlassCard className="p-0 overflow-hidden" hover={false}>
168+
<div className="overflow-x-auto">
169+
<table className="w-full text-sm text-left">
170+
<caption className="sr-only">Competitor Library Comparison</caption>
171+
<thead>
172+
<tr className="border-b border-white/[0.06]">
173+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Library</th>
174+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Language</th>
175+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Ops/sec</th>
176+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Memory/op</th>
177+
<th scope="col" className="px-3 py-3 sm:px-6 sm:py-4 font-medium text-zinc-400">Zero-copy</th>
178+
</tr>
179+
</thead>
180+
<tbody>
181+
<tr className="border-b border-white/[0.04] transition-colors bg-indigo-500/10 border-l-2 border-l-indigo-500">
182+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-white font-medium">
183+
GoSQLX{' '}
184+
<span className="ml-2 inline-block rounded-full bg-indigo-500/20 px-2 py-0.5 text-xs font-medium text-indigo-300">
185+
This Library
186+
</span>
187+
</td>
188+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Go</td>
189+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-mono">1.38M+</td>
190+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300">Low</td>
191+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-accent-green font-medium">
192+
<span aria-label="Yes">{'\u2713'}</span>
193+
</td>
194+
</tr>
195+
<tr className="border-b border-white/[0.04] hover:bg-white/[0.03] transition-colors">
196+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-medium">xwb1989/sqlparser</td>
197+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Go</td>
198+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400 font-mono">~380K</td>
199+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Higher</td>
200+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-500">
201+
<span aria-label="No">{'\u2717'}</span>
202+
</td>
203+
</tr>
204+
<tr className="border-b border-white/[0.04] hover:bg-white/[0.03] transition-colors">
205+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-medium">pg_query_go</td>
206+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Go</td>
207+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400 font-mono">~220K</td>
208+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Higher (CGo)</td>
209+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-500">
210+
<span aria-label="No">{'\u2717'}</span>
211+
</td>
212+
</tr>
213+
<tr className="border-b border-white/[0.04] hover:bg-white/[0.03] transition-colors">
214+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-300 font-medium">blastrain/sqlparser</td>
215+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Go</td>
216+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400 font-mono">~290K</td>
217+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-400">Medium</td>
218+
<td className="px-3 py-3 sm:px-6 sm:py-4 text-zinc-500">
219+
<span aria-label="No">{'\u2717'}</span>
220+
</td>
221+
</tr>
222+
</tbody>
223+
</table>
224+
</div>
225+
</GlassCard>
226+
<p className="text-xs text-zinc-500 mt-2 md:hidden">&larr; Swipe to see all columns &rarr;</p>
227+
</RawDataToggle>
182228
</FadeIn>
183229
</div>
184230
</section>

0 commit comments

Comments
 (0)