Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ light/dark preference in the browser. Without JavaScript, the page remains usabl
## Checks

Browser tests cover theme selection and persistence, keyboard navigation, responsive layouts,
product statuses, sharing metadata and image dimensions. They require Node.js and Python 3:
product statuses, sharing metadata, image dimensions and favicon transparency. They require Node.js and Python 3:

```sh
npm ci
Expand All @@ -35,13 +35,15 @@ publishing or deployment step.

## Sharing assets

`favicon.ico` contains 16px, 32px and 48px icons. PNG favicons and a 180px Apple touch icon use the
original SQLStack mark on white backing for legibility in light and dark browser chrome.
`favicon.ico` contains transparent 16px, 32px and 48px icons. The PNG favicons preserve the same
original SQLStack mark without its former white backing. The existing theme script selects a white
variant for dark browser chrome using the system preference, independently of the page's theme
override. The 180px Apple home-screen icon retains its opaque background.
`assets/og-sqlstack-dark.png` is an opaque 1200 × 630 sharing card using the original SQLStack
wordmark and the site's Inter typography on a near-black background. Its branding and text fit
inside the central 630px square so small thumbnail crops retain the complete message. The previous
`assets/og-sqlstack.png` remains available for cached metadata. These are raster derivatives, not
replacement logos or SVG originals.
`assets/og-sqlstack.png` remains available for cached metadata. These use the original raster artwork,
not replacement logos or SVG originals.

Canonical and Open Graph/Twitter URLs target `https://sqlstack.io/`. Local previews can verify the
metadata and assets, but public social-crawler validation requires a separately authorized deployment.
Binary file modified assets/favicon-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/favicon-32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/favicon-dark-32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified favicon.ico
Binary file not shown.
5 changes: 1 addition & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
<meta name="twitter:description" content="Rust-backed SQL and Python tools for data engineers. Compile-time checks, tested behaviour and enforceable architecture.">
<meta name="twitter:image" content="https://sqlstack.io/assets/og-sqlstack-dark.png">
<meta name="twitter:image:alt" content="SQLStack. Your data stack. Engineered to change. Rust-backed SQL and Python tools for data engineers.">
<link rel="icon" href="favicon.ico" type="image/vnd.microsoft.icon" sizes="16x16 32x32 48x48">
<link rel="icon" href="assets/favicon-16.png" type="image/png" sizes="16x16">
<link rel="icon" href="assets/favicon-32.png" type="image/png" sizes="32x32">
<link rel="icon" href="assets/favicon-32.png?v=2" type="image/png" sizes="32x32" data-favicon>
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png" sizes="180x180">
<link rel="preload" href="assets/fonts/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="assets/fonts/space-grotesk-bold.woff2" as="font" type="font/woff2" crossorigin>
Expand Down Expand Up @@ -58,7 +56,6 @@ <h1 id="headline"><span>Your data stack.</span> <span>Engineered to change.</spa
<p class="lede">Rust-backed SQL and Python tools for data engineers.</p>
<p class="principles">Compile-time checks, tested behaviour and enforceable architecture.</p>
<div class="hero-actions">
<a class="button primary" href="#tools">Find a tool <span aria-hidden="true">↓</span></a>
<a class="button" href="https://github.com/sqlstack-io/sqlstack">View on GitHub <span aria-hidden="true">↗</span></a>
</div>
</section>
Expand Down
8 changes: 0 additions & 8 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@
--focus: #1768ba;
--button-border: #bdc9da;
--button-hover: #f3f6fa;
--primary: #0866db;
--primary-ink: #ffffff;
--primary-hover: #0755b7;
--gold-ink: #765600;
--gold-bg: #fff3cb;
--pink-ink: #a52138;
Expand All @@ -50,9 +47,6 @@
--focus: #75b5ff;
--button-border: #626a76;
--button-hover: #171a1f;
--primary: #3788e8;
--primary-ink: #0d0f12;
--primary-hover: #66a6f0;
--gold-ink: #ffe07a;
--gold-bg: #352b12;
--pink-ink: #ffacba;
Expand Down Expand Up @@ -99,8 +93,6 @@ h1 span { display: block; }
.hero-actions { display: flex; justify-content: center; flex-wrap: wrap; gap: 12px; margin-top: 20px; }
.button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 14px 20px; min-height: 48px; border: 1px solid var(--button-border); border-radius: 2px; color: var(--ink); font-size: 14px; line-height: 20px; text-decoration: none; }
.button:hover { background: var(--button-hover); text-decoration: none; }
.button.primary { color: var(--primary-ink); background: var(--primary); border-color: var(--primary); }
.button.primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.tool-row { display: grid; grid-template-columns: 304px minmax(0, 1fr); gap: 24px; border-top: 1px solid var(--line); padding: 18px 0 16px; }
.category { padding-top: 8px; }
.category .eyebrow { margin: 0 0 8px; color: var(--muted); font-size: 14px; line-height: 20px; text-transform: uppercase; }
Expand Down
31 changes: 30 additions & 1 deletion tests/website.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ for (const system of ['light', 'dark']) {
const toggle = page.getByRole('button', { name: 'Dark mode', exact: true });
await expect(page.locator('html')).toHaveCSS('color-scheme', system);
await expect(toggle).toHaveAttribute('aria-pressed', String(system === 'dark'));
await expect(page.locator('[data-favicon]')).toHaveAttribute('href', `assets/favicon${system === 'dark' ? '-dark' : ''}-32.png?v=2`);

await page.emulateMedia({ colorScheme: other });
await expect(page.locator('html')).toHaveCSS('color-scheme', other);
await expect(page.locator('[data-favicon]')).toHaveAttribute('href', `assets/favicon${other === 'dark' ? '-dark' : ''}-32.png?v=2`);
await page.emulateMedia({ colorScheme: system });
await expect(page.locator('html')).toHaveCSS('color-scheme', system);

Expand All @@ -24,6 +26,7 @@ for (const system of ['light', 'dark']) {
await page.emulateMedia({ colorScheme: system });
await expect(page.locator('html')).toHaveCSS('color-scheme', other);
await expect(page.locator('meta[name="theme-color"]')).toHaveAttribute('content', other === 'dark' ? '#0d0f12' : '#ffffff');
await expect(page.locator('[data-favicon]')).toHaveAttribute('href', `assets/favicon${system === 'dark' ? '-dark' : ''}-32.png?v=2`);
});
}

