diff --git a/assets/static/js/main.ts b/assets/static/js/main.ts index 55d11b4..8a81e4e 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 { isQuote, pickRandomIndex, type Quote } from './quotes' // Shown if the quotes file can't be fetched or is empty, so the screen is never @@ -40,15 +41,6 @@ const loadQuote = async (): Promise => { } } -// 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() loadQuote().then(render) diff --git a/assets/static/styles/tailwind.css b/assets/static/styles/tailwind.css index 87da305..1fd97ee 100644 --- a/assets/static/styles/tailwind.css +++ b/assets/static/styles/tailwind.css @@ -8,52 +8,34 @@ ========================================================================= */ @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-gilt, font-display…). */ +/* Design tokens — also generate utilities (bg-ink, text-gilt…). The font + families (--font-display: Fraunces, --font-sans: Hanken Grotesk) come from the + kit preset; override here only to diverge. */ @theme { --color-ink: #16140f; --color-paper: #f1ece0; --color-paper-dim: #b7b0a0; --color-watermark: #2a2519; --color-gilt: #c9a24b; - --font-display: 'Fraunces', ui-serif, Georgia, 'Times New Roman', 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. */ -@font-face { - font-family: 'Fraunces'; - font-style: normal; - font-weight: 100 900; - font-display: swap; - src: url('/static/fonts/fraunces-latin-standard-normal.woff2') format('woff2'); -} -@font-face { - font-family: 'Fraunces'; - font-style: italic; - font-weight: 100 900; - font-display: swap; - src: url('/static/fonts/fraunces-latin-standard-italic.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(17px, 1.05vw + 1.05vh, 56px); + --root-min: 17px; + --root-gain: 1.05; + --root-max: 56px; } body { @@ -68,16 +50,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; - } - .quote { position: relative; margin: 0; @@ -146,28 +118,6 @@ text-transform: uppercase; color: var(--color-gilt); } - - .brand { - position: fixed; - right: clamp(1rem, 3.2vmin, 2.4rem); - bottom: clamp(1rem, 3.2vmin, 2.4rem); - 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 8bc00ca..87ad84f 100644 --- a/bun.lock +++ b/bun.lock @@ -5,9 +5,7 @@ "": { "name": "screenly-quotes-app", "dependencies": { - "@fontsource-variable/fraunces": "^5.2.9", - "@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", @@ -98,10 +96,18 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + "@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.1", "", { "dependencies": { "@parcel/watcher": "2.5.1", "@tailwindcss/node": "4.3.1", "@tailwindcss/oxide": "4.3.1", "enhanced-resolve": "5.21.6", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.3.1" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-ZWPy20rF+TBfTImxDMG3Wr75Y3RpaPlo9lc+oJbInlMyjT+XPkTVKVIL5RZ7JirXuIahcfHoLNFRmDorKi+JQQ=="], diff --git a/package.json b/package.json index 26081a8..9e2428c 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,7 @@ }, "license": "AGPL-3.0-only", "dependencies": { - "@fontsource-variable/fraunces": "^5.2.9", - "@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 786cbdb..60f18b2 100644 --- a/sync-fonts.js +++ b/sync-fonts.js @@ -1,41 +1,12 @@ #!/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: Fraunces (display serif, "standard" axis = opsz + wght, normal + italic) -// for the quote; Hanken Grotesk for the attribution byline. +// 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 "Reading room" design uses (Fraunces display + Hanken Grotesk). -const FONTS = [ - '@fontsource-variable/fraunces/files/fraunces-latin-standard-normal.woff2', - '@fontsource-variable/fraunces/files/fraunces-latin-standard-italic.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(['fraunces', '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() }