From 483bda5567783a9c4aff7b86aaeae90a54d6f91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sat, 9 May 2026 15:42:10 -0400 Subject: [PATCH] Redesign shell with glass morphism and warm clay palette Update design system: warm clay accent (#c96442), ink scale for text hierarchy, glass surface tokens with alpha, redesigned spacing/radius tokens. Restructure shell layout: sidebar and main pane as rounded glass panels, pill-shaped omnibar inside main pane, workspace tiles, section labels, profile avatar in sidebar footer. --- crates/ely_app/src/shell/render.rs | 467 +++++++++++++-------- crates/ely_app/src/shell/sidebar.rs | 33 +- crates/ely_design_system/src/colors.rs | 54 ++- crates/ely_design_system/src/lib.rs | 6 +- crates/ely_design_system/src/spacing.rs | 16 +- crates/ely_design_system/src/typography.rs | 18 +- 6 files changed, 373 insertions(+), 221 deletions(-) diff --git a/crates/ely_app/src/shell/render.rs b/crates/ely_app/src/shell/render.rs index dfdea86..968eeb0 100644 --- a/crates/ely_app/src/shell/render.rs +++ b/crates/ely_app/src/shell/render.rs @@ -1,14 +1,15 @@ use ely_browser_core::BrowserSnapshot; -use ely_design_system::{ELY_THEME, colors, spacing}; +use ely_design_system::{colors, spacing}; use ely_domain::{ArchivedTab, BrowserTab, Profile, Space}; use gpui::{ AnyElement, Context, InteractiveElement, IntoElement, ParentElement, Render, SharedString, - StatefulInteractiveElement, Styled, Window, div, px, rgb, + StatefulInteractiveElement, Styled, Window, div, px, rgb, rgba, }; use gpui_component::{ IconName, Selectable, Sizable, StyledExt, button::{Button, ButtonVariants}, input::Input, + scroll::ScrollableElement, }; use super::sidebar::{collapsed_sidebar_active, render_command_bar_identity}; @@ -64,33 +65,46 @@ impl ElyShell { .on_action(cx.listener(Self::on_toggle_sidebar)) .on_action(cx.listener(Self::on_zoom_in)) .on_action(cx.listener(Self::on_zoom_out)) - .bg(rgb(ELY_THEME.canvas)) - .text_color(rgb(ELY_THEME.ink)) + .bg(rgb(colors::CANVAS)) + .text_color(rgb(colors::INK)) + .p(px(spacing::SHELL_INSET)) + .gap(px(spacing::SIDEBAR_MAIN_GAP)) + .flex() + .child(self.render_sidebar(&snapshot, sidebar_width, sidebar_collapsed, cx)) + .child(self.render_main_pane(&snapshot, &active_tab, sidebar_collapsed, cx)) + .into_any_element() + } + + fn render_main_pane( + &mut self, + snapshot: &BrowserSnapshot, + active_tab: &BrowserTab, + sidebar_collapsed: bool, + cx: &mut Context, + ) -> AnyElement { + div() + .flex_1() + .h_full() + .min_w_0() .flex() .flex_col() - .child(self.render_command_bar( - &snapshot, - &active_tab, - sidebar_width, - sidebar_collapsed, - cx, - )) + .rounded(px(spacing::RADIUS_CARD)) + .bg(rgba(colors::GLASS)) + .overflow_hidden() + .child(self.render_topbar(snapshot, active_tab, sidebar_collapsed, cx)) .child( div() - .flex() .flex_1() .overflow_hidden() - .child(self.render_sidebar(&snapshot, sidebar_width, sidebar_collapsed, cx)) - .child(self.render_content_area(&snapshot, &active_tab, cx)), + .child(self.render_content_area(snapshot, active_tab, cx)), ) .into_any_element() } - fn render_command_bar( + fn render_topbar( &mut self, snapshot: &BrowserSnapshot, active_tab: &BrowserTab, - sidebar_width: f32, sidebar_collapsed: bool, cx: &mut Context, ) -> AnyElement { @@ -101,25 +115,41 @@ impl ElyShell { let pinned_tooltip = if active_tab.flags().pinned { "Unpin Tab" } else { "Pin Tab" }; div() - .h(px(spacing::COMMAND_BAR_HEIGHT)) - .pl(px(96.0)) - .pr_4() - .gap_3() + .h(px(spacing::TOPBAR_HEIGHT)) + .px(px(14.0)) + .gap_2() .flex() .items_center() - .border_b_1() - .border_color(rgb(colors::HAIRLINE)) - .child(render_command_bar_identity(snapshot, sidebar_width, sidebar_collapsed)) + .flex_shrink_0() + .children(if sidebar_collapsed { + Some(render_command_bar_identity(snapshot, 56.0, true)) + } else { + None + }) .child( div() .flex_1() - .h(px(40.0)) - .rounded_md() - .border_1() - .border_color(rgb(colors::HAIRLINE_STRONG)) - .bg(rgb(colors::SURFACE_CARD)) - .px_3() - .child(Input::new(&self.command_input).appearance(false).cleanable(true)), + .h(px(spacing::OMNIBAR_HEIGHT)) + .rounded(px(spacing::RADIUS_PILL)) + .bg(rgba(colors::GLASS_2)) + .px(px(14.0)) + .flex() + .items_center() + .gap(px(10.0)) + .child( + div() + .text_color(rgb(colors::INK_3)) + .child(IconName::Search), + ) + .child( + div() + .flex_1() + .child( + Input::new(&self.command_input) + .appearance(false) + .cleanable(true), + ), + ), ) .child( Button::new("toggle-pinned-tab") @@ -141,7 +171,7 @@ impl ElyShell { ) .child( Button::new("new-tab") - .primary() + .ghost() .small() .icon(IconName::Plus) .tooltip("New Tab") @@ -166,49 +196,131 @@ impl ElyShell { .h_full() .flex() .flex_col() - .gap_3() - .p_3() - .border_r_1() - .border_color(rgb(colors::HAIRLINE)) - .bg(rgb(colors::CANVAS)) - .child(section_label("Favorites")) - .children(snapshot.favorites.iter().map(|tab| { - self.render_favorite_row( - tab, - &snapshot.profiles, - tab.id() == &snapshot.active_tab_id, - cx, - ) - })) - .child(section_label("Pinned")) - .children(snapshot.pinned_tabs.iter().map(|tab| { - self.render_pinned_row( - tab, - &snapshot.profiles, - tab.id() == &snapshot.active_tab_id, - cx, - ) - })) - .child(section_label("Space")) - .children(snapshot.spaces.iter().map(|space| { - self.render_space_row(space, space.id() == &snapshot.active_space_id, cx) - })) - .child(section_label("Tabs")) - .children(self.render_sidebar_tab_rows(snapshot, cx)) - .child(section_label("Archive")) - .children( - snapshot - .archived_tabs - .iter() - .rev() - .map(|archived_tab| self.render_archived_row(archived_tab, snapshot, cx)), - ) - .child(div().flex_1()) - .child(section_label("Profile")) + .rounded(px(spacing::RADIUS_CARD)) + .bg(rgba(colors::GLASS)) + .overflow_hidden() + .child(self.render_sidebar_header(snapshot)) .child( div() - .text_sm() - .text_color(rgb(colors::BODY)) + .flex_1() + .overflow_y_scrollbar() + .p(px(10.0)) + .flex() + .flex_col() + .gap(px(6.0)) + .child(section_label("FAVORITES")) + .children(snapshot.favorites.iter().map(|tab| { + self.render_favorite_row( + tab, + &snapshot.profiles, + tab.id() == &snapshot.active_tab_id, + cx, + ) + })) + .child(section_label("PINNED")) + .children(snapshot.pinned_tabs.iter().map(|tab| { + self.render_pinned_row( + tab, + &snapshot.profiles, + tab.id() == &snapshot.active_tab_id, + cx, + ) + })) + .child(section_label("SPACES")) + .children(snapshot.spaces.iter().map(|space| { + self.render_space_row( + space, + space.id() == &snapshot.active_space_id, + cx, + ) + })) + .child(section_label("TABS")) + .children(self.render_sidebar_tab_rows(snapshot, cx)) + .child(section_label("ARCHIVE")) + .children( + snapshot + .archived_tabs + .iter() + .rev() + .map(|archived_tab| { + self.render_archived_row(archived_tab, snapshot, cx) + }), + ), + ) + .child(self.render_sidebar_footer(snapshot)) + .into_any_element() + } + + fn render_sidebar_header(&self, snapshot: &BrowserSnapshot) -> AnyElement { + let space_name = snapshot + .spaces + .iter() + .find(|s| s.id() == &snapshot.active_space_id) + .map(|s| s.name().to_string()) + .unwrap_or_default(); + + div() + .pt(px(48.0)) + .px(px(14.0)) + .pb(px(10.0)) + .flex() + .flex_col() + .gap_1() + .flex_shrink_0() + .child( + div() + .text_size(px(15.0)) + .font_semibold() + .text_color(rgb(colors::INK)) + .child("ELY Browser"), + ) + .child( + div() + .text_size(px(11.0)) + .text_color(rgb(colors::INK_3)) + .child(space_name), + ) + .into_any_element() + } + + fn render_sidebar_footer(&self, snapshot: &BrowserSnapshot) -> AnyElement { + div() + .px(px(14.0)) + .py(px(12.0)) + .flex() + .items_center() + .gap_2() + .flex_shrink_0() + .border_t_1() + .border_color(rgba(colors::DIVIDER)) + .child( + div() + .size(px(26.0)) + .rounded_full() + .bg(rgb(colors::ACCENT)) + .flex() + .items_center() + .justify_center() + .child( + div() + .text_size(px(10.0)) + .font_semibold() + .text_color(rgb(colors::SURFACE_CARD)) + .child( + snapshot + .active_profile_name + .chars() + .next() + .unwrap_or('P') + .to_uppercase() + .to_string(), + ), + ), + ) + .child( + div() + .text_size(px(13.0)) + .text_color(rgb(colors::INK_2)) .child(snapshot.active_profile_name.clone()), ) .into_any_element() @@ -221,34 +333,32 @@ impl ElyShell { cx: &mut Context, ) -> AnyElement { let space_id = space.id().clone(); - let background = if active { colors::SURFACE_CARD } else { colors::CANVAS }; - let border = if active { colors::HAIRLINE_STRONG } else { colors::HAIRLINE }; + let bg_color = if active { colors::GLASS_3 } else { 0x00000000 }; + let text_color = if active { colors::INK } else { colors::INK_2 }; div() .id(SharedString::from(format!("space-{}", space.id().as_str()))) - .rounded_md() - .border_1() - .border_color(rgb(border)) - .bg(rgb(background)) - .px_3() - .py_2() - .gap_2() + .rounded(px(spacing::RADIUS_NAV)) + .px(px(10.0)) + .py(px(7.0)) + .gap(px(10.0)) .flex() .items_center() .cursor_pointer() - .hover(|style| style.bg(rgb(colors::SURFACE_CARD))) + .hover(|style| style.bg(rgba(colors::GLASS_3))) .active(|style| style.opacity(0.82)) + .bg(rgba(bg_color)) .on_click(cx.listener(move |shell, _, window, cx| { shell.select_space(&space_id, window, cx); })) + .child(render_workspace_tile(space)) .child( div() - .text_xs() - .font_semibold() - .text_color(rgb(colors::PRIMARY)) - .child(space.icon().to_string()), + .text_size(px(13.0)) + .font_weight(gpui::FontWeight(500.0)) + .text_color(rgb(text_color)) + .child(space.name().to_string()), ) - .child(div().text_sm().font_semibold().child(space.name().to_string())) .into_any_element() } @@ -259,45 +369,7 @@ impl ElyShell { active: bool, cx: &mut Context, ) -> AnyElement { - let tab_id = tab.id().clone(); - let background = if active { colors::SURFACE_CARD } else { colors::CANVAS }; - let border = if active { colors::HAIRLINE_STRONG } else { colors::HAIRLINE }; - let detail = sidebar_tab_detail(tab, profiles); - - div() - .id(SharedString::from(format!("favorite-{}", tab.id().as_str()))) - .rounded_md() - .border_1() - .border_color(rgb(border)) - .bg(rgb(background)) - .px_3() - .py_2() - .gap_2() - .flex() - .items_center() - .cursor_pointer() - .hover(|style| style.bg(rgb(colors::SURFACE_CARD))) - .active(|style| style.opacity(0.82)) - .on_click(cx.listener(move |shell, _, window, cx| { - shell.select_tab(&tab_id, window, cx); - })) - .child(div().text_color(rgb(colors::PRIMARY)).child(IconName::Star)) - .child( - div() - .min_w_0() - .flex() - .flex_col() - .gap_1() - .child( - div() - .text_sm() - .font_semibold() - .text_color(rgb(colors::INK)) - .child(tab.title().to_string()), - ) - .child(div().text_xs().text_color(rgb(colors::MUTED)).child(detail)), - ) - .into_any_element() + self.render_nav_row(tab, profiles, active, IconName::Star, colors::ACCENT, cx) } fn render_pinned_row( @@ -306,44 +378,47 @@ impl ElyShell { profiles: &[Profile], active: bool, cx: &mut Context, + ) -> AnyElement { + self.render_nav_row(tab, profiles, active, IconName::Asterisk, colors::INK_3, cx) + } + + fn render_nav_row( + &mut self, + tab: &BrowserTab, + _profiles: &[Profile], + active: bool, + icon: IconName, + icon_color: u32, + cx: &mut Context, ) -> AnyElement { let tab_id = tab.id().clone(); - let background = if active { colors::SURFACE_CARD } else { colors::CANVAS }; - let border = if active { colors::HAIRLINE_STRONG } else { colors::HAIRLINE }; - let detail = sidebar_tab_detail(tab, profiles); + let bg_color = if active { colors::GLASS_3 } else { 0x00000000 }; + let text_color = if active { colors::INK } else { colors::INK_2 }; div() - .id(SharedString::from(format!("pinned-{}", tab.id().as_str()))) - .rounded_md() - .border_1() - .border_color(rgb(border)) - .bg(rgb(background)) - .px_3() - .py_2() - .gap_2() + .id(SharedString::from(format!("nav-{}", tab.id().as_str()))) + .rounded(px(spacing::RADIUS_NAV)) + .px(px(10.0)) + .py(px(7.0)) + .gap(px(10.0)) .flex() .items_center() .cursor_pointer() - .hover(|style| style.bg(rgb(colors::SURFACE_CARD))) + .hover(|style| style.bg(rgba(colors::GLASS_3))) .active(|style| style.opacity(0.82)) + .bg(rgba(bg_color)) .on_click(cx.listener(move |shell, _, window, cx| { shell.select_tab(&tab_id, window, cx); })) - .child(div().text_color(rgb(colors::MUTED)).child(IconName::Asterisk)) + .child(div().text_color(rgb(icon_color)).child(icon)) .child( div() .min_w_0() - .flex() - .flex_col() - .gap_1() - .child( - div() - .text_sm() - .font_semibold() - .text_color(rgb(colors::INK)) - .child(tab.title().to_string()), - ) - .child(div().text_xs().text_color(rgb(colors::MUTED)).child(detail)), + .overflow_hidden() + .text_size(px(13.0)) + .font_weight(gpui::FontWeight(500.0)) + .text_color(rgb(text_color)) + .child(tab.title().to_string()), ) .into_any_element() } @@ -355,34 +430,39 @@ impl ElyShell { cx: &mut Context, ) -> AnyElement { let tab_id = tab.id().clone(); - let background = if active { colors::SURFACE_CARD } else { colors::CANVAS }; - let border = if active { colors::HAIRLINE_STRONG } else { colors::HAIRLINE }; + let bg_color = if active { colors::GLASS_3 } else { 0x00000000 }; + let text_color = if active { colors::INK } else { colors::INK_2 }; div() .id(SharedString::from(tab.id().as_str().to_string())) - .rounded_md() - .border_1() - .border_color(rgb(border)) - .bg(rgb(background)) - .px_3() - .py_2() - .gap_1() + .rounded(px(spacing::RADIUS_NAV)) + .px(px(10.0)) + .py(px(7.0)) + .gap(px(6.0)) .flex() .flex_col() .cursor_pointer() - .hover(|style| style.bg(rgb(colors::SURFACE_CARD))) + .hover(|style| style.bg(rgba(colors::GLASS_3))) .active(|style| style.opacity(0.82)) + .bg(rgba(bg_color)) .on_click(cx.listener(move |shell, _, window, cx| { shell.select_tab(&tab_id, window, cx); })) .child( div() - .text_sm() - .font_semibold() - .text_color(rgb(colors::INK)) + .text_size(px(13.0)) + .font_weight(gpui::FontWeight(500.0)) + .text_color(rgb(text_color)) + .overflow_hidden() .child(tab.title().to_string()), ) - .child(div().text_xs().text_color(rgb(colors::MUTED)).child(tab.display_url())) + .child( + div() + .text_size(px(11.0)) + .text_color(rgb(colors::INK_4)) + .overflow_hidden() + .child(tab.display_url()), + ) .into_any_element() } @@ -398,22 +478,19 @@ impl ElyShell { div() .id(SharedString::from(format!("archived-{}", tab.id().as_str()))) - .rounded_md() - .border_1() - .border_color(rgb(colors::HAIRLINE)) - .bg(rgb(colors::CANVAS)) - .px_3() - .py_2() - .gap_2() + .rounded(px(spacing::RADIUS_NAV)) + .px(px(10.0)) + .py(px(7.0)) + .gap(px(10.0)) .flex() .items_center() .cursor_pointer() - .hover(|style| style.bg(rgb(colors::SURFACE_CARD))) + .hover(|style| style.bg(rgba(colors::GLASS_3))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(move |shell, _, window, cx| { shell.restore_archived_tab(&tab_id, window, cx); })) - .child(div().text_color(rgb(colors::MUTED)).child(IconName::Undo2)) + .child(div().text_color(rgb(colors::INK_4)).child(IconName::Undo2)) .child( div() .min_w_0() @@ -422,12 +499,19 @@ impl ElyShell { .gap_1() .child( div() - .text_sm() - .font_semibold() - .text_color(rgb(colors::INK)) + .text_size(px(13.0)) + .font_weight(gpui::FontWeight(500.0)) + .text_color(rgb(colors::INK_2)) + .overflow_hidden() .child(tab.title().to_string()), ) - .child(div().text_xs().text_color(rgb(colors::MUTED)).child(detail)), + .child( + div() + .text_size(px(11.0)) + .text_color(rgb(colors::INK_4)) + .overflow_hidden() + .child(detail), + ), ) .into_any_element() } @@ -446,7 +530,30 @@ fn render_error(message: String) -> AnyElement { } fn section_label(label: &'static str) -> impl IntoElement { - div().text_xs().font_semibold().text_color(rgb(colors::MUTED)).child(label) + div() + .pt(px(8.0)) + .pb(px(4.0)) + .px(px(10.0)) + .text_size(px(10.5)) + .font_weight(gpui::FontWeight(500.0)) + .text_color(rgb(colors::INK_4)) + .child(label) +} + +fn render_workspace_tile(space: &Space) -> impl IntoElement { + div() + .size(px(32.0)) + .rounded(px(9.0)) + .bg(rgba(colors::GLASS_3)) + .flex() + .items_center() + .justify_center() + .child( + div() + .text_size(px(14.0)) + .text_color(rgb(colors::ACCENT)) + .child(space.icon().to_string()), + ) } fn active_sidebar_width(snapshot: &BrowserSnapshot) -> Result { @@ -459,10 +566,6 @@ fn active_sidebar_width(snapshot: &BrowserSnapshot) -> Result { Ok(f32::from(active_space.sidebar_width_px())) } -fn sidebar_tab_detail(tab: &BrowserTab, profiles: &[Profile]) -> String { - format!("{} - {}", tab.display_url(), tab_profile_label(tab, profiles)) -} - pub(super) fn tab_profile_label(tab: &BrowserTab, profiles: &[Profile]) -> String { profiles .iter() diff --git a/crates/ely_app/src/shell/sidebar.rs b/crates/ely_app/src/shell/sidebar.rs index 511d9ca..8cd156c 100644 --- a/crates/ely_app/src/shell/sidebar.rs +++ b/crates/ely_app/src/shell/sidebar.rs @@ -3,7 +3,7 @@ use ely_design_system::{colors, spacing}; use ely_domain::{ ArchivedTab, BrowserTab, COLLAPSED_SIDEBAR_WIDTH_PX, DEFAULT_SIDEBAR_WIDTH_PX, Space, }; -use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, Window, div, px, rgb}; +use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, Window, div, px, rgb, rgba}; use gpui_component::{ IconName, Selectable, Sizable, StyledExt, button::{Button, ButtonVariants}, @@ -36,9 +36,8 @@ impl ElyShell { .items_center() .gap_2() .p_2() - .border_r_1() - .border_color(rgb(colors::HAIRLINE)) - .bg(rgb(colors::CANVAS)) + .rounded(px(spacing::RADIUS_CARD)) + .bg(rgba(colors::GLASS)) .children(snapshot.favorites.iter().enumerate().map(|(index, tab)| { self.render_compact_tab_button( ("compact-favorite", index), @@ -174,23 +173,20 @@ impl ElyShell { pub(super) fn render_command_bar_identity( snapshot: &BrowserSnapshot, - sidebar_width: f32, + _sidebar_width: f32, sidebar_collapsed: bool, ) -> AnyElement { - let width = sidebar_width - spacing::XL; if sidebar_collapsed { return div() - .w(px(width)) .flex() .items_center() - .justify_center() .child( div() .size(px(28.0)) - .rounded_md() - .bg(rgb(colors::PRIMARY)) - .text_color(rgb(colors::CANVAS)) - .text_xs() + .rounded(px(spacing::RADIUS_NAV)) + .bg(rgb(colors::ACCENT)) + .text_color(rgb(colors::SURFACE_CARD)) + .text_size(px(10.0)) .font_semibold() .flex() .items_center() @@ -201,15 +197,20 @@ pub(super) fn render_command_bar_identity( } div() - .w(px(width)) .flex() .items_center() .gap_2() - .child(div().text_size(px(18.0)).font_semibold().child("ELY Browser")) .child( div() - .text_xs() - .text_color(rgb(colors::MUTED)) + .text_size(px(15.0)) + .font_semibold() + .text_color(rgb(colors::INK)) + .child("ELY Browser"), + ) + .child( + div() + .text_size(px(11.0)) + .text_color(rgb(colors::INK_3)) .child(snapshot.active_space_name.clone()), ) .into_any_element() diff --git a/crates/ely_design_system/src/colors.rs b/crates/ely_design_system/src/colors.rs index 71d1d00..d5fd46b 100644 --- a/crates/ely_design_system/src/colors.rs +++ b/crates/ely_design_system/src/colors.rs @@ -1,14 +1,46 @@ -pub const PRIMARY: u32 = 0xf54e00; -pub const PRIMARY_ACTIVE: u32 = 0xd04200; -pub const INK: u32 = 0x26251e; -pub const BODY: u32 = 0x5a5852; -pub const MUTED: u32 = 0x807d72; -pub const MUTED_SOFT: u32 = 0xa09c92; -pub const HAIRLINE: u32 = 0xe6e5e0; -pub const HAIRLINE_STRONG: u32 = 0xcfcdc4; -pub const CANVAS: u32 = 0xf7f7f4; +// Ink scale — text hierarchy with warm undertones +pub const INK: u32 = 0x1d1c1a; +pub const INK_2: u32 = 0x3a3733; +pub const INK_3: u32 = 0x6b6660; +pub const INK_4: u32 = 0x9a948c; +pub const INK_5: u32 = 0xc8c2b8; + +// Glass surfaces — semi-transparent whites (use with rgba) +pub const GLASS: u32 = 0xffffff9e; // 62% white +pub const GLASS_2: u32 = 0xffffffc7; // 78% white +pub const GLASS_3: u32 = 0xffffffeb; // 92% white +pub const TINT: u32 = 0xffffff6b; // 42% white + +// Stroke & divider — subtle borders (use with rgba) +pub const STROKE: u32 = 0x281e1414; // 8% warm dark +pub const STROKE_2: u32 = 0x281e140a; // 4% warm dark +pub const DIVIDER: u32 = 0x281e140f; // 6% warm dark + +// Accent — warm clay palette +pub const ACCENT: u32 = 0xc96442; +pub const ACCENT_LIGHT: u32 = 0xd97757; +pub const ACCENT_HOVER: u32 = 0xc9644214; // 8% opacity for row highlights + +// Secondary accents +pub const VIOLET: u32 = 0x7c6cf7; +pub const ROSE: u32 = 0xec6a8e; +pub const MINT: u32 = 0x4fb59a; + +// Canvas backgrounds (opaque fallbacks) +pub const CANVAS: u32 = 0xf0eee9; pub const CANVAS_SOFT: u32 = 0xfafaf7; pub const SURFACE_CARD: u32 = 0xffffff; -pub const SURFACE_STRONG: u32 = 0xe6e5e0; -pub const SUCCESS: u32 = 0x1f8a65; + +// Semantic +pub const SUCCESS: u32 = 0x2f7d68; pub const ERROR: u32 = 0xcf2d56; + +// Legacy aliases — preserved for compatibility during migration +pub const PRIMARY: u32 = ACCENT; +pub const PRIMARY_ACTIVE: u32 = 0xb55a3c; +pub const BODY: u32 = INK_2; +pub const MUTED: u32 = INK_3; +pub const MUTED_SOFT: u32 = INK_4; +pub const HAIRLINE: u32 = 0xe6e5e0; +pub const HAIRLINE_STRONG: u32 = 0xcfcdc4; +pub const SURFACE_STRONG: u32 = 0xe6e5e0; diff --git a/crates/ely_design_system/src/lib.rs b/crates/ely_design_system/src/lib.rs index e08270d..04035d5 100644 --- a/crates/ely_design_system/src/lib.rs +++ b/crates/ely_design_system/src/lib.rs @@ -22,10 +22,10 @@ pub const ELY_THEME: Theme = Theme { canvas_soft: colors::CANVAS_SOFT, surface: colors::SURFACE_CARD, ink: colors::INK, - body: colors::BODY, - muted: colors::MUTED, + body: colors::INK_2, + muted: colors::INK_3, hairline: colors::HAIRLINE, - accent: colors::PRIMARY, + accent: colors::ACCENT, success: colors::SUCCESS, error: colors::ERROR, }; diff --git a/crates/ely_design_system/src/spacing.rs b/crates/ely_design_system/src/spacing.rs index 9fc710f..2fc1139 100644 --- a/crates/ely_design_system/src/spacing.rs +++ b/crates/ely_design_system/src/spacing.rs @@ -6,6 +6,18 @@ pub const MD: f32 = 20.0; pub const LG: f32 = 24.0; pub const XL: f32 = 32.0; pub const XXL: f32 = 48.0; -pub const SIDEBAR_WIDTH: f32 = 280.0; + +pub const SIDEBAR_WIDTH: f32 = 256.0; pub const SIDEBAR_COLLAPSED_WIDTH: f32 = 56.0; -pub const COMMAND_BAR_HEIGHT: f32 = 64.0; +pub const SHELL_INSET: f32 = 16.0; +pub const SIDEBAR_MAIN_GAP: f32 = 12.0; +pub const TOPBAR_HEIGHT: f32 = 54.0; +pub const OMNIBAR_HEIGHT: f32 = 36.0; +pub const COMMAND_BAR_HEIGHT: f32 = 54.0; + +pub const RADIUS_CARD: f32 = 18.0; +pub const RADIUS_PANE: f32 = 14.0; +pub const RADIUS_PILL: f32 = 999.0; +pub const RADIUS_NAV: f32 = 8.0; +pub const RADIUS_BUTTON: f32 = 8.0; +pub const RADIUS_INPUT: f32 = 9.0; diff --git a/crates/ely_design_system/src/typography.rs b/crates/ely_design_system/src/typography.rs index 9670326..0147305 100644 --- a/crates/ely_design_system/src/typography.rs +++ b/crates/ely_design_system/src/typography.rs @@ -5,10 +5,14 @@ pub struct TypeToken { pub weight: u16, } -pub const DISPLAY_MD: TypeToken = TypeToken { size_px: 26.0, line_height: 1.25, weight: 400 }; - -pub const TITLE_MD: TypeToken = TypeToken { size_px: 18.0, line_height: 1.4, weight: 600 }; - -pub const BODY_MD: TypeToken = TypeToken { size_px: 16.0, line_height: 1.5, weight: 400 }; - -pub const CAPTION: TypeToken = TypeToken { size_px: 13.0, line_height: 1.4, weight: 400 }; +pub const HERO: TypeToken = TypeToken { size_px: 64.0, line_height: 1.05, weight: 400 }; +pub const DISPLAY_LG: TypeToken = TypeToken { size_px: 48.0, line_height: 1.05, weight: 400 }; +pub const DISPLAY_MD: TypeToken = TypeToken { size_px: 34.0, line_height: 1.1, weight: 400 }; +pub const TITLE_LG: TypeToken = TypeToken { size_px: 22.0, line_height: 1.3, weight: 400 }; +pub const TITLE_MD: TypeToken = TypeToken { size_px: 18.0, line_height: 1.4, weight: 500 }; +pub const BODY_LG: TypeToken = TypeToken { size_px: 16.0, line_height: 1.5, weight: 400 }; +pub const BODY_MD: TypeToken = TypeToken { size_px: 14.0, line_height: 1.5, weight: 400 }; +pub const LABEL: TypeToken = TypeToken { size_px: 13.0, line_height: 1.4, weight: 500 }; +pub const CAPTION: TypeToken = TypeToken { size_px: 12.0, line_height: 1.4, weight: 500 }; +pub const SECTION_LABEL: TypeToken = TypeToken { size_px: 10.5, line_height: 1.4, weight: 500 }; +pub const META: TypeToken = TypeToken { size_px: 10.0, line_height: 1.4, weight: 500 };