Expand Down Expand Up @@ -59,7 +62,11 @@ test('approved products, sharing assets and responsive navigation work in both t
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
await expect(page.getByRole('button', { name: 'Dark mode', exact: true })).toBeInViewport();
}
await page.getByRole('link', { name: 'Find a tool', exact: false }).click();
await expect(page.locator('.hero').getByRole('link')).toHaveCount(1);
await expect(page.locator('.hero').getByRole('link')).toHaveAccessibleName('View on GitHub');
await expect(page.locator('.hero').getByRole('link')).toHaveAttribute('href', 'https://github.com/sqlstack-io/sqlstack');
await page.getByRole('link', { name: 'Skip to tools', exact: true }).focus();
await page.keyboard.press('Enter');
await expect(page).toHaveURL(/#tools$/);
await page.keyboard.press('Tab');
await expect(page.getByRole('link', { name: 'SQLBuild website', exact: true })).toBeFocused();
Expand All @@ -75,6 +82,7 @@ test('approved products, sharing assets and responsive navigation work in both t
['/assets/og-sqlstack-dark.png', 1200, 630],
['/assets/favicon-16.png', 16, 16],
['/assets/favicon-32.png', 32, 32],
['/assets/favicon-dark-32.png', 32, 32],
['/assets/apple-touch-icon.png', 180, 180],
]) {
const response = await request.get(path);
Expand All @@ -90,4 +98,25 @@ test('approved products, sharing assets and responsive navigation work in both t
expect(ico.readUInt16LE(2)).toBe(1);
expect(ico.readUInt16LE(4)).toBe(3);
expect([ico[6], ico[22], ico[38]]).toEqual([16, 32, 48]);

for (const path of ['/assets/favicon-16.png', '/assets/favicon-32.png', '/assets/favicon-dark-32.png', '/favicon.ico']) {
const pixels = await page.evaluate(async (src) => {
const image = new Image();
image.src = src;
await image.decode();
const canvas = document.createElement('canvas');
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
const context = canvas.getContext('2d');
context.drawImage(image, 0, 0);
const data = context.getImageData(0, 0, canvas.width, canvas.height).data;
const corners = [0, canvas.width - 1, canvas.width * (canvas.height - 1), canvas.width * canvas.height - 1];
return {
corners: corners.map((pixel) => data[pixel * 4 + 3]),
opaquePixels: Array.from(data).filter((value, index) => index % 4 === 3 && value === 255).length,
};
}, path);
expect(pixels.corners, path).toEqual([0, 0, 0, 0]);
expect(pixels.opaquePixels, path).toBeGreaterThan(0);
}
});
4 changes: 3 additions & 1 deletion theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
function apply(theme) {
root.dataset.theme = theme;
document.querySelector('meta[name="theme-color"]').content = theme === 'dark' ? '#0d0f12' : '#ffffff';
// Match browser chrome, even when the page theme is explicitly overridden.
document.querySelector('[data-favicon]').href = system.matches ? 'assets/favicon-dark-32.png?v=2' : 'assets/favicon-32.png?v=2';
const toggle = document.querySelector('[data-theme-toggle]');
if (toggle) {
toggle.setAttribute('aria-pressed', String(theme === 'dark'));
Expand All @@ -23,7 +25,7 @@
apply(preference || (system.matches ? 'dark' : 'light'));

system.addEventListener('change', () => {
if (!preference) apply(system.matches ? 'dark' : 'light');
apply(preference || (system.matches ? 'dark' : 'light'));
});

document.addEventListener('DOMContentLoaded', () => {
Expand Down
Loading