update theme
This commit is contained in:
@@ -162,7 +162,11 @@ impl Brand {
|
|||||||
/// The shipped palette for an appearance, rotated onto this brand.
|
/// The shipped palette for an appearance, rotated onto this brand.
|
||||||
pub fn branded(&self, appearance: ThemeMode) -> ThemeColors {
|
pub fn branded(&self, appearance: ThemeMode) -> ThemeColors {
|
||||||
let mut colors = ThemeColors::for_appearance(appearance);
|
let mut colors = ThemeColors::for_appearance(appearance);
|
||||||
self.apply(appearance, &mut colors);
|
// apply() re-asserts this brand's ink, so the default brand — whose
|
||||||
|
// ink is the shipped one — must skip it to stay a strict no-op.
|
||||||
|
if !self.is_default() {
|
||||||
|
self.apply(appearance, &mut colors);
|
||||||
|
}
|
||||||
colors
|
colors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -398,10 +398,12 @@ impl ThemeColors {
|
|||||||
self.danger_disabled = wash_for(appearance, 0.06);
|
self.danger_disabled = wash_for(appearance, 0.06);
|
||||||
|
|
||||||
// Warning
|
// Warning
|
||||||
self.warning_foreground = self.text;
|
// warning_muted is a translucent amber wash, so its ink is the amber
|
||||||
|
// token itself, and its state washes deepen the plate.
|
||||||
|
self.warning_foreground = self.warning;
|
||||||
self.warning_background = self.warning_muted;
|
self.warning_background = self.warning_muted;
|
||||||
self.warning_hover = self.element_hover;
|
self.warning_hover = plate_hover_for(appearance, self.warning_muted);
|
||||||
self.warning_active = self.element_active;
|
self.warning_active = plate_active_for(appearance, self.warning_muted);
|
||||||
self.warning_selected = self.element_active;
|
self.warning_selected = self.element_active;
|
||||||
self.warning_disabled = wash_for(appearance, 0.06);
|
self.warning_disabled = wash_for(appearance, 0.06);
|
||||||
|
|
||||||
@@ -417,7 +419,7 @@ impl ThemeColors {
|
|||||||
self.tab_background = self.surface;
|
self.tab_background = self.surface;
|
||||||
self.tab_foreground = self.text_muted;
|
self.tab_foreground = self.text_muted;
|
||||||
self.tab_hover_background = self.element_hover;
|
self.tab_hover_background = self.element_hover;
|
||||||
self.tab_active_background = self.bg;
|
self.tab_active_background = self.element_active;
|
||||||
self.tab_active_foreground = self.text;
|
self.tab_active_foreground = self.text;
|
||||||
|
|
||||||
// Scrollbar
|
// Scrollbar
|
||||||
|
|||||||
@@ -163,7 +163,9 @@ pub fn sync_base(cx: &mut App) {
|
|||||||
tokens.accent = colors.element_hover;
|
tokens.accent = colors.element_hover;
|
||||||
tokens.accent_foreground = colors.text;
|
tokens.accent_foreground = colors.text;
|
||||||
tokens.destructive = colors.danger_strong;
|
tokens.destructive = colors.danger_strong;
|
||||||
tokens.destructive_foreground = colors.on_accent;
|
// Bezel's destructive pairing: white ink on the red plate in both
|
||||||
|
// appearances.
|
||||||
|
tokens.destructive_foreground = gpui::white();
|
||||||
tokens.border = colors.border;
|
tokens.border = colors.border;
|
||||||
tokens.input = colors.border;
|
tokens.input = colors.border;
|
||||||
tokens.ring = colors.ring;
|
tokens.ring = colors.ring;
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ pub(crate) fn dark() -> ThemeColors {
|
|||||||
danger_muted: color::oklch(0.808, 0.114, 19.571), // red-300
|
danger_muted: color::oklch(0.808, 0.114, 19.571), // red-300
|
||||||
danger_strong: color::oklch(0.58, 0.16, 25.0),
|
danger_strong: color::oklch(0.58, 0.16, 25.0),
|
||||||
warning: color::oklch(0.828, 0.189, 84.429), // amber-400
|
warning: color::oklch(0.828, 0.189, 84.429), // amber-400
|
||||||
warning_muted: color::oklch(0.924, 0.12, 95.746), // amber-200
|
|
||||||
success: color::oklch(0.765, 0.177, 163.223), // emerald-400
|
success: color::oklch(0.765, 0.177, 163.223), // emerald-400
|
||||||
success_muted: color::oklch(0.845, 0.143, 164.978), // emerald-300
|
success_muted: color::oklch(0.845, 0.143, 164.978), // emerald-300
|
||||||
busy: color::oklch(0.718, 0.202, 349.761), // pink-400
|
busy: color::oklch(0.718, 0.202, 349.761), // pink-400
|
||||||
@@ -83,6 +82,9 @@ pub(crate) fn dark() -> ThemeColors {
|
|||||||
|
|
||||||
..ThemeColors::legacy_default()
|
..ThemeColors::legacy_default()
|
||||||
};
|
};
|
||||||
|
// Same warning-chip rule as light: a translucent amber wash under the
|
||||||
|
// `warning` token, not an opaque plate.
|
||||||
|
colors.warning_muted = colors.warning.alpha(0.16);
|
||||||
colors.sync_legacy(ThemeMode::Dark);
|
colors.sync_legacy(ThemeMode::Dark);
|
||||||
colors
|
colors
|
||||||
}
|
}
|
||||||
@@ -119,7 +121,9 @@ pub(crate) fn light() -> ThemeColors {
|
|||||||
|
|
||||||
// ---- text ----
|
// ---- text ----
|
||||||
text: TextInk::color(ThemeMode::Light, Ink::APPKIT.light.text),
|
text: TextInk::color(ThemeMode::Light, Ink::APPKIT.light.text),
|
||||||
text_muted: TextInk::color(ThemeMode::Light, Ink::APPKIT.light.muted),
|
// One rung darker than AppKit's secondary label: sidebar and list
|
||||||
|
// labels sit on the chrome grey and need ~5:1, not the system ~4:1.
|
||||||
|
text_muted: color::neutral(0.45),
|
||||||
text_faint: TextInk::color(ThemeMode::Light, Ink::APPKIT.light.faint),
|
text_faint: TextInk::color(ThemeMode::Light, Ink::APPKIT.light.faint),
|
||||||
text_dim: color::neutral(0.50),
|
text_dim: color::neutral(0.50),
|
||||||
|
|
||||||
@@ -137,7 +141,6 @@ pub(crate) fn light() -> ThemeColors {
|
|||||||
danger_muted: color::oklch(0.505, 0.213, 27.518), // red-700
|
danger_muted: color::oklch(0.505, 0.213, 27.518), // red-700
|
||||||
danger_strong: color::oklch(0.51, 0.20, 25.0),
|
danger_strong: color::oklch(0.51, 0.20, 25.0),
|
||||||
warning: color::oklch(0.555, 0.163, 48.998), // amber-700 — carries 12px text
|
warning: color::oklch(0.555, 0.163, 48.998), // amber-700 — carries 12px text
|
||||||
warning_muted: color::oklch(0.473, 0.137, 46.201), // amber-800
|
|
||||||
success: color::oklch(0.596, 0.145, 163.225), // emerald-600
|
success: color::oklch(0.596, 0.145, 163.225), // emerald-600
|
||||||
success_muted: color::oklch(0.508, 0.118, 165.612), // emerald-700
|
success_muted: color::oklch(0.508, 0.118, 165.612), // emerald-700
|
||||||
busy: color::oklch(0.592, 0.249, 0.584), // pink-600
|
busy: color::oklch(0.592, 0.249, 0.584), // pink-600
|
||||||
@@ -147,7 +150,9 @@ pub(crate) fn light() -> ThemeColors {
|
|||||||
// "brighten the plate, don't wash it out" rule, read the other way.
|
// "brighten the plate, don't wash it out" rule, read the other way.
|
||||||
surface_raised_hover: color::neutral(0.900),
|
surface_raised_hover: color::neutral(0.900),
|
||||||
band: band_for(ThemeMode::Light),
|
band: band_for(ThemeMode::Light),
|
||||||
input_bg: color::grey(0xff),
|
// Inputs read as one rung below the chrome grey — the old scale's
|
||||||
|
// step-3 input plate. Pure white vanished against the content plane.
|
||||||
|
input_bg: color::neutral(0.950),
|
||||||
selection: rgb(0xb3d7ff).into(),
|
selection: rgb(0xb3d7ff).into(),
|
||||||
cursor: color::neutral(0.205),
|
cursor: color::neutral(0.205),
|
||||||
caret: color::neutral(0.511),
|
caret: color::neutral(0.511),
|
||||||
@@ -169,6 +174,10 @@ pub(crate) fn light() -> ThemeColors {
|
|||||||
|
|
||||||
..ThemeColors::legacy_default()
|
..ThemeColors::legacy_default()
|
||||||
};
|
};
|
||||||
|
// The warning chip is a translucent amber wash, not an opaque plate: the
|
||||||
|
// 700-800 amber reads as muddy brown, and a wash lets the label ride on
|
||||||
|
// the `warning` token in both appearances.
|
||||||
|
colors.warning_muted = colors.warning.alpha(0.10);
|
||||||
colors.sync_legacy(ThemeMode::Light);
|
colors.sync_legacy(ThemeMode::Light);
|
||||||
colors
|
colors
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,21 @@ fn brand_tint_rotates_greys_and_keeps_semantics() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn warning_wash_carries_its_label() {
|
||||||
|
// warning_muted is a translucent amber wash, so the label pairing is
|
||||||
|
// measured against the plate flattened onto the content plane.
|
||||||
|
for appearance in [ThemeMode::Dark, ThemeMode::Light] {
|
||||||
|
let colors = ThemeColors::for_appearance(appearance);
|
||||||
|
let plate = flatten(colors.warning_muted, colors.bg);
|
||||||
|
assert!(
|
||||||
|
ratio(colors.warning, plate) >= 3.0,
|
||||||
|
"{appearance:?}: warning on warning_muted = {}",
|
||||||
|
ratio(colors.warning, plate)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn legacy_aliases_track_tokens() {
|
fn legacy_aliases_track_tokens() {
|
||||||
for appearance in [ThemeMode::Dark, ThemeMode::Light] {
|
for appearance in [ThemeMode::Dark, ThemeMode::Light] {
|
||||||
@@ -118,7 +133,7 @@ fn legacy_aliases_track_tokens() {
|
|||||||
assert_eq!(colors.text_placeholder, colors.text_faint);
|
assert_eq!(colors.text_placeholder, colors.text_faint);
|
||||||
assert_eq!(colors.element_background, colors.solid);
|
assert_eq!(colors.element_background, colors.solid);
|
||||||
assert_eq!(colors.secondary_background, colors.surface_raised);
|
assert_eq!(colors.secondary_background, colors.surface_raised);
|
||||||
assert_eq!(colors.tab_active_background, colors.bg);
|
assert_eq!(colors.tab_active_background, colors.element_active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -451,8 +451,12 @@ impl ButtonVariant {
|
|||||||
match self {
|
match self {
|
||||||
ButtonVariant::Primary => cx.theme().on_solid,
|
ButtonVariant::Primary => cx.theme().on_solid,
|
||||||
ButtonVariant::Secondary => cx.theme().text,
|
ButtonVariant::Secondary => cx.theme().text,
|
||||||
ButtonVariant::Danger => cx.theme().on_accent,
|
// Bezel's destructive pairing: the red plate carries white ink in
|
||||||
ButtonVariant::Warning => cx.theme().text,
|
// both appearances, not the appearance-flipping on_accent.
|
||||||
|
ButtonVariant::Danger => gpui::white(),
|
||||||
|
// warning_muted is a translucent amber wash, so the label is the
|
||||||
|
// amber token itself — dark amber in light, bright in dark.
|
||||||
|
ButtonVariant::Warning => cx.theme().warning,
|
||||||
ButtonVariant::Transparent => cx.theme().text_faint,
|
ButtonVariant::Transparent => cx.theme().text_faint,
|
||||||
ButtonVariant::Ghost { alt } => {
|
ButtonVariant::Ghost { alt } => {
|
||||||
if *alt {
|
if *alt {
|
||||||
@@ -470,7 +474,7 @@ impl ButtonVariant {
|
|||||||
ButtonVariant::Primary => plate_hover(cx.theme().solid),
|
ButtonVariant::Primary => plate_hover(cx.theme().solid),
|
||||||
ButtonVariant::Secondary => cx.theme().surface_raised_hover,
|
ButtonVariant::Secondary => cx.theme().surface_raised_hover,
|
||||||
ButtonVariant::Danger => plate_hover(cx.theme().danger_strong),
|
ButtonVariant::Danger => plate_hover(cx.theme().danger_strong),
|
||||||
ButtonVariant::Warning => cx.theme().element_hover,
|
ButtonVariant::Warning => plate_hover(cx.theme().warning_muted),
|
||||||
ButtonVariant::Ghost { .. } => cx.theme().element_hover,
|
ButtonVariant::Ghost { .. } => cx.theme().element_hover,
|
||||||
ButtonVariant::Transparent => gpui::transparent_black(),
|
ButtonVariant::Transparent => gpui::transparent_black(),
|
||||||
ButtonVariant::Custom(colors) => colors.hover,
|
ButtonVariant::Custom(colors) => colors.hover,
|
||||||
@@ -491,7 +495,7 @@ impl ButtonVariant {
|
|||||||
ButtonVariant::Primary => plate_active(cx.theme().solid),
|
ButtonVariant::Primary => plate_active(cx.theme().solid),
|
||||||
ButtonVariant::Secondary => plate_active(cx.theme().surface_raised),
|
ButtonVariant::Secondary => plate_active(cx.theme().surface_raised),
|
||||||
ButtonVariant::Danger => plate_active(cx.theme().danger_strong),
|
ButtonVariant::Danger => plate_active(cx.theme().danger_strong),
|
||||||
ButtonVariant::Warning => cx.theme().element_active,
|
ButtonVariant::Warning => plate_active(cx.theme().warning_muted),
|
||||||
ButtonVariant::Ghost { .. } => cx.theme().element_active,
|
ButtonVariant::Ghost { .. } => cx.theme().element_active,
|
||||||
ButtonVariant::Transparent => gpui::transparent_black(),
|
ButtonVariant::Transparent => gpui::transparent_black(),
|
||||||
ButtonVariant::Custom(colors) => colors.active,
|
ButtonVariant::Custom(colors) => colors.active,
|
||||||
@@ -511,7 +515,7 @@ impl ButtonVariant {
|
|||||||
ButtonVariant::Primary => cx.theme().element_active,
|
ButtonVariant::Primary => cx.theme().element_active,
|
||||||
ButtonVariant::Secondary => cx.theme().element_active,
|
ButtonVariant::Secondary => cx.theme().element_active,
|
||||||
ButtonVariant::Danger => plate_active(cx.theme().danger_strong),
|
ButtonVariant::Danger => plate_active(cx.theme().danger_strong),
|
||||||
ButtonVariant::Warning => cx.theme().element_active,
|
ButtonVariant::Warning => plate_active(cx.theme().warning_muted),
|
||||||
ButtonVariant::Ghost { .. } => cx.theme().element_active,
|
ButtonVariant::Ghost { .. } => cx.theme().element_active,
|
||||||
ButtonVariant::Transparent => gpui::transparent_black(),
|
ButtonVariant::Transparent => gpui::transparent_black(),
|
||||||
ButtonVariant::Custom(colors) => colors.active,
|
ButtonVariant::Custom(colors) => colors.active,
|
||||||
|
|||||||
@@ -467,7 +467,7 @@ impl TabGroupSkin {
|
|||||||
let bar = TabBar::new("tab-bar")
|
let bar = TabBar::new("tab-bar")
|
||||||
.track_scroll(&self.scroll_handle)
|
.track_scroll(&self.scroll_handle)
|
||||||
.h(TABBAR_HEIGHT)
|
.h(TABBAR_HEIGHT)
|
||||||
.bg(cx.theme().surface)
|
.bg(cx.theme().bg)
|
||||||
.when(needs_traffic_light_padding, |this| {
|
.when(needs_traffic_light_padding, |this| {
|
||||||
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
||||||
})
|
})
|
||||||
@@ -569,7 +569,7 @@ impl TabGroupSkin {
|
|||||||
h_flex()
|
h_flex()
|
||||||
.h(TABBAR_HEIGHT)
|
.h(TABBAR_HEIGHT)
|
||||||
.w_full()
|
.w_full()
|
||||||
.bg(cx.theme().surface)
|
.bg(cx.theme().bg)
|
||||||
.child(bar.flex_1())
|
.child(bar.flex_1())
|
||||||
.child(window_controls())
|
.child(window_controls())
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
@@ -718,7 +718,7 @@ impl TabGroupRenderer for TabGroupSkin {
|
|||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.size_full()
|
.size_full()
|
||||||
.bg(cx.theme().surface)
|
.bg(cx.theme().bg)
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.child(panel.cached(StyleRefinement::default().v_flex().size_full())),
|
.child(panel.cached(StyleRefinement::default().v_flex().size_full())),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ impl NotificationKind {
|
|||||||
Self::Info => (IconName::Info, cx.theme().text_muted),
|
Self::Info => (IconName::Info, cx.theme().text_muted),
|
||||||
Self::Success => (IconName::CheckCircle, cx.theme().success),
|
Self::Success => (IconName::CheckCircle, cx.theme().success),
|
||||||
Self::Warning => (IconName::Warning, cx.theme().warning),
|
Self::Warning => (IconName::Warning, cx.theme().warning),
|
||||||
Self::Error => (IconName::CloseCircle, cx.theme().on_accent),
|
Self::Error => (IconName::CloseCircle, gpui::white()),
|
||||||
};
|
};
|
||||||
Icon::new(name).with_size(Size::Medium).text_color(color)
|
Icon::new(name).with_size(Size::Medium).text_color(color)
|
||||||
}
|
}
|
||||||
@@ -284,7 +284,9 @@ impl Render for Notification {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let text_color = match self.kind {
|
let text_color = match self.kind {
|
||||||
Some(NotificationKind::Error) => cx.theme().on_accent,
|
// White on the danger plate in both appearances, per bezel's
|
||||||
|
// destructive pairing.
|
||||||
|
Some(NotificationKind::Error) => gpui::white(),
|
||||||
_ => cx.theme().text,
|
_ => cx.theme().text,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -191,7 +191,9 @@ impl RenderOnce for Tab {
|
|||||||
.text_sm()
|
.text_sm()
|
||||||
.rounded(px(theme::button_radius()))
|
.rounded(px(theme::button_radius()))
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.when(selected && !disabled, |this| this.bg(cx.theme().bg))
|
.when(selected && !disabled, |this| {
|
||||||
|
this.bg(cx.theme().element_active)
|
||||||
|
})
|
||||||
.when(!selected && !disabled, |this| {
|
.when(!selected && !disabled, |this| {
|
||||||
this.hover(|this| {
|
this.hover(|this| {
|
||||||
this.bg(cx.theme().element_hover)
|
this.bg(cx.theme().element_hover)
|
||||||
|
|||||||
@@ -70,7 +70,9 @@ impl ControlIcon {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn hover_fg(&self, cx: &App) -> Hsla {
|
fn hover_fg(&self, cx: &App) -> Hsla {
|
||||||
if self.is_close() {
|
if self.is_close() {
|
||||||
cx.theme().on_accent
|
// White on the danger plate in both appearances, per bezel's
|
||||||
|
// destructive pairing.
|
||||||
|
gpui::white()
|
||||||
} else {
|
} else {
|
||||||
cx.theme().text
|
cx.theme().text
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ is only consumed via `sync_base` or not at all.
|
|||||||
| Old | New | Notes | Uses |
|
| Old | New | Notes | Uses |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `text` | `text` | | 16 |
|
| `text` | `text` | | 16 |
|
||||||
| `text_muted` | `text_muted` | | 53 |
|
| `text_muted` | `text_muted` | light value darkened to `neutral(0.45)` (~5.3:1) — AppKit's secondary label was too faint on the chrome grey | 53 |
|
||||||
| `text_placeholder` | `text_faint` | | 25 |
|
| `text_placeholder` | `text_faint` | | 25 |
|
||||||
| `text_accent` | `accent` | | 1 |
|
| `text_accent` | `accent` | | 1 |
|
||||||
| `text_danger` | `danger` | soft/inline error copy → `danger_muted` | 10 |
|
| `text_danger` | `danger` | soft/inline error copy → `danger_muted` | 10 |
|
||||||
@@ -146,8 +146,8 @@ is only consumed via `sync_base` or not at all.
|
|||||||
| `danger_background` | `danger_strong` | destructive plate; tinted chips → `danger_muted` | 4 |
|
| `danger_background` | `danger_strong` | destructive plate; tinted chips → `danger_muted` | 4 |
|
||||||
| `danger_hover` / `danger_active` / `danger_selected` | plate-hover helper | one helper that brightens (dark) / darkens (light) a plate | 1/1/1 |
|
| `danger_hover` / `danger_active` / `danger_selected` | plate-hover helper | one helper that brightens (dark) / darkens (light) a plate | 1/1/1 |
|
||||||
| `danger_disabled` | `wash()` at disabled alpha | | 1 |
|
| `danger_disabled` | `wash()` at disabled alpha | | 1 |
|
||||||
| `warning_foreground` | `text` | icons/rules may use `warning` | 5 |
|
| `warning_foreground` | `warning` | `warning_muted` is a translucent amber wash; the label is the amber token itself | 5 |
|
||||||
| `warning_background` | `warning_muted` | | 5 |
|
| `warning_background` | `warning_muted` | translucent amber wash (`warning` at 10% light / 16% dark) | 5 |
|
||||||
| `warning_hover` / `warning_active` / `warning_selected` | `element_hover` / `element_active` | on the chip | 1/1/1 |
|
| `warning_hover` / `warning_active` / `warning_selected` | `element_hover` / `element_active` | on the chip | 1/1/1 |
|
||||||
| `warning_disabled` | `wash()` at disabled alpha | | 1 |
|
| `warning_disabled` | `wash()` at disabled alpha | | 1 |
|
||||||
| `ghost_element_background` | transparent | drop | 6 |
|
| `ghost_element_background` | transparent | drop | 6 |
|
||||||
@@ -163,7 +163,7 @@ is only consumed via `sync_base` or not at all.
|
|||||||
| `tab_background` | `surface` | | 2 |
|
| `tab_background` | `surface` | | 2 |
|
||||||
| `tab_foreground` | `text_muted` | | 1 |
|
| `tab_foreground` | `text_muted` | | 1 |
|
||||||
| `tab_hover_background` | `element_hover` | | 2 |
|
| `tab_hover_background` | `element_hover` | | 2 |
|
||||||
| `tab_active_background` | `bg` | active tab merges with the content plane, hairline separates | 1 |
|
| `tab_active_background` | `element_active` | one rung past `element_hover` (darker in light, brighter in dark); hairline separates from the plane | 1 |
|
||||||
| `tab_active_foreground` | `text` | | 2 |
|
| `tab_active_foreground` | `text` | | 2 |
|
||||||
| `scrollbar_*` (5 tokens) | gpui-base scrollbar tokens | via `sync_base` only | 0 |
|
| `scrollbar_*` (5 tokens) | gpui-base scrollbar tokens | via `sync_base` only | 0 |
|
||||||
| `drop_target_background` | `drop_target` | | 1 |
|
| `drop_target_background` | `drop_target` | | 1 |
|
||||||
@@ -363,6 +363,41 @@ passes inside this crate); `cargo check -p ui`, `cargo check --workspace`,
|
|||||||
`cargo test -p theme` (11 tests) and `cargo clippy -p ui -p theme` are green.
|
`cargo test -p theme` (11 tests) and `cargo clippy -p ui -p theme` are green.
|
||||||
Public APIs, element structure, sizes and heights unchanged (D2, D7).
|
Public APIs, element structure, sizes and heights unchanged (D2, D7).
|
||||||
|
|
||||||
|
Light-mode review fixes (after running the app):
|
||||||
|
|
||||||
|
- **Warning chip is a wash, not a plate.** Bezel's `warning_muted` is a
|
||||||
|
strong amber (800 light / 200 dark) meant to carry white/black ink; on a
|
||||||
|
button it read as muddy brown. Both appearances now derive
|
||||||
|
`warning_muted` as a translucent wash of the `warning` token (10% light,
|
||||||
|
16% dark) and pair it with `warning`-toned labels; button hover/active
|
||||||
|
deepen the plate with the plate helpers, and `sync_legacy` maps
|
||||||
|
`warning_foreground` → `warning`. A contrast test measures the label
|
||||||
|
against the wash flattened onto the content plane.
|
||||||
|
- **Inputs lost their grey.** Light `input_bg` was pure white; it is now
|
||||||
|
`neutral(0.950)`, one rung below the chrome grey like the old scale's
|
||||||
|
step-3 input plate. Dark stays bezel's white-at-3%.
|
||||||
|
- **Dock planes move to `bg`.** The dock tab bar, title strip and panel
|
||||||
|
container read `bg` (white light / `#060606` dark — bezel's main panel),
|
||||||
|
matching the old near-white `panel_background`. Sidebar keeps `surface`
|
||||||
|
(the chrome grey).
|
||||||
|
- **Active tab is a wash, not white.** The dock tab's selected plane is
|
||||||
|
`element_active` — one rung past the `element_hover` hover (darker in
|
||||||
|
light, brighter in dark). The segmented tab's active segment stays a
|
||||||
|
white raised plate with a shadow. `sync_legacy.tab_active_background`
|
||||||
|
follows.
|
||||||
|
- **Sidebar text contrast.** Light `text_muted` moves from AppKit's
|
||||||
|
secondary label (~4:1, ~3.7:1 on the grey sidebar) to `neutral(0.45)`
|
||||||
|
(~5.3:1 on white), which also narrows the dark/light muted gap. Faint
|
||||||
|
stays AppKit-faint by design (placeholders).
|
||||||
|
- `Brand::branded` now skips `apply` for the default brand so the test's
|
||||||
|
no-op assertion holds now that a shipped token (`text_muted`) diverges
|
||||||
|
from the AppKit ink `apply` would re-assert.
|
||||||
|
- **Danger plates carry white ink in both appearances** (bezel's
|
||||||
|
destructive pairing — their `buttons.rs` hardcodes `gpui::white()` over
|
||||||
|
`danger_strong`): Danger button, error notification plate, title-bar
|
||||||
|
close hover, and `sync_base.destructive_foreground`. Visually a dark-mode
|
||||||
|
change only; light `on_accent` was already near-white.
|
||||||
|
|
||||||
Validation remaining: run desktop, walk every component in light/dark
|
Validation remaining: run desktop, walk every component in light/dark
|
||||||
against the Phase 0 screenshots.
|
against the Phase 0 screenshots.
|
||||||
|
|
||||||
@@ -371,6 +406,39 @@ against the Phase 0 screenshots.
|
|||||||
Mechanical per the mapping table; ~370 sites across 8 crates. Use the
|
Mechanical per the mapping table; ~370 sites across 8 crates. Use the
|
||||||
deprecated accessors as the worklist — the goal is zero deprecated hits.
|
deprecated accessors as the worklist — the goal is zero deprecated hits.
|
||||||
|
|
||||||
|
Semantic states, per bezel's own widgets (`crates/ui/src/widgets/{status,
|
||||||
|
content,buttons}.rs`) — the recipes consumer banners, pills and state marks
|
||||||
|
should follow:
|
||||||
|
|
||||||
|
- **Strips (error/warning).** Bezel ships exactly two — "two tones, not a
|
||||||
|
level enum"; there is no info strip. Recipe: background = the semantic
|
||||||
|
token at 6% opacity, hairline ring = the same token at 20%, label and
|
||||||
|
leading triangle icon = the family's ink step at 90%, all at
|
||||||
|
`surface_radius()`. In coop tokens: error → bg `danger` at 6%, ring
|
||||||
|
`danger` at 20%, ink `danger_muted` (we kept bezel's ink step for danger);
|
||||||
|
warning → bg `warning_muted` (our wash token), ring `warning` at 20%, ink
|
||||||
|
`warning`.
|
||||||
|
- **Success pill.** Bezel's `badge_active`: background = `success` at 12%,
|
||||||
|
ink = `success_muted` at 90%, `rounded_full`, no border.
|
||||||
|
- **State dots.** No enum — the caller passes the tone
|
||||||
|
(`success`/`busy`/`warning`/`danger`, `text_faint` for "nothing yet") to a
|
||||||
|
6px bead.
|
||||||
|
- **Destructive controls.** Bezel's plate is `danger_strong` with **white
|
||||||
|
ink in both appearances** (`gpui::white()` in their `buttons.rs`), hover
|
||||||
|
dims the whole control to 90% opacity, and role is independent of emphasis
|
||||||
|
(a quiet destructive control is a ghost with `danger_strong`-toned text).
|
||||||
|
coop now carries the white-ink rule in button, notification error plate,
|
||||||
|
title-bar close hover, and `sync_base.destructive_foreground`.
|
||||||
|
- **Info.** No semantic tone — neutral copy on `text_muted`, or the quiet
|
||||||
|
hairline badge.
|
||||||
|
- Known divergence (kept deliberately): bezel keeps `warning_muted` as the
|
||||||
|
family's ink token and bakes wash alphas at call sites; coop bakes the
|
||||||
|
warning wash into the `warning_muted` token (10% light / 16% dark) because
|
||||||
|
the legacy `warning_background` alias and the button plate need a token,
|
||||||
|
and pairs it with `warning`-toned ink. The danger family keeps bezel's
|
||||||
|
exact semantics (`danger` wash source, `danger_muted` ink, `danger_strong`
|
||||||
|
plate).
|
||||||
|
|
||||||
- [ ] `crates/chat_ui` (largest: ~200 sites) — chips and rows on
|
- [ ] `crates/chat_ui` (largest: ~200 sites) — chips and rows on
|
||||||
`elevated_surface_background` review to `surface_raised` vs
|
`elevated_surface_background` review to `surface_raised` vs
|
||||||
`surface_card`; reactions/toggles to `element_*`; report banners to
|
`surface_card`; reactions/toggles to `element_*`; report banners to
|
||||||
|
|||||||
Reference in New Issue
Block a user