EU-Utility/core/eu_styles.py

888 lines
27 KiB
Python

"""
EU-Utility - Enhanced EU Styling System
Complete design system with:
- EU game aesthetic matching (dark sci-fi theme)
- Dark/Light theme support
- Responsive layout helpers
- Animation/transition utilities
- Accessibility features
"""
from PyQt6.QtCore import Qt, QPropertyAnimation, QEasingCurve, QParallelAnimationGroup, QSequentialAnimationGroup
from PyQt6.QtWidgets import QWidget, QGraphicsOpacityEffect
# ============================================================================
# COLOR SYSTEM - Dark/Light Theme Support
# ============================================================================
class EUTheme:
"""Theme manager for dark/light mode support."""
_current_theme = "dark"
@classmethod
def set_theme(cls, theme: str):
"""Set current theme ('dark' or 'light')."""
cls._current_theme = theme
@classmethod
def get_theme(cls) -> str:
"""Get current theme name."""
return cls._current_theme
@classmethod
def is_dark(cls) -> bool:
"""Check if dark theme is active."""
return cls._current_theme == "dark"
# Dark Theme - Primary (matches EU game aesthetic)
EU_DARK_COLORS = {
# Backgrounds - layered depth
'bg_primary': '#0d1117',
'bg_secondary': '#161b22',
'bg_tertiary': '#21262d',
'bg_elevated': '#1c2128',
'bg_overlay': 'rgba(13, 17, 23, 0.95)',
'bg_hover': 'rgba(48, 54, 61, 0.6)',
'bg_pressed': 'rgba(33, 38, 45, 0.8)',
'bg_selected': 'rgba(255, 140, 66, 0.15)',
# EU Game Accent Colors
'accent_orange': '#ff8c42',
'accent_orange_hover': '#ffa060',
'accent_orange_pressed': '#e67a35',
'accent_teal': '#4ecdc4',
'accent_gold': '#ffc107',
'accent_blue': '#4a9eff',
'accent_green': '#4caf50',
'accent_red': '#f44336',
'accent_purple': '#9c27b0',
# Text colors
'text_primary': '#f0f6fc',
'text_secondary': '#8b949e',
'text_muted': '#6e7681',
'text_disabled': '#484f58',
'text_inverse': '#0d1117',
# Border colors
'border_default': '#30363d',
'border_hover': '#8b949e',
'border_focus': '#ff8c42',
'border_active': '#4ecdc4',
# Status colors
'status_success': '#4caf50',
'status_warning': '#ffc107',
'status_error': '#f44336',
'status_info': '#4a9eff',
# Progress bars
'progress_bg': '#21262d',
'progress_fill': '#4ecdc4',
'progress_fill_alt': '#ff8c42',
}
# Light Theme
EU_LIGHT_COLORS = {
# Backgrounds
'bg_primary': '#ffffff',
'bg_secondary': '#f6f8fa',
'bg_tertiary': '#eaeef2',
'bg_elevated': '#ffffff',
'bg_overlay': 'rgba(255, 255, 255, 0.95)',
'bg_hover': 'rgba(234, 238, 242, 0.8)',
'bg_pressed': 'rgba(208, 215, 222, 0.8)',
'bg_selected': 'rgba(255, 140, 66, 0.1)',
# EU Game Accent Colors (same for brand consistency)
'accent_orange': '#ff8c42',
'accent_orange_hover': '#e67a35',
'accent_orange_pressed': '#cc6a2f',
'accent_teal': '#2d9d96',
'accent_gold': '#d4a017',
'accent_blue': '#2563eb',
'accent_green': '#2d8a3e',
'accent_red': '#dc2626',
'accent_purple': '#7c3aed',
# Text colors
'text_primary': '#24292f',
'text_secondary': '#57606a',
'text_muted': '#6e7781',
'text_disabled': '#8c959f',
'text_inverse': '#ffffff',
# Border colors
'border_default': '#d0d7de',
'border_hover': '#8c959f',
'border_focus': '#ff8c42',
'border_active': '#2d9d96',
# Status colors
'status_success': '#2d8a3e',
'status_warning': '#d4a017',
'status_error': '#dc2626',
'status_info': '#2563eb',
# Progress bars
'progress_bg': '#eaeef2',
'progress_fill': '#2d9d96',
'progress_fill_alt': '#ff8c42',
}
def get_color(name: str) -> str:
"""Get color by name for current theme."""
colors = EU_DARK_COLORS if EUTheme.is_dark() else EU_LIGHT_COLORS
return colors.get(name, EU_DARK_COLORS.get(name, '#ffffff'))
def get_all_colors() -> dict:
"""Get all colors for current theme."""
return EU_DARK_COLORS if EUTheme.is_dark() else EU_LIGHT_COLORS
# ============================================================================
# TYPOGRAPHY
# ============================================================================
EU_TYPOGRAPHY = {
'font_family': '"Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif',
'font_mono': '"JetBrains Mono", "Fira Code", "Consolas", monospace',
'size_xs': '11px',
'size_sm': '12px',
'size_base': '13px',
'size_md': '14px',
'size_lg': '16px',
'size_xl': '18px',
'size_2xl': '20px',
'size_3xl': '24px',
'weight_normal': '400',
'weight_medium': '500',
'weight_semibold': '600',
'weight_bold': '700',
'line_tight': '1.25',
'line_normal': '1.5',
'line_relaxed': '1.75',
}
# ============================================================================
# SPACING & SIZING
# ============================================================================
EU_SPACING = {
'xs': '4px',
'sm': '8px',
'md': '12px',
'lg': '16px',
'xl': '20px',
'2xl': '24px',
'3xl': '32px',
'4xl': '40px',
}
EU_SIZES = {
'radius_sm': '4px',
'radius_md': '6px',
'radius_lg': '8px',
'radius_xl': '12px',
'radius_full': '9999px',
'shadow_sm': '0 1px 2px rgba(0,0,0,0.1)',
'shadow_md': '0 4px 6px rgba(0,0,0,0.15)',
'shadow_lg': '0 10px 15px rgba(0,0,0,0.2)',
'shadow_glow': '0 0 20px rgba(255, 140, 66, 0.3)',
'shadow_glow_teal': '0 0 20px rgba(78, 205, 196, 0.3)',
}
# ============================================================================
# COMPONENT STYLES
# ============================================================================
def get_button_style(variant: str = "primary", size: str = "md") -> str:
"""
Get button stylesheet.
Variants: primary, secondary, ghost, danger, success
Sizes: sm, md, lg
"""
c = get_all_colors()
# Size configurations
sizes = {
'sm': {'padding': '6px 12px', 'font_size': EU_TYPOGRAPHY['size_xs']},
'md': {'padding': '8px 16px', 'font_size': EU_TYPOGRAPHY['size_sm']},
'lg': {'padding': '12px 24px', 'font_size': EU_TYPOGRAPHY['size_base']},
}
sz = sizes.get(size, sizes['md'])
# Variant configurations
variants = {
'primary': {
'bg': c['accent_orange'],
'color': '#ffffff',
'border': c['accent_orange'],
'hover_bg': c['accent_orange_hover'],
'hover_border': c['accent_orange_hover'],
'pressed_bg': c['accent_orange_pressed'],
},
'secondary': {
'bg': c['bg_tertiary'],
'color': c['text_primary'],
'border': c['border_default'],
'hover_bg': c['bg_hover'],
'hover_border': c['border_hover'],
'pressed_bg': c['bg_pressed'],
},
'ghost': {
'bg': 'transparent',
'color': c['text_secondary'],
'border': 'transparent',
'hover_bg': c['bg_hover'],
'hover_border': 'transparent',
'pressed_bg': c['bg_pressed'],
},
'danger': {
'bg': c['accent_red'],
'color': '#ffffff',
'border': c['accent_red'],
'hover_bg': '#dc2626',
'hover_border': '#dc2626',
'pressed_bg': '#b91c1c',
},
'success': {
'bg': c['accent_green'],
'color': '#ffffff',
'border': c['accent_green'],
'hover_bg': '#2d8a3e',
'hover_border': '#2d8a3e',
'pressed_bg': '#1f6b2c',
},
}
v = variants.get(variant, variants['primary'])
return f"""
QPushButton {{
background-color: {v['bg']};
color: {v['color']};
border: 1px solid {v['border']};
border-radius: {EU_SIZES['radius_md']};
padding: {sz['padding']};
font-size: {sz['font_size']};
font-weight: {EU_TYPOGRAPHY['weight_medium']};
font-family: {EU_TYPOGRAPHY['font_family']};
outline: none;
}}
QPushButton:hover {{
background-color: {v['hover_bg']};
border-color: {v['hover_border']};
}}
QPushButton:pressed {{
background-color: {v['pressed_bg']};
}}
QPushButton:disabled {{
background-color: {c['bg_tertiary']};
color: {c['text_disabled']};
border-color: {c['border_default']};
}}
QPushButton:focus {{
border-color: {c['border_focus']};
}}
"""
def get_input_style() -> str:
"""Get input field stylesheet."""
c = get_all_colors()
return f"""
QLineEdit, QTextEdit, QPlainTextEdit {{
background-color: {c['bg_secondary']};
color: {c['text_primary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_md']};
padding: 8px 12px;
font-size: {EU_TYPOGRAPHY['size_base']};
font-family: {EU_TYPOGRAPHY['font_family']};
selection-background-color: {c['accent_orange']};
selection-color: #ffffff;
}}
QLineEdit:hover, QTextEdit:hover, QPlainTextEdit:hover {{
border-color: {c['border_hover']};
}}
QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {{
border-color: {c['border_focus']};
background-color: {c['bg_primary']};
}}
QLineEdit:disabled, QTextEdit:disabled, QPlainTextEdit:disabled {{
background-color: {c['bg_tertiary']};
color: {c['text_disabled']};
}}
"""
def get_combo_style() -> str:
"""Get combobox/dropdown stylesheet."""
c = get_all_colors()
return f"""
QComboBox {{
background-color: {c['bg_secondary']};
color: {c['text_primary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_md']};
padding: 8px 12px;
min-width: 120px;
font-size: {EU_TYPOGRAPHY['size_base']};
}}
QComboBox:hover {{
border-color: {c['border_hover']};
}}
QComboBox:focus {{
border-color: {c['border_focus']};
}}
QComboBox::drop-down {{
border: none;
width: 24px;
}}
QComboBox::down-arrow {{
image: none;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid {c['text_secondary']};
}}
QComboBox QAbstractItemView {{
background-color: {c['bg_elevated']};
color: {c['text_primary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_md']};
selection-background-color: {c['bg_selected']};
selection-color: {c['text_primary']};
padding: 4px;
}}
"""
def get_table_style() -> str:
"""Get table widget stylesheet."""
c = get_all_colors()
return f"""
QTableWidget {{
background-color: {c['bg_secondary']};
color: {c['text_primary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_lg']};
gridline-color: {c['border_default']};
font-size: {EU_TYPOGRAPHY['size_sm']};
}}
QTableWidget::item {{
padding: 10px 12px;
border-bottom: 1px solid {c['border_default']};
}}
QTableWidget::item:selected {{
background-color: {c['bg_selected']};
color: {c['text_primary']};
}}
QTableWidget::item:hover {{
background-color: {c['bg_hover']};
}}
QHeaderView::section {{
background-color: {c['bg_tertiary']};
color: {c['text_secondary']};
padding: 10px 12px;
border: none;
border-right: 1px solid {c['border_default']};
border-bottom: 1px solid {c['border_default']};
font-weight: {EU_TYPOGRAPHY['weight_semibold']};
font-size: {EU_TYPOGRAPHY['size_xs']};
text-transform: uppercase;
}}
QHeaderView::section:first {{
border-top-left-radius: {EU_SIZES['radius_lg']};
}}
QHeaderView::section:last {{
border-top-right-radius: {EU_SIZES['radius_lg']};
border-right: none;
}}
QScrollBar:vertical {{
background-color: transparent;
width: 12px;
border-radius: 6px;
}}
QScrollBar::handle:vertical {{
background-color: {c['border_default']};
border-radius: 6px;
min-height: 40px;
}}
QScrollBar::handle:vertical:hover {{
background-color: {c['border_hover']};
}}
"""
def get_card_style() -> str:
"""Get card/container stylesheet."""
c = get_all_colors()
return f"""
QFrame#card {{
background-color: {c['bg_secondary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_lg']};
}}
QFrame#card:hover {{
border-color: {c['border_hover']};
}}
"""
def get_panel_style() -> str:
"""Get panel stylesheet."""
c = get_all_colors()
return f"""
QWidget#panel {{
background-color: {c['bg_secondary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_lg']};
}}
"""
def get_scrollbar_style() -> str:
"""Get scrollbar stylesheet."""
c = get_all_colors()
return f"""
QScrollBar:vertical {{
background-color: transparent;
width: 10px;
border-radius: 5px;
margin: 2px;
}}
QScrollBar::handle:vertical {{
background-color: {c['border_default']};
border-radius: 5px;
min-height: 30px;
}}
QScrollBar::handle:vertical:hover {{
background-color: {c['border_hover']};
}}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
height: 0px;
}}
QScrollBar:horizontal {{
background-color: transparent;
height: 10px;
border-radius: 5px;
margin: 2px;
}}
QScrollBar::handle:horizontal {{
background-color: {c['border_default']};
border-radius: 5px;
min-width: 30px;
}}
QScrollBar::handle:horizontal:hover {{
background-color: {c['border_hover']};
}}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {{
width: 0px;
}}
"""
def get_progress_bar_style() -> str:
"""Get progress bar stylesheet."""
c = get_all_colors()
return f"""
QProgressBar {{
background-color: {c['progress_bg']};
border: none;
border-radius: 4px;
height: 8px;
text-align: center;
color: transparent;
}}
QProgressBar::chunk {{
background-color: qlineargradient(
x1: 0, y1: 0, x2: 1, y2: 0,
stop: 0 {c['accent_teal']},
stop: 1 {c['accent_blue']}
);
border-radius: 4px;
}}
QProgressBar::chunk[style_variant="orange"] {{
background-color: qlineargradient(
x1: 0, y1: 0, x2: 1, y2: 0,
stop: 0 {c['accent_orange']},
stop: 1 {c['accent_gold']}
);
}}
"""
def get_tab_style() -> str:
"""Get tab widget stylesheet."""
c = get_all_colors()
return f"""
QTabWidget::pane {{
background-color: {c['bg_secondary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_lg']};
border-top-left-radius: 0;
top: -1px;
}}
QTabBar::tab {{
background-color: {c['bg_tertiary']};
color: {c['text_secondary']};
padding: 10px 20px;
border-top-left-radius: {EU_SIZES['radius_md']};
border-top-right-radius: {EU_SIZES['radius_md']};
border: 1px solid {c['border_default']};
border-bottom: none;
margin-right: 2px;
font-size: {EU_TYPOGRAPHY['size_sm']};
font-weight: {EU_TYPOGRAPHY['weight_medium']};
}}
QTabBar::tab:selected {{
background-color: {c['bg_secondary']};
color: {c['text_primary']};
border-bottom: 2px solid {c['accent_orange']};
}}
QTabBar::tab:hover:!selected {{
background-color: {c['bg_hover']};
color: {c['text_primary']};
}}
"""
def get_tooltip_style() -> str:
"""Get tooltip stylesheet."""
c = get_all_colors()
return f"""
QToolTip {{
background-color: {c['bg_elevated']};
color: {c['text_primary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_md']};
padding: 8px 12px;
font-size: {EU_TYPOGRAPHY['size_sm']};
}}
"""
# ============================================================================
# ANIMATION UTILITIES
# ============================================================================
class AnimationHelper:
"""Helper class for common widget animations."""
@staticmethod
def fade_in(widget: QWidget, duration: int = 200) -> QPropertyAnimation:
"""Fade in a widget."""
effect = QGraphicsOpacityEffect(widget)
widget.setGraphicsEffect(effect)
animation = QPropertyAnimation(effect, b"opacity")
animation.setDuration(duration)
animation.setStartValue(0.0)
animation.setEndValue(1.0)
animation.setEasingCurve(QEasingCurve.Type.InOutQuad)
return animation
@staticmethod
def fade_out(widget: QWidget, duration: int = 200) -> QPropertyAnimation:
"""Fade out a widget."""
effect = widget.graphicsEffect()
if not isinstance(effect, QGraphicsOpacityEffect):
effect = QGraphicsOpacityEffect(widget)
widget.setGraphicsEffect(effect)
animation = QPropertyAnimation(effect, b"opacity")
animation.setDuration(duration)
animation.setStartValue(1.0)
animation.setEndValue(0.0)
animation.setEasingCurve(QEasingCurve.Type.InOutQuad)
return animation
@staticmethod
def slide_in(widget: QWidget, direction: str = "left", duration: int = 300) -> QPropertyAnimation:
"""Slide widget in from direction."""
animation = QPropertyAnimation(widget, b"pos")
animation.setDuration(duration)
animation.setEasingCurve(QEasingCurve.Type.OutCubic)
current_pos = widget.pos()
if direction == "left":
animation.setStartValue(current_pos - widget.width())
elif direction == "right":
animation.setStartValue(current_pos + widget.width())
elif direction == "top":
animation.setStartValue(current_pos - widget.height())
elif direction == "bottom":
animation.setStartValue(current_pos + widget.height())
animation.setEndValue(current_pos)
return animation
@staticmethod
def pulse(widget: QWidget, duration: int = 1000) -> QSequentialAnimationGroup:
"""Create a pulsing animation."""
effect = QGraphicsOpacityEffect(widget)
widget.setGraphicsEffect(effect)
group = QSequentialAnimationGroup()
fade_out = QPropertyAnimation(effect, b"opacity")
fade_out.setDuration(duration // 2)
fade_out.setStartValue(1.0)
fade_out.setEndValue(0.5)
fade_out.setEasingCurve(QEasingCurve.Type.InOutQuad)
fade_in = QPropertyAnimation(effect, b"opacity")
fade_in.setDuration(duration // 2)
fade_in.setStartValue(0.5)
fade_in.setEndValue(1.0)
fade_in.setEasingCurve(QEasingCurve.Type.InOutQuad)
group.addAnimation(fade_out)
group.addAnimation(fade_in)
group.setLoopCount(-1) # Infinite
return group
# ============================================================================
# ACCESSIBILITY HELPERS
# ============================================================================
class AccessibilityHelper:
"""Accessibility helpers for better UX."""
FOCUS_RING_STYLE = """
QPushButton:focus, QLineEdit:focus, QComboBox:focus,
QTableWidget:focus, QTabWidget:focus, QTextEdit:focus {
outline: 2px solid #ff8c42;
outline-offset: 2px;
}
"""
@staticmethod
def set_accessible_name(widget: QWidget, name: str):
"""Set accessible name for screen readers."""
widget.setAccessibleName(name)
@staticmethod
def set_accessible_description(widget: QWidget, description: str):
"""Set accessible description for screen readers."""
widget.setAccessibleDescription(description)
@staticmethod
def make_high_contrast():
"""Enable high contrast mode."""
# Modify colors for high contrast
EU_DARK_COLORS['text_primary'] = '#ffffff'
EU_DARK_COLORS['text_secondary'] = '#cccccc'
EU_DARK_COLORS['border_default'] = '#666666'
EU_DARK_COLORS['border_focus'] = '#ffff00'
@staticmethod
def get_keyboard_shortcut_hint(shortcut: str) -> str:
"""Format keyboard shortcut for display."""
return f"({shortcut})"
# ============================================================================
# RESPONSIVE HELPERS
# ============================================================================
class ResponsiveHelper:
"""Helpers for responsive layouts."""
BREAKPOINTS = {
'sm': 640,
'md': 768,
'lg': 1024,
'xl': 1280,
'2xl': 1536,
}
@staticmethod
def get_breakpoint(width: int) -> str:
"""Get current breakpoint name based on width."""
for name, size in sorted(ResponsiveHelper.BREAKPOINTS.items(), key=lambda x: x[1]):
if width < size:
return name
return '2xl'
@staticmethod
def should_show_sidebar(width: int) -> bool:
"""Check if sidebar should be visible at this width."""
return width >= ResponsiveHelper.BREAKPOINTS['md']
@staticmethod
def get_content_margins(width: int) -> tuple:
"""Get appropriate content margins for screen width."""
if width < ResponsiveHelper.BREAKPOINTS['sm']:
return (8, 8, 8, 8)
elif width < ResponsiveHelper.BREAKPOINTS['lg']:
return (16, 16, 16, 16)
else:
return (24, 24, 24, 24)
# ============================================================================
# GLOBAL STYLESHEET
# ============================================================================
def get_global_stylesheet() -> str:
"""Get complete global stylesheet."""
c = get_all_colors()
return f"""
/* Base */
QWidget {{
font-family: {EU_TYPOGRAPHY['font_family']};
font-size: {EU_TYPOGRAPHY['size_base']};
color: {c['text_primary']};
}}
/* Main Window */
QMainWindow {{
background-color: {c['bg_primary']};
}}
/* Selection */
::selection {{
background-color: {c['accent_orange']};
color: #ffffff;
}}
/* Scrollbars */
{get_scrollbar_style()}
/* Tooltips */
{get_tooltip_style()}
/* Focus indicators for accessibility */
{AccessibilityHelper.FOCUS_RING_STYLE}
/* Menu */
QMenu {{
background-color: {c['bg_elevated']};
color: {c['text_primary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_md']};
padding: 8px;
}}
QMenu::item {{
padding: 8px 16px;
border-radius: {EU_SIZES['radius_sm']};
}}
QMenu::item:selected {{
background-color: {c['bg_hover']};
}}
QMenu::separator {{
height: 1px;
background-color: {c['border_default']};
margin: 6px 0;
}}
/* Group Box */
QGroupBox {{
background-color: {c['bg_secondary']};
border: 1px solid {c['border_default']};
border-radius: {EU_SIZES['radius_lg']};
margin-top: 12px;
padding-top: 16px;
font-weight: {EU_TYPOGRAPHY['weight_semibold']};
}}
QGroupBox::title {{
subcontrol-origin: margin;
left: 16px;
padding: 0 8px;
color: {c['text_secondary']};
}}
/* Check Box & Radio Button */
QCheckBox, QRadioButton {{
spacing: 8px;
}}
QCheckBox::indicator, QRadioButton::indicator {{
width: 18px;
height: 18px;
border: 2px solid {c['border_default']};
border-radius: 4px;
}}
QCheckBox::indicator:checked {{
background-color: {c['accent_orange']};
border-color: {c['accent_orange']};
}}
QRadioButton::indicator {{
border-radius: 9px;
}}
QRadioButton::indicator:checked {{
background-color: {c['accent_orange']};
border-color: {c['accent_orange']};
}}
/* Slider */
QSlider::groove:horizontal {{
height: 4px;
background-color: {c['bg_tertiary']};
border-radius: 2px;
}}
QSlider::handle:horizontal {{
width: 16px;
height: 16px;
background-color: {c['accent_orange']};
border-radius: 8px;
margin: -6px 0;
}}
QSlider::sub-page:horizontal {{
background-color: {c['accent_orange']};
border-radius: 2px;
}}
"""
# ============================================================================
# LEGACY COMPATIBILITY
# ============================================================================
# Keep old constants for backward compatibility
EU_COLORS = EU_DARK_COLORS
EU_RADIUS = {
'small': EU_SIZES['radius_sm'],
'medium': EU_SIZES['radius_md'],
'large': EU_SIZES['radius_lg'],
'button': EU_SIZES['radius_sm'],
}
EU_FONT = EU_TYPOGRAPHY['font_family']
# Legacy style getters
def get_eu_style(style_name: str) -> str:
"""Legacy style getter - maps to new system."""
style_map = {
'overlay_container': get_panel_style(),
'panel': get_panel_style(),
'header': get_card_style(),
'button_primary': get_button_style('primary'),
'button_secondary': get_button_style('secondary'),
'input': get_input_style(),
'table': get_table_style(),
'progress_bar': get_progress_bar_style(),
'tab': get_tab_style(),
'floating_icon': get_card_style(),
'floating_icon_hover': get_card_style(),
}
return style_map.get(style_name, "")