Skip to content

feat(ui): improve color config#131

Open
renefloor wants to merge 8 commits into
mainfrom
feat/improve-color-config
Open

feat(ui): improve color config#131
renefloor wants to merge 8 commits into
mainfrom
feat/improve-color-config

Conversation

@renefloor

@renefloor renefloor commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Submit a pull request

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Currently the StreamTheme.light and StreamTheme.dark are quite useless as you can't even change your primary color. I've extended these factories to easily set your primary color and base your chrome color on that primary color. I've also updated the gallery so you can see which colors are automatically generated and which colors are specifically overriden in the gallery by the user.

Screenshots / Videos

design.system.colors.demo.mov

Summary by CodeRabbit

  • New Features
    • Added brand and chrome color customization for light and dark themes, plus per-slot “reset to default” actions across the theme studio (including avatar palette).
    • Added optional saturation control for generated theme shades and improved shade generation based on brightness.
  • Documentation
    • Updated the upcoming features notes for brand/chrome customization.
  • Tests
    • Added golden and unit tests to lock in deterministic brand/chrome shade scales.

@renefloor
renefloor requested a review from a team as a code owner July 17, 2026 08:44
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@renefloor, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 432f7869-04fd-47c3-bb63-4d8da7cbc05d

📥 Commits

Reviewing files that changed from the base of the PR and between c999895 and 9467acd.

📒 Files selected for processing (2)
  • apps/design_system_gallery/lib/widgets/theme_studio/theme_customization_panel.dart
  • packages/stream_core_flutter/lib/src/theme/primitives/stream_colors.dart
📝 Walkthrough

Walkthrough

Changes

Theme color generation now supports saturation-aware brand and chrome swatches, seed-based color schemes, and brightness inference. Theme Studio exposes customization state and per-field reset controls across color sections and the avatar palette.

Theme customization

Layer / File(s) Summary
Color swatch and theme factory inputs
packages/stream_core_flutter/lib/src/theme/..., packages/stream_core_flutter/CHANGELOG.md
Color swatches accept saturation overrides, color schemes generate brand and chrome values from seeds, and StreamTheme derives effective brightness from supplied inputs.
Color generation validation
packages/stream_core_flutter/test/theme/*
Light and dark generated color scales are validated with deterministic assertions and golden previews.
Configuration overrides and theme rebuilding
apps/design_system_gallery/lib/config/theme_configuration.dart
Customization getters and per-field reset methods are added; theme rebuilding passes overrides through the color scheme factories.
Color picker default and reset presentation
apps/design_system_gallery/lib/widgets/theme_studio/color_picker_tile.dart
Color picker tiles display default state and conditionally render reset actions.
Theme Studio reset wiring
apps/design_system_gallery/lib/widgets/theme_studio/theme_customization_panel.dart
All color sections and the avatar palette connect customization state and reset callbacks.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ThemeStudio
  participant ThemeConfiguration
  participant StreamTheme
  participant StreamColorScheme
  participant StreamColorSwatch
  ThemeStudio->>ThemeConfiguration: set or reset color override
  ThemeConfiguration->>StreamTheme: rebuild light or dark theme
  StreamTheme->>StreamColorScheme: pass brand, chrome, and override values
  StreamColorScheme->>StreamColorSwatch: generate color swatches
  StreamColorSwatch-->>StreamColorScheme: return shade maps
  StreamColorScheme-->>ThemeConfiguration: return rebuilt color scheme
  ThemeConfiguration-->>ThemeStudio: notify listeners and refresh controls
Loading

Suggested reviewers: xsahil03x

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the change, but it is too generic to clearly describe the main improvement. Make the title specific, e.g. mention primary/chrome color customization in StreamTheme and the gallery.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the feature, checklist items, and screenshots, and it mostly matches the required template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/improve-color-config

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.

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.81818% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.97%. Comparing base (59b3f4e) to head (4232a03).

Files with missing lines Patch % Lines
...tream_core_flutter/lib/src/theme/stream_theme.dart 77.77% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #131      +/-   ##
==========================================
+ Coverage   44.53%   44.97%   +0.43%     
==========================================
  Files         178      178              
  Lines        7243     7260      +17     
==========================================
+ Hits         3226     3265      +39     
+ Misses       4017     3995      -22     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@renefloor renefloor changed the title Feat(ui): improve color config feat(ui): improve color config Jul 17, 2026

@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: 3

🧹 Nitpick comments (1)
packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart (1)

223-229: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the explicit chromeColor path.

All chrome assertions exercise fallback generation from brandColor. Add distinct brand and chrome seeds for both factories so ignored overrides or incorrect precedence are caught.

Suggested coverage
+    test('prefers an explicit chrome seed', () {
+      const chromeSeed = Color(0xFF2196F3);
+      final chrome = StreamTheme.light(
+        brandColor: const Color(0xFFF44336),
+        chromeColor: chromeSeed,
+      ).colorScheme.chrome;
+
+      expect(chrome[500], chromeSeed);
+    });

Add the equivalent assertion to the dark-theme group.

Also applies to: 449-455

🤖 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
`@packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart`
around lines 223 - 229, Extend the theme color generation tests around the light
and dark factory groups to provide distinct brandColor and chromeColor seeds,
then assert that each factory preserves the explicit chromeColor at its expected
chrome shade while brandColor remains mapped to brand shade 500. Cover both
StreamTheme.light and StreamTheme.dark so ignored overrides or incorrect
precedence are detected.
🤖 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 `@apps/design_system_gallery/lib/config/theme_configuration.dart`:
- Around line 519-590: The scheme rebuild in ThemeConfiguration should use the
public color getters rather than private fields, while gating each effective
value with its corresponding *IsCustom flag so non-custom properties remain null
and SDK defaults are preserved. Update effectiveBrand, effectiveChrome, and all
arguments passed to buildScheme, including accent, text, background, and border
properties, to follow this getter-and-flag pattern.

In
`@apps/design_system_gallery/lib/widgets/theme_studio/theme_customization_panel.dart`:
- Around line 222-224: Update the customization section builders in the theme
customization panel to obtain ThemeConfiguration with context.watch rather than
context.read, including the builders covering the listed color controls,
custom-state flags, and reset/default presentation. Preserve the existing
callbacks and rendering logic while ensuring each section rebuilds directly when
ThemeConfiguration changes.

In `@packages/stream_core_flutter/lib/src/theme/primitives/stream_colors.dart`:
- Around line 272-280: Update StreamColorSwatch.fromColor so the generated shade
map is computed before constructing the swatch, then use shades[500] as the
primary Color value instead of color.toARGB32(). Preserve the existing
brightness and saturation arguments passed to
StreamColorSwatchHelper.generateShadeMap.

---

Nitpick comments:
In
`@packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart`:
- Around line 223-229: Extend the theme color generation tests around the light
and dark factory groups to provide distinct brandColor and chromeColor seeds,
then assert that each factory preserves the explicit chromeColor at its expected
chrome shade while brandColor remains mapped to brand shade 500. Cover both
StreamTheme.light and StreamTheme.dark so ignored overrides or incorrect
precedence are detected.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f8809e68-894b-47ba-8c7e-55306db08cb7

📥 Commits

Reviewing files that changed from the base of the PR and between 59b3f4e and 4232a03.

⛔ Files ignored due to path filters (1)
  • packages/stream_core_flutter/test/theme/goldens/ci/stream_theme_color_generation.png is excluded by !**/*.png
📒 Files selected for processing (9)
  • apps/design_system_gallery/lib/config/theme_configuration.dart
  • apps/design_system_gallery/lib/widgets/theme_studio/color_picker_tile.dart
  • apps/design_system_gallery/lib/widgets/theme_studio/theme_customization_panel.dart
  • packages/stream_core_flutter/CHANGELOG.md
  • packages/stream_core_flutter/lib/src/theme/primitives/internal/stream_color_swatch_helper.dart
  • packages/stream_core_flutter/lib/src/theme/primitives/stream_colors.dart
  • packages/stream_core_flutter/lib/src/theme/stream_theme.dart
  • packages/stream_core_flutter/test/theme/stream_theme_color_generation_golden_test.dart
  • packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart

Comment thread apps/design_system_gallery/lib/config/theme_configuration.dart
Comment thread packages/stream_core_flutter/lib/src/theme/primitives/stream_colors.dart Outdated
Comment thread packages/stream_core_flutter/lib/src/theme/stream_theme.dart Outdated

@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: 1

🤖 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
`@packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart`:
- Around line 210-211: Replace Swift-style shorthand with explicit Dart symbols:
in stream_color_scheme.dart at lines 210-211 and 431-432, use
StreamColorScheme.raw and Brightness.light/dark; in stream_theme.dart at lines
162-163, 214-215, and 266-272, use Brightness.light/dark and StreamTheme.raw;
and in stream_theme_color_generation_test.dart at lines 232-456, use
Brightness.dark for dark-generation cases.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c5c20fd-0a80-43f1-98bd-4401e3270652

📥 Commits

Reviewing files that changed from the base of the PR and between 4232a03 and c999895.

📒 Files selected for processing (4)
  • packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart
  • packages/stream_core_flutter/lib/src/theme/stream_theme.dart
  • packages/stream_core_flutter/test/theme/stream_theme_color_generation_golden_test.dart
  • packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stream_core_flutter/test/theme/stream_theme_color_generation_golden_test.dart

