diff --git a/docs/graalvm.md b/docs/graalvm.md
index 2adf582d2..e8c4c5317 100644
--- a/docs/graalvm.md
+++ b/docs/graalvm.md
@@ -97,15 +97,9 @@ metadata directory, small enough to read in a minute:
- [Spring Boot example metadata](https://github.com/storm-orm/storm-example-kotlin-spring-boot-4-graalvm/blob/main/src/main/kotlin/st/orm/demo/imdb/ApplicationRuntimeHints.kt)
- [Ktor example metadata](https://github.com/storm-orm/storm-example-kotlin-ktor-graalvm/tree/main/src/main/resources/META-INF/native-image)
-## Measured
+## What you get
-Both examples, on an Apple M-series MacBook against PostgreSQL 17, verified by their Playwright
-interface suites running against the native binaries:
-
-| | Startup | Dataset import (1.5M rows) | Binary |
-|---|---|---|---|
-| Spring Boot 4 | ~0.25 s | 87 s | 122 MB |
-| Ktor 3 (CIO) | ~0.13 s | 83 s | 97 MB |
-
-The import runs faster natively than on the JVM (117 s): a one-shot batch job never gets the JIT
-warmup it would need to catch up.
+Both examples compile to a self-contained native binary that starts in a fraction of a second,
+with no JVM and no warmup, and both are verified by their Playwright interface suites running
+against the native binaries. A batch import can even finish faster natively than on the JVM: a
+one-shot job never runs long enough to earn back the JIT warmup it would need to catch up.
diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts
index 2ed76a3b2..3131c5acb 100644
--- a/website/docusaurus.config.ts
+++ b/website/docusaurus.config.ts
@@ -13,6 +13,12 @@ const config: Config = {
tagline: 'A modern, high-performance ORM for Kotlin 2.0+ and Java 21+',
favicon: 'img/storm-dark.png',
+ // Load the marketing type pair (Inter + JetBrains Mono) on the docs pages too,
+ // so the Docusaurus-chrome theme matches the custom pages (see custom.css).
+ stylesheets: [
+ 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap',
+ ],
+
url: 'https://orm.st',
baseUrl: '/',
@@ -151,7 +157,7 @@ const config: Config = {
// TODO(campaign): replace with a purpose-built 1200x630 social card.
image: 'img/storm.png',
navbar: {
- title: 'Storm',
+ title: 'ST/ORM',
logo: {
alt: 'Storm Logo',
src: 'img/storm-dark.png',
@@ -231,9 +237,9 @@ const config: Config = {
additionalLanguages: ['java', 'kotlin', 'groovy'],
},
colorMode: {
- defaultMode: 'light',
- disableSwitch: false,
- respectPrefersColorScheme: true,
+ defaultMode: 'dark',
+ disableSwitch: true,
+ respectPrefersColorScheme: false,
},
} satisfies Preset.ThemeConfig,
};
diff --git a/website/src/components/blog/blogTheme.js b/website/src/components/blog/blogTheme.js
index 66c3f05a0..4b4cf0ad9 100644
--- a/website/src/components/blog/blogTheme.js
+++ b/website/src/components/blog/blogTheme.js
@@ -15,7 +15,7 @@ export function BlogPage({title, description, slug, dateISO, body}) {
<>
-
{`${title} · Storm Blog`}
+
{`${title} · ST/ORM Blog`}
diff --git a/website/src/components/examples/ExampleReadmePage.js b/website/src/components/examples/ExampleReadmePage.js
index 6831a36c8..e3c120843 100644
--- a/website/src/components/examples/ExampleReadmePage.js
+++ b/website/src/components/examples/ExampleReadmePage.js
@@ -1,6 +1,6 @@
-import React from 'react';
+import React, {useEffect} from 'react';
import Head from '@docusaurus/Head';
-import {TUT_CSS, navHtml, FOOT_HTML} from '../tutorial/tutorialTheme';
+import {TUT_CSS, navHtml, FOOT_HTML, clonebar, wireSqlToggles} from '../tutorial/tutorialTheme';
// Route component for the /examples/ pages added by the
// example-readmes plugin. Receives the example (title, description, chips,
@@ -45,7 +45,9 @@ export default function ExampleReadmePage({example}) {
const {slug, repo, title, description, chips, html} = example;
const url = `https://orm.st/examples/${slug}/`;
const githubUrl = `https://github.com/storm-orm/${repo}`;
- const pageTitle = `${title} · Storm Example Projects`;
+ const pageTitle = `${title} · ST/ORM Example Projects`;
+
+ useEffect(() => wireSqlToggles(), []);
const body = `
${navHtml('examples')}
@@ -56,7 +58,7 @@ ${navHtml('examples')}
diff --git a/website/src/components/tutorial/tutorialTheme.js b/website/src/components/tutorial/tutorialTheme.js
index 79352f804..0b8d19518 100644
--- a/website/src/components/tutorial/tutorialTheme.js
+++ b/website/src/components/tutorial/tutorialTheme.js
@@ -130,6 +130,15 @@ export function editor({file, tag, code, sql, copy, variants}) {
`;
}
+// A one-line "$ " shell bar with a Copy button, matching the editor
+// copy control. `command` is copied verbatim (without the leading $). Wired by
+// wireSqlToggles(), so pages that render a clonebar() must call it in useEffect.
+export function clonebar(command) {
+ const data = esc(command).replace(/"/g, '"');
+ return `
$${esc(command)}` +
+ `
`;
+}
+
// A compact two-pane "at a glance" comparison for one-liner contrasts. Only
// use where both snippets are genuinely short; full examples stay sequential.
export function glance({left, right}) {
@@ -140,8 +149,43 @@ export function glance({left, right}) {
`;
}
-// Wires up every "Show SQL" and "Copy" button rendered by editor(). Returns a
-// cleanup function for the calling useEffect.
+// Wires one .copybtn: copies its data-copy to the clipboard and flips the label
+// to "Copied" for a moment. Returns a cleanup function.
+function attachCopy(copyBtn) {
+ const txt = copyBtn.querySelector('.copybtntext');
+ let timer = null;
+ const onCopy = () => {
+ const text = copyBtn.dataset.copy;
+ // The Clipboard API needs a secure context; fall back to a throwaway
+ // textarea so copy also works on plain-http previews (e.g. LAN).
+ if (navigator.clipboard && window.isSecureContext) {
+ navigator.clipboard.writeText(text);
+ } else {
+ const ta = document.createElement('textarea');
+ ta.value = text;
+ document.body.appendChild(ta);
+ ta.select();
+ document.execCommand('copy');
+ ta.remove();
+ }
+ copyBtn.classList.add('on');
+ if (txt) txt.textContent = 'Copied';
+ clearTimeout(timer);
+ timer = setTimeout(() => {
+ copyBtn.classList.remove('on');
+ if (txt) txt.textContent = 'Copy';
+ }, 1600);
+ };
+ copyBtn.addEventListener('click', onCopy);
+ return () => {
+ copyBtn.removeEventListener('click', onCopy);
+ clearTimeout(timer);
+ };
+}
+
+// Wires up every "Show SQL" and "Copy" button rendered by editor(), plus the
+// Copy button on any clonebar(). Returns a cleanup function for the calling
+// useEffect.
export function wireSqlToggles() {
const cleanups = [];
document.querySelectorAll('.storm-tut .editor').forEach((ed) => {
@@ -157,37 +201,7 @@ export function wireSqlToggles() {
cleanups.push(() => btn.removeEventListener('click', onClick));
}
const copyBtn = ed.querySelector('.copybtn');
- if (copyBtn) {
- const txt = copyBtn.querySelector('.copybtntext');
- let timer = null;
- const onCopy = () => {
- const text = copyBtn.dataset.copy;
- // The Clipboard API needs a secure context; fall back to a throwaway
- // textarea so copy also works on plain-http previews (e.g. LAN).
- if (navigator.clipboard && window.isSecureContext) {
- navigator.clipboard.writeText(text);
- } else {
- const ta = document.createElement('textarea');
- ta.value = text;
- document.body.appendChild(ta);
- ta.select();
- document.execCommand('copy');
- ta.remove();
- }
- copyBtn.classList.add('on');
- if (txt) txt.textContent = 'Copied';
- clearTimeout(timer);
- timer = setTimeout(() => {
- copyBtn.classList.remove('on');
- if (txt) txt.textContent = 'Copy';
- }, 1600);
- };
- copyBtn.addEventListener('click', onCopy);
- cleanups.push(() => {
- copyBtn.removeEventListener('click', onCopy);
- clearTimeout(timer);
- });
- }
+ if (copyBtn) cleanups.push(attachCopy(copyBtn));
const sel = ed.querySelector('.varsel');
if (sel) {
const panes = ed.querySelectorAll('.varpane');
@@ -202,6 +216,9 @@ export function wireSqlToggles() {
cleanups.push(() => sel.removeEventListener('change', onChange));
}
});
+ document.querySelectorAll('.storm-tut .clonebar .copybtn').forEach((copyBtn) => {
+ cleanups.push(attachCopy(copyBtn));
+ });
return () => cleanups.forEach((fn) => fn());
}
@@ -215,7 +232,7 @@ export function TutorialPage({title, description, slug, body}) {
<>
- {`${title} · Storm Tutorials`}
+ {`${title} · ST/ORM Tutorials`}
@@ -342,6 +359,8 @@ export const TUT_CSS = `
.storm-tut .sqlbtn:hover,.storm-tut .copybtn:hover{background:rgba(129,140,248,.12);border-color:rgba(129,140,248,.5)}
.storm-tut .sqlbtn.on,.storm-tut .copybtn.on{background:rgba(129,140,248,.16);color:#aab2ff}
.storm-tut .sqlbtn .ico,.storm-tut .copybtn .ico{width:13px;height:13px;opacity:.9}
+ .storm-tut .clonebar .clonecmd{overflow-x:auto}
+ .storm-tut .clonebar .copybtn{margin-left:auto;flex:none;gap:0;padding:5px 8px}
/* variant selector (same chrome as the buttons; native select behind a
custom chevron because appearance:none drops the platform arrow) */
.storm-tut .varwrap{position:relative;margin-left:14px;display:inline-flex;align-items:center}
diff --git a/website/src/css/custom.css b/website/src/css/custom.css
index cd8f603fe..8d35dd550 100644
--- a/website/src/css/custom.css
+++ b/website/src/css/custom.css
@@ -1,22 +1,79 @@
+/* Docs (Infima) theme aligned with the marketing site's dark palette. The
+ custom landing/tutorial/blog pages carry their own dark shell; these variables
+ bring the Docusaurus-chrome pages (/docs) onto the same tokens: background
+ #070709, panels #0f0f14, text #eaeaf0, indigo accent #818cf8, borders #20202a,
+ and the Inter / JetBrains Mono type pair. Default mode is dark (see
+ docusaurus.config.ts colorMode); the light theme keeps a readable purple. */
+
:root {
- --ifm-color-primary: #3949ab;
- --ifm-color-primary-dark: #303f9f;
- --ifm-color-primary-darker: #283593;
- --ifm-color-primary-darkest: #1a237e;
- --ifm-color-primary-light: #5c6bc0;
- --ifm-color-primary-lighter: #7986cb;
- --ifm-color-primary-lightest: #9fa8da;
- --ifm-code-font-size: 95%;
+ /* Type pair shared with the marketing pages. */
+ --ifm-font-family-base: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
+ --ifm-font-family-monospace: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
+ --ifm-code-font-size: 92%;
+
+ /* Light theme uses a darker indigo so buttons/links keep contrast on white. */
+ --ifm-color-primary: #4f46e5;
+ --ifm-color-primary-dark: #4740ce;
+ --ifm-color-primary-darker: #433cc2;
+ --ifm-color-primary-darkest: #3731a0;
+ --ifm-color-primary-light: #6a62e9;
+ --ifm-color-primary-lighter: #7972eb;
+ --ifm-color-primary-lightest: #9d98f0;
}
[data-theme='dark'] {
- --ifm-color-primary: #7986cb;
- --ifm-color-primary-dark: #5c6bc0;
- --ifm-color-primary-darker: #3949ab;
- --ifm-color-primary-darkest: #303f9f;
- --ifm-color-primary-light: #9fa8da;
- --ifm-color-primary-lighter: #c5cae9;
- --ifm-color-primary-lightest: #e8eaf6;
+ /* Accent: the marketing --accent (#818cf8), the lighter shade on dark. */
+ --ifm-color-primary: #818cf8;
+ --ifm-color-primary-dark: #6470f6;
+ --ifm-color-primary-darker: #5563f5;
+ --ifm-color-primary-darkest: #2b3cf2;
+ --ifm-color-primary-light: #9ea8fa;
+ --ifm-color-primary-lighter: #adb5fb;
+ --ifm-color-primary-lightest: #d8dcfe;
+
+ /* Surfaces and text mapped from the site tokens. */
+ --ifm-background-color: #070709;
+ --ifm-background-surface-color: #0f0f14;
+ --ifm-navbar-background-color: #0b0b0f;
+ --ifm-footer-background-color: #0b0b0f;
+ --ifm-font-color-base: #eaeaf0;
+ --ifm-heading-color: #eaeaf0;
+ --ifm-color-content: #b7b8c2;
+ --ifm-color-content-secondary: #8a8a96;
+
+ /* Borders / rules → --border #20202a, softened via the emphasis ramp. */
+ --ifm-toc-border-color: #20202a;
+ --ifm-table-border-color: #20202a;
+ --ifm-hr-border-color: #20202a;
+ --ifm-color-emphasis-100: #0f0f14;
+ --ifm-color-emphasis-200: #17171f;
+ --ifm-color-emphasis-300: #20202a;
+
+ /* Links, sidebar menu, breadcrumbs → accent. */
+ --ifm-link-color: #a5b4fc;
+ --ifm-menu-color: #b7b8c2;
+ --ifm-menu-color-active: #a5b4fc;
+ --ifm-menu-color-background-active: rgba(129, 140, 248, 0.08);
+ --ifm-menu-color-background-hover: rgba(129, 140, 248, 0.06);
+ --ifm-breadcrumb-color-active: #a5b4fc;
+ --ifm-breadcrumb-item-background-active: rgba(129, 140, 248, 0.1);
+
+ /* Code blocks → panel background (dracula tokens still read fine on it). */
+ --ifm-code-background: #0f0f14;
+ --prism-background-color: #0b0b0f;
+ --docusaurus-highlighted-code-line-bg: rgba(129, 140, 248, 0.14);
+}
+
+/* Navbar: subtle bottom rule like the marketing nav. */
+[data-theme='dark'] .navbar {
+ border-bottom: 1px solid #17171f;
+ box-shadow: none;
+}
+
+/* Inline code chips: match the marketing --panel/--border-soft look. */
+[data-theme='dark'] code {
+ background: #0f0f14;
+ border: 1px solid #17171f;
}
/* Cleaner tables in docs and blog. Infima draws a full cell grid (vertical +
diff --git a/website/src/pages/benchmarks.js b/website/src/pages/benchmarks.js
index d4eaa5c74..f569c0855 100644
--- a/website/src/pages/benchmarks.js
+++ b/website/src/pages/benchmarks.js
@@ -1,11 +1,11 @@
import React, {useEffect} from 'react';
import Head from '@docusaurus/Head';
import {
- TUT_CSS, navHtml, FOOT_HTML, wireSqlToggles, editor,
+ TUT_CSS, navHtml, FOOT_HTML, wireSqlToggles, editor, clonebar,
K, T, S, C, F, N, A, P, QK, QQ,
} from '../components/tutorial/tutorialTheme';
-const TITLE = 'Benchmarks · Storm ORM vs Hibernate, jOOQ, Exposed and Jimmer';
+const TITLE = 'Benchmarks · ST/ORM vs Hibernate, jOOQ, Exposed and Jimmer';
const DESC = 'Reproducible JMH benchmarks of Storm against JDBC, Hibernate, jOOQ, Exposed and Jimmer on PostgreSQL 17, with the entity and query code behind every number.';
// Results from the reproducible suite: one tuned PostgreSQL 17 container over TCP, JMH, 2 forks,
@@ -486,7 +486,7 @@ ${charts}
The repository contains the full methodology, the statement-log auditing tools used to verify round-trip counts, and every implementation in full; scripts/run.sh reproduces the numbers.
+ ${clonebar('git clone https://github.com/storm-orm/storm-benchmarks.git')}
View on GitHub →
diff --git a/website/src/pages/blog/index.js b/website/src/pages/blog/index.js
index e7efdb2bd..5003138c4 100644
--- a/website/src/pages/blog/index.js
+++ b/website/src/pages/blog/index.js
@@ -7,7 +7,7 @@ import {TUT_CSS, navHtml, FOOT_HTML} from '../../components/tutorial/tutorialThe
// matches the tutorials and examples. Each article lives next to this file as
// its own custom page; keep this list newest-first and in sync with them.
-const TITLE = 'Storm Blog · Design decisions and deep dives';
+const TITLE = 'ST/ORM Blog · Design decisions and deep dives';
const DESC =
'The design decisions behind ST/ORM, deep dives into how it works, and the ' +
'thinking that shapes the API. Plain values, explicit SQL, and no hidden ' +
diff --git a/website/src/pages/comparison.js b/website/src/pages/comparison.js
index fae1c1fd9..58ce65b2f 100644
--- a/website/src/pages/comparison.js
+++ b/website/src/pages/comparison.js
@@ -12,7 +12,7 @@ import {TUT_CSS, navHtml, FOOT_HTML, wireSqlToggles} from '../components/tutoria
// only differences that are real and that the rival does not already cover
// are called out.
-const TITLE = 'Comparison · Storm vs JPA, jOOQ, Exposed, Jimmer';
+const TITLE = 'Comparison · ST/ORM vs JPA, jOOQ, Exposed, Jimmer';
const DESC =
'How Storm compares to Hibernate/JPA, Spring Data, jOOQ, Exposed, Ktorm, ' +
'MyBatis, and Jimmer. A decision matrix plus a short, fair take on each, with ' +
diff --git a/website/src/pages/examples/index.js b/website/src/pages/examples/index.js
index d2389f75d..3a86cf416 100644
--- a/website/src/pages/examples/index.js
+++ b/website/src/pages/examples/index.js
@@ -8,7 +8,7 @@ import {TUT_CSS, navHtml, FOOT_HTML} from '../../components/tutorial/tutorialThe
// and Java. Each card links to its detail page (README rendered inline by
// the example-readmes plugin, with the clone command and GitHub link).
-const TITLE = 'Storm Example Projects · Complete applications built with Storm';
+const TITLE = 'ST/ORM Example Projects · Complete applications built with Storm';
const DESC =
'Complete example applications built with Storm ORM: a movie browser on ' +
'the public IMDB dataset, implemented on Spring Boot 4 and Ktor, in Kotlin ' +
diff --git a/website/src/pages/index.js b/website/src/pages/index.js
index f93f26450..718f67c33 100644
--- a/website/src/pages/index.js
+++ b/website/src/pages/index.js
@@ -774,7 +774,7 @@ export default function Home() {
<>
-