From 98aaf1030a61a5014a4f08bd218f6a4ab37e61db Mon Sep 17 00:00:00 2001 From: Wendelin Date: Tue, 27 May 2025 15:25:46 +0200 Subject: [PATCH] Fix theme and add docs --- .../src/pages/components/ha-button.markdown | 98 ++++++++++++ gallery/src/pages/components/ha-button.ts | 146 ++++++++++++++++++ src/common/dom/apply_themes_on_element.ts | 1 + src/components/ha-button.ts | 118 +++++++------- src/resources/theme/color.globals.ts | 32 ++++ 5 files changed, 342 insertions(+), 53 deletions(-) create mode 100644 gallery/src/pages/components/ha-button.markdown create mode 100644 gallery/src/pages/components/ha-button.ts diff --git a/gallery/src/pages/components/ha-button.markdown b/gallery/src/pages/components/ha-button.markdown new file mode 100644 index 0000000000..f52dde1fb7 --- /dev/null +++ b/gallery/src/pages/components/ha-button.markdown @@ -0,0 +1,98 @@ +--- +title: Button +--- + + + +# Button `` + +## Implementation + +### Example Usage + +
+ + simple button + + + plain button + + + filled button + + + + small + +
+ +```html + simple button + + small +``` + +### API + +This component is based on the shoelace button component. +Check the [shoelace documentation](https://shoelace.style/components/button) for more details. + +**Slots** + +- default slot: Label of the button + ` - no default +- `prefix`: The prefix container (usually for icons). + ` - no default +- `suffix`: The suffix container (usually for icons). + ` - no default + +**Properties/Attributes** + +| Name | Type | Default | Description | +| ----------- | ------------------------------------------------ | --------- | ---------------------------------------------------- | +| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. | +| variants | "primary"/"danger"/"neutral"/"warning"/"success" | "primary" | Sets the button color variant. "primary" is default. | +| size | "small"/"medium" | "medium" | Sets the button size. | +| hideContent | Boolean | false | Hides the button content (for overlays) | + +**CSS Custom Properties** + +- `--ha-button-font-family` - Font family for the button text. +- `--ha-button-font-weight` - buttons font weight. +- `--ha-button-font-size` - Font weight for the button text. +- `--ha-button-height` - Height of the button. +- `--ha-button-padding-inline-start` - padding for the button text on the left side. +- `--ha-button-padding-inline-end` - padding for the button text on the right side. +- `--ha-button-border-radius` - Border radius for the button. +- `--ha-button-border-width` - Border width for the button. +- `--ha-button-theme-color` - Main color for the button. +- `--ha-button-theme-dark-color` - Dark variant of the main color. +- `--ha-button-theme-darker-color` - Dark variant of the main color. +- `--ha-button-theme-light-color` - Light variant of the main color. +- `--ha-button-text-color` - Text color for the button. +- `--ha-button-focus-ring-color` - Focus ring color for the button. +- `--ha-button-primary-color` - Main color for the primary variant. +- `--ha-button-primary-light-color` - Light color for the primary variant. +- `--ha-button-primary-dark-color` - Dark color for the primary variant. +- `--ha-button-primary-darker-color` - Darker color for the primary variant. +- `--ha-button-error-color` - Main color for the error variant. +- `--ha-button-error-light-color` - Light color for the error variant. +- `--ha-button-error-dark-color` - Dark color for the error variant. +- `--ha-button-error-darker-color` - Darker color for the error variant. +- `--ha-button-neutral-color` - Main color for the neutral variant. +- `--ha-button-neutral-light-color` - Light color for the neutral variant. +- `--ha-button-neutral-dark-color` - Dark color for the neutral variant. +- `--ha-button-neutral-darker-color` - Darker color for the neutral variant. +- `--ha-button-warning-color` - Main color for the warning variant. +- `--ha-button-warning-light-color` - Light color for the warning variant. +- `--ha-button-warning-dark-color` - Dark color for the warning variant. +- `--ha-button-warning-darker-color` - Darker color for the warning variant. +- `--ha-button-success-color` - Main color for the success variant. +- `--ha-button-success-light-color` - Light color for the success variant. +- `--ha-button-success-dark-color` - Dark color for the success variant. +- `--ha-button-success-darker-color` - Darker color for the success variant. diff --git a/gallery/src/pages/components/ha-button.ts b/gallery/src/pages/components/ha-button.ts new file mode 100644 index 0000000000..c7eb1be6ce --- /dev/null +++ b/gallery/src/pages/components/ha-button.ts @@ -0,0 +1,146 @@ +import { mdiHome } from "@mdi/js"; +import type { TemplateResult } from "lit"; +import { css, html, LitElement } from "lit"; +import { customElement } from "lit/decorators"; +import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element"; +import { titleCase } from "../../../../src/common/string/title-case"; +import "../../../../src/components/ha-button"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-svg-icon"; +import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg"; + +const appearances = ["accent", "filled", "plain"]; +const variants = ["primary", "danger", "neutral", "warning", "success"]; + +@customElement("demo-components-ha-button") +export class DemoHaButton extends LitElement { + protected render(): TemplateResult { + return html` + ${["light", "dark"].map( + (mode) => html` +
+ +
+ ${variants.map( + (variant) => html` +
+ ${appearances.map( + (appearance) => html` + + + ${titleCase(`${variant} ${appearance}`)} + + + ` + )} +
+
+ ${appearances.map( + (appearance) => html` + + ${titleCase(`${variant} ${appearance}`)} + + ` + )} +
+
+ ${appearances.map( + (appearance) => html` + + ${titleCase(`${variant} ${appearance}`)} + + ` + )} +
+
+ ${appearances.map( + (appearance) => html` + + ${titleCase(`${variant} ${appearance}`)} + + ` + )} +
+ ` + )} +
+
+
+ ` + )} + `; + } + + firstUpdated(changedProps) { + super.firstUpdated(changedProps); + applyThemesOnElement( + this.shadowRoot!.querySelector(".dark"), + { + default_theme: "default", + default_dark_theme: "default", + themes: {}, + darkMode: true, + theme: "default", + }, + undefined, + undefined, + true + ); + } + + static styles = css` + :host { + display: flex; + justify-content: center; + } + .dark, + .light { + display: block; + background-color: var(--primary-background-color); + padding: 0 50px; + } + .button { + padding: unset; + } + ha-card { + margin: 24px auto; + } + .card-content { + display: flex; + flex-direction: column; + gap: 24px; + } + .card-content div { + display: flex; + gap: 8px; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "demo-components-ha-button": DemoHaButton; + } +} diff --git a/src/common/dom/apply_themes_on_element.ts b/src/common/dom/apply_themes_on_element.ts index b986e053a8..0873b3f3e2 100644 --- a/src/common/dom/apply_themes_on_element.ts +++ b/src/common/dom/apply_themes_on_element.ts @@ -77,6 +77,7 @@ export const applyThemesOnElement = ( const rgbLightPrimaryColor = lab2rgb(labBrighten(labPrimaryColor)); themeRules["light-primary-color"] = rgb2hex(rgbLightPrimaryColor); themeRules["dark-primary-color"] = lab2hex(labDarken(labPrimaryColor)); + themeRules["darker-primary-color"] = lab2hex(labDarken(labPrimaryColor, 2)); themeRules["text-primary-color"] = rgbContrast(rgbPrimaryColor, [33, 33, 33]) < 6 ? "#fff" : "#212121"; themeRules["text-light-primary-color"] = diff --git a/src/components/ha-button.ts b/src/components/ha-button.ts index 6fa4a834ee..ae18063886 100644 --- a/src/components/ha-button.ts +++ b/src/components/ha-button.ts @@ -27,21 +27,43 @@ export type Appearance = "accent" | "filled" | "plain"; * * @cssprop --ha-button-font-family - Font family for the button text. * @cssprop --ha-button-font-weight - buttons font weight. - * @cssprop --ha-button-border-width - Border width for the button. - * @cssprop --ha-button-theme-color - Main color for the button. - * @cssprop --ha-button-theme-darker-color - Darker variant of the main color. - * @cssprop --ha-button-theme-lighter-color - Lighter variant of the main color. - * @cssprop --ha-button-height - Height of the button. - * @cssprop --ha-button-border-radius - Border radius for the button. - * @cssprop --ha-button-text-color - Text color for the button. - * @cssprop --ha-button-focus-ring-color - Focus ring color for the button. * @cssprop --ha-button-font-size - Font weight for the button text. + * @cssprop --ha-button-height - Height of the button. * @cssprop --ha-button-padding-inline-start - padding for the button text on the left side. * @cssprop --ha-button-padding-inline-end - padding for the button text on the right side. + * @cssprop --ha-button-border-radius - Border radius for the button. + * @cssprop --ha-button-border-width - Border width for the button. + * @cssprop --ha-button-theme-color - Main color for the button. + * @cssprop --ha-button-theme-dark-color - Dark variant of the main color. + * @cssprop --ha-button-theme-darker-color - Dark variant of the main color. + * @cssprop --ha-button-theme-light-color - Light variant of the main color. + * @cssprop --ha-button-text-color - Text color for the button. + * @cssprop --ha-button-focus-ring-color - Focus ring color for the button. + * @cssprop --ha-button-primary-color - Main color for the primary variant. + * @cssprop --ha-button-primary-light-color - Light color for the primary variant. + * @cssprop --ha-button-primary-dark-color - Dark color for the primary variant. + * @cssprop --ha-button-primary-darker-color - Darker color for the primary variant. + * @cssprop --ha-button-error-color - Main color for the error variant. + * @cssprop --ha-button-error-light-color - Light color for the error variant. + * @cssprop --ha-button-error-dark-color - Dark color for the error variant. + * @cssprop --ha-button-error-darker-color - Darker color for the error variant. + * @cssprop --ha-button-neutral-color - Main color for the neutral variant. + * @cssprop --ha-button-neutral-light-color - Light color for the neutral variant. + * @cssprop --ha-button-neutral-dark-color - Dark color for the neutral variant. + * @cssprop --ha-button-neutral-darker-color - Darker color for the neutral variant. + * @cssprop --ha-button-warning-color - Main color for the warning variant. + * @cssprop --ha-button-warning-light-color - Light color for the warning variant. + * @cssprop --ha-button-warning-dark-color - Dark color for the warning variant. + * @cssprop --ha-button-warning-darker-color - Darker color for the warning variant. + * @cssprop --ha-button-success-color - Main color for the success variant. + * @cssprop --ha-button-success-light-color - Light color for the success variant. + * @cssprop --ha-button-success-dark-color - Dark color for the success variant. + * @cssprop --ha-button-success-darker-color - Darker color for the success variant. * * @attr {("small"|"medium")} size - Sets the button size. * @attr {("primary"|"danger"|"neutral"|"warning"|"success")} variant - Sets the button color variant. "primary" is default. * @attr {("accent"|"filled"|"plain")} appearance - Sets the button appearance. + * @attr {boolean} hideContent - Hides the button content (for overlays). */ @customElement("ha-button") export class HaButton extends Button { @@ -78,41 +100,41 @@ export class HaButton extends Button { --sl-spacing-small: 12px; --sl-spacing-x-small: 8px; - --ha-button-theme-color: var(--primary-color); - --ha-button-theme-darker-color: var(--dark-primary-color); - --ha-button-theme-active-color: #00669c; - --ha-button-theme-lighter-color: #dff3fc; + --ha-button-theme-color: var(--ha-button-primary-color); + --ha-button-theme-light-color: var(--ha-button-primary-light-color); + --ha-button-theme-dark-color: var(--ha-button-primary-dark-color); + --ha-button-theme-darker-color: var(--ha-button-primary-darker-color); line-height: 1; --sl-input-border-width: var(--ha-button-border-width, 0); } :host([variant="danger"]) { - --ha-button-theme-color: #b30532; - --ha-button-theme-darker-color: #64031d; - --ha-button-theme-active-color: #410213; - --ha-button-theme-lighter-color: #ffdedc; + --ha-button-theme-color: var(--ha-button-error-color); + --ha-button-theme-light-color: var(--ha-button-error-light-color); + --ha-button-theme-dark-color: var(--ha-button-error-dark-color); + --ha-button-theme-darker-color: var(--ha-button-error-darker-color); } :host([variant="neutral"]) { - --ha-button-theme-color: #545868; - --ha-button-theme-darker-color: #373a44; - --ha-button-theme-active-color: #1c1d22; - --ha-button-theme-lighter-color: #767986; + --ha-button-theme-color: var(--ha-button-neutral-color); + --ha-button-theme-light-color: var(--ha-button-neutral-light-color); + --ha-button-theme-dark-color: var(--ha-button-neutral-dark-color); + --ha-button-theme-darker-color: var(--ha-button-neutral-darker-color); } :host([variant="warning"]) { - --ha-button-theme-color: #b45f04; - --ha-button-theme-darker-color: #9c5203; - --ha-button-theme-active-color: #693803; - --ha-button-theme-lighter-color: #fef3cd; + --ha-button-theme-color: var(--ha-button-warning-color); + --ha-button-theme-light-color: var(--ha-button-warning-light-color); + --ha-button-theme-dark-color: var(--ha-button-warning-dark-color); + --ha-button-theme-darker-color: var(--ha-button-warning-darker-color); } :host([variant="success"]) { - --ha-button-theme-color: var(--success-color); - --ha-button-theme-darker-color: #275e2a; - --ha-button-theme-active-color: #1a411c; - --ha-button-theme-lighter-color: #5ce463; + --ha-button-theme-color: var(--ha-button-success-color); + --ha-button-theme-light-color: var(--ha-button-success-light-color); + --ha-button-theme-dark-color: var(--ha-button-success-dark-color); + --ha-button-theme-darker-color: var(--ha-button-success-darker-color); } .button { @@ -131,9 +153,10 @@ export class HaButton extends Button { .button--standard.button--neutral, .button--standard.button--danger, .button--standard.button--warning, - .button--standard.button--success { + .button--standard.button--success, + :host([appearance="filled"]) .button:hover:not(.button--disabled) { background-color: var(--ha-button-theme-color); - color: var(--ha-button-text-color, var(--white-color)); + color: var(--ha-button-text-color, var(--text-primary-color)); } .button:hover:not(.button--disabled), .button--standard.button--default:hover:not(.button--disabled), @@ -141,9 +164,10 @@ export class HaButton extends Button { .button--standard.button--neutral:hover:not(.button--disabled), .button--standard.button--warning:hover:not(.button--disabled), .button--standard.button--success:hover:not(.button--disabled), - .button--standard.button--danger:hover:not(.button--disabled) { - background-color: var(--ha-button-theme-darker-color); - color: var(--ha-button-text-color, var(--white-color)); + .button--standard.button--danger:hover:not(.button--disabled), + :host([appearance="filled"]) .button:active:not(.button--disabled) { + background-color: var(--ha-button-theme-dark-color); + color: var(--ha-button-text-color, var(--text-primary-color)); } .button--standard.button--default:active:not(.button--disabled), @@ -153,34 +177,22 @@ export class HaButton extends Button { .button--standard.button--warning:active:not(.button--disabled), .button--standard.button--success:active:not(.button--disabled), .button:active:not(.button--disabled) { - background-color: var(--ha-button-theme-active-color); - color: var(--ha-button-text-color, var(--white-color)); - } - - :host([appearance="filled"]) .button { - background-color: var(--ha-button-theme-lighter-color); - color: var(--ha-button-text-color, var(--ha-button-theme-color)); - } - :host([appearance="filled"]) .button:hover:not(.button--disabled) { - background-color: var(--ha-button-theme-color); - color: var(--white-color); - } - :host([appearance="filled"]) .button:active:not(.button--disabled) { background-color: var(--ha-button-theme-darker-color); - color: var(--white-color); + color: var(--ha-button-text-color, var(--text-primary-color)); } + :host([appearance="filled"]) .button, + :host([appearance="plain"]) .button:hover:not(.button--disabled) { + background-color: var(--ha-button-theme-light-color); + color: var(--ha-button-theme-color); + } :host([appearance="plain"]) .button { background-color: transparent; - color: var(--ha-button-text-color, var(--ha-button-theme-color)); - } - :host([appearance="plain"]) .button:hover:not(.button--disabled) { - background-color: var(--ha-button-theme-lighter-color); - color: var(--ha-button-text-color, var(--ha-button-theme-darker-color)); + color: var(--ha-button-theme-color); } :host([appearance="plain"]) .button:active:not(.button--disabled) { background-color: var(--ha-button-theme-color); - color: var(--white-color); + color: var(--ha-button-text-color, var(--text-primary-color)); } .button.button--medium .button__label { diff --git a/src/resources/theme/color.globals.ts b/src/resources/theme/color.globals.ts index e80932117e..003f28f48c 100644 --- a/src/resources/theme/color.globals.ts +++ b/src/resources/theme/color.globals.ts @@ -17,6 +17,7 @@ export const colorStyles = css` /* main interface colors */ --primary-color: #03a9f4; --dark-primary-color: #0288d1; + --darker-primary-color: #016194; --light-primary-color: #b3e5fc; --accent-color: #ff9800; --divider-color: rgba(0, 0, 0, 0.12); @@ -290,6 +291,32 @@ export const colorStyles = css` ); --chip-background-color: rgba(var(--rgb-primary-text-color), 0.15); + /* ha-button */ + --ha-button-primary-color: var(--primary-color); + --ha-button-primary-light-color: var(--light-primary-color); + --ha-button-primary-dark-color: var(--dark-primary-color); + --ha-button-primary-darker-color: var(--darker-primary-color); + + --ha-button-error-color: #B30532; + --ha-button-error-light-color: #FFDEDC; + --ha-button-error-dark-color: #6e021f; + --ha-button-error-darker-color: #530016; + + --ha-button-warning-color: var(--warning-color); + --ha-button-warning-light-color: #fae3b9c1; + --ha-button-warning-dark-color: #ce8600; + --ha-button-warning-darker-color: #a36a00; + + --ha-button-success-color: var(--success-color); + --ha-button-success-light-color: #add1ae; + --ha-button-success-dark-color: #317534; + --ha-button-success-darker-color: #225524; + + --ha-button-neutral-color: #545868; + --ha-button-neutral-light-color: #d2d5e0; + --ha-button-neutral-dark-color: #3c3f4b; + --ha-button-neutral-darker-color: #262830; + /* Vaadin */ --material-body-text-color: var(--primary-text-color); --material-background-color: var(--card-background-color); @@ -352,6 +379,11 @@ const darkColorStyles = css` --map-filter: invert(0.9) hue-rotate(170deg) brightness(1.5) contrast(1.2) saturate(0.3); --disabled-color: #464646; + + --ha-button-primary-light-color: #4082a040; + --ha-button-warning-light-color: #917b54c1; + --ha-button-neutral-color: #d9dae0; + --ha-button-neutral-light-color: #6a7081; } `; export const colorDerivedVariables = extractDerivedVars(colorStyles);