git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commitc8e0ab3e998a43c96f2c03022cec5c6488d6fcad
parent07a4d29b4c
authorLucas Galante <[email protected]>
date2026-08-25 12:59
7a-1: style.surface.plate.root.* — the canonical root-plate vocabulary

RFC Phase 7a, first cut, REVISED at implementation: style.surface.plate.*
already exists as the NESTED-plate namespace with deliberately different
values, so the canonical root spelling is role-scoped — plate.root.* (and
plate.root.menubar.*) — rather than a flat plate.* alias. Backplate = plate
in the root role.

Both config paths alias: the layout style-registry table maps plate.root.*
onto the same slots as backplate.*, and color.rs's JSON-pointer loads are
canonical-first .or_else chains, so the new spelling wins when both are
present. Canonical getters root_plate_{padding,gap,opacity,corner_radius}
and root_plate_{menubar,statusbar}_{color,text_color,blur}; the backplate_*
names stay as plain delegating wrappers — NOT deprecated yet, 15 crates +
the compositor still call them (7a-2 migrates them per-repo, then flips
deprecation). 'root' joins PROP_NODES for the config-editor helpers.
cce-ui's own callers (config tests, DemoApp, cce-relief, cce-ramp) migrate;
the legacy styling test now reads through canonical getters (legacy-config
-> canonical-getter equivalence) and a new test proves canonical parse,
precedence, and legacy fallback. 245 lib tests green; designer A/B AE=0.

 docs/rfc-core-rebuild.md | 33 +++++++++++++++---
 src/bin/cce-ramp.rs      |  8 ++---
 src/bin/cce-relief.rs    |  8 ++---
 src/color.rs             | 88 ++++++++++++++++++++++++++++++++++++++++--------
 src/config.rs            | 49 +++++++++++++++++++++++----
 src/layout.rs            | 39 ++++++++++++++++++---
 src/main.rs              |  8 ++---
 7 files changed, 191 insertions(+), 42 deletions(-)

diff --git a/docs/rfc-core-rebuild.md b/docs/rfc-core-rebuild.md
index 19ae20c..37b554b 100644
--- a/docs/rfc-core-rebuild.md
+++ b/docs/rfc-core-rebuild.md
@@ -2400,11 +2400,34 @@ Constraint respected: **each crate still builds standalone** — the new core is
     which parses the same `DropletSpec`.
 
   **Stages.**
-  - **7a — Vocabulary.** `style.surface.plate.*` becomes the canonical config namespace;
-    `backplate.*` keys stay as silent read-aliases (the existing `plate_corner_radius`
-    fallback is the pattern — extend it to every getter, then rename getters to `plate_*` with
-    deprecated `backplate_*` wrappers). User configs keep working unchanged. A/B: every client
-    AE=0.
+  - **7a — Vocabulary. REVISED at implementation, 7a-1 DONE (2026-08-25).** The original
+    text said "`style.surface.plate.*` becomes canonical" — implementation surveying found
+    `style.surface.plate.*` ALREADY EXISTS as the NESTED-plate style namespace (padding,
+    color, border_color, border_thickness, blur) carrying deliberately different values
+    from `backplate.*`; a flat alias would have merged root styling into pane styling.
+    The canonical namespace is therefore role-scoped: **`style.surface.plate.root.*`**
+    (with `plate.root.menubar.*` for the bar sub-style) — truer to the phase's thesis
+    anyway: backplate = plate in the root role. Getter names follow as `root_plate_*`.
+    - **7a-1 DONE.** Both config paths aliased: the layout style-registry table maps
+      `plate.root.*` rows onto the same slots as `backplate.*` (slot names keep the
+      historical prefix — invisible), and color.rs's JSON-pointer loads are canonical-first
+      chains (`/style/surface/plate/root/…` `.or_else(` `/backplate/…)`), so the new
+      spelling WINS when both are present; in the registry table both spellings write one
+      slot and document order decides (single-spelling configs — all real ones — are exact).
+      Canonical getters (`root_plate_{padding,gap,opacity,corner_radius}`,
+      `root_plate_{menubar,statusbar}_{color,text_color,blur}`) with the old `backplate_*`
+      names as plain delegating wrappers — NOT `#[deprecated]` yet: 15 crates + the
+      compositor still call them (16-crate caller census in the 7a-1 commit). "root" joined
+      `PROP_NODES` for the config-editor path helpers. cce-ui's own callers (config.rs
+      tests, DemoApp, cce-relief, cce-ramp) migrated. Tests: the legacy styling test now
+      reads through canonical getters (legacy-config → canonical-getter equivalence), plus
+      a canonical-spelling test proving parse, precedence over legacy, and legacy-only
+      fallback. 245 lib tests green; designer A/B AE=0.
+    - **7a-2 — pending.** Migrate the other crates' callers per-repo (cce-compositor's
+      `server/config.rs` is the largest at 9 sites — it reads the SHARED silhouette values,
+      so that migration must not change which slot it reads), then flip the `backplate_*`
+      wrappers to `#[deprecated]`. The `Application::is_movable_backplate_at` trait-method
+      NAME is 7b vocabulary (behavioral role naming), not 7a's.
   - **7b — `PlateSpec` + window-corner math toolkit-side.** Introduce the spec, port
     `pane_plate_radii` in, and give the engine a root-plate paint path fed by a spec instead of
     each app's hand-rolled quads (DemoApp first, then the clients). The designer's per-pane
