Skip to content

Text

Use layout.texts for headings, captions, button labels, units, and composed entity information placed anywhere on a card. Text can be literal, generated by JavaScript, or taken from an existing name, area, or state item.

A text item does not need an entity. Its default action is none, so a label placed over a rectangle does not block the rectangle's action.

Basic text

Set text to a string for a simple label:

layout:
  texts:
    - id: history-title
      text: History
      xpos: 50
      ypos: 15
      styles:
        font-size: 1.2em
        font-weight: bold
        text-anchor: middle

Use localize_tag instead of text to display a label from Home Assistant in the user's selected language:

layout:
  texts:
    - id: duration-title
      localize_tag: ui.dialogs.helper_settings.timer.duration
      xpos: 50
      ypos: 15

The tag must be an existing Home Assistant translation key. For example, ui.dialogs.helper_settings.counter.step displays the translated label for "Step size". FHS does not provide its own translation or fallback for a tag.

Multipart text

Set text to a list when parts need different styles or should appear on separate lines:

layout:
  texts:
    - id: history-label
      xpos: 50
      ypos: 45
      styles:
        text-anchor: middle
        dominant-baseline: middle
      text:
        - value: History
          styles:
            fill: var(--secondary-text-color)
        - value: 7 days
          new_line: true
          styles:
            fill: var(--primary-text-color)
            font-weight: bold

Parts on the same line are placed directly after each other. Add spaces to the part values where needed:

text:
  - localize_tag: ui.dialogs.helper_settings.timer.duration
  - value: ': '
  - value: 7 days
    styles:
      font-weight: bold

Each part has its own localize_tag, so translated labels and dynamic values can be combined and styled independently.

new_line: true starts a new line. Use dy to change the line spacing; its default is 1.2 em. dx and dy can also move individual parts.

Dynamic parts

Each part can use its own entity, JavaScript template, state map, color stops, styles, and animation. Text uses fill by default. Select show.item_style: colorstop for hard value ranges or show.item_style: colorstopgradient to blend between adjacent stops. The complete TextTool and each individual part can use their own mode. When entity_index is omitted from a part, it uses the entity from the complete text item when one is configured.

texts:
  - id: active-period
    xpos: 50
    ypos: 50
    entity_index: 0
    text:
      - value: 'Period: '
      - value: |
          [[[
            return `${state} day${Number(state) === 1 ? '' : 's'}`;
          ]]]
        styles:
          font-weight: bold

A state map can replace the displayed value and other settings for a part:

text:
  - value: Unknown
    entity_index: 1
    state_map:
      map:
        - state: 'on'
          value: Active
          styles:
            fill: var(--success-color)
        - state: 'off'
          value: Inactive
          styles:
            fill: var(--secondary-text-color)

Reusing names, areas, and states

A text part can reuse the displayed content of an existing item from layout.names, layout.areas, or layout.states. Select the source with its type and id:

layout:
  names:
    - id: room-name
      entity_index: 0
      visibility: hidden

  states:
    - id: room-temperature
      entity_index: 0
      visibility: hidden
      show:
        uom: end

  texts:
    - id: room-summary
      xpos: 50
      ypos: 50
      text:
        - type: name
          id: room-name
        - value: ': '
        - type: state
          id: room-temperature
          styles:
            font-size: 1em
          uom:
            styles:
              font-size: 0.75em

The source keeps processing its entity while hidden. Its formatted content, styles, color stops, and active animation are reused, but its position, group, actions, and visibility are not.

Set source_styles: false when only the formatted content is needed:

- type: state
  id: room-temperature
  source_styles: false

Styles on the text part override the source. A referenced state also includes its unit according to the source's show.uom setting. Use show.uom and uom on the text part to override its display or styling.

Ellipsis

Use ellipsis on the complete text item to limit every visual line. The limit is shared by all parts on that line and starts again after new_line.

texts:
  - id: compact-label
    xpos: 50
    ypos: 50
    ellipsis: 16
    text:
      - value: 'History period: '
      - value: 7 days
        styles:
          font-weight: bold

A part may also have its own ellipsis. That limit is applied before the limit of the complete text item.

Wrapping

Use text_overflow to wrap long text at spaces. The first line always remains at the configured ypos; continuation lines are added below it.

texts:
  - id: weather-summary
    xpos: 50
    ypos: 20
    text_overflow:
      mode: wrap
      wrap:
        max_width: 40
        max_lines: 3
        dy: 1.4
    text: A longer weather summary that may use up to three lines

