Skip to content

Commit 4c5935f

Browse files
authored
docs: rework React theming — capture harness + tutorial example (#3133)
## Summary Two sets of changes that together bring the React SDK's theming story up to date with v14: 1. **Rework the Playwright screenshot pipeline** used for the React theming doc (`docs-content: chat-sdk/react/v14-latest/02-ui-components/02-theming/01-themingv2.md`). 2. **Sync `examples/tutorial`** so its per-step `layout.css` + `App.tsx` match the revised tutorial published in getstream.io-tutorials. Companion doc-content PR: **GetStream/docs-content#1213** — rewrites the theming doc with working v14 tokens and regenerates every screenshot using the harness in this PR. ### 1. Capture-harness rework (`examples/vite/docs-playwright/`) **Pipeline reliability** - Shared capture context across all screenshots (previously each page spun up a fresh context, racing an empty channel list on themed variants). - Viewport bumped to 1280×1200 at 1× DPR (portrait framing fits more of the conversation). - `scrollToLatest()` before every screenshot so both users' latest bubbles are in frame. - `waitForChatUI()` now also waits for at least one rendered `.str-chat__li` before the capture fires. - Explicit channel create with both members + friendly name + truncate so reruns don't accumulate duplicate seed messages. - Headless by default. Set `HEADED=1` to override. - Fixed `ASSETS_DIR` — previous path (`../../docs/...`) resolved to a non-existent directory. **Content** - Extracted CSS override blocks to `docs-playwright/theming-variants.mjs` so the harness and the doc stay in lockstep. - Rewrote the seed conversation: two-user dialogue, markdown (`**bold**`, `*italic*`, `` `code` ``), GitHub URL preview, images, richer reaction variety. - Dropped obsolete capture flows (link-attachment text color, layout-only CSS) — neither applies to v14. **Deps** - Added `@playwright/test@^1.59.1` as a devDep of `examples/vite`. ### 2. `examples/tutorial` sync The per-step tutorial example was still using tokens that have **zero consumers** in v14's built CSS (`--brand-50..900`, `--radius-full`) and scoped the custom theme to `.str-chat__theme-custom`, which colonizes the SDK's `str-chat__` namespace for a user-defined class. - `layout.css` for steps 3–7 (byte-identical before this change): replaced the dead overrides with the semantic-token block used in the published tutorial. - `App.tsx` for steps 3/4/5: renamed `theme='str-chat__theme-custom'` → `theme='custom-theme'`. ### Test plan **Capture harness:** ```bash cd examples/vite yarn install yarn dev # in another shell node docs-playwright/seed-channel-and-screenshot.mjs # seed + capture node docs-playwright/seed-channel-and-screenshot.mjs --skip-seed # just recapture ``` Output lands in `../../../../docs/data/docs/chat-sdk/react/v14-latest/_assets/`. Use `HEADED=1 node ...` to watch the browser. **Tutorial example:** ```bash cd examples/tutorial yarn install yarn dev # visit each step and confirm the themed screens render with the new # deep-blue palette (navy outgoing bubbles, light-blue incoming, # pale-blue panel tint) ```
1 parent eac76dc commit 4c5935f

File tree

18 files changed

+625
-389
lines changed

18 files changed

+625
-389
lines changed

examples/tutorial/.env.example

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1+
# Required: your Stream app's public key.
12
VITE_API_KEY=REPLACE_WITH_API_KEY
2-
VITE_USER_ID=REPLACE_WITH_USER_ID
3-
VITE_USER_NAME=REPLACE_WITH_USER_NAME
4-
VITE_USER_TOKEN=REPLACE_WITH_USER_TOKEN
3+
4+
# Optional. If unset, the app defaults to user_id "react-tutorial" and
5+
# derives user_name from it. You can also override either value per-run
6+
# via URL params: ?user_id=alice&user_name=Alice
7+
# VITE_USER_ID=react-tutorial
8+
# VITE_USER_NAME=React Tutorial
9+
10+
# Optional. Token endpoint used to mint a fresh JWT for the active
11+
# user_id. Defaults to Stream's demo endpoint; override if you're
12+
# pointing at a different Stream app / token service.
13+
# VITE_TOKEN_ENDPOINT=https://pronto.getstream.io/api/auth/create-token
14+
# VITE_TOKEN_ENVIRONMENT=demo

examples/tutorial/src/1-client-setup/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { Chat, useCreateChatClient } from 'stream-chat-react';
2-
import { apiKey, userId, userName, userToken } from './credentials';
2+
import { apiKey, userId, userName, tokenProvider } from './credentials';
33

44
const App = () => {
55
const client = useCreateChatClient({
66
apiKey,
7-
tokenOrProvider: userToken,
7+
tokenOrProvider: tokenProvider,
88
userData: { id: userId, name: userName },
99
});
1010

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,45 @@
1-
// your Stream app information
1+
// Stream Chat credentials for the tutorial example.
2+
//
3+
// The example fetches a fresh JWT from pronto.getstream.io for whichever
4+
// user_id is active, so the app stays runnable without pasting a token
5+
// that expires, and you can switch users via URL params at runtime:
6+
//
7+
// ?user_id=alice // different user
8+
// ?user_id=alice&user_name=Alice // + display name override
9+
//
10+
// Notes:
11+
// - apiKey is the one thing you still need to set (via VITE_API_KEY).
12+
// - The token endpoint and environment default to the values shared with
13+
// the other example apps in this repo; override with VITE_TOKEN_ENDPOINT
14+
// and VITE_TOKEN_ENVIRONMENT if you're pointing at a different Stream
15+
// app.
16+
17+
const searchParams = new URLSearchParams(window.location.search);
18+
219
export const apiKey = import.meta.env.VITE_API_KEY;
3-
export const userId = import.meta.env.VITE_USER_ID;
4-
export const userName = import.meta.env.VITE_USER_NAME;
5-
export const userToken = import.meta.env.VITE_USER_TOKEN;
20+
21+
export const userId =
22+
searchParams.get('user_id') || import.meta.env.VITE_USER_ID || 'react-tutorial';
23+
24+
export const userName =
25+
searchParams.get('user_name') || import.meta.env.VITE_USER_NAME || userId;
26+
27+
const tokenEndpoint =
28+
import.meta.env.VITE_TOKEN_ENDPOINT ||
29+
'https://pronto.getstream.io/api/auth/create-token';
30+
const tokenEnvironment = import.meta.env.VITE_TOKEN_ENVIRONMENT || 'demo';
31+
32+
// Stream's `useCreateChatClient` accepts either a token string or a provider
33+
// function. A provider lets the SDK refresh the token on reconnect, which is
34+
// what we want for a long-running example session.
35+
export const tokenProvider = async (): Promise<string> => {
36+
const url = `${tokenEndpoint}?environment=${encodeURIComponent(
37+
tokenEnvironment,
38+
)}&user_id=${encodeURIComponent(userId)}`;
39+
const response = await fetch(url);
40+
if (!response.ok) {
41+
throw new Error(`Failed to mint token from ${tokenEndpoint} (${response.status})`);
42+
}
43+
const data = (await response.json()) as { token: string };
44+
return data.token;
45+
};

examples/tutorial/src/2-core-component-setup/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313

1414
import 'stream-chat-react/dist/css/index.css';
1515
import './layout.css';
16-
import { apiKey, userId, userName, userToken } from '../1-client-setup/credentials';
16+
import { apiKey, userId, userName, tokenProvider } from '../1-client-setup/credentials';
1717

1818
const user: User = {
1919
id: userId,
@@ -25,7 +25,7 @@ const App = () => {
2525
const [channel, setChannel] = useState<StreamChannel>();
2626
const client = useCreateChatClient({
2727
apiKey,
28-
tokenOrProvider: userToken,
28+
tokenOrProvider: tokenProvider,
2929
userData: user,
3030
});
3131

examples/tutorial/src/3-channel-list/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
} from 'stream-chat-react';
1313

1414
import './layout.css';
15-
import { apiKey, userId, userName, userToken } from '../1-client-setup/credentials';
15+
import { apiKey, userId, userName, tokenProvider } from '../1-client-setup/credentials';
1616

1717
const user: User = {
1818
id: userId,
@@ -32,14 +32,14 @@ const options: ChannelOptions = {
3232
const App = () => {
3333
const client = useCreateChatClient({
3434
apiKey,
35-
tokenOrProvider: userToken,
35+
tokenOrProvider: tokenProvider,
3636
userData: user,
3737
});
3838

3939
if (!client) return <div>Setting up client & connection...</div>;
4040

4141
return (
42-
<Chat client={client} theme='str-chat__theme-custom'>
42+
<Chat client={client} theme='custom-theme'>
4343
<ChannelList filters={filters} sort={sort} options={options} />
4444
<Channel>
4545
<Window>

examples/tutorial/src/3-channel-list/layout.css

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,31 @@
22
@import 'stream-chat-react/dist/css/index.css' layer(stream);
33

44
@layer stream-overrides {
5-
.str-chat__theme-custom {
6-
--brand-50: #edf7f7;
7-
--brand-100: #e0f2f1;
8-
--brand-150: #b2dfdb;
9-
--brand-200: #80cbc4;
10-
--brand-300: #4db6ac;
11-
--brand-400: #26a69a;
12-
--brand-500: #009688;
13-
--brand-600: #00897b;
14-
--brand-700: #00796b;
15-
--brand-800: #00695c;
16-
--brand-900: #004d40;
17-
--accent-primary: var(--brand-500);
18-
--radius-full: 6px;
5+
.custom-theme {
6+
/* Accent */
7+
--accent-primary: #0d47a1;
8+
9+
/* Message bubble colors */
10+
--chat-bg-outgoing: #1e3a8a;
11+
--chat-bg-attachment-outgoing: #0d47a1;
12+
--chat-bg-incoming: #dbeafe;
13+
--chat-text-outgoing: #ffffff;
14+
--chat-reply-indicator-outgoing: #93c5fd;
15+
16+
/* Links */
17+
--text-link: #1e40af;
18+
--chat-text-link: #93c5fd;
19+
20+
/* Panel backgrounds */
21+
--background-core-elevation-1: #dbeafe; /* channel list, surrounding panels */
22+
--background-core-app: #c7dafc; /* message list background */
23+
24+
/* Focus ring */
25+
--border-utility-focused: #1e40af;
26+
27+
/* Radii */
28+
--radius-max: 8px;
29+
--button-radius-full: 6px;
1930
}
2031
}
2132

examples/tutorial/src/4-custom-ui-components/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from 'stream-chat-react';
1818

1919
import './layout.css';
20-
import { apiKey, userId, userName, userToken } from '../1-client-setup/credentials';
20+
import { apiKey, userId, userName, tokenProvider } from '../1-client-setup/credentials';
2121

2222
const user: User = {
2323
id: userId,
@@ -118,7 +118,7 @@ const App = () => {
118118
const [isReady, setIsReady] = useState(false);
119119
const client = useCreateChatClient({
120120
apiKey,
121-
tokenOrProvider: userToken,
121+
tokenOrProvider: tokenProvider,
122122
userData: user,
123123
});
124124

@@ -151,7 +151,7 @@ const App = () => {
151151
Message: CustomMessage,
152152
}}
153153
>
154-
<Chat client={client} theme='str-chat__theme-custom'>
154+
<Chat client={client} theme='custom-theme'>
155155
<ChannelList filters={filters} sort={sort} options={options} />
156156
<Channel>
157157
<Window>

examples/tutorial/src/4-custom-ui-components/layout.css

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,31 @@
22
@import 'stream-chat-react/dist/css/index.css' layer(stream);
33

44
@layer stream-overrides {
5-
.str-chat__theme-custom {
6-
--brand-50: #edf7f7;
7-
--brand-100: #e0f2f1;
8-
--brand-150: #b2dfdb;
9-
--brand-200: #80cbc4;
10-
--brand-300: #4db6ac;
11-
--brand-400: #26a69a;
12-
--brand-500: #009688;
13-
--brand-600: #00897b;
14-
--brand-700: #00796b;
15-
--brand-800: #00695c;
16-
--brand-900: #004d40;
17-
--accent-primary: var(--brand-500);
18-
--radius-full: 6px;
5+
.custom-theme {
6+
/* Accent */
7+
--accent-primary: #0d47a1;
8+
9+
/* Message bubble colors */
10+
--chat-bg-outgoing: #1e3a8a;
11+
--chat-bg-attachment-outgoing: #0d47a1;
12+
--chat-bg-incoming: #dbeafe;
13+
--chat-text-outgoing: #ffffff;
14+
--chat-reply-indicator-outgoing: #93c5fd;
15+
16+
/* Links */
17+
--text-link: #1e40af;
18+
--chat-text-link: #93c5fd;
19+
20+
/* Panel backgrounds */
21+
--background-core-elevation-1: #dbeafe; /* channel list, surrounding panels */
22+
--background-core-app: #c7dafc; /* message list background */
23+
24+
/* Focus ring */
25+
--border-utility-focused: #1e40af;
26+
27+
/* Radii */
28+
--radius-max: 8px;
29+
--button-radius-full: 6px;
1930
}
2031
}
2132

examples/tutorial/src/5-custom-attachment-type/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
} from 'stream-chat-react';
2020

2121
import './layout.css';
22-
import { apiKey, userId, userName, userToken } from '../1-client-setup/credentials';
22+
import { apiKey, userId, userName, tokenProvider } from '../1-client-setup/credentials';
2323

2424
const user: User = {
2525
id: userId,
@@ -76,7 +76,7 @@ const App = () => {
7676
const [channel, setChannel] = useState<StreamChannel>();
7777
const client = useCreateChatClient({
7878
apiKey,
79-
tokenOrProvider: userToken,
79+
tokenOrProvider: tokenProvider,
8080
userData: user,
8181
});
8282

@@ -118,7 +118,7 @@ const App = () => {
118118

119119
return (
120120
<WithComponents overrides={{ Attachment: CustomAttachment }}>
121-
<Chat client={client} theme='str-chat__theme-custom'>
121+
<Chat client={client} theme='custom-theme'>
122122
<Channel channel={channel}>
123123
<Window>
124124
<ChannelHeader />

examples/tutorial/src/5-custom-attachment-type/layout.css

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,31 @@
22
@import 'stream-chat-react/dist/css/index.css' layer(stream);
33

44
@layer stream-overrides {
5-
.str-chat__theme-custom {
6-
--brand-50: #edf7f7;
7-
--brand-100: #e0f2f1;
8-
--brand-150: #b2dfdb;
9-
--brand-200: #80cbc4;
10-
--brand-300: #4db6ac;
11-
--brand-400: #26a69a;
12-
--brand-500: #009688;
13-
--brand-600: #00897b;
14-
--brand-700: #00796b;
15-
--brand-800: #00695c;
16-
--brand-900: #004d40;
17-
--accent-primary: var(--brand-500);
18-
--radius-full: 6px;
5+
.custom-theme {
6+
/* Accent */
7+
--accent-primary: #0d47a1;
8+
9+
/* Message bubble colors */
10+
--chat-bg-outgoing: #1e3a8a;
11+
--chat-bg-attachment-outgoing: #0d47a1;
12+
--chat-bg-incoming: #dbeafe;
13+
--chat-text-outgoing: #ffffff;
14+
--chat-reply-indicator-outgoing: #93c5fd;
15+
16+
/* Links */
17+
--text-link: #1e40af;
18+
--chat-text-link: #93c5fd;
19+
20+
/* Panel backgrounds */
21+
--background-core-elevation-1: #dbeafe; /* channel list, surrounding panels */
22+
--background-core-app: #c7dafc; /* message list background */
23+
24+
/* Focus ring */
25+
--border-utility-focused: #1e40af;
26+
27+
/* Radii */
28+
--radius-max: 8px;
29+
--button-radius-full: 6px;
1930
}
2031
}
2132

0 commit comments

Comments
 (0)