From 26051e54c9ce135b0186acd25f6edb570c220e5b Mon Sep 17 00:00:00 2001
From: Kevin Longe <273747162+kvlonge@users.noreply.github.com>
Date: Sun, 1 Nov 2026 05:54:32 +0000
Subject: [PATCH] Make favicons transparent and simplify hero actions
---
README.md | 12 +++++++-----
assets/favicon-16.png | Bin 610 -> 396 bytes
assets/favicon-32.png | Bin 1416 -> 918 bytes
assets/favicon-dark-32.png | Bin 0 -> 903 bytes
favicon.ico | Bin 5166 -> 3263 bytes
index.html | 5 +----
styles.css | 8 --------
tests/website.spec.mjs | 31 ++++++++++++++++++++++++++++++-
theme.js | 4 +++-
9 files changed, 41 insertions(+), 19 deletions(-)
create mode 100644 assets/favicon-dark-32.png
diff --git a/README.md b/README.md
index 5946185..9a3f020 100644
--- a/README.md
+++ b/README.md
@@ -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
@@ -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.
diff --git a/assets/favicon-16.png b/assets/favicon-16.png
index c63a40ab80f0a0cf55515bd5cc8a540bdf26059a..cca1cc995174b704eb0240a44791ee3d87e1145e 100644
GIT binary patch
delta 380
zcmV-?0fYYH1dIca8Gi-<001BJ|6u?C0aHmtK~#9!#nU}V98nYn;BRIvOw5ADLWqH&
z!6ua=qznWF!76B*CT%J)g_Vt9V`G)d!a`ZWK*}^0LGY(jB!LJjf<Ec~=?M2_4b2yyC`Ebq`i3m0$2Ah
2#_*
zlz&n}2&I%#!U$mmGeQ`nj4_Nc7g7ij6%|>Kf8gWvv~_9e8#ibB_~_qSvAhpJ_2tXk
zvbRT@%?P2SDT(`YcSV>?N<2acA%v>Y$+?pYE(8}sUHkZPak+bT7N0EA>-9;~p4+{5
zpSMn%lf5Y{%-Dyby}A1lLI?5=PMlz&lz&c6m=rg5O!4V6MaNIpT&c0$v~}3+N^L$q
z-j4w!N?eQ9g{_fPZSm987wUe&VIai*NOHDBwaQ@Jj?4(%U-&iL>>Ni^OY@a7{
zXXV!AVsBR+8)eIcWEe@nkcSY;Hpd)Z7Y+;PMO>{&BUKk)-j={`NTzWCwVmHU-C43G
zf-w(l#4QYAS$=)*GbFbrY;0rDoKj>gb=*q7!zerDo5v$)oX|T2GiSgaunxIVKLsnm
z*7aL7TSa56CZTx!<~?!wcabrJ!asoHmfW2FA|A7*fMipxBcZvsW@nVY!%V-iM{Iw+
zf&i$&w3TWUEkpkYD$c7m=Rc@C{QQmpfZ*G|s0OCh>~jtOqB=oDmKiHW!{f3{iOr4c
z;jkaE1q}Qv;h|*oO?%-r?;r2
1A-jD_F1*E-ZU_XO2L#f~*T~Mq
zo3!UX84(#JP`uu{lFQAT(r}~eZM-C;mlt^*85*7T>XtdaHy`%kvT@Gt;St;n>-Xvy
zT3ve^B|OwBfo%Q?n*eW4+`|fBrmriF9!gR_IG@k13YLmRtXp>yjaFZp(U59canSJ}
zZR45}E7Myb>>{H=mL{UYw=qFTVcZ+ldP?Z@r
EqkC!>IkEzb{@)SsWP+%qM6?^IBPZIJ>+4k_W+5CVqd2Amfpgor7Sx
z>@=P~Eox%
Your data stack. Engineered to change.Rust-backed SQL and Python tools for data engineers.
Compile-time checks, tested behaviour and enforceable architecture.
diff --git a/styles.css b/styles.css index c7ace19..ecfe6f2 100644 --- a/styles.css +++ b/styles.css @@ -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; @@ -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; @@ -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; } diff --git a/tests/website.spec.mjs b/tests/website.spec.mjs index 26b7b91..97816e4 100644 --- a/tests/website.spec.mjs +++ b/tests/website.spec.mjs @@ -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); @@ -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`); }); } @@ -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(); @@ -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); @@ -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); + } }); diff --git a/theme.js b/theme.js index 4d4ae7b..1aba173 100644 --- a/theme.js +++ b/theme.js @@ -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')); @@ -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', () => {