diff --git a/src/bin/cce-ramp.rs b/src/bin/cce-ramp.rs
index c5ae0dd..f240536 100644
--- a/src/bin/cce-ramp.rs
+++ b/src/bin/cce-ramp.rs
@@ -214,7 +214,7 @@ impl Application for RampPopup {
             // itself is inset by OVERFLOW_MARGIN so key pegs can render past
             // the window frame into the transparent surface rim. Key mode
             // reserves a Save/Cancel band under the curve.
-            let pad = OVERFLOW_MARGIN + cce_ui::layout::backplate_padding() / 2.0;
+            let pad = OVERFLOW_MARGIN + cce_ui::layout::root_plate_padding() / 2.0;
             let band = if self.target_key.is_some() { 56.0 } else { 0.0 };
             self.ramp.set_rect(
                 pad,
@@ -250,9 +250,9 @@ impl Application for RampPopup {
         let mut plate = cce_ui::color::page_low_color();
         if plate[3] > 0.001 {
             // Half the DE opacity: this popup reads better mostly-glass.
-            plate[3] = cce_ui::color::active_backplate_opacity() * 0.5;
+            plate[3] = cce_ui::color::root_plate_opacity() * 0.5;
         }
-        let radius = cce_ui::colors::backplate_corner_radius();
+        let radius = cce_ui::colors::root_plate_corner_radius();
         let bevel = cce_ui::layout::bevel_width();
         let m = OVERFLOW_MARGIN;
         pc.plate(
@@ -267,7 +267,7 @@ impl Application for RampPopup {
             cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.save_button, &mut pc);
             cce_ui::scene::painter::paint_root_into(&self.ui_context, &self.cancel_button, &mut pc);
             if !self.status.is_empty() {
-                let pad = OVERFLOW_MARGIN + cce_ui::layout::backplate_padding() / 2.0;
+                let pad = OVERFLOW_MARGIN + cce_ui::layout::root_plate_padding() / 2.0;
                 pc.text_with(
                     self.status.clone(),
                     pad + 2.0 * (96.0 + 12.0),
diff --git a/src/bin/cce-relief.rs b/src/bin/cce-relief.rs
index 767594b..d206a60 100644
--- a/src/bin/cce-relief.rs
+++ b/src/bin/cce-relief.rs
@@ -91,7 +91,7 @@ const SHADE_STRIP_H: f32 = 14.0;
 /// (The compositor may still restore a saved size over this; see the Utility
 /// window-mode proposal. Until then the request is only a request.)
 fn content_height(width: f32) -> f32 {
-    let pad = cce_ui::layout::backplate_padding();
+    let pad = cce_ui::layout::root_plate_padding();
     let w = (width - 2.0 * pad).max(0.0);
     let gap = 14.0;
     let strip = {
@@ -1218,7 +1218,7 @@ impl Application for BevelPopup {
                     .and_then(|o| o.as_f64())
                     .map(|f| (f as f32).clamp(0.0, 1.0))
             })
-            .unwrap_or_else(|| cce_ui::color::active_backplate_opacity());
+            .unwrap_or_else(|| cce_ui::color::root_plate_opacity());
         Self {
             profile_dropdown: Dropdown::new(
                 Shape::ALL.iter().map(|s| s.label().to_string()).collect(),
@@ -1334,7 +1334,7 @@ impl Application for BevelPopup {
             // Manual column layout: the profile selector, ONE cutaway, the
             // selected profile's knobs, then the global rows. The unselected
             // profile's knobs park off-screen.
-            let pad = cce_ui::layout::backplate_padding();
+            let pad = cce_ui::layout::root_plate_padding();
             let x = pad;
             let w = (self.width as f32 - 2.0 * pad).max(0.0);
             let gap = 14.0;
@@ -1435,7 +1435,7 @@ impl Application for BevelPopup {
         // profile — the popup is its own material sample.
         let mut plate = cce_ui::color::page_low_color();
         plate[3] = self.plate_opacity;
-        let radius = cce_ui::colors::backplate_corner_radius();
+        let radius = cce_ui::colors::root_plate_corner_radius();
         let bevel = cce_ui::layout::bevel_width();
         pc.plate(
             Rect { x: 0.0, y: 0.0, width: w, height: h },
diff --git a/src/color.rs b/src/color.rs
index 345862a..cb9da34 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -282,26 +282,41 @@ fn parse_and_set_colors(content: &str) {
         }
     }
 
-    if let Some(radius) = val.pointer("/style/surface/backplate/corner_radius").and_then(|v| v.as_f64()) {
+    // Phase 7a: `/style/surface/plate/root/...` is the canonical spelling of
+    // the root-plate style; the `backplate` pointers are its read-alias, and
+    // the chains are canonical-first so the new spelling wins when both are
+    // present.
+    if let Some(radius) = val.pointer("/style/surface/plate/root/corner_radius")
+        .or_else(|| val.pointer("/style/surface/backplate/corner_radius"))
+        .and_then(|v| v.as_f64())
+    {
         if let Ok(mut lock) = BACKPLATE_CORNER_RADIUS.write() {
             *lock = radius as f32;
         }
      }
 
-    if let Some(c) = get_color("/style/surface/backplate/color") {
+    if let Some(c) = get_color("/style/surface/plate/root/color")
+        .or_else(|| get_color("/style/surface/backplate/color"))
+    {
         if let Ok(mut lock) = PAGE_LOW_COLOR.write() { *lock = c; }
         if let Ok(mut lock) = BACKPLATE_OPACITY.write() { *lock = Some(c[3]); }
     }
 
-    if let Some(c) = get_color("/style/surface/backplate/menubar/color") {
+    if let Some(c) = get_color("/style/surface/plate/root/menubar/color")
+        .or_else(|| get_color("/style/surface/backplate/menubar/color"))
+    {
         if let Ok(mut lock) = BACKPLATE_MENUBAR_COLOR.write() { *lock = c; }
     }
-    if let Some(c) = get_color("/style/surface/backplate/menubar/text_color") {
+    if let Some(c) = get_color("/style/surface/plate/root/menubar/text_color")
+        .or_else(|| get_color("/style/surface/backplate/menubar/text_color"))
+    {
         if let Ok(mut lock) = BACKPLATE_MENUBAR_TEXT_COLOR.write() { *lock = c; }
     }
-    if let Some(blur) = val.pointer("/style/surface/backplate/menubar/blur").and_then(|v| v.as_bool()) {
+    let menubar_blur_ptr = val.pointer("/style/surface/plate/root/menubar/blur")
+        .or_else(|| val.pointer("/style/surface/backplate/menubar/blur"));
+    if let Some(blur) = menubar_blur_ptr.and_then(|v| v.as_bool()) {
         if let Ok(mut lock) = BACKPLATE_MENUBAR_BLUR.write() { *lock = blur; }
-    } else if let Some(blur_val) = val.pointer("/style/surface/backplate/menubar/blur").and_then(|v| v.as_f64()) {
+    } else if let Some(blur_val) = menubar_blur_ptr.and_then(|v| v.as_f64()) {
         if let Ok(mut lock) = BACKPLATE_MENUBAR_BLUR.write() { *lock = blur_val > 0.001; }
     }
 
@@ -1209,11 +1224,20 @@ pub fn set_popover_bg_color(color: [f32; 4]) {
     }
 }
 
-pub fn backplate_corner_radius() -> f32 {
+/// Corner radius of the root plate (`style.surface.plate.root.corner_radius`;
+/// legacy `backplate.corner_radius` reads as an alias). The window silhouette
+/// value — the compositor clips windows from the SHARED copy of this.
+pub fn root_plate_corner_radius() -> f32 {
     load_colors_once();
     *BACKPLATE_CORNER_RADIUS.read().unwrap()
 }
 
+/// Legacy alias for [`root_plate_corner_radius`] (RFC Phase 7a; callers
+/// migrate in 7a-2, after which this gains `#[deprecated]`).
+pub fn backplate_corner_radius() -> f32 {
+    root_plate_corner_radius()
+}
+
 pub fn ramp_background_color() -> [f32; 4] {
     load_colors_once();
     *RAMP_BACKGROUND_COLOR.read().unwrap()
@@ -1535,10 +1559,16 @@ pub fn active_window_mode() -> String {
     "floating".to_string()
 }
 
-pub fn active_backplate_opacity() -> f32 {
+/// Opacity of the root plate's fill (the alpha of the root-plate color).
+pub fn root_plate_opacity() -> f32 {
     page_low_color()[3]
 }
 
+/// Legacy alias for [`root_plate_opacity`] (RFC Phase 7a).
+pub fn active_backplate_opacity() -> f32 {
+    root_plate_opacity()
+}
+
 pub fn tree_background_color() -> [f32; 4] { *TREE_BACKGROUND_COLOR.read().unwrap() }
 pub fn tree_border_color() -> [f32; 4] { *TREE_BORDER_COLOR.read().unwrap() }
 pub fn tree_border_hover_color() -> [f32; 4] { *TREE_BORDER_HOVER_COLOR.read().unwrap() }
@@ -1619,50 +1649,80 @@ pub fn set_list_close_search_key(k: String) {
     }
 }
 
-pub fn backplate_menubar_color() -> [f32; 4] {
+pub fn root_plate_menubar_color() -> [f32; 4] {
     load_colors_once();
     *BACKPLATE_MENUBAR_COLOR.read().unwrap()
 }
+
+/// Legacy alias for [`root_plate_menubar_color`] (RFC Phase 7a).
+pub fn backplate_menubar_color() -> [f32; 4] {
+    root_plate_menubar_color()
+}
 pub fn set_backplate_menubar_color(c: [f32; 4]) {
     if let Ok(mut lock) = BACKPLATE_MENUBAR_COLOR.write() { *lock = c; }
 }
 
-pub fn backplate_menubar_text_color() -> [f32; 4] {
+pub fn root_plate_menubar_text_color() -> [f32; 4] {
     load_colors_once();
     *BACKPLATE_MENUBAR_TEXT_COLOR.read().unwrap()
 }
+
+/// Legacy alias for [`root_plate_menubar_text_color`] (RFC Phase 7a).
+pub fn backplate_menubar_text_color() -> [f32; 4] {
+    root_plate_menubar_text_color()
+}
 pub fn set_backplate_menubar_text_color(c: [f32; 4]) {
     if let Ok(mut lock) = BACKPLATE_MENUBAR_TEXT_COLOR.write() { *lock = c; }
 }
 
-pub fn backplate_menubar_blur() -> bool {
+pub fn root_plate_menubar_blur() -> bool {
     load_colors_once();
     *BACKPLATE_MENUBAR_BLUR.read().unwrap()
 }
+
+/// Legacy alias for [`root_plate_menubar_blur`] (RFC Phase 7a).
+pub fn backplate_menubar_blur() -> bool {
+    root_plate_menubar_blur()
+}
 pub fn set_backplate_menubar_blur(b: bool) {
     if let Ok(mut lock) = BACKPLATE_MENUBAR_BLUR.write() { *lock = b; }
 }
 
-pub fn backplate_statusbar_color() -> [f32; 4] {
+pub fn root_plate_statusbar_color() -> [f32; 4] {
     load_colors_once();
     *BACKPLATE_STATUSBAR_COLOR.read().unwrap()
 }
+
+/// Legacy alias for [`root_plate_statusbar_color`] (RFC Phase 7a).
+pub fn backplate_statusbar_color() -> [f32; 4] {
+    root_plate_statusbar_color()
+}
 pub fn set_backplate_statusbar_color(c: [f32; 4]) {
     if let Ok(mut lock) = BACKPLATE_STATUSBAR_COLOR.write() { *lock = c; }
 }
 
-pub fn backplate_statusbar_text_color() -> [f32; 4] {
+pub fn root_plate_statusbar_text_color() -> [f32; 4] {
     load_colors_once();
     *BACKPLATE_STATUSBAR_TEXT_COLOR.read().unwrap()
 }
+
+/// Legacy alias for [`root_plate_statusbar_text_color`] (RFC Phase 7a).
+pub fn backplate_statusbar_text_color() -> [f32; 4] {
+    root_plate_statusbar_text_color()
+}
 pub fn set_backplate_statusbar_text_color(c: [f32; 4]) {
     if let Ok(mut lock) = BACKPLATE_STATUSBAR_TEXT_COLOR.write() { *lock = c; }
 }
 
-pub fn backplate_statusbar_blur() -> bool {
+pub fn root_plate_statusbar_blur() -> bool {
     load_colors_once();
     *BACKPLATE_STATUSBAR_BLUR.read().unwrap()
 }
+
+/// Legacy alias for [`root_plate_statusbar_blur`] (RFC Phase 7a).
+pub fn backplate_statusbar_blur() -> bool {
+    root_plate_statusbar_blur()
+}
 pub fn set_backplate_statusbar_blur(b: bool) {
     if let Ok(mut lock) = BACKPLATE_STATUSBAR_BLUR.write() { *lock = b; }
 }
diff --git a/src/config.rs b/src/config.rs
index 9b1e914..96875f3 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -307,7 +307,7 @@ pub fn parse_config_path(key: &str, default_section: &str) -> (String, String, O
 const PROP_NODES: &[&str] = &[
     "gestures", "key_bindings", "pointer_bind", "gesture_bind",
     "button", "button_strip", "dropdown", "toggle", "spinbox", "slider", "font_selector",
-    "status", "overlay", "backplate", "desktop", "list", "section", "textbox", "multiline", "editor", "tree",
+    "status", "overlay", "backplate", "root", "desktop", "list", "section", "textbox", "multiline", "editor", "tree",
     "menubar", "statusbar", "node", "relief"
 ];
 
@@ -961,23 +961,60 @@ mod tests {
         // Parse into json and set colors
         crate::color::reload_colors(content);
 
-        // Verify values are parsed correctly
-        assert_eq!(crate::color::backplate_menubar_blur(), true);
+        // Verify values are parsed correctly — read through the CANONICAL
+        // getters: a legacy-spelling config must feed them (Phase 7a alias).
+        assert_eq!(crate::color::root_plate_menubar_blur(), true);
         
         let dd_color = crate::color::dropdown_background_color();
         assert!((dd_color[0] - crate::color::srgb_to_linear(8.0 / 255.0)).abs() < 0.0001);
         
         let placeholder_color = crate::color::textbox_placeholder_text_color();
         assert_eq!(placeholder_color, [0x60, 0x60, 0x6a]);
-        assert_eq!(crate::color::backplate_statusbar_blur(), false);
+        assert_eq!(crate::color::root_plate_statusbar_blur(), false);
 
         // Colors are in sRGB converted to linear, let's verify text colors
-        let menubar_txt = crate::color::backplate_menubar_text_color();
+        let menubar_txt = crate::color::root_plate_menubar_text_color();
         assert!(menubar_txt[0] > 0.0);
-        let statusbar_txt = crate::color::backplate_statusbar_text_color();
+        let statusbar_txt = crate::color::root_plate_statusbar_text_color();
         assert!(statusbar_txt[0] > 0.0);
     }
 
+    /// Phase 7a: `style.surface.plate.root.*` is the canonical root-plate
+    /// spelling — it feeds the same values as `backplate.*`, and wins when
+    /// both spellings are present (canonical-first pointer chains).
+    #[test]
+    fn test_plate_root_canonical_spelling() {
+        let _ = crate::color::root_plate_corner_radius();
+
+        let content = r##"
+            style {
+                surface {
+                    plate {
+                        root corner_radius=(i64)17 {
+                            menubar blur=(bool)true color=(rgba)"#1a1d26d0"
+                        }
+                    }
+                    backplate corner_radius=(i64)9
+                }
+            }
+        "##;
+        crate::color::reload_colors(content);
+        assert_eq!(crate::color::root_plate_corner_radius(), 17.0, "canonical wins over legacy");
+        assert_eq!(crate::color::backplate_corner_radius(), 17.0, "legacy getter follows");
+        assert_eq!(crate::color::root_plate_menubar_blur(), true);
+
+        // Legacy-only spelling still feeds the canonical getter.
+        let legacy = r##"
+            style {
+                surface {
+                    backplate corner_radius=(i64)9
+                }
+            }
+        "##;
+        crate::color::reload_colors(legacy);
+        assert_eq!(crate::color::root_plate_corner_radius(), 9.0);
+    }
+
     #[test]
     fn test_vec2i_lossless_roundtrip() {
         let content = "style {\n    surface {\n        cloud {\n            position_default (vec2i)100 200\n        }\n    }\n}\n";
diff --git a/src/layout.rs b/src/layout.rs
index 55e73c0..9cd649a 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -162,14 +162,30 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "style.surface.desktop.grid_cell_width" => "grid_cell_width",
                 "style.surface.desktop.grid_cell_height" => "grid_cell_height",
                 "style.surface.plate.padding" => "plate_padding",
+                // Phase 7a: `style.surface.plate.root.*` is the CANONICAL
+                // spelling of the root-plate style; `backplate.*` is its
+                // silent read-alias (same registry slots; the slot names
+                // keep the historical prefix). Both spellings write the one
+                // slot, so with both present the LAST in document order
+                // wins here — the color.rs pointer chains are canonical-
+                // first; single-spelling configs (all real ones) are exact.
+                "style.surface.plate.root.padding" => "backplate_padding",
                 "style.surface.backplate.padding" => "backplate_padding",
+                "style.surface.plate.root.gap" => "backplate_gap",
                 "style.surface.backplate.gap" => "backplate_gap",
+                "style.surface.plate.root.color" => "backplate_color",
                 "style.surface.backplate.color" => "backplate_color",
+                "style.surface.plate.root.blur" => "backplate_blur",
                 "style.surface.backplate.blur" => "backplate_blur",
+                "style.surface.plate.root.corner_radius" => "backplate_corner_radius",
                 "style.surface.backplate.corner_radius" => "backplate_corner_radius",
+                "style.surface.plate.root.menubar.color" => "backplate_menubar_color",
                 "style.surface.backplate.menubar.color" => "backplate_menubar_color",
+                "style.surface.plate.root.menubar.text_color" => "backplate_menubar_text_color",
                 "style.surface.backplate.menubar.text_color" => "backplate_menubar_text_color",
+                "style.surface.plate.root.menubar.blur" => "backplate_menubar_blur",
                 "style.surface.backplate.menubar.blur" => "backplate_menubar_blur",
+                "style.surface.plate.root.menubar.font" => "menubar_font",
                 "style.surface.backplate.menubar.font" => "menubar_font",
                 "style.surface.statusbar.color" => "backplate_statusbar_color",
                 "style.surface.statusbar.text_color" => "backplate_statusbar_text_color",
@@ -1755,21 +1771,34 @@ pub fn bevel_profile_generation() -> u64 {
 }
 
 /// Padding between the window plate's edge and the objects sitting on it, in
-/// logical px (`style.surface.backplate.padding` in config.kdl). DE-wide so
+/// logical px (`style.surface.plate.root.padding` in config.kdl; the legacy
+/// `style.surface.backplate.padding` spelling reads as an alias). DE-wide so
 /// every app's content sits the same distance off the plate rim.
-pub fn backplate_padding() -> f32 {
+pub fn root_plate_padding() -> f32 {
     lazy_init_style_registry();
     get_style_registry().read().unwrap().get_float("backplate_padding").unwrap_or(16.0)
 }
 
+/// Legacy alias for [`root_plate_padding`] (RFC Phase 7a; callers migrate in
+/// 7a-2, after which this gains `#[deprecated]`).
+pub fn backplate_padding() -> f32 {
+    root_plate_padding()
+}
+
 /// Gap between sibling objects on the window plate, in logical px
-/// (`style.surface.backplate.gap` in config.kdl) — pane splits, control rows.
-/// The companion to [`backplate_padding`]: rim distance vs object spacing.
-pub fn backplate_gap() -> f32 {
+/// (`style.surface.plate.root.gap`; `backplate.gap` is the legacy alias) —
+/// pane splits, control rows. The companion to [`root_plate_padding`]:
+/// rim distance vs object spacing.
+pub fn root_plate_gap() -> f32 {
     lazy_init_style_registry();
     get_style_registry().read().unwrap().get_float("backplate_gap").unwrap_or(12.0)
 }
 
+/// Legacy alias for [`root_plate_gap`] (RFC Phase 7a).
+pub fn backplate_gap() -> f32 {
+    root_plate_gap()
+}
+
 /// Roll-off width for the wall where a bar (menubar / status bar / the demo's
 /// header band) steps down into the window plate. Wider than the plate's own
 /// perimeter roll on purpose: the carve depth saturates at `bevel_width` in the
diff --git a/src/main.rs b/src/main.rs
index 949fdc2..2d97f77 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -261,8 +261,8 @@ impl Application for DemoApp {
             // intrinsic sizes; `grow` distributes leftover space; the solved rects are
             // assigned straight onto the widgets.
             // DE-wide plate spacing: rim padding and object gap from config.
-            let plate_pad = cce_ui::layout::backplate_padding();
-            let plate_gap = cce_ui::layout::backplate_gap();
+            let plate_pad = cce_ui::layout::root_plate_padding();
+            let plate_gap = cce_ui::layout::root_plate_gap();
             let mut arena: Arena<LayoutBox> = Arena::new();
             let root = arena.insert(LayoutBox::container(
                 Style::column().padding(plate_pad).gap(plate_gap).cross_align(CrossAlign::Stretch),
@@ -361,9 +361,9 @@ impl Application for DemoApp {
         // `bevel_width` so the surface reads as a physical plate rather than a flat fill.
         let mut plate = cce_ui::color::page_low_color();
         if plate[3] > 0.001 {
-            plate[3] = cce_ui::color::active_backplate_opacity();
+            plate[3] = cce_ui::color::root_plate_opacity();
         }
-        let radius = cce_ui::colors::backplate_corner_radius();
+        let radius = cce_ui::colors::root_plate_corner_radius();
         let frame = Rect { x: 0.0, y: 0.0, width: w, height: h };
         let bevel = cce_ui::layout::bevel_width();
         pc.plate(frame, (radius, radius, radius, radius), plate, bevel);