From 376831d50d0886b728c02fba709606020f65c32c Mon Sep 17 00:00:00 2001 From: Chris Holt <13071055+chrisdholt@users.noreply.github.com> Date: Mon, 4 May 2026 22:08:24 -0700 Subject: [PATCH 1/2] remove behaviors for forced-colors and match-media directives --- ...-0a4eeb00-f707-486c-b57e-9db249a4db83.json | 7 + .../web-components/docs/web-components.api.md | 34 ---- .../src/anchor-button/anchor-button.styles.ts | 9 +- .../web-components/src/badge/badge.styles.ts | 9 +- .../src/button/button.styles.ts | 10 +- .../src/checkbox/checkbox.styles.ts | 9 +- .../compound-button/compound-button.styles.ts | 9 +- .../src/dialog/dialog.styles.ts | 9 +- .../src/divider/divider.styles.ts | 10 +- packages/web-components/src/index.ts | 8 - .../web-components/src/link/link.styles.ts | 10 +- .../src/menu-item/menu-item.styles.ts | 10 +- .../src/progress-bar/progress-bar.styles.ts | 10 +- .../web-components/src/radio/radio.styles.ts | 9 +- .../rating-display/rating-display.styles.ts | 10 +- .../src/slider/slider.styles.ts | 10 +- .../src/spinner/spinner.styles.ts | 10 +- .../src/switch/switch.styles.ts | 9 +- packages/web-components/src/tab/tab.styles.ts | 10 +- .../src/textarea/textarea.styles.ts | 9 +- .../src/toggle-button/toggle-button.styles.ts | 9 +- .../match-media-stylesheet-behavior.ts | 187 ------------------ packages/web-components/src/utils/index.ts | 1 - 23 files changed, 88 insertions(+), 320 deletions(-) create mode 100644 change/@fluentui-web-components-0a4eeb00-f707-486c-b57e-9db249a4db83.json delete mode 100644 packages/web-components/src/utils/behaviors/match-media-stylesheet-behavior.ts diff --git a/change/@fluentui-web-components-0a4eeb00-f707-486c-b57e-9db249a4db83.json b/change/@fluentui-web-components-0a4eeb00-f707-486c-b57e-9db249a4db83.json new file mode 100644 index 00000000000000..a78e9fe18b37bb --- /dev/null +++ b/change/@fluentui-web-components-0a4eeb00-f707-486c-b57e-9db249a4db83.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "fix: remove behaviors in favor of direct css inclusion", + "packageName": "@fluentui/web-components", + "email": "13071055+chrisdholt@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/docs/web-components.api.md b/packages/web-components/docs/web-components.api.md index a10902f8bf576b..b09ad94c0234c6 100644 --- a/packages/web-components/docs/web-components.api.md +++ b/packages/web-components/docs/web-components.api.md @@ -11,8 +11,6 @@ import { ElementStyles } from '@microsoft/fast-element'; import { ElementViewTemplate } from '@microsoft/fast-element'; import { FASTElement } from '@microsoft/fast-element'; import { FASTElementDefinition } from '@microsoft/fast-element'; -import type { HostBehavior } from '@microsoft/fast-element'; -import type { HostController } from '@microsoft/fast-element'; import { HTMLDirective } from '@microsoft/fast-element'; import { Orientation } from '@microsoft/fast-web-utilities'; import { SyntheticViewTemplate } from '@microsoft/fast-element'; @@ -2517,9 +2515,6 @@ export const curveEasyEaseMax = "var(--curveEasyEaseMax)"; // @public export const curveLinear = "var(--curveLinear)"; -// @public -export const darkModeStylesheetBehavior: (styles: ElementStyles) => MatchMediaStyleSheetBehavior; - // @public export class Dialog extends FASTElement { ariaDescribedby?: string; @@ -2980,9 +2975,6 @@ export const fontWeightRegular = "var(--fontWeightRegular)"; // @public export const fontWeightSemibold = "var(--fontWeightSemibold)"; -// @public -export const forcedColorsStylesheetBehavior: (styles: ElementStyles) => MatchMediaStyleSheetBehavior; - // @public export const getDirection: (rootNode: HTMLElement) => Direction; @@ -3088,9 +3080,6 @@ export const LabelWeight: { // @public export type LabelWeight = ValuesOf; -// @public -export const lightModeStylesheetBehavior: (styles: ElementStyles) => MatchMediaStyleSheetBehavior; - // @public export const lineHeightBase100 = "var(--lineHeightBase100)"; @@ -3201,29 +3190,6 @@ export const ListboxTemplate: ElementViewTemplate; // @public export function listboxTemplate(): ElementViewTemplate; -// @public -export abstract class MatchMediaBehavior implements HostBehavior { - constructor(query: MediaQueryList); - connectedCallback(controller: HostController): void; - protected abstract constructListener(controller: HostController): MediaQueryListListener; - disconnectedCallback(controller: HostController): void; - readonly query: MediaQueryList; -} - -// @public -export class MatchMediaStyleSheetBehavior extends MatchMediaBehavior { - constructor(query: MediaQueryList, styles: ElementStyles); - protected constructListener(controller: HostController): MediaQueryListListener; - readonly query: MediaQueryList; - // @internal - removedCallback(controller: HostController): void; - readonly styles: ElementStyles; - static with(query: MediaQueryList): (styles: ElementStyles) => MatchMediaStyleSheetBehavior; -} - -// @public -export type MediaQueryListListener = (this: MediaQueryList, ev?: MediaQueryListEvent) => void; - // @public export class Menu extends FASTElement { closeMenu: (event?: Event) => void; diff --git a/packages/web-components/src/anchor-button/anchor-button.styles.ts b/packages/web-components/src/anchor-button/anchor-button.styles.ts index 3a1aea693695b1..366375b9014d31 100644 --- a/packages/web-components/src/anchor-button/anchor-button.styles.ts +++ b/packages/web-components/src/anchor-button/anchor-button.styles.ts @@ -1,6 +1,5 @@ import { css } from '@microsoft/fast-element'; import { baseButtonStyles } from '../button/button.styles.js'; -import { forcedColorsStylesheetBehavior } from '../utils/index.js'; // Need to support icon hover styles export const styles = css` @@ -10,11 +9,11 @@ export const styles = css` position: absolute; inset: 0; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host { border-color: LinkText; color: LinkText; } - `), -); + } +`; diff --git a/packages/web-components/src/badge/badge.styles.ts b/packages/web-components/src/badge/badge.styles.ts index 4a015e1e2654f2..55df66bf7832f2 100644 --- a/packages/web-components/src/badge/badge.styles.ts +++ b/packages/web-components/src/badge/badge.styles.ts @@ -1,5 +1,4 @@ import { css } from '@microsoft/fast-element'; -import { forcedColorsStylesheetBehavior } from '../utils/index.js'; import { badgeBaseStyles, badgeFilledStyles, @@ -32,12 +31,12 @@ export const styles = css` ${badgeFilledStyles} ${badgeSizeStyles} ${badgeBaseStyles} -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host, :host([appearance='outline']), :host([appearance='tint']) { border-color: CanvasText; } - `), -); + } +`; diff --git a/packages/web-components/src/button/button.styles.ts b/packages/web-components/src/button/button.styles.ts index a015210f34fd1e..21e31703a57783 100644 --- a/packages/web-components/src/button/button.styles.ts +++ b/packages/web-components/src/button/button.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { borderRadiusCircular, borderRadiusLarge, @@ -310,8 +310,8 @@ export const styles = css` border-color: transparent; background-color: ${colorTransparentBackground}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host { background-color: ButtonFace; color: ButtonText; @@ -332,5 +332,5 @@ export const styles = css` color: GrayText; border-color: ButtonText; } - `), -); + } +`; diff --git a/packages/web-components/src/checkbox/checkbox.styles.ts b/packages/web-components/src/checkbox/checkbox.styles.ts index b733b74d8697c7..d3d07c3ab1bb2d 100644 --- a/packages/web-components/src/checkbox/checkbox.styles.ts +++ b/packages/web-components/src/checkbox/checkbox.styles.ts @@ -22,7 +22,6 @@ import { strokeWidthThick, strokeWidthThin, } from '../theme/design-tokens.js'; -import { forcedColorsStylesheetBehavior } from '../utils/behaviors/match-media-stylesheet-behavior.js'; import { display } from '../utils/display.js'; /** Checkbox styles @@ -153,8 +152,8 @@ export const styles = css` :host([disabled]${checkedState}) .checked-indicator { color: ${colorNeutralStrokeDisabled}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host { border-color: FieldText; } @@ -196,5 +195,5 @@ export const styles = css` :host([disabled]${checkedState}) .checked-indicator { color: GrayText; } - `), -); + } +`; diff --git a/packages/web-components/src/compound-button/compound-button.styles.ts b/packages/web-components/src/compound-button/compound-button.styles.ts index d41888d2a445e1..f506f4fdc2be64 100644 --- a/packages/web-components/src/compound-button/compound-button.styles.ts +++ b/packages/web-components/src/compound-button/compound-button.styles.ts @@ -18,7 +18,6 @@ import { spacingHorizontalSNudge, spacingHorizontalXS, } from '../theme/design-tokens.js'; -import { forcedColorsStylesheetBehavior } from '../utils/index.js'; // Need to support icon hover styles export const styles = css` @@ -118,11 +117,11 @@ export const styles = css` :host([size='large']) ::slotted([slot='description']) { font-size: ${fontSizeBase300}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host([appearance='primary']:not(:hover, :focus-visible, :disabled, [disabled-focusable])) ::slotted([slot='description']) { color: HighlightText; } - `), -); + } +`; diff --git a/packages/web-components/src/dialog/dialog.styles.ts b/packages/web-components/src/dialog/dialog.styles.ts index 107086b4802d6c..4bfeda7e0262f4 100644 --- a/packages/web-components/src/dialog/dialog.styles.ts +++ b/packages/web-components/src/dialog/dialog.styles.ts @@ -12,7 +12,6 @@ import { shadow64, strokeWidthThin, } from '../theme/design-tokens.js'; -import { forcedColorsStylesheetBehavior } from '../utils/behaviors/match-media-stylesheet-behavior.js'; /** Dialog styles * @public @@ -95,12 +94,12 @@ export const styles = css` } } } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { @layer base { dialog { border: ${strokeWidthThin} solid ${colorTransparentStroke}; } } - `), -); + } +`; diff --git a/packages/web-components/src/divider/divider.styles.ts b/packages/web-components/src/divider/divider.styles.ts index 48ef841e9c2361..174aca4bf1c033 100644 --- a/packages/web-components/src/divider/divider.styles.ts +++ b/packages/web-components/src/divider/divider.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { colorBrandForeground1, colorBrandStroke1, @@ -123,8 +123,8 @@ export const styles = css` :host([appearance='subtle']) ::slotted(*) { color: ${colorNeutralForeground3}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host([appearance='strong'])::before, :host([appearance='strong'])::after, :host([appearance='brand'])::before, @@ -136,5 +136,5 @@ export const styles = css` background: WindowText; color: WindowText; } - `), -); + } +`; diff --git a/packages/web-components/src/index.ts b/packages/web-components/src/index.ts index f7915ba7a6fd34..314e4b7f441440 100644 --- a/packages/web-components/src/index.ts +++ b/packages/web-components/src/index.ts @@ -331,13 +331,5 @@ export { export { BaseTree, Tree, TreeDefinition, TreeTemplate, TreeStyles } from './tree/index.js'; export { TreeItem, TreeItemDefinition, TreeItemTemplate, TreeItemStyles } from './tree-item/index.js'; export type { isTreeItem, TreeItemAppearance, TreeItemSize } from './tree-item/index.js'; -export { - darkModeStylesheetBehavior, - forcedColorsStylesheetBehavior, - lightModeStylesheetBehavior, - MatchMediaBehavior, - MatchMediaStyleSheetBehavior, -} from './utils/behaviors/match-media-stylesheet-behavior.js'; -export type { MediaQueryListListener } from './utils/behaviors/match-media-stylesheet-behavior.js'; export { getDirection } from './utils/direction.js'; export { display } from './utils/display.js'; diff --git a/packages/web-components/src/link/link.styles.ts b/packages/web-components/src/link/link.styles.ts index d7142e2ab0edaf..635eb8fb07f0b3 100644 --- a/packages/web-components/src/link/link.styles.ts +++ b/packages/web-components/src/link/link.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { colorBrandForegroundLink, colorBrandForegroundLinkHover, @@ -75,10 +75,10 @@ export const styles = css` position: absolute; inset: 0; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host { color: LinkText; } - `), -); + } +`; diff --git a/packages/web-components/src/menu-item/menu-item.styles.ts b/packages/web-components/src/menu-item/menu-item.styles.ts index c1412351b95769..bf2b23690889d8 100644 --- a/packages/web-components/src/menu-item/menu-item.styles.ts +++ b/packages/web-components/src/menu-item/menu-item.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { borderRadiusMedium, colorCompoundBrandForeground1Pressed, @@ -169,12 +169,12 @@ export const styles = css` } } } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host(${disabledState}), :host(${disabledState}) ::slotted([slot='start']), :host(${disabledState}) ::slotted([slot='end']) { color: GrayText; } - `), -); + } +`; diff --git a/packages/web-components/src/progress-bar/progress-bar.styles.ts b/packages/web-components/src/progress-bar/progress-bar.styles.ts index 076a81830aec83..96e4c5086c2d2b 100644 --- a/packages/web-components/src/progress-bar/progress-bar.styles.ts +++ b/packages/web-components/src/progress-bar/progress-bar.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { borderRadiusMedium, borderRadiusNone, @@ -103,8 +103,8 @@ export const styles = css` inset-inline-start: 100%; } } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host { background-color: CanvasText; } @@ -112,5 +112,5 @@ export const styles = css` :host(:is([validation-state='success'], [validation-state='warning'], [validation-state='error'])) .indicator { background-color: Highlight; } - `), -); + } +`; diff --git a/packages/web-components/src/radio/radio.styles.ts b/packages/web-components/src/radio/radio.styles.ts index 99fbc204f886f7..58d2e42735a2ed 100644 --- a/packages/web-components/src/radio/radio.styles.ts +++ b/packages/web-components/src/radio/radio.styles.ts @@ -19,7 +19,6 @@ import { strokeWidthThick, strokeWidthThin, } from '../theme/design-tokens.js'; -import { forcedColorsStylesheetBehavior } from '../utils/behaviors/match-media-stylesheet-behavior.js'; import { display } from '../utils/display.js'; /** @@ -106,8 +105,8 @@ export const styles = css` :host(${checkedState}${disabledState}) .checked-indicator { background-color: ${colorNeutralStrokeDisabled}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host { border-color: FieldText; } @@ -141,5 +140,5 @@ export const styles = css` :host(${disabledState}${checkedState}) .checked-indicator { background-color: GrayText; } - `), -); + } +`; diff --git a/packages/web-components/src/rating-display/rating-display.styles.ts b/packages/web-components/src/rating-display/rating-display.styles.ts index 1db3210d73603a..0800de84951a6c 100644 --- a/packages/web-components/src/rating-display/rating-display.styles.ts +++ b/packages/web-components/src/rating-display/rating-display.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { colorBrandBackground2, colorBrandForeground1, @@ -147,8 +147,8 @@ export const styles = css` :host([size='large']) ::slotted([slot='count'])::before { margin-inline: ${spacingHorizontalSNudge}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { .display { --_icon-color-value: CanvasText; --_icon-color-empty: Canvas; @@ -164,5 +164,5 @@ export const styles = css` mask: inherit; mask-image: var(--_mask-image-outlined); } - `), -); + } +`; diff --git a/packages/web-components/src/slider/slider.styles.ts b/packages/web-components/src/slider/slider.styles.ts index 7a2b21644fb75c..44d1ac03c7d91b 100644 --- a/packages/web-components/src/slider/slider.styles.ts +++ b/packages/web-components/src/slider/slider.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { borderRadiusCircular, borderRadiusMedium, @@ -189,8 +189,8 @@ export const styles = css` .track::before { background-color: var(--rail-color); } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { .track:hover, .track:active, .track { @@ -207,5 +207,5 @@ export const styles = css` .track::before { background: Highlight; } - `), -); + } +`; diff --git a/packages/web-components/src/spinner/spinner.styles.ts b/packages/web-components/src/spinner/spinner.styles.ts index cf1b783a8c142f..7334ec356986b7 100644 --- a/packages/web-components/src/spinner/spinner.styles.ts +++ b/packages/web-components/src/spinner/spinner.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { colorBrandStroke1, colorBrandStroke2, @@ -154,8 +154,8 @@ export const styles = css` transform: rotate(70deg); } } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { .background { display: none; } @@ -164,5 +164,5 @@ export const styles = css` border-block-start-color: Highlight; border-right-color: Highlight; } - `), -); + } +`; diff --git a/packages/web-components/src/switch/switch.styles.ts b/packages/web-components/src/switch/switch.styles.ts index 987de36f85173d..d92bcbc38cf57b 100644 --- a/packages/web-components/src/switch/switch.styles.ts +++ b/packages/web-components/src/switch/switch.styles.ts @@ -26,7 +26,6 @@ import { spacingHorizontalXXS, strokeWidthThick, } from '../theme/design-tokens.js'; -import { forcedColorsStylesheetBehavior } from '../utils/behaviors/match-media-stylesheet-behavior.js'; import { display } from '../utils/display.js'; export const styles = css` @@ -124,8 +123,8 @@ export const styles = css` outline-offset: 1px; box-shadow: ${shadow4}, 0 0 0 2px ${colorStrokeFocus2}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host { border-color: InactiveBorder; } @@ -149,5 +148,5 @@ export const styles = css` :host(${checkedState}:disabled) .checked-indicator { background-color: GrayText; } - `), -); + } +`; diff --git a/packages/web-components/src/tab/tab.styles.ts b/packages/web-components/src/tab/tab.styles.ts index 4bf64dbede3c64..fcfd0823ea2cfd 100644 --- a/packages/web-components/src/tab/tab.styles.ts +++ b/packages/web-components/src/tab/tab.styles.ts @@ -1,5 +1,5 @@ import { css } from '@microsoft/fast-element'; -import { display, forcedColorsStylesheetBehavior } from '../utils/index.js'; +import { display } from '../utils/index.js'; import { borderRadiusCircular, borderRadiusMedium, @@ -113,10 +113,10 @@ export const styles = css` :host([data-hasIndent]) .tab-content { grid-column: 2; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host([aria-selected='true'])::after { background-color: Highlight; } - `), -); + } +`; diff --git a/packages/web-components/src/textarea/textarea.styles.ts b/packages/web-components/src/textarea/textarea.styles.ts index c9c0d6bead88db..07ea67b0caa8fe 100644 --- a/packages/web-components/src/textarea/textarea.styles.ts +++ b/packages/web-components/src/textarea/textarea.styles.ts @@ -42,7 +42,6 @@ import { spacingVerticalXS, strokeWidthThin, } from '../theme/design-tokens.js'; -import { forcedColorsStylesheetBehavior } from '../utils/behaviors/match-media-stylesheet-behavior.js'; import { display } from '../utils/display.js'; import { userInvalidState } from '../styles/states/index.js'; @@ -286,8 +285,8 @@ export const styles: ElementStyles = css` color: ${colorNeutralForegroundInverted}; background-color: ${colorNeutralBackgroundInverted}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host { --border-color: FieldText; --border-block-end-color: FieldText; @@ -308,5 +307,5 @@ export const styles: ElementStyles = css` --border-block-end-color: GrayText; --placeholder-color: GrayText; } - `), -); + } +`; diff --git a/packages/web-components/src/toggle-button/toggle-button.styles.ts b/packages/web-components/src/toggle-button/toggle-button.styles.ts index cc4ebc3462d06c..0304d8e7612867 100644 --- a/packages/web-components/src/toggle-button/toggle-button.styles.ts +++ b/packages/web-components/src/toggle-button/toggle-button.styles.ts @@ -26,7 +26,6 @@ import { colorTransparentBackgroundSelected, strokeWidthThin, } from '../theme/design-tokens.js'; -import { forcedColorsStylesheetBehavior } from '../utils/behaviors/match-media-stylesheet-behavior.js'; import { pressedState } from '../styles/states/index.js'; /** @@ -116,8 +115,8 @@ export const styles = css` :host(${pressedState}[appearance='transparent']:active) { color: ${colorNeutralForeground2BrandPressed}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { :host(${pressedState}), :host( ${pressedState}:is([appearance='primary'], [appearance='subtle'], [appearance='outline'], [appearance='transparent']) @@ -125,5 +124,5 @@ export const styles = css` background: SelectedItem; color: SelectedItemText; } - `), -); + } +`; diff --git a/packages/web-components/src/utils/behaviors/match-media-stylesheet-behavior.ts b/packages/web-components/src/utils/behaviors/match-media-stylesheet-behavior.ts deleted file mode 100644 index 42f46e7494a716..00000000000000 --- a/packages/web-components/src/utils/behaviors/match-media-stylesheet-behavior.ts +++ /dev/null @@ -1,187 +0,0 @@ -import type { ElementStyles, HostBehavior, HostController } from '@microsoft/fast-element'; - -/** - * An event listener fired by a {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList | MediaQueryList }. - * @public - */ -export type MediaQueryListListener = (this: MediaQueryList, ev?: MediaQueryListEvent) => void; - -/** - * An abstract behavior to react to media queries. Implementations should implement - * the `constructListener` method to perform some action based on media query changes. - * - * @public - */ -export abstract class MatchMediaBehavior implements HostBehavior { - /** - * The behavior needs to operate on element instances but elements might share a behavior instance. - * To ensure proper attachment / detachment per instance, we construct a listener for - * each bind invocation and cache the listeners by element reference. - */ - private listenerCache = new WeakMap(); - - /** - * The media query that the behavior operates on. - */ - public readonly query: MediaQueryList; - - /** - * - * @param query - The media query to operate from. - */ - constructor(query: MediaQueryList) { - this.query = query; - } - - /** - * Constructs a function that will be invoked with the MediaQueryList context - * @param controller - The host controller orchestrating this behavior. - */ - protected abstract constructListener(controller: HostController): MediaQueryListListener; - - /** - * Binds the behavior to the element. - * @param controller - The host controller orchestrating this behavior. - */ - connectedCallback(controller: HostController) { - const { query } = this; - let listener = this.listenerCache.get(controller); - - if (!listener) { - listener = this.constructListener(controller); - this.listenerCache.set(controller, listener); - } - - // Invoke immediately to add if the query currently matches - listener.bind(query)(); - query.addEventListener('change', listener); - } - - /** - * Unbinds the behavior from the element. - * @param controller - The host controller orchestrating this behavior. - */ - disconnectedCallback(controller: HostController) { - const listener = this.listenerCache.get(controller); - if (listener) { - this.query.removeEventListener('change', listener); - } - } -} - -/** - * A behavior to add or remove a stylesheet from an element based on a media query. The behavior ensures that - * styles are applied while the a query matches the environment and that styles are not applied if the query does - * not match the environment. - * - * @public - */ -export class MatchMediaStyleSheetBehavior extends MatchMediaBehavior { - /** - * The media query that the behavior operates on. - */ - public readonly query!: MediaQueryList; - - /** - * The styles object to be managed by the behavior. - */ - public readonly styles: ElementStyles; - - /** - * Constructs a {@link MatchMediaStyleSheetBehavior} instance. - * @param query - The media query to operate from. - * @param styles - The styles to coordinate with the query. - */ - constructor(query: MediaQueryList, styles: ElementStyles) { - super(query); - this.styles = styles; - } - - /** - * Defines a function to construct {@link MatchMediaStyleSheetBehavior | MatchMediaStyleSheetBehaviors} for - * a provided query. - * @param query - The media query to operate from. - * - * @public - * @example - * - * ```ts - * import { css } from "@microsoft/fast-element"; - * import { MatchMediaStyleSheetBehavior } from "@fluentui/web-components"; - * - * const landscapeBehavior = MatchMediaStyleSheetBehavior.with( - * window.matchMedia("(orientation: landscape)") - * ); - * - * const styles = css` - * :host { - * width: 200px; - * height: 400px; - * } - * ` - * .withBehaviors(landscapeBehavior(css` - * :host { - * width: 400px; - * height: 200px; - * } - * `)) - * ``` - */ - public static with(query: MediaQueryList) { - return (styles: ElementStyles) => { - return new MatchMediaStyleSheetBehavior(query, styles); - }; - } - - /** - * Constructs a match-media listener for a provided element. - * @param source - the element for which to attach or detach styles. - */ - protected constructListener(controller: HostController): MediaQueryListListener { - let attached = false; - const styles = this.styles; - - return function listener(this: { matches: boolean }) { - const { matches } = this; - - if (matches && !attached) { - controller.addStyles(styles); - attached = matches; - } else if (!matches && attached) { - controller.removeStyles(styles); - attached = matches; - } - }; - } - - /** - * Unbinds the behavior from the element. - * @param controller - The host controller orchestrating this behavior. - * @internal - */ - public removedCallback(controller: HostController): void { - controller.removeStyles(this.styles); - } -} - -/** - * This can be used to construct a behavior to apply a forced-colors only stylesheet. - * @public - */ -export const forcedColorsStylesheetBehavior = MatchMediaStyleSheetBehavior.with(window.matchMedia('(forced-colors)')); - -/** - * This can be used to construct a behavior to apply a prefers color scheme: dark only stylesheet. - * @public - */ -export const darkModeStylesheetBehavior = MatchMediaStyleSheetBehavior.with( - window.matchMedia('(prefers-color-scheme: dark)'), -); - -/** - * This can be used to construct a behavior to apply a prefers color scheme: light only stylesheet. - * @public - */ -export const lightModeStylesheetBehavior = MatchMediaStyleSheetBehavior.with( - window.matchMedia('(prefers-color-scheme: light)'), -); diff --git a/packages/web-components/src/utils/index.ts b/packages/web-components/src/utils/index.ts index d0e30727270a87..28802ac6dd4306 100644 --- a/packages/web-components/src/utils/index.ts +++ b/packages/web-components/src/utils/index.ts @@ -4,5 +4,4 @@ export * from './typings.js'; export * from './template-helpers.js'; export * from './whitespace-filter.js'; export * from './display.js'; -export * from './behaviors/match-media-stylesheet-behavior.js'; export * from './support.js'; From b4054272e056c65726ff2651a603bb7f1211d6cc Mon Sep 17 00:00:00 2001 From: Chris Holt <13071055+chrisdholt@users.noreply.github.com> Date: Mon, 4 May 2026 22:30:12 -0700 Subject: [PATCH 2/2] fix(charts): replace removed forcedColorsStylesheetBehavior with native @media query The web-components package removed the forcedColorsStylesheetBehavior export and withBehaviors API. Update chart-web-components donut-chart and horizontal-bar-chart styles to use native @media (forced-colors: active) instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...-components-0b344c29-d1f5-4a0a-98e8-08d0cb7c3142.json | 7 +++++++ .../src/donut-chart/donut-chart.styles.ts | 9 ++++----- .../horizontal-bar-chart/horizontal-bar-chart.styles.ts | 9 ++++----- 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 change/@fluentui-chart-web-components-0b344c29-d1f5-4a0a-98e8-08d0cb7c3142.json diff --git a/change/@fluentui-chart-web-components-0b344c29-d1f5-4a0a-98e8-08d0cb7c3142.json b/change/@fluentui-chart-web-components-0b344c29-d1f5-4a0a-98e8-08d0cb7c3142.json new file mode 100644 index 00000000000000..c635c1dc0b33a9 --- /dev/null +++ b/change/@fluentui-chart-web-components-0b344c29-d1f5-4a0a-98e8-08d0cb7c3142.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: remove behaviors in favor of direct css inclusion", + "packageName": "@fluentui/chart-web-components", + "email": "13071055+chrisdholt@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/charts/chart-web-components/src/donut-chart/donut-chart.styles.ts b/packages/charts/chart-web-components/src/donut-chart/donut-chart.styles.ts index 5833ddfa60df27..7c1063bebe9789 100644 --- a/packages/charts/chart-web-components/src/donut-chart/donut-chart.styles.ts +++ b/packages/charts/chart-web-components/src/donut-chart/donut-chart.styles.ts @@ -9,7 +9,6 @@ import { colorStrokeFocus2, colorTransparentStroke, display, - forcedColorsStylesheetBehavior, spacingHorizontalL, spacingHorizontalNone, spacingHorizontalS, @@ -139,8 +138,8 @@ export const styles = css` .tooltip-content-y { ${typographyTitle2Styles} } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { .text-inside-donut { fill: CanvasText; } @@ -155,5 +154,5 @@ export const styles = css` forced-color-adjust: auto; color: CanvasText; } - `), -); + } +`; diff --git a/packages/charts/chart-web-components/src/horizontal-bar-chart/horizontal-bar-chart.styles.ts b/packages/charts/chart-web-components/src/horizontal-bar-chart/horizontal-bar-chart.styles.ts index 9932537a89f26d..6e19763d1d66dc 100644 --- a/packages/charts/chart-web-components/src/horizontal-bar-chart/horizontal-bar-chart.styles.ts +++ b/packages/charts/chart-web-components/src/horizontal-bar-chart/horizontal-bar-chart.styles.ts @@ -5,7 +5,6 @@ import { colorNeutralForeground1, colorNeutralStrokeAccessible, display, - forcedColorsStylesheetBehavior, shadow4, spacingHorizontalL, spacingHorizontalNone, @@ -171,8 +170,8 @@ export const styles: ElementStyles = css` ${typographyBody1StrongStyles} color: ${colorNeutralForeground1}; } -`.withBehaviors( - forcedColorsStylesheetBehavior(css` + + @media (forced-colors: active) { .legend-rect, .tooltip-line, .triangle { @@ -186,5 +185,5 @@ export const styles: ElementStyles = css` .bar-label { fill: CanvasText !important; } - `), -); + } +`;