Skip to content

smithumble/ha-calendar-week-grid-card

Repository files navigation

HA Calendar Week Grid Card

A custom Home Assistant card that displays calendar events in a week grid format. Check out the Demo

Calendar Week Grid Card Screenshot

Installation

HACS Installation (Recommended)

The easiest way to install Calendar Week Grid Card is via HACS (Home Assistant Community Store).

Open in HACS

If it doesn't work, add this repository to HACS manually:

  1. Ensure HACS is installed in Home Assistant.
  2. Go to HACS → 3 dots (top right) → Custom Repositories.
  3. Add this repository: https://github.com/smithumble/ha-calendar-week-grid-card as type Dashboard
  4. Install Calendar Week Grid Card from HACS.
  5. Clear your browser cache and reload Home Assistant.

Manual Installation

Manual Installation Steps
  1. Download the latest release: calendar-week-grid-card.js
  2. Place it in your www folder (e.g. /config/www/calendar-week-grid-card.js).
  3. Add a reference to calendar-week-grid-card.js.

There are two ways to add a reference:

Using UI

  1. Go to Settings → Dashboards → 3 dots (top right) → Resources.

[!NOTE] If you do not see the Resources Tab, you will need to enable Advanced Mode in your User Profile.

  1. Click the Add Resource button.
  2. Set Url to /local/calendar-week-grid-card.js?v=1.

[!NOTE] After any update of the file you will have to edit /local/calendar-week-grid-card.js?v=1 and change the version to any higher number.

  1. Set Resource type to JavaScript module.

Using YAML

Add the resource to your yaml configuration:

url: /local/calendar-week-grid-card.js
type: module

Configuration

Visual Editor

This card supports Home Assistant's visual editor, providing an intuitive interface to configure all card options without writing YAML.

Visual Editor

Simply click the Configure button when editing your dashboard to access the visual editor.

The editor allows you to:

  • Add and configure calendar entities
  • Customize date and time formats
  • Set up event styling and icons
  • Configure theme variables
  • Adjust display options like week start, days shown, and theme mode

All configuration options available in YAML are accessible through the visual editor interface.

YAML

YAML Configuration Options
Name Type Required Description
type string Yes custom:calendar-week-grid-card
entities list Yes List of calendar entities or objects.
language string No Language code for days (e.g., en, fr).
primary_date_format object No Primary date format options for day headers. Default: { weekday: 'short' }. See Date Format.
secondary_date_format object No Secondary date format options for day headers (displayed below primary). Optional. See Date Format.
time_format string/object No Time format pattern (string) or options (object). Default: h A (string) or { hour: 'numeric' } (object). See Time Format.
time_range boolean No Display time as a range (e.g., "09 - 10" instead of "09"). Default: false.
start_hour number No First hour to display (0-23). Default: 0.
end_hour number No Last hour to display (0-23). Default: 24.
filter string No Global filter text for event summary.
icons_container string No Where to render icons: cell (in the cell) or event (in event blocks). Default: cell.
icons_mode string No Which events show icons: top (only main event) or all (all events). Default: top.
event object No Default event configuration with icon and theme_values. See Event Configuration.
blank_event object No Configuration for cells with no events with icon and theme_values. See Event Configuration.
blank_all_day_event object No Configuration for all-day cells with no events with icon and theme_values. See Event Configuration.
all_day string No Where to display all-day events: grid (in the grid), row (in a separate row), or both (in both the grid and a separate row). Default: grid.
all_day_label string No Label text for the all-day row in the time column. Default: empty string.
theme_variables object No Theme variables definition for the visual editor. See Theme Variables.
theme_values_examples array No Example theme values for the visual editor. See Theme Variables.
week_start string No Day of the week to start the calendar: today, sunday, monday, tuesday, wednesday, thursday, friday, or saturday. Default: today.
days number No Number of days to display. Default: 7.
css string No CSS styles for the card.

Date Format

The primary_date_format and secondary_date_format options use Intl.DateTimeFormatOptions to format day headers. The primary format is displayed as the main label, and the secondary format (if provided) is displayed below it in smaller text.

Primary Date Format

Default: { weekday: 'short' } (e.g., "Mon", "Tue", "Wed")

Secondary Date Format

Optional. Displayed below the primary format.

Available Options

  • weekday: 'narrow' | 'short' | 'long' - Day of week (e.g., "M", "Mon", "Monday")
  • day: 'numeric' | '2-digit' - Day of month (e.g., "15", "05")
  • month: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long' - Month (e.g., "3", "03", "M", "Mar", "March")
  • year: 'numeric' | '2-digit' - Year (e.g., "2024", "24")

Examples

# Primary: weekday, Secondary: day and month
primary_date_format:
  weekday: 'short'
secondary_date_format:
  day: 'numeric'
  month: 'short'
# Results: "Mon" (primary), "15 Jan" (secondary)

# Primary: long weekday, Secondary: full date
primary_date_format:
  weekday: 'long'
secondary_date_format:
  day: 'numeric'
  month: 'long'
  year: 'numeric'
