TailwindCSS Template Card - build custom cards with HTML and TailwindCSS

May you send me an entire card config as YAML so i can try to reproduce it here?
I tried, but i could not reproduce the flickering

Also, you are not using the “Always update” option, right? Just to confirm
The card would update on every state change of every entity on Home Assistant (i.e. every time the object hass is set on the element), that’s the intention of this option

This is one of the cards that keep closing. I’ve put the card in a clean view but refreshes don’t stop. There’s no binding and no action involved (yet).

entity: ''
content: |-
  <div class="drawer drawer-end z-20">
    <input id="my-drawer-4" type="checkbox" class="drawer-toggle" />
    <div class="drawer-content">
      <!-- Page content here -->
      <label for="my-drawer-4" class="drawer-button btn btn-primary fixed bottom-40 right-0 mb-4
  mr-4 z-0">Kamers</label>
    </div> 
    <div class="drawer-side">
      <label for="my-drawer-4" class="drawer-overlay"></label>

      <ul class="menu menu-lg bg-base-200 w-56 h-full rounded-box text-base-content">
      <li class="menu-title">Kies een kamer</li>
      

  <li>
    <details closed>
      <summary class="">Kelder</summary>
      <ul>
        <li><a>Kapsalon</a></li>
        <li><a>Toilet</a></li>
        <li><a>Spoelruimte</a></li>
        <li><a>Wachtruimte</a></li>
        <li><a>Knipruimte</a></li>
        <li><a>Technische ruimte</a></li>
        <li><a>Hal</a></li>
        <li><a>Trappenhal</a></li>
        <li>
          <details open>
            <summary>Privé</summary>
            <ul>
              <li><a>level 3 item 1</a></li>
              <li><a>level 3 item 2</a></li>
            </ul>
          </details>
        </li>
      </ul>
    </details>
  </li>
  <li>
    <details open>
      <summary class="">Begane grond</summary>
      <ul>
        <li><a>Toilet</a></li>
        <li><a>Bureau</a></li>
        <li><a>Badkamer</a></li>
        <li><a>Living</a></li>
        <li><a>Keuken</a></li>
        <li><a>Berging</a></li>
        <li><a>Trappenhal</a></li>
        <li><a>Inkomhal</a></li>
        <li>
          <details open>
            <summary>Parent</summary>
            <ul>
              <li><a>level 3 item 1</a></li>
              <li><a>level 3 item 2</a></li>
            </ul>
          </details>
        </li>
      </ul>
    </details>
  </li>
  <li>
    <details open>
      <summary class="menu-title">Eerste verdieping</summary>
      <ul>
        <li><a>Badkamer</a></li>
      </ul>
    </details open>
  </li>
  <li><a>Item 3</a></li>
  </ul>
      
    </div>
  </div>
ignore_line_breaks: true
always_update: false
parse_jinja: true
code_editor: Ace
entities: []
bindings: []
actions: []
debounceChangePeriod: 500
plugins:
  daisyui:
    enabled: true
    url: https://cdn.jsdelivr.net/npm/daisyui@latest/dist/full.css
    theme: inherit
type: custom:tailwindcss-template-card

There’s another small bug in the editor. At binding it shows text as binding type, but if you check code it is blank as in ’ '. Selecting it again doesn’t change it to text, you need to go to text editor and type it manually.

Another question guys. I feel like repeating myself to much in the code using binding and actions. Is there a way to write script that would let us getelements by id,class,tags? Is there a better way to write the following tab? The line using document.getelementbyid isn’t working. The action part can be discarded and has no use.

For example:

entity: ' '
content: |
  <div class="tabs tabs-boxed">
    <a href="#tab1" id="btnTab1" class="tab" >Tab 1</a> 
    <a href="#tab2" id="btnTab2" class="tab" >Tab 2</a> 
    <a href="#tab3" id="btnTab3" class="tab" >Tab 3</a>
  </div> 

  <div id="contentTab1" class="hidden" >
    <p>tab 1</p>
  </div>

  <div id="contentTab2" class="hidden">
    <p>tab 2</p>
  </div>

  <div id="contentTab3" class="hidden">
    <p>tab 3</p>
  </div>
