4bb64a8ca3
- ticker / QR ad overlay / break slide graphics (1920×1080) with all geometry derived from one shared module (src/shared/obs-geometry.mjs) - OBS scene-collection generator (scripts/generate-obs-scenes.mjs) and a read-only in-dashboard setup verifier, both driven by the same module - en/sr dashboard i18n selected via bundle config; configschema.json with neutral first-run defaults seeded from optional branding config - install.sh / systemd user service / update.sh for venue deployment - reference OBS scene collection + profile from a real venue as fixtures
142 lines
5.0 KiB
CSS
142 lines
5.0 KiB
CSS
/* @import url('https://fonts.googleapis.com/css2?family=Margarine&family=Schoolbell&display=swap'); */
|
||
|
||
@import "tailwindcss";
|
||
|
||
@font-face {
|
||
font-family: "Margarine";
|
||
src: url("./fonts/Margarine-Regular.ttf") format("truetype");
|
||
font-weight: 100 900;
|
||
font-display: block;
|
||
}
|
||
|
||
/* ── FONT LAB ────────────────────────────────────────────────────────────
|
||
Two roles, applied everywhere via the `font-ticker` / `font-display`
|
||
utility classes. Swap candidates by editing the two variables below,
|
||
save (watch rebuild), refresh the graphic.
|
||
|
||
- ticker → the scrolling marquee text
|
||
- display → big statements: ad row/column text, break slide
|
||
|
||
Candidates to try (must be installed on the OBS machine, or bundled
|
||
below; the stack falls through to the next name if one is missing):
|
||
grotesque: "Inter", "Archivo", "Manrope", "Roboto"
|
||
condensed: "Oswald", "Archivo Narrow", "Roboto Condensed"
|
||
poster: "Anton", "Archivo Black", "Bebas Neue", Impact
|
||
|
||
To bundle a font file (works offline, recommended for the venue):
|
||
drop the .woff2/.ttf into src/graphics/fonts/ and uncomment:
|
||
|
||
@font-face {
|
||
font-family: "MojFont";
|
||
src: url("./fonts/MojFont.woff2") format("woff2");
|
||
font-weight: 100 900;
|
||
font-display: block;
|
||
}
|
||
|
||
Google Fonts also works if the OBS machine has internet — add e.g.
|
||
@import url("https://fonts.googleapis.com/css2?family=Anton&display=block");
|
||
directly under the tailwindcss import above (keep all @import lines at
|
||
the very top of this file). Latin-extended fonts cover č ć đ š ž.
|
||
──────────────────────────────────────────────────────────────────────── */
|
||
/* ── THEME LAB ───────────────────────────────────────────────────────────
|
||
Every brand color the graphics use, in one place. Edit, save (watch
|
||
rebuild), refresh the graphic — nothing else references raw colors.
|
||
|
||
- ticker-* the scrolling marquee: bottom→top gradient, top border,
|
||
and the accent used for the recurring blurb text
|
||
- ad-bg-* fallback gradient behind ad backgrounds while the image
|
||
loads (or when none is uploaded)
|
||
- break-bg-* break-slide backdrop gradient (a/b = edges/center)
|
||
- break-accent the "where they're from" line on the break slide
|
||
- break-blob-* the three drifting ambient blobs shown when no background
|
||
image is uploaded (keep them translucent)
|
||
──────────────────────────────────────────────────────────────────────── */
|
||
@theme {
|
||
--font-ticker: "Margarine", system-ui, sans-serif;
|
||
--font-display: "Margarine", system-ui, sans-serif;
|
||
|
||
--color-ticker-start: var(--color-cyan-800);
|
||
--color-ticker-end: var(--color-teal-500);
|
||
--color-ticker-border: var(--color-blue-400);
|
||
--color-ticker-accent: var(--color-amber-200);
|
||
|
||
--color-ad-bg-start: #0b1020;
|
||
--color-ad-bg-end: #1a2140;
|
||
|
||
--color-break-bg-a: #0a0e1a;
|
||
--color-break-bg-b: #171030;
|
||
--color-break-accent: color-mix(in srgb, var(--color-amber-300) 90%, transparent);
|
||
--color-break-blob-1: rgba(99, 102, 241, 0.35);
|
||
--color-break-blob-2: rgba(168, 85, 247, 0.3);
|
||
--color-break-blob-3: rgba(245, 158, 11, 0.18);
|
||
}
|
||
|
||
html,
|
||
body {
|
||
margin: 0;
|
||
background: transparent;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Break slide ambient motion */
|
||
@keyframes drift-a {
|
||
from {
|
||
transform: translate(-120px, -80px) scale(1);
|
||
}
|
||
to {
|
||
transform: translate(180px, 140px) scale(1.2);
|
||
}
|
||
}
|
||
|
||
@keyframes drift-b {
|
||
from {
|
||
transform: translate(120px, 60px) scale(1.1);
|
||
}
|
||
to {
|
||
transform: translate(-160px, -110px) scale(0.9);
|
||
}
|
||
}
|
||
|
||
@keyframes drift-c {
|
||
from {
|
||
transform: translate(0, 120px) scale(1);
|
||
}
|
||
to {
|
||
transform: translate(100px, -140px) scale(1.3);
|
||
}
|
||
}
|
||
|
||
/* Ad background: slow pan; the 1.15 scale gives the ±3% translate room to
|
||
move without ever exposing an edge (7.5% margin > 3% × 1.15 travel) */
|
||
@keyframes ad-pan {
|
||
from {
|
||
transform: scale(1.15) translate(-3%, -2%);
|
||
}
|
||
to {
|
||
transform: scale(1.15) translate(3%, 2%);
|
||
}
|
||
}
|
||
|
||
/* Break background: full edge-to-edge sweep that visibly reverses. At 1.15
|
||
scale the hidden margin is 7.5% of the screen per side, i.e. ~6.5% of the
|
||
element's own size — ±6%/±4% stays just inside that, so each bounce lands
|
||
right at the image edge without exposing it */
|
||
@keyframes break-bounce {
|
||
from {
|
||
transform: scale(1.15) translate(-6%, -4%);
|
||
}
|
||
to {
|
||
transform: scale(1.15) translate(6%, 4%);
|
||
}
|
||
}
|
||
|
||
@keyframes breathe {
|
||
0%,
|
||
100% {
|
||
transform: scale(1);
|
||
}
|
||
50% {
|
||
transform: scale(1.045);
|
||
}
|
||
}
|