diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs
--- a/crates/ui-egui/src/theme.rs
+++ b/crates/ui-egui/src/theme.rs
@@ -1,6 +1,7 @@
 //! Design tokens. Colours were chosen by eye to give a dark, studio-style look; they are ours.
 
 use egui::{Color32, FontFamily, FontId};
+use soundcraft_model::EditMode;
 use std::sync::atomic::{AtomicBool, Ordering};
 
 /// Whether the Light palette is active. Dark is the default (the incumbent is dark-only).
@@ -165,6 +166,13 @@ pub struct Tokens {
     pub group_border: Color32,
     /// Edit-mode cell, not selected.
     pub mode_off: Color32,
+    /// Edit-mode cell, selected: one colour per mode so the active mode reads at a glance
+    /// (see [`Tokens::edit_mode_on`]). The label on them is `mode_on_text`.
+    pub mode_shuffle: Color32,
+    pub mode_spot: Color32,
+    pub mode_slip: Color32,
+    pub mode_grid: Color32,
+    pub mode_grid_relative: Color32,
     /// Counter-panel toggle (Grid, Count Off), off.
     pub counter_off: Color32,
     /// Outline of the counter panels.
@@ -272,6 +280,11 @@ impl Tokens {
         menu_bar: Color32::from_rgb(22, 22, 23),
         group_border: Color32::from_rgb(8, 8, 8),
         mode_off: Color32::from_rgb(20, 36, 22),
+        mode_shuffle: Color32::from_rgb(246, 112, 102),
+        mode_spot: Color32::from_rgb(236, 148, 38),
+        mode_slip: Color32::from_rgb(92, 196, 108),
+        mode_grid: Color32::from_rgb(86, 162, 238),
+        mode_grid_relative: Color32::from_rgb(176, 134, 238),
         counter_off: Color32::from_rgb(30, 50, 32),
         counter_border: Color32::from_rgb(50, 50, 50),
         counter_rule: Color32::from_rgb(40, 40, 40),
@@ -357,6 +370,11 @@ impl Tokens {
         menu_bar: Color32::from_rgb(204, 206, 210),
         group_border: Color32::from_rgb(118, 121, 127),
         mode_off: Color32::from_rgb(202, 212, 204),
+        mode_shuffle: Color32::from_rgb(188, 48, 42),
+        mode_spot: Color32::from_rgb(176, 94, 4),
+        mode_slip: Color32::from_rgb(44, 128, 62),
+        mode_grid: Color32::from_rgb(62, 116, 180),
+        mode_grid_relative: Color32::from_rgb(124, 82, 178),
         counter_off: Color32::from_rgb(192, 212, 198),
         counter_border: Color32::from_rgb(118, 121, 127),
         counter_rule: Color32::from_rgb(188, 191, 196),
@@ -386,6 +404,19 @@ impl Tokens {
     pub fn current() -> Tokens {
         if is_light() { Self::LIGHT } else { Self::DARK }
     }
+
+    /// Fill of the edit-mode key while `mode` is the active one.
+    // ponytail: one label colour (`mode_on_text`) serves the five fills; Light needs fills dark enough for
+    // white text, so Spot is a deep orange. A per-mode label token is the way out if a fill must go lighter.
+    pub fn edit_mode_on(&self, mode: EditMode) -> Color32 {
+        match mode {
+            EditMode::Shuffle => self.mode_shuffle,
+            EditMode::Spot => self.mode_spot,
+            EditMode::Slip => self.mode_slip,
+            EditMode::Grid => self.mode_grid,
+            EditMode::GridRelative => self.mode_grid_relative,
+        }
+    }
 }
 
 /// Whether the Light palette is the active one.
@@ -620,4 +651,36 @@ mod tests {
             }
         }
     }
+
+    #[test]
+    fn each_edit_mode_has_its_own_readable_active_colour() {
+        // WCAG relative luminance and contrast ratio.
+        fn lum(c: Color32) -> f64 {
+            let lin = |v: u8| {
+                let v = f64::from(v) / 255.0;
+                if v <= 0.04045 { v / 12.92 } else { ((v + 0.055) / 1.055).powf(2.4) }
+            };
+            0.2126 * lin(c.r()) + 0.7152 * lin(c.g()) + 0.0722 * lin(c.b())
+        }
+        fn contrast(a: Color32, b: Color32) -> f64 {
+            let (a, b) = (lum(a), lum(b));
+            (a.max(b) + 0.05) / (a.min(b) + 0.05)
+        }
+        let modes = [EditMode::Shuffle, EditMode::Spot, EditMode::Slip, EditMode::Grid, EditMode::GridRelative];
+        for (name, t) in [("dark", Tokens::DARK), ("light", Tokens::LIGHT)] {
+            for (i, a) in modes.into_iter().enumerate() {
+                let fill = t.edit_mode_on(a);
+                assert_ne!(fill, t.mode_off, "{name}: {a:?} active must differ from inactive");
+                // Small bold label: WCAG AA wants 4.5:1, at rest and at both ends of the pressed gradient.
+                let pressed = crate::widgets::PRESSED_SHADE.map(|k| crate::widgets::shade(fill, k));
+                for (state, face) in [("active", fill), ("pressed top", pressed[0]), ("pressed bottom", pressed[1])] {
+                    let ratio = contrast(face, t.mode_on_text);
+                    assert!(ratio >= 4.5, "{name}: {a:?} {state} label contrast {ratio:.2} is below 4.5");
+                }
+                for b in &modes[i + 1..] {
+                    assert_ne!(fill, t.edit_mode_on(*b), "{name}: {a:?} and {b:?} share an active colour");
+                }
+            }
+        }
+    }
 }