ignore_line_breaks: true
always_update: false
parse_jinja: true
code_editor: Textarea
entities: []
bindings:
  - type: class
    selector: div#contentTab1
    bind: |
      let hash = window.location.hash;
      if (window.location.hash =="#tab1") {
        // Fragment exists
        this.classList.remove("hidden");
      this.classList.add("visible");
      console.log("window.location.hash : " +  hash);
      } else {
        // Fragment doesn't exist
        this.classList.remove("visible");
      this.classList.add("hidden");
       console.log("no  window.location.hash" );
      }
  - type: class
    selector: div#contentTab2
    bind: |
      let hash = window.location.hash;
      if (window.location.hash =="#tab2") {
        // Fragment exists
        this.classList.remove("hidden");
      this.classList.add("visible");
      console.log("window.location.hash : " +  hash);
      } else {
        // Fragment doesn't exist
        this.classList.remove("visible");
      this.classList.add("hidden");
       console.log("no  window.location.hash" );
      }
  - type: class
    selector: div#contentTab3
    bind: |
      let hash = window.location.hash;
      if (window.location.hash =="#tab3") {
        // Fragment exists
        this.classList.remove("hidden");
      this.classList.add("visible");
      console.log("window.location.hash : " +  hash);
      } else {
        // Fragment doesn't exist
        this.classList.remove("visible");
      this.classList.add("hidden");
       console.log("no  window.location.hash" );
      }
  - type: class
    selector: a#btnTab1
    bind: |
      let hash = window.location.hash;
      if (window.location.hash =="#tab1") {
        // Fragment exists
        
      this.classList.add("tab-active");
      console.log("window.location.hash : " +  hash);
      } else {
        // Fragment doesn't exist
        this.classList.remove("tab-active");

       console.log("no  window.location.hash" );
      }
  - type: class
    selector: a#btnTab2
    bind: |
      let hash = window.location.hash;
      if (window.location.hash =="#tab2") {
        // Fragment exists
        
      this.classList.add("tab-active");
      console.log("window.location.hash : " +  hash);
      } else {
        // Fragment doesn't exist
        this.classList.remove("tab-active");

       console.log("no  window.location.hash" );
      }
  - type: class
    selector: a#btnTab3
    bind: |
      let hash = window.location.hash;
      if (window.location.hash =="#tab3") {
        // Fragment exists
        
      this.classList.add("tab-active");
      console.log("window.location.hash : " +  hash);
      } else {
        // Fragment doesn't exist
        this.classList.remove("tab-active");

       console.log("no  window.location.hash" );
      }
actions:
  - type: click
    selector: a#btnTab1
    call: >-
      this.classList.add("tab-active");

      document.getElementById("contentTab1").classList.remove("bg-teal-500").add("bg-red-500");
  - type: click
    selector: a#btnTab2
    call: this.classList.add("tab-active");
  - type: click
    selector: a#btnTab3
    call: this.classList.add("tab-active");
debounceChangePeriod: 500
plugins:
  daisyui:
    enabled: true
    url: https://cdn.jsdelivr.net/npm/daisyui@latest/dist/full.css
    theme: inherit
type: custom:tailwindcss-template-card

This is quite awesome! Thanks!

I used this with feedparser to display a rss feed from my main news source.

If anyone is curious to how i set it up

In my configuration.yaml file i have this:

sensor:
  - platform: feedparser
    name: nrk_toppsaker
    feed_url: "https://www.nrk.no/nyheter/siste.rss"
    scan_interval:
      minutes: 1
    inclusions:
      - title
      - link
      - summary
      - published
      - media_content

and the HTML content for the card is like this:

<div class="w-full max-w-[90%] flex flex-col   mx-auto">
  <div class="w-full flex flex-col items-center relative">
    <svg
      viewBox="0 0 35 14"
      class="nrk-logo-nrk"
      width="3.188em"
      height="2.875em"
      aria-hidden="true"
      focusable="false"
    >
      <path
        fill="#1767ce"
        d="M31.0878023 7.55918018c-.2517074-.42642875-.2661333-.68861697-.02338-1.10029556L34.399462 1h-4.6267395s-2.7797248 4.54205453-3.1597731 5.17316245c-.3764003.63027925-.3601505 1.02273292.0154208 1.67041405C27.0084185 8.49042897 29.7727225 13 29.7727225 13h4.6267395s-3.2751804-5.37353258-3.3116597-5.44081982M21.5941377 13h4.2077247V1h-4.2077247M18.9405537.77010873c-1.3163763 0-2.3817762 1.07539896-2.3817762 2.40310754 0 1.32687533 1.0653999 2.4056073 2.3817762 2.4056073 1.3185428 0 2.3884423-1.07873197 2.3884423-2.4056073 0-1.32770858-1.0698995-2.40310754-2.3884423-2.40310754M12.0635332 13h4.2052375V1h-4.2052375M0 13h4.19744416V1H0M9.50872938 2.92796177C9.29333553 1.82832914 8.33144198 1 7.17852938 1H4.47507953l2.71654925 12h4.56969912L9.50872938 2.92796177z"
      ></path>
    </svg>
    <h1 class="absolute mt-[28px] font-bold text-xl text-[#1767ce]">Toppsaker</h1>
  </div>
  <hr class="mt-5" />

  <div class="w-full flex flex-col gap-4 justify-center p-4 text-center items-center mt-1">
    {%- for id in state_attr('sensor.nrk_toppsaker','entries') -%}
    {%- if loop.index <= 10 -%}
    <div class="w-full flex flex-col gap-2">
      <h1 class="text-2xl text-blue-500 underline">
        <a href="{{ id.link }}">{{ id.title }}</a>
      </h1>
      <small class="text-xs font-semibold text-gray-600"
        >{{ id.published }}</small
      >
      <p class="text-gray-600">{{ id.summary }}</p>
      <hr />
    </div>
    {%- endif %}
    {% endfor %}
  </div>
