diff --git a/assets/static/js/main.ts b/assets/static/js/main.ts index 4765504..18968d2 100644 --- a/assets/static/js/main.ts +++ b/assets/static/js/main.ts @@ -4,7 +4,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 './polyfills' +import '@screenly-labs/signage-kit/polyfills' import { computeState, pad2, parseTarget } from './timer' diff --git a/assets/static/js/polyfills.ts b/assets/static/js/polyfills.ts deleted file mode 100644 index 4262c22..0000000 --- a/assets/static/js/polyfills.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Minimal runtime shim for the older-browser degraded mode. esbuild lowers modern -// *syntax* (?., ??, spread) at build time, but it does not add missing runtime -// APIs. The one DOM gap the apps can hit at the support floor is -// Element.prototype.replaceChildren (Chrome 86 / Safari 14, 2020); it's included -// as a defensive shim even in apps that don't call it directly. Everything else -// the apps use (fetch, Intl, URLSearchParams) predates the floor, so no core-js. -// -// Imported only for its side effect (installing the shim) as the first line of -// main.ts; it exports nothing. - -if (typeof Element !== 'undefined' && !Element.prototype.replaceChildren) { - Element.prototype.replaceChildren = function replaceChildren( - this: Element, - ...nodes: (Node | string)[] - ): void { - // Build the new children in a fragment first so a bad node throws before we - // touch the element, keeping the swap effectively atomic like the native API. - const frag = document.createDocumentFragment() - frag.append(...nodes) - while (this.firstChild) this.removeChild(this.firstChild) - this.appendChild(frag) - } -} diff --git a/assets/static/styles/tailwind.css b/assets/static/styles/tailwind.css index 64b6906..8f93957 100644 --- a/assets/static/styles/tailwind.css +++ b/assets/static/styles/tailwind.css @@ -9,6 +9,10 @@ ========================================================================= */ @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. */ +@import '@screenly-labs/signage-kit/styles/preset.css'; /* Design tokens — also generate utilities (bg-ink, text-mint, font-display…). */ @theme { @@ -228,13 +232,9 @@ } /* ========================================================================= - Degraded mode — old/weak signage players (html.legacy set by the inline gate - in index.html). Assume the device can't afford animation; drop it entirely. + Degraded mode — old/weak signage players (html.legacy set by the shared gate). + The generic kill-switch (drops animation/transition/will-change) now comes from + the kit's preset, imported above. The clock ticks in JS and the only CSS motion + is the one-off .timer entrance, which the kill-switch already disables — there's + no animated resting state to hold, so this app needs no extra html.legacy rule. ========================================================================= */ -html.legacy *, -html.legacy *::before, -html.legacy *::after { - animation: none !important; - transition: none !important; - will-change: auto !important; -} diff --git a/build.js b/build.js index 7be0882..e3c90c0 100644 --- a/build.js +++ b/build.js @@ -14,22 +14,16 @@ // dist/ is gitignored; CI uploads it as the Pages artifact. import { rm, mkdir, cp, readFile, writeFile } from 'node:fs/promises' -import cascadeLayers from '@csstools/postcss-cascade-layers' -import browserslist from 'browserslist' -import { build as esbuild } from 'esbuild' -import { browserslistToTargets, transform as lightningcss } from 'lightningcss' -import postcss from 'postcss' +import { bundleJs, injectGate, processCss } from '@screenly-labs/signage-kit/build' import { run as syncFonts } from './sync-fonts.js' const DIST = 'dist' const DOMAIN = 'timer.srly.io' -// The `browserslist` field in package.json is the CSS support floor: Lightning -// CSS down-levels the stylesheet to it. The JS is lowered separately by esbuild to -// a fixed ES2017 syntax floor (kept at/below the browserslist minimum); esbuild -// can't read browserslist, so keep the two in sync if you change the floor. See -// the degraded-mode notes in index.html / tailwind.css. -const cssTargets = browserslistToTargets(browserslist()) +// The degraded-mode support floor, the CSS down-leveling recipe (cascade-layers +// flatten + Lightning CSS), the JS bundler, and the inline degraded-mode gate all +// come from @screenly-labs/signage-kit. This file only orchestrates the +// app-specific steps. // 1. Vendor the Bun-managed webfonts into ./assets before copying. await syncFonts() @@ -44,16 +38,15 @@ await mkdir(`${DIST}/static/styles`, { recursive: true }) await mkdir(`${DIST}/static/js`, { recursive: true }) await cp('assets/static/fonts', `${DIST}/static/fonts`, { recursive: true }) await cp('assets/static/images', `${DIST}/static/images`, { recursive: true }) -await cp('index.html', `${DIST}/index.html`) +// Copy the page shell with the shared degraded-mode gate injected before the +// stylesheet so it runs before first paint. +await writeFile(`${DIST}/index.html`, injectGate(await readFile('index.html', 'utf8'))) // Signage app manifest served at /.well-known/signage-app.json (see the // app-store's docs/app-manifest.md). GitHub Pages returns it as application/json // with Access-Control-Allow-Origin: * so the store and players can fetch it. await cp('.well-known', `${DIST}/.well-known`, { recursive: true }) -// 3. Tailwind: compile the source CSS (unminified), then down-level + minify it -// for the browserslist floor. cascade-layers flattens @layer into :not(#\#) -// specificity so the cascade survives on engines that drop @layer contents; -// Lightning CSS then lowers color-mix()/nesting, adds prefixes, and minifies. +// 3. Tailwind -> the kit's CSS pipeline (flatten @layer, down-level to the floor). const cssOut = `${DIST}/static/styles/main.css` const tailwind = Bun.spawn( [ @@ -70,42 +63,23 @@ if ((await tailwind.exited) !== 0) { process.exit(1) } try { - const flattened = await postcss([cascadeLayers()]).process(await readFile(cssOut, 'utf8'), { - from: cssOut - }) - const { code: cssCode } = lightningcss({ - filename: cssOut, - code: Buffer.from(flattened.css), - minify: true, - targets: cssTargets - }) - await writeFile(cssOut, cssCode) -} catch (err) { + await writeFile(cssOut, await processCss(await readFile(cssOut, 'utf8'), { flattenLayers: true, filename: cssOut })) +} catch (error) { console.error(`✗ CSS build failed (${cssOut})`) - console.error(err) + console.error(error) process.exit(1) } -console.log(`✓ CSS: ${cssOut} (Tailwind → cascade-layers flatten → Lightning CSS)`) +console.log(`✓ CSS: ${cssOut}`) -// 4. TypeScript → browser JS with esbuild. Bundles main.ts (inlining ./timer and -// the polyfills shim), lowers modern syntax (?., ??, spread) to the ES2017 floor -// so old engines can parse it, and emits an IIFE so the output stays a -// self-contained self-executing classic script loadable from a plain + diff --git a/package.json b/package.json index 4e8966c..ff9c781 100644 --- a/package.json +++ b/package.json @@ -13,15 +13,10 @@ "dev": "bun run build.js && bunx serve dist" }, "license": "AGPL-3.0-only", - "browserslist": [ - "chrome >= 87", - "firefox >= 78", - "safari >= 14.1", - "edge >= 87" - ], "dependencies": { "@fontsource-variable/bricolage-grotesque": "^5.2.5", - "@fontsource-variable/hanken-grotesk": "^5.2.8" + "@fontsource-variable/hanken-grotesk": "^5.2.8", + "@screenly-labs/signage-kit": "github:Screenly-Labs/signage-kit#2026.7.0" }, "devDependencies": { "@biomejs/biome": "^2.5.1",