# Results: "Monday" (primary), "15 January 2024" (secondary)

Time Format

The time_format option supports two formats:

String Format (Legacy - Backward Compatible)

Custom pattern with tokens. Default: h A.

Available tokens:

  • H: Hour (0-23)
  • HH: Hour (00-23)
  • h: Hour (1-12)
  • hh: Hour (01-12)
  • m: Minute (0-59)
  • mm: Minute (00-59)
  • a: am/pm
  • A: AM/PM

Examples:

  • time_format: "h A"9 AM
  • time_format: "HH:mm"09:00
  • time_format: "hh:mm A"09:00 AM

Object Format (Recommended)

Uses Intl.DateTimeFormatOptions for locale-aware formatting. Default: { hour: 'numeric' }

Available options:

  • hour: 'numeric' | '2-digit' - Hour format
  • minute: 'numeric' | '2-digit' - Minute format
  • second: 'numeric' | '2-digit' - Second format
  • hour12: true | false - 12-hour vs 24-hour format

Examples:

# 12-hour format with AM/PM
time_format:
  hour: 'numeric'
  hour12: true
# Results: "9 AM", "3 PM" (locale-aware)

# 24-hour format with minutes
time_format:
  hour: '2-digit'
  minute: '2-digit'
# Results: "09:00", "15:00"

# 12-hour format with minutes
time_format:
  hour: 'numeric'
  minute: '2-digit'
  hour12: true
# Results: "9:00 AM", "3:00 PM"

Time Range

When time_range: true is set, time labels are displayed as ranges showing the current hour and the next hour (e.g., "09 - 10" for the 9:00-10:00 time slot).

Note: When using object format with time_range, if hour12 is not explicitly set, it will default to 24-hour format (hour12: false) for consistency.

Examples:

# 24-hour range format (default for ranges)
time_format:
  hour: '2-digit'
time_range: true
# Results: "00 - 01", "09 - 10", "15 - 16"

# Explicit 24-hour range format
time_format:
  hour: '2-digit'
  hour12: false
time_range: true
# Results: "00 - 01", "09 - 10", "15 - 16"

# 12-hour range format (must explicitly set hour12: true)
time_format:
  hour: 'numeric'
  hour12: true
time_range: true
# Results: "12 AM - 1 AM", "9 AM - 10 AM", "3 PM - 4 PM"

# String format with range
time_format: 'HH'
time_range: true
# Results: "00 - 01", "09 - 10", "15 - 16"

Event Configuration

The event, blank_event, and blank_all_day_event options allow you to configure default event styling and icons.

Name Type Description
icon string Icon for the event type.
theme_values object Theme values to apply. These can reference variables defined in theme_variables.

Examples

# Default event configuration
event:
  icon: mdi:check-circle
  theme_values:
    color: var(--primary-color)
    opacity: 0.2

# Blank event (cells with no events)
blank_event:
  icon: mdi:checkbox-blank-circle-outline
  theme_values:
    icon-opacity: 0.3
    opacity: 0
    border-opacity: 0.3

# Blank all-day event
blank_all_day_event:
  icon: mdi:checkbox-blank-circle

Theme Variables

The theme_variables and theme_values_examples options are used by the visual editor to provide a user-friendly interface for customizing event appearance.

Theme Variables

Define custom variables that can be used in theme_values. Each variable can have a name and description for the editor.

theme_variables:
  color:
    name: Event Color
    description: The color of the event.
  opacity:
    name: Opacity
    description: The opacity of the event background (0.1 to 1.0).
  border-style:
    name: Border Style
    description: The border style of the event (solid, dashed, dotted, double).

Theme Values Examples

Provide example configurations that the visual editor can use as presets.

theme_values_examples:
  - color: var(--error-color)
    opacity: 0.1
    border-style: solid
  - color: var(--warning-color)
    opacity: 0.1
    border-style: dashed

Entity Configuration

Name Type Required Description
name string No Friendly name for the entity.
entity string Yes The entity_id of the calendar.
filter string No Filter text for events.
icon string No Icon for the entity.
type string No Type identifier for the entity.
theme_values object No Theme values to apply. These can reference variables defined in theme_variables.
under array No Events to render underneath this one. See Event Layering.
over array No Events to render on top of this one. See Event Layering.
hide array No Events to hide when this event is present. See Event Hiding.

Data Attributes

Event elements (.event-wrapper and .event-icon) include the following data attributes that can be used for CSS styling:

Attribute Description
data-name The friendly name from the entity configuration.
data-entity The entity_id of the calendar.
data-filter The filter text for events.
data-type The type identifier from the entity configuration.

Event Layering

The under and over options allow you to control the rendering order (z-index) of overlapping events. Events are matched using OR logic - if an event matches any of the criteria in the list, it will be repositioned.

Layer Criteria

Each item in under or over can be:

  • A string: Treated as a name match
  • An object with one or more of:
    • name: Match by entity name
    • type: Match by entity type
    • entity: Match by entity ID
    • filter: Match by filter text

Under

Events matching under criteria that appear after the current event will be moved before it, rendering them underneath (behind) the current event.

