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
2 changes: 1 addition & 1 deletion assets/static/js/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
23 changes: 0 additions & 23 deletions assets/static/js/polyfills.ts

This file was deleted.

18 changes: 9 additions & 9 deletions assets/static/styles/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
62 changes: 18 additions & 44 deletions build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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(
[
Expand All @@ -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 <script>.
// 4. Client TS -> the kit's bundler (self-contained IIFE at the floor's syntax level).
try {
await esbuild({
entryPoints: ['assets/static/js/main.ts'],
bundle: true,
minify: true,
format: 'iife',
target: ['es2017'],
outfile: `${DIST}/static/js/main.js`
})
} catch (err) {
await bundleJs('assets/static/js/main.ts', `${DIST}/static/js/main.js`)
} catch (error) {
console.error('✗ JS build failed')
console.error(err)
console.error(error)
process.exit(1)
}
console.log(`✓ JS: ${DIST}/static/js/main.js (esbuild, iife, es2017)`)
console.log(`✓ JS: ${DIST}/static/js/main.js`)

// 5. Cache-busting: hash the built JS + CSS so the token changes exactly when
// shipped code changes, then stamp it into the page's asset URLs.
Expand Down
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 2 additions & 18 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,8 @@
type="font/woff2"
crossorigin
/>
<!-- Degraded mode for older/weaker signage players. Runs before the
stylesheet so html.legacy is set on the first paint: flags the device as
legacy when the browser engine is old (no Element.replaceChildren, a
2020-era API) or the
hardware looks weak, then the stylesheet drops all animation. -->
<script>
(function () {
try {
var slow =
(navigator.deviceMemory && navigator.deviceMemory <= 2) ||
(navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 2)
var old = !('replaceChildren' in Element.prototype)
if (slow || old) document.documentElement.classList.add('legacy')
} catch (e) {
document.documentElement.classList.add('legacy')
}
})()
</script>
<!-- The degraded-mode gate is injected here at build time by
@screenly-labs/signage-kit (injectGate), before the stylesheet. -->
<link rel="stylesheet" href="/static/styles/main.css?v=__ASSET_VERSION__" />
<script src="/static/js/main.js?v=__ASSET_VERSION__" defer></script>
</head>
Expand Down
9 changes: 2 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Comment thread
vpetersson marked this conversation as resolved.
"devDependencies": {
"@biomejs/biome": "^2.5.1",
Expand Down
Loading