From 644d1060a899fcfb158495bbdb6e1312314398cf Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Mon, 30 Mar 2026 17:03:51 +0200 Subject: [PATCH 1/4] fix: close CSS gaps in SDK layout and fix ChannelList dialog portal Several SDK components were missing layout properties that consumers had to work around (e.g. min-height: 0 on flex children, width: 100% on headers and main-panel-inner). Additionally, the ChannelList's DialogManagerProvider rendered its portal destination outside the .str-chat element, causing context menus to lose theme styles. - Add min-height: 0 to .str-chat__chat-view, __chat-view__channels, and __channel (flex overflow fix) - Add width: 100% to header-layout mixin and .str-chat__main-panel-inner - Add scrollbar-gutter: stable to scrollable-y mixin - Remove incorrect height: auto override on thread's main-panel-inner - Move DialogManagerProvider inside .str-chat div in ChannelList - Remove now-redundant overrides from vite example --- examples/vite/src/index.scss | 62 ------------------- src/components/Channel/styling/Channel.scss | 1 + src/components/ChannelList/ChannelList.tsx | 16 ++--- src/components/ChatView/styling/ChatView.scss | 2 + .../MessageList/styling/MessageList.scss | 1 + src/components/Thread/styling/Thread.scss | 11 ---- src/styling/_utils.scss | 2 + 7 files changed, 14 insertions(+), 81 deletions(-) diff --git a/examples/vite/src/index.scss b/examples/vite/src/index.scss index 8540efdc6b..3c432f8f9d 100644 --- a/examples/vite/src/index.scss +++ b/examples/vite/src/index.scss @@ -61,15 +61,7 @@ body { width: 100%; } - .str-chat__chat-view, - .str-chat__chat-view-channels, - .str-chat__channel, - .str-chat__window { - min-height: 0; - } - .str-chat__chat-view { - height: 100%; container-type: inline-size; } @@ -179,74 +171,20 @@ body { min-width: 0; } - .str-chat__chat-view-channels { - height: 100%; - gap: 0; - } - - .str-chat__channel-list { - height: 100%; - } - - .str-chat__main-panel { - height: 100%; - align-items: stretch; - } - - .str-chat__main-panel-inner { - width: 100%; - height: 100%; - max-width: none; - } - - .str-chat__window { - height: 100%; - } - .app-loading-screen__window { width: 100%; } - .str-chat__channel-header, - .str-chat__thread-header { - width: 100%; - max-width: none; - } - .str-chat__list, .str-chat__virtual-list { display: block; - scrollbar-gutter: stable; scrollbar-width: thin; } - .str-chat__message-input { - width: 100%; - //max-width: none; - } - .str-chat__tooltip { z-index: 10; } - /* Threads view: thread detail takes all space (higher specificity than channel 360px rules). */ - .str-chat__chat-view .str-chat__chat-view__threads { - .str-chat__dropzone-root--thread, - .str-chat__thread-container { - flex: 1 1 auto; - min-width: 0; - max-width: none; - width: 100%; - } - } - - .str-chat__thread-list-container .str-chat__thread-container { - flex: 1 1 auto; - min-width: 0; - width: 100%; - max-width: none; - } - @media (max-width: 767px) { .app-chat-resize-handle { display: none; diff --git a/src/components/Channel/styling/Channel.scss b/src/components/Channel/styling/Channel.scss index e5b114bffb..445d48cb13 100644 --- a/src/components/Channel/styling/Channel.scss +++ b/src/components/Channel/styling/Channel.scss @@ -4,6 +4,7 @@ height: 100%; display: flex; flex-direction: column; + min-height: 0; position: relative; .str-chat__container { diff --git a/src/components/ChannelList/ChannelList.tsx b/src/components/ChannelList/ChannelList.tsx index 8f2ce3c7b3..95679a3a51 100644 --- a/src/components/ChannelList/ChannelList.tsx +++ b/src/components/ChannelList/ChannelList.tsx @@ -341,11 +341,11 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { const showChannelList = !searchIsActive; return ( - - -
+ +
+ {showChannelSearch && } {showChannelList && ( @@ -374,9 +374,9 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { )} -
-
- + +
+
); }; diff --git a/src/components/ChatView/styling/ChatView.scss b/src/components/ChatView/styling/ChatView.scss index 587e5a8eb6..53346b7541 100644 --- a/src/components/ChatView/styling/ChatView.scss +++ b/src/components/ChatView/styling/ChatView.scss @@ -18,6 +18,7 @@ display: flex; width: 100%; height: 100%; + min-height: 0; position: relative; .str-chat__chat-view__selector { @@ -136,6 +137,7 @@ display: flex; flex-grow: 1; min-width: 0; + min-height: 0; position: relative; } diff --git a/src/components/MessageList/styling/MessageList.scss b/src/components/MessageList/styling/MessageList.scss index e48e860e23..d3f1d3648a 100644 --- a/src/components/MessageList/styling/MessageList.scss +++ b/src/components/MessageList/styling/MessageList.scss @@ -2,6 +2,7 @@ // Layout .str-chat__main-panel-inner { + width: 100%; height: 100%; display: flex; flex-direction: column; diff --git a/src/components/Thread/styling/Thread.scss b/src/components/Thread/styling/Thread.scss index 0d0feda27b..78702b9866 100644 --- a/src/components/Thread/styling/Thread.scss +++ b/src/components/Thread/styling/Thread.scss @@ -39,14 +39,3 @@ width: 100%; } -.str-chat__thread { - .str-chat__main-panel-inner { - height: auto; - } -} - -.str-chat__thread--virtualized { - .str-chat__main-panel-inner { - height: 100%; - } -} diff --git a/src/styling/_utils.scss b/src/styling/_utils.scss index 62eeb2d6a7..f43a82838a 100644 --- a/src/styling/_utils.scss +++ b/src/styling/_utils.scss @@ -77,6 +77,7 @@ overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; + scrollbar-gutter: stable; } @mixin modal { @@ -104,6 +105,7 @@ @mixin header-layout { display: flex; align-items: center; + width: 100%; padding: var(--spacing-md); gap: var(--spacing-sm); height: var(--str-chat__channel-header-height); From 21516b1f1757a54b86adcac35e0c7407cb33e840 Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Mon, 30 Mar 2026 17:06:47 +0200 Subject: [PATCH 2/4] chore: shorten imports --- examples/vite/src/App.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index b873e68a03..35ae6300d5 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -34,7 +34,7 @@ import { init, SearchIndex } from 'emoji-mart'; import data from '@emoji-mart/data/sets/14/native.json'; import { humanId } from 'human-id'; -import { appSettingsStore, useAppSettingsSelector } from './AppSettings/state.ts'; +import { appSettingsStore, useAppSettingsSelector } from './AppSettings'; import { DESKTOP_LAYOUT_BREAKPOINT } from './ChatLayout/constants.ts'; import { ChannelsPanels, ThreadsPanels } from './ChatLayout/Panels.tsx'; import { @@ -60,7 +60,7 @@ import { getMessageUiVariant, getReactionsVariant, getSystemMessageVariant, -} from './CustomMessageUi/index.tsx'; +} from './CustomMessageUi'; init({ data }); @@ -191,7 +191,6 @@ const App = () => { () => initialSearchParams.get('thread'), [initialSearchParams], ); - const initialThreadOpen = useMemo(() => Boolean(initialThreadId), [initialThreadId]); const initialPanelLayout = useMemo( () => appSettingsStore.getLatestValue().panelLayout, [], From 4bb79c06b909c30e7d8e9dd897c3fcfbcf3d1f3c Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Mon, 30 Mar 2026 17:21:48 +0200 Subject: [PATCH 3/4] chore: fix trailing newline in Thread.scss --- src/components/Thread/styling/Thread.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Thread/styling/Thread.scss b/src/components/Thread/styling/Thread.scss index 78702b9866..be1b9aef7a 100644 --- a/src/components/Thread/styling/Thread.scss +++ b/src/components/Thread/styling/Thread.scss @@ -38,4 +38,3 @@ flex-direction: column; width: 100%; } - From 4e4a0a54e822093af2a3fccdd745521b8c25afcc Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Mon, 30 Mar 2026 18:03:00 +0200 Subject: [PATCH 4/4] fix(Icons): fix filled icons and remove unused icon exports Use IconThreadFill for the active threads tab in ChatView instead of the old IconBubbleText6SolidChatMessage. Fix MessageActions CSS that overrode fill='none' on stroke-based Phosphor icons, causing the reply icon to appear filled. Remove 9 unused icon exports to reduce bundle size. Update vite example app to use available icons and local definitions for gear/lightbulb. --- .../ActionsMenu/NotificationPromptDialog.tsx | 9 +-- examples/vite/src/AppSettings/AppSettings.tsx | 30 ++++++-- src/components/ChatView/ChatView.tsx | 6 +- src/components/Icons/icons.tsx | 76 ------------------- .../styling/MessageActions.scss | 10 +-- src/components/Notifications/Notification.tsx | 2 +- 6 files changed, 32 insertions(+), 101 deletions(-) diff --git a/examples/vite/src/AppSettings/ActionsMenu/NotificationPromptDialog.tsx b/examples/vite/src/AppSettings/ActionsMenu/NotificationPromptDialog.tsx index d786183651..92397de8a1 100644 --- a/examples/vite/src/AppSettings/ActionsMenu/NotificationPromptDialog.tsx +++ b/examples/vite/src/AppSettings/ActionsMenu/NotificationPromptDialog.tsx @@ -5,16 +5,15 @@ import { addNotificationTargetTag, IconArrowDown, IconArrowLeft, - IconArrowRight, + IconChevronRight, Button, DialogAnchor, IconRefresh, IconArrowUp, IconCheckmark, - IconInfo, + IconExclamationMark, IconClock, IconXmark, - IconExclamationMark, IconExclamationTriangle, IconPlusSmall, NumericInput, @@ -51,7 +50,7 @@ const severityIcons: Partial< Record> > = { error: IconExclamationMark, - info: IconInfo, + info: IconExclamationMark, loading: IconRefresh, success: IconCheckmark, warning: IconExclamationTriangle, @@ -62,7 +61,7 @@ const directionIcons: Record< React.ComponentType<{ className?: string }> > = { bottom: IconArrowUp, - left: IconArrowRight, + left: IconChevronRight, right: IconArrowLeft, top: IconArrowDown, }; diff --git a/examples/vite/src/AppSettings/AppSettings.tsx b/examples/vite/src/AppSettings/AppSettings.tsx index ad10b06f63..a13e56bfd0 100644 --- a/examples/vite/src/AppSettings/AppSettings.tsx +++ b/examples/vite/src/AppSettings/AppSettings.tsx @@ -1,14 +1,29 @@ +import React from 'react'; import { Button, ChatViewSelectorButton, + createIcon, GlobalModal, - IconBubble3ChatMessage, IconBell, IconEmoji, - IconLightBulbSimple, - IconSettingsGear2, + IconMessageBubble, } from 'stream-chat-react'; import { type ComponentType, useState } from 'react'; + +const IconGear = createIcon( + 'IconGear', + , + { viewBox: '0 0 16 16' }, +); + +const IconLightBulb = createIcon( + 'IconLightBulb', + <> + + + , + { viewBox: '0 0 16 16' }, +); import { ActionsMenu } from './ActionsMenu'; import { NotificationsTab } from './tabs/Notifications'; import { ReactionsTab } from './tabs/Reactions'; @@ -19,7 +34,7 @@ type TabId = 'notifications' | 'reactions' | 'sidebar'; const tabConfig: { Icon: ComponentType; id: TabId; title: string }[] = [ { Icon: IconBell, id: 'notifications', title: 'Notifications' }, - { Icon: IconBubble3ChatMessage, id: 'sidebar', title: 'Sidebar' }, + { Icon: IconMessageBubble, id: 'sidebar', title: 'Sidebar' }, { Icon: IconEmoji, id: 'reactions', title: 'Reactions' }, ]; @@ -36,7 +51,7 @@ const SidebarThemeToggle = ({ iconOnly = true }: { iconOnly?: boolean }) => { aria-selected={mode === 'dark'} className='app__settings-group_button' iconOnly={iconOnly} - Icon={IconLightBulbSimple} + Icon={IconLightBulb} isActive={mode === 'dark'} onClick={() => appSettingsStore.partialNext({ @@ -44,6 +59,7 @@ const SidebarThemeToggle = ({ iconOnly = true }: { iconOnly?: boolean }) => { }) } role='switch' + style={{ color: mode === 'dark' ? '#facc15' : undefined }} text={mode === 'dark' ? 'Dark mode' : 'Light mode'} /> ); @@ -60,14 +76,14 @@ export const AppSettings = ({ iconOnly = true }: { iconOnly?: boolean }) => { setOpen(true)} text='Settings' /> setOpen(false)}>
- + Settings
diff --git a/src/components/ChatView/ChatView.tsx b/src/components/ChatView/ChatView.tsx index 2abe9aa732..89f06671fc 100644 --- a/src/components/ChatView/ChatView.tsx +++ b/src/components/ChatView/ChatView.tsx @@ -11,10 +11,10 @@ import React, { import { Button, type ButtonProps } from '../Button'; import { EmptyStateIndicator as DefaultEmptyStateIndicator } from '../EmptyStateIndicator'; import { - IconBubbleText6SolidChatMessage, IconMessageBubble, IconMessageBubbleFill, IconThread, + IconThreadFill, } from '../Icons'; import { ThreadProvider } from '../Threads'; import { UnreadCountBadge } from '../Threads/UnreadCountBadge'; @@ -273,7 +273,7 @@ export const ChatViewThreadsSelectorButton = ({ const isActive = activeChatView === 'threads'; return ( - {isActive ? : } + {isActive ? : } ); diff --git a/src/components/Icons/icons.tsx b/src/components/Icons/icons.tsx index 17080f0dce..a85747a3bb 100644 --- a/src/components/Icons/icons.tsx +++ b/src/components/Icons/icons.tsx @@ -46,13 +46,6 @@ export const IconArrowLeft = createIcon( />, ); -// no new icon mapping -export const IconArrowRight = createIcon( - 'IconArrowRight', - , - { viewBox: '0 0 16 16' }, -); - // was: IconArrowRightUp export const IconArrowUpRight = createIcon( 'IconArrowUpRight', @@ -200,12 +193,6 @@ export const IconMessageBubbleFill = createIcon( , ); -export const IconBubble3ChatMessage = createIcon( - 'IconBubble3ChatMessage', - , - { viewBox: '0 0 16 16' }, -); - // was: IconBubbleText6ChatMessage export const IconThread = createIcon( 'IconThread', @@ -219,13 +206,6 @@ export const IconThread = createIcon( />, ); -// no new icon mapping -export const IconBubbleText6SolidChatMessage = createIcon( - 'IconBubbleText6SolidChatMessage', - , - { viewBox: '0 0 16 16' }, -); - // was: IconBubbleText6Solid export const IconThreadFill = createIcon( 'IconThreadFill', @@ -368,16 +348,6 @@ export const IconNoSign = createIcon( />, ); -// was: IconCircleInfoTooltip -export const IconInfo = createIcon( - 'IconInfo', - <> - - - - , -); - // was: IconCircleMinus export const IconMinusCircle = createIcon( 'IconMinusCircle', @@ -611,15 +581,6 @@ export const IconLayoutAlignLeft = createIcon( />, ); -export const IconLightBulbSimple = createIcon( - 'IconLightBulbSimple', - <> - - - , - { viewBox: '0 0 16 16' }, -); - // was: IconMagnifyingGlassSearch export const IconSearch = createIcon( 'IconSearch', @@ -669,37 +630,6 @@ export const IconVoice = createIcon( />, ); -// was: IconMicrophoneSolid -export const IconVoiceFill = createIcon( - 'IconVoiceFill', - , -); - -// was: IconMinusLarge -export const IconMinus = createIcon( - 'IconMinus', - , -); - -export const IconMinusSmall = createIcon( - 'IconMinusSmall', - , -); - export const IconMute = createIcon( 'IconMute', , ); -export const IconSettingsGear2 = createIcon( - 'IconSettingsGear2', - , - { viewBox: '0 0 16 16' }, -); - // was: IconSquareBehindSquare2_Copy export const IconCopy = createIcon( 'IconCopy', diff --git a/src/components/MessageActions/styling/MessageActions.scss b/src/components/MessageActions/styling/MessageActions.scss index 486b717284..e1a6591c8d 100644 --- a/src/components/MessageActions/styling/MessageActions.scss +++ b/src/components/MessageActions/styling/MessageActions.scss @@ -23,12 +23,6 @@ height: var(--str-chat__message-options-button-size); border-radius: var(--str-chat__message-options-border-radius); color: var(--str-chat__message-options-color); - - .str-chat__message-action-icon path { - fill: var(--str-chat__message-options-color); - stroke: var(--str-chat__message-options-color); - stroke-width: 0.25; - } } .str-chat__message-actions-box-button:hover, @@ -40,9 +34,7 @@ .str-chat__message-actions-box-button:active, .str-chat__message-reply-in-thread-button:active, .str-chat__message-reactions-button:active { - .str-chat__message-action-icon path { - fill: var(--str-chat__message-options-active-color); - } + color: var(--str-chat__message-options-active-color); } .str-chat__message-actions-box-button, diff --git a/src/components/Notifications/Notification.tsx b/src/components/Notifications/Notification.tsx index 213f084fc9..6a0c51fa67 100644 --- a/src/components/Notifications/Notification.tsx +++ b/src/components/Notifications/Notification.tsx @@ -23,7 +23,7 @@ export type NotificationIconProps = { const IconsBySeverity: Record = { error: IconExclamationMark, - info: null, // IconInfo, + info: null, loading: IconRefresh, success: IconCheckmark, warning: IconExclamationTriangle,