Skip to content

feat: configurable main icon→name gap via name_gap#401

Open
BlackZork wants to merge 1 commit into
benct:masterfrom
BlackZork:feat/configurable-name-gap
Open

feat: configurable main icon→name gap via name_gap#401
BlackZork wants to merge 1 commit into
benct:masterfrom
BlackZork:feat/configurable-name-gap

Conversation

@BlackZork

Copy link
Copy Markdown

Disclaimer

Changes in this PR and description below were entirely generated by Claude Code with my supervision only. Please verify. I needed to shorten the hard-coded 16px gap between main icon and main label. 16px is way to big for RPI 800x480 display.

Summary

Adds a card-level name_gap option (and a matching --multiple-entity-row-name-gap
CSS variable) that controls the horizontal spacing between the main entity's icon and the
row name.

type: custom:multiple-entity-row
entity: light.living_room
name_gap: 8px      # or a number (treated as px), or any CSS length / var(--x)

Default rendering is completely unchanged — the option is opt-in and a no-op when unset.

Motivation

That gap is HA core hui-generic-entity-row's .info padding-inline-start, hardcoded at
16px inside that element's own shadow DOM. It is not exposed as a CSS variable, padding
is not an inherited property, and styles from outside a shadow tree can't cross the boundary
(a theme or card-mod can't reach it). So today the icon→name spacing on a multiple-entity-row
simply can't be adjusted. On dense/compact dashboards (e.g. a wall-panel layout) 16px is often
too wide.

Because this card creates the hui-generic-entity-row it wraps, it can legitimately reach into
that child to expose the setting.

How it works

When (and only when) name_gap is set:

  1. render() sets --multiple-entity-row-name-gap: <value> on the hui-generic-entity-row
    host (same mechanism as the existing icon_coloriconColorCss inline var).
  2. updated() injects a one-time <style> into that child's shadowRoot:
    :host .info {
      padding-inline-start: var(--multiple-entity-row-name-gap, 16px);
      padding-left:         var(--multiple-entity-row-name-gap, 16px);
    }

Design points:

  • :host .info (specificity 0,2,0), not !important. Core's static styles are applied via
    adoptedStyleSheets, which the cascade orders after a <style> appended to the shadow root —
    so an equal-specificity rule would lose. Higher specificity wins regardless of order, and leaving
    !important off means a user's own override still wins.
  • Both logical + physical padding are set, so it also applies under RTL.
  • 16px fallback in the var() keeps the default byte-for-byte identical.
  • Gated on name_gap — rows that don't use it get zero shadow modification and no injected
    style at all.
  • The injected rule is static (reads the variable), so changing name_gap later only updates the
    host variable via re-render; no re-injection. Injection is idempotent (guarded by a marker attr).
  • A number is treated as px; a string passes through (8px, 0.5em, var(--x)); 0 is honored.

Changes

  • src/types.tsname_gap?: string | number on MultipleEntityRowConfig.
  • src/entity.jsnameGapCss() helper (alongside iconColorCss).
  • src/index.js — host variable in render(); updated() shadow injection.
  • src/editor_schemas.tsname_gap field + label in the visual editor (Main tab).
  • README.md — options table row + a Theming section entry documenting the variable.
  • src/index.test.js — unit tests for the host-variable half (see note below).

Testing

  • yarn lint clean, full vitest suite passes (161 tests, incl. 4 new name_gap cases).
  • The unit tests cover the host-variable side (number→px, string passthrough, 0 honored, omitted
    when unset). The deep shadow injection can only be exercised against a real core
    hui-generic-entity-row (the jsdom stub has no shadow), so it's verified at runtime rather than
    in the suite.
  • Verified live on Home Assistant 2025.10.3: with name_gap: 5px/8px the row's core .info
    padding-inline-start computes to the configured value across multiple rows; unset rows stay at
    16px; toggling the value updates it without re-injection; default rows are untouched.

Notes

  • Fully backward compatible; no change for existing configs.
  • Min HA unchanged (works on current stable; uses only hui-generic-entity-row internals already
    relied upon for the main row).

Add a `name_gap` option (and a `--multiple-entity-row-name-gap` CSS
variable) that controls the spacing between the main entity icon and the
row name. Accepts a CSS length string (`8px`, `0.5em`, `var(--x)`) or a
number (treated as px). Default rendering is unchanged.

That gap is core `hui-generic-entity-row`'s `.info` `padding-inline-start`,
hardcoded at 16px inside that element's own shadow DOM. It is not a CSS
variable, `padding` is not an inherited property, and styles from outside a
shadow tree (themes, card-mod) cannot cross the boundary — so until now it
could not be adjusted at all. Since this card creates that child element, it
sets the variable on the child's host in `render()` and, only when `name_gap`
is configured, injects a one-time scoped `.info` override into the child's
shadow that reads the variable with a 16px fallback. Rows that don't set
`name_gap` get zero shadow modification and render byte-for-byte as before.

Also exposes the option in the visual editor, documents it in the README
(options table + Theming), and adds unit tests for the host-variable half
(the deep shadow injection is only exercisable against a real core row, so
it's verified at runtime rather than in the jsdom suite).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@jpettitt jpettitt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this is a well-put-together PR, and the shadow-injection analysis is correct (verified against the current frontend bundle: .info{padding-left:16px;padding-right:8px;flex:30%;padding-inline:16px 8px}, no variable exposed).

One fix needed: the injected padding-left line breaks RTL rather than supporting it. Core's end-padding is 8px (padding-inline: 16px 8px); in RTL, padding-left is the inline-end side, so your rule overrides that 8px with the gap value. padding-inline-start alone is correct in both directions (universally supported on any browser running HA 2024.4+, this card's minimum) — please drop the physical property and the RTL sentence from the comment.

Happy to merge with that change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants