diff --git a/gallery/src/pages/components/ha-wa-dialog.markdown b/gallery/src/pages/components/ha-wa-dialog.markdown new file mode 100644 index 000000000000..2ae337e35a58 --- /dev/null +++ b/gallery/src/pages/components/ha-wa-dialog.markdown @@ -0,0 +1,3 @@ +--- +title: Dialog (ha-wa-dialog) +--- diff --git a/gallery/src/pages/components/ha-wa-dialog.ts b/gallery/src/pages/components/ha-wa-dialog.ts new file mode 100644 index 000000000000..41fafe785d59 --- /dev/null +++ b/gallery/src/pages/components/ha-wa-dialog.ts @@ -0,0 +1,523 @@ +import { css, html, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import { mdiCog, mdiHelp } from "@mdi/js"; +import "../../../../src/components/ha-button"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-dialog-footer"; +import "../../../../src/components/ha-form/ha-form"; +import "../../../../src/components/ha-icon-button"; +import "../../../../src/components/ha-wa-dialog"; +import type { HaFormSchema } from "../../../../src/components/ha-form/types"; + +const SCHEMA: HaFormSchema[] = [ + { type: "string", name: "Name", default: "", autofocus: true }, + { type: "string", name: "Email", default: "" }, +]; + +type DialogType = + | false + | "basic" + | "basic-subtitle-below" + | "basic-subtitle-above" + | "form" + | "actions"; + +@customElement("demo-components-ha-wa-dialog") +export class DemoHaWaDialog extends LitElement { + @state() private _openDialog: DialogType = false; + + protected render() { + return html` +
+

Dialog <ha-wa-dialog>

+ +

Dialog component built with WebAwesome.

+ +

Demos

+ +
+ Basic dialog + Basic dialog with subtitle below + Basic dialog with subtitle above + Dialog with form + Dialog with actions +
+ + +
Dialog content
+
+ + +
Dialog content
+
+ + +
Dialog content
+
+ + + + + Cancel + Submit + + + + +
+ + +
+ +
Dialog content
+
+ +

Design

+ +

Width

+ +

There are multiple widths available for the dialog.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
NameValue
smallmin(320px, var(--full-width))
mediummin(580px, var(--full-width))
largemin(720px, var(--full-width))
fullvar(--full-width)
+ +

+ --full-width is calculated based on the available width + of the screen. 95vw is the maximum width of the dialog on a large + screen, while on a small screen it is 100vw minus the safe area + insets. +

+ +

Dialogs have a default width of medium.

+ +

Prevent scrim close

+ +

+ You can prevent the dialog from being closed by clicking the + scrim/overlay. This is allowed by default. +

+ +

Header

+ +

The header contains a title, a subtitle and action items.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
SlotDescription
headerThe entire header area.
headerTitleThe header title text.
headerSubtitleThe header subtitle text.
headerActionItemsThe header action items.
+ +

Header title

+ +

The header title is a text string.

+ +

Header subtitle

+ +

The header subtitle is a text string.

+ +

Header action items

+ +

+ The header action items usually containing icon buttons and/or menu + buttons. +

+ +

Body

+ +

The body is the content of the dialog.

+ +

Footer

+ +

The footer is the footer of the dialog.

+ +

+ It is recommended to use the ha-dialog-footer component + for the footer and to style the buttons inside the footer as so: +

+ + + + + + + + + + + + + + + + + + + + + +
SlotDescriptionVariant to use
secondaryActionThe secondary action button(s).plain
primaryActionThe primary action button(s).accent
+ +

Implementation

+ +

Example Usage

+ +
<ha-wa-dialog
+  open
+  header-title="Dialog title"
+  header-subtitle="Dialog subtitle"
+  prevent-scrim-close
+>
+  <div slot="headerActionItems">
+    <ha-icon-button label="Settings" path="mdiCog"></ha-icon-button>
+    <ha-icon-button label="Help" path="mdiHelp"></ha-icon-button>
+  </div>
+  <div>Dialog content</div>
+  <ha-dialog-footer slot="footer">
+    <ha-button data-dialog="close" slot="secondaryAction" variant="plain"
+      >Cancel</ha-button
+    >
+    <ha-button slot="primaryAction" variant="accent">Submit</ha-button>
+  </ha-dialog-footer>
+</ha-wa-dialog>
+ +

API

+ +

+ This component is based on the webawesome dialog component. Check the + webawesome documentation + for more details. +

+ +

Attributes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionDefaultOptions
openControls the dialog open state.falsefalse, true
widthPreferred dialog width preset.medium + small, medium, large, + full +
prevent-scrim-close + Prevents closing the dialog by clicking the scrim/overlay. + falsetrue
header-titleHeader title text when no custom title slot is provided.
header-subtitle + Header subtitle text when no custom subtitle slot is provided. +
header-subtitle-positionPosition of the subtitle relative to the title.belowabove, below
flexcontent + Makes the dialog body a flex container for flexible layouts. + falsefalse, true
+ +

CSS Custom Properties

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CSS PropertyDescription
--dialog-content-paddingPadding for dialog content sections.
--ha-dialog-show-durationShow animation duration.
--ha-dialog-hide-durationHide animation duration.
--ha-dialog-surface-backgroundDialog background color.
--ha-dialog-border-radiusBorder radius of the dialog surface.
--dialog-z-indexZ-index for the dialog.
--dialog-surface-positionCSS position of the dialog surface.
--dialog-surface-margin-topTop margin for the dialog surface.
+ +

Events

+ + + + + + + + + + + + + + + + + + +
EventDescription
openedFired when the dialog is shown.
closedFired after the dialog is hidden.
+
+ `; + } + + private _handleOpenDialog = (dialog: DialogType) => () => { + this._openDialog = dialog; + }; + + private _handleClosed = () => { + this._openDialog = false; + }; + + static styles = [ + css` + :host { + display: block; + padding: var(--ha-space-4); + } + + .content { + max-width: 1000px; + margin: 0 auto; + } + + h1 { + margin-top: 0; + margin-bottom: var(--ha-space-2); + } + + h2 { + margin-top: var(--ha-space-6); + margin-bottom: var(--ha-space-3); + } + + h3, + h4 { + margin-top: var(--ha-space-4); + margin-bottom: var(--ha-space-2); + } + + p { + margin: var(--ha-space-2) 0; + line-height: 1.6; + } + + .subtitle { + color: var(--secondary-text-color); + font-size: 1.1em; + margin-bottom: var(--ha-space-4); + } + + table { + width: 100%; + border-collapse: collapse; + margin: var(--ha-space-3) 0; + } + + th, + td { + text-align: left; + padding: var(--ha-space-2); + border-bottom: 1px solid var(--divider-color); + } + + th { + font-weight: 500; + } + + code { + background-color: var(--secondary-background-color); + padding: 2px 6px; + border-radius: 4px; + font-family: monospace; + font-size: 0.9em; + } + + pre { + background-color: var(--secondary-background-color); + padding: var(--ha-space-3); + border-radius: 8px; + overflow-x: auto; + margin: var(--ha-space-3) 0; + } + + pre code { + background-color: transparent; + padding: 0; + } + + .buttons { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: var(--ha-space-2); + margin: var(--ha-space-4) 0; + } + + a { + color: var(--primary-color); + } + `, + ]; +} + +declare global { + interface HTMLElementTagNameMap { + "demo-components-ha-wa-dialog": DemoHaWaDialog; + } +} diff --git a/src/components/ha-dialog-footer.ts b/src/components/ha-dialog-footer.ts new file mode 100644 index 000000000000..e6dd229bd805 --- /dev/null +++ b/src/components/ha-dialog-footer.ts @@ -0,0 +1,52 @@ +import { css, html, LitElement } from "lit"; +import { customElement } from "lit/decorators"; + +/** + * Home Assistant dialog footer component + * + * @element ha-dialog-footer + * @extends {LitElement} + * + * @summary + * A simple footer container for dialog actions, + * typically used as the `footer` slot in `ha-wa-dialog`. + * + * @slot primaryAction - Primary action button(s), aligned to the end. + * @slot secondaryAction - Secondary action button(s), placed before the primary action. + * + * @remarks + * **Button Styling Guidance:** + * - `primaryAction` slot: Use `variant="accent"` + * - `secondaryAction` slot: Use `variant="plain"` + */ +@customElement("ha-dialog-footer") +export class HaDialogFooter extends LitElement { + protected render() { + return html` + + `; + } + + static get styles() { + return [ + css` + footer { + display: flex; + gap: var(--ha-space-3); + justify-content: flex-end; + align-items: center; + width: 100%; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-dialog-footer": HaDialogFooter; + } +} diff --git a/src/components/ha-dialog-header.ts b/src/components/ha-dialog-header.ts index ea6541f5952a..f6ede8ed46e2 100644 --- a/src/components/ha-dialog-header.ts +++ b/src/components/ha-dialog-header.ts @@ -1,9 +1,20 @@ import { css, html, LitElement } from "lit"; -import { customElement } from "lit/decorators"; +import { customElement, property } from "lit/decorators"; @customElement("ha-dialog-header") export class HaDialogHeader extends LitElement { + @property({ type: String, attribute: "subtitle-position" }) + public subtitlePosition: "above" | "below" = "below"; + protected render() { + const titleSlot = html`
+ +
`; + + const subtitleSlot = html`
+ +
`; + return html`
@@ -11,12 +22,9 @@ export class HaDialogHeader extends LitElement {
-
- -
-
- -
+ ${this.subtitlePosition === "above" + ? html`${subtitleSlot}${titleSlot}` + : html`${titleSlot}${subtitleSlot}`}
@@ -32,6 +40,7 @@ export class HaDialogHeader extends LitElement { css` :host { display: block; + min-height: 48px; } :host([show-border]) { border-bottom: 1px solid @@ -40,7 +49,7 @@ export class HaDialogHeader extends LitElement { .header-bar { display: flex; flex-direction: row; - align-items: flex-start; + align-items: center; padding: 4px; box-sizing: border-box; } @@ -53,13 +62,17 @@ export class HaDialogHeader extends LitElement { white-space: nowrap; } .header-title { + height: var( + --ha-dialog-header-title-height, + calc(var(--ha-font-size-xl) + 4px) + ); font-size: var(--ha-font-size-xl); line-height: var(--ha-line-height-condensed); - font-weight: var(--ha-font-weight-normal); + font-weight: var(--ha-font-weight-medium); } .header-subtitle { font-size: var(--ha-font-size-m); - line-height: 20px; + line-height: var(--ha-line-height-normal); color: var(--secondary-text-color); } @media all and (min-width: 450px) and (min-height: 500px) { diff --git a/src/components/ha-form/ha-form-string.ts b/src/components/ha-form/ha-form-string.ts index 74c8333a0ffc..cee07aef312e 100644 --- a/src/components/ha-form/ha-form-string.ts +++ b/src/components/ha-form/ha-form-string.ts @@ -61,6 +61,7 @@ export class HaFormString extends LitElement implements HaFormElement { .required=${this.schema.required} .autoValidate=${this.schema.required} .name=${this.schema.name} + .autofocus=${this.schema.autofocus} .autocomplete=${this.schema.autocomplete} .suffix=${this.isPassword ? // reserve some space for the icon. diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index fbc79a56dd59..7b0d8b390609 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -105,6 +105,11 @@ export class HaForm extends LitElement implements HaFormElement { } } + static shadowRootOptions: ShadowRootInit = { + mode: "open", + delegatesFocus: true, + }; + protected render(): TemplateResult { return html`
diff --git a/src/components/ha-wa-dialog.ts b/src/components/ha-wa-dialog.ts new file mode 100644 index 000000000000..1e3118e919bf --- /dev/null +++ b/src/components/ha-wa-dialog.ts @@ -0,0 +1,320 @@ +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import "@home-assistant/webawesome/dist/components/dialog/dialog"; +import { mdiClose } from "@mdi/js"; +import "./ha-dialog-header"; +import "./ha-icon-button"; +import type { HomeAssistant } from "../types"; +import { fireEvent } from "../common/dom/fire_event"; +import { haStyleScrollbar } from "../resources/styles"; + +export type DialogWidth = "small" | "medium" | "large" | "full"; + +/** + * Home Assistant dialog component + * + * @element ha-wa-dialog + * @extends {LitElement} + * + * @summary + * A stylable dialog built using the `wa-dialog` component, providing a standardized header (ha-dialog-header), + * body, and footer (preferably using `ha-dialog-footer`) with slots + * + * You can open and close the dialog declaratively by using the `data-dialog="close"` attribute. + * @see https://webawesome.com/docs/components/dialog/#opening-and-closing-dialogs-declaratively + * + * @slot header - Replace the entire header area. + * @slot headerNavigationIcon - Leading header action (e.g. close/back button). + * @slot headerActionItems - Trailing header actions (e.g. buttons, menus). + * @slot - Dialog content body. + * @slot footer - Dialog footer content. + * + * @csspart dialog - The dialog surface. + * @csspart header - The header container. + * @csspart body - The scrollable body container. + * @csspart footer - The footer container. + * + * @cssprop --dialog-content-padding - Padding for the dialog content sections. + * @cssprop --ha-dialog-show-duration - Show animation duration. + * @cssprop --ha-dialog-hide-duration - Hide animation duration. + * @cssprop --ha-dialog-surface-background - Dialog background color. + * @cssprop --ha-dialog-border-radius - Border radius of the dialog surface. + * @cssprop --dialog-z-index - Z-index for the dialog. + * @cssprop --dialog-surface-position - CSS position of the dialog surface. + * @cssprop --dialog-surface-margin-top - Top margin for the dialog surface. + * + * @attr {boolean} open - Controls the dialog open state. + * @attr {("small"|"medium"|"large"|"full")} width - Preferred dialog width preset. Defaults to "medium". + * @attr {boolean} prevent-scrim-close - Prevents closing the dialog by clicking the scrim/overlay. Defaults to false. + * @attr {string} header-title - Header title text when no custom title slot is provided. + * @attr {string} header-subtitle - Header subtitle text when no custom subtitle slot is provided. + * @attr {("above"|"below")} header-subtitle-position - Position of the subtitle relative to the title. Defaults to "below". + * @attr {boolean} flexcontent - Makes the dialog body a flex container for flexible layouts. + * + * @event opened - Fired when the dialog is shown. + * @event closed - Fired after the dialog is hidden. + * + * @remarks + * **Focus Management:** + * To automatically focus an element when the dialog opens, add the `autofocus` attribute to it. + * Components with `delegatesFocus: true` (like `ha-form`) will forward focus to their first focusable child. + * Example: `` + * + * @see https://github.com/home-assistant/frontend/issues/27143 + */ +@customElement("ha-wa-dialog") +export class HaWaDialog extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean, reflect: true }) + public open = false; + + @property({ type: String, reflect: true, attribute: "width" }) + public width: DialogWidth = "medium"; + + @property({ type: Boolean, reflect: true, attribute: "prevent-scrim-close" }) + public preventScrimClose = false; + + @property({ type: String, attribute: "header-title" }) + public headerTitle = ""; + + @property({ type: String, attribute: "header-subtitle" }) + public headerSubtitle = ""; + + @property({ type: String, attribute: "header-subtitle-position" }) + public headerSubtitlePosition: "above" | "below" = "below"; + + @property({ type: Boolean, reflect: true, attribute: "flexcontent" }) + public flexContent = false; + + @state() + private _open = false; + + protected updated( + changedProperties: Map + ): void { + super.updated(changedProperties); + + if (changedProperties.has("open")) { + this._open = this.open; + } + } + + protected render() { + return html` + + + + + + + ${this.headerTitle + ? html` + ${this.headerTitle} + ` + : nothing} + ${this.headerSubtitle + ? html`${this.headerSubtitle}` + : nothing} + + + +
+ +
+ +
+ `; + } + + private _handleShow = async () => { + this._open = true; + fireEvent(this, "opened"); + + await this.updateComplete; + + (this.querySelector("[autofocus]") as HTMLElement | null)?.focus(); + }; + + private _handleAfterHide = () => { + this._open = false; + fireEvent(this, "closed"); + }; + + public disconnectedCallback(): void { + super.disconnectedCallback(); + this._open = false; + } + + static styles = [ + haStyleScrollbar, + css` + wa-dialog { + --full-width: var( + --ha-dialog-width-full, + min( + 95vw, + calc( + 100vw - var(--safe-area-inset-left, var(--ha-space-0)) - var( + --safe-area-inset-right, + var(--ha-space-0) + ) + ) + ) + ); + --width: var(--ha-dialog-width-md, min(580px, var(--full-width))); + --spacing: var(--dialog-content-padding, var(--ha-space-6)); + --show-duration: var(--ha-dialog-show-duration, 200ms); + --hide-duration: var(--ha-dialog-hide-duration, 200ms); + --ha-dialog-surface-background: var( + --card-background-color, + var(--ha-color-surface-default) + ); + --wa-color-surface-raised: var( + --ha-dialog-surface-background, + var(--card-background-color, var(--ha-color-surface-default)) + ); + --wa-panel-border-radius: var( + --ha-dialog-border-radius, + var(--ha-border-radius-3xl) + ); + max-width: var(--ha-dialog-max-width, 100vw); + max-width: var(--ha-dialog-max-width, 100svw); + } + + :host([width="small"]) wa-dialog { + --width: var(--ha-dialog-width-sm, min(320px, var(--full-width))); + } + + :host([width="large"]) wa-dialog { + --width: var(--ha-dialog-width-lg, min(720px, var(--full-width))); + } + + :host([width="full"]) wa-dialog { + --width: var(--full-width); + } + + wa-dialog::part(dialog) { + min-width: var(--width, var(--full-width)); + max-width: var(--width, var(--full-width)); + max-height: var( + --ha-dialog-max-height, + calc(100% - var(--ha-space-20)) + ); + position: var(--dialog-surface-position, relative); + margin-top: var(--dialog-surface-margin-top, auto); + display: flex; + flex-direction: column; + overflow: hidden; + } + + @media all and (max-width: 450px), all and (max-height: 500px) { + :host { + --ha-dialog-border-radius: var(--ha-space-0); + } + + wa-dialog { + --full-width: var(--ha-dialog-width-full, 100vw); + } + + wa-dialog::part(dialog) { + min-height: var(--ha-dialog-min-height, 100vh); + min-height: var(--ha-dialog-min-height, 100svh); + max-height: var(--ha-dialog-max-height, 100vh); + max-height: var(--ha-dialog-max-height, 100svh); + padding-top: var(--safe-area-inset-top, var(--ha-space-0)); + padding-bottom: var(--safe-area-inset-bottom, var(--ha-space-0)); + padding-left: var(--safe-area-inset-left, var(--ha-space-0)); + padding-right: var(--safe-area-inset-right, var(--ha-space-0)); + } + } + + .header-title-container { + display: flex; + align-items: center; + } + + .header-title { + margin: 0; + margin-bottom: 0; + color: var( + --ha-dialog-header-title-color, + var(--ha-color-on-surface-default, var(--primary-text-color)) + ); + font-size: var( + --ha-dialog-header-title-font-size, + var(--ha-font-size-2xl) + ); + line-height: var( + --ha-dialog-header-title-line-height, + var(--ha-line-height-condensed) + ); + font-weight: var( + --ha-dialog-header-title-font-weight, + var(--ha-font-weight-normal) + ); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-right: var(--ha-space-3); + } + + wa-dialog::part(body) { + padding: 0; + display: flex; + flex-direction: column; + max-width: 100%; + overflow: hidden; + } + + .body { + position: var(--dialog-content-position, relative); + padding: 0 var(--dialog-content-padding, var(--ha-space-6)) + var(--dialog-content-padding, var(--ha-space-6)) + var(--dialog-content-padding, var(--ha-space-6)); + overflow: auto; + flex-grow: 1; + } + :host([flexcontent]) .body { + max-width: 100%; + display: flex; + flex-direction: column; + } + + wa-dialog::part(footer) { + padding: var(--ha-space-0); + } + + ::slotted([slot="footer"]) { + display: flex; + padding: var(--ha-space-3) var(--ha-space-4) var(--ha-space-4) + var(--ha-space-4); + gap: var(--ha-space-3); + justify-content: flex-end; + align-items: center; + width: 100%; + } + `, + ]; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-wa-dialog": HaWaDialog; + } + + interface HASSDomEvents { + opened: undefined; + closed: undefined; + } +} diff --git a/src/dialogs/restart/dialog-restart.ts b/src/dialogs/restart/dialog-restart.ts index 06830489f736..26815442c4ca 100644 --- a/src/dialogs/restart/dialog-restart.ts +++ b/src/dialogs/restart/dialog-restart.ts @@ -1,7 +1,6 @@ import "@material/mwc-linear-progress/mwc-linear-progress"; import { mdiAutoFix, - mdiClose, mdiLifebuoy, mdiPower, mdiPowerCycle, @@ -9,16 +8,14 @@ import { } from "@mdi/js"; import type { CSSResultGroup } from "lit"; import { LitElement, css, html, nothing } from "lit"; -import { customElement, property, query, state } from "lit/decorators"; +import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { fireEvent } from "../../common/dom/fire_event"; import "../../components/ha-alert"; import "../../components/ha-expansion-panel"; import "../../components/ha-fade-in"; -import "../../components/ha-icon-button"; import "../../components/ha-icon-next"; -import "../../components/ha-md-dialog"; -import type { HaMdDialog } from "../../components/ha-md-dialog"; +import "../../components/ha-wa-dialog"; import "../../components/ha-md-list"; import "../../components/ha-md-list-item"; import "../../components/ha-spinner"; @@ -58,12 +55,14 @@ class DialogRestart extends LitElement { @state() private _hostInfo?: HassioHostInfo; - @query("ha-md-dialog") private _dialog?: HaMdDialog; + @state() + private _dialogOpen = false; public async showDialog(): Promise { const isHassioLoaded = isComponentLoaded(this.hass, "hassio"); this._open = true; + this._dialogOpen = true; if (isHassioLoaded && !this._hostInfo) { this._loadHostInfo(); @@ -92,16 +91,13 @@ class DialogRestart extends LitElement { } private _dialogClosed(): void { + this._dialogOpen = false; this._open = false; this._loadingHostInfo = false; this._loadingBackupInfo = false; fireEvent(this, "dialog-closed", { dialog: this.localName }); } - public closeDialog(): void { - this._dialog?.close(); - } - protected render() { if (!this._open) { return nothing; @@ -113,17 +109,13 @@ class DialogRestart extends LitElement { const dialogTitle = this.hass.localize("ui.dialogs.restart.heading"); return html` - - - - ${dialogTitle} - -
+ +
${this._loadingBackupInfo ? html` @@ -265,12 +257,12 @@ class DialogRestart extends LitElement { `}
- + `; } private async _reload() { - this.closeDialog(); + this._dialogOpen = false; showToast(this, { message: this.hass.localize("ui.dialogs.restart.reload.reloading"), @@ -374,7 +366,7 @@ class DialogRestart extends LitElement { return; } - this.closeDialog(); + this._dialogOpen = false; let actionFunc; @@ -413,15 +405,9 @@ class DialogRestart extends LitElement { haStyle, haStyleDialog, css` - ha-md-dialog { + ha-wa-dialog { --dialog-content-padding: 0; } - @media all and (min-width: 550px) { - ha-md-dialog { - min-width: 500px; - max-width: 500px; - } - } ha-expansion-panel { border-top: 1px solid var(--divider-color); diff --git a/src/resources/theme/color/semantic.globals.ts b/src/resources/theme/color/semantic.globals.ts index d99291d83314..e0fa043be759 100644 --- a/src/resources/theme/color/semantic.globals.ts +++ b/src/resources/theme/color/semantic.globals.ts @@ -152,6 +152,10 @@ export const semanticColorStyles = css` --ha-color-on-success-quiet: var(--ha-color-green-50); --ha-color-on-success-normal: var(--ha-color-green-40); --ha-color-on-success-loud: var(--white-color); + + /* Surfaces */ + --ha-color-surface-default: var(--ha-color-neutral-95); + --ha-color-on-surface-default: var(--ha-color-neutral-05); } `; @@ -280,5 +284,9 @@ export const darkSemanticColorStyles = css` --ha-color-on-success-quiet: var(--ha-color-green-70); --ha-color-on-success-normal: var(--ha-color-green-60); --ha-color-on-success-loud: var(--white-color); + + /* Surfaces */ + --ha-color-surface-default: var(--ha-color-neutral-10); + --ha-color-on-surface-default: var(--ha-color-neutral-95); } `;