refactor(app): unify status colours on one token family
Three systems were reporting the same states: a full and a muted status tier in the theme, plus raw palette steps in StatusBadge and getStatusDotColor. A PR icon was muted in the sidebar and full-strength in the PR panel, and the Resolved badge used a theme-independent green so it painted a dark block in light mode. The muted tier is deleted and every status surface now reads the one token per signal. Dots keep a band of their own. A dot is a few points of solid colour with no shape to read and no label attached, and the running one pulses, so at the status band's lightness it sat below the check icons and host badge on its own row — backwards, since the dot is the row's state. Chroma caps at 90% of gamut rather than 100% because at the edge green reaches C=0.215 while blue manages 0.116 and the set stops reading as one family; dark lightness caps at 0.72 because red desaturates to pink above it. Host badges needed a foreground variant of the identity table. That table is tuned for a white letter on the colour as a fill; used as text it cannot clear 4.5:1 on both themes at once, since light needs luminance <= 0.183 and dark needs >= 0.212. The new set is generated on the status family's exact lightness and chroma fraction, so a host name and a CI check in the same meta row carry equal weight instead of the brighter one winning the row. Also: PR avatars had their own eight-hex palette and hash duplicating identity-colors; workspace rows under a project group now indent to the same rail as status-grouped rows; group headers are muted; and the pulsing dot is faster and fades further.
This commit is contained in:
@@ -29,24 +29,15 @@ import { resolveSidebarWorkspacePrimaryLabel } from "@/components/sidebar/sideba
|
||||
const SCRIM_WIDTH = 48;
|
||||
const SCRIM_SOLID_OFFSET = "55%";
|
||||
|
||||
/**
|
||||
* How far a workspace row sits inside the status header above it. Only status grouping
|
||||
* indents: a project row carries an icon and its workspaces line up under it already, but a
|
||||
* status header is just a label, so its rows need the offset to read as belonging to it.
|
||||
*
|
||||
* It is row padding rather than a margin on the list, because the row's hover and selected
|
||||
* backgrounds have to keep spanning the group's full width. Indenting the container instead
|
||||
* pulls the highlight in with the content and the row stops lining up with its header.
|
||||
*/
|
||||
const WORKSPACE_ROW_INDENT = 12;
|
||||
|
||||
const DEFAULT_STATUS_DOT_SIZE = 7;
|
||||
const EMPHASIZED_STATUS_DOT_SIZE = 9;
|
||||
const DEFAULT_STATUS_DOT_OFFSET = 0;
|
||||
const EMPHASIZED_STATUS_DOT_OFFSET = -1;
|
||||
|
||||
const foregroundMutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted });
|
||||
const amberColorMapping = (theme: Theme) => ({ color: theme.colors.palette.amber[500] });
|
||||
const needsInputColorMapping = (theme: Theme) => ({
|
||||
color: getStatusDotColor({ theme, bucket: "needs_input" }) ?? undefined,
|
||||
});
|
||||
|
||||
const ThemedCircleAlert = withUnistyles(CircleAlert);
|
||||
const ThemedMonitor = withUnistyles(Monitor);
|
||||
@@ -249,7 +240,7 @@ function WorkspaceStatusIndicator({
|
||||
if (bucket === "needs_input") {
|
||||
return (
|
||||
<View style={styles.workspaceStatusDot} testID="workspace-status-indicator-needs_input">
|
||||
<ThemedCircleAlert size={14} uniProps={amberColorMapping} />
|
||||
<ThemedCircleAlert size={14} uniProps={needsInputColorMapping} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -342,10 +333,16 @@ function getStatusDotColorStyle(bucket: SidebarStateBucket) {
|
||||
}
|
||||
|
||||
export const sidebarWorkspaceRowStyles = StyleSheet.create((theme) => ({
|
||||
// Layered over the row's own padding rather than replacing it, so the indent stays one
|
||||
// number here instead of being baked into a row style.
|
||||
// How far a workspace row sits inside the group header above it — a project row or a
|
||||
// status group header. Both groupings share this one indent, so every grouped workspace row
|
||||
// in the sidebar sits on the same rail regardless of how the list is grouped. Pinned rows
|
||||
// are not grouped and stay flush.
|
||||
//
|
||||
// It is row padding rather than a margin on the list, because the row's hover and selected
|
||||
// backgrounds have to keep spanning the group's full width. Indenting the container instead
|
||||
// pulls the highlight in with the content and the row stops lining up with its header.
|
||||
rowIndented: {
|
||||
paddingLeft: theme.spacing[2] + WORKSPACE_ROW_INDENT,
|
||||
paddingLeft: theme.spacing[2] + theme.spacing[2],
|
||||
},
|
||||
rowRight: {
|
||||
flexDirection: "row",
|
||||
@@ -575,7 +572,7 @@ const styles = StyleSheet.create((theme) => ({
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: theme.borderRadius.full,
|
||||
backgroundColor: theme.colors.palette.green[500],
|
||||
backgroundColor: getStatusDotColor({ theme, bucket: "attention" }) ?? undefined,
|
||||
},
|
||||
standaloneRunningDot: {
|
||||
width: 8,
|
||||
@@ -608,11 +605,11 @@ const styles = StyleSheet.create((theme) => ({
|
||||
opacity: 1,
|
||||
},
|
||||
statusDotNeedsInput: {
|
||||
backgroundColor: theme.colors.palette.amber[500],
|
||||
backgroundColor: getStatusDotColor({ theme, bucket: "needs_input" }) ?? undefined,
|
||||
borderColor: theme.colors.surface0,
|
||||
},
|
||||
statusDotFailed: {
|
||||
backgroundColor: theme.colors.palette.red[500],
|
||||
backgroundColor: getStatusDotColor({ theme, bucket: "failed" }) ?? undefined,
|
||||
borderColor: theme.colors.surface0,
|
||||
},
|
||||
statusDotRunning: {
|
||||
@@ -620,7 +617,7 @@ const styles = StyleSheet.create((theme) => ({
|
||||
borderColor: theme.colors.surface0,
|
||||
},
|
||||
statusDotAttention: {
|
||||
backgroundColor: theme.colors.palette.green[500],
|
||||
backgroundColor: getStatusDotColor({ theme, bucket: "attention" }) ?? undefined,
|
||||
borderColor: theme.colors.surface0,
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user