diff --git a/crates/ui-egui/src/toolbar.rs b/crates/ui-egui/src/toolbar.rs
--- a/crates/ui-egui/src/toolbar.rs
+++ b/crates/ui-egui/src/toolbar.rs
@@ -60,10 +60,13 @@ fn edit_mode_tooltip(m: EditMode, locked: bool, cmd: &str, fkey: &str) -> String
 
 fn draw_mode_cell(p: &egui::Painter, cell: Rect, resp: &egui::Response, t: &Tokens, m: EditMode, state: &ModeCellState, label: &str) {
     let on = state.cur == m || (m == EditMode::Grid && state.cur == EditMode::GridRelative);
-    key(p, cell, resp, if on { t.mode_on } else { t.mode_off });
+    // `on` means `cur == m`, or Grid showing Relative Grid: the active colour is always cur's.
+    key(p, cell, resp, if on { t.edit_mode_on(state.cur) } else { t.mode_off });
     let fg = if on { t.mode_on_text } else { t.mode_off_text };
     if m == EditMode::Grid && state.snap {
-        p.rect_stroke(cell, 1.0, Stroke::new(1.5, t.mode_on), StrokeKind::Inside);
+        // The Snap to Grid outline wears the Grid colour, so it never reads as the green of Slip.
+        let grid = t.edit_mode_on(if state.cur.is_grid() { state.cur } else { EditMode::Grid });
+        p.rect_stroke(cell, 1.0, Stroke::new(1.5, grid), StrokeKind::Inside);
     }
     if m == EditMode::Shuffle && state.locked {
         icons::draw(p, Rect::from_center_size(cell.center(), vec2(15.0, 15.0)), "lock", fg);
diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs
--- a/crates/ui-egui/src/widgets.rs
+++ b/crates/ui-egui/src/widgets.rs
@@ -69,11 +69,15 @@ pub fn key_fill(resp: &Response) -> Color32 {
     if resp.hovered() { t.button_hi } else { t.button }
 }
 
+/// The [`shade`] of a pressed key's face, top and bottom of its gradient.
+pub const PRESSED_SHADE: [f32; 2] = [-0.14, 0.02];
+
 /// A key in `fill`, softly lit from above; it sinks while pressed.
 pub fn key(p: &Painter, r: Rect, resp: &Response, fill: Color32) {
     let face = r.shrink(1.0);
     if resp.is_pointer_button_down_on() {
-        vgradient(p, face, shade(fill, -0.14), shade(fill, 0.02));
+        let [top, bottom] = PRESSED_SHADE;
+        vgradient(p, face, shade(fill, top), shade(fill, bottom));
     } else {
         p.rect_filled(face, 0.0, fill);
         key_light(p, face);
