[DevTools] Tweak the rects design and create multi-environment color scheme (#34880)
<img width="1011" height="811" alt="Screenshot 2025-10-16 at 2 20 46 PM" src="https://github.com/user-attachments/assets/6dea3962-d369-4823-b44f-2c62b566c8f1" /> The selection is now clearer with a wider outline which spans the bounding box if there are multi rects. The color now gets darked changes on hover with a slight animation. The colors are now mixed from constants defined which are consistently used in the rects, the time span in the "suspended by" side bar and the scrubber. I also have constants defined for "server" and "other" debug environments which will be used in a follow up.
Sebastian Markbåge committed
Oct 17, 2025 at 18:51 UTC
ef88c588d51366d16c5323dba4bd197aeb85e4ea
5 files changed
+74
-23
packages/react-devtools-shared/src/devtools/constants.js
+16
-4
@@ -136,8 +136,6 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any, ...} = {
136
'--color-timeline-text-dim-color': '#ccc',
137
'--color-timeline-react-work-border': '#eeeeee',
138
'--color-timebar-background': '#f6f6f6',
139
- '--color-timespan-background': '#62bc6a',
140
- '--color-timespan-background-errored': '#d57066',
139
'--color-search-match': 'yellow',
140
'--color-search-match-current': '#f7923b',
141
'--color-selected-tree-highlight-active': 'rgba(0, 136, 250, 0.1)',
@@ -156,6 +154,14 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any, ...} = {
154
'--color-warning-text-color': '#ffffff',
155
'--color-warning-text-color-inverted': '#fd4d69',
156
157
+ '--color-suspense': '#0088fa',
158
+ '--color-transition': '#6a51b2',
159
+ '--color-suspense-server': '#62bc6a',
160
+ '--color-transition-server': '#3f7844',
161
+ '--color-suspense-other': '#f3ce49',
162
+ '--color-transition-other': '#917b2c',
163
+ '--color-suspense-errored': '#d57066',
164
+
165
// The styles below should be kept in sync with 'root.css'
166
// They are repeated there because they're used by e.g. tooltips or context menus
167
// which get rendered outside of the DOM subtree (where normal theme/styles are written).
@@ -290,8 +296,6 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any, ...} = {
296
'--color-timeline-text-dim-color': '#555b66',
297
'--color-timeline-react-work-border': '#3d424a',
298
'--color-timebar-background': '#1d2129',
293
- '--color-timespan-background': '#62bc6a',
294
- '--color-timespan-background-errored': '#d57066',
299
'--color-search-match': 'yellow',
300
'--color-search-match-current': '#f7923b',
301
'--color-selected-tree-highlight-active': 'rgba(23, 143, 185, 0.15)',
@@ -311,6 +315,14 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any, ...} = {
315
'--color-warning-text-color': '#ffffff',
316
'--color-warning-text-color-inverted': '#ee1638',
317
318
+ '--color-suspense': '#61dafb',
319
+ '--color-transition': '#6a51b2',
320
+ '--color-suspense-server': '#62bc6a',
321
+ '--color-transition-server': '#3f7844',
322
+ '--color-suspense-other': '#f3ce49',
323
+ '--color-transition-other': '#917b2c',
324
+ '--color-suspense-errored': '#d57066',
325
+
326
// The styles below should be kept in sync with 'root.css'
327
// They are repeated there because they're used by e.g. tooltips or context menus
328
// which get rendered outside of the DOM subtree (where normal theme/styles are written).
packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSharedStyles.css
+2
-2
@@ -128,13 +128,13 @@
128
.TimeBarSpan, .TimeBarSpanErrored {
129
position: absolute;
130
border-radius: 0.125rem;
131
- background-color: var(--color-timespan-background);
131
+ background-color: var(--color-suspense);
132
width: 100%;
133
height: 100%;
134
}
135
136
.TimeBarSpanErrored {
137
- background-color: var(--color-timespan-background-errored);
137
+ background-color: var(--color-suspense-errored);
138
}
139
140
.SmallHeader {
packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css
+40
-11
@@ -1,12 +1,20 @@
1
.SuspenseRectsContainer {
2
padding: .25rem;
3
cursor: pointer;
4
- outline: 1px solid var(--color-component-name);
4
+ outline-color: var(--color-transition);
5
+ outline-style: solid;
6
+ outline-width: 1px;
7
border-radius: 0.25rem;
8
+ background-color: color-mix(in srgb, var(--color-transition) 5%, transparent);
9
+}
10
+
11
+.SuspenseRectsContainer:hover:not(:has(.SuspenseRectsBoundary:hover))[data-highlighted='false'] {
12
+ outline-width: 1px;
13
}
14
15
.SuspenseRectsContainer[data-highlighted='true'] {
9
- background: var(--color-dimmest);
16
+ outline-style: solid;
17
+ outline-width: 4px;
18
}
19
20
.SuspenseRectsViewBox {
@@ -15,6 +23,11 @@
23
24
.SuspenseRectsBoundary {
25
pointer-events: all;
26
+ border-radius: 0.125rem;
27
+}
28
+
29
+.SuspenseRectsBoundary[data-visible='false'] {
30
+ background-color: transparent;
31
}
32
33
.SuspenseRectsBoundaryChildren {
@@ -28,15 +41,18 @@
41
.SuspenseRectsRect {
42
box-shadow: var(--elevation-4);
43
pointer-events: all;
44
+ cursor: pointer;
45
+ border-radius: 0.125rem;
46
+ background-color: color-mix(in srgb, var(--color-background) 50%, var(--color-suspense) 25%);
47
+ backdrop-filter: grayscale(100%);
48
+ transition: background-color 0.2s ease-in;
49
+ outline-color: var(--color-suspense);
50
outline-style: solid;
51
outline-width: 1px;
33
- border-radius: 0.125rem;
34
- cursor: pointer;
52
}
53
54
.SuspenseRectsScaledRect {
55
position: absolute;
39
- outline-color: var(--color-background-selected);
56
}
57
58
.SuspenseRectsScaledRect[data-visible='false'] {
@@ -44,15 +60,28 @@
60
outline-width: 0;
61
}
62
47
-.SuspenseRectsScaledRect[data-suspended='true'] {
48
- opacity: 0.3;
63
+.SuspenseRectsBoundary[data-suspended='true'] {
64
+ opacity: 0.33;
65
}
66
67
/* highlight this boundary */
52
-.SuspenseRectsBoundary:hover:not(:has(.SuspenseRectsBoundary:hover)) > .SuspenseRectsRect, .SuspenseRectsBoundary[data-highlighted='true'] > .SuspenseRectsRect {
53
- background-color: var(--color-background-hover);
68
+.SuspenseRectsBoundary:hover:not(:has(.SuspenseRectsBoundary:hover)) > .SuspenseRectsRect {
69
+ background-color: color-mix(in srgb, var(--color-background) 50%, var(--color-suspense) 50%);
70
+ transition: background-color 0.2s ease-out;
71
+}
72
+
73
+.SuspenseRectsBoundary[data-selected='true'] {
74
+ box-shadow: var(--elevation-4);
75
+}
76
+
77
+.SuspenseRectOutline {
78
+ outline-color: var(--color-suspense);
79
+ outline-style: solid;
80
+ outline-width: 4px;
81
+ border-radius: 0.125rem;
82
+ pointer-events: none;
83
}
84
56
-.SuspenseRectsRect[data-highlighted='true'] {
57
- background-color: var(--color-selected-tree-highlight-active);
85
+.SuspenseRectsBoundary[data-selected='true'] > .SuspenseRectsRect {
86
+ box-shadow: none;
87
}
packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js
+11
-1
@@ -36,6 +36,7 @@ function ScaledRect({
36
rect,
37
visible,
38
suspended,
39
+ selected,
40
adjust,
41
...props
42
}: {
@@ -43,6 +44,7 @@ function ScaledRect({
44
rect: Rect,
45
visible: boolean,
46
suspended: boolean,
47
+ selected?: boolean,
48
adjust?: boolean,
49
...
50
}): React$Node {
@@ -58,6 +60,7 @@ function ScaledRect({
60
className={styles.SuspenseRectsScaledRect + ' ' + className}
61
data-visible={visible}
62
data-suspended={suspended}
63
+ data-selected={selected}
64
style={{
65
// Shrink one pixel so that the bottom outline will line up with the top outline of the next one.
66
width: adjust ? 'calc(' + width + ' - 1px)' : width,
@@ -152,6 +155,7 @@ function SuspenseRects({
155
rect={boundingBox}
156
className={styles.SuspenseRectsBoundary}
157
visible={visible}
158
+ selected={selected}
159
suspended={suspense.isSuspended}>
160
<ViewBox.Provider value={boundingBox}>
161
{visible &&
@@ -162,7 +166,6 @@ function SuspenseRects({
166
key={index}
167
className={styles.SuspenseRectsRect}
168
rect={rect}
165
- data-highlighted={selected}
169
adjust={true}
170
onClick={handleClick}
171
onDoubleClick={handleDoubleClick}
@@ -182,6 +185,13 @@ function SuspenseRects({
185
})}
186
</ScaledRect>
187
)}
188
+ {selected ? (
189
+ <ScaledRect
190
+ className={styles.SuspenseRectOutline}
191
+ rect={boundingBox}
192
+ adjust={true}
193
+ />
194
+ ) : null}
195
</ViewBox.Provider>
196
</ScaledRect>
197
);
packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.css
+5
-5
@@ -40,22 +40,22 @@
40
.SuspenseScrubberBead {
41
flex: 1;
42
height: 0.5rem;
43
- background: var(--color-background-selected);
43
border-radius: 0.5rem;
45
- background: var(--color-selected-tree-highlight-active);
46
- transition: all 0.3s ease-in-out;
44
+ background: color-mix(in srgb, var(--color-suspense) 10%, transparent);
45
+ transition: all 0.3s ease-in;
46
}
47
48
.SuspenseScrubberBeadSelected {
49
height: 1rem;
51
- background: var(--color-background-selected);
50
+ background: var(--color-suspense);
51
}
52
53
.SuspenseScrubberBeadTransition {
55
- background: var(--color-component-name);
54
+ background: var(--color-transition);
55
}
56
57
.SuspenseScrubberStepHighlight > .SuspenseScrubberBead,
58
.SuspenseScrubberStep:hover > .SuspenseScrubberBead {
59
height: 0.75rem;
60
+ transition: all 0.3s ease-out;
61
}