From 7c2a5bf8c851276c7fb5e1d6fb12abe27678e890 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sat, 15 Aug 2026 11:19:50 +0200 Subject: [PATCH] Darken the desktop base palette. --- README.md | 2 +- crates/bds-ui/src/components/inputs.rs | 40 ++++++++++++++------ crates/bds-ui/src/platform/menu.rs | 14 +++---- crates/bds-ui/src/views/activity_bar.rs | 7 ++-- crates/bds-ui/src/views/chat_surfaces.rs | 5 +-- crates/bds-ui/src/views/dashboard.rs | 9 ++--- crates/bds-ui/src/views/media_editor.rs | 4 +- crates/bds-ui/src/views/modal.rs | 42 +++++++++------------ crates/bds-ui/src/views/panel.rs | 24 ++++++------ crates/bds-ui/src/views/project_selector.rs | 15 ++++---- crates/bds-ui/src/views/sidebar.rs | 20 +++++----- crates/bds-ui/src/views/site_validation.rs | 2 +- crates/bds-ui/src/views/status_bar.rs | 15 ++++---- crates/bds-ui/src/views/style_view.rs | 6 +-- crates/bds-ui/src/views/tab_bar.rs | 10 ++--- crates/bds-ui/src/views/tags_view.rs | 6 +-- crates/bds-ui/src/views/welcome.rs | 3 +- crates/bds-ui/src/views/workspace.rs | 8 ++-- docs/UI_STYLE_GUIDE.md | 6 +++ 19 files changed, 127 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index 6af96a7..e1ccce4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ The project is under active development. Core blogging workflows are broadly ava ## Available Features -- Native Iced desktop workspace with a localized native macOS menu and in-window Linux/Windows menu bar, tabs, anchored editor popovers, automatically paged post/media sidebars with locale-aware post dates, calendar months, and relative-dated entity lists, row deletion, dialogs, embedded Wry previews, a live Pico CSS theme editor, direct Preferences navigation, and per-project restart restoration of the active activity, shell visibility, and open editor tabs. Every enabled control participates in Tab/Shift-Tab focus traversal and Enter/Space activation; holding Control+Option on macOS or Control+Alt elsewhere reveals direct shortcuts plus stable two/three-letter codes for visible generic controls, filters those codes as they are typed, and lets Escape cancel while Option/Alt alone remains available for character entry. The in-window menu supports Alt mnemonics and arrow-key navigation. Its shared task UI keeps queued work off blocking workers, presents active local and remote work plus recent history in bDS2-style grouped progress cards, and supports cooperative cancellation without oversubscribing background work. +- Native Iced desktop workspace with a deep shared dark palette, a localized native macOS menu and in-window Linux/Windows menu bar, tabs, anchored editor popovers, automatically paged post/media sidebars with locale-aware post dates, calendar months, and relative-dated entity lists, row deletion, dialogs, embedded Wry previews, a live Pico CSS theme editor, direct Preferences navigation, and per-project restart restoration of the active activity, shell visibility, and open editor tabs. Every enabled control participates in Tab/Shift-Tab focus traversal and Enter/Space activation; holding Control+Option on macOS or Control+Alt elsewhere reveals direct shortcuts plus stable two/three-letter codes for visible generic controls, filters those codes as they are typed, and lets Escape cancel while Option/Alt alone remains available for character entry. The in-window menu supports Alt mnemonics and arrow-key navigation. Its shared task UI keeps queued work off blocking workers, presents active local and remote work plus recent history in bDS2-style grouped progress cards, and supports cooperative cancellation without oversubscribing background work. - Post and translation authoring with change-aware draft/published/archive lifecycle, file-backed change discard, canonical draft reopening after manual translation edits, non-disruptive automatic translation, desktop archive/unarchive actions, in-place published-frontmatter updates, metadata, tags, categories, cursor-preserving link and media insertion, live link/backlink graphs, media, and batch gallery-image import. - Media import including HEIC/HEIF decoding, q80 WebP thumbnails (plus q85 AI JPEGs), metadata translations, filters, validation, post assignment, and sequential drag-and-drop insertion into post editors. - WordPress WXR migration with saved analyses, HTML-to-Markdown and shortcode conversion, conflict/taxonomy review, recoverable 500-item execution batches, media-parent linking, progress reporting, and optional AI-assisted taxonomy mapping. diff --git a/crates/bds-ui/src/components/inputs.rs b/crates/bds-ui/src/components/inputs.rs index 0c429ed..a33b1f0 100644 --- a/crates/bds-ui/src/components/inputs.rs +++ b/crates/bds-ui/src/components/inputs.rs @@ -10,16 +10,21 @@ use super::keyboard; /// Standard form field label color. pub const LABEL_COLOR: Color = rgb8(0xB5, 0xBA, 0xC4); pub const SECTION_COLOR: Color = rgb8(0x9D, 0xA5, 0xB4); -const FIELD_BG: Color = rgb8(0x24, 0x24, 0x26); -const SURFACE_BG: Color = rgb8(0x25, 0x25, 0x26); -const BORDER_COLOR: Color = rgb8(0x3C, 0x3C, 0x3C); +pub const APP_BG: Color = rgb8(0x0D, 0x11, 0x17); +pub const ACTIVITY_BG: Color = rgb8(0x09, 0x0C, 0x10); +pub const SIDEBAR_BG: Color = rgb8(0x11, 0x16, 0x1D); +pub const SURFACE_BG: Color = rgb8(0x16, 0x1B, 0x22); +pub const ELEVATED_BG: Color = rgb8(0x1C, 0x21, 0x28); +pub const HOVER_BG: Color = rgb8(0x21, 0x26, 0x2D); +pub const FIELD_BG: Color = APP_BG; +pub const BORDER_COLOR: Color = rgb8(0x30, 0x36, 0x3D); const FOCUS_COLOR: Color = rgb8(0x00, 0x7F, 0xD4); const DANGER_BG: Color = rgb8(0xB6, 0x23, 0x24); const DANGER_HOVER: Color = rgb8(0xCF, 0x2F, 0x30); const PRIMARY_BG: Color = rgb8(0x0E, 0x63, 0x9C); const PRIMARY_HOVER: Color = rgb8(0x11, 0x77, 0xBB); -const SECONDARY_BG: Color = rgb8(0x2D, 0x2D, 0x30); -const SECONDARY_HOVER: Color = rgb8(0x3A, 0x3D, 0x41); +const SECONDARY_BG: Color = HOVER_BG; +const SECONDARY_HOVER: Color = BORDER_COLOR; const fn rgb8(red: u8, green: u8, blue: u8) -> Color { Color { @@ -30,12 +35,12 @@ const fn rgb8(red: u8, green: u8, blue: u8) -> Color { } } -/// Application-wide VS Code-like palette for native Iced controls. +/// Application-wide deep dark palette for native Iced controls. pub fn app_theme() -> Theme { Theme::custom( "bDS".to_string(), iced::theme::Palette { - background: Color::from_rgb8(0x1E, 0x1E, 0x1E), + background: APP_BG, text: Color::from_rgb8(0xCC, 0xCC, 0xCC), primary: PRIMARY_BG, success: Color::from_rgb8(0x2E, 0x7D, 0x32), @@ -48,9 +53,9 @@ pub fn app_theme() -> Theme { /// Opaque tooltip surface that remains legible over editor and sidebar content. pub fn tooltip_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.20, 0.20, 0.24))), + background: Some(Background::Color(ELEVATED_BG)), border: Border { - color: Color::from_rgb(0.35, 0.35, 0.40), + color: BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, @@ -382,7 +387,7 @@ pub fn card<'a, Message: 'a>(content: impl Into>) -> Contai .style(|_: &Theme| container::Style { background: Some(Background::Color(SURFACE_BG)), border: Border { - color: Color::from_rgb8(0x31, 0x31, 0x33), + color: BORDER_COLOR, width: 1.0, radius: 10.0.into(), }, @@ -423,7 +428,7 @@ pub fn toolbar<'a, Message: 'a>( .style(|_: &Theme| container::Style { background: Some(Background::Color(SURFACE_BG)), border: Border { - color: Color::from_rgb8(0x31, 0x31, 0x33), + color: BORDER_COLOR, width: 1.0, radius: 10.0.into(), }, @@ -467,6 +472,19 @@ fn format_timestamp(ms: i64) -> String { mod tests { use super::*; + #[test] + fn application_base_uses_the_deep_dark_palette() { + assert_eq!(app_theme().palette().background, APP_BG); + assert_eq!(APP_BG, rgb8(0x0D, 0x11, 0x17)); + assert_eq!(ACTIVITY_BG, rgb8(0x09, 0x0C, 0x10)); + assert_eq!(SIDEBAR_BG, rgb8(0x11, 0x16, 0x1D)); + assert_eq!(SURFACE_BG, rgb8(0x16, 0x1B, 0x22)); + assert_eq!(ELEVATED_BG, rgb8(0x1C, 0x21, 0x28)); + assert_eq!(HOVER_BG, rgb8(0x21, 0x26, 0x2D)); + assert_eq!(FIELD_BG, APP_BG); + assert_eq!(BORDER_COLOR, rgb8(0x30, 0x36, 0x3D)); + } + #[test] fn editor_controls_have_distinct_hover_and_focus_states() { let theme = app_theme(); diff --git a/crates/bds-ui/src/platform/menu.rs b/crates/bds-ui/src/platform/menu.rs index 3c685e7..0bc0216 100644 --- a/crates/bds-ui/src/platform/menu.rs +++ b/crates/bds-ui/src/platform/menu.rs @@ -549,9 +549,9 @@ fn shortcut_hint(action: MenuAction) -> Option<&'static str> { fn menu_bar_style(_theme: &Theme) -> iced::widget::container::Style { iced::widget::container::Style { - background: Some(Background::Color(Color::from_rgb8(0x25, 0x25, 0x26))), + background: Some(Background::Color(inputs::SURFACE_BG)), border: Border { - color: Color::from_rgb8(0x3C, 0x3C, 0x3C), + color: inputs::BORDER_COLOR, width: 0.0, radius: 0.0.into(), }, @@ -561,9 +561,9 @@ fn menu_bar_style(_theme: &Theme) -> iced::widget::container::Style { fn menu_popup_style(_theme: &Theme) -> iced::widget::container::Style { iced::widget::container::Style { - background: Some(Background::Color(Color::from_rgb8(0x25, 0x25, 0x26))), + background: Some(Background::Color(inputs::SURFACE_BG)), border: Border { - color: Color::from_rgb8(0x4A, 0x4A, 0x4D), + color: inputs::BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, @@ -583,7 +583,7 @@ fn menu_button_style( ) -> iced::widget::button::Style { let highlighted = active || matches!(status, iced::widget::button::Status::Hovered); iced::widget::button::Style { - background: highlighted.then_some(Background::Color(Color::from_rgb8(0x3A, 0x3D, 0x41))), + background: highlighted.then_some(Background::Color(inputs::HOVER_BG)), text_color: Color::from_rgb8(0xE4, 0xE4, 0xE4), border: Border { radius: 4.0.into(), @@ -601,7 +601,7 @@ fn menu_item_style( let highlighted = selected || matches!(status, iced::widget::button::Status::Hovered); let disabled = matches!(status, iced::widget::button::Status::Disabled); iced::widget::button::Style { - background: highlighted.then_some(Background::Color(Color::from_rgb8(0x3A, 0x3D, 0x41))), + background: highlighted.then_some(Background::Color(inputs::HOVER_BG)), text_color: if disabled { Color::from_rgb8(0x7D, 0x7D, 0x7D) } else { @@ -655,7 +655,7 @@ fn window_menu_popup<'a>( .height(Length::Fixed(1.0)) .width(Length::Fill) .style(|_| iced::widget::container::Style { - background: Some(Background::Color(Color::from_rgb8(0x3C, 0x3C, 0x3C))), + background: Some(Background::Color(inputs::BORDER_COLOR)), ..iced::widget::container::Style::default() }), ); diff --git a/crates/bds-ui/src/views/activity_bar.rs b/crates/bds-ui/src/views/activity_bar.rs index a4ea042..7109a7f 100644 --- a/crates/bds-ui/src/views/activity_bar.rs +++ b/crates/bds-ui/src/views/activity_bar.rs @@ -1,4 +1,4 @@ -use crate::components::keyboard; +use crate::components::{inputs, keyboard}; use iced::widget::button; use iced::widget::text::Shaping; use iced::widget::{Column, Space, column, container, svg, text, tooltip}; @@ -7,7 +7,6 @@ use iced::{Background, Border, Color, Element, Length, Theme}; use bds_core::i18n::UiLocale; use crate::app::Message; -use crate::components::inputs; use crate::i18n::t; use crate::state::navigation::SidebarView; @@ -81,7 +80,7 @@ fn active_button_style(_theme: &Theme, _status: button::Status) -> button::Style /// Inactive button: hover brightens background. fn inactive_button_style(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + button::Status::Hovered => inputs::HOVER_BG, _ => Color::TRANSPARENT, }; button::Style { @@ -99,7 +98,7 @@ fn inactive_button_style(_theme: &Theme, status: button::Status) -> button::Styl /// Activity bar container: dark background. fn bar_background_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.14, 0.14, 0.18))), + background: Some(Background::Color(inputs::ACTIVITY_BG)), ..container::Style::default() } } diff --git a/crates/bds-ui/src/views/chat_surfaces.rs b/crates/bds-ui/src/views/chat_surfaces.rs index d648de3..95db499 100644 --- a/crates/bds-ui/src/views/chat_surfaces.rs +++ b/crates/bds-ui/src/views/chat_surfaces.rs @@ -700,10 +700,7 @@ fn draw_pie(frame: &mut canvas::Frame, series: &[ChartSeries], donut: bool) { start = end; } if donut { - frame.fill( - &Path::circle(center, radius * 0.54), - Color::from_rgb8(0x25, 0x25, 0x26), - ); + frame.fill(&Path::circle(center, radius * 0.54), inputs::SURFACE_BG); } } diff --git a/crates/bds-ui/src/views/dashboard.rs b/crates/bds-ui/src/views/dashboard.rs index 07c007b..5fe24f6 100644 --- a/crates/bds-ui/src/views/dashboard.rs +++ b/crates/bds-ui/src/views/dashboard.rs @@ -184,7 +184,6 @@ pub fn view<'a>(state: &'a DashboardState, locale: UiLocale) -> Element<'a, Mess } fn stat_card<'a>(label: &str, value: String, details: Vec) -> Element<'a, Message> { - let card_bg = Color::from_rgb(0.15, 0.16, 0.20); container( column( std::iter::once(text(value).size(28).color(Color::WHITE).into()) @@ -207,8 +206,8 @@ fn stat_card<'a>(label: &str, value: String, details: Vec) -> Element<'a ) .padding(16) .width(Length::FillPortion(1)) - .style(move |_: &Theme| container::Style { - background: Some(Background::Color(card_bg)), + .style(|_: &Theme| container::Style { + background: Some(Background::Color(inputs::SURFACE_BG)), border: iced::Border { radius: 8.0.into(), ..iced::Border::default() @@ -310,7 +309,7 @@ fn category_cloud<'a>( ) .padding([6, 10]) .style(|_: &Theme| container::Style { - background: Some(Background::Color(Color::from_rgb(0.16, 0.18, 0.22))), + background: Some(Background::Color(inputs::ELEVATED_BG)), border: iced::Border { radius: 999.0.into(), ..iced::Border::default() @@ -374,7 +373,7 @@ fn recent_posts<'a>(posts: &'a [DashboardRecentPost], locale: UiLocale) -> Eleme ) .padding(12) .style(|_: &Theme| container::Style { - background: Some(Background::Color(Color::from_rgb(0.13, 0.14, 0.18))), + background: Some(Background::Color(inputs::ELEVATED_BG)), border: iced::Border { radius: 8.0.into(), ..iced::Border::default() diff --git a/crates/bds-ui/src/views/media_editor.rs b/crates/bds-ui/src/views/media_editor.rs index b8f24bd..091235c 100644 --- a/crates/bds-ui/src/views/media_editor.rs +++ b/crates/bds-ui/src/views/media_editor.rs @@ -474,9 +474,9 @@ pub fn view<'a>( .padding(8), ) .style(|_: &iced::Theme| iced::widget::container::Style { - background: Some(iced::Background::Color(Color::from_rgb(0.16, 0.18, 0.22))), + background: Some(iced::Background::Color(inputs::SURFACE_BG)), border: iced::Border { - color: Color::from_rgb(0.28, 0.28, 0.32), + color: inputs::BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, diff --git a/crates/bds-ui/src/views/modal.rs b/crates/bds-ui/src/views/modal.rs index 26e38a6..eb0eca9 100644 --- a/crates/bds-ui/src/views/modal.rs +++ b/crates/bds-ui/src/views/modal.rs @@ -155,9 +155,9 @@ fn backdrop_style(_theme: &Theme) -> container::Style { fn modal_box_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.18, 0.18, 0.22))), + background: Some(Background::Color(inputs::ELEVATED_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 10.0.into(), }, @@ -693,11 +693,9 @@ pub fn view( .padding([3, 8]) .style(|_: &Theme| { container::Style { - background: Some(Background::Color(Color::from_rgb( - 0.18, 0.20, 0.25, - ))), + background: Some(Background::Color(inputs::SURFACE_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 999.0.into(), }, @@ -713,10 +711,10 @@ pub fn view( ))) .padding([8, 12]) .style(|_: &Theme, _| button::Style { - background: Some(Background::Color(Color::from_rgb(0.22, 0.24, 0.30))), + background: Some(Background::Color(inputs::HOVER_BG)), text_color: Color::from_rgb(0.85, 0.85, 0.90), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, @@ -769,10 +767,10 @@ pub fn view( .on_press(Message::PostEditor(PostEditorMsg::PostInsertLinkCreate)) .padding([6, 12]) .style(|_: &Theme, _| button::Style { - background: Some(Background::Color(Color::from_rgb(0.18, 0.20, 0.25))), + background: Some(Background::Color(inputs::SURFACE_BG)), text_color: Color::from_rgb(0.60, 0.60, 0.65), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, @@ -886,11 +884,9 @@ pub fn view( .center_x(Length::Fill) .center_y(Length::Fill) .style(|_| container::Style { - background: Some(Background::Color(Color::from_rgb( - 0.18, 0.20, 0.25 - ))), + background: Some(Background::Color(inputs::SURFACE_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, @@ -909,9 +905,9 @@ pub fn view( ))) .padding(8) .style(|_: &Theme, _| button::Style { - background: Some(Background::Color(Color::from_rgb(0.20, 0.22, 0.27))), + background: Some(Background::Color(inputs::ELEVATED_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 8.0.into(), }, @@ -1029,11 +1025,9 @@ pub fn view( .center_x(Length::Fill) .center_y(Length::Fill) .style(|_| container::Style { - background: Some(Background::Color(Color::from_rgb( - 0.18, 0.20, 0.25 - ))), + background: Some(Background::Color(inputs::SURFACE_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, @@ -1051,9 +1045,9 @@ pub fn view( )) .padding(8) .style(|_: &Theme, _| button::Style { - background: Some(Background::Color(Color::from_rgb(0.20, 0.22, 0.27))), + background: Some(Background::Color(inputs::ELEVATED_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 8.0.into(), }, @@ -1201,9 +1195,9 @@ pub fn view( ) .padding(10) .style(|_: &Theme| container::Style { - background: Some(Background::Color(Color::from_rgb(0.18, 0.20, 0.25))), + background: Some(Background::Color(inputs::SURFACE_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, diff --git a/crates/bds-ui/src/views/panel.rs b/crates/bds-ui/src/views/panel.rs index a91561b..ac467eb 100644 --- a/crates/bds-ui/src/views/panel.rs +++ b/crates/bds-ui/src/views/panel.rs @@ -121,9 +121,9 @@ fn task_status_color(status: &TaskStatus) -> Color { fn task_entry_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb8(0x25, 0x25, 0x26))), + background: Some(Background::Color(inputs::SURFACE_BG)), border: Border { - color: Color::from_rgb8(0x3C, 0x3C, 0x3C), + color: inputs::BORDER_COLOR, width: 1.0, radius: 4.0.into(), }, @@ -133,7 +133,7 @@ fn task_entry_style(_theme: &Theme) -> container::Style { fn task_progress_style(_theme: &Theme) -> progress_bar::Style { progress_bar::Style { - background: Background::Color(Color::from_rgb8(0x3C, 0x3C, 0x3C)), + background: Background::Color(inputs::BORDER_COLOR), bar: Background::Color(Color::from_rgb8(0x0E, 0x63, 0x9C)), border: Border { radius: 3.0.into(), @@ -144,14 +144,14 @@ fn task_progress_style(_theme: &Theme) -> progress_bar::Style { fn task_group_button_style(_theme: &Theme, status: button::Status) -> button::Style { let background = match status { - button::Status::Hovered | button::Status::Pressed => Color::from_rgb8(0x2D, 0x2D, 0x30), - _ => Color::from_rgb8(0x25, 0x25, 0x26), + button::Status::Hovered | button::Status::Pressed => inputs::HOVER_BG, + _ => inputs::SURFACE_BG, }; button::Style { background: Some(Background::Color(background)), text_color: Color::from_rgb8(0xCC, 0xCC, 0xCC), border: Border { - color: Color::from_rgb8(0x3C, 0x3C, 0x3C), + color: inputs::BORDER_COLOR, width: 1.0, radius: 4.0.into(), }, @@ -197,7 +197,7 @@ fn task_group_meta(members: &[&TaskSnapshot], locale: UiLocale) -> String { /// Panel background style. fn panel_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.13, 0.13, 0.16))), + background: Some(Background::Color(inputs::APP_BG)), ..container::Style::default() } } @@ -205,7 +205,7 @@ fn panel_style(_theme: &Theme) -> container::Style { /// Panel tab button — active. fn tab_active(_theme: &Theme, _status: button::Status) -> button::Style { button::Style { - background: Some(Background::Color(Color::from_rgb(0.20, 0.20, 0.25))), + background: Some(Background::Color(inputs::HOVER_BG)), text_color: Color::WHITE, border: Border { color: Color::from_rgb(0.30, 0.55, 0.90), @@ -219,7 +219,7 @@ fn tab_active(_theme: &Theme, _status: button::Status) -> button::Style { /// Panel tab button — inactive. fn tab_inactive(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.18, 0.18, 0.22), + button::Status::Hovered => inputs::ELEVATED_BG, _ => Color::TRANSPARENT, }; button::Style { @@ -646,13 +646,13 @@ mod progress_tests { } #[test] - fn task_cards_use_bds2_surface_and_status_palette() { + fn task_cards_use_shared_surface_and_bds2_status_palette() { let style = task_entry_style(&Theme::Dark); assert_eq!( style.background, - Some(Background::Color(Color::from_rgb8(0x25, 0x25, 0x26))) + Some(Background::Color(inputs::SURFACE_BG)) ); - assert_eq!(style.border.color, Color::from_rgb8(0x3C, 0x3C, 0x3C)); + assert_eq!(style.border.color, inputs::BORDER_COLOR); assert_eq!(style.border.width, 1.0); let group_style = task_group_button_style(&Theme::Dark, button::Status::Active); assert_eq!(group_style.background, style.background); diff --git a/crates/bds-ui/src/views/project_selector.rs b/crates/bds-ui/src/views/project_selector.rs index d0aa205..5ee10b8 100644 --- a/crates/bds-ui/src/views/project_selector.rs +++ b/crates/bds-ui/src/views/project_selector.rs @@ -8,6 +8,7 @@ use bds_core::i18n::UiLocale; use bds_core::model::Project; use crate::app::Message; +use crate::components::inputs; use crate::i18n::t; // --------------------------------------------------------------------------- @@ -16,9 +17,9 @@ use crate::i18n::t; fn dropdown_bg(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.18, 0.18, 0.22))), + background: Some(Background::Color(inputs::ELEVATED_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 10.0.into(), }, @@ -28,8 +29,8 @@ fn dropdown_bg(_theme: &Theme) -> container::Style { fn project_item(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.25, 0.25, 0.30), - _ => Color::from_rgb(0.18, 0.18, 0.22), + button::Status::Hovered => inputs::BORDER_COLOR, + _ => inputs::ELEVATED_BG, }; button::Style { background: Some(Background::Color(bg)), @@ -62,7 +63,7 @@ fn project_item_active(_theme: &Theme, status: button::Status) -> button::Style fn new_project_btn(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.25, 0.25, 0.30), + button::Status::Hovered => inputs::BORDER_COLOR, _ => Color::TRANSPARENT, }; button::Style { @@ -163,7 +164,7 @@ pub fn view( items.push( container(Space::new().width(Length::Fill).height(Length::Fixed(1.0))) .style(|_theme: &Theme| container::Style { - background: Some(Background::Color(Color::from_rgb(0.30, 0.30, 0.35))), + background: Some(Background::Color(inputs::BORDER_COLOR)), ..container::Style::default() }) .width(Length::Fill) @@ -227,7 +228,7 @@ pub fn view( pub fn trigger_style(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + button::Status::Hovered => inputs::HOVER_BG, _ => Color::TRANSPARENT, }; button::Style { diff --git a/crates/bds-ui/src/views/sidebar.rs b/crates/bds-ui/src/views/sidebar.rs index 921d640..46b31b1 100644 --- a/crates/bds-ui/src/views/sidebar.rs +++ b/crates/bds-ui/src/views/sidebar.rs @@ -21,7 +21,7 @@ use crate::views::git::GitUiState; /// Sidebar container style — dark background. fn sidebar_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.16, 0.16, 0.20))), + background: Some(Background::Color(inputs::SIDEBAR_BG)), ..container::Style::default() } } @@ -29,7 +29,7 @@ fn sidebar_style(_theme: &Theme) -> container::Style { /// Sidebar item button style. fn item_style(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + button::Status::Hovered => inputs::HOVER_BG, _ => Color::TRANSPARENT, }; button::Style { @@ -46,8 +46,8 @@ fn item_style(_theme: &Theme, status: button::Status) -> button::Style { /// Sidebar item button style — active/selected. fn item_active_style(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.26, 0.26, 0.32), - _ => Color::from_rgb(0.22, 0.22, 0.28), + button::Status::Hovered => inputs::BORDER_COLOR, + _ => inputs::ELEVATED_BG, }; button::Style { background: Some(Background::Color(bg)), @@ -63,7 +63,7 @@ fn item_active_style(_theme: &Theme, status: button::Status) -> button::Style { /// 40×40 thumbnail container: rounded corners, dark background. fn thumbnail_container_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.14, 0.14, 0.17))), + background: Some(Background::Color(inputs::APP_BG)), border: Border { radius: 6.0.into(), ..Border::default() @@ -172,7 +172,7 @@ fn search_input_style(theme: &Theme, status: text_input::Status) -> text_input:: /// Style for filter toggle / clear buttons in the header. fn filter_button_style(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.25, 0.25, 0.30), + button::Status::Hovered => inputs::BORDER_COLOR, _ => Color::TRANSPARENT, }; button::Style { @@ -206,8 +206,8 @@ fn filter_button_active_style(_theme: &Theme, status: button::Status) -> button: /// Tag/category chip style (unselected). fn chip_style(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.24, 0.24, 0.30), - _ => Color::from_rgb(0.18, 0.18, 0.22), + button::Status::Hovered => inputs::HOVER_BG, + _ => inputs::ELEVATED_BG, }; button::Style { background: Some(Background::Color(bg)), @@ -242,7 +242,7 @@ fn chip_selected_style(_theme: &Theme, status: button::Status) -> button::Style /// Calendar year/month button style. fn calendar_style(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + button::Status::Hovered => inputs::HOVER_BG, _ => Color::TRANSPARENT, }; button::Style { @@ -289,7 +289,7 @@ fn row_delete_style(_theme: &Theme, status: button::Status) -> button::Style { _ => Color::from_rgb(0.60, 0.60, 0.65), }; button::Style { - background: Some(Background::Color(Color::from_rgb(0.20, 0.20, 0.25))), + background: Some(Background::Color(inputs::HOVER_BG)), text_color, border: Border { radius: 4.0.into(), diff --git a/crates/bds-ui/src/views/site_validation.rs b/crates/bds-ui/src/views/site_validation.rs index b830abd..f683b85 100644 --- a/crates/bds-ui/src/views/site_validation.rs +++ b/crates/bds-ui/src/views/site_validation.rs @@ -143,7 +143,7 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a, .height(Length::Fill) .padding(24) .style(|_theme: &Theme| container::Style { - background: Some(Background::Color(Color::from_rgb(0.11, 0.11, 0.14))), + background: Some(Background::Color(inputs::APP_BG)), ..container::Style::default() }) .into() diff --git a/crates/bds-ui/src/views/status_bar.rs b/crates/bds-ui/src/views/status_bar.rs index 44db091..23a1863 100644 --- a/crates/bds-ui/src/views/status_bar.rs +++ b/crates/bds-ui/src/views/status_bar.rs @@ -8,6 +8,7 @@ use bds_core::engine::task::TaskStatus; use bds_core::i18n::UiLocale; use crate::app::Message; +use crate::components::inputs; use crate::i18n::{t, tw}; use crate::state::navigation::TaskSnapshot; use crate::views::project_selector; @@ -19,7 +20,7 @@ use crate::views::project_selector; /// Status bar background. fn bar_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.12, 0.12, 0.16))), + background: Some(Background::Color(inputs::ACTIVITY_BG)), ..container::Style::default() } } @@ -27,7 +28,7 @@ fn bar_style(_theme: &Theme) -> container::Style { /// Airplane button — inactive (dim). fn airplane_inactive(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + button::Status::Hovered => inputs::HOVER_BG, _ => Color::TRANSPARENT, }; button::Style { @@ -63,7 +64,7 @@ fn airplane_active(_theme: &Theme, status: button::Status) -> button::Style { /// Dropdown trigger button style. pub fn dropdown_trigger(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.22, 0.22, 0.27), + button::Status::Hovered => inputs::HOVER_BG, _ => Color::TRANSPARENT, }; button::Style { @@ -81,8 +82,8 @@ pub fn dropdown_trigger(_theme: &Theme, status: button::Status) -> button::Style /// Dropdown item style. pub fn dropdown_item(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.25, 0.25, 0.30), - _ => Color::from_rgb(0.18, 0.18, 0.22), + button::Status::Hovered => inputs::BORDER_COLOR, + _ => inputs::ELEVATED_BG, }; button::Style { background: Some(Background::Color(bg)), @@ -99,9 +100,9 @@ pub fn dropdown_item(_theme: &Theme, status: button::Status) -> button::Style { /// Dropdown container background. pub fn dropdown_bg(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.18, 0.18, 0.22))), + background: Some(Background::Color(inputs::ELEVATED_BG)), border: Border { - color: Color::from_rgb(0.30, 0.30, 0.35), + color: inputs::BORDER_COLOR, width: 1.0, radius: 6.0.into(), }, diff --git a/crates/bds-ui/src/views/style_view.rs b/crates/bds-ui/src/views/style_view.rs index 133a366..dc1dd26 100644 --- a/crates/bds-ui/src/views/style_view.rs +++ b/crates/bds-ui/src/views/style_view.rs @@ -178,16 +178,16 @@ fn theme_button_style(selected: bool, status: button::Status) -> button::Style { background: Some(Background::Color(if selected { Color::from_rgb8(0x1F, 0x45, 0x63) } else if hovered { - Color::from_rgb8(0x32, 0x32, 0x35) + inputs::HOVER_BG } else { - Color::from_rgb8(0x24, 0x24, 0x26) + inputs::FIELD_BG })), text_color: Color::from_rgb8(0xE4, 0xE4, 0xE4), border: Border { color: if selected { Color::from_rgb8(0x00, 0x7F, 0xD4) } else { - Color::from_rgb8(0x3C, 0x3C, 0x3C) + inputs::BORDER_COLOR }, width: if selected { 2.0 } else { 1.0 }, radius: 6.0.into(), diff --git a/crates/bds-ui/src/views/tab_bar.rs b/crates/bds-ui/src/views/tab_bar.rs index 65c3810..db94672 100644 --- a/crates/bds-ui/src/views/tab_bar.rs +++ b/crates/bds-ui/src/views/tab_bar.rs @@ -26,9 +26,9 @@ const TAB_TITLE_MAX_LEN: usize = 17; /// Tab bar background. fn bar_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.14, 0.14, 0.18))), + background: Some(Background::Color(inputs::SIDEBAR_BG)), border: Border { - color: Color::from_rgb(0.25, 0.25, 0.30), + color: inputs::BORDER_COLOR, width: 0.0, radius: 0.0.into(), }, @@ -39,7 +39,7 @@ fn bar_style(_theme: &Theme) -> container::Style { /// Active tab style. fn tab_active(_theme: &Theme, _status: button::Status) -> button::Style { button::Style { - background: Some(Background::Color(Color::from_rgb(0.11, 0.11, 0.14))), + background: Some(Background::Color(inputs::APP_BG)), text_color: Color::WHITE, border: Border { color: Color::from_rgb(0.30, 0.55, 0.90), @@ -53,8 +53,8 @@ fn tab_active(_theme: &Theme, _status: button::Status) -> button::Style { /// Inactive tab style. fn tab_inactive(_theme: &Theme, status: button::Status) -> button::Style { let bg = match status { - button::Status::Hovered => Color::from_rgb(0.18, 0.18, 0.22), - _ => Color::from_rgb(0.14, 0.14, 0.18), + button::Status::Hovered => inputs::ELEVATED_BG, + _ => inputs::SIDEBAR_BG, }; button::Style { background: Some(Background::Color(bg)), diff --git a/crates/bds-ui/src/views/tags_view.rs b/crates/bds-ui/src/views/tags_view.rs index 659891e..83360ed 100644 --- a/crates/bds-ui/src/views/tags_view.rs +++ b/crates/bds-ui/src/views/tags_view.rs @@ -478,8 +478,8 @@ fn view_cloud<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Mes background: Some(Background::Color(match (selected, status) { (true, button::Status::Hovered) => Color::from_rgb(0.20, 0.24, 0.30), (true, _) => Color::from_rgb(0.17, 0.20, 0.25), - (false, button::Status::Hovered) => Color::from_rgb(0.23, 0.24, 0.26), - (false, _) => Color::from_rgb(0.18, 0.18, 0.19), + (false, button::Status::Hovered) => inputs::HOVER_BG, + (false, _) => inputs::ELEVATED_BG, })), border: iced::Border { radius: 12.0.into(), @@ -617,7 +617,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me .padding([6, 8]) .style(move |_theme: &Theme, _status| button::Style { background: Some(Background::Color(if selected { - Color::from_rgb(0.22, 0.24, 0.30) + inputs::HOVER_BG } else { Color::TRANSPARENT })), diff --git a/crates/bds-ui/src/views/welcome.rs b/crates/bds-ui/src/views/welcome.rs index 13c3738..ce4713c 100644 --- a/crates/bds-ui/src/views/welcome.rs +++ b/crates/bds-ui/src/views/welcome.rs @@ -5,6 +5,7 @@ use iced::{Background, Color, Element, Length, Theme}; use bds_core::i18n::UiLocale; use crate::app::Message; +use crate::components::inputs; use crate::i18n::t; pub fn view(locale: UiLocale) -> Element<'static, Message> { @@ -30,7 +31,7 @@ pub fn view(locale: UiLocale) -> Element<'static, Message> { .center_x(Length::Fill) .center_y(Length::Fill) .style(|_theme: &Theme| container::Style { - background: Some(Background::Color(Color::from_rgb(0.11, 0.11, 0.14))), + background: Some(Background::Color(inputs::APP_BG)), ..container::Style::default() }) .into() diff --git a/crates/bds-ui/src/views/workspace.rs b/crates/bds-ui/src/views/workspace.rs index 9706ef5..39d5ca5 100644 --- a/crates/bds-ui/src/views/workspace.rs +++ b/crates/bds-ui/src/views/workspace.rs @@ -1,4 +1,4 @@ -use crate::components::keyboard; +use crate::components::{inputs, keyboard}; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; @@ -45,7 +45,7 @@ use crate::views::{ /// Main content area background. fn content_bg(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.11, 0.11, 0.14))), + background: Some(Background::Color(inputs::APP_BG)), ..container::Style::default() } } @@ -53,7 +53,7 @@ fn content_bg(_theme: &Theme) -> container::Style { /// Sidebar resize drag handle style. fn drag_handle_style(_theme: &Theme) -> container::Style { container::Style { - background: Some(Background::Color(Color::from_rgb(0.25, 0.25, 0.30))), + background: Some(Background::Color(inputs::BORDER_COLOR)), ..container::Style::default() } } @@ -64,7 +64,7 @@ fn separator_h<'a>() -> Element<'a, Message> { .width(Length::Fill) .height(Length::Fixed(1.0)) .style(|_theme: &Theme| container::Style { - background: Some(Background::Color(Color::from_rgb(0.25, 0.25, 0.30))), + background: Some(Background::Color(inputs::BORDER_COLOR)), ..container::Style::default() }) .into() diff --git a/docs/UI_STYLE_GUIDE.md b/docs/UI_STYLE_GUIDE.md index ca9849a..80a9f2c 100644 --- a/docs/UI_STYLE_GUIDE.md +++ b/docs/UI_STYLE_GUIDE.md @@ -9,6 +9,12 @@ Use the post editor as the visual reference for every screen. New UI must feel l - Follow `crates/bds-ui/src/views/post_editor.rs` for editor hierarchy and spacing. - Extend a shared primitive when several screens need the same treatment; do not copy color or border closures into each view. +### Base palette + +- Use the named colors in `components::inputs`: `APP_BG` (`#0d1117`), `ACTIVITY_BG` (`#090c10`), `SIDEBAR_BG` (`#11161d`), `SURFACE_BG` (`#161b22`), `ELEVATED_BG` (`#1c2128`), `HOVER_BG` (`#21262d`), and `BORDER_COLOR` (`#30363d`). +- Fields use `FIELD_BG`, which deliberately matches `APP_BG`; their border supplies separation. +- Keep semantic status, selection, and accent colors separate from the base palette. + ## Layout - Use 16 px outer editor padding and 8–12 px spacing between groups.