From ddd5766a5ca13f2b36497747970cc4eab9656068 Mon Sep 17 00:00:00 2001 From: Eugene Olonov Date: Thu, 12 Nov 2026 03:14:59 -0800 Subject: [PATCH 1/3] Add a test of the retry link with a custom accent color Snapshots written with the build of main, before the retry link moves to CSS Modules. --- .../sendFailedRetry.accent.html | 71 ++++++++++++++++++ .../sendFailedRetry.accent.html.snap-1.png | Bin 0 -> 5201 bytes .../sendFailedRetry.accent.html.snap-2.png | Bin 0 -> 5361 bytes 3 files changed, 71 insertions(+) create mode 100644 __tests__/html2/activityStatus/sendFailedRetry.accent.html create mode 100644 __tests__/html2/activityStatus/sendFailedRetry.accent.html.snap-1.png create mode 100644 __tests__/html2/activityStatus/sendFailedRetry.accent.html.snap-2.png diff --git a/__tests__/html2/activityStatus/sendFailedRetry.accent.html b/__tests__/html2/activityStatus/sendFailedRetry.accent.html new file mode 100644 index 0000000000..ab7223d650 --- /dev/null +++ b/__tests__/html2/activityStatus/sendFailedRetry.accent.html @@ -0,0 +1,71 @@ + + + + + + +
+ + + + diff --git a/__tests__/html2/activityStatus/sendFailedRetry.accent.html.snap-1.png b/__tests__/html2/activityStatus/sendFailedRetry.accent.html.snap-1.png new file mode 100644 index 0000000000000000000000000000000000000000..8ab09bc90b8222cae16fe2829645702851e0498a GIT binary patch literal 5201 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 N|Lo>ib^6k+e*sAtPp|+0 literal 0 HcmV?d00001 diff --git a/__tests__/html2/activityStatus/sendFailedRetry.accent.html.snap-2.png b/__tests__/html2/activityStatus/sendFailedRetry.accent.html.snap-2.png new file mode 100644 index 0000000000000000000000000000000000000000..1b0e139ef94985ba883ca233e326ead45764dfae GIT binary patch literal 5361 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 F{{W^DsY?I= literal 0 HcmV?d00001 From 2568e7a9784a9a26fa4818d01ce0daa7c335f66b Mon Sep 17 00:00:00 2001 From: Eugene Olonov Date: Thu, 12 Nov 2026 03:23:19 -0800 Subject: [PATCH 2/3] Convert ScreenReaderText, LiveRegionActivity, FixedWidthImage and InlineMarkdown to CSS modules Moves the local emotion styles of the 4 components to a module next to each, with the webchat__ class names (useStyles with webChatPrefixedClassName). - Same rules; ScreenReaderText (0,1,0 -> 0,2,0) and the retry link (0,2,1 -> 0,3,1) gain the .webchat scope, the others keep their specificity. - The retry link reads --webchat__color--accent instead of styleOptions.accent, so the Fluent theme gives it Fluent's accent color. - ScreenReaderText and InlineMarkdown get webchat__screen-reader-text and webchat__inline-markdown (they had only an emotion class); the webchat__live-region-activity and webchat__fixed-width-image* names stay. --- .../LiveRegion/LiveRegionActivity.module.css | 10 +++++ .../src/LiveRegion/LiveRegionActivity.tsx | 21 ++------- .../component/src/ScreenReaderText.module.css | 17 +++++++ packages/component/src/ScreenReaderText.tsx | 25 +++-------- .../src/Utils/FixedWidthImage.module.css | 22 +++++++++ .../component/src/Utils/FixedWidthImage.tsx | 45 ++++--------------- .../src/Utils/InlineMarkdown.module.css | 17 +++++++ .../component/src/Utils/InlineMarkdown.tsx | 35 ++------------- 8 files changed, 87 insertions(+), 105 deletions(-) create mode 100644 packages/component/src/LiveRegion/LiveRegionActivity.module.css create mode 100644 packages/component/src/ScreenReaderText.module.css create mode 100644 packages/component/src/Utils/FixedWidthImage.module.css create mode 100644 packages/component/src/Utils/InlineMarkdown.module.css diff --git a/packages/component/src/LiveRegion/LiveRegionActivity.module.css b/packages/component/src/LiveRegion/LiveRegionActivity.module.css new file mode 100644 index 0000000000..9fdb56c3e5 --- /dev/null +++ b/packages/component/src/LiveRegion/LiveRegionActivity.module.css @@ -0,0 +1,10 @@ +:global(.webchat) .live-region-activity { + color: transparent; + height: 1px; + opacity: 0; + overflow: hidden; + position: absolute; + top: 0; + white-space: nowrap; + width: 1px; +} diff --git a/packages/component/src/LiveRegion/LiveRegionActivity.tsx b/packages/component/src/LiveRegion/LiveRegionActivity.tsx index 2a0f235748..8115a85c92 100644 --- a/packages/component/src/LiveRegion/LiveRegionActivity.tsx +++ b/packages/component/src/LiveRegion/LiveRegionActivity.tsx @@ -1,30 +1,17 @@ /* eslint no-magic-numbers: ["error", { "ignore": [2] }] */ import { validateProps, type InferReactProps } from '@msinternal/botframework-webchat-react-valibot'; -import classNames from 'classnames'; +import { useStyles, webChatPrefixedClassName } from '@msinternal/botframework-webchat-styles/react'; import React, { Fragment, useMemo } from 'react'; import { any, object, pipe, readonly } from 'valibot'; -import { useStyleToEmotionObject } from '../hooks/internal/styleToEmotionObject'; import useLocalizeActivityGreetingAlt from '../hooks/internal/useLocalizeActivityGreetingAlt.js'; import useRenderMarkdownAsHTML from '../hooks/useRenderMarkdownAsHTML'; import activityAltText from '../Utils/activityAltText'; +import styles from './LiveRegionActivity.module.css'; import LiveRegionAttachments from './private/LiveRegionAttachments'; import LiveRegionSuggestedActions from './private/LiveRegionSuggestedActions'; -const ROOT_STYLE = { - '&.webchat__live-region-activity': { - color: 'transparent', - height: 1, - opacity: 0, - overflow: 'hidden', - position: 'absolute', - top: 0, - whiteSpace: 'nowrap', - width: 1 - } -}; - const liveRegionActivityPropsSchema = pipe( object({ activity: any() @@ -41,8 +28,8 @@ function LiveRegionActivity(props: LiveRegionActivityProps) { const fallbackText: string | undefined = type === 'message' ? activity.channelData['webchat:fallback-text'] : undefined; const [greetingAlt] = useLocalizeActivityGreetingAlt()(activity); + const classNames = useStyles(styles, webChatPrefixedClassName); const renderMarkdownAsHTML = useRenderMarkdownAsHTML('accessible name'); - const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + ''; const textAlt = useMemo(() => activityAltText(activity, renderMarkdownAsHTML), [activity, renderMarkdownAsHTML]); const validFallbackText = fallbackText && typeof fallbackText === 'string'; @@ -50,7 +37,7 @@ function LiveRegionActivity(props: LiveRegionActivityProps) { // Lines are `role="none"`: on Windows, generic `
` and `

` are exposed as "group", screen readers would read "group" before each line. // The message itself stays in the accessibility tree, as required by `aria-atomic`. return ( -

+
{greetingAlt}
{validFallbackText ? (
{fallbackText}
diff --git a/packages/component/src/ScreenReaderText.module.css b/packages/component/src/ScreenReaderText.module.css new file mode 100644 index 0000000000..0fd0fd94e7 --- /dev/null +++ b/packages/component/src/ScreenReaderText.module.css @@ -0,0 +1,17 @@ +/* .sr-only - This component is intended to be invisible to the visual Web Chat user, but read by the AT when using a screen reader */ +:global(.webchat) .screen-reader-text { + color: transparent; + height: 1px; + overflow: hidden; + position: absolute; + /* + * We need to set top: 0, otherwise, it will repro: + * - Run NVDA + * - Make the transcript long enough to show the scrollbar + * - Press SHIFT-TAB, focus on upload button + * - Press up arrow multiple times + */ + top: 0; + white-space: nowrap; + width: 1px; +} diff --git a/packages/component/src/ScreenReaderText.tsx b/packages/component/src/ScreenReaderText.tsx index a1003392be..f722153a77 100644 --- a/packages/component/src/ScreenReaderText.tsx +++ b/packages/component/src/ScreenReaderText.tsx @@ -1,26 +1,11 @@ /* eslint react/forbid-dom-props: ["off"] */ import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles, webChatPrefixedClassName } from '@msinternal/botframework-webchat-styles/react'; import React, { forwardRef, memo } from 'react'; - import { boolean, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; -import { useStyleToEmotionObject } from './hooks/internal/styleToEmotionObject'; - -const ROOT_STYLE = { - // .sr-only - This component is intended to be invisible to the visual Web Chat user, but read by the AT when using a screen reader - color: 'transparent', - height: 1, - overflow: 'hidden', - position: 'absolute', - // We need to set top: 0, otherwise, it will repro: - // - Run NVDA - // - Make the transcript long enough to show the scrollbar - // - Press SHIFT-TAB, focus on upload button - // - Press up arrow multiple times - top: 0, - whiteSpace: 'nowrap', - width: 1 -}; + +import styles from './ScreenReaderText.module.css'; const screenReaderTextPropsSchema = pipe( object({ @@ -37,7 +22,7 @@ type ScreenReaderTextProps = InferInput; const ScreenReaderText = forwardRef(function ScreenReaderText(props, ref) { const { 'aria-hidden': ariaHidden, id, text } = validateProps(screenReaderTextPropsSchema, props); - const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + ''; + const classNames = useStyles(styles, webChatPrefixedClassName); if (ariaHidden && !id) { console.warn( @@ -46,7 +31,7 @@ const ScreenReaderText = forwardRef(funct } return ( -
+
{text}
); diff --git a/packages/component/src/Utils/FixedWidthImage.module.css b/packages/component/src/Utils/FixedWidthImage.module.css new file mode 100644 index 0000000000..c4a92ed6c1 --- /dev/null +++ b/packages/component/src/Utils/FixedWidthImage.module.css @@ -0,0 +1,22 @@ +:global(.webchat) .fixed-width-image { + display: flex; + justify-content: center; + max-height: var(--webchat__max-height--image-bubble); + min-height: var(--webchat__min-height--image-bubble); + overflow: hidden; + position: relative; + + .fixed-width-image__filler { + visibility: hidden; + width: 100%; + } + + .fixed-width-image__image { + left: 50%; + object-fit: cover; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + width: 100%; + } +} diff --git a/packages/component/src/Utils/FixedWidthImage.tsx b/packages/component/src/Utils/FixedWidthImage.tsx index c3dbabffdb..43b70e2699 100644 --- a/packages/component/src/Utils/FixedWidthImage.tsx +++ b/packages/component/src/Utils/FixedWidthImage.tsx @@ -1,5 +1,6 @@ import { validateProps, type InferReactProps } from '@msinternal/botframework-webchat-react-valibot'; -import classNames from 'classnames'; +import { useStyles, webChatPrefixedClassName } from '@msinternal/botframework-webchat-styles/react'; +import cx from 'classnames'; import React, { memo, type SyntheticEvent } from 'react'; import { args, @@ -17,33 +18,7 @@ import { void_ } from 'valibot'; -import { useStyleToEmotionObject } from '../hooks/internal/styleToEmotionObject'; -import CSSTokens from '../Styles/CSSTokens'; - -const ROOT_STYLE = { - '&.webchat__fixed-width-image': { - display: 'flex', - justifyContent: 'center', - maxHeight: CSSTokens.MaxHeightImageBubble, - minHeight: CSSTokens.MinHeightImageBubble, - overflow: 'hidden', - position: 'relative', - - '& .webchat__fixed-width-image__filler': { - visibility: 'hidden', - width: '100%' - }, - - '& .webchat__fixed-width-image__image': { - left: '50%', - objectFit: 'cover', - position: 'absolute', - top: '50%', - transform: 'translate(-50%, -50%)', - width: '100%' - } - } -}; +import styles from './FixedWidthImage.module.css'; // React calls event handlers with a `SyntheticEvent`, which is not an `Event`, it wraps one in `nativeEvent`. // `custom()` passes the event as-is, `object()` would pass a copy with only the validated properties. @@ -72,16 +47,12 @@ type FixedWidthImageProps = InferReactProps; function FixedWidthImage(props: FixedWidthImageProps) { const { alt, className, onError, onLoad, src } = validateProps(fixedWidthImagePropsSchema, props); + const classNames = useStyles(styles, webChatPrefixedClassName); + return ( -
- - {alt} +
+ + {alt}
); } diff --git a/packages/component/src/Utils/InlineMarkdown.module.css b/packages/component/src/Utils/InlineMarkdown.module.css new file mode 100644 index 0000000000..f9ec009055 --- /dev/null +++ b/packages/component/src/Utils/InlineMarkdown.module.css @@ -0,0 +1,17 @@ +:global(.webchat) .inline-markdown { + button[data-markdown-href] { + appearance: none; + background-color: transparent; + border: 0; + color: var(--webchat__color--accent); + cursor: pointer; + font-family: inherit; + font-size: inherit; + padding: 0; + + @media screen and (forced-colors: active) { + color: LinkText; + text-decoration: underline; + } + } +} diff --git a/packages/component/src/Utils/InlineMarkdown.tsx b/packages/component/src/Utils/InlineMarkdown.tsx index e97dbb6254..9ed9726a10 100644 --- a/packages/component/src/Utils/InlineMarkdown.tsx +++ b/packages/component/src/Utils/InlineMarkdown.tsx @@ -7,7 +7,7 @@ import { stripParagraphContainer } from '@msinternal/botframework-webchat-component-better-link'; import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; -import { useStyleOptions } from 'botframework-webchat-api/hook.js'; +import { useStyles, webChatPrefixedClassName } from '@msinternal/botframework-webchat-styles/react'; import { micromark } from 'micromark'; import React, { useCallback, useMemo } from 'react'; import { @@ -26,8 +26,8 @@ import { type InferInput } from 'valibot'; -import { useStyleToEmotionObject } from '../hooks/internal/styleToEmotionObject'; import createCustomEvent from './createCustomEvent'; +import styles from './InlineMarkdown.module.css'; const referenceEventSchema = union([instance(Event), object({ data: string() })]); @@ -45,34 +45,7 @@ type InlineMarkdownProps = InferInput; const InlineMarkdown = (props: InlineMarkdownProps) => { const { markdown, onReference, references } = validateProps(inlineMarkdownPropsSchema, props); - const [{ accent }] = useStyleOptions(); - const styleToClassName = useStyleToEmotionObject(); - - // We inlined the style here because this style is: - // 1. Internal to Web Chat - // 2. Not customizable from developers (other than setting `styleOptions.accent`) - const className = useMemo( - () => - styleToClassName({ - '& button[data-markdown-href]': { - appearance: 'none', - backgroundColor: 'transparent', - border: 0, - color: accent, - cursor: 'pointer', - fontFamily: 'inherit', - fontSize: 'inherit', - padding: 0 - }, - '@media screen and (forced-colors: active)': { - '& button[data-markdown-href]': { - color: 'LinkText', - textDecoration: 'underline' - } - } - }) + '', - [accent, styleToClassName] - ); + const classNames = useStyles(styles, webChatPrefixedClassName); const html = useMemo(() => { let markdownWithLinkReferenceDefinitions = markdown; @@ -114,7 +87,7 @@ const InlineMarkdown = (props: InlineMarkdownProps) => { [onReference] ); - return ; + return ; }; export default InlineMarkdown; From 2796c2eeb54c7e5ed1719f2e9f29b1d7243068f9 Mon Sep 17 00:00:00 2001 From: Eugene Olonov Date: Fri, 13 Nov 2026 16:42:30 -0800 Subject: [PATCH 3/3] Add the changelog fragment --- .changelog/6052-fix-css-modules-small-components.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/6052-fix-css-modules-small-components.md diff --git a/.changelog/6052-fix-css-modules-small-components.md b/.changelog/6052-fix-css-modules-small-components.md new file mode 100644 index 0000000000..ad66e2e40a --- /dev/null +++ b/.changelog/6052-fix-css-modules-small-components.md @@ -0,0 +1,3 @@ +### Changed + +- Converted the screen reader text, live region activity, fixed-width image and send failed retry link to CSS Modules with `webchat__` class names; in the Fluent theme, the send failed retry link now uses Fluent's accent color, in PR [#6052](https://github.com/microsoft/BotFramework-WebChat/pull/6052), by [@OEvgeny](https://github.com/OEvgeny)