Skip to content

Lay it out in YAML

Each skin is one Lovelace card: custom:dashhaus-orb for Orb, custom:dashhaus-field for Field. The card draws the whole dashboard from its YAML.

With nothing but its type, the card lays out every supported entity it can see, a row per area from Home Assistant’s registries, and keeps up as you add devices. The dashboards the deploy tool creates start this way. Hidden, configuration and diagnostic entities are left out; room sensors are paired with the humidity and battery sensors on the same device.

Open the dashboard, ⋮ → Edit dashboard, the card’s pencil, then Show code editor. The preview redraws as you type, and every mistake is listed at once, with where it is and what you probably meant:

Dashhaus config has 2 problems:
• rows[1].cards[0] (sensor.bedroom_temperature): unknown key "humdity" — did you mean "humidity"?
• rows[2].cards[3]: media_player.tv: media_player.* entities have no card yet

The simplest layout is a list of cards, drawn in that order on one grid. columns sets how many fit across; leave it out and the card fits as many as the screen holds. Add, remove or move a line and the page follows:

type: custom:dashhaus-orb
title: Home
columns: 2
cards:
- light.living_room
- climate.living_room
- cover.living_room

For headings, write rows instead: one under another, each with its own title, columns and cards. A row is flat: it holds cards, never other rows. Each card can say where it sits in its row:

  • width: how many columns it spans, or full for all of them. Never pixels.
  • column: the column it starts in. Cards given the same column stack down it.
  • footprint: compact, standard or (Orb) featured: the card’s fixed height.

And each row how it packs them:

  • flow: dense lets a later card fill an earlier gap; the default keeps your order on screen.
  • align: bottom sets a row’s shorter columns on its foot: a sidebar beside a tall hero ends where the hero does.
type: custom:dashhaus-orb
title: Home
rows:
- title: At a glance
columns: 3
align: bottom
cards:
- entity: climate.living_room
column: 1
width: 2
footprint: featured
- entity: alarm_control_panel.house
column: 3
- entity: light.living_room
column: 3
footprint: compact
- title: Around the house
columns: 3
flow: dense
cards:
- entity: weather.home
width: 2
- entity: cover.living_room
column: 3
footprint: compact
- entity: sensor.living_room_temperature
width: 2
- entity: switch.porch
footprint: compact
- bundle: joined
label: Modes
width: full
cards: [input_boolean.guest_mode, input_boolean.dnd, switch.office]

A row’s cards lists entities, either as the id alone or as a map with options, and bundles: bundle: joined puts compact cards side by side in one outer card (lead and titled are the others). Every footprint is a whole number of Orb’s 60 px units, so a compact card stacks beside a standard one without gaps.

On a narrower screen a row keeps its cards and their heights and takes fewer columns: widths shrink to fit, and once a row has fewer columns than it was written for, its cards flow in your order instead of keeping their column. A row narrowed by its surroundings does the same. A state change never moves a card.

On a tablet: a large thermostat across two columns, the alarm and a light stacked beside it and ending where it ends; then the weather and a room sensor two columns wide, with the blinds and the porch switch stacked beside them, and three modes in one card across the row.On a tablet: a large thermostat across two columns, the alarm and a light stacked beside it and ending where it ends; then the weather and a room sensor two columns wide, with the blinds and the porch switch stacked beside them, and three modes in one card across the row.
The Orb sample above, on a tablet: the thermostat spans two, the alarm and the light stack in the third and end where it ends (align: bottom), and the porch switch fills the space under the blinds (flow: dense).
The same YAML on a phone: two columns, the thermostat across both, then the alarm and the light, the weather, the blinds and the porch switch side by side, the room sensor, and the three modes stacked in one card.The same YAML on a phone: two columns, the thermostat across both, then the alarm and the light, the weather, the blinds and the porch switch side by side, the room sensor, and the three modes stacked in one card.
The same YAML on a phone: two columns fit, so the anchors let go and the cards flow in order, at their own heights.

