CoolFace
Apppublic

uxoxo/eb2ab

sourceHugging Faceapache-2.0updated 11mo agoView on Hugging Face
0likes
theme.py356 linesDownload Raw Back to design_system
1"""2Design System Theme - Gradio theme configuration3 4This module creates a custom Gradio theme based on the design system tokens.5 6Author: Claude Code7Date: 2025-10-218"""9 10import gradio as gr11from gradio.themes.base import Base12from gradio.themes.utils import colors, fonts, sizes13from .design_tokens import DesignTokens as DT14 15 16class Ebook2AudiobookTheme(Base):17    """18    Custom Gradio theme for ebook2audiobook19    Based on OOUX design principles with clarity and transparency20    """21 22    def __init__(23        self,24        *,25        primary_hue: colors.Color = colors.blue,26        secondary_hue: colors.Color = colors.purple,27        neutral_hue: colors.Color = colors.gray,28        spacing_size: sizes.Size = sizes.spacing_md,29        radius_size: sizes.Size = sizes.radius_md,30        text_size: sizes.Size = sizes.text_md,31        font: tuple[str] = (32            "Inter",33            "-apple-system",34            "BlinkMacSystemFont",35            "Segoe UI",36            "sans-serif",37        ),38        font_mono: tuple[str] = (39            "JetBrains Mono",40            "Consolas",41            "Monaco",42            "monospace",43        ),44    ):45        super().__init__(46            primary_hue=primary_hue,47            secondary_hue=secondary_hue,48            neutral_hue=neutral_hue,49            spacing_size=spacing_size,50            radius_size=radius_size,51            text_size=text_size,52            font=font,53            font_mono=font_mono,54        )55 56        # Override specific theme properties57        self.set(58            # Button styles59            button_primary_background_fill=DT.PRIMARY,60            button_primary_background_fill_hover=DT.PRIMARY_HOVER,61            button_primary_text_color="white",62            button_primary_border_color=DT.PRIMARY,63 64            button_secondary_background_fill=DT.SECONDARY,65            button_secondary_background_fill_hover=DT.SECONDARY_HOVER,66            button_secondary_text_color="white",67 68            # Borders69            border_color_primary=DT.BORDER_DEFAULT,70            border_color_accent=DT.PRIMARY,71 72            # Backgrounds73            background_fill_primary="white",74            background_fill_secondary=DT.BG_SECONDARY,75 76            # Shadow77            shadow_drop=DT.SHADOW_BASE,78            shadow_drop_lg=DT.SHADOW_LG,79 80            # Block81            block_background_fill="white",82            block_border_width="1px",83            block_padding=DT.SPACE_6,84            block_radius=DT.RADIUS_LG,85            block_shadow=DT.SHADOW_SM,86 87            # Input88            input_background_fill="white",89            input_border_color=DT.BORDER_DEFAULT,90            input_border_color_focus=DT.BORDER_FOCUS,91            input_border_width="1px",92            input_padding=DT.SPACE_3,93            input_radius=DT.RADIUS_MD,94        )95 96 97def create_theme() -> gr.Theme:98    """99    Create and return the ebook2audiobook theme100 101    Returns:102        gr.Theme instance103    """104    return Ebook2AudiobookTheme()105 106 107def get_custom_css() -> str:108    """109    Get custom CSS to inject into Gradio app110 111    Returns:112        CSS string113    """114    # Combine design tokens CSS vars with custom styles115    css = DT.to_css_vars()116 117    # Add custom CSS from styles.css118    # Note: In production, you'd load this from the actual CSS file119    css += """120    /* Additional custom styles for Gradio */121 122    /* Gradio-specific overrides */123    .gradio-container {124        font-family: var(--font-primary) !important;125        max-width: 1440px;126        margin: 0 auto;127    }128 129    /* Tab styling */130    .tab-nav button {131        font-weight: 500;132        font-size: var(--font-size-base);133        padding: var(--space-3) var(--space-6);134        border-radius: var(--radius-md) var(--radius-md) 0 0;135        transition: all var(--transition-base);136    }137 138    .tab-nav button.selected {139        background: var(--color-primary-light);140        color: var(--color-primary-dark);141        border-bottom: 2px solid var(--color-primary);142    }143 144    /* Card hover effects */145    .card:hover {146        transform: translateY(-2px);147        box-shadow: var(--shadow-lg);148    }149 150    /* Progress animations */151    @keyframes progress-shimmer {152        0% {153            background-position: -200% 0;154        }155        100% {156            background-position: 200% 0;157        }158    }159 160    .progress-bar-fill.processing {161        background: linear-gradient(162            90deg,163            var(--color-warning) 0%,164            var(--color-warning-light) 50%,165            var(--color-warning) 100%166        );167        background-size: 200% 100%;168        animation: progress-shimmer 2s linear infinite;169    }170 171    /* Smooth transitions for all interactive elements */172    button, input, select, textarea {173        transition: all var(--transition-base);174    }175 176    /* Focus states for accessibility */177    button:focus-visible,178    input:focus-visible,179    select:focus-visible,180    textarea:focus-visible {181        outline: 2px solid var(--color-primary);182        outline-offset: 2px;183    }184 185    /* Responsive typography */186    @media (max-width: 768px) {187        .heading-1 {188            font-size: var(--font-size-3xl);189        }190 191        .heading-2 {192            font-size: var(--font-size-xl);193        }194 195        .gradio-container {196            padding: var(--space-4);197        }198    }199 200    /* Status indicators with pulse animation */201    .tag-processing {202        animation: pulse-border 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;203    }204 205    @keyframes pulse-border {206        0%, 100% {207            border-color: var(--color-warning);208        }209        50% {210            border-color: var(--color-warning-light);211        }212    }213 214    /* File upload area */215    .file-upload {216        border: 2px dashed var(--color-neutral-300);217        border-radius: var(--radius-lg);218        padding: var(--space-8);219        text-align: center;220        transition: all var(--transition-base);221    }222 223    .file-upload:hover {224        border-color: var(--color-primary);225        background: var(--color-primary-light);226    }227 228    /* Toast notifications (if using) */229    .toast {230        position: fixed;231        bottom: var(--space-6);232        right: var(--space-6);233        background: white;234        border-radius: var(--radius-lg);235        box-shadow: var(--shadow-xl);236        padding: var(--space-4);237        min-width: 300px;238        z-index: 9999;239        animation: slide-in-right 0.3s ease-out;240    }241 242    @keyframes slide-in-right {243        from {244            transform: translateX(100%);245            opacity: 0;246        }247        to {248            transform: translateX(0);249            opacity: 1;250        }251    }252 253    /* Scrollbar styling */254    ::-webkit-scrollbar {255        width: 8px;256        height: 8px;257    }258 259    ::-webkit-scrollbar-track {260        background: var(--color-neutral-100);261    }262 263    ::-webkit-scrollbar-thumb {264        background: var(--color-neutral-400);265        border-radius: var(--radius-full);266    }267 268    ::-webkit-scrollbar-thumb:hover {269        background: var(--color-neutral-500);270    }271 272    /* Loading states */273    .loading {274        position: relative;275        pointer-events: none;276        opacity: 0.6;277    }278 279    .loading::after {280        content: "";281        position: absolute;282        top: 50%;283        left: 50%;284        width: 20px;285        height: 20px;286        margin: -10px 0 0 -10px;287        border: 3px solid var(--color-neutral-200);288        border-top-color: var(--color-primary);289        border-radius: 50%;290        animation: spin 0.8s linear infinite;291    }292 293    /* Disabled states */294    button:disabled,295    input:disabled,296    select:disabled,297    textarea:disabled {298        opacity: 0.5;299        cursor: not-allowed;300    }301 302    /* Success checkmark animation */303    @keyframes checkmark {304        0% {305            stroke-dashoffset: 50;306        }307        100% {308            stroke-dashoffset: 0;309        }310    }311 312    .checkmark-icon {313        stroke-dasharray: 50;314        stroke-dashoffset: 50;315        animation: checkmark 0.5s ease-out forwards;316    }317    """318 319    return css320 321 322# Load external CSS file323def load_css_file(filepath: str = "design_system/styles.css") -> str:324    """325    Load CSS from external file326 327    Args:328        filepath: Path to CSS file329 330    Returns:331        CSS content as string332    """333    try:334        with open(filepath, 'r', encoding='utf-8') as f:335            return f.read()336    except FileNotFoundError:337        print(f"Warning: CSS file not found at {filepath}")338        return ""339 340 341def apply_theme_to_app(app: gr.Blocks) -> gr.Blocks:342    """343    Apply design system theme to a Gradio Blocks app344 345    Args:346        app: Gradio Blocks app347 348    Returns:349        Modified app with theme applied350    """351    # This would be called when creating the app352    # Example usage in app creation:353    # with gr.Blocks(theme=create_theme(), css=get_custom_css()) as app:354    #     ...355    return app356