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

Expand Down Expand Up @@ -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()
Expand Down
77 changes: 16 additions & 61 deletions assets/static/styles/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
14 changes: 10 additions & 4 deletions bun.lock

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

4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
40 changes: 6 additions & 34 deletions sync-fonts.js
Original file line number Diff line number Diff line change
@@ -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()
}
Loading