A bundle draws several cards as one outer card, so things that belong together read as one. It is an entry of a row’s cards, with bundle: saying which of the three it is and its own cards listing the members:

type: custom:dashhaus-orb
title: Home
rows:
- title: Nursery
columns: 3
cards:
- bundle: titled
title: Sound machine
area: Nursery
cards:
- entity: switch.sound_machine_power
name: Power
- entity: light.sound_machine_night_light
name: Night light
- entity: switch.sound_machine_white_noise
card: sound
name: White noise
- bundle: lead
label: Upstairs heating
sheet_title: Also upstairs
cards: [climate.second_floor, climate.dyson_heater]
- bundle: joined
label: Bedtime
cards: [input_boolean.sleep_mode, input_boolean.do_not_disturb]
  • titled: the parts of one thing, often one device split into several entities, under a lid that names it (title, required) and its room (area) once. Name each part for what it is on the thing (“Power”, “Night light”), not “Sound machine power”. A part in the lid’s room leaves the room off its line and says its own detail instead: since when, a light’s warmth, a sound’s volume. The lid is a heading and opens nothing; each part opens its own sheet. See it in Storybook.
  • lead: one card that matters most, drawn standard, with related cards hanging under it as compact rows: a floor’s thermostat and the heater beside it. The lead’s sheet lists the rows again, live, under sheet_title (“Also in this bundle” when left out). See it in Storybook.
  • joined: two or more equal compact cards side by side in one card, with no heading: a few modes, say. Once each would get less than about 170 px they stack instead. See it in Storybook.

label names a joined or lead bundle for screen readers; it isn’t drawn. Every member is still its own card, with its own tap, press and hold, and sheet, and takes its own card’s keys (name, card:, history …), but the bundle sets their footprints and places them: a member takes no footprint, its width and column go on the bundle, and a bundle can’t hold another. A bundle is as tall as its members make, in the same 60 px units as every other card: a titled bundle’s lid and three parts are as tall as a featured card. Field has no bundles; its card refuses the key, saying so.

The card checks the whole YAML before it draws, and lists every problem at once: where it is, what’s wrong, and what to write instead. This Orb card has five of the mistakes people make most:

type: custom:dashhaus-orb
title: Home
cards:
- light.living_room
rows:
- climate.living_room
- title: Comfort
columns: 2
cards:
- entity: climate.living_room
width: 3
- entity: light.living_room
span: 2
- title: Outside
columns: "3"
cards:
- weather.home
Dashhaus config has 5 problems:
• "cards" and "rows" both lay out this page: keep "cards" for one plain grid, or move those cards into a row under "rows"
• rows[0] (climate.living_room): this is a card, not a row: put it in a row's "cards", or list the cards under "cards" at the top for one plain grid
• rows[1].cards[0] (climate.living_room): "width: 3" is wider than this row's 2 columns: use width: full, or raise the row's columns to 3
• rows[1].cards[1] (light.living_room): "span" isn't a key: a card spans columns with "width", a whole number or full
• rows[2]: "columns" must be a whole number from 1 to 12, or auto — write columns: 3, without quotes
  • cards and rows together. A page is one or the other: cards for one plain grid, rows for titled groups. Move the loose cards into a row of their own.
  • A card where a row belongs. Every entry under rows is a map with its own cards; an entity id there is a card. Indent it under a row’s cards.
  • A card wider than its row. width counts the row’s columns, so it can’t be more than the row’s columns. Use width: full for all of them.
  • span. A card’s columns are width.
  • A number in quotes. "3" is text to YAML; write columns: 3.

In Field, a row holds one footprint, so a compact module beside standard ones needs either a row of its own or a column on every module of the row, which stacks them by column:

type: custom:dashhaus-field
title: Home
rows:
- title: Living room
cards:
- light.ceiling
- entity: cover.living_room
footprint: compact
Dashhaus config has 2 problems:
• rows[0].cards[0] (light.ceiling): this row mixes standard and compact modules, so each stacks in a column: give it a "column", or move the compact ones into a row of their own
• rows[0].cards[1] (cover.living_room): this row mixes standard and compact modules, so each stacks in a column: give it a "column", or move the compact ones into a row of their own

Home Assistant reads the YAML before the card does, so it reports a broken file itself (a bad indent, a tab, an unclosed quote) and the card never sees it. A mistake that is still valid YAML, such as a missing - that turns a list into one map, reaches the card as whatever it parsed to.

The entity’s domain picks its card. Where two cards draw one domain, card: picks the other:

EntityCard
light.*light
input_boolean.*, switch.*toggle
cover.*cover
switch.* with card: soundsound, with its volume number.* found beside it
climate.*climate
humidifier.*humidifier
fan.*fan
sensor.*sensor, with its device's humidity and battery sensors
weather.*weather, with its air-quality sensor and sun.sun
sensor.*_voucherguestWifi, with its join QR code image and what refreshes it and makes a new one; card: guestWifi for one named otherwise
alarm_control_panel.*alarm
lock.*lock, with its device's battery sensor
sensor.*_sleep_stateowlet, with its heart rate, oxygen, charging and base station sensors; card: owlet for one named otherwise

An entity with no card is an error that says so, rather than an empty space.

WhereOrbField
The cardtitle, subtitle, show_time, hour_cycle, viewing, theme, accent, nav, pagestitle, subtitle, show_time, hour_cycle, viewing, finish, footprint, quiet, pages, nav
The page (the card, or one of its pages)cards, rows, columns, flow; columns from 1 to 12, or autocards, rows, columns, flow; columns from 1 to 4, or auto
A rowtitle, note, columns, flow, align, cardstitle, note, columns, flow, align, cards
Any cardentity, card, name, area, footprint, width, column, show_control, historyentity, card, name, area, footprint, width, column, history
A bundlebundle (joined, lead, titled), cards, width, column, label (joined, lead), sheet_title (lead), title (titled), area (titled)none

Each card adds its own:

CardOrbField
lightnonenone
toggleicon, descriptionicon, description
covernonenone
soundvolumevolume
climateunitunit
humidifiernonenone
fannonenone
sensorhumidity, battery, outdoor, unithumidity, battery, outdoor, unit
weatheraqi, aqi_scale, sun, forecastsaqi, aqi_scale, sun, forecasts
guestWifiaccess, network, qr, new_code, refreshaccess, network, qr, new_code, refresh
alarmread_only, exit_delay, entry_delay, open_sensorsread_only, exit_delay, entry_delay, open_sensors
lockbatterybattery
owletheart_rate, oxygen, charging, base_station, alerts, stale_afterheart_rate, oxygen, charging, base_station, alerts, stale_after

viewing: far is for a wall panel read from across the room: larger status lines and captions, heavier figures, the same card heights. A key Orb takes and Field doesn’t (theme, accent, show_control, bundles) is refused with the reason, for YAML copied from one card to the other.

The specification’s deploy page has every value and default.

A nav docks at the bottom of the screen, one pill or key per destination. Without nav, it lists the card’s pages, or else its titled rows. pages are more pages in the same card, each with its own cards or rows; the URL keeps the page open (#page=nursery), so the back button returns.

type: custom:dashhaus-orb
title: Home
rows:
- title: Quick actions
cards: [light.living_room, climate.living_room]
pages:
- id: nursery
title: Nursery
cards:
- sensor.owlet_sock_ac000w0123_sleep_state
- { entity: switch.nursery_music, card: sound }
nav:
- page: home
- page: nursery
- navigate: /energy
label: Energy

A destination is a section: of the main page, a titled row by its title, a page: by its id (home is the main page), or navigate: to another Home Assistant dashboard by its path. nav: false turns the nav off; a wall panel has none unless nav lists one.

Both skins take the same pages and nav.