Files
root 92b68ca21a Style kiosk dashboards with the CubeCraft Creations display system
The design system was only ever expressed in the production card's
stylesheet (custom_components/cubecraft_production/www/
cubecraft-production-card.js), which is gitignored, so the dashboards had
no way to share it. Mirror those tokens into an HA theme and apply the
card's own motifs to the wall panels.

themes/cubecraft.yaml carries the brand gradient, Nunito, the 4px scale,
radii, shadows and semantic colours. It uses `modes:` so it follows HA's
light/dark setting: light is the system as authored, dark uses
violet-tinted neutrals with the primary lifted to #8B8AF2 (5.6:1 on the
card surface) since #3B3AB8 is unreadable as an accent on dark. The
gradient itself is unchanged in both modes -- white clears AA across its
whole span.

At-a-Glance, Family Hub and Voice Alarms pick up three motifs from the
card: the .title gradient bar, .column header section labels, and the
4px brand left rule on each tile. Tile colour is cut back to brand
indigo, with green/amber/red reserved for real signals (printer offline,
bin full) -- the same discipline the card uses, where only a blocked
order goes red. CubeOps gets the theme too, so the board and the
dashboards land on the same ramp instead of the card improvising against
HA's defaults.

The card injects its @font-face only on its own page, so a small frontend
module declares Nunito document-wide. It points at the woff2 the
integration already serves rather than vendoring a second copy, which
needed a .gitignore carve-out since www/ is excluded wholesale.

dakboard-dark is left in place; switching a dashboard back is a one-line
change.

Not yet validated by `ha core check` -- this worktree is outside the
config tree HA actually loads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 11:27:35 -04:00

50 lines
2.0 KiB
JavaScript

// CubeCraft Creations brand font loader.
//
// The `cubecraft` theme sets Nunito as the font family, but a theme is only a
// bag of CSS variables — it cannot declare an @font-face. The production card
// declares one for itself (ensureBrandFont() in cubecraft-production-card.js),
// but only on the dashboard where that card is mounted, and only inside that
// page's document. Everything else falls back to system-ui.
//
// Loading this as a frontend module puts the declaration in the document head
// on every dashboard, so the whole UI gets Nunito.
//
// The .woff2 itself is NOT vendored here. It is already shipped and served by
// the cubecraft_production integration at STATIC_URL (see const.py: DOMAIN ->
// "/cubecraft_production"), which keeps a single copy of the file and one
// version to bump. If that integration is ever removed the URL 404s, the
// font-family falls through to the stack in the theme, and nothing else breaks.
//
// Wired up by `frontend.extra_module_url` in configuration.yaml.
const FONT_STYLE_ID = "cubecraft-brand-font";
const FONT_URL = "/cubecraft_production/nunito.woff2";
// extra_module_url modules are evaluated once per page load, but the card may
// have already injected its own copy under a different id — matching on the
// family as well keeps us from stacking duplicate @font-face rules.
function injectBrandFont() {
if (document.getElementById(FONT_STYLE_ID)) return;
if (document.getElementById("cubecraft-production-font")) return;
const style = document.createElement("style");
style.id = FONT_STYLE_ID;
// Variable weight range 400..800 — the card uses 400 (body), 600/700
// (labels, summaries) and 800 (titles, order numbers).
style.textContent =
"@font-face{" +
"font-family:'Nunito';" +
`src:url('${FONT_URL}') format('woff2');` +
"font-weight:400 800;" +
"font-style:normal;" +
"font-display:swap;" +
"}";
document.head.appendChild(style);
}
if (document.head) {
injectBrandFont();
} else {
document.addEventListener("DOMContentLoaded", injectBrandFont, { once: true });
}