max_width uses the actual displayed width in card dimensions. Use characters instead when an approximate character limit is preferred. Set one of these two fields, not both. dy controls the line spacing in em.

Wrapping only occurs at spaces, so words are never split. When max_lines is set, remaining text on the final line ends with ... and still fits within max_width. Without max_lines, TextTool creates as many lines as needed.

Use ellipsis with max_width to shorten every visual line to its displayed width:

text_overflow:
  mode: ellipsis
  ellipsis:
    max_width: 40

Use ellipsis.characters instead for a character limit. Set either characters or max_width, not both. The existing ellipsis: 40 shorthand remains supported for character-based ellipsis.

Use fit to reduce the complete TextTool when its widest line exceeds a configured width:

text_overflow:
  mode: fit
  fit:
    max_width: 40
    min_font_size: 0.7em

Fit mode works with single-line and manually configured multiline text. Every line and styled part is reduced by the same amount, so their relative font sizes and spacing remain intact. Text is never enlarged, wrapped, or shortened. Omit min_font_size to keep reducing until the widest line fits. When a minimum is configured, text may remain wider than max_width after reaching that font size.

Configuration fields

Text item

Field Required Default Description
id Generated Identifies the item for same_as, animations, and references.
text Alternative A string or list of text parts. Not required when localize_tag supplies a simple label.
localize_tag Alternative Home Assistant translation key for a simple label.
xpos Horizontal position on the card canvas.
ypos Vertical position on the card canvas.
entity_index None Entity used by the item and inherited by parts without their own entity.
ellipsis None Maximum number of characters on each visual line.
text_overflow None Wraps, shortens, or fits text using the selected mode. TextTool only.
styles Styles inherited by all parts, including alignment and font settings.
color_stops None Colors the complete text item from its entity value.
show.item_style colorstop Selects a hard or blended color-stop color.
colorstop.fill / .stroke true / false Selects which text properties receive the hard color.
colorstopgradient.fill / .stroke true / false Selects which text properties receive the blended color.
tap_action none Action for the complete text item.
hold_action None Hold action for the complete text item.
double_tap_action None Double-tap action for the complete text item.
group Card Places the text in a configured group.
same_as None Reuses an earlier item from layout.texts.

Text overflow

Field Required Default Description
mode Selects wrap, ellipsis, or fit.
wrap.characters Alternative Approximate maximum number of characters per visual line. Do not combine with wrap.max_width.
wrap.max_width Alternative Maximum displayed width of every wrapped line in card dimensions. Do not combine with wrap.characters.
wrap.max_lines Unlimited Maximum number of wrapped lines. Remaining text ends with ....
wrap.dy 1.2 Distance between automatically wrapped lines in em.
ellipsis.characters Alternative Maximum number of characters per visual line. Do not combine with ellipsis.max_width.
ellipsis.max_width Alternative Maximum displayed width of every visual line in card dimensions. Do not combine with ellipsis.characters.
fit.max_width For fit Maximum width of the complete TextTool in card dimensions.
fit.min_font_size None Smallest base font size in em used by fit mode.

Text part

Field Required Default Description
value For text Text or a JavaScript template that returns text.
localize_tag Alternative Home Assistant translation key used as this part's value.
type text Uses literal text, or references a name, area, or state.
id For a reference Item id in the corresponding names, areas, or states section.
source_styles true Set to false to reuse content without source styles, color stops, or animations.
entity_index Item entity Entity used by templates, state maps, and color stops for this part.
new_line false Starts the part on a new line.
dx 0 Horizontal offset in em.
dy 0, or 1.2 for a new line Vertical offset in em.
ellipsis None Maximum number of characters for this part.
styles Overrides inherited styles for this part.
state_map None Changes the part for matching entity states.
color_stops None Colors the part from its entity value.
show.item_style colorstop Selects a hard or blended color-stop color for this part.
colorstop.fill / .stroke true / false Selects which text properties receive the hard color.
colorstopgradient.fill / .stroke true / false Selects which text properties receive the blended color.
animation_id None Connects this part to a text animation.
show.uom Source setting Overrides the UOM position for a referenced state.
uom Source setting Overrides UOM offsets and styles for a referenced state.

Reusing labels

Text items support same_as and the usual numeric delta fields. This makes a row of button labels concise:

texts:
  - id: one-day
    text: 1 day
    xpos: 25
    ypos: 50
  - id: two-days
    same_as: one-day
    text: 2 days
    same_as_dxpos: 25
  - id: seven-days
    same_as: two-days
    text: 7 days
    same_as_dxpos: 25