feat(www): landing page (#25)

* design: landing page spec + app icon asset

* chore(www): add anime.js, fonts, and landing page screenshot assets

* feat(www): add design tokens, fonts, shared layout

* feat(www): add landing page sections and styling

* feat(www): add theme toggle and anime.js viewport animations

* feat(www): feedback updates — no hero card, os-aware download, footer email, lowercase colored heading

* feat(www): remove screenshot borders, move macos hint below ctas, mobile github fallback

* feat(www): keep hero CTAs side by side

* feat(www): set initial nav border to transparent

* feat(www): add Rybbit prod-only, sitemap, SEO head, OG image, robots.txt, 404 page

* docs(AGENTS): include www landing page version in release sync checklist

* chore(www): add railpack.json for Dokploy deploy (force node provider, TZ)

* chore(www): use bun for Railpack deploy (packageManager + standalone www/bun.lock)

* docs(AGENTS): note www/bun.lock sync for Railpack deploys

* chore(www): add vitest test tooling

* feat(www): add FAQ data source

* feat(www): add SEO JSON-LD builders

* feat(www): add Seo JSON-LD component

* feat(www): wire Seo component and social/meta tags into Layout

* feat(www): add homepage JSON-LD, keyword H1, data-driven FAQ

* fix(www): point Roadmap footer link to GitHub

* feat(www): add lastmod to sitemap

* perf(www): self-host anime.js

* refactor(www): move nav and footer into shared Layout

* feat(www): add changelog page

* fix(www): make shared component styles global in Layout

* perf(www): optimize screenshots with Astro Image (webp/avif + srcset)

* feat(www): add web app manifest

* feat(www): add helpful links to 404 page

* fix(www): shorten homepage H1 copy

* fix(www): OS-aware navbar download + footer layout

* feat(www): build full changelog from v0.7.0 to v0.7.10
This commit is contained in:
2026-08-17 09:16:58 +08:00
committed by GitHub
parent 62eed15844
commit 501fed962c
33 changed files with 2361 additions and 16 deletions
+197 -14
View File
@@ -1,17 +1,200 @@
---
import "../styles/global.css";
import Layout from "../layouts/Layout.astro";
import { faqItems } from "../data/faq";
import {
buildSoftwareApplicationJsonLd,
buildFaqJsonLd,
buildOrganizationJsonLd,
buildWebSiteJsonLd,
} from "../lib/seo";
import { Image } from "astro:assets";
import queryEditor from "../assets/screenshots/query-editor.png";
import erDiagram from "../assets/screenshots/er-diagram.png";
import home from "../assets/screenshots/home.png";
import dataGrid from "../assets/screenshots/data-grid.png";
const version = "0.7.10";
const site = "https://getgridline.app";
const jsonLd = [
buildSoftwareApplicationJsonLd({
name: "Gridline",
url: `${site}/`,
description:
"The open-source database GUI for PostgreSQL, MySQL, SQLite, and Redis. No paywalls, no tab limits, fully open source.",
image: `${site}/og.png`,
operatingSystem: "macOS, Windows, Linux",
applicationCategory: "DeveloperApplication",
offersPrice: 0,
offersCurrency: "USD",
downloadUrl: "https://github.com/AdrianBonpin/gridline/releases",
}),
buildFaqJsonLd(faqItems),
buildOrganizationJsonLd({
name: "Gridline",
url: `${site}/`,
logo: `${site}/gridline-icon.svg`,
sameAs: [
"https://github.com/AdrianBonpin/gridline",
"https://x.com/AdrianBonpin",
],
}),
buildWebSiteJsonLd({
name: "Gridline",
url: `${site}/`,
}),
];
---
<Layout title="Gridline — Open-Source Database GUI" jsonLd={jsonLd}>
<script src="/landing.js" defer></script>
<!-- Hero -->
<header id="hero" class="relative pt-32 pb-20 md:pt-44 md:pb-28 overflow-hidden">
<div class="max-w-[1200px] mx-auto px-6 text-center">
<div class="hero-animate">
<p class="font-mono text-xs tracking-[0.15em] uppercase text-[var(--accent)] mb-5">Open-Source Database GUI</p>
<h1 class="font-serif text-5xl md:text-7xl lg:text-8xl leading-[1.05] tracking-tight max-w-4xl mx-auto lowercase">
the open-source database <span class="text-[var(--accent)]">gui</span>
</h1>
<p class="mt-6 text-lg md:text-xl text-[var(--text-muted)] max-w-xl mx-auto">
Query, explore, sync, and edit PostgreSQL, MySQL, SQLite, and Redis — with no paywalls, no tab limits, no account required.
</p>
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
<a id="download" href="https://github.com/AdrianBonpin/gridline/releases" class="primary-btn" data-download>
Download
</a>
<a href="https://github.com/AdrianBonpin/gridline" target="_blank" rel="noopener" class="ghost-btn">
View on GitHub
</a>
</div>
<div id="macos-hint" class="hidden max-w-lg mx-auto w-full rounded-xl border border-[var(--border)] bg-[var(--surface)] p-3 text-left mt-4">
<p class="text-xs text-[var(--text-muted)] mb-2">First launch on macOS? Run this in Terminal:</p>
<div class="flex items-center justify-between gap-3 rounded-lg bg-[var(--bg)] border border-[var(--border)] px-3 py-2">
<code class="font-mono text-xs truncate">sudo xattr -dr com.apple.quarantine /Applications/Gridline.app</code>
<button id="copy-quarantine" class="text-xs font-medium text-[var(--primary)] hover:text-[var(--primary-hover)] transition-colors shrink-0" aria-label="Copy command">Copy</button>
</div>
</div>
</div>
<div class="hero-screenshot mt-14 md:mt-20 mx-auto max-w-5xl">
<Image
src={queryEditor}
alt="Gridline query editor and data grid"
widths={[640, 1024, 1600]}
sizes="(max-width: 1024px) 100vw, 1024px"
formats={["webp", "avif"]}
loading="eager"
fetchpriority="high"
class="w-full h-auto"
/>
</div>
<p class="mt-8 text-xs md:text-sm text-[var(--text-muted)] font-mono">
Free for personal and commercial use · Apache 2.0 · v{version}
</p>
</div>
</header>
<!-- Why Gridline -->
<section id="why" class="section-animate py-20 md:py-28">
<div class="max-w-[1200px] mx-auto px-6">
<h2 class="font-serif text-3xl md:text-5xl text-center max-w-2xl mx-auto">
Everything the paid apps lock behind a subscription.
</h2>
<div class="mt-12 grid md:grid-cols-3 gap-5">
<article class="glass-card">
<p class="font-mono text-xs text-[var(--accent)] mb-3">01</p>
<h3 class="text-xl font-semibold mb-2">No limits</h3>
<p class="text-[var(--text-muted)] text-sm leading-relaxed">
Unlimited connections, open tabs, saved queries, and exports. The free plan is the full plan.
</p>
</article>
<article class="glass-card">
<p class="font-mono text-xs text-[var(--accent)] mb-3">02</p>
<h3 class="text-xl font-semibold mb-2">DB-to-DB sync</h3>
<p class="text-[var(--text-muted)] text-sm leading-relaxed">
Pipe <code class="font-mono text-xs">pg_dump</code> directly into <code class="font-mono text-xs">pg_restore</code> between two live connections. No file round-trip needed.
</p>
</article>
<article class="glass-card">
<p class="font-mono text-xs text-[var(--accent)] mb-3">03</p>
<h3 class="text-xl font-semibold mb-2">Deeper PG explorer</h3>
<p class="text-[var(--text-muted)] text-sm leading-relaxed">
Full detail views for functions, triggers, sequences, enums, extensions, roles, and grants.
</p>
</article>
</div>
</div>
</section>
<!-- Feature highlights -->
<section id="features" class="section-animate py-20 md:py-28">
<div class="max-w-[1200px] mx-auto px-6">
<p class="font-mono text-xs text-[var(--accent)] uppercase tracking-[0.15em] text-center mb-3">Features</p>
<h2 class="font-serif text-3xl md:text-5xl text-center mb-14">Explore, query, and move data.</h2>
<div class="grid md:grid-cols-2 gap-5">
<article class="feature-card">
<p class="font-mono text-xs text-[var(--text-muted)] mb-3">QUERY & EDIT</p>
<h3 class="text-xl font-semibold mb-2">SQL editor + virtualized grid</h3>
<p class="text-[var(--text-muted)] text-sm mb-4">Run queries with autocomplete, inline edit cells, and browse 100k+ rows without breaking a sweat.</p>
<Image src={queryEditor} alt="SQL query editor" widths={[640, 1024]} sizes="(max-width: 1024px) 100vw, 640px" formats={["webp", "avif"]} loading="lazy" class="w-full h-auto" />
</article>
<article class="feature-card">
<p class="font-mono text-xs text-[var(--text-muted)] mb-3">VISUALIZE</p>
<h3 class="text-xl font-semibold mb-2">ER diagram</h3>
<p class="text-[var(--text-muted)] text-sm mb-4">Auto-layout schema graphs with crow’s foot relationships across schemas, right inside the app.</p>
<Image src={erDiagram} alt="ER diagram" widths={[640, 1024]} sizes="(max-width: 1024px) 100vw, 640px" formats={["webp", "avif"]} loading="lazy" class="w-full h-auto" />
</article>
<article class="feature-card">
<p class="font-mono text-xs text-[var(--text-muted)] mb-3">MOVE DATA</p>
<h3 class="text-xl font-semibold mb-2">Backup, restore, and sync</h3>
<p class="text-[var(--text-muted)] text-sm mb-4">Point-and-click <code class="font-mono text-xs">pg_dump</code>/<code class="font-mono text-xs">pg_restore</code>, SQLite dumps, and live DB-to-DB sync.</p>
<Image src={home} alt="Gridline home screen" widths={[640, 1024]} sizes="(max-width: 1024px) 100vw, 640px" formats={["webp", "avif"]} loading="lazy" class="w-full h-auto" />
</article>
<article class="feature-card">
<p class="font-mono text-xs text-[var(--text-muted)] mb-3">MANAGE</p>
<h3 class="text-xl font-semibold mb-2">Objects, roles, and grants</h3>
<p class="text-[var(--text-muted)] text-sm mb-4">Create and edit tables, foreign keys, indexes, functions, triggers, sequences, roles, and privileges.</p>
<Image src={dataGrid} alt="Data grid with foreign key preview" widths={[640, 1024]} sizes="(max-width: 1024px) 100vw, 640px" formats={["webp", "avif"]} loading="lazy" class="w-full h-auto" />
</article>
</div>
</div>
</section>
<!-- Open source -->
<section id="oss" class="section-animate py-20 md:py-28 text-center">
<div class="max-w-[800px] mx-auto px-6">
<p class="font-mono text-xs text-[var(--accent)] uppercase tracking-[0.15em] mb-3">Open Source</p>
<h2 class="font-serif text-3xl md:text-5xl mb-5">Built in public.<br />No VC roadmap, no rug pulls.</h2>
<p class="text-[var(--text-muted)] mb-8 max-w-md mx-auto">
Gridline is Apache 2.0 and developed openly. Contributions, issues, and feature requests are welcome.
</p>
<a href="https://github.com/AdrianBonpin/gridline" target="_blank" rel="noopener" class="ghost-btn">
Star on GitHub
</a>
</div>
</section>
<!-- FAQ -->
<section id="faq" class="section-animate py-20 md:py-28">
<div class="max-w-[800px] mx-auto px-6">
<h2 class="font-serif text-3xl md:text-5xl text-center mb-12">Questions? Answered.</h2>
<div class="faq-list space-y-3">
{
faqItems.map((item, i) => (
<details class="faq-item" open={i === 0}>
<summary class="faq-summary">
<span>{item.question}</span>
<span class="faq-chevron">⌄</span>
</summary>
<div class="faq-body">
<p class="text-[var(--text-muted)] text-sm leading-relaxed">{item.answer}</p>
</div>
</details>
))
}
</div>
</div>
</section>
</Layout>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro</h1>
</body>
</html>