From f347ec330ac6b669ba7755abbd349d00331e279f Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Mon, 13 Jul 2026 19:03:53 +0000 Subject: [PATCH] Adopt shared chrome from signage-kit 2026.7.2 Pull the footer badge, canonical @font-face set, fonts vendoring, and the .stage frame from @screenly-labs/signage-kit instead of restating them here: - @import the kit's fonts.css / brand.css / stage.css; drop the local @font-face blocks, the .brand rules, and the .stage rule (byte-identical to the canonical frame). - Drop the @fontsource deps (the kit owns them) and reduce sync-fonts.js to a one-line call to the kit's syncFonts(['bricolage-grotesque','hanken-grotesk']). - Import removeScreenlyBranding from the kit instead of the local copy. - Express the fluid root as --root-min/-gain/-max overrides (clamp lives in the preset); --font-sans (Hanken Grotesk) now comes from the preset, while --font-display stays overridden to Bricolage Grotesque, this app's display face. Keep the portrait 2x2 grid rule. Repoint the kit to #2026.7.2. Verified: build clean; badge in full+degraded across the resolution matrix (1080p/portrait/800x480/4K); removed under screenly-viewer UA; design unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- assets/static/js/main.ts | 10 +--- assets/static/styles/tailwind.css | 77 +++++++------------------------ bun.lock | 14 ++++-- package.json | 4 +- sync-fonts.js | 40 +++------------- 5 files changed, 34 insertions(+), 111 deletions(-) diff --git a/assets/static/js/main.ts b/assets/static/js/main.ts index 18968d2..f10e29e 100644 --- a/assets/static/js/main.ts +++ b/assets/static/js/main.ts @@ -5,6 +5,7 @@ // Side-effect import: installs the replaceChildren shim for the older-browser // degraded mode. Must stay first so the shim is in place before any render. import '@screenly-labs/signage-kit/polyfills' +import { removeScreenlyBranding } from '@screenly-labs/signage-kit/branding' import { computeState, pad2, parseTarget } from './timer' @@ -93,15 +94,6 @@ const render = (): void => { scheduleTick() } -// On a Screenly player the viewer is already a Screenly customer, so the -// promotional Screenly badge is removed. The 'screenly-viewer' token in the user -// agent marks these devices; every other browser keeps the badge. -const removeScreenlyBranding = (): void => { - if (navigator.userAgent.includes('screenly-viewer')) { - document.querySelector('.brand')?.remove() - } -} - const init = (): void => { removeScreenlyBranding() render() diff --git a/assets/static/styles/tailwind.css b/assets/static/styles/tailwind.css index 8f93957..fc90930 100644 --- a/assets/static/styles/tailwind.css +++ b/assets/static/styles/tailwind.css @@ -9,12 +9,18 @@ ========================================================================= */ @import 'tailwindcss'; -/* Shared base (brand tokens + fluid root + the degraded-mode kill-switch) from - @screenly-labs/signage-kit. The app's own @theme + component styles below layer - on top; the design identity stays here, per-app. */ +/* Shared base (brand/font tokens + fluid root + the degraded-mode kill-switch), + the canonical @font-face set, the standardized footer badge, and the .stage + frame — all from @screenly-labs/signage-kit. The app's own @theme + component + styles below layer on top; the design identity stays here, per-app. */ @import '@screenly-labs/signage-kit/styles/preset.css'; +@import '@screenly-labs/signage-kit/styles/fonts.css'; +@import '@screenly-labs/signage-kit/styles/brand.css'; +@import '@screenly-labs/signage-kit/styles/stage.css'; -/* Design tokens — also generate utilities (bg-ink, text-mint, font-display…). */ +/* Design tokens — also generate utilities (bg-ink, text-mint, font-display…). + --font-sans (Hanken Grotesk) comes from the kit preset; --font-display is + overridden to Bricolage Grotesque, this app's grotesque display face. */ @theme { --color-ink: #0b0e13; --color-ink-deep: #05070b; @@ -24,32 +30,16 @@ --color-mint: #38e0b0; --color-panel: #131822; --font-display: 'Bricolage Grotesque', ui-sans-serif, system-ui, sans-serif; - --font-sans: 'Hanken Grotesk', ui-sans-serif, system-ui, sans-serif; -} - -/* Webfonts: self-hosted, vendored from Bun-managed @fontsource (sync-fonts.js). - Variable "standard" axis = optical-size + weight + width. */ -@font-face { - font-family: 'Bricolage Grotesque'; - font-style: normal; - font-weight: 200 800; - font-display: swap; - src: url('/static/fonts/bricolage-grotesque-latin-standard-normal.woff2') format('woff2'); -} -@font-face { - font-family: 'Hanken Grotesk'; - font-style: normal; - font-weight: 100 900; - font-display: swap; - src: url('/static/fonts/hanken-grotesk-latin-wght-normal.woff2') format('woff2'); } @layer base { - /* One fluid root drives the whole scale; vw+vh keeps it orientation-neutral so - portrait and landscape resolve to the same type size. Children size in rem, - so the layout scales cleanly from 480px → 4K with no breakpoints. */ + /* Tune the kit's fluid root (clamp of vw+vh) for this design; the clamp itself + lives in the preset. Children size in rem, so the layout scales cleanly from + 480px → 4K, portrait and landscape, with no breakpoints. */ :root { - font-size: clamp(16px, 1vw + 1vh, 54px); + --root-min: 16px; + --root-gain: 1; + --root-max: 54px; } body { @@ -67,16 +57,6 @@ } @layer components { - .stage { - position: relative; - display: grid; - place-items: center; - min-height: 100vh; /* fallback for engines below the svh floor (~2018 players) */ - min-height: 100svh; - padding: clamp(1.5rem, 6vmin, 4rem); - overflow: hidden; - } - .timer { position: relative; z-index: 1; @@ -184,31 +164,6 @@ :root[data-direction='up'] .unit__num { color: var(--color-mint); } - - /* Promotional Screenly badge, quiet in the corner. Removed on Screenly players - by main.ts (the viewer is already a customer). */ - .brand { - position: fixed; - right: clamp(1rem, 3.2vmin, 2.4rem); - bottom: clamp(1rem, 3.2vmin, 2.4rem); - z-index: 2; - display: block; - opacity: 0.55; - transition: opacity 200ms ease; - border-radius: 4px; - outline-offset: 6px; - } - - .brand:hover, - .brand:focus-visible { - opacity: 0.95; - } - - .brand img { - display: block; - width: auto; - height: clamp(13px, 1.3rem, 26px); - } } @layer base { diff --git a/bun.lock b/bun.lock index e83251d..5c1ca67 100644 --- a/bun.lock +++ b/bun.lock @@ -5,9 +5,7 @@ "": { "name": "screenly-timer-app", "dependencies": { - "@fontsource-variable/bricolage-grotesque": "^5.2.5", - "@fontsource-variable/hanken-grotesk": "^5.2.8", - "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.7.0", + "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.7.2", }, "devDependencies": { "@biomejs/biome": "^2.5.1", @@ -100,8 +98,16 @@ "@fontsource-variable/bricolage-grotesque": ["@fontsource-variable/bricolage-grotesque@5.2.10", "", {}, "sha512-5EDsCqgGpKVcJWE4sg9ydli+t5WM97mISYw5lla/Ev4z71FwXh1oN0YUU8xjkRW9+wBCGD9R+ntAvI8G4bUFJg=="], + "@fontsource-variable/fraunces": ["@fontsource-variable/fraunces@5.2.9", "", {}, "sha512-Y6IjunlN9Ni723np+GIgAaKzCDBrPRrqNi01TZxHs5wtHYROWFM9W6yiT+/gGwSjWIRD18oX17kD/BRWekc/Lw=="], + "@fontsource-variable/hanken-grotesk": ["@fontsource-variable/hanken-grotesk@5.2.8", "", {}, "sha512-K7hu09ZKReBc7EifRLeM4K94NZBrxFEg0nGcISmVwlFQOJo4EmYkLXTWEwr5JcNW4z2hr5zy8vLS2sxC8JDmrQ=="], + "@fontsource-variable/jetbrains-mono": ["@fontsource-variable/jetbrains-mono@5.2.8", "", {}, "sha512-WBA9elru6Jdp5df2mES55wuOO0WIrn3kpXnI4+W2ek5u3ZgLS9XS4gmIlcQhiZOWEKl95meYdvK7xI+ETLCq/Q=="], + + "@fontsource-variable/newsreader": ["@fontsource-variable/newsreader@5.2.10", "", {}, "sha512-MdI2iRwrqWpMOU/2aV2BgfZ4dJNlj/XlYaY8Zb7t87mWqHskYW0XiUANkt1cyOCiEfW2VQ0bQ5vZgGPp6B2B4w=="], + + "@fontsource/space-mono": ["@fontsource/space-mono@5.2.9", "", {}, "sha512-b61faFOHEISQ/pD25G+cfGY9o/WW6lRv6hBQQfpWvEJ4y1V+S4gmth95EVyBE2VL3qDYHeVQ8nBzrplzdXTDDg=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], @@ -140,7 +146,7 @@ "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], - "@screenly-labs/signage-kit": ["@screenly-labs/signage-kit@github:Screenly-Labs/signage-kit#eb470fb", { "peerDependencies": { "@csstools/postcss-cascade-layers": "^6", "browserslist": "^4", "esbuild": "^0.28", "lightningcss": "^1.32", "postcss": "^8" }, "optionalPeers": ["@csstools/postcss-cascade-layers", "postcss"] }, "Screenly-Labs-signage-kit-eb470fb", "sha512-acuAb2R/77crUjgnT9GFVlNdpgUTzvK5OLRRsZsZ30dZcJ7ThH/f8al2janEIJWC1JoyqTiarJ2x1uAH6In86A=="], + "@screenly-labs/signage-kit": ["@screenly-labs/signage-kit@github:Screenly-Labs/signage-kit#b7f3d44", { "dependencies": { "@fontsource-variable/bricolage-grotesque": "^5.2.10", "@fontsource-variable/fraunces": "^5.2.9", "@fontsource-variable/hanken-grotesk": "^5.2.8", "@fontsource-variable/jetbrains-mono": "^5.2.8", "@fontsource-variable/newsreader": "^5.2.10", "@fontsource/space-mono": "^5.2.9" }, "peerDependencies": { "@csstools/postcss-cascade-layers": "^6", "browserslist": "^4", "esbuild": "^0.28", "lightningcss": "^1.32", "postcss": "^8" }, "optionalPeers": ["@csstools/postcss-cascade-layers", "postcss"] }, "Screenly-Labs-signage-kit-b7f3d44", "sha512-42YdnSCPpYpTyMucl1T+FGv3HQeyzz2UDEvYoLdP4EMVAD1m0U1G8WN2hgTE4vFVs3w8hGcqFisWBjkEc26rFA=="], "@tailwindcss/cli": ["@tailwindcss/cli@4.3.2", "", { "dependencies": { "@parcel/watcher": "2.5.1", "@tailwindcss/node": "4.3.2", "@tailwindcss/oxide": "4.3.2", "enhanced-resolve": "5.21.6", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.3.2" }, "bin": { "tailwindcss": "./dist/index.mjs" } }, "sha512-Fzt+HrIZHDlkRYKdLMBeufaroaPvwCBG70sMLdmurdeadNMO/LxbmT8Sbb+P83ep0iAlAImettb7Y+rO+37rXw=="], diff --git a/package.json b/package.json index ff9c781..d9c6bca 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,7 @@ }, "license": "AGPL-3.0-only", "dependencies": { - "@fontsource-variable/bricolage-grotesque": "^5.2.5", - "@fontsource-variable/hanken-grotesk": "^5.2.8", - "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.7.0" + "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.7.2" }, "devDependencies": { "@biomejs/biome": "^2.5.1", diff --git a/sync-fonts.js b/sync-fonts.js index f4117c8..0362a9e 100644 --- a/sync-fonts.js +++ b/sync-fonts.js @@ -1,41 +1,13 @@ #!/usr/bin/env bun -/* global Bun */ -// Copies the self-hosted webfont files out of the Bun-managed @fontsource -// packages and into ./assets/static/fonts, where they're shipped to /static/fonts/. -// Bun owns the font versions (package.json); this step vendors the exact files we -// serve ourselves — no CDN at runtime. -// -// Fonts: Bricolage Grotesque (display face, "standard" axis = opsz + wght + wdth) -// for the big ticking digits; Hanken Grotesk for the title, unit labels, and the -// target line. +// Vendor this app's webfonts into ./assets/static/fonts. The files, versions, +// and copy logic all live in @screenly-labs/signage-kit — this just names the +// families the "Countdown" design uses (Bricolage Grotesque display + Hanken +// Grotesk for the labels and target line). -const FONTS = [ - '@fontsource-variable/bricolage-grotesque/files/bricolage-grotesque-latin-standard-normal.woff2', - '@fontsource-variable/hanken-grotesk/files/hanken-grotesk-latin-wght-normal.woff2' -] -const DEST_DIR = 'assets/static/fonts' +import { syncFonts } from '@screenly-labs/signage-kit/sync-fonts' -export const run = async () => { - let count = 0 +export const run = () => syncFonts(['bricolage-grotesque', 'hanken-grotesk']) - for (const rel of FONTS) { - const file = rel.split('/').pop() - const src = Bun.file(`node_modules/${rel}`) - - if (!(await src.exists())) { - console.error(`✗ Missing ${file} — run \`bun install\` first.`) - process.exit(1) - } - - await Bun.write(`${DEST_DIR}/${file}`, src) - console.log(`✓ Font: ${DEST_DIR}/${file}`) - count++ - } - - console.log(`Fonts synced — ${count} file(s) vendored from @fontsource.`) -} - -// Allow running standalone: `bun run sync-fonts.js` if (import.meta.main) { await run() }