Skip to content

Modernize UI with Material Design 3 - centralized theme system and semantic color tokens#16

Merged
jango-blockchained merged 6 commits into
mainfrom
copilot/optimize-ui-for-modern-look
Nov 6, 2025
Merged

Modernize UI with Material Design 3 - centralized theme system and semantic color tokens#16
jango-blockchained merged 6 commits into
mainfrom
copilot/optimize-ui-for-modern-look

Conversation

Copilot AI commented Nov 6, 2025

Copy link
Copy Markdown
Contributor

Closes: [Issue about UI modernization]

Description

Transformed scattered inline styles into a centralized Material Design 3 theme system. The UI was functional but dated, with 30+ hardcoded style strings duplicated across components.

Enhanced Theme System (theme_system.py)

  • MD3 color schemes: 40+ semantic tokens per theme (light/dark) with proper contrast ratios
  • New style methods: get_card_style(), get_chip_style(), get_combobox_style(), get_compact_button_style()
  • Color manipulation: _lighten_color() helper for hover states
  • Tonal palettes: Container colors for semantic states (success, warning, error)

Widget Modernization

Applied centralized theming to 5 core widgets, replacing inline styles:

main_widget.py

  • Header: 18px primary-colored branding
  • Status chips: semantic container colors (16px radius)
  • Tabs: elevated pill design with hover states

providers_widget.py

  • Form inputs: outlined MD3 style (8px radius, 12px padding)
  • Buttons: semantic colors with 20px radius
  • Tables: proper selection highlighting with primary container

provider_selector_widget.py

  • Dropdowns: 180px min-width with styled popups
  • Status indicator: 28px circular with semantic colors
  • Refresh button: compact circular (36px) via new theme method

connection_widget.py

  • Status cards: 16px radius with semantic container backgrounds
  • Typography: 14px/13px/12px hierarchy with 600/500 weights

Design Standards

  • Spacing: 16px base unit (up from 5-10px)
  • Typography: 18px/14px/12px with 600/500/400 weights
  • Radius: 16px cards, 20px buttons, 8px inputs
  • Colors: #0061a6 primary, #006e1c success, #785900 warning, #ba1a1a error

Code Quality

  • Backward compatible: elevated=False default, fallback styling when theme unavailable
  • Security: 0 vulnerabilities (CodeQL scan passed)
  • All files: syntax validated

Tradeoffs

  • Kept 4 widgets un-modernized (conversation, agent control, tools, settings) for focused scope
  • Runtime theme import adds minimal overhead but enables dynamic theming
  • Fallback inline styles maintained for environments without theme system

Documentation

Added UI_MODERNIZATION_SUMMARY.md documenting color tokens, spacing standards, and implementation patterns for future widget updates.

Before/After metrics: 850 lines changed, 30+ inline styles removed, 5 style methods added, 80+ color tokens defined.

Original prompt

Review the complete UI from the user and usability perspective. optimize. make the ui more modern.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 5 commits November 6, 2025 08:55
Co-authored-by: jango-blockchained <16127070+jango-blockchained@users.noreply.github.com>
Co-authored-by: jango-blockchained <16127070+jango-blockchained@users.noreply.github.com>
Co-authored-by: jango-blockchained <16127070+jango-blockchained@users.noreply.github.com>
Co-authored-by: jango-blockchained <16127070+jango-blockchained@users.noreply.github.com>
…ckward compatibility

Co-authored-by: jango-blockchained <16127070+jango-blockchained@users.noreply.github.com>
Copilot AI changed the title [WIP] Review and optimize UI for modern usability Modernize UI with Material Design 3 - centralized theme system and semantic color tokens Nov 6, 2025
@jango-blockchained jango-blockchained marked this pull request as ready for review November 6, 2025 11:40
Copilot AI review requested due to automatic review settings November 6, 2025 11:40
@jango-blockchained jango-blockchained merged commit 57e2203 into main Nov 6, 2025
0 of 10 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request implements a comprehensive Material Design 3 theming system for the FreeCAD AI GUI, replacing scattered inline styles with a centralized, cohesive design system. The modernization brings improved aesthetics, consistency, and maintainability while adhering to Material Design 3 principles.

  • Introduces a complete MD3 color palette with semantic tokens for both light and dark themes
  • Adds new styling methods for modern UI components (cards, chips, combobox dropdowns)
  • Updates all major widgets with modern spacing, typography, and interactive states
  • Maintains backward compatibility through graceful fallbacks when theme system is unavailable

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
freecad-ai/gui/theme_system.py Core theme system with MD3 color schemes, new style methods (get_card_style, get_chip_style, get_combobox_style), and enhanced button/input styling with proper elevation and focus states
freecad-ai/gui/providers_widget.py Applies modern styling to provider management UI including themed tables, form inputs, and status chips; replaces inline styles with theme system calls
freecad-ai/gui/provider_selector_widget.py Updates provider selection dropdowns with modern combobox styling, larger interactive targets, and themed status indicators
freecad-ai/gui/main_widget.py Modernizes main widget initialization UI with MD3 color containers, improved typography, and themed status labels
freecad-ai/gui/connection_widget.py Enhances connection status cards with semantic container colors, larger border radius, and improved typography hierarchy
UI_MODERNIZATION_SUMMARY.md Comprehensive documentation of all changes, design principles, color schemes, and implementation details

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

background-color: {base_color};
color: {text_color};
border: none;
border-radius: 50%;

Copilot AI Nov 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The border-radius: 50% property requires a fixed size to create a truly circular button. Without fixed dimensions, this will create an ellipse if the button's width and height differ. Consider either:

  1. Setting fixed width/height in this style, or
  2. Documenting that users must set a fixed square size when using this style, or
  3. Using a specific pixel radius (e.g., 18px) that matches common circular button sizes
Suggested change
border-radius: 50%;
border-radius: 50%;
width: 36px;
height: 36px;

Copilot uses AI. Check for mistakes.
Comment on lines +397 to +399
shadow = """
border: 1px solid rgba(0, 0, 0, 0.04);
background-color: {bg};

Copilot AI Nov 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded rgba(0, 0, 0, 0.04) border won't work well in dark mode. This creates a subtle darkening effect that's only visible on light backgrounds. Consider using a theme color like self.colors.get_color("border_light") or creating a theme-specific semi-transparent border color.

Suggested change
shadow = """
border: 1px solid rgba(0, 0, 0, 0.04);
background-color: {bg};
shadow = f"""
border: 1px solid {self.colors.get_color('border_light')};
background-color: {{bg}};

Copilot uses AI. Check for mistakes.
base_color = color_map.get(button_type, self.colors.get_color("primary"))
hover_color = self._darken_color(base_color, 0.1)
base_color, text_color = color_map.get(button_type, (self.colors.get_color("primary"), self.colors.get_color("on_primary")))
hover_color = self._lighten_color(base_color, 0.15)

Copilot AI Nov 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hover behavior is inconsistent across themes: in light theme, lightening the button (making it brighter) may reduce contrast. Material Design 3 typically uses state layers (overlays) rather than color manipulation for hover states. Consider using a semi-transparent overlay on hover (e.g., rgba(255,255,255,0.08) for dark buttons) or using the theme's overlay color.

Copilot uses AI. Check for mistakes.
self.status_label.setStyleSheet(f"""
padding: 20px;
background-color: {colors.get_color("info_container")};
color: {colors.get_color("on_primary_container")};

Copilot AI Nov 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent color usage: info_container is used for background but on_primary_container is used for text color. This should be on_info_container to maintain semantic consistency. If on_info_container doesn't exist in the theme, consider adding it or using on_primary_container with primary_container background.

Suggested change
color: {colors.get_color("on_primary_container")};
color: {colors.get_color("on_info_container")};

