Skip to content

Fluent: style Web Chat elements through the CSS module theme - #5993

Merged
Eugene (OEvgeny) merged 25 commits into
mainfrom
OEvgeny/fix-css-fluent-context
Oct 31, 2026
Merged

Eugene (OEvgeny) merged 25 commits into
mainfrom
OEvgeny/fix-css-fluent-context

Conversation

@OEvgeny

@OEvgeny Eugene (OEvgeny) commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Fluent styles Web Chat elements by their readable class names (:global(.bubble)), so Web Chat cannot drop or rename them.

Changelog Entry

None: internal refactoring with no visible change, label no-changelog.

Description

Fluent's CSS modules selected 101 class names of Web Chat CSS modules through :global(). Fluent's rules for Web Chat elements now live in one override CSS module, which FluentThemeProvider passes whole through <CSSModuleThemeComposer> (#5984). Each element gets at most two Fluent classes.

Design

  • fluent-theme/src/private/fluentCSSModuleTheme.module.css: rules moved unchanged from Fluent's PartGroupingDecorator, SendBox and Theme modules, keeping order, regions and scope, so the cascade does not change
  • A rule without styles keeps its class with --webchat-preserve-reason: target-class;, which esbuildPlugins.mjs strips after the class map is built; vibe-grep fails the build if one is left

Specific Changes

  • FluentThemeProvider: wraps everything in <CSSModuleThemeComposer>
  • ActivityButton, SendBox: classes for themes, kept by the declaration above
  • fluentCSSModuleTheme.spec.ts: only the override module uses Web Chat local names, never through :global()
  • root package.json: lightningcss dev dependency (1.32.0, as installed today)
  • No snapshot changes in the Fluent html2 tests
  • I have added tests and executed them locally
  • I have added .changelog/<PR#>-<slug>.md
  • I have updated documentation

Review Checklist

This section is for contributors to review your work.

  • Accessibility reviewed (tab order, content readability, alt text, color contrast)
  • Browser and platform compatibilities reviewed
  • CSS styles reviewed (minimal rules, no z-index)
  • Documents reviewed (docs, samples, live demo)
  • Internationalization reviewed (strings, unit formatting)
  • package.json and package-lock.json reviewed
  • Security reviewed (no data URIs, check for nonce leak)
  • Tests reviewed (coverage, legitimacy)

Eugene Olonov added 9 commits October 30, 2026 10:43
FluentThemeProvider wraps everything in <CSSModuleThemeComposer> with Fluent's class names for the
Web Chat local names it styles, so Fluent rules select the feedback button and form by local name
instead of :global() readable class names. fluentCSSModuleOverrides.spec.ts checks the lists against the CSS.
…e CSS module theme

No Fluent CSS module selects a Web Chat CSS module class through :global() anymore;
fluentCSSModuleOverrides.spec.ts now checks this for every Fluent CSS module.
… as a root dev dependency

Same version as the one unplugin-lightningcss already installs (1.32.0), so the lock file only gains the root entry.
Read local names with the build's CSS modules options, so grid-area names are not counted.
pickCSSModule() reads the CSS module through a Map instead of a computed property.
@OEvgeny Eugene (OEvgeny) added the no-changelog No .changelog/ fragment needed (development environment or test-only change) label Oct 30, 2026
Eugene Olonov added 12 commits October 30, 2026 17:47
…-preserve-reason

Lightning CSS drops empty rules and their class names. A rule that only declares a class name for themes now says why
with "--webchat-preserve-reason: target-class"; once the class map is built, the shared CSS plugin removes the
declaration, and the rule if it becomes empty, from the CSS.
…de modules

Rules move verbatim from Theme.module.css to overrides/FeedbackForm.module.css and overrides/ThumbButton.module.css,
one per Web Chat CSS module. FluentThemeProvider passes override modules whole to the CSS module theme, so their local
names need no list; the spec checks they only use Web Chat local names and Fluent's own scoping names.
Rules move verbatim from PartGroupingDecorator.module.css and Theme.module.css to overrides/CodeBlock.module.css,
CodeBlockContent.module.css, CodeBlockCopyButton.module.css and ViewCodeDialog.module.css.
Rules move verbatim from SendBox.module.css and Theme.module.css to overrides/AttachmentBar.module.css,
AttachmentBarItem.module.css, FilePreview.module.css and TextArea.module.css.
Rules move verbatim from Theme.module.css to overrides/TranscriptFocus.module.css.
Rules move verbatim from PartGroupingDecorator.module.css and Theme.module.css to overrides/ActivityStatus.module.css,
BorderFlair.module.css, Bubble.module.css, CarouselFilmStrip.module.css, ComponentIcon.module.css,
MessageStatusLoader.module.css and StackedLayout.module.css.
Rules move verbatim from PartGroupingDecorator.module.css and Theme.module.css to overrides/ActivityButton.module.css,
CollapsibleContent.module.css, CollapsibleGrouping.module.css and PartGroupingActivity.module.css.
Rules move verbatim from PartGroupingDecorator.module.css and Theme.module.css to overrides/LinkDefinitions.module.css,
RenderMarkdown.module.css and TextContent.module.css.
…e modules

Rules move verbatim from PartGroupingDecorator.module.css and Theme.module.css to
overrides/AdaptiveCardRenderer.module.css and AnimatedImageContent.module.css.
…ery Fluent override is in an override module

fluentCSSModuleTheme.spec.ts checks that only override modules use Web Chat local names, that each mirrors a Web Chat
CSS module, and that FluentThemeProvider passes each of them.
…ride

The thumb rule for hover and a focused text area input had landed in its own TextArea override, which loads after the
rest of the block: with the same specificity as the thumb hover rule, it won. The block is whole again, in its original
order.
@OEvgeny

Copy link
Copy Markdown
Collaborator Author

PR 1 follow-up: Fluent override modules (old → new map)

Rules moved verbatim (same selectors and declarations), re-wrapped in their original scope
(:global(.webchat-fluent) .part-grouping-decorator {…}, :global(.webchat-fluent).theme, :global(.webchat-fluent) .sendbox__attachment-bar…).
One override module per Web Chat CSS module, under packages/fluent-theme/src/overrides/, each passed whole to <CSSModuleThemeComposer>.

Old (packages/fluent-theme/src/components/…) New (packages/fluent-theme/src/overrides/…)
activity/PartGroupingDecorator.module.css ActivityButton, ActivityStatus, AdaptiveCardRenderer, BorderFlair, Bubble, CarouselFilmStrip, CodeBlockContent, CodeBlockCopyButton, CollapsibleContent, CollapsibleGrouping, ComponentIcon, LinkDefinitions, MessageStatusLoader, PartGroupingActivity, RenderMarkdown, StackedLayout, TextContent, TranscriptFocus
sendBox/SendBox.module.css AttachmentBar, AttachmentBarItem, FilePreview
theme/Theme.module.css ActivityButton, AnimatedImageContent, Bubble, CodeBlock, CodeBlockContent, CodeBlockCopyButton, CollapsibleGrouping, FeedbackForm, ThumbButton, TranscriptFocus, ViewCodeDialog

Rule file: the module of the Web Chat local name closest to the rule's subject.
Fluent's own rules stay in Fluent's modules. SendBox.module.css keeps .sendbox__attachment-bar through
--webchat-preserve-reason: target-class, because its only rule moved and the override rule scopes by it.
Removed: fluentCSSModuleOverrides.ts (lists), pickCSSModule.ts. Spec renamed to fluentCSSModuleTheme.spec.ts.

Commits (on top of baed602):

  • 6aeacbf preserve marker + second Lightning CSS pass
  • c310cf0 feedback form, thumb buttons
  • fb0812f code blocks
  • 66028d0 send box
  • 4c7366a transcript focus
  • 4ddeb96 activity layout
  • 7736c88 part grouping
  • 78b3ada text and Markdown
  • 4a79ead Adaptive Cards, animated images
  • 518cd05 drop lists and pickCSSModule
  • ba8d210 buttonText → ButtonText in overrides/ActivityButton.module.css (maintainer-lint; CSS system colors are case-insensitive)

Tests (stack 3, --maxWorkers 1), all after the last CSS change except where noted:

  • 241 Fluent html2 tests at 518cd05: S/pr1split-html2.log (14 failed: timeouts, images not loaded, 2 flaky snaps) → re-run alone S/pr1split-rerun.log (12/14) → S/pr1split-rerun2.log (2/2). 0 snapshot diffs remain.
  • At ba8d210: collapsible, part-grouping, forcedColors: S/pr1split-final.log (37/38, 215 snaps) + S/pr1split-final-rerun.log (navigation.html 1/1).
  • 8a8dd78 keeps the scrollbar block whole in overrides/CodeBlockContent.module.css (it had been split, with the thumb rule for :hover and a focused text area input in a TextArea override that loaded later, so a 3 px border beat the 1 px thumb-hover border at equal specificity 0,6,1). overrides/TextArea.module.css is gone.
  • At 8a8dd78: 66 Fluent send box, text area, code block, attachment and scroll tests: 65 passed, 1 skipped, 121 snaps (S/pr1fix-html2.log).

Cascade check (S/cascade-check.mjs <worktree> baed602d7): for every pair of Fluent rules that may style the same element (subject compound shares a non-scope class, same pseudo-element and at-rule), declare the same property, and have equal specificity, it reports pairs whose order changed. Before the fix: the scrollbar pairs; after: 0 (S/cascade-check2.log). Split blocks (expected, by design): S/split-report.log.

Tools: ~/webchat-tools/scratch/css-fluent-context/split-overrides.mjs, regen-theme.mjs.
Rule-set equivalence (Lightning CSS, nesting lowered, [local] names) at 8a8dd78: 329 rules before, 330 after; the only differences are buttonText → ButtonText and the new .sendbox__attachment-bar { --webchat-preserve-reason } rule, which the build strips.

Comment thread esbuildPlugins.mjs Resolved
Eugene Olonov added 2 commits October 30, 2026 21:17
…s the output CSS

A second rule in the lightning-css vibe-grep preset, which component, bundle and fluent-theme already run on their
output CSS after the build.
… on their class maps

The class map of a CSS module also goes through this onLoad. It has no --webchat-preserve-reason today, but would with
dashed ident renaming, and Lightning CSS cannot parse JavaScript.
Comment thread esbuildPlugins.mjs Resolved
Replaces the 26 override modules, one per Web Chat CSS module, with fluentCSSModuleTheme.module.css, which keeps the
rules in their original order and regions, scoped by :global(.webchat-fluent) as before. Fluent's elements now get one
class for each scope name (e.g. ".theme") from it, instead of one per override module.
@OEvgeny
Eugene (OEvgeny) marked this pull request as ready for review October 31, 2026 17:41
Copilot AI balanced review requested due to automatic review settings October 31, 2026 17:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@OEvgeny
Eugene (OEvgeny) merged commit 8241b2b into main Oct 31, 2026
45 checks passed
@OEvgeny
Eugene (OEvgeny) deleted the OEvgeny/fix-css-fluent-context branch October 31, 2026 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog No .changelog/ fragment needed (development environment or test-only change)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants