--- name: gpui-styling description: GPUI styling system including theme design, responsive layouts, visual design patterns, and style composition. Use when user needs help with styling, theming, or visual design in GPUI. --- # GPUI Styling ## Metadata This skill provides comprehensive guidance on GPUI's styling system, theme management, and visual design patterns for creating beautiful, consistent user interfaces. ## Instructions ### Styling API Fundamentals #### Basic Styling ```rust use gpui::*; div() // Colors .bg(rgb(0x2563eb)) // Background .text_color(white()) // Text color .border_color(rgb(0xe5e7eb)) // Border color // Spacing .p_4() // Padding: 1rem .px_6() // Padding horizontal .py_2() // Padding vertical .m_4() // Margin .gap_3() // Gap between children // Sizing .w_64() // Width: 16rem .h_32() // Height: 8rem .w_full() // Width: 100% .h_full() // Height: 100% // Borders .border_1() // Border: 1px .rounded_lg() // Border radius: large ``` #### Color Types ```rust // RGB from hex let blue = rgb(0x2563eb); // RGBA with alpha let transparent_blue = rgba(0x2563eb, 0.5); // HSLA (hue, saturation, lightness, alpha) let hsla_color = hsla(0.6, 0.8, 0.5, 1.0); // Named colors let white = white(); let black = black(); ``` #### Layout with Flexbox ```rust div() .flex() // Enable flexbox .flex_row() // Horizontal layout .flex_col() // Vertical layout .items_center() // Align items center .justify_between() // Space between .gap_4() // Gap between items .child(/* ... */) .child(/* ... */) ``` ### Theme System #### Basic Theme Structure ```rust use gpui::*; #[derive(Clone)] pub struct AppTheme { pub colors: ThemeColors, pub typography: Typography, pub spacing: Spacing, pub shadows: Shadows, } #[derive(Clone)] pub struct ThemeColors { // Base colors pub background: Hsla, pub foreground: Hsla, // UI colors pub primary: Hsla, pub primary_foreground: Hsla, pub primary_hover: Hsla, pub secondary: Hsla, pub secondary_foreground: Hsla, pub secondary_hover: Hsla, pub accent: Hsla, pub accent_foreground: Hsla, pub destructive: Hsla, pub destructive_foreground: Hsla, // Neutral colors pub muted: Hsla, pub muted_foreground: Hsla, pub border: Hsla, pub input: Hsla, pub ring: Hsla, } #[derive(Clone)] pub struct Typography { pub font_sans: Vec, pub font_mono: Vec, pub text_xs: Pixels, pub text_sm: Pixels, pub text_base: Pixels, pub text_lg: Pixels, pub text_xl: Pixels, pub text_2xl: Pixels, } #[derive(Clone)] pub struct Spacing { pub xs: Pixels, pub sm: Pixels, pub md: Pixels, pub lg: Pixels, pub xl: Pixels, } ``` #### Light Theme Implementation ```rust impl AppTheme { pub fn light() -> Self { Self { colors: ThemeColors { background: rgb(0xffffff), foreground: rgb(0x0a0a0a), primary: rgb(0x2563eb), primary_foreground: rgb(0xffffff), primary_hover: rgb(0x1d4ed8), secondary: rgb(0xf1f5f9), secondary_foreground: rgb(0x0f172a), secondary_hover: rgb(0xe2e8f0), accent: rgb(0xf1f5f9), accent_foreground: rgb(0x0f172a), destructive: rgb(0xef4444), destructive_foreground: rgb(0xffffff), muted: rgb(0xf1f5f9), muted_foreground: rgb(0x64748b), border: rgb(0xe2e8f0), input: rgb(0xe2e8f0), ring: rgb(0x2563eb), }, typography: Typography { font_sans: vec![ "Inter".to_string(), "system-ui".to_string(), "sans-serif".to_string(), ], font_mono: vec![ "JetBrains Mono".to_string(), "monospace".to_string(), ], text_xs: px(12.0), text_sm: px(14.0), text_base: px(16.0), text_lg: px(18.0), text_xl: px(20.0), text_2xl: px(24.0), }, spacing: Spacing { xs: px(4.0), sm: px(8.0), md: px(16.0), lg: px(24.0), xl: px(32.0), }, shadows: Shadows { sm: Shadow::new(px(1.0), rgba(0x000000, 0.05)), md: Shadow::new(px(4.0), rgba(0x000000, 0.1)), lg: Shadow::new(px(8.0), rgba(0x000000, 0.15)), }, } } } ``` #### Dark Theme Implementation ```rust impl AppTheme { pub fn dark() -> Self { Self { colors: ThemeColors { background: rgb(0x0a0a0a), foreground: rgb(0xfafafa), primary: rgb(0x3b82f6), primary_foreground: rgb(0xffffff), primary_hover: rgb(0x2563eb), secondary: rgb(0x1e293b), secondary_foreground: rgb(0xf1f5f9), secondary_hover: rgb(0x334155), accent: rgb(0x1e293b), accent_foreground: rgb(0xf1f5f9), destructive: rgb(0xef4444), destructive_foreground: rgb(0xffffff), muted: rgb(0x1e293b), muted_foreground: rgb(0x94a3b8), border: rgb(0x334155), input: rgb(0x334155), ring: rgb(0x3b82f6), }, typography: Typography { font_sans: vec![ "Inter".to_string(), "system-ui".to_string(), "sans-serif".to_string(), ], font_mono: vec![ "JetBrains Mono".to_string(), "monospace".to_string(), ], text_xs: px(12.0), text_sm: px(14.0), text_base: px(16.0), text_lg: px(18.0), text_xl: px(20.0), text_2xl: px(24.0), }, spacing: Spacing { xs: px(4.0), sm: px(8.0), md: px(16.0), lg: px(24.0), xl: px(32.0), }, shadows: Shadows { sm: Shadow::new(px(1.0), rgba(0x000000, 0.2)), md: Shadow::new(px(4.0), rgba(0x000000, 0.3)), lg: Shadow::new(px(8.0), rgba(0x000000, 0.4)), }, } } } ``` #### Using Themes in Components ```rust impl Render for ThemedComponent { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let theme = cx.global::(); div() .bg(theme.colors.background) .text_color(theme.colors.foreground) .p(theme.spacing.md) .border_1() .border_color(theme.colors.border) .child("Themed content") } } ``` #### Theme Switching ```rust pub fn toggle_theme(cx: &mut AppContext) { let current = cx.global::().clone(); let new_theme = match current.mode { ThemeMode::Light => AppTheme::dark(), ThemeMode::Dark => AppTheme::light(), }; cx.set_global(new_theme); cx.refresh(); } ``` ### Responsive Design #### Window Size Detection ```rust impl Render for ResponsiveView { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let window_size = cx.window_bounds().get_bounds().size; let is_mobile = window_size.width < px(768.0); let is_tablet = window_size.width >= px(768.0) && window_size.width < px(1024.0); let is_desktop = window_size.width >= px(1024.0); div() .flex() .when(is_mobile, |this| { this.flex_col().gap_2() }) .when(is_desktop, |this| { this.flex_row().gap_6() }) .child(sidebar()) .child(main_content()) } } ``` #### Breakpoint-Based Styling ```rust pub struct Breakpoints; impl Breakpoints { pub const SM: f32 = 640.0; pub const MD: f32 = 768.0; pub const LG: f32 = 1024.0; pub const XL: f32 = 1280.0; pub const XXL: f32 = 1536.0; } fn responsive_grid(width: Pixels) -> impl IntoElement { div() .grid() .when(width.0 < Breakpoints::SM, |this| this.grid_cols_1()) .when(width.0 >= Breakpoints::SM && width.0 < Breakpoints::LG, |this| { this.grid_cols_2() }) .when(width.0 >= Breakpoints::LG, |this| this.grid_cols_3()) .gap_4() } ``` ### Visual Design Patterns #### Card Component ```rust pub fn card( title: impl Into, description: impl Into, content: impl IntoElement, ) -> impl IntoElement { let theme = cx.global::(); div() .bg(theme.colors.background) .border_1() .border_color(theme.colors.border) .rounded_lg() .shadow_sm() .overflow_hidden() .child( div() .p_6() .border_b_1() .border_color(theme.colors.border) .child( div() .text_lg() .font_semibold() .child(title.into()) ) .child( div() .text_sm() .text_color(theme.colors.muted_foreground) .child(description.into()) ) ) .child( div() .p_6() .child(content) ) } ``` #### Button Variants ```rust pub enum ButtonVariant { Primary, Secondary, Outline, Ghost, Destructive, } pub fn button( label: &str, variant: ButtonVariant, ) -> impl IntoElement { let theme = cx.global::(); let (bg, fg, hover_bg) = match variant { ButtonVariant::Primary => ( theme.colors.primary, theme.colors.primary_foreground, theme.colors.primary_hover, ), ButtonVariant::Secondary => ( theme.colors.secondary, theme.colors.secondary_foreground, theme.colors.secondary_hover, ), ButtonVariant::Outline => ( hsla(0.0, 0.0, 0.0, 0.0), theme.colors.foreground, theme.colors.accent, ), ButtonVariant::Ghost => ( hsla(0.0, 0.0, 0.0, 0.0), theme.colors.foreground, theme.colors.accent, ), ButtonVariant::Destructive => ( theme.colors.destructive, theme.colors.destructive_foreground, theme.colors.destructive, ), }; div() .px_4() .py_2() .bg(bg) .text_color(fg) .rounded_md() .font_medium() .cursor_pointer() .when(matches!(variant, ButtonVariant::Outline), |this| { this.border_1().border_color(theme.colors.border) }) .hover(|this| this.bg(hover_bg)) .transition_colors() .duration_150() .child(label) } ``` #### Input Fields ```rust pub fn text_input( value: &str, placeholder: &str, ) -> impl IntoElement { let theme = cx.global::(); div() .flex() .items_center() .w_full() .px_3() .py_2() .bg(theme.colors.background) .border_1() .border_color(theme.colors.input) .rounded_md() .text_color(theme.colors.foreground) .focus(|this| { this.border_color(theme.colors.ring) .ring_2() .ring_color(rgba(theme.colors.ring, 0.2)) }) .child( input() .w_full() .bg(hsla(0.0, 0.0, 0.0, 0.0)) .placeholder(placeholder) .value(value) ) } ``` ### Style Composition #### Reusable Style Functions ```rust pub fn focus_ring(theme: &AppTheme) -> StyleRefinement { StyleRefinement::default() .ring_2() .ring_color(rgba(theme.colors.ring, 0.2)) .border_color(theme.colors.ring) } pub fn shadow_sm(theme: &AppTheme) -> StyleRefinement { StyleRefinement::default() .shadow(theme.shadows.sm) } // Usage div() .apply(focus_ring(&theme)) .apply(shadow_sm(&theme)) .child("Styled element") ``` #### Conditional Styles ```rust fn dynamic_button( label: &str, is_loading: bool, is_disabled: bool, ) -> impl IntoElement { let theme = cx.global::(); div() .px_4() .py_2() .bg(theme.colors.primary) .text_color(theme.colors.primary_foreground) .rounded_md() .when(is_disabled || is_loading, |this| { this.opacity(0.5).cursor_not_allowed() }) .when(!is_disabled && !is_loading, |this| { this.cursor_pointer() .hover(|this| this.bg(theme.colors.primary_hover)) }) .child( if is_loading { "Loading..." } else { label } ) } ``` ### Animation and Transitions #### Hover Transitions ```rust div() .transition_all() // Transition all properties .duration_200() // 200ms duration .bg(blue_500()) .hover(|this| { this.bg(blue_600()) .scale_105() // Scale to 105% }) .child("Hover me") ``` #### Transform Animations ```rust div() .transition_transform() .duration_300() .ease_in_out() .hover(|this| { this.rotate(5.0) // Rotate 5 degrees .translate_y(px(-2.0)) // Move up 2px }) .child("Animated element") ``` ## Resources ### Color Systems - Use HSL for color manipulation - Maintain consistent color contrast ratios - Define semantic color names (primary, secondary, etc.) - Support both light and dark themes ### Typography Scale - Base: 16px (1rem) - Scale: 1.125 (Major Second) or 1.2 (Minor Third) - Sizes: xs, sm, base, lg, xl, 2xl, etc. - Weights: normal, medium, semibold, bold ### Spacing Scale - Base unit: 4px or 8px - Multipliers: 0.5, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32 - Consistent throughout application - Used for padding, margin, gap ### Best Practices - Define theme at app level - Use semantic color names - Implement both light and dark themes - Support responsive design - Maintain consistent spacing - Use transitions for smooth interactions - Ensure accessibility (contrast, focus indicators) - Document theme structure