Align StreamDesign.Colors with Figma design token system#6272
Conversation
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
SDK Size Comparison 📏
|
e7750e3 to
5efd281
Compare
WalkthroughComprehensive theme color token refactoring introducing a new scale-based architecture with Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment Tip CodeRabbit can use OpenGrep to find security vulnerabilities and bugs across 17+ programming languages.OpenGrep is compatible with Semgrep configurations. Add an |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
stream-chat-android-compose/api/stream-chat-android-compose.api (1)
6324-6361: Consider adding an old→new token mapping to the v7 migration notes.This public rename set is large enough (
avatarPresenceBorder,backgroundCoreElevation*,backgroundUtilitySelected,borderCoreOpacity*, plusbrand/chrome) that downstream theme overrides will be much easier to update with a short mapping table in the changelog or migration doc.Based on learnings, in PRs against major version branches (e.g., v7), maintainers prefer documenting migration paths covering function signature changes and renamed/removed composables.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/api/stream-chat-android-compose.api` around lines 6324 - 6361, Add an explicit old→new token mapping to the v7 migration notes covering the public renames shown (e.g., avatarPresenceBorder → getAvatarPresenceBorder-0d7_KjU, backgroundCoreElevation0..4 → getBackgroundCoreElevation0-0d7_KjU .. getBackgroundCoreElevation4-0d7_KjU, backgroundUtilitySelected → getBackgroundUtilitySelected-0d7_KjU, borderCoreOpacityStrong/Subtle → getBorderCoreOpacityStrong-0d7_KjU / getBorderCoreOpacitySubtle-0d7_KjU, brand → getBrand (StreamDesign$ColorScale), chrome → getChrome (StreamDesign$ChromeScale), plus chat-related tokens like getChatPollProgressFillOutgoing-0d7_KjU and reply indicator tokens) and list each old name alongside its new API symbol; update the v7 changelog/migration doc with a concise table or bullet list mapping every renamed token so downstream theme overrides can be updated easily.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.kt (1)
17-18: Narrow the suppression scope.Moving
MagicNumberandLongMethodto file scope now hides future findings anywhere in this public API file. Please keep the suppressions on the specific factory declarations that need them and add a short rationale there.As per coding guidelines,
**/*.kt: "Use@OptInannotations explicitly; avoid suppressions unless documented".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.kt` around lines 17 - 18, Remove the file-level `@file`:Suppress("MagicNumber", "LongMethod") in StreamDesign.kt and instead apply `@Suppress`("MagicNumber") and/or `@Suppress`("LongMethod") directly on the specific factory declarations that actually trigger these warnings (the theme/factory functions in this file). Add a short rationale comment above each suppression (e.g., "// Suppress MagicNumber: design tokens/constants expressed as raw numbers for theme factory" or "// Suppress LongMethod: factory builds many palette entries; splitting reduces clarity") so the scope is narrow and documented.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.kt`:
- Around line 682-694: ColorScale.from currently always builds stops with s50 as
lightest and s900 as darkest, which breaks dark-mode ordering used by
defaultDark(); update the API so generated brand scales match the intended theme
orientation—either add an explicit parameter (e.g., ColorScale.from(brandColor,
isDark: Boolean)) or provide a dedicated factory (e.g.,
ColorScale.fromDark/ColorScale.fromLight) and use it in places that expect
reversed stops (refer to ColorScale.from, ColorScale.defaultDark,
ColorScale.defaultLight and tokens like chatBgOutgoing/chatTextOutgoing) so that
when dark branding is required the s50..s900 ordering is reversed to match
defaultDark()’s orientation.
- Around line 78-80: The KDoc for backgroundCoreOnAccent claims it "must remain
white across themes" but defaultDark() maps backgroundCoreOnAccent to chrome.s0
while ChromeScale.defaultDark() defines s0 as black; update either the docs or
the mapping so they match: either change the KDoc to reflect that
backgroundCoreOnAccent follows the chrome.s0 (non-white) behavior, or change
defaultDark() to map backgroundCoreOnAccent to a white token (e.g., a
chrome.s100/white-equivalent) and ensure ChromeScale.defaultDark() defines the
matching s* value as white; inspect and update the implementations of
backgroundCoreOnAccent, defaultDark(), and ChromeScale.defaultDark() so the
token semantics and docs are consistent (also apply the same fix at the other
occurrences noted).
- Around line 454-457: The public factory functions that build Colors (e.g., the
default() and dark/defaultDark() factories) currently instantiate
ColorScale.defaultLight()/defaultDark() and
ChromeScale.defaultLight()/defaultDark() internally, preventing callers from
supplying custom scales; change those factory signatures (the public fun
default() and the corresponding dark variant around lines 530-533) to accept
parameters brand: ColorScale = ColorScale.defaultLight() and chrome: ChromeScale
= ChromeScale.defaultLight() (and analogously for the dark factory using
defaultDark()), then use those parameters when constructing and deriving token
values so callers can pass custom scales while preserving existing defaults.
---
Nitpick comments:
In `@stream-chat-android-compose/api/stream-chat-android-compose.api`:
- Around line 6324-6361: Add an explicit old→new token mapping to the v7
migration notes covering the public renames shown (e.g., avatarPresenceBorder →
getAvatarPresenceBorder-0d7_KjU, backgroundCoreElevation0..4 →
getBackgroundCoreElevation0-0d7_KjU .. getBackgroundCoreElevation4-0d7_KjU,
backgroundUtilitySelected → getBackgroundUtilitySelected-0d7_KjU,
borderCoreOpacityStrong/Subtle → getBorderCoreOpacityStrong-0d7_KjU /
getBorderCoreOpacitySubtle-0d7_KjU, brand → getBrand (StreamDesign$ColorScale),
chrome → getChrome (StreamDesign$ChromeScale), plus chat-related tokens like
getChatPollProgressFillOutgoing-0d7_KjU and reply indicator tokens) and list
each old name alongside its new API symbol; update the v7 changelog/migration
doc with a concise table or bullet list mapping every renamed token so
downstream theme overrides can be updated easily.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.kt`:
- Around line 17-18: Remove the file-level `@file`:Suppress("MagicNumber",
"LongMethod") in StreamDesign.kt and instead apply `@Suppress`("MagicNumber")
and/or `@Suppress`("LongMethod") directly on the specific factory declarations
that actually trigger these warnings (the theme/factory functions in this file).
Add a short rationale comment above each suppression (e.g., "// Suppress
MagicNumber: design tokens/constants expressed as raw numbers for theme factory"
or "// Suppress LongMethod: factory builds many palette entries; splitting
reduces clarity") so the scope is narrow and documented.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 58ea5316-231b-4d2e-bc8f-e952521d35ae
⛔ Files ignored due to path filters (9)
stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.attachments.images_ImagesPickerTest_add_more.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.attachments.images_ImagesPickerTest_selection.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages.attachments_AttachmentMediaPickerTest_selection.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages.composer.internal.attachments_MessageComposerAttachmentsTest_message_composer_attachments.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages.composer.internal.attachments_MessageComposerAttachmentsTest_video_attachment_item.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageComposerInputTest_attachments.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageComposerInputTest_attachments_and_link.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageComposerInputTest_edit,_attachments,_and_link.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageComposerInputTest_reply,_attachments,_and_link.pngis excluded by!**/*.png
📒 Files selected for processing (72)
stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/add/AddChannelScreen.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/add/component/SearchUserTextField.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/add/group/AddGroupChannelScreen.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/reminders/MessageReminderDialogs.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/channel/AddMembersDialog.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/AppBottomBar.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/AppToolbar.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/Pane.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/SharedLocationItem.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/location/DurationDropdownMenu.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/location/LocationComponentFactory.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/login/CustomLoginActivity.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/login/UserLoginActivity.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/profile/UserProfilePushPreferencesScreen.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/profile/UserProfileScreen.ktstream-chat-android-compose/api/stream-chat-android-compose.apistream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/MediaAttachmentContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivity.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewOptionsMenu.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/MediaGalleryPhotosMenu.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/attachments/ChannelMediaAttachmentsGrid.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/info/ChannelInfoMemberInfoModalSheet.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channels/header/ChannelListHeader.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channels/list/ChannelItem.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/OnlineIndicator.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/PullToRefreshBox.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/SimpleDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/SimpleMenu.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/images/ImagesPicker.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/avatar/Avatar.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/avatar/ChannelAvatar.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/avatar/OnlineIndicator.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/button/StreamButtonStyle.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/ContextualMenu.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/MediaBadges.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/RadioControls.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/ComposerLinkPreview.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/MessageInput.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageReactions.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/PollMessageContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/ScrollToBottomButton.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollAnswers.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollVoteItem.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/reactions/ReactionToggle.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/MessageMenuHeader.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/ReactionCountRow.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/ReactionsMenu.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentPicker.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentSystemPicker.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentTypePicker.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollOptionList.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollQuestionInput.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchList.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/MessageComposer.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/AudioRecordingButton.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/AudioRecordingContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputCenterBottomContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputTrailingContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerLeadingContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/suggestions/SuggestionsMenu.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/header/MessageListHeader.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactory.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamPrimitiveColors.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/channel/info/ChannelInfoMemberInfoModalSheetTest.ktstream-chat-android-docs/src/main/kotlin/io/getstream/chat/docs/kotlin/compose/guides/AddingCustomAttachments.ktstream-chat-android-docs/src/main/kotlin/io/getstream/chat/docs/kotlin/compose/messages/MessageList.ktstream-chat-android-ui-guides/src/main/java/io/getstream/chat/android/guides/catalog/compose/customattachments/CustomChatComponentFactory.kt
...hat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.kt
Show resolved
Hide resolved
...hat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.kt
Outdated
Show resolved
Hide resolved
...hat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.kt
Show resolved
Hide resolved
VelikovPetar
left a comment
There was a problem hiding this comment.
LGTM!
Maybe @gpunto can also have a look, as I wasn't fully included in this part - in case I am missing anything!
c12dd78 to
cf0b053
Compare
|
Jurgen is making some changes in Figma so we can avoid exposing the only 4 remaining component tokens. |
|
Now, it remains only one. Waiting for Jurgen's response... |
…atBgIncoming`, `controlRemoveBg`) from the primary constructor to derived properties with default values.
…was renamed to backgroundCoreElevation1 in the design token reorganization.
…ferences as per Figma tokens
… `public` to `internal`.
…hatReplyIndicatorOutgoing`, and `controlPlaybackThumbBgDefault` to the end of the `Colors` class and its default factory methods to match the updated organizational structure.
…alette constructors.
…le default colors.
…d `chatReplyIndicatorOutgoing` internal derived, and update `skeletonLoadingHighlight` values.
066b041 to
0ed6835
Compare
|



Goal
Align the Compose SDK's
StreamDesign.ColorsAPI with the Figma design token system to establish a 1:1 mapping between code and design. Every constructor parameter, derived property, and KDoc comment inStreamDesign.ktshould trace directly to a Figma semantic token — making handoff between design and engineering predictable and auditable.Implementation
1. Introduce
ColorScaleandChromeScaletypesFigma defines two intermediate alias scales — brand (accent ramp, 11 stops) and chrome (neutral gray ramp, 13 stops) — that sit between raw primitives and semantic tokens. Previously, the SDK flattened these into ~24 individual
Colorparameters, losing the scale relationship.New
data classtypes replace those loose params:ColorScale.from(brandColor)generates an 11-stop ramp from a single color via Oklablerp, andinverted()creates the dark-theme counterpart:2. Rename tokens for Figma traceability
All token names now mirror the Figma semantic path. A developer can read a token name and find the exact Figma variable:
border.core.opacity-subtleborderCoreOpacitySubtlebackground.utility.disabledbackgroundUtilityDisabledbackground.core.elevation-*backgroundCoreElevation*avatar.presence.borderavatarPresenceBordercontrol.chip.textcontrolChipText3. Semantic vs component token split
Constructor parameters contain only semantic-level tokens (reusable across components), while component-level tokens (aliases of semantic tokens) are
internalderived body vals. Integrators use the constructor params for theming via.copy()or factory parameters; component tokens are implementation details of the SDK.Result: 2 objects + 60 colors = 62 constructor params (was 74), 90 internal derived body vals (was 76 public).
4. Factory methods accept
brand/chromeand use scale referencesdefault()anddefaultDark()accept optionalbrandandchromeparameters, and referencebrand.sX/chrome.sXinstead of resolved primitives, matching the Figma alias chain:Tokens that Figma references via raw primitives (e.g.,
{red.500},{yellow.50}) or raw rgba values (scrim, overlays) remain asStreamPrimitiveColors.*.5. Remove dead SDK-only tokens
Removed tokens that had no Figma counterpart and were either unused or pointed to the wrong underlying value:
inputBorderDefault,inputBorderHover,inputBorderSelected— mismatched Figma; usages updated to reference the correct underlying tokens directlychipBg,composerBg— dead code, no Figma equivalent6. Reorder constructor params and derived vals
Constructor params follow the Figma token hierarchy:
Derived body vals are organized alphabetically within component groups:
7. Align all KDoc with Figma JSON comments
Cross-referenced every
@paramtag and derived val comment against Figma's"comment"fields. Fixes fall into three categories:backgroundCoreSurfacesaid "buttons", Figma says "Standard section background";borderUtilityActivesaid "active/selected", Figma says "Focus ring or focus border")backgroundCoreOnAccentwas missing "Do not use for general UI surfaces";avatarPresenceBorderwas missing high-contrast behavior note)🎨 UI Changes
The only UI change is on the media badge, which now has the correct background color on dark mode.
Testing
Summary by CodeRabbit
Release Notes
Style
New Features
Tests