Over

Events matching over criteria that appear before the current event will be moved after it, rendering them on top (in front) of the current event.

Event Hiding

The hide option allows you to hide specific events when the current event is present in the same cell. Events are matched using OR logic - if an event matches any of the criteria in the list, it will be removed from the display.

Hide Criteria

Each item in hide can be:

  • A string: Treated as a name match
  • An object with one or more of:
    • name: Match by entity name
    • type: Match by entity type
    • entity: Match by entity ID
    • filter: Match by filter text

Examples

Themes from those examples are available in Visual Editor -> Styling -> Theme or can be configured via the YAML Editor.

Note

The examples use the HA Yasno Outages integration calendar, which shows outages in Ukraine caused by Russian attacks on civilian and energy infrastructure during the invasion of Ukraine.

Basic

Calendar Week Grid Card Example: Basic

YAML Configuration
type: custom:calendar-week-grid-card
language: en
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: false
icons_mode: top
icons_container: cell
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:check-circle-outline
  - name: Probable Outages
    entity: calendar.probable_outages

Simple

Calendar Week Grid Card Example: Simple

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
  month: short
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: false
icons_mode: top
icons_container: cell
all_day: row
theme_variables:
  icon-opacity:
    name: Icon Opacity
    description: The opacity of the event icon (0.0 to 1.0).
  opacity:
    name: Opacity
    description: The opacity of the event background (0.1 to 1.0).
  border-opacity:
    name: Border Opacity
    description: The opacity of the event border (0.0 to 1.0).
  border-style:
    name: Border Style
    description: The border style of the event (solid, dashed, dotted, double).
blank_event:
  icon: mdi:checkbox-blank-circle-outline
  theme_values:
    icon-opacity: 0.3
    opacity: 0
    border-opacity: 0.3
blank_all_day_event:
  icon: mdi:checkbox-blank-circle
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:flash-off
    theme_values:
      opacity: 0.3
      border-style: solid
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:alert-circle-outline
    theme_values:
      opacity: 0.1
      border-style: dashed
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
    theme_values:
      opacity: 0.2
      border-style: double
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:flash-off
    theme_values:
      opacity: 0.1
      border-style: dotted
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      opacity: 0.1
      border-style: dotted
    hide:
      - Planned Outages
css: |
  .event-block {
    border-radius: 4px;
    border: 1px var(--border-style, dotted) rgb(from var(--primary-text-color) r g b / var(--border-opacity, 1.0));
  }

  .event-icon {
    opacity: var(--icon-opacity, 1.0);
  }

  .event-sub-block {
    background-color: rgb(from var(--primary-text-color) r g b / var(--opacity, 0.1));
  }

Simple Colored

Calendar Week Grid Card Example: Simple Colored

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
  month: short
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: false
icons_mode: top
icons_container: cell
all_day: row
theme_variables:
  icon-opacity:
    name: Icon Opacity
    description: The opacity of the event icon (0.0 to 1.0).
  color:
    name: Event Color
    description: The color of the event.
  opacity:
    name: Opacity
    description: The opacity of the event background (0.1 to 1.0).
  border-style:
    name: Border Style
    description: The border style of the event (solid, dashed, dotted, double).
  border-opacity:
    name: Border Opacity
    description: The opacity of the event border (0.0 to 1.0).
blank_event:
  icon: mdi:checkbox-blank-circle-outline
  theme_values:
    icon-opacity: 0.3
    opacity: 0
    border-opacity: 0.3
blank_all_day_event:
  icon: mdi:checkbox-blank-circle
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:flash-off
    theme_values:
      color: var(--error-color)
      opacity: 0.1
      border-style: solid
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:alert-circle-outline
    theme_values:
      color: var(--warning-color)
      opacity: 0.1
      border-style: dashed
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
    theme_values:
      color: var(--error-color)
      opacity: 0.2
      border-style: double
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:flash-off
    theme_values:
      color: var(--success-color)
      opacity: 0.1
      border-style: dotted
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      color: var(--info-color)
      opacity: 0.1
      border-style: dotted
    hide:
      - Planned Outages
css: |
  .event-block {
    border-radius: 4px;
    border: 1px var(--border-style, dotted) rgb(from var(--color, var(--primary-text-color)) r g b / var(--border-opacity, 1.0));
  }

  .event-icon {
    color: var(--color, var(--primary-text-color));
    opacity: var(--icon-opacity, 1.0);
  }

  .event-sub-block {
    background-color: rgb(from var(--color, var(--primary-text-color)) r g b / var(--opacity, 0.1));
  }

Classic

Calendar Week Grid Card Example: Classic

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
  month: short
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: false
icons_mode: top
icons_container: cell
all_day: row
theme_variables:
  icon-opacity:
    name: Icon Opacity
    description: The opacity of the event icon (0.0 to 1.0).
  color:
    name: Event Color
    description: The color of the event.
  opacity:
    name: Opacity
    description: The opacity of the event background (0.1 to 1.0).
blank_event:
  icon: mdi:checkbox-blank-circle-outline
  theme_values:
    icon-opacity: 0.3
    opacity: 0
blank_all_day_event:
  icon: mdi:checkbox-blank-circle
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:flash-off
    theme_values:
      color: var(--error-color)
      opacity: 0.2
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:flash-off
    theme_values:
      color: var(--primary-color)
      opacity: 0.2
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
    theme_values:
      color: var(--error-color)
      opacity: 0.4
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:flash-off
    theme_values:
      color: var(--success-color)
      opacity: 0.2
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      color: var(--warning-color)
      opacity: 0.2
    hide:
      - Planned Outages
css: |
  .event-block {
    border-radius: 4px;
  }

  .event-icon {
    color: var(--color, var(--primary-text-color));
    opacity: var(--icon-opacity, 1.0);
  }

  .event-sub-block {
    background-color: rgb(from var(--color, var(--primary-text-color)) r g b / var(--opacity, 0.2));
  }

Neon

Calendar Week Grid Card Example: Neon

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
  month: short
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: false
icons_mode: top
icons_container: cell
all_day: row
theme_variables:
  icon-opacity:
    name: Icon Opacity
    description: The opacity of the event icon (0.0 to 1.0).
  color:
    name: Event Color
    description: The color of the event.
  opacity:
    name: Opacity
    description: The opacity of the event background (0.1 to 1.0).
  background:
    name: Background
    description: The background of the event (color or repeating-linear-gradient).
blank_event:
  icon: mdi:lightning-bolt
  theme_values:
    icon-opacity: 0.7
    color: var(--neon-green)
    opacity: 0.01
blank_all_day_event:
  icon: mdi:circle-outline
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:flash-off
    theme_values:
      color: '#FF5252'
      opacity: 0.08
      background: 'repeating-linear-gradient(45deg, rgb(from #FF5252 r g b / 0.08), rgb(from #FF5252 r g b / 0.08) 10px, rgb(from #FF5252 r g b / 0.15) 10px, rgb(from #FF5252 r g b / 0.15) 20px)'
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:alert-circle-outline
    theme_values:
      color: '#FF9800'
      opacity: 0.08
      background: 'repeating-linear-gradient(45deg, rgb(from #FF9800 r g b / 0.08), rgb(from #FF9800 r g b / 0.08) 10px, rgb(from #FF9800 r g b / 0.15) 10px, rgb(from #FF9800 r g b / 0.15) 20px)'
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
    theme_values:
      color: '#FF8A80'
      opacity: 0.2
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:calendar-check
    theme_values:
      color: '#29B6F6'
      opacity: 0.05
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      color: '#90A4AE'
      opacity: 0.05
    hide:
      - Planned Outages
css: |
  ha-card {
    --neon-green: #00E676;
    --neon-green-shadow: rgba(0, 230, 118, 0.3);
    --icon-size: 18px;
  }

  .cell {
    height: 28px;
    border-radius: 6px;
  }

  .event-icon {
    color: var(--color, var(--primary-text-color));
    opacity: var(--icon-opacity, 1.0);
    filter: drop-shadow(0 0 2px rgb(from var(--color, var(--primary-text-color)) r g b / 0.3));
  }

  .event-wrapper .event-sub-block {
    background: var(--background, rgb(from var(--color, var(--primary-text-color)) r g b / var(--opacity, 0.1)));
  }

Soft UI

Calendar Week Grid Card Example: Soft UI

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
  month: short
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: false
icons_container: event
icons_mode: all
all_day: row
theme_variables:
  color:
    name: Event Color
    description: The color of the event icon.
  background:
    name: Background
    description: The background color of the event.
blank_event:
  icon: mdi:circle-outline
  theme_values:
    color: '#E0E0E0'
    background: '#F5F5F5'
blank_all_day_event:
  icon: mdi:checkbox-blank-circle
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:close-circle
    theme_values:
      color: '#FFFFFF'
      background: '#FF8A80'
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:close-circle
    theme_values:
      color: '#1B5E20'
      background: '#B9F6CA'
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:alert-circle
    theme_values:
      color: '#FFFFFF'
      background: '#FF80AB'
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:circle-outline
    theme_values:
      color: '#F57F17'
      background: '#FFF176'
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:clock-outline
    theme_values:
      color: '#006064'
      background: '#80DEEA'
    hide:
      - Planned Outages
css: |
  ha-card {
    --text-color: #444;
    --text-primary: var(--text-color);
    --text-secondary: var(--text-color);
  }

  ha-card {
    background: #FFFFFF;
    border-radius: 24px;
    box-shadow: 0 8px 16px rgba(0,0,0,0.1);
  }

  .grid-container {
    gap: 8px;
  }

  .cell-wrapper {
    min-height: 28px;
  }

  .cell {
    height:34px;
    border-radius: 17px;
  }

  .event-icon {
    --icon-size: 20px;
    color: var(--color, var(--primary-text-color));
  }

  .event-block {
    background-color: var(--background, #F5F5F5);
  }

Yasno Legacy

Calendar Week Grid Card Example: Yasno Legacy

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
  month: short
time_format:
  hour: 2-digit
  hour12: false
time_range: true
icons_container: event
icons_mode: all
all_day: row
theme_variables:
  color:
    name: Event Color
    description: The color of the event icon.
  background:
    name: Background
    description: The background color of the event.
  opacity:
    name: Opacity
    description: The opacity of the event icon (0.0 to 1.0).
blank_event:
  icon: null
  theme_values:
    opacity: 0.3
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    type: highlight
    icon: mdi:flash-off
    theme_values:
      color: var(--color-highlight-icon)
      background: var(--color-highlight)
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:flash-off
    theme_values:
      color: var(--color-highlight-light-icon)
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      color: var(--color-highlight-light-icon)
      opacity: 0.5
    hide:
      - Planned Outages
css: |
  ha-card {
    --color-highlight: #FDD631;
    --color-highlight-icon: #1e1e2e;
    --color-highlight-light: #FDD631;
    --color-highlight-light-icon: var(--primary-text-color);
    padding: 0;
  }

  ha-card.theme-dark {
    --color-highlight-light-icon: #FDD631;
  }

  .grid-container {
    gap: 0px;
  }

  .event-icon {
    --icon-size: 16px;
  }

  .event-block, .time-label {
    background-color: var(--card-background-color);
  }

  .time-label {
    padding: 0 18px;
  }

  .time-label.now {
    color: var(--primary-text-color);
  }

  .today, .now {
    &.day-header, &.time-label, .event-sub-block {
      background-color: rgb(from var(--color-highlight-light) r g b / 0.4);
    }
  }

  .event-block, .day-header {
    border-radius: 0px;
  }

  .event-icon {
    color: var(--color, var(--primary-text-color));
    opacity: var(--opacity, 1.0);
  }

  .event-block {
    background-color: var(--background, var(--card-background-color));
  }

  .grid-container > .row-odd {
    .event-wrapper:not([data-type="highlight"]) .event-block, .time-label {
      filter: contrast(1.05) brightness(0.95); 
    }
  }

Google Calendar

Calendar Week Grid Card Example: Google Calendar

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: true
icons_mode: all
icons_container: event
all_day: row
theme_variables:
  color:
    name: Event Color
    description: The color of the event.
blank_event:
  theme_values:
    color: transparent
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:flash-off
    theme_values:
      color: var(--error-color)
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:flash-off
    theme_values:
      color: var(--primary-color)
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
    theme_values:
      color: var(--error-color)
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:flash-off
    theme_values:
      color: var(--success-color)
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      color: var(--warning-color)
    hide:
      - Planned Outages
css: |
  ha-card { 
    --grid-bg: var(--card-background-color, #FFF);
    --grid-border-color: var(--divider-color, #dde3ea);
    --grid-primary-text-color: var(--primary-text-color, #1f1f1f);
    --grid-secondary-text-color: var(--secondary-text-color, #444746);
    --grid-event-text-color: var(--card-background-color, #fff);
    --grid-accent-color: var(--primary-color, #0b57d0);
    --grid-accent-text-color: var(--card-background-color, #fff);
  }

  ha-card.theme-dark {
    --grid-bg: var(--card-background-color, #202124);
    --grid-border-color: var(--divider-color, #3c4043);
    --grid-primary-text-color: var(--primary-text-color, #e3e3e3);
    --grid-secondary-text-color: var(--secondary-text-color, #c4c7c5);
    --grid-event-text-color: var(--card-background-color, #131314);
    --grid-accent-color: var(--primary-color, #a8c7fa);
    --grid-accent-text-color: var(--card-background-color, #062e6f);
  }

  ha-card {
    background-color: var(--grid-bg);
    font-family: 'Google Sans', Roboto, Arial, sans-serif;
  }

  /* Grid Styling */

  .grid-container {
    gap: 0px;
  }

  .time-label-wrapper::after {
    content: '';
    position: absolute;
    bottom: 0;
    right: 0;
    width: 10px;
  }

  .time-label-wrapper + .cell-wrapper,
  .cell-wrapper + .cell-wrapper,
  .time-label-wrapper::after {
    border-bottom: 1px solid var(--grid-border-color);
  }

  .time-label-wrapper + .cell-wrapper,
  .cell-wrapper + .cell-wrapper {
    border-left: 1px solid var(--grid-border-color);
    padding-right: 4px;
    padding-bottom: 4px;
  }

  .cell {
    position: relative;
    top: -1px;
    left: -1px;
    height: 24px;
  }

  .day-header {
    padding-bottom: 12px;
  }

  .day-header.today {
    color: var(--grid-accent-color);
  }

  .day-header-primary {
    text-transform: uppercase;
    font-size: 10px;
    font-weight: 500;
    color: var(--grid-secondary-text-color);
  }

  .day-header-secondary {
    position: relative;
    font-size: 16px;
    font-weight: 400;
    color: var(--grid-primary-text-color);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    margin-top: 4px;
    padding: 6px;
    aspect-ratio: 1/1;
    line-height: 1.2;
    min-width: fit-content;
  }

  .day-header.today .day-header-secondary {
    color: var(--grid-accent-text-color);
  }

  .day-header.today .day-header-secondary:before {
    content: '';
    position: absolute;
    z-index: -1; 
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 50%;
    background-color: var(--grid-accent-color);
  }

  .time-label-wrapper {
    position: relative;
  }

  .time-label {
    display: block;
    position: relative;
    font-size: 10px;
    line-height: 10px;
    color: var(--grid-secondary-text-color);
    padding-right: 0;
    margin-right: 20px;
    text-align: right;
  }

  .time-label-all-day {
    font-size: 8px;
  }

  .time-label-hour-separator {
    display: none;
  }

  .time-label-hour-start,
  .time-label-all-day,
  .time-label-hour-end {
    display: block;
  }

  .time-label-hour:not(.time-label-hour-end), 
  .time-label-all-day {
    position: relative;
    top: -5px;
    right: 0;
  }

  .time-label-hour-end {
    position: absolute;
    right: 0;
    bottom: -5px;
  }

  .time-label-wrapper:has(~ .time-label-wrapper) .time-label-hour-end {
    visibility: hidden;
  }

  .event-block {
    border-radius: 6px;
  }

  /* Icons: Subtle placement */

  .event-icon {
    color: var(--grid-event-text-color);
    --icon-size: 14px;
  }

  /* Current Time Line */

  .current-time-line {
    background-color: var(--warning-color);
    height: 2px;
  }
  .current-time-circle {
    background-color: var(--warning-color);
    width: 8px;
    height: 8px;
    left: -4px;
    top: -3px;
  }

  /* Default Event Styles */

  .event-sub-block {
    background-color: var(--color, var(--grid-primary-text-color));
  }

Google Calendar Separated

Calendar Week Grid Card Example: Google Calendar Separated

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: true
icons_mode: all
icons_container: event
all_day: row
theme_variables:
  color:
    name: Event Color
    description: The color of the event.
blank_event:
  theme_values:
    color: transparent
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:flash-off
    theme_values:
      color: var(--error-color)
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:flash-off
    theme_values:
      color: var(--primary-color)
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
    theme_values:
      color: var(--error-color)
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:flash-off
    theme_values:
      color: var(--success-color)
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      color: var(--warning-color)
    hide:
      - Planned Outages
css: |
  ha-card { 
    --grid-bg: var(--card-background-color, #FFF);
    --grid-border-color: var(--divider-color, #dde3ea);
    --grid-primary-text-color: var(--primary-text-color, #1f1f1f);
    --grid-secondary-text-color: var(--secondary-text-color, #444746);
    --grid-event-text-color: var(--card-background-color, #fff);
    --grid-accent-color: var(--primary-color, #0b57d0);
    --grid-accent-text-color: var(--card-background-color, #fff);
  }

  ha-card.theme-dark {
    --grid-bg: var(--card-background-color, #202124);
    --grid-border-color: var(--divider-color, #3c4043);
    --grid-primary-text-color: var(--primary-text-color, #e3e3e3);
    --grid-secondary-text-color: var(--secondary-text-color, #c4c7c5);
    --grid-event-text-color: var(--card-background-color, #131314);
    --grid-accent-color: var(--primary-color, #a8c7fa);
    --grid-accent-text-color: var(--card-background-color, #062e6f);
  }

  ha-card {
    background-color: var(--grid-bg);
    font-family: 'Google Sans', Roboto, Arial, sans-serif;
  }

  /* Grid Styling */

  .grid-container {
    gap: 0px;
  }

  .time-label-wrapper::after {
    content: '';
    position: absolute;
    bottom: 0;
    right: 0;
    width: 10px;
  }

  .time-label-wrapper + .cell-wrapper,
  .cell-wrapper + .cell-wrapper,
  .time-label-wrapper::after {
    border-bottom: 1px solid var(--grid-border-color);
  }

  .time-label-wrapper + .cell-wrapper,
  .cell-wrapper + .cell-wrapper {
    border-left: 1px solid var(--grid-border-color);
  }

  .cell {
    position: relative;
    top: -3px;
    left: -3px;
    height: 28px;
  }

  .day-header {
    padding-bottom: 12px;
  }

  .day-header.today {
    color: var(--grid-accent-color);
  }

  .day-header-primary {
    text-transform: uppercase;
    font-size: 10px;
    font-weight: 500;
    color: var(--grid-secondary-text-color);
  }

  .day-header-secondary {
    position: relative;
    font-size: 16px;
    font-weight: 400;
    color: var(--grid-primary-text-color);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    margin-top: 4px;
    padding: 6px;
    aspect-ratio: 1/1;
    line-height: 1.2;
    min-width: fit-content;
  }

  .day-header.today .day-header-secondary {
    color: var(--grid-accent-text-color);
  }

  .day-header.today .day-header-secondary:before {
    content: '';
    position: absolute;
    z-index: -1; 
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 50%;
    background-color: var(--grid-accent-color);
  }

  .time-label-wrapper {
    position: relative;
  }

  .time-label {
    display: block;
    position: relative;
    font-size: 10px;
    line-height: 10px;
    color: var(--grid-secondary-text-color);
    padding-right: 0;
    margin-right: 20px;
    text-align: right;
  }

  .time-label-all-day {
    font-size: 8px;
  }

  .time-label-hour-separator {
    display: none;
  }

  .time-label-hour-start,
  .time-label-all-day,
  .time-label-hour-end {
    display: block;
  }

  .time-label-hour:not(.time-label-hour-end), 
  .time-label-all-day {
    position: relative;
    top: -5px;
    right: 0;
  }

  .time-label-hour-end {
    position: absolute;
    right: 0;
    bottom: -5px;
  }

  .time-label-wrapper:has(~ .time-label-wrapper) .time-label-hour-end {
    visibility: hidden;
  }

  .event-sub-block {
    padding: 2px;
  }

  .event-sub-block:after {
    content: '';
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 6px;
  }

  /* Icons: Subtle placement */

  .event-icon {
    color: var(--grid-event-text-color);
    --icon-size: 14px;
  }

  .event-wrapper:has(.event-sub-block:not([style*="top: 0%;"][style*="height: 100%;"])) .event-icon-overlay {
    display: none;
  }

  /* Current Time Line */

  .current-time-line {
    background-color: var(--warning-color);
    height: 2px;
  }
  .current-time-circle {
    background-color: var(--warning-color);
    width: 8px;
    height: 8px;
    left: -4px;
    top: -3px;
  }

  /* Default Event Styles */

  .event-sub-block:after {
    background-color: var(--color, var(--grid-primary-text-color));
  }

Google Calendar Original

Calendar Week Grid Card Example: Google Calendar Original

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: false
icons_mode: all
icons_container: event
all_day: row
theme_variables:
  color:
    name: Event Color
    description: The color of the event.
blank_event:
  theme_values:
    color: transparent
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:flash-off
    theme_values:
      color: '#F3511E'
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:flash-off
    theme_values:
      color: '#4285F4'
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
    theme_values:
      color: '#D40101'
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:flash-off
    theme_values:
      color: '#34B779'
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      color: '#F6BE28'
    hide:
      - Planned Outages
css: |
  ha-card { 
    --grid-bg: #FFF;
    --grid-border-color: #dde3ea;
    --grid-primary-text-color: #1f1f1f;
    --grid-secondary-text-color: #444746;
    --grid-event-text-color: #fff;
    --grid-accent-color: #0b57d0;
    --grid-accent-text-color: #fff;
  }

  ha-card.theme-dark {
    --grid-bg: #202124;
    --grid-border-color: #3c4043;
    --grid-primary-text-color: #e3e3e3;
    --grid-secondary-text-color: #c4c7c5;
    --grid-event-text-color: #131314;
    --grid-accent-color: #a8c7fa;
    --grid-accent-text-color: #062e6f;
  }

  ha-card {
    background-color: var(--grid-bg);
    box-shadow: none !important;
    border: none !important;
    font-family: 'Google Sans', Roboto, Arial, sans-serif;
  }

  /* Grid Styling */

  .grid-container {
    gap: 0px;
  }

  .time-label-wrapper::after {
    content: '';
    position: absolute;
    bottom: 0;
    right: 0;
    width: 10px;
  }

  .time-label-wrapper + .cell-wrapper,
  .cell-wrapper + .cell-wrapper,
  .time-label-wrapper::after {
    border-bottom: 1px solid var(--grid-border-color);
  }

  .time-label-wrapper + .cell-wrapper,
  .cell-wrapper + .cell-wrapper {
    border-left: 1px solid var(--grid-border-color);
    padding-right: 4px;
    padding-bottom: 4px;
  }

  .cell {
    position: relative;
    top: -1px;
    left: -1px;
    height: 24px;
  }

  .day-header {
    padding-bottom: 12px;
  }

  .day-header.today {
    color: var(--grid-accent-color);
  }

  .day-header-primary {
    text-transform: uppercase;
    font-size: 10px;
    font-weight: 500;
    color: var(--grid-secondary-text-color);
  }

  .day-header-secondary {
    position: relative;
    font-size: 16px;
    font-weight: 400;
    color: var(--grid-primary-text-color);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    margin-top: 4px;
    padding: 6px;
    aspect-ratio: 1/1;
    line-height: 1.2;
    min-width: fit-content;
  }

  .day-header.today .day-header-secondary {
    color: var(--grid-accent-text-color);
  }

  .day-header.today .day-header-secondary:before {
    content: '';
    position: absolute;
    z-index: -1; 
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 50%;
    background-color: var(--grid-accent-color);
  }

  .time-label {
    top: -14px;
    font-size: 10px;
    line-height: 10px;
    color: var(--grid-secondary-text-color);
    padding-right: 20px;
  }

  .event-block {
    border-radius: 6px;
  }

  /* Icons: Subtle placement */

  .event-icon {
    color: var(--grid-event-text-color);
    --icon-size: 14px;
  }

  /* Current Time Line */

  .current-time-line {
    background-color: #ea4335;
    height: 2px;
  }
  .current-time-circle {
    background-color: #ea4335;
    width: 8px;
    height: 8px;
    left: -4px;
    top: -3px;
  }

  /* Default Event Styles */

  .event-sub-block {
    background-color: var(--color, var(--grid-primary-text-color));
  }

Google Calendar Original Separated

Calendar Week Grid Card Example: Google Calendar Original Separated

YAML Configuration
type: custom:calendar-week-grid-card
primary_date_format:
  weekday: short
secondary_date_format:
  day: numeric
time_format:
  hour: 2-digit
  minute: 2-digit
  hour12: false
time_range: false
icons_mode: all
icons_container: event
all_day: row
theme_variables:
  color:
    name: Event Color
    description: The color of the event.
blank_event:
  theme_values:
    color: transparent
entities:
  - name: Planned Outages
    entity: calendar.planned_outages
    filter: Outage
    icon: mdi:flash-off
    theme_values:
      color: '#F3511E'
  - name: Probable Outages
    entity: calendar.probable_outages
    icon: mdi:flash-off
    theme_values:
      color: '#4285F4'
  - name: Emergency Shutdowns
    entity: calendar.planned_outages
    filter: Emergency Shutdowns
    icon: mdi:transmission-tower-off
    theme_values:
      color: '#D40101'
  - name: Schedule Applies
    entity: calendar.planned_outages
    filter: Schedule Applies
    icon: mdi:flash-off
    theme_values:
      color: '#34B779'
  - name: Waiting for Schedule
    entity: calendar.planned_outages
    filter: Waiting for Schedule
    icon: mdi:timer-sand
    theme_values:
      color: '#F6BE28'
    hide:
      - Planned Outages
css: |
  ha-card {
    --grid-bg: #FFF;
    --grid-border-color: #dde3ea;
    --grid-primary-text-color: #1f1f1f;
    --grid-secondary-text-color: #444746;
    --grid-event-text-color: #fff;
    --grid-accent-color: #0b57d0;
    --grid-accent-text-color: #fff;
  }

  ha-card.theme-dark {
    --grid-bg: #202124;
    --grid-border-color: #3c4043;
    --grid-primary-text-color: #e3e3e3;
    --grid-secondary-text-color: #c4c7c5;
    --grid-event-text-color: #131314;
    --grid-accent-color: #a8c7fa;
    --grid-accent-text-color: #062e6f;
  }

  ha-card {
    background-color: var(--grid-bg);
    box-shadow: none !important;
    border: none !important;
    border-radius: 16px;
    font-family: 'Google Sans', Roboto, Arial, sans-serif;
  }

  /* Grid Styling */

  .grid-container {
    gap: 0px;
  }

  .time-label-wrapper::after {
    content: '';
    position: absolute;
    bottom: 0;
    right: 0;
    width: 10px;
  }

  .time-label-wrapper + .cell-wrapper,
  .cell-wrapper + .cell-wrapper,
  .time-label-wrapper::after {
    border-bottom: 1px solid var(--grid-border-color);
  }

  .time-label-wrapper + .cell-wrapper,
  .cell-wrapper + .cell-wrapper {
    border-left: 1px solid var(--grid-border-color);
  }

  .cell {
    position: relative;
    top: -3px;
    left: -3px;
    height: 28px;
  }

  .day-header {
    padding-bottom: 12px;
  }

  .day-header.today {
    color: var(--grid-accent-color);
  }

  .day-header-primary {
    text-transform: uppercase;
    font-size: 10px;
    font-weight: 500;
    color: var(--grid-secondary-text-color);
  }

  .day-header-secondary {
    position: relative;
    font-size: 16px;
    font-weight: 400;
    color: var(--grid-primary-text-color);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    margin-top: 4px;
    padding: 6px;
    aspect-ratio: 1/1;
    line-height: 1.2;
    min-width: fit-content;
  }

  .day-header.today .day-header-secondary {
    color: var(--grid-accent-text-color);
  }

  .day-header.today .day-header-secondary:before {
    content: '';
    position: absolute;
    z-index: -1; 
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    border-radius: 50%;
    background-color: var(--grid-accent-color);
  }

  .time-label {
    top: -14px;
    font-size: 10px;
    line-height: 10px;
    color: var(--grid-secondary-text-color);
    padding-right: 20px;
  }

  .event-sub-block {
    padding: 2px;
  }

  .event-sub-block:after {
    content: '';
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 6px;
  }

  /* Icons: Subtle placement */

  .event-icon {
    color: var(--grid-event-text-color);
    --icon-size: 14px;
  }

  .event-wrapper:has(.event-sub-block:not([style*="top: 0%;"][style*="height: 100%;"])) .event-icon-overlay {
    display: none;
  }

  /* Current Time Line */

  .current-time-line {
    background-color: #ea4335;
    height: 2px;
  }
  .current-time-circle {
    background-color: #ea4335;
    width: 8px;
    height: 8px;
    left: -4px;
    top: -3px;
  }

  /* Default Event Styles */

  .event-sub-block:after {
    background-color: var(--color, var(--grid-primary-text-color));
  }