</div>

@Dan-Levi that’s amazing!!! Thanks for sharing!

Did you find a solution for the constant refreshes?

Hi,

The constant refresh it’s a problem indeed and it’s holding me back from migrating a lot of cards to this.

@usernein I’m not sure, but I think the problem can be related to the needsRender function in the TailwindTemplateCard.tsx.

This function is defined like this:

needsRender () {
    if (
      !this._hass ||
      !this._oldHass ||
      !this._entitiesToWatch ||
      this._config.always_update ||
      this._oldConfig['bindings'] != this._config['bindings']
    )
      return true

    for (const entity_id of this._entitiesToWatch) {
      if (!this._hass.states[entity_id]) continue
      if (
        this._oldHass.states[entity_id] !== this._hass.states[entity_id] ||
        this._oldHass.states[entity_id].attributes !==
          this._hass.states[entity_id].attributes
      )
        return true
    }

    return false
  }

I guess the problem can be when comparing attributes objects here:

this._oldHass.states[entity_id].attributes !== this._hass.states[entity_id].attributes

I haven’t tried, but I guess attributes is an object and this will always evaluate to false and the function will return true.

What do you think?

I’m starting to play around with updating my dashboard using Tailwind. Like others, the refresh is a concern. In this particular example, for my front load washing machine, this card is on a sub-page that wouldn’t be active all the time.

I’ve attached a couple of examples of the card in action. When the status is “off”, the additional detail is not shown. When the machine is on, the status is updated to whatever the current cycle is according to the machine: Standby, Load Sensing, Washing, Rinsing, Spinning.

Looking at the code below, what suggestions do people have for streamlining / making it better?

Code
<div class="grid grid-cols-1 gap-x-6 gap-y-8 xl:gap-x-8">
  <div class="overflow-hidden rounded-xl border border-gray-200">
    {%- if states('sensor.front_load_washer') == 'off' -%}
    <div class="flex items-center gap-x-4 border-b border-gray-900/5 bg-gray-100 p-6">
    {%- else -%}
    <div class="flex items-center gap-x-4 border-b border-gray-900/5 bg-green-300 p-6">
    {%- endif -%}
        
      <svg class="h-12 w-12 flex-none rounded-lg bg-white object-cover ring-1 ring-gray-900/10 fill-green-800" data-name="Layer 1" id="Layer_1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><title/><path d="M6,3V29H26V3H6ZM25,4V8H7V4H25ZM7,28V9H25V28H7Z"/><circle cx="10" cy="6" r="1"/><circle cx="14" cy="6" r="1"/><circle cx="18" cy="6" r="1"/><circle cx="22" cy="6" r="1"/><path d="M16,11a7,7,0,1,0,7,7A7,7,0,0,0,16,11Zm0,13a6,6,0,1,1,6-6A6,6,0,0,1,16,24Z"/><path d="M16,14a4,4,0,1,0,4,4A4,4,0,0,0,16,14Zm0,7a3,3,0,1,1,3-3A3,3,0,0,1,16,21Z"/></svg>
      <div class="text-sm font-medium leading-6 text-gray-900">Washing Machine <br> {%- if states('sensor.front_load_washer') == 'off' -%} {{ states('sensor.front_load_washer') | title }} {%- else -%} {{ states('sensor.front_load_washer_run_state') }} {%- endif -%}</div>
    </div>
    {%- if states('sensor.front_load_washer') != 'off' -%}
    <dl class="-my-3 px-4 py-4 text-sm leading-6">
      <div class="flex grid grid-cols-2 py-3">
        <div class="relative flex items-center space-x-3 rounded-lg px-3 py-5 focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2 hover:border-gray-400">
          <div class="flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-600">
            <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
              <path stroke-linecap="round" stroke-linejoin="round" d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0111.186 0z" />
            </svg>
          </div>
          <div class="min-w-0 flex-1">
            <a href="#" class="focus:outline-none">
              <span class="absolute inset-0" aria-hidden="true"></span>
              <p class="text-sm font-medium text-gray-200">Selection</p>
              <p class="truncate text-sm text-gray-400">{{ states('sensor.front_load_washer_current_course') }}</p>
            </a>
          </div>
        </div>
        <div class="relative flex items-center space-x-3 rounded-lg px-3 py-5 focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2 hover:border-gray-400">
          <div class="flex h-10 w-10 items-center justify-center rounded-lg bg-green-600">
            <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
              <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
            </svg>
          </div>
          <div class="min-w-0 flex-1">
            <a href="#" class="focus:outline-none">
              <span class="absolute inset-0" aria-hidden="true"></span>
              <p class="text-sm font-medium text-gray-200">Time left</p>
              <p class="truncate text-sm text-gray-400">{{ states('sensor.front_load_washer_remaining_time') }}</p>
            </a>
          </div>
        </div>
      </div>
    </dl>
    {%- endif -%}
  </div>
</div>

Washing machine is off

Standby

Load Sensing

Washing

Hello, thanks for your patience! I did some fixes and i believe the version v3.1.1 fixes the constant refreshing. May you try it please?

Hi, I’m about to migrate all my cards to this. Thanks for the project!

An idea to expand the card forward (if that’s your goal: the card is good as is):

Just like button-card and config-template-card some reusable templating would be nice.

Update on some feedback:

The card is preloading the full.css from DaisyUI on page load, even if we uncheck it, which creates a lot of conflicts with my own classes.
The card pre-generates with a bunch of opinions which aren’t OK, IMO:

content: |-
  <div class="flex flex-row gap-2 justify-center">
    {% for color in ["primary", "secondary", "accent", "info", "warning", "error", "info"] %}
      <div class="w-12 h-12 bg-{{color}} rounded-lg cursor-pointer hover:translate-y-2 transition-all animate-bounce hover:animate-spin"></div>
    {% endfor %}
  </div>
ignore_line_breaks: true
always_update: false
parse_jinja: true
code_editor: Ace
entities: []
bindings: []
actions: []
debounceChangePeriod: 100
plugins:
  daisyui:
    enabled: true
    url: https://cdn.jsdelivr.net/npm/daisyui@latest/dist/full.css
    theme: dark - dark
    overrideCardBackground: false
  tailwindElements:
    enabled: false

… all of this should be optional and disabled by default. For example, it’s good to have the option to add DaisyUI, but it should be optional and disabled by default without having to specifically disable it.

IMO, the card should start like this

type: custom:tailwindcss-template-card
entity: ''
content: |-
entities: []
bindings: []
actions: []
1 Like

Wonderful work, thank you very much for this!
Here’s my contribution - a shopping list in a flex-wrap grid, that gives you an overview of what’s in it (in combination with the built-in shopping list that’s synced with my Alexa shopping list):

Capture

And this is the code (with a conditional card that hide the card completely if the list if empty)

  - type: conditional
    conditions:
      - condition: numeric_state
        entity: todo.alexa_shopping_list
        above: 0
    card:
      entity: ''
      content: |-
        <div class="font-bold text-2xl grid justify-items-center p-1">
          <div>
            Shopping List
          </div>
        </div>
        <div class="flex flex-wrap gap-2 justify-center p-2">
          {% set items = state_attr("calendar.alexa_shopping_list",'all_tasks') %}
        {% for i in range(0, items | count, 1) %}

            <div class="bg-accent rounded-lg p-3">
                <span style="font-size:22px">
                {{ items[i]}}
              </span>
            </div>
            {% endfor %}
          
        </div>
      ignore_line_breaks: true
      always_update: false
      parse_jinja: true
      code_editor: Ace
      entities: []
      bindings: []
      actions: []
      debounceChangePeriod: 100
      plugins:
        daisyui:
          enabled: true
          url: https://cdn.jsdelivr.net/npm/daisyui@latest/dist/full.css
          theme: dark - dark
          overrideCardBackground: false
        tailwindElements:
          enabled: false
      type: custom:tailwindcss-template-card
2 Likes

@silvio here is a sample card which I made for my front door lock. This handles javascript within the tailwind card. I know it is little messy as I duplicated code in multiple places, but at least I managed to interact with the other elements.

Screenshot 2023-11-21 at 18.58.32

On clicking the key button a popup is opened within the card. Hope this will help you a little.

The SVG is taken from freepriks.com

entity: lock.front_door_lock
    content: |-

      <div class="container relative text-sky-900 rounded-2xl bg-sky-300 p-3
       flex capitalize w-full gap-4 item-center justify-center" 
             style="
                   --mdc-icon-size: 30px;
                    --door-color: #0c4a6e;
                ">

            <div class="w-2/3 order-2" >
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1025 1117" fill="none" class="w-full h-40">
                 <g id="plant">
                     <path d="M223.422 958.559C223.422 958.559 199.478 930.626 221.944 912.882C221.944 912.882 238.355 902.948 251.736 903.754C251.736 903.754 232.775 935.549 238.91 938.711C245.044 941.874 257.483 901.76 257.483 901.76C257.483 901.76 294.828 898.138 306.842 903.914C306.842 903.914 274.915 930.04 281.64 931.596C288.364 933.151 322.848 907.507 322.848 907.507C322.848 907.507 357.606 913.423 355.752 934.626C353.898 955.83 341.923 962.377 336.658 961.813C331.393 961.25 300.602 949.496 299.931 953.808C299.26 958.121 317.795 972.217 330.775 972.875C330.775 972.875 306.27 999.9 289.983 992.101C273.698 984.302 272.307 971.939 262.155 967.301C252.003 962.665 243.337 962.676 247.528 967.405C251.719 972.134 270.093 976.73 275.899 987C281.705 997.27 286.914 1004.2 270.063 1001.66C253.213 999.125 225.824 986.796 224.622 972.677C223.422 958.559 223.422 958.559 223.422 958.559Z" fill="#5EA04E"/>
                     <path d="M338.675 935.199C338.675 935.199 244.269 931.56 223.422 958.559C223.422 958.559 218.81 972.369 204.622 984.943L202.411 990.969C202.411 990.969 218.768 973.72 224.622 972.677C224.622 972.677 215.469 939.229 338.675 935.199Z" fill="#5EA04E"/>
                     <path d="M204.622 984.943C204.622 984.943 184.359 999.929 182.951 1026.71L186.629 1027.03C186.629 1027.03 193.101 999.347 204.798 988.52C216.496 977.694 204.622 984.943 204.622 984.943Z" fill="#5EA04E"/>
                     <path d="M139.707 923.43C139.707 923.43 177.794 902.101 160.02 873.154C160.02 873.154 145.8 855.381 130.566 850.809C130.566 850.809 138.69 893.975 130.566 894.992C122.44 896.008 124.979 846.239 124.979 846.239C124.979 846.239 84.8602 826.941 69.1182 828.465C69.1182 828.465 94.0013 870.616 85.8773 869.6C77.7503 868.585 49.8193 825.927 49.8193 825.927C49.8193 825.927 8.68432 818.309 2.08332 842.685C-4.51968 867.061 6.14633 879.249 12.2393 880.773C18.3343 882.296 57.4363 881.789 56.4223 886.867C55.4063 891.945 28.9983 900.071 14.2723 895.5C14.2723 895.5 30.5222 935.62 51.8522 933.589C73.1802 931.557 79.7833 918.354 92.9873 917.338C106.19 916.322 115.84 919.877 109.237 923.432C102.635 926.987 80.2913 924.597 69.6263 933.663C58.9623 942.729 50.3282 948.315 70.1342 952.378C89.9412 956.441 125.488 953.902 132.599 938.667C139.707 923.43 139.707 923.43 139.707 923.43Z" fill="#82B378"/>
                     <path d="M20.8733 850.302C20.8733 850.302 127.519 884.834 139.707 923.43C139.707 923.43 139.198 940.697 149.863 960.502V968.119C149.863 968.119 138.69 942.22 132.597 938.665C132.597 938.665 156.465 905.148 20.8733 850.302Z" fill="#5EA04E"/>
                     <path d="M149.864 960.502C149.864 960.502 166.311 985.477 156.934 1015.88L152.704 1014.74C152.704 1014.74 156.81 981.257 148.206 964.415C139.601 947.575 149.864 960.502 149.864 960.502Z" fill="#5EA04E"/>
                     <path d="M200.301 894.014C200.301 894.014 150.095 865.899 173.525 827.742C173.525 827.742 192.268 804.311 212.351 798.288C212.351 798.288 201.64 855.188 212.351 856.527C223.062 857.866 219.715 792.262 219.715 792.262C219.715 792.262 272.6 766.824 293.351 768.831C293.351 768.831 260.55 824.394 271.26 823.055C281.971 821.716 318.789 765.485 318.789 765.485C318.789 765.485 373.013 755.444 381.715 787.576C390.417 819.709 376.36 835.775 368.326 837.783C360.294 839.792 308.748 839.123 310.087 845.816C311.426 852.51 346.235 863.22 365.649 857.196C365.649 857.196 344.227 910.081 316.112 907.403C287.997 904.725 279.294 887.32 261.888 885.981C244.482 884.642 231.764 889.329 240.467 894.014C249.169 898.7 278.624 895.55 292.682 907.5C306.74 919.452 318.12 926.816 292.013 932.171C265.906 937.526 219.045 934.18 209.674 914.097C200.301 894.014 200.301 894.014 200.301 894.014Z" fill="#5EA04E"/>
                     <path d="M356.947 797.617C356.947 797.617 216.368 843.138 200.301 894.014C200.301 894.014 200.971 916.775 186.912 942.882V952.923C186.912 952.923 201.64 918.782 209.674 914.097C209.674 914.097 178.211 869.914 356.947 797.617Z" fill="#5EA04E"/>
                     <path d="M186.912 942.882C186.912 942.882 165.233 975.804 177.594 1015.88L183.171 1014.38C183.171 1014.38 177.757 970.24 189.101 948.04C200.442 925.84 186.912 942.882 186.912 942.882Z" fill="#5EA04E"/>
                     <path d="M296.96 1004.43C296.96 1058.75 252.923 1102.79 198.606 1102.79C191.977 1102.79 185.49 1102.13 179.221 1100.87C134.193 1091.87 100.263 1052.11 100.263 1004.43H296.96Z" fill="#E58435"/>
                     <path d="M258.167 1004.43C258.167 1052.11 224.249 1091.86 179.221 1100.87C134.193 1091.87 100.263 1052.11 100.263 1004.43H258.167Z" fill="#F1A34F"/>
                     <path d="M298.311 1107.79H98.9003C98.9003 1104.43 100.263 1101.4 102.455 1099.21C104.657 1097.02 107.687 1095.65 111.036 1095.65H286.177C292.882 1095.65 298.311 1101.08 298.311 1107.79Z" fill="#E58435"/>
                     <path d="M173.782 1095.65H187.116C151.039 1081.08 136.682 1045.74 136.682 1004.43H112.249C112.249 1045.73 137.706 1081.08 173.782 1095.65Z" fill="#FFB973"/>
                     <path d="M204.918 1107.79H98.9003C98.9003 1104.43 100.263 1101.4 102.455 1099.21C104.657 1097.02 107.686 1095.65 111.035 1095.65H196.639C203.334 1095.65 204.918 1101.08 204.918 1107.79Z" fill="#F1A34F"/>
                     <path d="M297.685 1007.45H98.5383C97.7053 1007.45 97.0293 1006.78 97.0293 1005.94C97.0293 1005.11 97.7053 1004.43 98.5383 1004.43H297.685C298.518 1004.43 299.194 1005.11 299.194 1005.94C299.194 1006.78 298.518 1007.45 297.685 1007.45Z" fill="#D86713"/>
                 </g>

                 {%- if is_state('binary_sensor.front_door', 'off') -%}
                     <g id="closed">
                       <path d="M999.883 101H457V1116.67H999.883V101Z" fill="var(--door-color)"/>
                       <path d="M974.656 128.943H482.227V1087.1H974.656V128.943Z" fill="var(--door-color)"/>
                       <path d="M944.122 156.307H519.698V397.887H944.122V156.307Z" fill="#cffafe"/>
                       <path d="M944.122 436.498H519.698V678.078H944.122V436.498Z" fill="#cffafe"/>
                       <path d="M944.122 714.811H519.698V1039.89H944.122V714.811Z" fill="#cffafe"/>

                       {%- if is_state('lock.front_door_lock', 'locked') -%}
                         <g id="lock" class="animate-pulse">
                             <path d="M1022.47 696.379L1008.73 649.222C1007.56 645.216 1003.33 642.894 999.323 644.061L872.436 681.029C868.43 682.196 866.108 686.428 867.275 690.434L881.476 739.176C890.198 769.114 921.828 786.471 951.766 777.749L982.77 768.716C1013.58 759.74 1031.44 727.189 1022.47 696.379Z" fill="#D86713"/>
                             <path d="M1010.58 699.756L996.837 652.599C995.67 648.593 991.438 646.271 987.432 647.438L986.93 647.584C986.69 646.575 986.425 645.565 986.131 644.559C976.603 611.854 942.242 592.998 909.539 602.527C876.835 612.056 857.979 646.415 867.507 679.12C867.801 680.127 868.119 681.121 868.459 682.101L860.545 684.407C856.539 685.573 854.217 689.805 855.385 693.81L869.584 742.552C878.307 772.49 909.937 789.849 939.875 781.126L970.879 772.094C1001.69 763.118 1019.55 730.566 1010.58 699.756ZM878.779 675.835C871.06 649.346 886.333 621.516 912.822 613.799C939.312 606.081 967.141 621.354 974.859 647.842C975.153 648.852 975.403 649.863 975.631 650.876L879.756 678.809C879.405 677.832 879.072 676.845 878.779 675.835Z" fill="#F1A34F"/>
                             <path d="M962.32 746.101C962.32 748.462 960.772 750.644 958.4 751.327L958.33 751.348C958.32 751.348 958.32 751.358 958.309 751.358L957.767 751.508C957.756 751.508 957.746 751.519 957.736 751.519L956.932 751.759C956.922 751.759 956.922 751.759 956.912 751.759C956.902 751.759 956.882 751.759 956.871 751.77L951.866 753.237L951.415 753.368C951.284 753.409 951.153 753.438 951.013 753.458C951.013 753.458 951.003 753.469 950.992 753.469C950.862 753.52 950.711 753.57 950.569 753.61C947.625 754.464 944.519 752.766 943.665 749.821L939.655 736.043L939.634 735.982L939.394 736.052C939.383 736.052 939.373 736.052 939.373 736.052L938.117 736.423C935.242 737.258 932.207 735.6 931.362 732.726C930.528 729.851 932.197 726.806 935.072 725.971L936.579 725.529L934.91 719.791C927.081 719.609 919.935 714.425 917.635 706.505C914.779 696.726 920.398 686.484 930.177 683.641C931.082 683.379 931.987 683.188 932.88 683.067C933.242 683.018 933.595 682.988 933.945 682.958C942.468 682.294 950.559 687.661 953.05 696.185C954.498 701.16 953.754 706.245 951.392 710.425C949.996 712.898 948.036 715.049 945.615 716.666L947.292 722.456L948.769 727.52C948.79 727.56 948.799 727.6 948.81 727.641C948.83 727.702 948.84 727.772 948.86 727.831L952.87 741.6L955.352 740.877C955.433 740.857 955.503 740.837 955.583 740.816C955.754 740.776 955.925 740.746 956.086 740.715H956.107C956.337 740.675 956.569 740.665 956.8 740.665C958.287 740.644 959.695 741.237 960.72 742.253V742.263C961.353 742.886 961.835 743.669 962.097 744.585C962.249 745.085 962.32 745.598 962.32 746.101Z" fill="#C65406"/>
                             <path d="M962.099 744.583C961.837 743.667 961.355 742.884 960.722 742.261V742.251C959.697 741.235 958.289 740.643 956.802 740.663L955.635 741.005L955.585 740.814C955.505 740.835 955.435 740.855 955.354 740.875L952.872 741.598L948.862 727.829C948.842 727.769 948.832 727.699 948.812 727.639C948.801 727.599 948.792 727.558 948.771 727.518L947.294 722.454L945.617 716.664C948.038 715.047 949.999 712.895 951.394 710.423C953.756 706.242 954.5 701.157 953.052 696.183C950.561 687.66 942.47 682.292 933.947 682.956C933.596 682.986 933.244 683.017 932.882 683.065C923.145 685.93 917.556 696.151 920.4 705.91C922.703 713.83 929.847 719.016 937.676 719.197L939.345 724.935V724.946L937.838 725.378C934.963 726.222 933.295 729.257 934.129 732.132C934.832 734.503 937.013 736.052 939.375 736.052C939.375 736.052 939.385 736.052 939.396 736.052C939.487 736.052 939.566 736.052 939.657 736.042C940.059 736.021 940.481 735.951 940.884 735.831L942.402 735.388L946.431 749.228C947.145 751.681 949.427 753.269 951.869 753.238L956.874 751.771C956.885 751.76 956.905 751.76 956.915 751.76C956.925 751.76 956.925 751.76 956.935 751.76L957.739 751.52C957.749 751.52 957.76 751.509 957.77 751.509L958.312 751.359C958.323 751.359 958.323 751.349 958.333 751.349L958.403 751.328C960.775 750.645 962.323 748.464 962.323 746.102C962.32 745.598 962.249 745.085 962.099 744.583Z" fill="#D86713"/>
                         </g>
                       {%- endif -%}
                       <path d="M921.93 542.909H915.901C909.268 542.909 903.841 548.337 903.841 554.969V631.85C903.841 638.484 909.268 643.911 915.901 643.911H921.93C928.563 643.911 933.99 638.484 933.99 631.85V554.969C933.989 548.336 928.562 542.909 921.93 542.909Z" fill="#74ACBE"/>
                     </g>
                  {%- else -%}
                      <g id="open">
                           <path d="M999.927 106.592H434.112V1108.32H999.927V106.592Z" fill="#a5f3fc"/>
                           <path d="M813.143 106.592H443.311V1108.32H813.143V106.592Z" fill="#7dd3fc"/>
                           <path d="M188.123 0V1113.38H434.112V95.697L188.123 0Z" fill="var(--door-color)"/>
                           <path d="M188.123 0H163.843V1113.38H188.123V0Z" fill="var(--door-color)"/>
                           <path d="M976.992 1031.05H457.038V1116.54H976.992V1031.05Z" fill="#599DAD"/>
                           <path d="M977.003 945.566H568.661V1031.05H977.003V945.566Z" fill="#5FABB7"/>
                           <path d="M977.002 860.078H673.686V945.566H977.003L977.002 860.078Z" fill="#6BB7BF"/>
                           <path d="M977.002 774.589H778.8V860.077L977.002 860.078V774.589Z" fill="#79C8CC"/>
                           <path d="M434.112 97.0979V1113.38L999.927 1113.38V97.0979H434.112ZM976.996 1089.36H457.043V121.123H976.996V1089.36Z" fill="var(--door-color)"/>
                           <path d="M237.52 539.618H231.491C224.858 539.618 219.43 545.046 219.43 551.678V628.559C219.43 635.193 224.858 640.62 231.491 640.62H237.52C244.153 640.62 249.581 635.193 249.581 628.559V551.678C249.58 545.045 244.153 539.618 237.52 539.618Z" fill="var(--door-color)"/>
                       </g>
                  {%- endif -%}
               </svg>
            </div>

           
            <div class="order-1 w-1/3 flex flex-col gap-5 justify-center items-center">  
             {%- if is_state('lock.front_door_lock', 'locked') or is_state('binary_sensor.front_door', 'off') -%}
                <button onClick="this.closest('.container').querySelector('.popup').classList.replace('scale-0', 'scale-1')"
                  class="order-1 h-12 w-12 bg-sky-900 text-sky-200 rounded-full ring-4 ring-sky-900 disabled:opacity-25 active:ring-0"
                  >
                  <ha-icon icon="mdi:key"></ha-icon>
                </button>
             {%- endif -%}
             
             {%- if is_state('lock.front_door_lock', 'unlocked') -%}
                <button onClick="hass.callService('lock', 'lock', {entity_id: 'lock.front_door_lock'})" class="order-2 h-12 w-12 bg-sky-900 text-sky-200 rounded-full ring-4 ring-sky-900 active:ring-0 disabled:opacity-25" {{ 'disabled' if is_state('binary_sensor.front_door', 'on') }}>
                  <ha-icon icon="mdi:lock"></ha-icon>
                </button>
             {%- endif -%}
            </div>


           <div class="popup scale-0 transition-transform duration-200 ease-in-out absolute w-full h-full top-0 left-0 p-3 flex items-center justify-center">
             <div class="z-10 flex justify-between w-3/4">
               <button onClick="this.closest('.popup').classList.replace('scale-1', 'scale-0')" class="h-12 w-12 bg-red-900 text-red-200 rounded-full ring-4 ring-red-900 active:ring-0">
                  <ha-icon icon="mdi:close"></ha-icon>
                </button>
               <button onClick="hass.callService('lock', 'open', {entity_id: 'lock.front_door_lock'});this.closest('.popup').classList.replace('scale-1', 'scale-0')" class="h-12 w-12 bg-green-900 text-green-200 rounded-full ring-4 ring-green-900 active:ring-0">
                  <ha-icon icon="mdi:check"></ha-icon>
                </button>
             </div>
            <div class="z-0 absolute rounded-xl w-full h-full bg-gray-900 opacity-95 top-0 left-0"></div>
           </div>
       </div>
    ignore_line_breaks: true
    always_update: false
    parse_jinja: true
    code_editor: Ace
    entities: []
    bindings: []
    actions: []
    debounceChangePeriod: 100
    plugins:
      daisyui:
        enabled: true
        url: https://cdn.jsdelivr.net/npm/daisyui@latest/dist/full.css
        theme: dark - dark
        overrideCardBackground: false
      tailwindElements:
        enabled: false
    type: custom:tailwindcss-template-card

@usernein Great work with this card. I discovered that the user variable is not available inside the tailwindcss-template-card though, and that’s super useful for customising cards depending on the logged-in user. I opened an issue here but have since done some investigation and I think the fix is simple. I’ve created a fork and made the change but I’m not easily able to test it - would you be open to an untested PR?

1 Like

Great, card! Just what i was looking for my homekit clone.

entity: light.office
content: |-
  {% set device = 'light.office' %}
  <button 
      id="card"
      data-status="{{ states(device) }}"
      data-type="{{ device.split('.')[0] }}"
      class="group relative w-full cursor-pointer font-sans flex flex-col items-start p-2.5 gap-2.5 bg-[rgba(56,56,56,0.5)] data-[status=on]:bg-[rgba(255,255,255,0.9)] duration-500 rounded-[18px] "
    >
    <div class="flex gap-[11px]">
      <i id="icon" class="rounded-full active:scale-90 bg-[#2F2F2F] min-w-[42px] min-h-[42px] grid place-items-center group-data-[status=on]:bg-[#FFCC02] duration-500">
        <ha-icon class="pointer-events-none w-fit h-fit group-data-[type=light]:text-[#FFCC02] group-data-[status=on]:text-white duration-500" icon="{{ states[device].attributes.icon }}"/>
      </i>
      <dl class="pointer-events-none flex flex-col items-start justify-center gap-1">
        <dt id="name" class="text-[13px] font-[600] leading-none group-data-[status=on]:!text-black duration-500 name"/>
        <dd id="state" class="text-[13px] text-[#AFAFAF] group-data-[status=on]:text-[#818185] leading-none -mt-px first-letter:uppercase duration-500 status"/>
      </dl>
    </div>
    </div>
    </button>

The only thing that is missing for me is to be able to use something like:

data-status="{{ states(config.entity) }}"

Instead of:

data-status="{{ states('light.office') }}"

Is there any plan to implement this? Or is there any way to get a similar result? It would be amazing to use this with the auto entities card.

Hey silvio! How did you manage to get jQuery running in Home Assistant. I’m searching for a solution…

would you be open to an untested PR?

Sure! I could test it here from my side

Hi guys, any advice on how to make a complete dashboard out of it, kind of a website? Issue with HA is, that everything is in that damn grid…

Hi,
how can i make tailwind CSS card background transparent? Cannot figure it out using card-mod. All options I used doesn’t work.

image

Found a solution, must to add !important; by the end of each change in card-mod.