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>
This commit is contained in:
+10
-1
@@ -112,7 +112,16 @@ home-assistant_v2.db-wal
|
||||
# Dependencies and cache
|
||||
deps/
|
||||
tts/
|
||||
www/
|
||||
# www/ is HA's public static dir: HACS frontend plugins, camera snapshots and
|
||||
# TTS junk all land in it, so it stays ignored — except for the small
|
||||
# hand-written frontend modules we actually author and want in git.
|
||||
# (git will not descend into an excluded directory, so each level has to be
|
||||
# re-included before the next one can be narrowed again.)
|
||||
!www/
|
||||
www/*
|
||||
!www/cubecraft/
|
||||
www/cubecraft/*
|
||||
!www/cubecraft/*.js
|
||||
custom_components/
|
||||
image/
|
||||
ms365_storage/
|
||||
|
||||
@@ -7,6 +7,12 @@ default_config:
|
||||
# Load frontend themes from the themes folder
|
||||
frontend:
|
||||
themes: !include_dir_merge_named themes
|
||||
# Declares the Nunito @font-face document-wide for the CubeCraft display
|
||||
# system. A theme can only set font-family, not load the face — see
|
||||
# www/cubecraft/cubecraft-brand-font.js. Bump ?v= after editing that file to
|
||||
# get past the browser cache.
|
||||
extra_module_url:
|
||||
- /local/cubecraft/cubecraft-brand-font.js?v=1
|
||||
|
||||
lovelace:
|
||||
dashboards: !include dashboards/dashboards.yaml
|
||||
|
||||
@@ -1,13 +1,33 @@
|
||||
# At-a-Glance — wall panel
|
||||
#
|
||||
# Styled with the CubeCraft Creations display system. The tokens live in
|
||||
# themes/cubecraft.yaml, which mirrors the `:host` block of the production
|
||||
# card's stylesheet (custom_components/cubecraft_production/www/
|
||||
# cubecraft-production-card.js). Anything referenced here as --cc-* comes from
|
||||
# that theme, so this file never hard-codes a brand hex.
|
||||
#
|
||||
# Three motifs carry the style, all lifted from the production card:
|
||||
# .title → gradient title bar, white 800-weight type, brand radius
|
||||
# .column header → uppercase 700/.05em muted section labels
|
||||
# article → 8px radius, 4px brand left rule, shadow-sm on each tile
|
||||
#
|
||||
# Colour is deliberately restrained: brand indigo for anything informational,
|
||||
# and green/amber/red kept in reserve so they still mean something when they
|
||||
# show up. That is the same discipline the production card uses, where only a
|
||||
# blocked order goes red.
|
||||
title: At-a-Glance
|
||||
theme: dakboard-dark
|
||||
theme: cubecraft
|
||||
views:
|
||||
- title: At-a-Glance
|
||||
path: at-a-glance
|
||||
icon: mdi:eye-outline
|
||||
theme: dakboard-dark
|
||||
theme: cubecraft
|
||||
type: masonry
|
||||
cards:
|
||||
# ── Header ──
|
||||
# ── Brand title bar ──
|
||||
# Mirrors `.shell.kiosk .title`: gradient ground, white type at 1.75rem,
|
||||
# brand radius and the brand shadow. ha-markdown supplies its own 16px
|
||||
# inset, so ha-card only tops that up to roughly the card's 20px/28px.
|
||||
- type: markdown
|
||||
content: |
|
||||
# {{ now().strftime("%A, %B %d, %Y") }}
|
||||
@@ -15,21 +35,28 @@ views:
|
||||
card_mod:
|
||||
style: |
|
||||
ha-card {
|
||||
background: var(--cc-gradient);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: var(--cc-radius-brand);
|
||||
box-shadow: var(--cc-shadow-brand);
|
||||
padding: 6px 12px;
|
||||
text-align: center;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
padding: 0 0 4px 0;
|
||||
}
|
||||
ha-card h1 {
|
||||
font-size: 1.2em;
|
||||
margin: 0;
|
||||
font-weight: 300;
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
opacity: 0.85;
|
||||
color: #fff;
|
||||
}
|
||||
ha-card h2 {
|
||||
font-size: 2em;
|
||||
margin: 2px 0 0 0;
|
||||
font-weight: 400;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.01em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
# ── Weather Now ──
|
||||
@@ -41,127 +68,200 @@ views:
|
||||
round_temperature: true
|
||||
|
||||
# ── Indoor Climate ──
|
||||
# First use of the section-label treatment; the rest of the file aliases
|
||||
# it as *cc_section. Selectors are listed broadly on purpose — a heading
|
||||
# card's internals are not a stable API, and any selector that misses
|
||||
# simply leaves that bit at the theme default rather than breaking.
|
||||
- type: heading
|
||||
heading: Indoor Climate
|
||||
heading_style: title
|
||||
icon: mdi:home-thermometer-outline
|
||||
card_mod: &cc_section
|
||||
style: |
|
||||
ha-card {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
margin: 4px 0 -4px 0;
|
||||
}
|
||||
ha-card .heading,
|
||||
ha-card .content p,
|
||||
ha-card p {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-weight: 700;
|
||||
font-size: 0.9375rem;
|
||||
color: var(--cc-text-muted, var(--secondary-text-color));
|
||||
}
|
||||
ha-card ha-icon,
|
||||
ha-card ha-svg-icon {
|
||||
color: var(--cc-accent, var(--primary-color));
|
||||
}
|
||||
|
||||
- type: grid
|
||||
columns: 3
|
||||
square: false
|
||||
cards:
|
||||
# First use of the `article` treatment; aliased as *cc_tile below.
|
||||
- type: tile
|
||||
entity: sensor.living_room_temperature
|
||||
name: Living Room
|
||||
icon: mdi:sofa
|
||||
color: amber
|
||||
color: primary
|
||||
card_mod: &cc_tile
|
||||
style: |
|
||||
ha-card {
|
||||
border-radius: var(--cc-radius-md);
|
||||
border-left: 4px solid var(--cc-accent);
|
||||
box-shadow: var(--cc-shadow-sm);
|
||||
}
|
||||
- type: tile
|
||||
entity: sensor.office_temperature
|
||||
name: Office
|
||||
icon: mdi:desk
|
||||
color: teal
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
- type: tile
|
||||
entity: sensor.bedroom_temperature
|
||||
name: Master Bedroom
|
||||
icon: mdi:bed
|
||||
color: purple
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
- type: tile
|
||||
entity: sensor.upstairs_temperature
|
||||
name: Upstairs
|
||||
icon: mdi:stairs
|
||||
color: indigo
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
- type: tile
|
||||
entity: sensor.blink_family_room_temperature
|
||||
name: Family Room
|
||||
icon: mdi:television
|
||||
color: red
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
- type: tile
|
||||
entity: sensor.blink_kitchen_temperature
|
||||
name: Kitchen
|
||||
icon: mdi:stove
|
||||
color: deep-orange
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
|
||||
# ── 3D Printer Status ──
|
||||
- type: heading
|
||||
heading: 3D Printers
|
||||
heading_style: title
|
||||
icon: mdi:printer-3d-nozzle
|
||||
card_mod: *cc_section
|
||||
|
||||
- type: grid
|
||||
columns: 2
|
||||
square: false
|
||||
cards:
|
||||
# Online/offline is a genuine success signal, so these keep green and
|
||||
# take the success-coloured left rule.
|
||||
- type: tile
|
||||
entity: binary_sensor.p1s_01p09c470102673_online
|
||||
name: PrintsCharming
|
||||
icon: mdi:printer-3d-nozzle
|
||||
show_entity_picture: false
|
||||
color: green
|
||||
card_mod: &cc_tile_success
|
||||
style: |
|
||||
ha-card {
|
||||
border-radius: var(--cc-radius-md);
|
||||
border-left: 4px solid var(--success-color);
|
||||
box-shadow: var(--cc-shadow-sm);
|
||||
}
|
||||
- type: tile
|
||||
entity: binary_sensor.a1_03919d540806387_online
|
||||
name: Ds-Turd-Flinger
|
||||
icon: mdi:printer-3d-nozzle
|
||||
show_entity_picture: false
|
||||
color: green
|
||||
card_mod: *cc_tile_success
|
||||
- type: tile
|
||||
entity: binary_sensor.a1mini_0309ca580403653_online
|
||||
name: Polly-Pocket
|
||||
icon: mdi:printer-3d-nozzle
|
||||
show_entity_picture: false
|
||||
color: green
|
||||
card_mod: *cc_tile_success
|
||||
- type: tile
|
||||
entity: binary_sensor.constipation_orion_online
|
||||
name: Constipation-Orion
|
||||
icon: mdi:printer-3d-nozzle
|
||||
show_entity_picture: false
|
||||
color: green
|
||||
card_mod: *cc_tile_success
|
||||
- type: tile
|
||||
entity: binary_sensor.jordyns_layer_slayer_online
|
||||
name: Jordyn's Layer Slayer
|
||||
icon: mdi:printer-3d-nozzle
|
||||
show_entity_picture: false
|
||||
color: green
|
||||
card_mod: *cc_tile_success
|
||||
- type: tile
|
||||
entity: binary_sensor.centauri_carbon_sdcp_status
|
||||
name: Centauri Carbon
|
||||
icon: mdi:printer-3d-nozzle
|
||||
show_entity_picture: false
|
||||
color: green
|
||||
card_mod: *cc_tile_success
|
||||
|
||||
# ── Litter Boxes ──
|
||||
- type: heading
|
||||
heading: Litter Boxes
|
||||
heading_style: title
|
||||
icon: mdi:cat
|
||||
card_mod: *cc_section
|
||||
|
||||
- type: grid
|
||||
columns: 2
|
||||
square: false
|
||||
cards:
|
||||
# These are the "something needs doing" tiles — the equivalent of a
|
||||
# blocked order in the production card, so they take the error rule.
|
||||
- type: tile
|
||||
entity: binary_sensor.cat_crapper_3000_wastebin_filled
|
||||
name: Cat Crapper 3000
|
||||
icon: mdi:delete-empty
|
||||
show_entity_picture: false
|
||||
color: red
|
||||
card_mod: &cc_tile_alert
|
||||
style: |
|
||||
ha-card {
|
||||
border-radius: var(--cc-radius-md);
|
||||
border-left: 4px solid var(--error-color);
|
||||
box-shadow: var(--cc-shadow-sm);
|
||||
}
|
||||
- type: tile
|
||||
entity: binary_sensor.cat_crapper_3000_sand_lack
|
||||
name: CC3000 Litter
|
||||
icon: mdi:grain
|
||||
show_entity_picture: false
|
||||
color: amber
|
||||
card_mod:
|
||||
style: |
|
||||
ha-card {
|
||||
border-radius: var(--cc-radius-md);
|
||||
border-left: 4px solid var(--warning-color);
|
||||
box-shadow: var(--cc-shadow-sm);
|
||||
}
|
||||
- type: tile
|
||||
entity: binary_sensor.poop_box_wastebin_filled
|
||||
name: Poop Box
|
||||
icon: mdi:delete-empty
|
||||
show_entity_picture: false
|
||||
color: red
|
||||
card_mod: *cc_tile_alert
|
||||
- type: tile
|
||||
entity: binary_sensor.triple_threat_wastebin_filled
|
||||
name: Triple Threat
|
||||
icon: mdi:delete-empty
|
||||
show_entity_picture: false
|
||||
color: red
|
||||
card_mod: *cc_tile_alert
|
||||
|
||||
# ── Security ──
|
||||
- type: heading
|
||||
@@ -181,6 +281,7 @@ views:
|
||||
- type: entity
|
||||
entity: alarm_control_panel.blink_rear
|
||||
name: Rear
|
||||
card_mod: *cc_section
|
||||
|
||||
# ── Recent Activity ──
|
||||
- type: logbook
|
||||
|
||||
@@ -13,12 +13,21 @@
|
||||
# and registers its own Lovelace resource, so there is nothing to add under
|
||||
# Settings > Dashboards > Resources.
|
||||
|
||||
# kiosk-mode plugin (HACS > Frontend). Without it the board still fills the
|
||||
# screen; you just keep HA's header and sidebar.
|
||||
# kiosk-mode plugin (HACS > Frontend). NOT currently installed — there is no
|
||||
# lovelace-kiosk-mode under www/community — so this block is inert today.
|
||||
# `theme: cubecraft` below already collapses the header via header-height, so
|
||||
# installing the plugin now only buys you hide_sidebar.
|
||||
kiosk_mode:
|
||||
hide_header: true
|
||||
hide_sidebar: true
|
||||
|
||||
# The CubeCraft display system. The card reads HA's theme variables for its
|
||||
# surfaces, text and borders (see the `--cc-surface` fallbacks in
|
||||
# cubecraft-production-card.js), so setting this makes the board and the rest
|
||||
# of the dashboards land on exactly the same ramp instead of the card
|
||||
# improvising against HA's defaults.
|
||||
theme: cubecraft
|
||||
|
||||
views:
|
||||
- title: Production
|
||||
# Panel mode gives the card the whole viewport with no dashboard padding.
|
||||
|
||||
@@ -1,41 +1,58 @@
|
||||
# Family Hub — wall panel
|
||||
#
|
||||
# Styled with the CubeCraft Creations display system; see
|
||||
# dashboards/views/at-a-glance-dashboard.yaml for the motif notes and
|
||||
# themes/cubecraft.yaml for the tokens. Everything brand-coloured here comes
|
||||
# through --cc-* variables rather than a literal hex.
|
||||
title: Family Hub
|
||||
theme: dakboard-dark
|
||||
theme: cubecraft
|
||||
views:
|
||||
- title: Family Hub
|
||||
path: family-hub
|
||||
icon: mdi:home-heart
|
||||
theme: dakboard-dark
|
||||
theme: cubecraft
|
||||
type: masonry
|
||||
cards:
|
||||
# ── Date & Greeting Header ──
|
||||
# ── Brand greeting bar ──
|
||||
# The production card's `.title` treatment, carrying the date and a
|
||||
# time-of-day greeting instead of a board name.
|
||||
- type: markdown
|
||||
content: |
|
||||
# {{ now().strftime("%A, %B %d") }}
|
||||
{% set h = now().hour %}
|
||||
{% if h < 12 %}Good morning{% elif h < 17 %}Good afternoon{% else %}Good evening{% endif %}, family!
|
||||
## {% if h < 12 %}Good morning{% elif h < 17 %}Good afternoon{% else %}Good evening{% endif %}, family!
|
||||
entity_id:
|
||||
- sensor.time
|
||||
card_mod:
|
||||
style: |
|
||||
ha-card {
|
||||
background: var(--cc-gradient);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: var(--cc-radius-brand);
|
||||
box-shadow: var(--cc-shadow-brand);
|
||||
padding: 6px 12px;
|
||||
text-align: center;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
padding: 0 0 8px 0;
|
||||
}
|
||||
ha-card h1 {
|
||||
font-size: 1.4em;
|
||||
margin: 0 0 4px 0;
|
||||
font-weight: 300;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-card p {
|
||||
font-size: 1.1em;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
opacity: 0.85;
|
||||
color: #fff;
|
||||
}
|
||||
ha-card h2 {
|
||||
margin: 2px 0 0 0;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.01em;
|
||||
color: #fff;
|
||||
}
|
||||
entity_id:
|
||||
- sensor.time
|
||||
|
||||
# ── Clock ──
|
||||
# Left on the sunken surface rather than a card, so the gradient bar above
|
||||
# stays the only heavyweight element in the column.
|
||||
- type: clock
|
||||
clock_size: large
|
||||
clock_style: analog
|
||||
@@ -48,6 +65,7 @@ views:
|
||||
style: |
|
||||
ha-card {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -73,8 +91,11 @@ views:
|
||||
card_mod:
|
||||
style: |
|
||||
ha-card {
|
||||
--calendar-day-color: var(--primary-text-color);
|
||||
--calendar-day-weekday-color: var(--secondary-text-color);
|
||||
--calendar-day-color: var(--cc-text, var(--primary-text-color));
|
||||
--calendar-day-weekday-color: var(--cc-text-muted, var(--secondary-text-color));
|
||||
--fc-today-bg-color: var(--cc-accent-soft);
|
||||
--fc-event-bg-color: var(--cc-accent);
|
||||
--fc-event-border-color: var(--cc-accent);
|
||||
}
|
||||
|
||||
# ── Quick Glance ──
|
||||
@@ -92,64 +113,104 @@ views:
|
||||
- type: entity
|
||||
entity: sensor.office_temperature
|
||||
name: Office
|
||||
card_mod: &cc_section
|
||||
style: |
|
||||
ha-card {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
margin: 4px 0 -4px 0;
|
||||
}
|
||||
ha-card .heading,
|
||||
ha-card .content p,
|
||||
ha-card p {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-weight: 700;
|
||||
font-size: 0.9375rem;
|
||||
color: var(--cc-text-muted, var(--secondary-text-color));
|
||||
}
|
||||
ha-card ha-icon,
|
||||
ha-card ha-svg-icon {
|
||||
color: var(--cc-accent, var(--primary-color));
|
||||
}
|
||||
|
||||
- type: grid
|
||||
columns: 3
|
||||
square: false
|
||||
cards:
|
||||
# Outdoor temp
|
||||
# Temperatures are informational, so they all take the brand rule
|
||||
# rather than a colour each — see the colour note in at-a-glance.
|
||||
- type: tile
|
||||
entity: sensor.openweathermap_temperature
|
||||
name: Outside
|
||||
icon: mdi:thermometer
|
||||
show_entity_picture: false
|
||||
color: blue
|
||||
# Living Room
|
||||
color: primary
|
||||
card_mod: &cc_tile
|
||||
style: |
|
||||
ha-card {
|
||||
border-radius: var(--cc-radius-md);
|
||||
border-left: 4px solid var(--cc-accent);
|
||||
box-shadow: var(--cc-shadow-sm);
|
||||
}
|
||||
- type: tile
|
||||
entity: sensor.living_room_temperature
|
||||
name: Living Room
|
||||
icon: mdi:sofa
|
||||
show_entity_picture: false
|
||||
color: amber
|
||||
# Office
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
- type: tile
|
||||
entity: sensor.office_temperature
|
||||
name: Office
|
||||
icon: mdi:desk
|
||||
show_entity_picture: false
|
||||
color: teal
|
||||
# Master Bedroom
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
- type: tile
|
||||
entity: sensor.bedroom_temperature
|
||||
name: Master Bedroom
|
||||
icon: mdi:bed
|
||||
show_entity_picture: false
|
||||
color: purple
|
||||
# Upstairs
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
- type: tile
|
||||
entity: sensor.upstairs_temperature
|
||||
name: Upstairs
|
||||
icon: mdi:stairs
|
||||
show_entity_picture: false
|
||||
color: indigo
|
||||
# Family Room (Blink)
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
- type: tile
|
||||
entity: sensor.blink_family_room_temperature
|
||||
name: Family Room
|
||||
icon: mdi:television
|
||||
show_entity_picture: false
|
||||
color: red
|
||||
color: primary
|
||||
card_mod: *cc_tile
|
||||
|
||||
# ── Shopping List ──
|
||||
- type: heading
|
||||
heading: Shopping List
|
||||
heading_style: title
|
||||
icon: mdi:cart-outline
|
||||
card_mod: *cc_section
|
||||
|
||||
- type: todo-list
|
||||
entity: todo.shopping_list
|
||||
title: Shopping List
|
||||
hide_completed: true
|
||||
display_order: none
|
||||
|
||||
# ── Chore Tracker Quick View ──
|
||||
- type: heading
|
||||
heading: Chores Today
|
||||
heading_style: title
|
||||
icon: mdi:check-circle-outline
|
||||
card_mod: *cc_section
|
||||
|
||||
- type: entities
|
||||
title: Chores Today
|
||||
show_header_toggle: false
|
||||
entities:
|
||||
- entity: binary_sensor.cat_medication_tracker_all_cats_medicated
|
||||
@@ -171,3 +232,4 @@ views:
|
||||
- type: entity
|
||||
entity: binary_sensor.upstairs_occupancy
|
||||
name: Upstairs
|
||||
card_mod: *cc_section
|
||||
|
||||
@@ -1,23 +1,111 @@
|
||||
# Voice Alarms — satellite control panel
|
||||
#
|
||||
# Styled with the CubeCraft Creations display system; see
|
||||
# dashboards/views/at-a-glance-dashboard.yaml for the motif notes and
|
||||
# themes/cubecraft.yaml for the tokens.
|
||||
#
|
||||
# This one is a control panel rather than a glanceable board, so the per-tile
|
||||
# left rule is dropped — the entity rows already carry their own structure and
|
||||
# a rule on every card would just add noise. It keeps the gradient title bar
|
||||
# and the section labels.
|
||||
#
|
||||
# Note that the theme collapses the HA toolbar (header-height: 0). This
|
||||
# dashboard is reached by URL rather than the sidebar (show_in_sidebar: false
|
||||
# in dashboards/voice-alarms.yaml), so navigate away via the sidebar.
|
||||
title: Voice Alarms
|
||||
theme: cubecraft
|
||||
views:
|
||||
- title: Voice Alarms
|
||||
path: voice-alarms
|
||||
icon: mdi:alarm-panel
|
||||
theme: cubecraft
|
||||
type: panel
|
||||
cards:
|
||||
- type: vertical-stack
|
||||
cards:
|
||||
# ── Brand title bar ──
|
||||
- type: markdown
|
||||
content: |
|
||||
# Voice Alarms
|
||||
## Jarvis & ReSpeaker satellites
|
||||
card_mod:
|
||||
style: |
|
||||
ha-card {
|
||||
background: var(--cc-gradient);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: var(--cc-radius-brand);
|
||||
box-shadow: var(--cc-shadow-brand);
|
||||
padding: 6px 12px;
|
||||
}
|
||||
ha-card h1 {
|
||||
margin: 0;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.01em;
|
||||
color: #fff;
|
||||
}
|
||||
ha-card h2 {
|
||||
margin: 2px 0 0 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
opacity: 0.85;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
These alarms are managed by Home Assistant and announced on the selected
|
||||
Jarvis and ReSpeaker satellites.
|
||||
- type: markdown
|
||||
content: >
|
||||
These alarms are managed by Home Assistant and announced on the
|
||||
selected Jarvis and ReSpeaker satellites.
|
||||
card_mod:
|
||||
style: |
|
||||
ha-card {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
ha-card p {
|
||||
margin: 0;
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.5;
|
||||
color: var(--cc-text-muted, var(--secondary-text-color));
|
||||
}
|
||||
|
||||
- type: heading
|
||||
heading: Satellites
|
||||
heading_style: title
|
||||
icon: mdi:speaker-message
|
||||
card_mod:
|
||||
style: |
|
||||
ha-card {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
margin: 4px 0 -4px 0;
|
||||
}
|
||||
ha-card .heading,
|
||||
ha-card .content p,
|
||||
ha-card p {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-weight: 700;
|
||||
font-size: 0.9375rem;
|
||||
color: var(--cc-text-muted, var(--secondary-text-color));
|
||||
}
|
||||
ha-card ha-icon,
|
||||
ha-card ha-svg-icon {
|
||||
color: var(--cc-accent, var(--primary-color));
|
||||
}
|
||||
|
||||
- type: grid
|
||||
columns: 2
|
||||
square: false
|
||||
cards:
|
||||
# Card titles take the production card's column-header treatment:
|
||||
# uppercase, 700, .05em tracking, muted. First use defines the
|
||||
# anchor the other three alias.
|
||||
- type: entities
|
||||
title: Office Jarvis
|
||||
show_header_toggle: false
|
||||
@@ -30,6 +118,21 @@ views:
|
||||
name: Alarm time
|
||||
- entity: script.test_voice_alarm_office_jarvis
|
||||
name: Test alarm
|
||||
card_mod: &cc_panel
|
||||
style: |
|
||||
ha-card {
|
||||
border-radius: var(--cc-radius-brand);
|
||||
box-shadow: var(--cc-shadow-md);
|
||||
}
|
||||
ha-card .card-header,
|
||||
ha-card h1.card-header {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-weight: 700;
|
||||
font-size: 0.9375rem;
|
||||
color: var(--cc-text-muted, var(--secondary-text-color));
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
- type: entities
|
||||
title: ReSpeaker Family Room
|
||||
@@ -43,6 +146,7 @@ views:
|
||||
name: Alarm time
|
||||
- entity: script.test_voice_alarm_respeaker_family_room
|
||||
name: Test alarm
|
||||
card_mod: *cc_panel
|
||||
|
||||
- type: entities
|
||||
title: ReSpeaker Living Room
|
||||
@@ -56,6 +160,7 @@ views:
|
||||
name: Alarm time
|
||||
- entity: script.test_voice_alarm_respeaker_living_room
|
||||
name: Test alarm
|
||||
card_mod: *cc_panel
|
||||
|
||||
- type: entities
|
||||
title: ReSpeaker Master Bedroom
|
||||
@@ -69,3 +174,4 @@ views:
|
||||
name: Alarm time
|
||||
- entity: script.test_voice_alarm_respeaker_master_bedroom
|
||||
name: Test alarm
|
||||
card_mod: *cc_panel
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
# CubeCraft Creations display system — Home Assistant theme
|
||||
#
|
||||
# Source of truth for the tokens below is the production card's stylesheet:
|
||||
# custom_components/cubecraft_production/www/cubecraft-production-card.js
|
||||
# Keep the two in sync. The card reads HA theme variables for its surfaces, so
|
||||
# once this theme is active the CubeOps board and the rest of a dashboard end up
|
||||
# on the same surfaces, borders and text colours automatically.
|
||||
#
|
||||
# The theme carries `modes:`, so it follows HA's light/dark setting. The brand
|
||||
# gradient, type, radii and semantic colours are identical in both modes; only
|
||||
# the surface and text ramps flip.
|
||||
#
|
||||
# TUNED FOR WALL PANELS: `header-height: 0px` near the bottom collapses the HA
|
||||
# toolbar, matching what dakboard-dark did on these dashboards. If you ever want
|
||||
# CubeCraft on a phone or desktop where you still need the toolbar, copy this
|
||||
# file to themes/cubecraft-desktop.yaml, rename the top-level key, and delete
|
||||
# that one line. (HA themes cannot inherit from each other, and this repo's YAML
|
||||
# is loaded by HA's own loader, so a `<<:` merge anchor is not worth the risk of
|
||||
# taking the whole themes include down.)
|
||||
#
|
||||
# Hiding the sidebar as well needs the kiosk-mode frontend plugin (HACS), which
|
||||
# is not currently installed — see dashboards/views/cubeops-dashboard.yaml.
|
||||
|
||||
cubecraft:
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
# Brand tokens — mirrors the `:host` block in cubecraft-production-card.js.
|
||||
# Exposed as --cc-* so dashboard card_mod blocks can reference them by name
|
||||
# instead of hard-coding hexes in a dozen places.
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
cc-blue: "#3B3AB8"
|
||||
cc-purple: "#A035CC"
|
||||
cc-gradient: "linear-gradient(135deg, #3B3AB8 0%, #A035CC 100%)"
|
||||
|
||||
cc-primary-50: "#EBEBF8"
|
||||
cc-primary-500: "#3B3AB8"
|
||||
cc-primary-700: "#201F7A"
|
||||
|
||||
cc-neutral-0: "#FFFFFF"
|
||||
cc-neutral-50: "#FAFAFA"
|
||||
cc-neutral-100: "#F4F4F6"
|
||||
cc-neutral-150: "#EDEDF1"
|
||||
cc-neutral-200: "#E4E4EA"
|
||||
cc-neutral-400: "#9898A8"
|
||||
cc-neutral-600: "#52525E"
|
||||
cc-neutral-900: "#18181D"
|
||||
|
||||
cc-success: "#16A34A"
|
||||
cc-warning: "#D97706"
|
||||
cc-warning-bg: "#FEF3C7"
|
||||
cc-error: "#DC2626"
|
||||
cc-error-bg: "#FEE2E2"
|
||||
|
||||
# 4px spacing scale
|
||||
cc-space-1: "4px"
|
||||
cc-space-2: "8px"
|
||||
cc-space-3: "12px"
|
||||
cc-space-4: "16px"
|
||||
cc-space-5: "20px"
|
||||
cc-space-6: "24px"
|
||||
cc-space-7: "28px"
|
||||
|
||||
cc-radius-sm: "6px"
|
||||
cc-radius-md: "8px"
|
||||
cc-radius-lg: "12px"
|
||||
cc-radius-xl: "16px"
|
||||
cc-radius-brand: "20px"
|
||||
cc-radius-full: "9999px"
|
||||
|
||||
cc-shadow-sm: "0 2px 6px 0 rgba(0,0,0,.08)"
|
||||
cc-shadow-md: "0 4px 12px 0 rgba(0,0,0,.10)"
|
||||
cc-shadow-brand: "0 4px 20px 0 rgba(59,58,184,.25)"
|
||||
|
||||
cc-ease: "cubic-bezier(0,0,.2,1)"
|
||||
cc-dur: "200ms"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
# Typography — Nunito 400..800.
|
||||
# The @font-face has to be declared in the document, not a shadow root; that
|
||||
# is what www/cubecraft/cubecraft-brand-font.js does (wired up via
|
||||
# frontend.extra_module_url in configuration.yaml). Without it everything
|
||||
# below falls back to system-ui and the rest of the theme still works.
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
cc-font: "'Nunito', 'Nunito Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif"
|
||||
primary-font-family: "var(--cc-font)"
|
||||
ha-font-family-body: "var(--cc-font)"
|
||||
ha-font-family-heading: "var(--cc-font)"
|
||||
mdc-typography-font-family: "var(--cc-font)"
|
||||
paper-font-common-base_-_font-family: "var(--cc-font)"
|
||||
paper-font-common-nowrap_-_font-family: "var(--cc-font)"
|
||||
paper-font-body1_-_font-family: "var(--cc-font)"
|
||||
paper-font-subhead_-_font-family: "var(--cc-font)"
|
||||
paper-font-headline_-_font-family: "var(--cc-font)"
|
||||
paper-font-title_-_font-family: "var(--cc-font)"
|
||||
paper-font-caption_-_font-family: "var(--cc-font)"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
# Shape — brand radius on cards, pills on controls.
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
ha-card-border-radius: "var(--cc-radius-brand)"
|
||||
ha-card-border-width: "0"
|
||||
ha-dialog-border-radius: "var(--cc-radius-brand)"
|
||||
ha-badge-border-radius: "var(--cc-radius-full)"
|
||||
mdc-shape-small: "var(--cc-radius-full)"
|
||||
mdc-shape-medium: "var(--cc-radius-lg)"
|
||||
mdc-shape-large: "var(--cc-radius-xl)"
|
||||
button-border-radius: "var(--cc-radius-full)"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
# Colour — brand ramp. Surfaces/text live in the mode blocks below.
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
success-color: "var(--cc-success)"
|
||||
warning-color: "var(--cc-warning)"
|
||||
error-color: "var(--cc-error)"
|
||||
|
||||
label-badge-green: "var(--cc-success)"
|
||||
label-badge-yellow: "var(--cc-warning)"
|
||||
label-badge-red: "var(--cc-error)"
|
||||
|
||||
# Text sitting on top of primary-color (buttons, selected states). White
|
||||
# clears AA against both ends of the gradient (8.5:1 on the blue, 5.4:1 on
|
||||
# the purple), so it is safe wherever the gradient is used.
|
||||
text-primary-color: "#FFFFFF"
|
||||
app-header-text-color: "#FFFFFF"
|
||||
mdc-theme-on-primary: "#FFFFFF"
|
||||
|
||||
# Wall-panel chrome: collapse the HA toolbar. Delete this line for a variant
|
||||
# you navigate by hand (see the header comment).
|
||||
header-height: "0px"
|
||||
|
||||
modes:
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# Light — the design system as authored: white surfaces, neutral sunken
|
||||
# backgrounds, near-black text.
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
light:
|
||||
cc-surface: "var(--cc-neutral-0)"
|
||||
cc-surface-sunken: "var(--cc-neutral-100)"
|
||||
cc-text: "var(--cc-neutral-900)"
|
||||
cc-text-muted: "var(--cc-neutral-600)"
|
||||
cc-border: "var(--cc-neutral-150)"
|
||||
cc-accent: "var(--cc-primary-500)"
|
||||
cc-accent-soft: "var(--cc-primary-50)"
|
||||
|
||||
primary-color: "#3B3AB8"
|
||||
accent-color: "#A035CC"
|
||||
dark-primary-color: "#201F7A"
|
||||
light-primary-color: "#EBEBF8"
|
||||
info-color: "#3B3AB8"
|
||||
|
||||
primary-text-color: "#18181D"
|
||||
secondary-text-color: "#52525E"
|
||||
disabled-text-color: "#9898A8"
|
||||
|
||||
primary-background-color: "#F4F4F6"
|
||||
secondary-background-color: "#EDEDF1"
|
||||
card-background-color: "#FFFFFF"
|
||||
ha-card-background: "#FFFFFF"
|
||||
divider-color: "#EDEDF1"
|
||||
ha-card-box-shadow: "0 4px 12px 0 rgba(0,0,0,.10)"
|
||||
|
||||
app-header-background-color: "#3B3AB8"
|
||||
more-info-header-background: "#F4F4F6"
|
||||
|
||||
sidebar-background-color: "#FFFFFF"
|
||||
sidebar-text-color: "#52525E"
|
||||
sidebar-icon-color: "#9898A8"
|
||||
sidebar-selected-text-color: "#3B3AB8"
|
||||
sidebar-selected-icon-color: "#3B3AB8"
|
||||
|
||||
state-icon-color: "#3B3AB8"
|
||||
state-icon-active-color: "#A035CC"
|
||||
state-unavailable-color: "#9898A8"
|
||||
paper-item-icon-color: "#9898A8"
|
||||
paper-item-icon-active-color: "#3B3AB8"
|
||||
|
||||
switch-checked-color: "#3B3AB8"
|
||||
switch-checked-track-color: "#3B3AB8"
|
||||
switch-unchecked-color: "#9898A8"
|
||||
switch-unchecked-track-color: "#E4E4EA"
|
||||
slider-color: "#3B3AB8"
|
||||
slider-bar-color: "#E4E4EA"
|
||||
|
||||
label-badge-blue: "#3B3AB8"
|
||||
label-badge-gray: "#9898A8"
|
||||
label-badge-text-color: "#18181D"
|
||||
ha-label-badge-background-color: "#FFFFFF"
|
||||
ha-label-badge-border-color: "#EDEDF1"
|
||||
|
||||
table-row-background-color: "#FFFFFF"
|
||||
table-row-alternative-background-color: "#FAFAFA"
|
||||
markdown-code-background-color: "#F4F4F6"
|
||||
code-editor-background-color: "#FAFAFA"
|
||||
|
||||
paper-card-background-color: "#FFFFFF"
|
||||
paper-card-header-color: "#18181D"
|
||||
paper-dialog-background-color: "#FFFFFF"
|
||||
paper-listbox-background-color: "#FFFFFF"
|
||||
input-fill-color: "#F4F4F6"
|
||||
input-ink-color: "#18181D"
|
||||
input-label-ink-color: "#52525E"
|
||||
iron-overlay-backdrop-background-color: "rgba(24,24,29,.32)"
|
||||
|
||||
mdc-theme-primary: "#3B3AB8"
|
||||
mdc-theme-secondary: "#A035CC"
|
||||
mdc-theme-surface: "#FFFFFF"
|
||||
|
||||
# Weather glyphs, kept in the brand family rather than default sky blue.
|
||||
weather-icon-sun-color: "#D97706"
|
||||
weather-icon-rain-color: "#3B3AB8"
|
||||
weather-icon-cloud-front-color: "#9898A8"
|
||||
weather-icon-cloud-back-color: "#E4E4EA"
|
||||
weather-icon-moon-color: "#52525E"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# Dark — violet-tinted neutrals so the brand gradient sits in the same
|
||||
# family as the surfaces rather than floating on plain grey. The primary
|
||||
# is lightened to #8B8AF2 (5.6:1 on the card surface); #3B3AB8 itself is
|
||||
# too dark to read as an accent here, though it stays correct inside the
|
||||
# gradient, which is left untouched.
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
dark:
|
||||
cc-surface: "#1C1C27"
|
||||
cc-surface-sunken: "#16161F"
|
||||
cc-text: "#EDEDF1"
|
||||
cc-text-muted: "#9898A8"
|
||||
cc-border: "#2A2A38"
|
||||
cc-accent: "#8B8AF2"
|
||||
cc-accent-soft: "#252438"
|
||||
|
||||
primary-color: "#8B8AF2"
|
||||
accent-color: "#C77BEE"
|
||||
dark-primary-color: "#3B3AB8"
|
||||
light-primary-color: "#C9C8FA"
|
||||
info-color: "#8B8AF2"
|
||||
|
||||
primary-text-color: "#EDEDF1"
|
||||
secondary-text-color: "#9898A8"
|
||||
disabled-text-color: "#6A6A7C"
|
||||
|
||||
primary-background-color: "#0E0E14"
|
||||
secondary-background-color: "#16161F"
|
||||
card-background-color: "#1C1C27"
|
||||
ha-card-background: "#1C1C27"
|
||||
divider-color: "#2A2A38"
|
||||
ha-card-box-shadow: "0 4px 12px 0 rgba(0,0,0,.45)"
|
||||
|
||||
app-header-background-color: "#16161F"
|
||||
more-info-header-background: "#16161F"
|
||||
|
||||
sidebar-background-color: "#0E0E14"
|
||||
sidebar-text-color: "#9898A8"
|
||||
sidebar-icon-color: "#6A6A7C"
|
||||
sidebar-selected-text-color: "#8B8AF2"
|
||||
sidebar-selected-icon-color: "#8B8AF2"
|
||||
|
||||
state-icon-color: "#8B8AF2"
|
||||
state-icon-active-color: "#C77BEE"
|
||||
state-unavailable-color: "#6A6A7C"
|
||||
paper-item-icon-color: "#6A6A7C"
|
||||
paper-item-icon-active-color: "#8B8AF2"
|
||||
|
||||
switch-checked-color: "#8B8AF2"
|
||||
switch-checked-track-color: "#8B8AF2"
|
||||
switch-unchecked-color: "#6A6A7C"
|
||||
switch-unchecked-track-color: "#2A2A38"
|
||||
slider-color: "#8B8AF2"
|
||||
slider-bar-color: "#2A2A38"
|
||||
|
||||
# Semantic colours lifted for dark surfaces; the light-mode values are
|
||||
# too dense to read at a distance on #1C1C27.
|
||||
success-color: "#4ADE80"
|
||||
warning-color: "#FBBF24"
|
||||
error-color: "#F87171"
|
||||
label-badge-green: "#4ADE80"
|
||||
label-badge-yellow: "#FBBF24"
|
||||
label-badge-red: "#F87171"
|
||||
label-badge-blue: "#8B8AF2"
|
||||
label-badge-gray: "#6A6A7C"
|
||||
label-badge-text-color: "#EDEDF1"
|
||||
ha-label-badge-background-color: "#1C1C27"
|
||||
ha-label-badge-border-color: "#2A2A38"
|
||||
|
||||
table-row-background-color: "#1C1C27"
|
||||
table-row-alternative-background-color: "#20202C"
|
||||
markdown-code-background-color: "#252433"
|
||||
code-editor-background-color: "#16161F"
|
||||
|
||||
paper-card-background-color: "#1C1C27"
|
||||
paper-card-header-color: "#EDEDF1"
|
||||
paper-dialog-background-color: "#1C1C27"
|
||||
paper-listbox-background-color: "#1C1C27"
|
||||
input-fill-color: "#252433"
|
||||
input-ink-color: "#EDEDF1"
|
||||
input-label-ink-color: "#9898A8"
|
||||
iron-overlay-backdrop-background-color: "rgba(0,0,0,.6)"
|
||||
|
||||
mdc-theme-primary: "#8B8AF2"
|
||||
mdc-theme-secondary: "#C77BEE"
|
||||
mdc-theme-surface: "#1C1C27"
|
||||
|
||||
weather-icon-sun-color: "#FBBF24"
|
||||
weather-icon-rain-color: "#8B8AF2"
|
||||
weather-icon-cloud-front-color: "#9898A8"
|
||||
weather-icon-cloud-back-color: "#3A3A4A"
|
||||
weather-icon-moon-color: "#C9C8FA"
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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 });
|
||||
}
|
||||
Reference in New Issue
Block a user