Comment on lines 210 to +211
return .raw(
brightness: .light,

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 | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat << 'EOF' > syntax_test.dart
enum Brightness { light, dark }
class Theme {
  final Brightness b;
  const Theme.raw({required this.b});
  factory Theme.light() => .raw(b: .light);
}
EOF
dart analyze syntax_test.dart || echo "Analyzer correctly rejected Swift-style syntax."

Repository: GetStream/stream-core-flutter

Length of output: 254


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files \
  packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart \
  packages/stream_core_flutter/lib/src/theme/stream_theme.dart \
  packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart

printf '\n== relevant excerpts ==\n'
for f in \
  packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart \
  packages/stream_core_flutter/lib/src/theme/stream_theme.dart \
  packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart
do
  echo "--- $f ---"
  nl -ba "$f" | sed -n '200,230p;420,440p;150,175p;208,222p;260,280p;232,456p'
done

printf '\n== search for implicit-member style syntax ==\n'
rg -n --no-heading '(^|[^[:alnum:]_])\.[A-Za-z_][A-Za-z0-9_]*' \
  packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart \
  packages/stream_core_flutter/lib/src/theme/stream_theme.dart \
  packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart

Repository: GetStream/stream-core-flutter

Length of output: 551


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in \
  packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart \
  packages/stream_core_flutter/lib/src/theme/stream_theme.dart \
  packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart
do
  echo "--- $f (selected lines) ---"
  sed -n '200,230p;420,440p;150,175p;208,222p;260,280p;232,456p' "$f" | cat -n
done

echo "--- search for shorthand member syntax in these files ---"
rg -n --no-heading '(^|[^[:alnum:]_])\.[A-Za-z_][A-Za-z0-9_]*' \
  packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart \
  packages/stream_core_flutter/lib/src/theme/stream_theme.dart \
  packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart

Repository: GetStream/stream-core-flutter

Length of output: 50387


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

files = [
    "packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart",
    "packages/stream_core_flutter/lib/src/theme/stream_theme.dart",
    "packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart",
]

for path in files:
    print(f"--- {path} ---")
    lines = Path(path).read_text().splitlines()
    for start, end in [(205, 218), (425, 438), (158, 167), (210, 218), (262, 274), (232, 456)]:
        if start <= len(lines):
            print(f"[{start}-{end}]")
            for i in range(start, min(end, len(lines)) + 1):
                print(f"{i:>4}: {lines[i-1]}")
    print()

print("--- shorthand token matches ---")
for path in files:
    text = Path(path).read_text()
    for token in [".raw(", ".light", ".dark"]:
        if token in text:
            print(f"{path}: contains {token}")
PY

Repository: GetStream/stream-core-flutter

Length of output: 40263


Replace the Swift-style shorthand with explicit Dart names.
Dart doesn’t accept .raw, .light, or .dark here.

  • packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart#L210-L211 and #L431-L432: use StreamColorScheme.raw(...) and Brightness.light/dark.
  • packages/stream_core_flutter/lib/src/theme/stream_theme.dart#L162-L163, #L214-L215, and #L266-L272: use Brightness.light/dark and StreamTheme.raw(...).
  • packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart#L232-L456: use Brightness.dark in the dark-generation cases.
📍 Affects 3 files
  • packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart#L210-L211 (this comment)
  • packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart#L431-L432
  • packages/stream_core_flutter/lib/src/theme/stream_theme.dart#L162-L163
  • packages/stream_core_flutter/lib/src/theme/stream_theme.dart#L214-L215
  • packages/stream_core_flutter/lib/src/theme/stream_theme.dart#L266-L272
  • packages/stream_core_flutter/test/theme/stream_theme_color_generation_test.dart#L232-L456
🤖 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
`@packages/stream_core_flutter/lib/src/theme/semantics/stream_color_scheme.dart`
around lines 210 - 211, Replace Swift-style shorthand with explicit Dart
symbols: in stream_color_scheme.dart at lines 210-211 and 431-432, use
StreamColorScheme.raw and Brightness.light/dark; in stream_theme.dart at lines
162-163, 214-215, and 266-272, use Brightness.light/dark and StreamTheme.raw;
and in stream_theme_color_generation_test.dart at lines 232-456, use
Brightness.dark for dark-generation cases.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We also need to regenerate this file for the new brightness field.

}

void resetToDefaults() {
_brandPrimaryColor = null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we should keep this right? Also reset the _chromePrimaryColor

Comment on lines 23 to +31
static Map<int, Color> generateShadeMap(
Color baseColor, {
Brightness brightness = Brightness.light,
double? saturation,
}) {
final hslBase = HSLColor.fromColor(baseColor);
var hslBase = HSLColor.fromColor(baseColor);
if (saturation != null) {
hslBase = hslBase.withSaturation(saturation);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we instead use HCT instead of HSL? I read a bit and Material and new Design systems now prefer to use HCT over the old HSL

brightness: brightness,
),
) ??
StreamColorSwatch.fromColor(brand, brightness: brightness, saturation: 0.1),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we should not provide any default here, as the default constructor already set the default chrome value if not available

}

const StreamColorScheme.raw({
this.brightness = Brightness.light,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lets keep it required here.

Suggested change
this.brightness = Brightness.light,
required this.brightness,

factory StreamTheme.dark() => StreamTheme(brightness: .dark);

const StreamTheme.raw({
required this.brightness,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would also remove the brightness as the field and convert it into a getter instead similar to Material.

/// The overall theme brightness.
  ///
  /// The default [TextStyle] color for the [textTheme] is black if the
  /// theme is constructed with [Brightness.light] and white if the
  /// theme is constructed with [Brightness.dark].
  Brightness get brightness => colorScheme.brightness;

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.

2 participants