From cf5a75e112273dc6821dd34ee1542c710cb938b6 Mon Sep 17 00:00:00 2001 From: Alex Kaduk Date: Thu, 30 Jul 2026 09:31:32 +0300 Subject: [PATCH 1/2] fix(core): show active state on open dropdown button EIX-84 --- .changeset/dropdown-button-expanded-active.md | 5 ++ .../dropdown-button/dropdown-button.ct.ts | 87 +++++++++++++++++++ .../dropdown-button/dropdown-button.scss | 29 +++++++ 3 files changed, 121 insertions(+) create mode 100644 .changeset/dropdown-button-expanded-active.md diff --git a/.changeset/dropdown-button-expanded-active.md b/.changeset/dropdown-button-expanded-active.md new file mode 100644 index 00000000000..ab46b5ab8d4 --- /dev/null +++ b/.changeset/dropdown-button-expanded-active.md @@ -0,0 +1,5 @@ +--- +'@siemens/ix': patch +--- + +Show an active visual state on `ix-dropdown-button` while the dropdown is open (`aria-expanded`), so the trigger stays clearly linked to the menu. Fixes #1402 diff --git a/packages/core/src/components/dropdown-button/dropdown-button.ct.ts b/packages/core/src/components/dropdown-button/dropdown-button.ct.ts index 2ab8e9ab5b7..94ad06fea47 100644 --- a/packages/core/src/components/dropdown-button/dropdown-button.ct.ts +++ b/packages/core/src/components/dropdown-button/dropdown-button.ct.ts @@ -6,6 +6,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ +import { iconCheckboxes } from '@siemens/ix-icons/icons'; import { expect } from '@playwright/test'; import { regressionTest } from '@utils/test'; @@ -103,6 +104,92 @@ regressionTest( } ); +regressionTest( + 'applies active button appearance while dropdown is expanded', + async ({ page, mount }) => { + await mount(` + + + + `); + + const dropdownButton = page.locator('ix-dropdown-button'); + const trigger = dropdownButton.locator('ix-button'); + + const getTriggerBackground = () => + trigger.evaluate((el) => { + const button = el.shadowRoot?.querySelector('button'); + return button ? getComputedStyle(button).backgroundColor : ''; + }); + + const closedBackground = await getTriggerBackground(); + + await dropdownButton.click(); + await expect(dropdownButton).toHaveAttribute('aria-expanded', 'true'); + await expect(dropdownButton.locator('ix-dropdown')).toBeVisible(); + + const openBackground = await getTriggerBackground(); + expect(openBackground).not.toBe(closedBackground); + + const activeBackground = await dropdownButton.evaluate((el) => { + const probe = document.createElement('div'); + probe.style.backgroundColor = + 'var(--theme-btn-primary--background--active)'; + el.appendChild(probe); + const value = getComputedStyle(probe).backgroundColor; + probe.remove(); + return value; + }); + + expect(openBackground).toBe(activeBackground); + } +); + +regressionTest( + 'applies active icon button appearance while dropdown is expanded', + async ({ page, mount }) => { + await mount( + ` + + + + `, + { + icons: { iconCheckboxes }, + } + ); + + const dropdownButton = page.locator('ix-dropdown-button'); + const trigger = dropdownButton.locator('ix-icon-button'); + + const getTriggerBackground = () => + trigger.evaluate((el) => { + const button = el.shadowRoot?.querySelector('button'); + return button ? getComputedStyle(button).backgroundColor : ''; + }); + + const closedBackground = await getTriggerBackground(); + + await dropdownButton.click(); + await expect(dropdownButton).toHaveAttribute('aria-expanded', 'true'); + + const openBackground = await getTriggerBackground(); + expect(openBackground).not.toBe(closedBackground); + + const activeBackground = await dropdownButton.evaluate((el) => { + const probe = document.createElement('div'); + probe.style.backgroundColor = + 'var(--theme-btn-primary--background--active)'; + el.appendChild(probe); + const value = getComputedStyle(probe).backgroundColor; + probe.remove(); + return value; + }); + + expect(openBackground).toBe(activeBackground); + } +); + regressionTest( 'should not render focus outline on trigger while dropdown is expanded', async ({ page, mount }) => { diff --git a/packages/core/src/components/dropdown-button/dropdown-button.scss b/packages/core/src/components/dropdown-button/dropdown-button.scss index 9a849fcbb1a..db026d234b2 100644 --- a/packages/core/src/components/dropdown-button/dropdown-button.scss +++ b/packages/core/src/components/dropdown-button/dropdown-button.scss @@ -35,6 +35,23 @@ } } +@mixin expanded-active-variant($name) { + --theme-btn-#{$name}--background: var( + --theme-btn-#{$name}--background--active + ); + --theme-btn-#{$name}--background--hover: var( + --theme-btn-#{$name}--background--active + ); + --theme-btn-#{$name}--color: var(--theme-btn-#{$name}--color--active); + --theme-btn-#{$name}--color--hover: var(--theme-btn-#{$name}--color--active); + --theme-btn-#{$name}--border-color: var( + --theme-btn-#{$name}--border-color--active + ); + --theme-btn-#{$name}--border-color--hover: var( + --theme-btn-#{$name}--border-color--active + ); +} + :host { display: inline-block; position: relative; @@ -134,6 +151,18 @@ outline-offset: var(--theme-btn--focus--outline-offset); } +:host([aria-expanded='true']) { + @include expanded-active-variant('primary'); + @include expanded-active-variant('secondary'); + @include expanded-active-variant('tertiary'); + @include expanded-active-variant('subtle-primary'); + @include expanded-active-variant('subtle-secondary'); + @include expanded-active-variant('subtle-tertiary'); + @include expanded-active-variant('danger-primary'); + @include expanded-active-variant('danger-secondary'); + @include expanded-active-variant('danger-tertiary'); +} + :host([aria-expanded='true']:focus-visible) { outline: none; } From 80c8159d5cb51f5fa9bbae3c4987f5362966fb08 Mon Sep 17 00:00:00 2001 From: Alex Kaduk Date: Tue, 4 Aug 2026 17:00:04 +0300 Subject: [PATCH 2/2] refactor(core): route dropdown-button theme colors through vars aliases EIX-84 --- .../dropdown-button/dropdown-button.scss | 54 ++++++++++--------- .../dropdown-button/dropdown-button.vars.scss | 42 +++++++++++++++ 2 files changed, 70 insertions(+), 26 deletions(-) create mode 100644 packages/core/src/components/dropdown-button/dropdown-button.vars.scss diff --git a/packages/core/src/components/dropdown-button/dropdown-button.scss b/packages/core/src/components/dropdown-button/dropdown-button.scss index db026d234b2..c1130f7631d 100644 --- a/packages/core/src/components/dropdown-button/dropdown-button.scss +++ b/packages/core/src/components/dropdown-button/dropdown-button.scss @@ -8,6 +8,7 @@ */ @use 'misc/common-variables' as vars; @use 'mixins/shadow-dom/component'; +@use './dropdown-button.vars' as *; @mixin triangle($name: '') { @if ($name != '') { @@ -15,40 +16,43 @@ } .triangle.#{$name}primary { - color: var(--theme-btn-#{$name}primary--color); + color: var(--ix-dropdown-button-#{$name}primary-color); } .triangle.#{$name}secondary { - color: var(--theme-btn-#{$name}secondary--color); + color: var(--ix-dropdown-button-#{$name}secondary-color); } .triangle.#{$name}tertiary { - color: var(--theme-btn-#{$name}tertiary--color); + color: var(--ix-dropdown-button-#{$name}tertiary-color); } .triangle.#{$name}primary.disabled { - color: var(--theme-btn-#{$name}primary--color--disabled); + color: var(--ix-dropdown-button-#{$name}primary-color-disabled); } .triangle.#{$name}secondary.disabled { - color: var(--theme-btn-#{$name}secondary--color--disabled); + color: var(--ix-dropdown-button-#{$name}secondary-color-disabled); } .triangle.#{$name}tertiary.disabled { - color: var(--theme-btn-#{$name}tertiary--color--disabled); + color: var(--ix-dropdown-button-#{$name}tertiary-color-disabled); } } +// Apply Expanded Active look to nested ix-button / ix-icon-button via their theme vars. @mixin expanded-active-variant($name) { --theme-btn-#{$name}--background: var( - --theme-btn-#{$name}--background--active + --ix-dropdown-button-#{$name}-background-active ); --theme-btn-#{$name}--background--hover: var( - --theme-btn-#{$name}--background--active + --ix-dropdown-button-#{$name}-background-active + ); + --theme-btn-#{$name}--color: var(--ix-dropdown-button-#{$name}-color-active); + --theme-btn-#{$name}--color--hover: var( + --ix-dropdown-button-#{$name}-color-active ); - --theme-btn-#{$name}--color: var(--theme-btn-#{$name}--color--active); - --theme-btn-#{$name}--color--hover: var(--theme-btn-#{$name}--color--active); --theme-btn-#{$name}--border-color: var( - --theme-btn-#{$name}--border-color--active + --ix-dropdown-button-#{$name}-border-color-active ); --theme-btn-#{$name}--border-color--hover: var( - --theme-btn-#{$name}--border-color--active + --ix-dropdown-button-#{$name}-border-color-active ); } @@ -58,8 +62,12 @@ height: vars.$x-large-space; width: auto; - --ix-dropdown-button-border-radius-left: var(--theme-btn--border-radius); - --ix-dropdown-button-border-radius-right: var(--theme-btn--border-radius); + --ix-dropdown-button-border-radius-left: var( + --ix-dropdown-button-border-radius + ); + --ix-dropdown-button-border-radius-right: var( + --ix-dropdown-button-border-radius + ); border-top-left-radius: var(--ix-dropdown-button-border-radius-left); border-bottom-left-radius: var(--ix-dropdown-button-border-radius-left); @@ -114,7 +122,7 @@ border-left: 4px solid transparent; border-top: 0 solid transparent; border-bottom: 4px solid; - color: var(--theme-btn-primary--color); + color: var(--ix-dropdown-button-primary-color); } @include triangle(); @@ -147,20 +155,14 @@ } :host(:focus-visible) { - outline: 1px solid var(--theme-color-focus-bdr); - outline-offset: var(--theme-btn--focus--outline-offset); + outline: 1px solid var(--ix-dropdown-button-focus-outline-color); + outline-offset: var(--ix-dropdown-button-focus-outline-offset); } :host([aria-expanded='true']) { - @include expanded-active-variant('primary'); - @include expanded-active-variant('secondary'); - @include expanded-active-variant('tertiary'); - @include expanded-active-variant('subtle-primary'); - @include expanded-active-variant('subtle-secondary'); - @include expanded-active-variant('subtle-tertiary'); - @include expanded-active-variant('danger-primary'); - @include expanded-active-variant('danger-secondary'); - @include expanded-active-variant('danger-tertiary'); + @each $variant in $dropdown-button-variants { + @include expanded-active-variant($variant); + } } :host([aria-expanded='true']:focus-visible) { diff --git a/packages/core/src/components/dropdown-button/dropdown-button.vars.scss b/packages/core/src/components/dropdown-button/dropdown-button.vars.scss new file mode 100644 index 00000000000..9b9e418cb27 --- /dev/null +++ b/packages/core/src/components/dropdown-button/dropdown-button.vars.scss @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Component token aliases. Keep --theme-* references in this file only. + +$dropdown-button-variants: primary, secondary, tertiary, subtle-primary, + subtle-secondary, subtle-tertiary, danger-primary, danger-secondary, + danger-tertiary; + +@mixin dropdown-button-variant-aliases($name) { + --ix-dropdown-button-#{$name}-color: var(--theme-btn-#{$name}--color); + --ix-dropdown-button-#{$name}-color-disabled: var( + --theme-btn-#{$name}--color--disabled + ); + --ix-dropdown-button-#{$name}-background-active: var( + --theme-btn-#{$name}--background--active + ); + --ix-dropdown-button-#{$name}-color-active: var( + --theme-btn-#{$name}--color--active + ); + --ix-dropdown-button-#{$name}-border-color-active: var( + --theme-btn-#{$name}--border-color--active + ); +} + +:host { + --ix-dropdown-button-border-radius: var(--theme-btn--border-radius); + --ix-dropdown-button-focus-outline-color: var(--theme-color-focus-bdr); + --ix-dropdown-button-focus-outline-offset: var( + --theme-btn--focus--outline-offset + ); + + @each $variant in $dropdown-button-variants { + @include dropdown-button-variant-aliases($variant); + } +}