diff --git a/doc/assets/visualize.svg b/doc/assets/visualize.svg
new file mode 100644
index 00000000..72e18bdc
--- /dev/null
+++ b/doc/assets/visualize.svg
@@ -0,0 +1,35 @@
+
+
+
diff --git a/doc/index.qmd b/doc/index.qmd
index 8ee87c4b..ca61e912 100644
--- a/doc/index.qmd
+++ b/doc/index.qmd
@@ -5,22 +5,170 @@ section-divs: false
toc: false
lightbox: false
repo-actions: false
+listing:
+ - id: example-carousel
+ contents: gallery/examples
+ type: grid
+ grid-columns: 6
+ image-height: 150px
+ fields: [image, title]
+ sort: "order"
+ max-items: 6
+include-in-header:
+ text: |
+
---
::: {.hero-banner}
-{.hero-image}
-::: {.hero-content}
-# SQL meets Grammar of Graphics
+# QUERY {.hviz aria-hidden="true"}VISUALIZE UNDERSTAND
-A declarative visualization language that extends SQL with powerful data visualization capabilities.
+::: {.hero-body}
+ggsql brings the elegance of the Grammar of Graphics to SQL. Write familiar queries, add visualization clauses, and see your data transform into beautiful, composable charts — no context switching, no separate tools, just SQL with superpowers.
::: {.hero-buttons}
-[Get Started](get_started.qmd){.btn .btn-secondary .btn-lg}
-[View Examples](gallery/index.qmd){.btn .btn-outline-light .btn-lg}
+[Get Started](get_started.qmd){.btn .btn-primary .btn-lg}
+[View Examples](gallery/index.qmd){.btn .btn-outline-primary .btn-lg}
:::
+:::
+
+:::
+
+::: {.content-block .dark-bg}
+### Try it out
+
+ggsql runs in the browser as well! Edit the code and watch the plot update.
+
+```{ggsql}
+-- Regular query
+SELECT * FROM ggsql:penguins
+WHERE island = 'Biscoe'
+-- Followed by visualization declaration
+VISUALISE bill_len AS x, bill_dep AS y, body_mass AS fill
+DRAW point
+DRAW linear
+ MAPPING 0.4 AS coef, -1 AS intercept
+SCALE BINNED fill
+LABEL
+ title => 'Relationship between bill dimensions in 3 species of penguins',
+ x => 'Bill length (mm)',
+ y => 'Bill depth (mm)'
+```
+
+:::
+
+::: {.content-block .examples-carousel}
+## Explore the examples
+::: {#example-carousel}
:::
+
+[See all examples →](gallery/index.qmd){.see-all-link}
+
+:::
+
+::: {.content-block .dark-bg .install-section}
+### Install it today
+
+::: {#installer-buttons}
+
+:::
+
+::: {.install-cli-options}
+**Or via command line:**
+`uv pip install ggsql-jupyter && ggsql-jupyter --install`
+·
+`cargo install ggsql`
+:::
+
+```{=html}
+
+```
:::
::: {.content-block}
@@ -65,35 +213,41 @@ You also avoid needing agents to launch full programming languages like Python o
:::
::: {.feature}
-### There where you need it
+### Connect directly to your data
+
+ggsql interfaces directly with your database. Want to create a histogram over 1 billion observations? No problem! All calculations are pushed to the database so you only extract what is needed for the visual.
+:::
-ggsql is available where you do your data analysis:
+:::
+
+:::
+
+::: {.content-block .integrations}
+
+## Available where your data is
::: {.tool-grid}
::: {.tool-item}
{.tool-logo}
-Positron
+[Positron](https://positron.posit.co/)
:::
::: {.tool-item}
{.tool-logo}
-Quarto
+[Quarto](https://quarto.org/)
:::
::: {.tool-item}
{.tool-logo}
-Jupyter
+[Jupyter](https://jupyter.org)
:::
::: {.tool-item}
{.tool-logo}
-VS Code
+[VS Code](https://code.visualstudio.com/)
:::
-:::
-:::
-
:::
:::
-::: {.content-block .alt-bg}
+::: {.content-block}
::: {.cta-section}
@@ -112,3 +266,4 @@ Or try our [online playground](wasm/) to experience the syntax _right now_.
:::
:::
+
diff --git a/doc/styles.scss b/doc/styles.scss
index 31bad831..9f98455c 100644
--- a/doc/styles.scss
+++ b/doc/styles.scss
@@ -31,134 +31,150 @@ code {
margin-bottom: 1.5rem;
}
+// Front page paleteal background with subtle grid
+body:has(.hero-banner) {
+ background-color: var(--brand-paleteal, #DEF1EB);
+ background-image:
+ linear-gradient(rgba(0, 95, 115, 0.06) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(0, 95, 115, 0.06) 1px, transparent 1px);
+ background-size: 40px 40px;
+ background-attachment: fixed;
+}
+
.hero-banner {
- padding: 0;
- margin: 0;
width: 100%;
- display: flex;
- flex-direction: column;
- align-items: center;
- background: var(--brand-paleteal, #DEF1EB);
+ padding: 4rem 2rem;
+ background: transparent;
- &>p {
- margin: 0;
- }
- .hero-image {
- width: 100%;
- max-width: 1200px;
- height: auto;
- object-fit: contain;
- }
- .hero-content {
- width: 100%;
- padding: 2rem;
- background: var(--brand-darkteal);
- text-align: center;
+ h1, .hero-body {
+ max-width: 900px;
+ margin-left: auto;
+ margin-right: auto;
- h1 {
- font-size: 2.5rem;
- font-weight: 600;
- color: var(--brand-lightteal);
- margin-bottom: 0.5rem;
+ .hcode {
+ font-family: "Fira Code";
+ font-weight: 100;
+ }
+
+ .hviz-wrapper {
+ position: relative;
+ display: inline-block;
+ vertical-align: baseline;
+ margin: 0 0.1em;
}
- p {
- font-size: 1.25rem;
- color: var(--brand-white);
- max-width: 600px;
- margin: 0 auto 1.5rem;
+ .hviz-wrapper .hviz {
+ display: block;
+ height: 0.85em;
+ width: auto;
+ margin: 0;
+ position: relative;
+ top: 0.08em;
+ pointer-events: none;
+ user-select: none;
+ }
+
+ // Accessible + selectable text overlay
+ .hviz-wrapper .sr-only {
+ position: absolute;
+ inset: 0;
+ color: transparent;
+ user-select: all;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .hund {
+ font-family: "Antic Didone", serif;
+ font-weight: 400;
+ font-style: normal;
}
}
- .hero-buttons {
+ h1 {
+ font-size: 3rem;
+ font-weight: 700;
+ color: var(--brand-darkteal, #005F73);
+ margin-bottom: 1.5rem;
+ line-height: 1.2;
+ letter-spacing: -0.02em;
+ text-align: left;
+ }
+
+ .hero-body {
display: flex;
- gap: 1rem;
- justify-content: center;
- flex-wrap: wrap;
+ flex-direction: column;
+ gap: 2rem;
+ align-items: center;
}
+ .hero-body > p {
+ font-size: 1.25rem;
+ color: var(--brand-black, #001219);
+ line-height: 1.7;
+ margin: 0;
+ text-align: center;
+ }
- // Wide layout: content box to the right of image
- @media (min-width: 1200px) {
- flex-direction: row;
- align-items: center;
- justify-content: flex-end;
- min-height: 400px;
- overflow: hidden;
+ .hero-buttons {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ width: fit-content;
- // Constrain total width to match content-blocks
- &>p, .hero-content {
- max-width: 1200px;
+ > p {
+ display: contents;
}
- &>p {
- flex: 1 1 auto;
- display: flex;
- justify-content: flex-end;
- align-items: center;
- margin: 0;
+ .btn {
+ text-align: center;
+ display: block;
}
+ }
- .hero-image {
- max-width: 100%;
- max-height: 100%;
- object-fit: contain;
- object-position: center;
- mask-image: linear-gradient(
- to right,
- transparent 0%,
- black 15%,
- black 85%,
- transparent 100%
- );
- -webkit-mask-image: linear-gradient(
- to right,
- transparent 0%,
- black 15%,
- black 85%,
- transparent 100%
- );
- }
-
- .hero-content {
- flex: 0 0 auto;
- width: auto;
- max-width: 400px;
- padding: 0rem 1rem 1rem;
- margin: 4rem calc((100vw - 1200px) / 2) 2rem -80px;
- border-radius: 12px;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
- position: relative;
- z-index: 1;
+ @media (min-width: 768px) {
+ padding: 5rem 2rem;
- h1 {
- font-size: 1.5rem;
- }
+ h1 {
+ font-size: 3.5rem;
+ }
- p {
- font-size: 1rem;
- margin-bottom: 1rem;
- }
+ .hero-body {
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: 3rem;
}
- .hero-buttons {
- gap: 0.75rem;
+ .hero-body > p {
+ font-size: 1.35rem;
+ text-align: left;
+ flex: 1;
+ }
- .btn.btn-lg {
- padding: 0.6rem 1.25rem;
- font-size: 0.95rem;
- }
+ .hero-buttons {
+ flex-shrink: 0;
+ align-items: stretch;
}
}
}
.content-block {
- max-width: 100%;
- margin: 0 auto;
- padding: 4rem 2rem;
- background: var(--brand-darkteal);
- color: var(--brand-paleteal);
+ max-width: 1200px;
+ margin: 2rem auto;
+ padding: 4rem 2rem 2rem;
+ background: transparent;
+ color: var(--brand-darkteal);
+ border-radius: 24px;
+
+ @media (max-width: 1232px) {
+ border-radius: 0;
+ margin-left: 0;
+ margin-right: 0;
+ max-width: 100%;
+ }
> * {
max-width: 1200px;
@@ -167,28 +183,32 @@ code {
}
> h2 {
- text-align: center;
+ text-align: left;
font-size: 2rem;
- color: var(--brand-paleteal);
+ color: var(--brand-darkteal);
margin-bottom: 3rem;
- border-bottom-color: var(--brand-paleteal);
+ border-bottom-color: var(--brand-darkteal);
}
a {
- color: var(--brand-lightteal);
+ color: var(--brand-darkteal);
}
- &.alt-bg {
- background: var(--brand-paleteal, #DEF1EB);
+ &.dark-bg {
+ background: rgba(249, 249, 249, 0.7);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
color: var(--brand-darkteal);
+ box-shadow: 0 8px 32px rgba(0, 95, 115, 0.1);
+ border: 1px solid rgba(249, 249, 249, 0.5);
- > section > p > a {
- color: black
+ > h2, > h3 {
+ color: var(--brand-darkteal);
+ border-bottom-color: var(--brand-darkteal);
}
- > h2 {
+ a:not(.btn) {
color: var(--brand-darkteal);
- border-bottom-color: var(--brand-darkteal);
}
}
}
@@ -211,11 +231,19 @@ code {
grid-template-rows: auto 1fr;
column-gap: 2rem;
row-gap: 0.75rem;
- background: white;
- border-radius: 8px;
+ background: rgba(255, 255, 255, 0.85);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+ border-radius: 16px;
padding: 2rem;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
- border: 1px solid var(--brand-lightteal, #94D2BD);
+ box-shadow: 0 8px 32px rgba(0, 95, 115, 0.1);
+ border: 1px solid rgba(148, 210, 189, 0.4);
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+
+ &:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 12px 40px rgba(0, 95, 115, 0.15);
+ }
h3 {
grid-column: 1;
@@ -321,15 +349,25 @@ code {
.tool-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
- gap: 1rem;
- margin-top: 1rem;
+ gap: 1.5rem;
+ margin-top: 1.5rem;
+ justify-items: center;
- // Single row when box is wide
- @media (min-width: 769px) and (max-width: 999px) {
+ @media (min-width: 600px) {
grid-template-columns: repeat(4, 1fr);
}
}
+.integrations {
+ text-align: center;
+
+ > p {
+ max-width: 600px;
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
.tool-item {
display: flex;
flex-direction: column;
@@ -343,6 +381,15 @@ code {
> p {
margin: 0;
}
+
+ a {
+ text-decoration: none;
+ color: inherit;
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
}
.tool-logo {
@@ -351,6 +398,81 @@ code {
object-fit: contain;
}
+// Install section
+.install-section {
+ text-align: center;
+
+ .install-grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 2rem;
+ margin-top: 1.5rem;
+
+ @media (min-width: 600px) {
+ grid-template-columns: 1fr 1fr;
+ gap: 3rem;
+ }
+ }
+
+ .install-column {
+ h4 {
+ font-size: 1.1rem;
+ margin-bottom: 1rem;
+ opacity: 0.9;
+ }
+
+ pre {
+ text-align: left;
+ font-size: 0.9rem;
+ }
+ }
+
+ #installer-buttons {
+ .install-version {
+ margin-bottom: 1.5rem;
+ font-size: 0.95rem;
+ opacity: 0.8;
+ }
+
+ .install-buttons-row {
+ display: flex;
+ gap: 2rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+
+ .install-option {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.5rem;
+ }
+
+ .install-btn {
+ min-width: 160px;
+ }
+
+ .install-description {
+ font-size: 0.85rem;
+ opacity: 0.7;
+ margin: 0;
+ }
+ }
+
+ .install-cli-options {
+ margin-top: 1.5rem;
+ font-size: 0.9rem;
+ opacity: 0.85;
+
+ code {
+ background: rgba(0, 95, 115, 0.08);
+ padding: 0.2em 0.5em;
+ border-radius: 4px;
+ font-size: 0.85em;
+ }
+ }
+}
+
.cta-section {
text-align: center;
padding: 2rem 0;
@@ -373,11 +495,91 @@ code {
}
}
-// Hide category badges on gallery example pages
-.quarto-listing-category {
- // Keep category sidebar for filtering
+// Example carousel
+.examples-carousel {
+ #listing-example-carousel {
+ overflow-x: auto;
+ scroll-snap-type: x mandatory;
+ -webkit-overflow-scrolling: touch;
+ padding-bottom: 1rem;
+ mask-image: linear-gradient(
+ to right,
+ transparent 0%,
+ black 2%,
+ black 90%,
+ transparent 100%
+ );
+ -webkit-mask-image: linear-gradient(
+ to right,
+ transparent 0%,
+ black 2%,
+ black 90%,
+ transparent 100%
+ );
+
+ // Force grid to single row with fixed column sizes
+ .list.grid {
+ display: grid !important;
+ grid-template-columns: repeat(6, 320px) !important;
+ grid-template-rows: 1fr !important;
+ grid-auto-flow: column !important;
+ gap: 1.5rem !important;
+ width: max-content !important;
+ max-width: none !important;
+ }
+
+ // Remove Bootstrap column classes
+ .list.grid > [class*="g-col"] {
+ grid-column: auto !important;
+ max-width: none !important;
+ width: auto !important;
+ }
+
+ .quarto-grid-item {
+ background: rgba(255, 255, 255, 0.8);
+ border-radius: 12px;
+ overflow: hidden;
+ border: 1px solid rgba(148, 210, 189, 0.3);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+ scroll-snap-align: start;
+
+ &:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 8px 24px rgba(0, 95, 115, 0.15);
+ }
+
+ .card-img-top {
+ width: 100%;
+ height: auto;
+ }
+
+ .card-title {
+ font-size: 0.95rem;
+ padding: 0.5rem 0.75rem;
+ margin: 0;
+ text-align: center;
+
+ a {
+ text-decoration: none;
+ color: var(--brand-darkteal);
+ }
+ }
+
+ .card-body {
+ padding: 0;
+ }
+ }
+ }
+
+ .see-all-link {
+ display: inline-block;
+ margin-top: 1.5rem;
+ font-weight: 500;
+ color: var(--brand-darkteal);
+ }
}
+// Hide category badges on gallery example pages
body:has(.quarto-title) .quarto-categories {
display: none;
}
@@ -385,7 +587,7 @@ body:has(.quarto-title) .quarto-categories {
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
- border-radius: 6px;
+ border-radius: 50rem;
font-weight: 500;
text-decoration: none;
transition: all 0.2s ease;
@@ -405,6 +607,20 @@ body:has(.quarto-title) .quarto-categories {
border-color: var(--brand-teal, #0A9396);
color: white;
text-decoration: none;
+ box-shadow: 0 0 20px rgba(0, 95, 115, 0.3);
+ transform: translateY(-2px);
+ }
+ }
+
+ &.btn-outline-light {
+ background: transparent;
+ color: var(--brand-darkteal, #005F73);
+ border: 2px solid var(--brand-darkteal, #005F73);
+
+ &:hover {
+ background: var(--brand-darkteal, #005F73);
+ color: white;
+ text-decoration: none;
}
}