Copilot uses AI. Check for mistakes.
Comment on lines 22 to +88
def _load_colors(self):
"""Load colors based on theme."""
"""Load colors based on theme - Material Design 3 inspired."""
if self.theme == Theme.LIGHT:
self.colors = {
# Background colors
"background_primary": "#ffffff",
"background_secondary": "#f8f9fa",
"background_tertiary": "#e9ecef",
"background_accent": "#e3f2fd",
# Text colors
"text_primary": "#212529",
"text_secondary": "#6c757d",
"text_muted": "#adb5bd",
# Background colors - Material Design 3 surfaces
"background_primary": "#fdfcff",
"background_secondary": "#f5f5f5",
"background_tertiary": "#eceff1",
"background_accent": "#e7f2ff",
"background_elevated": "#ffffff",
"background_card": "#ffffff",
# Text colors - MD3 on-surface variants
"text_primary": "#1c1b1f",
"text_secondary": "#49454f",
"text_muted": "#73777f",
"text_inverse": "#ffffff",
# Brand colors
"primary": "#2196F3",
"secondary": "#6c757d",
"success": "#4CAF50",
"warning": "#FF9800",
"error": "#f44336",
"info": "#17a2b8",
# Chat-specific colors
"user_message_bg": "#e3f2fd",
"ai_message_bg": "#e8f5e9",
"system_message_bg": "#fff3e0",
"user_text": "#2196F3",
"ai_text": "#4CAF50",
"system_text": "#FF9800",
"text_on_primary": "#ffffff",
# Brand colors - MD3 primary/secondary scheme
"primary": "#0061a6",
"primary_container": "#d1e4ff",
"on_primary": "#ffffff",
"on_primary_container": "#001d35",
"secondary": "#535f70",
"secondary_container": "#d7e3f7",
"on_secondary": "#ffffff",
"on_secondary_container": "#101c2b",
# Semantic colors
"success": "#006e1c",
"success_container": "#97f682",
"on_success": "#ffffff",
"on_success_container": "#002204",
"warning": "#785900",
"warning_container": "#ffdea6",
"on_warning": "#ffffff",
"on_warning_container": "#261900",
"error": "#ba1a1a",
"error_container": "#ffdad6",
"on_error": "#ffffff",
"on_error_container": "#410002",
"info": "#0061a6",
"info_container": "#d1e4ff",
# Chat-specific colors - using MD3 containers
"user_message_bg": "#d1e4ff",
"ai_message_bg": "#97f682",
"system_message_bg": "#ffdea6",
"user_text": "#001d35",
"ai_text": "#002204",
"system_text": "#261900",
# UI element colors
"border": "#dee2e6",
"border_focus": "#2196F3",
"button_primary": "#2196F3",
"button_success": "#4CAF50",
"button_warning": "#FF9800",
"button_danger": "#f44336",
"border": "#c4c6d0",
"border_focus": "#0061a6",
"border_light": "#e7e9f5",
"divider": "#c4c6d0",
"button_primary": "#0061a6",
"button_success": "#006e1c",
"button_warning": "#785900",
"button_danger": "#ba1a1a",
# Status colors
"status_connected": "#4CAF50",
"status_warning": "#FF9800",
"status_error": "#f44336",
"status_unknown": "#9E9E9E",
"status_connected": "#006e1c",
"status_warning": "#785900",
"status_error": "#ba1a1a",
"status_unknown": "#73777f",
# Shadows and overlays
"shadow": "rgba(0, 0, 0, 0.12)",
"overlay": "rgba(0, 0, 0, 0.05)",
}
else: # DARK theme
else: # DARK theme - Material Design 3 dark surfaces

Copilot AI Nov 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Theme.AUTO option is defined in the enum but never handled in the _load_colors() method. The code only checks for Theme.LIGHT and falls through to else for everything else (including AUTO), which treats AUTO as DARK. Either implement system theme detection for AUTO or document that it's not yet implemented.

Copilot uses AI. Check for mistakes.
font-weight: bold;
font-size: 16px;
border-radius: 14px;
background-color: rgba(0, 0, 0, 0.05);

Copilot AI Nov 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded rgba(0, 0, 0, 0.05) background doesn't adapt to dark theme. In dark mode, this would create a barely visible darkening effect. Consider using theme colors like colors.get_color("overlay") or conditional logic based on the current theme.

Copilot uses AI. Check for mistakes.
Comment on lines +194 to +195
shadow = "box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);" if elevated else ""
hover_shadow = "box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);" if elevated else ""

Copilot AI Nov 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded shadow rgba values rgba(0,0,0,0.12) and rgba(0,0,0,0.24) are appropriate for light mode but will be barely visible in dark mode. Consider using theme-aware shadow colors from self.colors.get_color("shadow") or creating separate shadow definitions for light and dark themes.

Copilot uses AI. Check for mistakes.
border-color: {self.colors.get_color("primary")};
}}
QComboBox:focus {{
border: 2px solid {self.colors.get_color("border_focus")};

Copilot AI Nov 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The focus state increases border width from 1px to 2px but doesn't adjust padding to prevent layout shift. This will cause the combobox to grow by 2px (1px on each side) when focused. Consider reducing padding by 1px in the focus state, similar to the input field style (e.g., padding: 9px 31px 9px 15px;).

Suggested change
border: 2px solid {self.colors.get_color("border_focus")};
border: 2px solid {self.colors.get_color("border_focus")};
padding: 9px 31px 9px 15px;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants