Bubble Card is a minimalist and customizable card collection for Home Assistant with a nice pop-up touch.
Installation
Configuration
Pop-up
Horizontal buttons stack
Button
Media player
Cover
Select
Climate
Separator
Empty column
Sub-buttons
Card layouts
Actions
Styling
Templates
Conflicts
Help
Donate
Home Assistant lowest supported version: 2023.9.0
Without HACS
- Download these files: bubble-card.js and bubble-pop-up-fix.js
- Add these files to your
<config>/www
folder - On your dashboard click on the icon at the right top corner then on
Edit dashboard
- Click again on that icon and then click on
Manage resources
- Click on
Add resource
- Copy and paste this:
/local/bubble-card.js?v=1
- Click on
JavaScript Module
thenCreate
- Go back and refresh your page
- You can now click on
Add card
in the bottom right corner and search forBubble Card
- After any update of the file you will have to edit
/local/bubble-card.js?v=1
and change the version to any higher number
If it's not working, just try to clear your browser cache.`
With HACS (Recommended)
This method allows you to get updates directly on the HACS main page
- If HACS is not installed yet, download it following the instructions on https://hacs.xyz/docs/setup/download/
- Proceed to the HACS initial configuration following the instructions on https://hacs.xyz/docs/configuration/basic
- On your sidebar go to
HACS
>Frontend
- Click on the
+
button at the bottom right corner - Now search for
Bubble Card
and then click on the button at the bottom right corner to download it - Go back on your dashboard and click on the icon at the right top corner then on
Edit dashboard
- You can now click on
Add card
in the bottom right corner and search forBubble Card
If it's not working, try to clear your browser cache.
You can also take a look at my YouTube channel for step by step videos.
All options can be configured in the Home Assistant editor. But you can find more details and the YAML in the documentation below.
Main options (YAML + description)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
type |
string | Required | custom:bubble-card |
Type of the card |
card_type |
string | Required | button , cover , empty-column , horizontal-buttons-stack , media-player , pop-up , select or separator |
Type of the Bubble Card, see below |
styles |
object list | Optional | Any CSS stylesheets | Allows you to customize your Bubble Card CSS, see styling |
Global CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-border-radius |
px |
Border radius for all supported elements |
--bubble-main-background-color |
color |
Main background color for all supported elements |
--bubble-secondary-background-color |
color |
Secondary background color for all supported elements |
--bubble-accent-color |
color |
Accent color for all supported elements |
--bubble-icon-border-radius |
px |
Icon border radius for all supported elements |
--bubble-icon-background-color |
color |
Icon background color for all supported elements |
--bubble-sub-button-border-radius |
px |
Border radius for all sub-buttons |
--bubble-sub-button-background-color |
color |
Background color for all sub-buttons |
--bubble-box-shadow |
see box shadow | Box shadow for all supported elements |
Check out this video to learn about Bubble Card and its capabilities. My YouTube channel is quite new and focuses on tutorials about Home Assistant and Bubble Card. Don’t hesitate to subscribe to help increase my channel’s visibility. Thank you in advance!
This card allows you to convert any vertical stack into a pop-up. Each pop-up is hidden by default and can be opened by targeting its link (e.g. '#pop-up-name'
), with any card that supports the navigate
action, or with the horizontal buttons stack that is included.
Important
This card must be placed within a vertical stack card at the topmost position to function properly. To avoid misalignment with your view, place vertical stacks/pop-ups after all other dashboard cards. It should be called from the same view to work.
How to create a pop-up in the editor
- To add a pop-up you first need to add a
Vertical stack
card to your dashboard. - This
Vertical stack
must be after all your other cards in your view order and before your horizontal buttons stack if you have one. In a section view type it can be placed anywhere. - Now add a
Bubble Card
with thePop-up
type. - Just fill in the
Hash
input and the ones you need.
You can also watch this step by step video.
Tip
This feature allows you to open a pop-up based on the state of any entity, for example, you can open a "Security" pop-up with a camera when a person is in front of your house. You can also create a toggle helper (input_boolean) and trigger its opening/closing in an automation.
Opening a pop-up when a binary_sensor
is on
type: custom:bubble-card
card_type: pop-up
hash: '#kitchen'
name: Security
icon: mdi:video
trigger_entity: binary_sensor.front_door_motion
trigger_state: 'on'
trigger_close: true
They are many ways to close a pop-up. For instance, you can swipe from the pop-up header to the bottom, by doing a long swipe inside the pop-up to the bottom, by pressing Escape on desktop, by removing the hash in the URL or by simply pressing the close button.
If you notice that pop-up content appears upon page load, consider installing this fix as an additional module.
Installation
You can do this by adding bubble-pop-up-fix.js
to your configuration.yaml
like so:
frontend:
extra_module_url:
- /hacsfiles/Bubble-Card/bubble-pop-up-fix.js
If you didn't install it with HACS, change the path accordingly. Then, clear your browser cache.
For Android Home Assistant Companion App users, you can close the app, then clear the app cache. If it's still not working, you can close and restart the app again.
For iOS Home Assistant Companion App users, you can go to your Home Assistant settings, then navigate to Companion App > Debug > Clear Frontend Cache (or something similar), then refresh the page or restart the app.
For previous users of the Optimized mode, you will need to replace your type: custom:bubble-pop-up
with this in YAML mode:
type: vertical-stack
cards:
- type: custom:bubble-card
card_type: pop-up
Options (YAML + descriptions)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
hash |
string | Required | Any unique hash (e.g. '#kitchen' ) with ' ' |
This is how you will open your pop-up |
auto_close |
string | Optional | A timeout in milliseconds (e.g. 10000 for 10s) |
Auto close the pop-up after a timeout |
close_on_click |
boolean | Optional | true or false (default) |
Automatically close the pop-up after any interaction |
close_by_clicking_outside |
boolean | Optional | true (default) or false |
Close the pop-up by clicking outside of it |
width_desktop |
string | Optional | Any CSS value | Width on desktop (100% by default on mobile) |
margin |
string | Optional | Any CSS value | Use this only if your pop-up is not well centered on mobile (e.g. 13px ) |
margin_top_mobile |
string | Optional | Any CSS value | Top margin on mobile (e.g. -56px if your header is hidden) |
margin_top_desktop |
string | Optional | Any CSS value | Top margin on desktop (e.g. 50vh for a half-sized pop-up or calc(100vh - 400px) for a fixed height of 400px ) |
bg_color |
string | Optional | Any hex, rgb or rgba value | The background color of your pop-up (e.g. #ffffff for a white background) |
bg_opacity |
string | Optional | Any value from 0 to 100 |
The background opacity of your pop-up (e.g. 100 for no transparency) |
bg_blur |
string | Optional | Any value from 0 to 100 |
The background blur effect of your pop-up, this only work if bg_opacity is not set to 100 (e.g. 0 for no blur) |
shadow_opacity |
string | Optional | Any value from 0 to 100 |
The shadow opacity of your pop-up (e.g. 0 to hide it) |
hide_backdrop |
boolean | Optional | true or false (default) |
Set this to true on the first pop-up of your main dashboard to disable the backdrop on all pop-ups. |
background_update |
boolean | Optional | true or false (default) |
Update pop-up content in background (not recommended) |
trigger_entity |
string | Optional | Any entity | Open this pop-up based on the state of any entity |
trigger_state |
string | Optional (Required if trigger_entity is defined) |
Any entity state | Entity state to open the pop-up |
trigger_close |
boolean | Optional | true or false (default) |
Close the pop-up when trigger_state is different |
open_action |
object | Optional | See actions | Trigger an action when the pop-up is opening |
close_action |
object | Optional | See actions | Trigger an action when the pop-up is closing |
card_layout |
string | Optional | normal (default), large , large-2-rows |
Styling layout of the header, see card layouts |
show_header |
boolean | Optional | true or false (default) |
Show/Hide the pop-up header fully |
You also have access to all the button settings for the header of the pop-up. | Optional | If undefined no header will be shown |
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-pop-up-border-radius |
px |
Border radius for the pop-up |
--bubble-pop-up-main-background-color |
color |
Main background color for supported elements of the pop-up |
--bubble-pop-up-background-color |
color |
Background color of the pop-up |
--bubble-backdrop-background-color |
color |
Background color for the backdrop |
You also have access to all the button CSS variables for the header of the pop-up. |
A pop-up
type: vertical-stack
cards:
- type: custom:bubble-card
card_type: pop-up
hash: '#kitchen'
name: Kitchen
icon: mdi:fridge
entity: light.kitchen
A button to open the pop-up
type: custom:bubble-card
card_type: button
button_type: name
name: Kitchen
icon: mdi:fridge
button_action:
tap_action:
action: navigate
navigation_path: '#kitchen'
A pop-up with a fixed height
Replace 400px
with the size you need.
type: vertical-stack
cards:
- type: custom:bubble-card
card_type: pop-up
hash: '#kitchen'
name: Kitchen
icon: mdi:fridge
entity: light.kitchen
margin_top_mobile: calc(100vh - 400px)
margin_top_desktop: calc(100vh - 400px)
This card is a good companion to the pop-up card, allowing you to open the corresponding pop-ups. It also allows you to open any page of your dashboard. In addition, you can add your motion/occupancy sensors so that the order of the buttons adapts according to the room you just entered. This card is scrollable, remains visible, and acts as a footer.
Important
This card has to be the last one in your view (after every card and pop-up). It can't be inside any stack.
Options (YAML + descriptions)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
1_link |
string | Required | The pop-up hash (e.g. '#kitchen' ) with ' ' or any link |
A link to open |
1_name |
string | Optional | Any string | A name for your button |
1_icon |
string | Optional | Any mdi: icon |
An icon for your button |
1_entity |
string | Optional | Any light or light group | Display the color of that light in background |
1_pir_sensor |
string | Optional | Any binary sensor | At least one pir sensor or more for auto_order , in fact it also works with any entity type, for example you can add light groups and the order will change based on the last changed states. |
auto_order |
boolean | Optional | true or false (default) |
Change the order of the buttons according to the _pir_sensor last changed time, it needs to be false if you don't have any _pir_sensor in your code |
margin |
string | Optional | Any CSS value | Use this only if your horizontal-buttons-stack is not well centered on mobile (e.g. 13px ) |
width_desktop |
string | Optional | Any CSS value | Width on desktop (100% by default on mobile) |
is_sidebar_hidden |
boolean | Optional | true or false (default) |
Fix the horizontal buttons stack position if the sidebar is hidden on the desktop (only if you have made a modification to hide it yourself) |
rise_animation |
boolean | Optional | true (default) or false |
Set this to false to disable the animation that activates once the page has loaded |
highlight_current_view |
boolean | Optional | true or false (default) |
Highlight current hash / view with a smooth animation |
hide_gradient |
boolean | Optional | true or false (default) |
Set this to false to hide the gradient |
[!IMPORTANT]
The variables starting with a number define your buttons, just change this number to add more buttons (see example below).
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-horizontal-buttons-stack-border-radius |
px |
Border radius for horizontal button stack buttons |
--bubble-horizontal-buttons-stack-background-color |
color |
Background color for horizontal button stack buttons |
An horizontal buttons stack that reorganize itself based on occupancy sensors
type: custom:bubble-card
card_type: horizontal-buttons-stack
auto_order: true
1_name: Living room
1_icon: mdi:sofa
1_link: '#living-room'
1_entity: light.living_room
1_pir_sensor: binary_sensor.living_room_motion
2_name: Kitchen
2_icon: mdi:fridge
2_link: '#kitchen'
2_entity: light.kitchen
2_pir_sensor: binary_sensor.kitchen_motion
3_name: Dining room
3_icon: mdi:silverware-fork-knife
3_link: '#dining-room'
3_entity: light.dining_room
3_pir_sensor: binary_sensor.dining_room_motion
This card allows you to control your entities and can be customized in many ways. To access color / control of an entity, simply tap on the icon.
Tip
-
The button switch is the default one. By default, it toggles an entity and its background color changes based on the entity’s state or the color of a light.
-
The button slider can control the brightness of a light, the volume of a media player, the position of a cover, and it also supports input numbers. Its background color can change based on the color of a light.
-
The button state is perfect for displaying information from a sensor or any entity. When you press it, it will show the "More info" panel of the entity. Its background color does not change.
-
The button name is the only one that doesn't need an entity and allows you to display a short text, a name or a title. You can also add some actions to it. Its background color does not change.
Options (YAML + descriptions)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
entity |
string | Required | Any entity | An entity to control |
button_type |
string | Optional | switch (default), slider , state or name |
The behavior of your button |
name |
string | Optional | Any string | A name for your button, if not defined it will display the entity name |
icon |
string | Optional | Any mdi: icon |
An icon for your button, if not defined it will display the entity icon or the entity-picture |
force_icon |
boolean | Optional | true or false (default) |
Give the priority to the icon instead of the entity-picture |
show_state |
boolean | Optional | true or false (default) |
Show or hide the state of your entity |
show_name |
boolean | Optional | true (default) or false |
Show or hide the name |
show_icon |
boolean | Optional | true (default) or false |
Show or hide the icon |
show_last_changed |
boolean | Optional | true or false (default) |
Show the last changed time of your entity |
show_attribute |
boolean | Optional | true or false (default) |
Show an attribute of your entity below its name |
attribute |
string | Optional (required if show_attribute is set to true ) |
An attribute from your entity |
The attribute to show (e.g. brightness ) |
scrolling_effect |
boolean | Optional | true (default) or false |
Allow text to scroll when the content exceeds the size of their container |
button_action |
object | Optional | tap_action , double_tap_action or hold_action , see below |
Allow to change the default actions on button click. Not available for the slider type. |
tap_action |
object | Optional | See actions | Define the type of action on icon click, if undefined, more-info will be used. |
double_tap_action |
object | Optional | See actions | Define the type of action on icon double click, if undefined, toggle will be used. |
hold_action |
object | Optional | See actions | Define the type of action on icon hold, if undefined, more-info will be used. |
card_layout |
string | Optional | normal (default), large , large-2-rows |
Styling layout of the card, see card layouts |
columns |
string | Optional | 1 , 2 , 3 or 4 (default) |
Number of columns when placed in a section view (e.g. 2 is 2/4) |
rows |
string | Optional | 1 (default), 2 , 3 or 4 |
Number of rows when placed in a section view (e.g. 2 is 2/4) |
sub_button |
object | Optional | See sub-buttons | Add customized buttons fixed to the right |
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-button-main-background-color |
color |
Main background color for supported elements in the button |
--bubble-button-border-radius |
px |
Border radius for the button |
--bubble-button-icon-border-radius |
px |
Border radius for the button icon container |
--bubble-button-icon-background-color |
color |
Background color for the button icon container |
--bubble-button-box-shadow |
See box shadow | Box shadow for the button |
A slider button that can control the brightness of a light
type: custom:bubble-card
card_type: button
button_type: slider
entity: light.kitchen_led
name: Kitchen LED
icon: mdi:led-strip-variant
A button with all the options
entity: light.your_light
button_type: switch
show_icon: true
force_icon: true
show_name: true
show_last_changed: true
show_state: true
show_last_updated: true
show_attribute: true
attribute: brightness
scrolling_effect: true
card_layout: large
button_action:
tap_action:
action: toggle
tap_action:
action: more-info
sub_button:
- entity: light.your_light
icon: ''
show_state: false
show_attribute: true
attribute: brightness
show_icon: false
show_background: false
show_name: false
This card allows you to control a media player. You can tap on the icon to get more control.
Options (YAML + descriptions)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
entity |
string | Required | Any media player | The media player to control |
name |
string | Optional | Any string | A name for your media player, if not defined it will display the entity name |
icon |
string | Optional | Any mdi: icon |
An icon for your media player, if not defined it will display the entity icon or the entity-picture |
force_icon |
boolean | Optional | true or false (default) |
Give the priority to the icon instead of the entity-picture |
show_state |
boolean | Optional | true or false (default) |
Show or hide the state of your entity |
show_name |
boolean | Optional | true (default) or false |
Show or hide the name |
show_icon |
boolean | Optional | true (default) or false |
Show or hide the icon |
show_last_changed |
boolean | Optional | true or false (default) |
Show the last changed time of your entity |
show_attribute |
boolean | Optional | true or false (default) |
Show an attribute of your entity below its name |
attribute |
string | Optional (required if show_attribute is set to true ) |
An attribute from your entity |
The attribute to show (e.g. brightness ) |
scrolling_effect |
boolean | Optional | true (default) or false |
Allow text to scroll when the content exceeds the size of their container |
tap_action |
object | Optional | See actions | Define the type of action on icon click, if undefined, more-info will be used. |
double_tap_action |
object | Optional | See actions | Define the type of action on icon double click, if undefined, toggle will be used. |
hold_action |
object | Optional | See actions | Define the type of action on icon hold, if undefined, more-info will be used. |
card_layout |
string | Optional | normal (default), large , large-2-rows |
Styling layout of the card, see card layouts |
columns |
string | Optional | 1 , 2 , 3 or 4 (default) |
Number of columns when placed in a section view (e.g. 2 is 2/4) |
rows |
string | Optional | 1 (default), 2 , 3 or 4 |
Number of rows when placed in a section view |
sub_button |
object | Optional | See sub-buttons | Add customized buttons fixed to the right |
hide |
object | Optional | See below | Show or hide buttons |
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
play_pause_button |
boolean | Optional | true (default) or false |
Show or hide the play/pause button |
volume_button |
boolean | Optional | true (default) or false |
Show or hide the volume button |
previous_button |
boolean | Optional | true (default) or false |
Show or hide the previous button |
next_button |
boolean | Optional | true (default) or false |
Show or hide the next button |
power_button |
boolean | Optional | true (default) or false |
Show or hide the power button |
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-media-player-main-background-color |
color |
Main background color for the media player |
--bubble-media-player-border-radius |
px |
Border radius for the media player |
--bubble-media-player-buttons-border-radius |
px |
Border radius for the media player buttons |
--bubble-media-player-slider-background-color |
color |
Background color for the volume slider |
--bubble-media-player-icon-border-radius |
px |
Border radius for the media player icon container |
--bubble-media-player-icon-background-color |
color |
Background color for the media player icon container |
--bubble-media-player-box-shadow |
See box shadow | Box shadow for the media player |
A media player with all the options
type: custom:bubble-card
card_type: media-player
name: Media player
entity: media_player.your_media_player
show_state: true
show_last_updated: true
show_attribute: true
attribute: assumed_state
card_layout: large
scrolling_effect: false
show_icon: false
force_icon: true
show_name: false
show_last_changed: true
columns: 2
rows: 1
tap_action:
action: toggle
hide:
play_pause_button: true
volume_button: true
previous_button: true
next_button: true
power_button: true
sub_button:
- entity: media_player.salon_2
icon: mdi:volume-high
name: Volume level
tap_action:
action: more-info
show_name: false
show_state: false
show_last_updated: false
show_attribute: true
show_background: false
attribute: volume_level
This card allows you to control your cover
entities.
Options (YAML + descriptions)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
entity |
string | Required | Any cover | A cover to control |
name |
string | Optional | Any string | A name for your cover, if not defined it will display the entity name |
force_icon |
boolean | Optional | true or false (default) |
Give the priority to the icon instead of the entity-picture |
show_state |
boolean | Optional | true or false (default) |
Show or hide the state of your entity |
show_name |
boolean | Optional | true (default) or false |
Show or hide the name |
show_icon |
boolean | Optional | true (default) or false |
Show or hide the icon |
show_last_changed |
boolean | Optional | true or false (default) |
Show the last changed time of your entity |
show_attribute |
boolean | Optional | true or false (default) |
Show an attribute of your entity below its name |
attribute |
string | Optional (required if show_attribute is set to true ) |
An attribute from your entity |
The attribute to show (e.g. brightness ) |
scrolling_effect |
boolean | Optional | true (default) or false |
Allow text to scroll when the content exceeds the size of their container |
icon_open |
string | Optional | Any mdi: icon |
An icon for your open cover, if not defined it will display the default open cover icon |
icon_close |
string | Optional | Any mdi: icon |
An icon for your closed cover, if not defined it will display the default closed cover icon |
icon_up |
string | Optional | Any mdi: icon |
An icon for your open cover button, if not defined it will display the default open cover icon |
icon_down |
string | Optional | Any mdi: icon |
An icon for your close cover button, if not defined it will display the default close cover icon |
open_service |
string | Optional | Any service or script | A service to open your cover, default to cover.open_cover |
stop_service |
string | Optional | Any service or script | A service to stop your cover, default to cover.stop_cover |
close_service |
string | Optional | Any service or script | A service to close your cover, default to cover.close_cover |
tap_action |
object | Optional | See actions | Define the type of action on icon click, if undefined, more-info will be used. |
double_tap_action |
object | Optional | See actions | Define the type of action on icon double click, if undefined, toggle will be used. |
hold_action |
object | Optional | See actions | Define the type of action on icon hold, if undefined, more-info will be used. |
card_layout |
string | Optional | normal (default), large , large-2-rows |
Styling layout of the card, see card layouts |
columns |
string | Optional | 1 , 2 , 3 or 4 (default) |
Number of columns when placed in a section view (e.g. 2 is 2/4) |
rows |
string | Optional | 1 (default), 2 , 3 or 4 |
Number of rows when placed in a section view |
sub_button |
object | Optional | See sub-buttons | Add customized buttons fixed to the right |
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-cover-main-background-color |
color |
Main background color for supported elements in the cover card |
--bubble-cover-border-radius |
px |
Border radius for the cover card |
--bubble-cover-icon-border-radius |
px |
Border radius for the cover card icon container |
--bubble-cover-icon-background-color |
color |
Background color for the cover card icon container |
--bubble-cover-box-shadow |
See box shadow | Box shadow for the cover card |
--bubble-button-box-shadow |
See box shadow | Box shadow for buttons in the cover card |
A card that can control a roller shade
type: custom:bubble-card
card_type: cover
entity: cover.kitchen
name: Kitchen
icon_open: mdi:roller-shade
icon_close: mdi:roller-shade-closed
This card allows you to add a dropdown menu for your input_select
/ select
entities. This card also supports the sub-buttons and all the common Bubble Card features.
Tip
You can also have select sub-buttons if you want, this feature is available in all the cards that support the sub-buttons.
Options (YAML + descriptions)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
entity |
string | Required | Any entity | An entity to control |
name |
string | Optional | Any string | A name for your select, if not defined it will display the entity name |
icon |
string | Optional | Any mdi: icon |
An icon for your select, if not defined it will display the entity icon or the entity-picture |
force_icon |
boolean | Optional | true or false (default) |
Give the priority to the icon instead of the entity-picture |
show_state |
boolean | Optional | true or false (default) |
Show or hide the state of your entity |
show_name |
boolean | Optional | true (default) or false |
Show or hide the name |
show_icon |
boolean | Optional | true (default) or false |
Show or hide the icon |
show_last_changed |
boolean | Optional | true or false (default) |
Show the last changed time of your entity |
show_attribute |
boolean | Optional | true or false (default) |
Show an attribute of your entity below its name |
attribute |
string | Optional (required if show_attribute is set to true ) |
An attribute from your entity |
The attribute to show (e.g. brightness ) |
scrolling_effect |
boolean | Optional | true (default) or false |
Allow text to scroll when the content exceeds the size of their container |
tap_action |
object | Optional | See actions | Define the type of action on icon click, if undefined, more-info will be used. |
double_tap_action |
object | Optional | See actions | Define the type of action on icon double click, if undefined, toggle will be used. |
hold_action |
object | Optional | See actions | Define the type of action on icon hold, if undefined, more-info will be used. |
card_layout |
string | Optional | normal (default), large , large-2-rows |
Styling layout of the card, see card layouts |
columns |
string | Optional | 1 , 2 , 3 or 4 (default) |
Number of columns when placed in a section view (e.g. 2 is 2/4) |
rows |
string | Optional | 1 (default), 2 , 3 or 4 |
Number of rows when placed in a section view (e.g. 2 is 2/4) |
sub_button |
object | Optional | See sub-buttons | Add customized buttons fixed to the right |
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-select-main-background-color |
color |
Main background color for supported elements in the select card |
--bubble-select-background-color |
color |
Background color for select card |
--bubble-select-list-border-radius |
px |
Border radius for the dropdown menu in the card |
--bubble-select-list-item-accent-color |
color |
Accent color for the selected item |
--bubble-select-list-background-color |
color |
Background color for the dropdown menu in the card |
--bubble-select-list-width |
px |
Width of the dropdown menu in the card |
--bubble-select-arrow-background-color |
color |
Background color for dropdown arrow |
--bubble-select-button-border-radius |
px |
Border radius for select button |
--bubble-select-border-radius |
px |
Border radius for the select card |
--bubble-select-icon-border-radius |
px |
Border radius for the select card icon container |
--bubble-select-icon-background-color |
color |
Background color for the select card icon container |
--bubble-select-box-shadow |
See box shadow | Box shadow for the select card |
A select card with a list of scenes
type: custom:bubble-card
card_type: select
name: Scene
entity: input_select.scenes
icon: mdi:brightness-4
show_state: true
This card allows you to control your climate
entities.
Tip
The mode selection menu is a sub-button that is added automatically when creating the card. You can then modify or remove it as you wish.
Options (YAML + descriptions)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
entity |
string | Required | Climate entity | The entity to control (e.g., climate.living_room ). |
name |
string | Optional | Any string | A custom name for the card. If not defined, it will display the entity name. |
icon |
string | Optional | Any mdi: icon |
A custom icon for the card. If not defined, the entity icon or entity-picture will be used. |
force_icon |
boolean | Optional | true or false (default) |
Gives priority to the icon over the entity-picture . |
show_state |
boolean | Optional | true or false (default) |
Show or hide the current state of the entity . |
show_name |
boolean | Optional | true (default) or false |
Show or hide the name of the entity. |
show_icon |
boolean | Optional | true (default) or false |
Show or hide the icon. |
hide_target_temp_low |
boolean | Optional (only for entities supporting target_temp_low ) |
true or false (default) |
Hides the low target temperature control if supported by the entity . |
hide_target_temp_high |
boolean | Optional (only for entities supporting target_temp_high ) |
true or false (default) |
Hides the high target temperature control if supported by the entity . |
state_color |
boolean | Optional | true or false (default) |
Applies a constant background color when the climate entity is ON. |
tap_action |
object | Optional | See actions | Define the action triggered on tap. If not defined, more-info will be used. |
double_tap_action |
object | Optional | See actions | Define the action triggered on double tap. If not defined, toggle will be used. |
hold_action |
object | Optional | See actions | Define the action triggered on hold. If not defined, more-info will be used. |
card_layout |
string | Optional | normal (default), large , large-2-rows |
Defines the styling layout of the card. See card layouts. |
columns |
string | Optional | 1 , 2 , 3 , or 4 (default) |
Number of columns when placed in a section view. |
rows |
string | Optional | 1 (default), 2 , 3 , or 4 |
Number of rows when placed in a section view. |
sub_button |
object | Optional | See sub-buttons | Adds custom buttons fixed to the right. Useful for a climate mode select menu. |
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-climate-main-background-color |
color |
Main background color for supported elements in the climate card |
--bubble-climate-border-radius |
px |
Border radius for supported elements in the climate card elements |
--bubble-climate-button-background-color |
color |
Background color for the climate card buttons |
--bubble-climate-icon-border-radius |
px |
Border radius for the climate card icon container |
--bubble-state-climate-fan-only-color |
color |
Overlay color for the fan-only state |
--bubble-state-climate-dry-color |
color |
Overlay color for the dry state |
--bubble-state-climate-cool-color |
color |
Overlay color for the cool state |
--bubble-state-climate-heat-color |
color |
Overlay color for the heat state |
--bubble-state-climate-auto-color |
color |
Overlay color for the auto state |
--bubble-state-climate-heat-cool-color |
color |
Overlay color for the heat-cool state |
--bubble-climate-accent-color |
color |
Accent color for the climate card |
--bubble-climate-box-shadow |
See box shadow | Box shadow for climate container. |
A climate card with an HVAC modes dropdown menu
type: custom:bubble-card
card_type: climate
entity: climate.test_climate
sub_button:
- name: HVAC modes menu
select_attribute: hvac_modes
show_arrow: false
state_background: false
This card is a simple separator for dividing your pop-up into categories / sections. e.g. Lights, Devices, Covers, Settings, Automations...
Options (YAML + descriptions)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
name |
string | Optional but recommended | Any string | A name for your separator |
icon |
string | Optional but recommended | Any mdi: icon |
An icon for your separator |
card_layout |
string | Optional | normal (default), large , large-2-rows |
Styling layout of the card, see card layouts |
columns |
string | Optional | 1 , 2 , 3 or 4 (default) |
Number of columns when placed in a section view (e.g. 2 is 2/4) |
rows |
string | Optional | 1 (default), 2 , 3 or 4 |
Number of rows when placed in a section view |
sub_button |
object | Optional | See sub-buttons | Add customized buttons fixed to the right |
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-line-background-color |
color |
Background color for the line in the separator |
A separator/divider for a "Covers" section
type: custom:bubble-card
card_type: separator
name: Covers
icon: mdi:window-shutter
This card is here to fill an empty column. This is useful if you have a horizontal-stack
in your pop-up with only one card. Take a look at the bottom right corner of this screenshot to (not) see it.
This card has no options and doesn’t support styling, though it does support layout options for HA sections.
An empty column in an horizontal stack
type: horizontal-stack
cards:
- type: custom:bubble-card
card_type: button
...
- type: custom:bubble-card
card_type: empty-column
In every card that supports that option, you can add sub-buttons to customize your cards even more. You can, for exemple, create a button that can control a vacuum, a weather card, or almost anything that you can come up with. These sub-buttons support the tap actions and most of the button options.
Options (YAML + description)
Name | Type | Requirement | Supported options | Description |
---|---|---|---|---|
entity |
string | Optional | Any entity | An entity to control |
name |
string | Optional | Any string | A name for your sub-button, if not defined it will display the entity name |
icon |
string | Optional | Any mdi: icon |
An icon for your sub-button, if not defined it will display the entity icon |
show_background |
boolean | Optional | true (default) or false |
Show a background for your sub-button, it will change its color based on your entity state |
show_state |
boolean | Optional | true or false (default) |
Show or hide the state of your entity |
show_name |
boolean | Optional | true or false (default) |
Show or hide the name |
show_icon |
boolean | Optional | true (default) or false |
Show or hide the icon |
show_last_changed |
boolean | Optional | true or false (default) |
Show the last changed time of your entity |
show_attribute |
boolean | Optional | true or false (default) |
Show an attribute of your entity below its name |
attribute |
string | Optional (required if show_attribute is set to true ) |
An attribute from your entity |
The attribute to show (e.g. brightness ) |
tap_action |
object | Optional | See actions | Define the type of action on sub-button click, if undefined, more-info will be used. |
double_tap_action |
object | Optional | See actions | Define the type of action on sub-button double click, if undefined, toggle will be used. |
hold_action |
object | Optional | See actions | Define the type of action on sub-button hold, if undefined, more-info will be used. |
CSS variables (see Styling)
Variable | Expected value | Description |
---|---|---|
--bubble-sub-button-border-radius |
px |
Border radius for the sub-buttons |
--bubble-sub-button-background-color |
color |
Background color for the sub-buttons |
A button with some sub-buttons to make a vacuum card (like on the screenshot)
type: custom:bubble-card
card_type: button
button_type: switch
name: Vacuum
entity: vacuum.downstairs
icon: mdi:robot-vacuum
show_state: true
show_last_changed: true
tap_action:
action: more-info
button_action:
tap_action:
action: more-info
sub_button:
- name: Battery
icon: mdi:battery
show_name: false
show_icon: true
show_background: false
show_attribute: true
attribute: battery_level
- name: Return to dock
icon: mdi:home
show_background: false
tap_action:
action: call-service
service: vacuum.return_to_base
target:
entity_id: vacuum.downstairs
- name: Pause
icon: mdi:pause
show_background: false
tap_action:
action: call-service
service: vacuum.pause
target:
entity_id: vacuum.downstairs
- name: Start
icon: mdi:play
tap_action:
action: call-service
service: vacuum.start
target:
entity_id: vacuum.downstairs
styles: >-
.bubble-button-card-container {
/* Change the background color when the vacuum get an error (optional), more details in the styles template section */
background: ${state === 'error' ? 'rgb(200, 80, 40)' : ''} !important;
}
/* Change the first sub-button battery icon based on the battery_icon attribute, more details in the styles template section */
${subButtonIcon[0].setAttribute("icon", hass.states['vacuum.downstairs'].attributes.battery_icon)}
A button slider with a sub-button that shows the brightness and one that toggle the light (like on the screenshot)
type: custom:bubble-card
card_type: button
button_type: slider
name: Kitchen
entity: light.kitchen
icon: mdi:fridge-outline
show_last_updated: true
sub_button:
- name: Brightness
icon: mdi:fridge-outline
show_icon: false
show_background: false
show_attribute: true
attribute: brightness
- name: Toggle button
icon: mdi:lightbulb
tap_action:
action: toggle
A button that shows the inside and outside temperature with the weather for today and tomorrow (screenshot included)
Bad luck for me it's cloudy all the time but all the icons are changing based on the weather.
type: custom:bubble-card
card_type: button
button_type: state
entity: weather.openweathermap
name: Weather
show_state: true
card_layout: large-2-rows
sub_button:
- name: Home temperature
icon: mdi:home-thermometer-outline
entity: sensor.home_temperature
show_state: true
show_icon: true
show_background: false
- name: Outside temperature
entity: sensor.outside_temperature
show_state: true
show_background: false
- name: Today
entity: sensor.home_realfeel_temperature_max_0d
show_name: true
show_state: true
tap_action:
action: more-info
- name: Tomorrow
entity: sensor.home_realfeel_temperature_max_1d
show_name: true
show_state: true
show_background: false
styles: >-
/* Change the third and fourth sub-button icon based on the forecast.condition attribute, more details in the styles template section */
${subButtonIcon[2].setAttribute("icon", getWeatherIcon(hass.states['sensor.weather_forecast_daily'].attributes.forecast[0]?.condition))}
${subButtonIcon[3].setAttribute("icon", getWeatherIcon(hass.states['sensor.weather_forecast_daily'].attributes.forecast[1]?.condition))}
Bubble Card fully supports the Home Assistant section view, you can change the card layout to make the card bigger and also change the number of columns or rows the card should occupy in your section view (only on the cards that support that option). These layouts are also supported in all other view types.
Available card layouts
Layout | Description |
---|---|
normal |
The regular layout (not optimized for the section view) |
large |
A larger layout (optimized for the section view) |
large-2-rows |
A larger layout with 2 rows of sub-buttons (optimized for the section view) |
A large button that shows energy statistics with 2 rows of sub-buttons (screenshot included)
type: custom:bubble-card
card_type: button
button_type: state
card_layout: large-2-rows
name: Energy
entity: sensor.current_power_production
icon: mdi:home-lightning-bolt-outline
show_state: true
button_action:
tap_action:
action: navigate
navigation_path: '#energy'
sub_button:
- entity: sensor.electricity_counter
icon: mdi:counter
show_background: false
show_state: true
tap_action:
action: more-info
- entity: sensor.today_s_energy_production
show_state: true
show_background: false
- entity: sensor.average_daily_consumption
show_background: false
show_state: true
- entity: sensor.this_week_production
show_state: true
show_background: false
icon: mdi:calendar-week
You can also use Home Assistant default tap actions, double tap actions and hold actions on the cards that supports this option. For example, this allows you to display the “more info” window by holding a button icon or running a service when a sub-button is pressed.
Options (YAML + description)
Name | Type | Supported options | Description |
---|---|---|---|
action |
string | more-info , toggle , call-service , navigate , url , fire-dom-event , none |
Action to perform |
target |
object | Only works with call-service . Follows the home-assistant syntax |
|
navigation_path |
string | Any path of your dashboard | Path to navigate to (e.g. '#kitchen' for opening a pop-up) when action defined as navigate |
url_path |
string | Any link | URL to open on click (e.g. https://www.google.com ) when action is url |
service |
string | Any service | Service to call (e.g. media_player.media_play_pause ) when action defined as call-service |
data or service_data |
object | Any service data | Service data to include (e.g. entity_id: media_player.kitchen ) when action defined as call-service |
confirmation |
object | See confirmation | Display a confirmation pop-up (not a Bubble Card one), overrides the default confirmation object |
A button to open a pop-up
type: custom:bubble-card
card_type: button
button_type: name
name: Kitchen
icon: mdi:fridge
button_action:
tap_action:
action: navigate
navigation_path: '#kitchen'
You can add custom styles to modify the CSS of all cards without using card-mod in three ways:
-
In the editor, go to the card you want to modify, then navigate to Styling options > Custom styles / Templates, and add your custom styles (check the tips and examples below).
-
In a theme file by adding CSS variables in YAML (these are available in each card's documentation above). This allows for global modifications.
Example
Don't copy the
Bubble:
line, this is the name of the theme you use. You also need to remove the--
from the variables.You need to run the
frontend.reload_themes
action to refresh the theme after any modifications.Bubble: # Bubble Card variables test bubble-border-radius: "8px" bubble-main-background-color: "rgb(50,70,90)" bubble-secondary-background-color: "rgb(0,70,90)" bubble-pop-up-main-background-color: "rgba(200,200,200,0.5)" bubble-accent-color: "rgb(100,140,180)" bubble-icon-background-color: "rgb(50,80,100)" bubble-select-list-width: "200px" bubble-select-list-background-color: "rgb(100,140,180)"
-
In YAML by adding
styles: |
followed by your custom styles (check the tips and examples below).
Tip
To understand which style classes can be modified, you can take a look at the src/cards
folder in this repository. In each card folder, you will find a file named styles.ts
. These files contain all the applied styles. This allows for a lot more possibilities than CSS variables, but it needs to be added individually to each card.
You can also find a lot of examples from the community, or some from the Home Assistant forum by doing a bit of searching.
The Bubble theme for Home Assistant (like on the screenshots) can be found here.
A tutorial video is coming soon on my YouTube channel!
Important
Please note that you will have to add !important;
to some CSS styles that are already defined (see examples below).
Changing the font size of any Bubble Card
styles: |
* {
font-size: 16px !important;
}
Changing the background color of a single button in an horizontal buttons stack
styles: >
/* Selector for the '#kitchen' button */
.kitchen > .color-background {
background-color: blue !important;
}
Changing the background color of a card
This one works on all Bubble Card types (except for the pop-ups):
styles: |
ha-card {
--bubble-main-background-color: rgba(12,120,50,0.5) !important;
}
This one is doing the same in a button card only (it works for the pop-up header):
styles: |
.bubble-button-card-container {
background: rgba(12,120,50,0.5) !important;
}
To change the color when it's on
take a look at the style templates below.
Changing the color of a button slider
styles: |
.bubble-range-fill {
background: rgba(79, 69, 87, 1) !important;
opacity: 1 !important;
}
Changing the line color of a separator
styles: |
.bubble-line {
background: var(--primary-text-color);
opacity: 0.1;
}
Changing the color of an icon
styles: |
.bubble-icon {
color: white !important;
}
For an horizontal buttons stack icon.
.kitchen > .bubble-icon {
color: grey !important
}
Changing the background color of an icon container
This one works on all Bubble Card types (except for the pop-ups):
styles: |
ha-card {
--bubble-icon-background-color: rgb(230, 128, 41) !important;
}
This one is doing the same for the pop-up header:
styles: |
.bubble-icon-container {
background: rgb(230, 128, 41) !important;
}
Changing the size of the sub-buttons (perfect for the large layout)
styles: |
.bubble-sub-button {
height: 48px !important;
min-width: 48px !important;
}
Changing the background color of the second sub-button
styles: |
.bubble-sub-button-2 {
background-color: blue !important;
}
Changing the size of an icon
For the main icon.
styles: |
.bubble-icon {
--mdc-icon-size: 26px !important;
}
For the sub-button icons.
styles: |
.bubble-sub-button-icon {
--mdc-icon-size: 26px !important;
}
Using a picture rather than an icon in a sub button
sub_button:
- icon: none
styles: |-
.bubble-sub-button-1 {
background-image: url("/local/pictures/your_picture.jpg");
background-size: cover;
}
Just upload this picture in a “pictures” folder (or the name you want) in the Home Assistant “www” folder.
Advanced example: Creating an horizontal row of sub-buttons (screenshot included)
I really love this one, I use it as a header on my dashboard.
type: custom:bubble-card
card_type: button
card_layout: large
button_type: name
show_icon: false
show_name: false
sub_button:
- name: Mute
icon: mdi:volume-off
tap_action:
action: toggle
service: input_boolean.toggle
entity: input_boolean.silent_mode
- name: Covers
entity: cover.all_group
show_background: false
tap_action:
action: navigate
navigation_path: '#cover'
- name: Shopping list
icon: mdi:cart-outline
show_background: false
tap_action:
action: navigate
navigation_path: '#shopping-list'
- name: Security
icon: mdi:video-outline
show_background: false
tap_action:
action: navigate
navigation_path: '#security'
- name: Settings
icon: mdi:cog
show_background: false
tap_action:
action: navigate
navigation_path: '#configuration'
styles: |
.card-content {
width: 100%;
margin: 0 !important;
}
.bubble-button-card-container {
background: none;
}
.bubble-sub-button {
height: 46px !important;
width: 46px !important;
}
.bubble-sub-button-container {
width: 100%;
justify-content: space-between !important;
}
.bubble-sub-button-icon {
--mdc-icon-size: inherit !important;
}
.bubble-name-container {
margin-right: 0px !important;
}
Bubble Card doesn’t support Jinja templates but advanced users can add templates in JS directly in their custom styles. For example, this allows you to dynamically change an icon, the texts or the colors of an element, to show or hide an element conditionally (like a sub-button), or almost anything based on a state, an attribute and more.
Tip
More information about JS templates here. My advice is to always take a look at your browser console to be sure that everything is working correctly.
Important
All templates that are not modifying a CSS property must be placed at the end! Like modifying an icon, a text or any element.
Variables
You have access to these variables in most cards:
-
state
will return the state of your definedentity
. -
entity
will return your entity you defined likeswitch.test
in this example. -
icon
can be used like this to change the iconicon.setAttribute("icon", "mdi:lightbulb")
. -
subButtonIcon[0]
can be used like this to change the first sub-button iconsubButtonIcon[0].setAttribute("icon", "mdi:lightbulb")
,[0]
is the first sub-button icon,[1]
the second... -
card
will return the card element in the DOM. -
hass
is an advanced variable that allows you even more control, for example you can return the state oflight.kitchen
like thishass.states['light.kitchen'].state
or an attribute like thishass.states[entity].attributes.brightness
. -
this
will return a lot of usefull informations about your setup and dashboard, only use this if you know what you are doing.
Functions
You have access to all the global JS functions, but you have also access to:
-
getWeatherIcon
can be used to return a weather icon based on a state that return the weather. For example, you can can do this${subButtonIcon[2].setAttribute("icon", getWeatherIcon(hass.states['sensor.weather_forecast_daily'].attributes.forecast[0]?.condition))}
to change the third sub-button icon to today's weather icon,.forecast[1]?.condition
is for tomorrow...You will have to create a template sensor for that. Here is what you can add in your
configuration.yaml
:- trigger: - platform: time_pattern hours: /2 action: - service: weather.get_forecasts data: type: daily target: entity_id: weather.home response_variable: daily sensor: - name: Weather Forecast Daily unique_id: weather_forecast_daily state: "{{ now().isoformat() }}" attributes: forecast: "{{ daily['weather.home'].forecast }}"
-
hass.formatEntityState(state)
can be used to transtale a state (Can also be used to get a state unit, without the need to add it manually). -
hass.formatEntityAttributeValue(state, "attribute")
can be used to translate an attribute (Can also be used to get a state unit, without the need to add it manually).
You can find a lot of examples below, but you can also find very advanced templates on my Patreon page, like one (my favorite) that allows up to four conditional badges placed around the card’s icons. It’s also a great way to learn about all the possibilities of Bubble Card custom styles and templates!
Examples from my Patreon page
Adding Home Assistant like badges to any card
Showing formatted date and time in a separator without using any entity
Showing a sub-button state on two lines
Customizing labels and icons inside a select sub-button
Changing the background color of a button that is red when it's off
and blue when it's on
type: custom:bubble-card
card_type: button
entity: switch.test
name: Test
styles: |
.bubble-button-background {
opacity: 1 !important;
background-color: ${state === 'on' ? 'blue' : 'red'} !important;
}
Changing the background color of a button based on an entity for the horizontal buttons stack
styles: |
.kitchen > .color-background {
background-color: ${hass.states['light.kitchen'].state === 'on' ? 'blue' : 'red'} !important;
}
Showing/Hiding a sub-button conditionally
This one is showing the first sub-button only when my vacuum is stuck.
styles: |
.bubble-sub-button-1 {
display: ${hass.states['vacuum.downstairs'].state === 'error' ? '' : 'none'} !important;
}
This one is showing a sub-button when the battery is below 10%. Usefull with a sub-button that shows "Low battery".
styles: |
.bubble-sub-button-1 {
display: ${hass.states['vacuum.downstairs'].attributes.battery_level <= 10 ? '' : 'none'} !important;
}
Changing an icon or sub-button icon conditionally
This one is changing a button icon only when a vacuum is stuck.
styles: |
${icon.setAttribute("icon", hass.states['vacuum.downstairs'].state === 'error' ? 'mdi:alert' : 'mdi:robot-vacuum')}
This one is changing the first sub-button icon only when a vacuum is stuck.
styles: |
${subButtonIcon[0].setAttribute("icon", hass.states['vacuum.downstairs'].state === 'error' ? 'mdi:alert' : 'mdi:robot-vacuum')}
Changing an icon or sub-button icon color conditionally
This one is changing a button icon color based on its state.
styles: |
.bubble-icon {
color: ${hass.states['light.your_light'].state === 'on' ? 'green' : 'red'} !important;
}
This one is changing a sub-button icon color based on its state. .bubble-sub-button-1
is the first sub-button, replace 1
if you want to change another sub-button icon.
styles: |
.bubble-sub-button-1 > ha-icon {
color: ${hass.states['light.your_light'].state === 'on' ? 'green' : 'red'} !important;
}
Animating a fan icon conditionally
This one is rotating a button icon when a fan is on
.
styles: |-
.bubble-icon {
animation: ${hass.states['fan.you_fan'].state === 'on' ? 'slow-rotate 2s linear infinite' : ''};
}
@keyframes slow-rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
Templating texts (like name or state)
This one is changing a button name/state with "It's currently sunny" depending on your weather.
styles: |
${card.querySelector('.bubble-name').innerText = "It's currently " + hass.states['weather.home'].state}
If you want to template the state (.bubble-state
) don't toggle show_state: true
just toggle show_attribute: true
without any attribute.
Advanced example: Changing the color of a sub-button when a pop-up is open
styles: |
${window.addEventListener('location-changed', () => {
card.querySelector('.bubble-sub-button-1').style.backgroundColor = this.location.href.includes('#kitchen') ? 'blue' : '';
})}
Advanced example: Templating a separator name based on a state translated to your language
You can use hass.formatEntityState(state)
to transtale a state and hass.formatEntityAttributeValue(state, "attribute")
to translate an attribute.
This one is changing the name and the icon based on the weather, "Nuageux" means "Cloudy" in French.
type: custom:bubble-card
card_type: separator
icon: mdi:weather-cloudy
sub_button:
- entity: sensor.outside_temperature
icon: mdi:thermometer
name: Temperature
show_state: true
show_background: false
styles: >
.bubble-line {
background: white;
opacity: 1;
}
${card.querySelector('.bubble-name').innerText =
hass.formatEntityState(hass.states['weather.maison'])}
${icon.setAttribute("icon",
getWeatherIcon(hass.states['weather.maison'].state))}
- Kiosk mode, but this is fixed in Kiosk mode v6.0.1
Feel free to open an issue if something is not working as expected.
Got questions or thoughts about Bubble Card? Want to share your dashboards or discoveries? You can go on the Home Assistant forum, on the Bubble Card subreddit or on the GitHub Discussions section.
I dedicate most of my spare time to making this project the best it can be. So if you appreciate my work, any donation would be a great way to show your support 🍻
Thank you everyone for your support, you all are my greatest motivation!