Darken the desktop base palette.

This commit is contained in:
2026-08-15 11:19:50 +02:00
parent ef9a55c2a2
commit 7c2a5bf8c8
19 changed files with 127 additions and 111 deletions

View File

@@ -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.

View File

@@ -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<Element<'a, Message>>) -> 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();

View File

@@ -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()
}),
);

View File

@@ -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()
}
}

View File

@@ -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);
}
}

View File

@@ -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<String>) -> 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<String>) -> 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()

View File

@@ -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(),
},

View File

@@ -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(),
},

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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(),

View File

@@ -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()

View File

@@ -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(),
},

View File

@@ -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(),

View File

@@ -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)),

View File

@@ -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
})),

View File

@@ -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()

View File

@@ -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()

View File

@@ -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 812 px spacing between groups.