Dark mode was persistent in `AppearanceSettings` but never reached any
paint code: every call site in the shell read `colors::INK` etc. as a
`pub const u32`, so toggling `ThemeMode::Dark` mutated state nothing
sampled. Root-cause fix is to invert the contract — the design-system
exports functions that resolve through a thread-local `Mode`, and the
GPUI render impl sets that mode each frame.
What lands:
- `ely_design_system::colors::Mode` + thread-local + `set_mode` /
`mode` accessors. Every ink shade, glass surface, stroke, divider,
hairline, canvas, success / error chip now picks the warm-dark
counterpart when the active mode is `Mode::Dark`.
- `ElyShell::render` resolves `ThemeMode::System` against
`Window::appearance()` and pushes the mode before traversing the
tree, so widgets lower down read the right shade without owning a
`Mode` parameter.
- `render_wallpaper` + `panel_bg` now branch on `colors::mode()` so
the gradient base, panel tint, and overlay highlights flip to
warm-graphite when dark mode is active.
- Mechanical conversion across 687 call sites in 55 files from
`colors::FOO` constants to `colors::foo()` accessors. The
`Theme` / `ELY_THEME` const surface (unused outside the design
system) is removed; the function surface is the new contract.
The Moon icon previously navigated to ely://settings/appearance,
which is misleading for a button visually framed as a one-tap theme
control. Add cycle_theme_mode (System → Light → Dark → System) and
swap the icon between Sun and Moon to mirror the active state.
The back/forward arrows in the topbar were styled as live buttons
(cursor_pointer, hover swap) but they had no on_click — they were
clickable affordances that did nothing. Per the "no fake handlers"
rule that's worse than no button at all: it lies to the user.
Pull the cursor and hover off, drop the text color to INK_5 so they
read as the design's `disabled` state (`var(--ely-ink-5)`), and
leave them untouched until real per-tab history lands in
BrowserCore. When the navigation API arrives the call site can
swap to a live variant.
- Nav-back / nav-forward now render lucide ArrowLeft / ArrowRight to
match the design's TopBar exactly. Chevron arrows read as accordion
toggles in this design language; arrow heads read as page navigation.
- Omnibar uses uniform px(14) horizontal padding instead of asymmetric
pl(14) pr(8), so the inner chips inherit the design's symmetric breathing
room. The flex/gap layout already keeps the chips at the right edge.
- AppearanceSettings derives Default instead of carrying a manual impl
that's identical to the derived one.
- topbar::render_lock_or_search collapses the duplicated Search arms
into a single fallback so clippy stops flagging identical blocks.
- command_overlay row helpers bundle id/title/hint/keys into a small
CommandRowContent struct so render_row, render_row_with_glyph, and
render_row_inner stay under the 7-arg threshold without losing any
call-site clarity.
Brings hover/active opacity feedback to a consistent 0.92/0.82 baseline
across the omnibar content, the home search pill, and the workspace
picker tile + add button. The home search pill and the omnibar styled
URL display previously had no hover state at all, so the click target
felt invisible. The motion design framework wants every interactive
element to acknowledge hover and click; this change closes the gap
without introducing new keyframe animations (GPUI 0.2.2 has no
transition layer to drive them).
The design omnibar shows the host in ink-1 weight 500 and the path in
ink-3, switching to a plain editable address only while the user types.
The shell now compares the input value to the active tab URL: when they
match (and the URL isn't ely://new-tab) the omnibar renders the styled
host/path display; clicking it routes through focus_address_bar which
re-mounts the Input via the same listener that already drove typing.
Internal: render_lock_or_search picks Search vs Globe based on the URL
scheme so insecure pages get a different leading glyph in display mode.
Move topbar rendering into chrome::topbar so it does not bloat render.rs.
The omnibar now ends in two glass chips (filters + favorite star) inline
with the address input, matching the design's pill layout. The toolbar
right cluster is rebuilt as copy-url, downloads, theme, and menu icons:
- Copy chip writes the active tab's URL to the system clipboard.
- Downloads opens ely://downloads (existing internal route).
- Theme chip routes to ely://settings/appearance for now (a Light/Dark
toggle still has to live in appearance settings before it can be a
one-shot).
- Menu opens ely://settings.
The pin/new-tab buttons are removed; they were not part of the design's
topbar. Pinning still works through the favorite chip + sidebar
selection state, and Cmd+T continues to create tabs.