diff --git a/crates/ui-egui/src/panes/nav.rs b/crates/ui-egui/src/panes/nav.rs
--- a/crates/ui-egui/src/panes/nav.rs
+++ b/crates/ui-egui/src/panes/nav.rs
@@ -29,14 +29,13 @@ pub(super) fn nav(app: &mut WordApp, ui: &mut Ui) {
         }
     }
     ui.add_space(4.0);
-    ui.horizontal_wrapped(|ui| {
-        for (k, l) in [("headings", "Headings"), ("pages", "Pages"), ("results", "Results")] {
-            if ui.selectable_label(app.ui.nav_tab == k, tl!(l)).clicked() {
-                app.ui.nav_tab = k.into();
-            }
-        }
-    });
-    ui.separator();
+    let keys = ["headings", "pages", "results"];
+    let names = ["Headings", "Pages", "Results"].map(|n| tl!(n).to_string());
+    let current = keys.iter().position(|k| *k == app.ui.nav_tab).unwrap_or(0);
+    if let Some(i) = crate::widgets::dialog_tabs_within(ui, &names, current, ui.available_width()) {
+        app.ui.nav_tab = keys[i].into();
+    }
+    ui.add_space(4.0);
     egui::ScrollArea::vertical().show(ui, |ui| match app.ui.nav_tab.as_str() {
         "pages" => {
             let l = app.session.layout();
diff --git a/crates/ui-egui/src/panes/tests.rs b/crates/ui-egui/src/panes/tests.rs
--- a/crates/ui-egui/src/panes/tests.rs
+++ b/crates/ui-egui/src/panes/tests.rs
@@ -64,6 +64,21 @@ fn f6_past_the_last_pane_types_into_the_document() {
     assert_eq!(h.state().session.doc.plain_text(wordcraft_doc::StoryRef::Body).trim(), "typed");
 }
 
+/// The Navigation pane's views use the shared dialog tabs and each click selects that view.
+#[test]
+#[cfg(not(target_arch = "wasm32"))]
+fn navigation_tabs_switch_the_view() {
+    use egui_kittest::kittest::Queryable;
+    let mut app = crate::WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::from_text("Open it")), crate::Services::default());
+    app.run("view.navigationPane", serde_json::json!({"value": true})).unwrap();
+    let mut h = pane_harness(app);
+    for (label, key) in [("Pages", "pages"), ("Results", "results"), ("Headings", "headings")] {
+        h.get_by_label(label).click();
+        h.run_steps(3);
+        assert_eq!(h.state().ui.nav_tab, key, "{label} tab");
+    }
+}
+
 fn pane_harness(app: crate::WordApp) -> egui_kittest::Harness<'static, crate::WordApp> {
     let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1440.0, 900.0)).build_ui_state(
         |ui, app: &mut crate::WordApp| {
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
@@ -26,7 +26,8 @@ pub use combo::{
 use combo::{Row, highlight_row, section_row, section_rows};
 pub use groups::{command_icon, command_menu, customize_menu_items, group, group_titled, row};
 pub use icon_buttons::{
-    dialog_tabs, icon_button, icon_menu_item, icon_only_button, icon_text_button, icon_text_button_width, icon_text_menu_button, primary_button,
+    dialog_tabs, dialog_tabs_within, icon_button, icon_menu_item, icon_only_button, icon_text_button, icon_text_button_width, icon_text_menu_button,
+    primary_button,
 };
 #[cfg(test)]
 use tooltip::show_hint;
diff --git a/crates/ui-egui/src/widgets/icon_buttons.rs b/crates/ui-egui/src/widgets/icon_buttons.rs
--- a/crates/ui-egui/src/widgets/icon_buttons.rs
+++ b/crates/ui-egui/src/widgets/icon_buttons.rs
@@ -108,10 +108,19 @@ pub fn icon_menu_item(ui: &mut Ui, icon: &str, text: &str, shortcut: &str, min_w
 /// A strip of dialog tabs. The active tab joins the page below it.
 /// Returns the index of the tab the user clicked.
 pub fn dialog_tabs(ui: &mut Ui, labels: &[String], selected: usize) -> Option<usize> {
+    dialog_tabs_within(ui, labels, selected, f32::INFINITY)
+}
+
+/// `dialog_tabs` for a narrow pane: the tab padding shrinks, to 4 pt
+/// per side at least, so the strip fits `max_width`.
+pub fn dialog_tabs_within(ui: &mut Ui, labels: &[String], selected: usize, max_width: f32) -> Option<usize> {
     const H: f32 = 26.0;
     let t = Tokens::get(ui.ctx());
     let page = ui.visuals().window_fill;
-    let need: f32 = labels.iter().map(|l| ui.painter().layout_no_wrap(l.clone(), regular(12.5), t.text).size().x + 27.0).sum::<f32>() + 1.0;
+    let texts: Vec<f32> = labels.iter().map(|l| ui.painter().layout_no_wrap(l.clone(), regular(12.5), t.text).size().x).collect();
+    let spare = (max_width - 1.0 - texts.iter().sum::<f32>()) / texts.len().max(1) as f32;
+    let pad = spare.clamp(8.0, 28.0);
+    let need: f32 = texts.iter().map(|w| w + pad - 1.0).sum::<f32>() + 1.0;
     ui.set_min_width(need);
     let origin = ui.cursor().min;
     let width = ui.available_width();
@@ -123,7 +132,7 @@ pub fn dialog_tabs(ui: &mut Ui, labels: &[String], selected: usize) -> Option<us
     for (i, label) in labels.iter().enumerate() {
         let on = i == selected;
         let galley = ui.painter().layout_no_wrap(label.clone(), regular(12.5), t.text);
-        let w = galley.size().x + 28.0;
+        let w = galley.size().x + pad;
         let r = Rect::from_min_max(pos2(x, origin.y + 2.0), pos2(x + w, base_y));
         let resp = ui.interact(r, ui.scope_id().with(("dialog_tab", i)), Sense::click());
         resp.widget_info(|| egui::WidgetInfo::selected(egui::Role::Button, true, on, label));
