Skip to content

feat(theme): put Element Call on the OrisoScheme contract, show the scheme in Storybook - #902

Merged
shanzaeimran merged 8 commits into
pre-devfrom
feat/element-call-oriso-theme
Aug 4, 2026
Merged

feat(theme): put Element Call on the OrisoScheme contract, show the scheme in Storybook#902
shanzaeimran merged 8 commits into
pre-devfrom
feat/element-call-oriso-theme

Conversation

@Storypapst

@Storypapst Storypapst commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Refs #896
Closes #897
Closes #898
Closes #899
Closes #900

Pairs with OpenResilienceInitiative/ORISO-ElementCall#40, which consumes the stylesheet this generates.

Why

Element Call was the last surface not on the ORISO colour contract, and there was no place anywhere to see the platform's colours. The Figma export was meant to be that place and had drifted eleven roles out of date — because it was a copy, and copies drift. Everything here is built so the same thing cannot happen again.

The reconciliation behind that number is in #896. Short version: 30 of 41 roles still matched; the differences were systematic (Figma reused light containers in dark, and used a warm seed-tinted background that the engine's own test suite would reject). Decision: the engine wins, in full.

What changed

Generator (#897)src/utils/theme/callTheme.ts maps Compound's semantic --cpd-* roles onto engine --m3-* roles; scripts/generate-call-theme.ts emits the stylesheet the fork ships. A golden test compares the checked-in artefact against fresh engine output, so a re-tune that forgets to regenerate fails CI rather than shipping a stale call.

Roles left unmapped on purpose, and why, are recorded in the module: the six-hue decorative set tells participant avatars apart and has no ORISO counterpart — collapsing it onto one brand colour would make every participant look identical.

Storybook (#898, #899) — a colour-role page (light/dark/inverted, hex, contrast, AA failures flagged in red), the call mapping page, a live embed of the deployed call with a switchable theme, and a toolbar scheme switch that repaints any story.

Every swatch calls computeOrisoPalette at render time. There is no hex list to maintain, so the page cannot be wrong.

Theme pin (#900) — the widget URL now carries theme=dark explicitly. The call was dark only because Element Call's own fallback picks dark when nothing is asked for; that made our appearance an accident of an upstream default. No visual change — this pins what already happens.

Two findings worth your attention

  1. The contrast assertions caught a real defect before it shipped. --cpd-color-border-focused initially mapped onto --m3-primary-outline, a role the engine tunes to stay dark on light brand surfaces. On the dark canvas that renders the focus ring at 1.5:1 — a keyboard focus indicator nobody can see. Eyeballing a screenshot would have passed it.

  2. I had guessed two token names. --cpd-color-text-primary-alpha and --cpd-color-text-secondary-alpha follow Compound's naming pattern but do not exist. Checked against the real token list and removed. --cpd-color-text-placeholder is also not a Compound role, but the fork reads it in input/Input.module.css, so it stays — with that reason recorded next to it.

Scope held deliberately

ACTIVE_SCHEMES.dark is still false. The app ships light; only the embedded call is dark. Components will look wrong when you switch Storybook to dark — that is the inventory this makes visible, not a regression it introduces. Fixing them is a future app-dark decision.

Follow-ups noted on #896: runtime tenant-token injection into the call (so a tenant's brand colour reaches the call UI instead of the default-tenant snapshot), extracting the engine as a shared package instead of generate-and-copy, and Playwright screenshot tests inside the fork.

Incidental

lint-staged reformatted the generated artefact on commit and the drift test failed — correctly. Generated files are now in .prettierignore; the generator owns their bytes. vite-node also needed the noExternal treatment the vitest config already documents for @material/material-color-utilities.

Verification

  • 36 unit tests on the mapping; 178 green across the theme and call suites.
  • The drift guard was proven, not assumed: changing a tone in orisoTuning.ts makes it fail; restoring it makes it pass.
  • Storybook built and driven for real — all three schemes verified to apply distinct engine values to the canvas (light #fcf9f9 / dark #131314 / inverted #303031).

Reviewer test plan

  • Open Storybook → "Design System / Colour tokens" lists every role with readable swatches in all three schemes
  • Compare a few light values against the running app → identical
  • Switch the toolbar scheme to dark on any component story → it repaints without a reload
  • Open the call mapping page → each Compound role's ORISO counterpart looks intentional, not coincidental
  • Open the live embed story and flip the theme control → the embedded call changes scheme
  • Start a group call in the app → it opens dark, and the iframe URL contains theme=dark
  • Confirm the rest of the app is still light on desktop and on a 320px phone
  • Change a tone in orisoTuning.ts and run npm run test:unit → the drift test fails and names the generator

Summary by CodeRabbit

  • New Features

    • Element Call now opens with the dark theme applied consistently.
    • Added design-system previews for light, dark, and inverted colour roles, including contrast information.
    • Added a live Element Call preview with configurable theme, room, and fullscreen viewing.
  • Bug Fixes

    • Improved Element Call colour mapping and contrast across text, icons, borders, overlays, and surfaces.
  • Documentation

    • Added visual documentation showing how Element Call colours map to the ORISO design system.

Storypapst and others added 5 commits August 3, 2026 02:02
The call UI runs on Compound's --cpd-* roles while ORISO speaks --m3-*.
Nothing bridged the two, so the fork carried hand-written colours that
could not follow a re-tune of the engine — the same drift that left the
Figma export 11 roles out of date.

Map the Compound semantic roles onto engine roles and emit the
stylesheet the fork ships. Compound declares `@layer cpd-semantic,
custom, cpd-base` for exactly this, so the override needs no !important
and no selector games.

The contrast assertions caught a real defect while writing them:
--cpd-color-border-focused mapped onto --m3-primary-outline, a role
tuned to stay dark on light brand surfaces, rendered the focus ring at
1.5:1 on the dark canvas. --m3-primary flips with the scheme and clears
3:1 in both.

vite-node needs the same `noExternal` treatment the vitest config
already documents for @material/material-color-utilities.

Refs #897

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lint-staged reformatted the artefact on commit, so the checked-in file
no longer matched the generator and the drift test failed — correctly.
A generated file should not be subject to formatting churn: the
generator owns its bytes, and the test compares them exactly.

Refs #897

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two of the mapped roles (--cpd-color-text-primary-alpha and
--cpd-color-text-secondary-alpha) do not exist in Compound — I had
guessed them from the naming pattern. Checked against the real token
list, they are gone. --cpd-color-text-placeholder is not a Compound
role either, but the fork reads it in input/Input.module.css, so it
stays with that reason recorded.

Also assert the defect that caused the epic: Compound derives
--cpd-color-icon-primary-alpha from --cpd-color-alpha-gray-1400, which
is near-white in the dark theme. The fork had flattened it to opaque
black for video tiles, taking every icon on that role with it. The new
pairs fail if those two concerns are ever conflated again.

Refs #897

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The widget URL carried no theme parameter, so the call was dark only
because Element Call's own fallback picks dark when nothing is asked
for. That made our appearance an accident of an upstream default, with
nothing in our code to point at if it ever changed.

The call is deliberately dark while the app around it stays light;
app-wide dark remains a separate decision (ACTIVE_SCHEMES). No visual
change — this pins what already happens.

Refs #900

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was no place to see the platform's colours. The Figma file was
meant to be that place and drifted eleven roles out of date, because it
was a copy. These pages compute every swatch from the engine as they
render, so they cannot go stale the same way.

- Colour roles: light, dark and inverted side by side, each role with
  its hex and — where a role has a defined partner — its contrast
  ratio, flagged in red when it misses AA.
- Element Call mapping: the --cpd-* ← --m3-* bridge, read from the same
  table the generator writes, so this page cannot disagree with the
  stylesheet the fork ships.
- Live embed: the deployed call in an iframe with a switchable theme.
  Deliberately not imported components — the call's screens pull
  matrix-js-sdk, livekit-client and widget state, and the fork runs
  React 19; mocking that in would show mocks, not the design, and would
  break on every upstream merge.
- Toolbar scheme switch that repaints any story in any scheme.

The switch is a Storybook capability only: ACTIVE_SCHEMES is untouched
and the app still ships light. Components will look wrong in dark —
that is the inventory this makes visible, and fixing them belongs to a
future app-dark decision, not here.

Refs #898
Refs #899

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Storypapst Storypapst self-assigned this Aug 3, 2026
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds an ORISO-to-Element Call theme bridge, deterministic CSS generation with drift tests, Storybook token and live-embed views, and explicit dark-mode widget configuration.

Changes

Element Call theming

Layer / File(s) Summary
Compound-to-ORISO theme bridge
src/utils/theme/callTheme.ts
Maps Compound semantic tokens to ORISO roles and generates light and dark CSS blocks with overlay support and provenance metadata.
Explicit dark widget theme
src/components/call/widget/useElementCallWidget.ts, src/components/call/widget/useElementCallWidget.test.tsx
Adds and tests the theme=dark URL parameter and ready-time theme update.
Generated CSS and drift validation
scripts/generate-call-theme.ts, src/utils/theme/callTheme.test.ts
Adds configurable CSS generation, --check drift detection, deterministic output tests, provenance checks, and WCAG contrast assertions.
Storybook token reference and embed
src/components/designTokens/DesignTokens.stories.tsx, src/components/designTokens/ElementCallEmbed.stories.tsx
Adds live ORISO role and contrast views, Compound mapping displays, and a configurable Element Call iframe story.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

  • OpenResilienceInitiative/ORISO-Frontend issue 896 — The PR implements the requested theme generation, Storybook token views, live embed, and explicit dark-mode behavior.

Possibly related PRs

Suggested labels: coderabbit

Suggested reviewers: shirloin, shazia-k, shanzaeimran

Sequence Diagram(s)

sequenceDiagram
  participant Storybook
  participant DesignTokens
  participant callTheme
  participant ElementCall
  Storybook->>DesignTokens: render ORISO roles and Compound mappings
  DesignTokens->>callTheme: read palette roles and CALL_TOKEN_MAP
  callTheme-->>DesignTokens: return mapped values and swatches
  Storybook->>ElementCall: load configured iframe URL
  ElementCall-->>Storybook: render the selected theme
Loading
🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The generator, tests, token reference, live embed, and dark Element Call changes match #897#900, but the toolbar switcher is excluded from review. Inspect .storybook/preview.tsx, excluded by path filters, to verify the light/dark/inverted toolbar switcher and confirm ACTIVE_SCHEMES remains unchanged.
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The conventional title names the theme area, Element Call, OrisoScheme, and Storybook, and it matches the PR's main changes.
Out of Scope Changes check ✅ Passed All reviewed changes support the linked objectives: theme generation, validation, Storybook references and embed, and explicit dark Element Call rendering.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Validation Evidence ✅ Passed Relevant tests are included: callTheme.test.ts covers mapping, determinism, contrast, and artefact drift; the widget test asserts theme=dark in the URL and updateTheme({ name: 'dark' }).
Shared Ui Architecture ✅ Passed UI changes reuse computeOrisoPalette, CALL_TOKEN_MAP, getElementCallBaseUrl, the shared Storybook scheme decorator, and the existing call widget hook; no duplicate search, active-state, or fo...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/element-call-oriso-theme

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/generate-call-theme.ts`:
- Around line 26-34: Centralize the generated theme artefact path in a shared
named constant, exporting it from the existing theme module or a small shared
configuration module. In scripts/generate-call-theme.ts at lines 26-34, replace
the local path.join construction with that constant; in
src/utils/theme/callTheme.test.ts at lines 83-87, import and reuse the same
constant instead of rebuilding the path.

In `@src/components/call/widget/useElementCallWidget.ts`:
- Around line 164-169: Align the ready-time theme update in the api.on('ready',
...) handler with the embedded call URL by changing the
ClientWidgetApi.updateTheme payload from light to dark, or remove the update so
theme=dark remains the single source of truth.

In `@src/components/designTokens/DesignTokens.stories.tsx`:
- Around line 102-163: Update RoleTable to compute each scheme’s palette once
and pass its tokens to SchemeColumn instead of having SchemeColumn call
computeOrisoPalette for every cell. Change SchemeColumn’s props and value lookup
to use the supplied tokens, while preserving the existing role iteration, scheme
columns, and contrast behavior.

In `@src/components/designTokens/ElementCallEmbed.stories.tsx`:
- Around line 1-16: Update the introductory rationale in the story comment above
ElementCallEmbed so it no longer claims the fork uses React 19 while the app
does not. Preserve the explanation that the iframe avoids bundling the call’s
dependencies and conflicting design systems, rewriting the React-version wording
only if needed to accurately support the deliberate iframe choice.

In `@src/utils/theme/callTheme.test.ts`:
- Around line 38-51: Add an exhaustive exact-value test in callTheme.test.ts
that iterates over every entry in CALL_TOKEN_MAP, reads the generated token for
both dark and light themes, and asserts each value equals the corresponding
mapped --m3-* role in the matching engine palette. Keep the existing focused
tests and contrast/foreign-colour checks unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 345faf89-49b0-4cb3-95d0-ee90aebb2563

📥 Commits

Reviewing files that changed from the base of the PR and between 57e4bb0 and 73bb07f.

⛔ Files ignored due to path filters (6)
  • .prettierignore is excluded by none and included by none
  • .storybook/preview.tsx is excluded by none and included by none
  • .storybook/withOrisoScheme.tsx is excluded by none and included by none
  • package.json is excluded by none and included by none
  • src/utils/theme/generated/element-call-theme.css is excluded by !**/generated/** and included by src/**
  • vitest.config.mts is excluded by none and included by none
📒 Files selected for processing (7)
  • scripts/generate-call-theme.ts
  • src/components/call/widget/useElementCallWidget.test.tsx
  • src/components/call/widget/useElementCallWidget.ts
  • src/components/designTokens/DesignTokens.stories.tsx
  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/utils/theme/callTheme.test.ts
  • src/utils/theme/callTheme.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: lint, type-check, build and Storybook Docker validation
  • GitHub Check: lint, type-check, test, build and Docker validation
  • GitHub Check: lint, type-check, build and test frontend
  • GitHub Check: lint, type-check and build frontend
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Keep shared behavior in hooks/utilities when multiple screens need the same state, route, selection, or formatting logic.
UI changes must have accessible focus/keyboard behavior and must not rely on color alone.

Files:

  • scripts/generate-call-theme.ts
  • src/components/designTokens/DesignTokens.stories.tsx
  • src/components/call/widget/useElementCallWidget.ts
  • src/utils/theme/callTheme.test.ts
  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/components/call/widget/useElementCallWidget.test.tsx
  • src/utils/theme/callTheme.ts
**/*.{scss,sass,css,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Reuse existing design tokens, Sass mixins, and component patterns; avoid one-off hardcoded CSS for controls, active list states, focus rings, and responsive layout.

Files:

  • scripts/generate-call-theme.ts
  • src/components/designTokens/DesignTokens.stories.tsx
  • src/components/call/widget/useElementCallWidget.ts
  • src/utils/theme/callTheme.test.ts
  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/components/call/widget/useElementCallWidget.test.tsx
  • src/utils/theme/callTheme.ts
src/**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

src/**/*.{ts,tsx,js,jsx}: Check behavior against the surrounding ORISO flow, not only types.
Flag duplicated route/state logic, missing error/loading handling,
privacy regressions around Matrix/chat/drafts, and changes that should
be expressed through shared hooks or utilities instead of local flags.

Files:

  • src/components/designTokens/DesignTokens.stories.tsx
  • src/components/call/widget/useElementCallWidget.ts
  • src/utils/theme/callTheme.test.ts
  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/components/call/widget/useElementCallWidget.test.tsx
  • src/utils/theme/callTheme.ts
🧠 Learnings (1)
📚 Learning: 2026-07-18T03:56:46.133Z
Learnt from: kiodreambau
Repo: OpenResilienceInitiative/ORISO-Frontend PR: 516
File: src/components/groupChat/waitingClock/WaitingAreaCountdown.stories.tsx:0-0
Timestamp: 2026-07-18T03:56:46.133Z
Learning: When reviewing Storybook stories (e.g., `*.stories.tsx`), treat story-specific styling done via isolated inline decorators/fixtures as Storybook simulation, not as production design tokens or focus-style behavior. Do not flag the story’s styles as production focus/regression issues unless the change involves the actual production components (not the story wrapper/fixture).

Applied to files:

  • src/components/designTokens/DesignTokens.stories.tsx
  • src/components/designTokens/ElementCallEmbed.stories.tsx
🪛 ast-grep (0.45.0)
scripts/generate-call-theme.ts

[warning] 46-46: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(out, 'utf8')
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)


[warning] 59-59: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFileSync(out, css, 'utf8')
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

src/utils/theme/callTheme.test.ts

[warning] 18-21: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
\\.cpd-theme-${scheme}[^{]*\\{([\\s\\S]*?)\\n\\},
'm'
)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)


[warning] 23-23: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(${token}:\\s*([^;]+);)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)


[warning] 93-93: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(artefact, 'utf8')
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

🪛 OpenGrep (1.26.0)
src/utils/theme/callTheme.test.ts

[ERROR] 19-22: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 24-24: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🔇 Additional comments (8)
src/components/call/widget/useElementCallWidget.test.tsx (1)

163-167: LGTM!

src/utils/theme/callTheme.ts (1)

1-229: LGTM!

scripts/generate-call-theme.ts (1)

1-25: LGTM!

Also applies to: 36-62

src/utils/theme/callTheme.test.ts (1)

1-37: LGTM!

Also applies to: 53-82, 98-161

src/components/designTokens/DesignTokens.stories.tsx (1)

1-100: LGTM!

Also applies to: 165-201, 203-261, 263-285

src/components/designTokens/ElementCallEmbed.stories.tsx (3)

18-22: LGTM!


24-68: 🔒 Security & Privacy | ⚡ Quick win

Reuse the shared URL utility, confirm param parity, and add defense-in-depth origin validation.

Three points on this segment:

  1. Line 25 reimplements trailing-slash stripping locally. getElementCallBaseUrl already composes stripTrailingSlashes (context snippet 1). Export and reuse that helper here instead of a duplicate regex, so the two call sites stay in sync.
  2. The header comment claims this story shows the call "the way useElementCallWidget embeds it." The useElementCallWidget.ts file is not in this batch. Confirm that roomId, theme, confineToRoom, header, skipLobby, intent, and callIntent match the actual widget hook's parameter set, so this documentation page cannot silently drift from production behaviour the way the Figma export did for colour roles.
  3. baseUrl is a free-text control used directly as the iframe src origin, and the iframe grants camera, microphone, display-capture, and clipboard-write via allow. React 19 rejects javascript: URLs in src/href, and Storybook restricts URL-driven args values to alphanumeric characters, spaces, underscores, and dashes, which blocks a crafted external link from injecting a full attacker origin. Given both platform-level mitigations, add an explicit http/https protocol check before building url as defense-in-depth, in case a developer pastes an arbitrary origin directly into the control.
🔒 Proposed defense-in-depth check
 const Embed: React.FC<EmbedArgs> = ({ baseUrl, theme, room }) => {
 	const origin = (baseUrl || '').replace(/\/+$/, '');
-	if (!origin) {
+	const isHttpOrigin = /^https?:\/\//i.test(origin);
+	if (!origin || !isHttpOrigin) {
 		return (
 			<p style={{ padding: 24, maxWidth: '65ch' }}>
-				No Element Call origin configured. Set{' '}
+				No valid Element Call origin configured. Set{' '}
 				<code>REACT_APP_ELEMENT_CALL_BASE_URL</code>, or type an origin
 				into the <code>baseUrl</code> control.
 			</p>
 		);
 	}
As per path instructions, "changes that should be expressed through shared hooks or utilities instead of local flags" — reusing `stripTrailingSlashes` instead of a duplicate regex satisfies this.

Source: Path instructions


70-88: LGTM!

Comment thread scripts/generate-call-theme.ts Outdated
Comment thread src/components/call/widget/useElementCallWidget.ts
Comment thread src/components/designTokens/DesignTokens.stories.tsx Outdated
Comment thread src/components/designTokens/ElementCallEmbed.stories.tsx
Comment thread src/utils/theme/callTheme.test.ts
kiodreambau and others added 2 commits August 4, 2026 01:31
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Align the ready-time updateTheme with the theme=dark URL pin (the
  light payload flipped the embedded call to light after launch)
- Share the generated-artefact location: export CALL_THEME_ARTEFACT_PATH
  from callTheme.ts and use it in both the generator CLI and the drift
  test instead of two independent path.join constructions
- Assert every CALL_TOKEN_MAP entry against the engine palette in both
  schemes, so a mapping pointed at the wrong --m3-* role fails a test
- Compute each scheme's palette once in RoleTable instead of once per
  table cell
- Drop the stale React-19 rationale from the ElementCallEmbed story
  comment (this app is on React 19 too now)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kiodreambau

Copy link
Copy Markdown
Contributor

Pushed cfa3080 addressing all five review findings (all verified as valid against the code), plus a clean merge of origin/pre-dev (was 77 commits behind, no conflicts).

  • Theme mismatch (major): the ready-time updateTheme({ name: 'light' }) contradicted the theme=dark URL pin and flipped the embedded call to light after launch. It now sends dark (kept as a guard re-asserting the pin); the unit test asserts the payload.
  • Shared artefact path: CALL_THEME_ARTEFACT_PATH is exported from callTheme.ts and used by both the generator CLI and the drift test.
  • Exhaustive mapping test: it.each over the full CALL_TOKEN_MAP asserts every Compound token (incl. alpha color-mix values) traces to its --m3-* role in both schemes — ~150 new assertions.
  • Palette per cell: RoleTable now computes one palette per scheme and passes tokens down.
  • Stale React-19 comment: removed (app is on react ^19.2.3).

Local verification: eslint --max-warnings=0 + tsc + stylelint + typecheck:storybook clean; vitest for src/utils/theme + src/components/call: 8 files, 254 tests passed (incl. the artefact drift guard).

@kiodreambau

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/utils/theme/callTheme.ts (2)

215-237: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make custom-seed provenance reproducible.

buildCallThemeCss(seed) accepts custom seeds, but the generated header always says (ORISO default tenant) and always gives the default regeneration command. A stylesheet generated with --seed can therefore contain false provenance and cannot be reproduced from its own metadata. Make the tenant label and regeneration instruction conditional on the supplied seed. (raw.githubusercontent.com)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/theme/callTheme.ts` around lines 215 - 237, Update
buildCallThemeCss to derive the header’s tenant label and regeneration command
from the supplied seed: retain the ORISO default tenant metadata and default
command for the default seed, but emit custom-seed provenance and a command that
includes --seed with the actual seed for other values. Ensure the generated
metadata can reproduce the stylesheet.

68-75: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix low-contrast status mappings and test the rendered pairs.

The generated light theme renders --cpd-color-text-info-primary as #4d5660 on --cpd-color-bg-info-subtle #656e79, about 1.44:1. The critical subtle background maps to #de0077 instead of an error-container-like surface, so --cpd-color-text-critical-primary also renders at 1.44:1 in light mode. Update src/utils/theme/callTheme.ts to map these status roles to engine roles that have guaranteed contrast, and add the real status pair assertions to TEXT_PAIRS.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/theme/callTheme.ts` around lines 68 - 75, Update the status role
mappings in src/utils/theme/callTheme.ts at lines 68-75 and 120-132 so the info
and critical text/background pairs use engine roles with guaranteed contrast,
including an error-container-like surface for critical backgrounds. In
src/utils/theme/callTheme.test.ts lines 143-182, add assertions for the rendered
info and critical status pairs to TEXT_PAIRS and verify their light-theme
contrast.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/generate-call-theme.ts`:
- Around line 29-31: Update the argument-value handling in argValue so it
rejects missing next arguments and any value beginning with "--", including
cases such as --out --check and --seed --check, instead of treating the flag as
a path or seed. Preserve normal value parsing for valid non-flag arguments and
ensure invalid usage reports the existing argument error.

In `@src/components/call/widget/useElementCallWidget.test.tsx`:
- Around line 316-318: Strengthen the ready-handler assertion around
api.updateTheme by verifying it is called exactly once, in addition to checking
the dark theme payload. Keep the existing dark update expectation and prevent
any light or contradictory update from passing the test.

---

Outside diff comments:
In `@src/utils/theme/callTheme.ts`:
- Around line 215-237: Update buildCallThemeCss to derive the header’s tenant
label and regeneration command from the supplied seed: retain the ORISO default
tenant metadata and default command for the default seed, but emit custom-seed
provenance and a command that includes --seed with the actual seed for other
values. Ensure the generated metadata can reproduce the stylesheet.
- Around line 68-75: Update the status role mappings in
src/utils/theme/callTheme.ts at lines 68-75 and 120-132 so the info and critical
text/background pairs use engine roles with guaranteed contrast, including an
error-container-like surface for critical backgrounds. In
src/utils/theme/callTheme.test.ts lines 143-182, add assertions for the rendered
info and critical status pairs to TEXT_PAIRS and verify their light-theme
contrast.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 526b4ac8-7c54-411a-a8c7-7416fe3a8a13

📥 Commits

Reviewing files that changed from the base of the PR and between 73bb07f and 9d2cccf.

⛔ Files ignored due to path filters (1)
  • .storybook/preview.tsx is excluded by none and included by none
📒 Files selected for processing (7)
  • scripts/generate-call-theme.ts
  • src/components/call/widget/useElementCallWidget.test.tsx
  • src/components/call/widget/useElementCallWidget.ts
  • src/components/designTokens/DesignTokens.stories.tsx
  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/utils/theme/callTheme.test.ts
  • src/utils/theme/callTheme.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: lint, type-check, build and Storybook Docker validation
  • GitHub Check: lint, type-check, test, build and Docker validation
  • GitHub Check: lint, type-check, build and test frontend
  • GitHub Check: lint, type-check and build frontend
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Keep shared behavior in hooks/utilities when multiple screens need the same state, route, selection, or formatting logic.
UI changes must have accessible focus/keyboard behavior and must not rely on color alone.

Files:

  • src/components/call/widget/useElementCallWidget.test.tsx
  • src/components/call/widget/useElementCallWidget.ts
  • scripts/generate-call-theme.ts
  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/components/designTokens/DesignTokens.stories.tsx
  • src/utils/theme/callTheme.test.ts
  • src/utils/theme/callTheme.ts
**/*.{scss,sass,css,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Reuse existing design tokens, Sass mixins, and component patterns; avoid one-off hardcoded CSS for controls, active list states, focus rings, and responsive layout.

Files:

  • src/components/call/widget/useElementCallWidget.test.tsx
  • src/components/call/widget/useElementCallWidget.ts
  • scripts/generate-call-theme.ts
  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/components/designTokens/DesignTokens.stories.tsx
  • src/utils/theme/callTheme.test.ts
  • src/utils/theme/callTheme.ts
src/**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

src/**/*.{ts,tsx,js,jsx}: Check behavior against the surrounding ORISO flow, not only types.
Flag duplicated route/state logic, missing error/loading handling,
privacy regressions around Matrix/chat/drafts, and changes that should
be expressed through shared hooks or utilities instead of local flags.

Files:

  • src/components/call/widget/useElementCallWidget.test.tsx
  • src/components/call/widget/useElementCallWidget.ts
  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/components/designTokens/DesignTokens.stories.tsx
  • src/utils/theme/callTheme.test.ts
  • src/utils/theme/callTheme.ts
🧠 Learnings (2)
📚 Learning: 2026-07-18T03:56:46.133Z
Learnt from: kiodreambau
Repo: OpenResilienceInitiative/ORISO-Frontend PR: 516
File: src/components/groupChat/waitingClock/WaitingAreaCountdown.stories.tsx:0-0
Timestamp: 2026-07-18T03:56:46.133Z
Learning: When reviewing Storybook stories (e.g., `*.stories.tsx`), treat story-specific styling done via isolated inline decorators/fixtures as Storybook simulation, not as production design tokens or focus-style behavior. Do not flag the story’s styles as production focus/regression issues unless the change involves the actual production components (not the story wrapper/fixture).

Applied to files:

  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/components/designTokens/DesignTokens.stories.tsx
📚 Learning: 2026-08-03T08:28:22.927Z
Learnt from: Storypapst
Repo: OpenResilienceInitiative/ORISO-Frontend PR: 849
File: src/components/pseudonym/BotMessageAnimation.stories.tsx:0-0
Timestamp: 2026-08-03T08:28:22.927Z
Learning: In the ORISO frontend Storybook 10 setup, select viewports through Storybook `globals`, not `parameters.viewport.defaultViewport`. The built-in `mobile1` viewport is not registered. For stories claiming 375px or 390px widths, use the custom `phone375` (375×812) or `phone390` (390×844) viewports registered in `.storybook/preview.tsx`, via the exported `phone375Globals` and `phone390Globals` helpers.

Applied to files:

  • src/components/designTokens/ElementCallEmbed.stories.tsx
  • src/components/designTokens/DesignTokens.stories.tsx
🔇 Additional comments (9)
src/components/call/widget/useElementCallWidget.ts (2)

164-169: LGTM!

Also applies to: 284-287


171-177: 🎯 Functional Correctness

Verify the appConfig lifecycle for the E2EE kill-switch.

prepare reads module-level appConfig when it builds the URL, but this effect does not rerun when setAppConfig mutates that value. If configuration loads or changes after URL construction, the call can keep the wrong perParticipantE2EE setting. Confirm that configuration is initialized before this hook and remains immutable, or expose the effective toggle through reactive state.

Source: Path instructions

src/components/call/widget/useElementCallWidget.test.tsx (2)

163-167: LGTM!


172-195: 🎯 Functional Correctness

Isolate the module-level appConfig state in these tests.

The positive assertion does not set an explicit enabled configuration, while the following test sets enableCallMediaE2EE to false. Confirm that beforeEach or afterEach restores the configuration. Otherwise, test order can change the generated URL and mask the default behavior.

Source: Path instructions

src/utils/theme/callTheme.ts (1)

1-18: LGTM!

Also applies to: 19-26, 27-31, 33-57, 58-67, 76-119, 133-170, 172-178, 180-197, 199-213

scripts/generate-call-theme.ts (1)

1-28: LGTM!

Also applies to: 32-60

src/utils/theme/callTheme.test.ts (1)

1-56: LGTM!

Also applies to: 57-92, 94-117, 119-141

src/components/designTokens/DesignTokens.stories.tsx (1)

102-105: LGTM!

Also applies to: 127-131, 156-161

src/components/designTokens/ElementCallEmbed.stories.tsx (1)

8-12: LGTM!

Comment on lines +29 to +31
*/
const DEFAULT_OUT = path.join(__dirname, '..', CALL_THEME_ARTEFACT_PATH);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject flags with missing values.

argValue returns the next argument without checking it. With --out --check, the generator treats --check as the output path and checks the wrong file. With --seed --check, it passes a flag as a colour seed and reports a confusing palette error. Reject a missing value or a next argument beginning with --. (raw.githubusercontent.com)

Suggested validation
 const argValue = (flag: string): string | undefined => {
 	const i = process.argv.indexOf(flag);
-	return i === -1 ? undefined : process.argv[i + 1];
+	if (i === -1) return undefined;
+	const value = process.argv[i + 1];
+	if (!value || value.startsWith('--')) {
+		throw new Error(`Missing value for ${flag}`);
+	}
+	return value;
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/generate-call-theme.ts` around lines 29 - 31, Update the
argument-value handling in argValue so it rejects missing next arguments and any
value beginning with "--", including cases such as --out --check and --seed
--check, instead of treating the flag as a path or seed. Preserve normal value
parsing for valid non-flag arguments and ensure invalid usage reports the
existing argument error.

Comment on lines +316 to +318
// Must match the `theme=dark` pin in the widget URL — a ready-time
// update to any other scheme would flip the call after launch.
expect(api.updateTheme).toHaveBeenCalledWith({ name: 'dark' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert that the ready handler sends only the dark update.

toHaveBeenCalledWith passes if the mock receives both light and dark updates. Also assert toHaveBeenCalledTimes(1) or compare the complete call list to prevent a contradictory update from passing the test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/call/widget/useElementCallWidget.test.tsx` around lines 316 -
318, Strengthen the ready-handler assertion around api.updateTheme by verifying
it is called exactly once, in addition to checking the dark theme payload. Keep
the existing dark update expectation and prevent any light or contradictory
update from passing the test.

@shanzaeimran
shanzaeimran merged commit bb8c543 into pre-dev Aug 4, 2026
5 checks passed
@shanzaeimran
shanzaeimran deleted the feat/element-call-oriso-theme branch August 4, 2026 07:41
@shanzaeimran

Copy link
Copy Markdown
Collaborator

QA verification — Element Call ORISO theming (predev + Storybook)

Ran through the full reviewer test plan against predev.oriso.org and the Storybook Storybook-Frontend build.

✅ Item 1 — Storybook color tokens page
"Design System / Colour tokens → ORISO roles (--m3-*)" lists every role with readable swatches across light/dark/inverted, with contrast ratios shown for paired roles.

📎 image

✅ Item 2 — Light values match the running app
Compared --m3-primary: #a5000a from the token page against the live login button — DevTools computed style shows background: var(--m3-primary, #a5000a) resolving to the exact same hex.

📎 image

✅ Item 3 — Toolbar scheme switch repaints live
Switched the Storybook toolbar to dark on the Button/Primary story — repainted instantly to the dark-scheme colors (#ffb4aa button, dark red text), no reload, URL updated to globals=scheme:dark.

📎 image

✅ Item 4 — Call mapping page
"Element Call mapping (--cpd-* ← --m3-*)" table checked — every Compound → ORISO pairing reads as sensible and intentional (canvas→surface, text→on-surface, etc.), matches the source mapping exactly.

📎 image

✅ Item 5 — Live embed theme control
Flipped the theme control on the "Element Call (live)" story between light/dark — the embedded call changed scheme accordingly.

✅ Item 6 — Real group call opens dark, URL pinned
Started an actual group call in the app — rendered dark, no flash of light-then-dark on load. Confirmed the iframe src contains theme=dark.

📎 image

✅ Item 7 — Rest of the app stays light
Checked app chrome (sidebar, conversation list) at desktop width and 425px mobile — light and coherent at both, unaffected by the call being dark.

📎 *image
image
*

✅ Item 8 — Drift guard works
Edited a tone value in orisoTuning.ts locally, ran npm run test:unit — the drift test in callTheme.test.ts failed and correctly pointed at the generator. Reverted → passed clean again.


All 8 items from the reviewer test plan verified. Also resolves the question I raised in code review about the old updateTheme({name: 'light'}) call — item 6 shows no flash-of-light on load, so the prior behavior was effectively already a no-op; the new pinned theme=dark is a genuine, correct simplification, not a behavior change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants