| """ |
| Sage & Sand β Neumorphic Gradio Theme |
| For AUTOMATIC1111 Stable Diffusion WebUI |
| |
| Palette: |
| #CCD5AE sage green (accent, highlights) |
| #E9EDC9 pale sage (secondary accent) |
| #FEFAE0 ivory cream (light surfaces) |
| #FAEDCD warm parchment (mid surfaces) |
| #D4A373 caramel tan (page background β all neumorphic shadows derived here) |
| |
| Neumorphic shadow values (derived from #D4A373): |
| Light highlight : #E8C49A (+20% brightness) |
| Dark recess : #A87845 (-20% brightness) |
| |
| Usage in AUTOMATIC1111: |
| 1. Place theme.py and app.py in your A1111 root folder. |
| 2. In webui.py, locate the gr.Blocks(...) call and add: |
| |
| from theme import SageAndSandNeumorphic, NEUMORPHIC_CSS |
| with gr.Blocks(theme=SageAndSandNeumorphic(), css=NEUMORPHIC_CSS) as demo: |
| |
| 3. Restart the WebUI. |
| |
| Publishing to Hugging Face Hub (standalone theme): |
| from theme import SageAndSandNeumorphic |
| theme = SageAndSandNeumorphic() |
| theme.push_to_hub( |
| repo_name="NeoSand", |
| org_name="kbray", |
| hf_token="hf_...", |
| ) |
| """ |
|
|
| import gradio as gr |
| from gradio.themes.base import Base |
| from gradio.themes.utils import colors, fonts, sizes |
|
|
| |
| BG = "#D4A373" |
| LIGHT_SHDW = "#E8C49A" |
| DARK_SHDW = "#A87845" |
| PANEL = "#DAAA7A" |
| INPUT_BG = "#D9A870" |
| ACCENT = "#CCD5AE" |
| ACCENT2 = "#E9EDC9" |
| CREAM = "#FEFAE0" |
| PARCHMENT = "#FAEDCD" |
| TEXT_DARK = "#3a3a2a" |
| TEXT_MID = "#5a4a30" |
| TEXT_MUTED = "#7a6248" |
|
|
| |
| |
| |
| NEUMORPHIC_CSS = f""" |
| /* ββ Reset & base βββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
| gradio-app {{ |
| background: {BG} !important; |
| }} |
| |
| /* ββ Block / panel containers β raised ββββββββββββββββββββββββββββββββββ */ |
| .gradio-container .block, |
| .gradio-container .form, |
| .gradio-container .gap {{ |
| background: {BG} !important; |
| border: none !important; |
| border-radius: 14px !important; |
| box-shadow: -6px -6px 12px {LIGHT_SHDW}, |
| 6px 6px 12px {DARK_SHDW} !important; |
| }} |
| |
| /* ββ Inputs / textareas β recessed (inset shadow) ββββββββββββββββββββββββ */ |
| .gradio-container input[type="text"], |
| .gradio-container input[type="number"], |
| .gradio-container input[type="search"], |
| .gradio-container textarea, |
| .gradio-container select {{ |
| background: {INPUT_BG} !important; |
| border: none !important; |
| border-radius: 10px !important; |
| box-shadow: inset -3px -3px 7px {LIGHT_SHDW}, |
| inset 3px 3px 7px {DARK_SHDW} !important; |
| color: {TEXT_DARK} !important; |
| transition: box-shadow 0.2s ease !important; |
| }} |
| .gradio-container input[type="text"]:focus, |
| .gradio-container input[type="number"]:focus, |
| .gradio-container textarea:focus, |
| .gradio-container select:focus {{ |
| box-shadow: inset -2px -2px 5px {LIGHT_SHDW}, |
| inset 2px 2px 5px {DARK_SHDW}, |
| 0 0 0 2px rgba(204,213,174,0.55) !important; |
| outline: none !important; |
| }} |
| |
| /* ββ Sliders β track recessed, thumb raised ββββββββββββββββββββββββββββββ */ |
| .gradio-container input[type="range"] {{ |
| -webkit-appearance: none; |
| appearance: none; |
| background: transparent !important; |
| border: none !important; |
| box-shadow: none !important; |
| }} |
| .gradio-container input[type="range"]::-webkit-slider-runnable-track {{ |
| height: 6px; |
| border-radius: 3px; |
| background: {INPUT_BG}; |
| box-shadow: inset -2px -2px 4px {LIGHT_SHDW}, |
| inset 2px 2px 4px {DARK_SHDW}; |
| border: none; |
| }} |
| .gradio-container input[type="range"]::-webkit-slider-thumb {{ |
| -webkit-appearance: none; |
| width: 18px; |
| height: 18px; |
| border-radius: 50%; |
| background: {BG}; |
| margin-top: -6px; |
| box-shadow: -3px -3px 6px {LIGHT_SHDW}, |
| 3px 3px 6px {DARK_SHDW}; |
| border: none; |
| cursor: pointer; |
| transition: box-shadow 0.15s ease; |
| }} |
| .gradio-container input[type="range"]::-webkit-slider-thumb:hover {{ |
| box-shadow: -4px -4px 8px {LIGHT_SHDW}, |
| 4px 4px 8px {DARK_SHDW}, |
| 0 0 0 3px rgba(204,213,174,0.45); |
| }} |
| .gradio-container input[type="range"]::-moz-range-track {{ |
| height: 6px; |
| border-radius: 3px; |
| background: {INPUT_BG}; |
| box-shadow: inset -2px -2px 4px {LIGHT_SHDW}, |
| inset 2px 2px 4px {DARK_SHDW}; |
| border: none; |
| }} |
| .gradio-container input[type="range"]::-moz-range-thumb {{ |
| width: 18px; |
| height: 18px; |
| border-radius: 50%; |
| background: {BG}; |
| box-shadow: -3px -3px 6px {LIGHT_SHDW}, |
| 3px 3px 6px {DARK_SHDW}; |
| border: none; |
| cursor: pointer; |
| }} |
| |
| /* ββ Checkboxes βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
| .gradio-container input[type="checkbox"] {{ |
| -webkit-appearance: none; |
| appearance: none; |
| width: 18px; |
| height: 18px; |
| border-radius: 5px; |
| background: {BG}; |
| box-shadow: inset -2px -2px 5px {LIGHT_SHDW}, |
| inset 2px 2px 5px {DARK_SHDW}; |
| border: none; |
| cursor: pointer; |
| transition: all 0.15s ease; |
| }} |
| .gradio-container input[type="checkbox"]:checked {{ |
| background: {ACCENT}; |
| box-shadow: inset -2px -2px 5px rgba(255,255,255,0.3), |
| inset 2px 2px 5px rgba(0,0,0,0.15); |
| }} |
| |
| /* ββ Buttons β primary (raised, pressed on active) βββββββββββββββββββββββ */ |
| .gradio-container .btn, |
| .gradio-container button.primary, |
| .gradio-container button[variant="primary"] {{ |
| background: {BG} !important; |
| border: none !important; |
| border-radius: 10px !important; |
| box-shadow: -5px -5px 10px {LIGHT_SHDW}, |
| 5px 5px 10px {DARK_SHDW} !important; |
| color: {TEXT_DARK} !important; |
| font-weight: 500 !important; |
| transition: box-shadow 0.15s ease, transform 0.1s ease !important; |
| }} |
| .gradio-container button.primary, |
| .gradio-container button[variant="primary"] {{ |
| background: linear-gradient(145deg, {ACCENT}, #b8c49a) !important; |
| color: {TEXT_DARK} !important; |
| }} |
| .gradio-container .btn:hover, |
| .gradio-container button:hover {{ |
| box-shadow: -7px -7px 14px {LIGHT_SHDW}, |
| 7px 7px 14px {DARK_SHDW} !important; |
| }} |
| .gradio-container .btn:active, |
| .gradio-container button:active {{ |
| box-shadow: inset -3px -3px 7px {LIGHT_SHDW}, |
| inset 3px 3px 7px {DARK_SHDW} !important; |
| transform: scale(0.985) !important; |
| }} |
| |
| /* ββ Stop / cancel button ββββββββββββββββββββββββββββββββββββββββββββββββ */ |
| .gradio-container button[variant="stop"], |
| .gradio-container button.stop {{ |
| background: linear-gradient(145deg, #e8c9a0, #d4b080) !important; |
| }} |
| |
| /* ββ Tabs ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
| .gradio-container .tabs > .tab-nav {{ |
| border-bottom: none !important; |
| gap: 8px; |
| }} |
| .gradio-container .tabs > .tab-nav > button {{ |
| background: {BG} !important; |
| border: none !important; |
| border-radius: 10px !important; |
| box-shadow: -4px -4px 8px {LIGHT_SHDW}, |
| 4px 4px 8px {DARK_SHDW} !important; |
| color: {TEXT_MID} !important; |
| padding: 8px 18px !important; |
| transition: all 0.15s ease !important; |
| }} |
| .gradio-container .tabs > .tab-nav > button.selected {{ |
| background: {BG} !important; |
| box-shadow: inset -3px -3px 7px {LIGHT_SHDW}, |
| inset 3px 3px 7px {DARK_SHDW} !important; |
| color: {TEXT_DARK} !important; |
| font-weight: 500 !important; |
| }} |
| |
| /* ββ Accordion βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
| .gradio-container .accordion {{ |
| border: none !important; |
| border-radius: 12px !important; |
| box-shadow: -5px -5px 10px {LIGHT_SHDW}, |
| 5px 5px 10px {DARK_SHDW} !important; |
| overflow: hidden; |
| }} |
| .gradio-container .accordion > .label-wrap {{ |
| background: {BG} !important; |
| }} |
| |
| /* ββ Image output panel ββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
| .gradio-container .image-container, |
| .gradio-container .output-image {{ |
| border: none !important; |
| border-radius: 14px !important; |
| box-shadow: inset -4px -4px 10px {LIGHT_SHDW}, |
| inset 4px 4px 10px {DARK_SHDW} !important; |
| overflow: hidden; |
| }} |
| |
| /* ββ Labels & typography βββββββββββββββββββββββββββββββββββββββββββββββββ */ |
| .gradio-container label span, |
| .gradio-container .block-label, |
| .gradio-container .label-wrap span {{ |
| color: {TEXT_MID} !important; |
| font-weight: 500 !important; |
| font-size: 13px !important; |
| letter-spacing: 0.02em !important; |
| }} |
| .gradio-container .prose h1, |
| .gradio-container .prose h2, |
| .gradio-container .prose h3 {{ |
| color: {TEXT_DARK} !important; |
| }} |
| .gradio-container .prose p, |
| .gradio-container .prose li {{ |
| color: {TEXT_MID} !important; |
| }} |
| |
| /* ββ Scrollbars ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
| .gradio-container ::-webkit-scrollbar {{ |
| width: 6px; |
| height: 6px; |
| }} |
| .gradio-container ::-webkit-scrollbar-track {{ |
| background: {INPUT_BG}; |
| border-radius: 3px; |
| box-shadow: inset 0 0 4px {DARK_SHDW}; |
| }} |
| .gradio-container ::-webkit-scrollbar-thumb {{ |
| background: {ACCENT}; |
| border-radius: 3px; |
| }} |
| """ |
|
|
|
|
| |
| sage_sand = colors.Color( |
| name="sage_sand", |
| c50="#f7f5ec", |
| c100="#FEFAE0", |
| c200="#FAEDCD", |
| c300="#E9EDC9", |
| c400="#CCD5AE", |
| c500="#b8c49a", |
| c600="#a0ad80", |
| c700="#D4A373", |
| c800="#b8895a", |
| c900="#8a6140", |
| c950="#5c3d24", |
| ) |
|
|
|
|
| class SageAndSandNeumorphic(Base): |
| """ |
| Sage & Sand Neumorphic β a soft-extruded Gradio theme. |
| Pair with NEUMORPHIC_CSS for the full effect: |
| |
| import gradio as gr |
| from theme import SageAndSandNeumorphic, NEUMORPHIC_CSS |
| with gr.Blocks(theme=SageAndSandNeumorphic(), css=NEUMORPHIC_CSS) as demo: |
| ... |
| """ |
|
|
| def __init__( |
| self, |
| primary_hue: colors.Color = sage_sand, |
| secondary_hue: colors.Color = sage_sand, |
| neutral_hue: colors.Color = sage_sand, |
| spacing_size: sizes.Size = sizes.spacing_md, |
| radius_size: sizes.Size = sizes.radius_lg, |
| text_size: sizes.Size = sizes.text_md, |
| font=( |
| fonts.GoogleFont("DM Sans"), |
| "ui-sans-serif", |
| "system-ui", |
| "sans-serif", |
| ), |
| font_mono=( |
| fonts.GoogleFont("JetBrains Mono"), |
| "ui-monospace", |
| "monospace", |
| ), |
| ): |
| super().__init__( |
| primary_hue=primary_hue, |
| secondary_hue=secondary_hue, |
| neutral_hue=neutral_hue, |
| spacing_size=spacing_size, |
| radius_size=radius_size, |
| text_size=text_size, |
| font=font, |
| font_mono=font_mono, |
| ) |
| super().set( |
| |
| body_background_fill=BG, |
| body_background_fill_dark="#8a6140", |
|
|
| |
| block_background_fill=BG, |
| block_background_fill_dark="#a07040", |
| block_border_width="0px", |
| block_border_color="transparent", |
| block_shadow=f"-6px -6px 12px {LIGHT_SHDW}, 6px 6px 12px {DARK_SHDW}", |
| block_shadow_dark="none", |
|
|
| |
| panel_background_fill=BG, |
| panel_background_fill_dark="#9a7040", |
| panel_border_width="0px", |
|
|
| |
| block_label_background_fill="transparent", |
| block_label_background_fill_dark="transparent", |
| block_label_text_color=TEXT_MID, |
| block_label_text_color_dark=CREAM, |
| block_label_text_size="*text_sm", |
| block_label_text_weight="500", |
| block_title_text_color=TEXT_DARK, |
| block_title_text_color_dark=CREAM, |
| block_title_text_weight="600", |
|
|
| |
| input_background_fill=INPUT_BG, |
| input_background_fill_dark="#9a7040", |
| input_background_fill_focus=INPUT_BG, |
| input_border_width="0px", |
| input_border_color="transparent", |
| input_shadow=f"inset -3px -3px 7px {LIGHT_SHDW}, inset 3px 3px 7px {DARK_SHDW}", |
| input_shadow_focus=f"inset -2px -2px 5px {LIGHT_SHDW}, inset 2px 2px 5px {DARK_SHDW}, 0 0 0 2px rgba(204,213,174,0.55)", |
| input_placeholder_color=TEXT_MUTED, |
| input_placeholder_color_dark="#c8a878", |
|
|
| |
| slider_color=ACCENT, |
| slider_color_dark=ACCENT2, |
|
|
| |
| checkbox_background_color=BG, |
| checkbox_background_color_dark="#a07040", |
| checkbox_background_color_selected=ACCENT, |
| checkbox_background_color_selected_dark="#a0ad80", |
| checkbox_border_width="0px", |
| checkbox_border_color="transparent", |
| checkbox_label_background_fill=BG, |
| checkbox_label_background_fill_dark="#9a7040", |
| checkbox_label_background_fill_hover=BG, |
| checkbox_label_background_fill_selected=ACCENT, |
| checkbox_label_background_fill_selected_dark="#a0ad80", |
|
|
| |
| button_primary_background_fill=f"linear-gradient(145deg, {ACCENT}, #b8c49a)", |
| button_primary_background_fill_dark=f"linear-gradient(145deg, #a0ad80, #8a9668)", |
| button_primary_background_fill_hover=f"linear-gradient(145deg, #b8c49a, {ACCENT})", |
| button_primary_text_color=TEXT_DARK, |
| button_primary_text_color_dark=CREAM, |
| button_primary_border_color="transparent", |
| button_primary_shadow=f"-5px -5px 10px {LIGHT_SHDW}, 5px 5px 10px {DARK_SHDW}", |
| button_primary_shadow_hover=f"-7px -7px 14px {LIGHT_SHDW}, 7px 7px 14px {DARK_SHDW}", |
| button_primary_shadow_active=f"inset -3px -3px 7px {LIGHT_SHDW}, inset 3px 3px 7px {DARK_SHDW}", |
| button_transition="box-shadow 0.15s ease, transform 0.1s ease", |
|
|
| button_secondary_background_fill=BG, |
| button_secondary_background_fill_dark="#9a7040", |
| button_secondary_background_fill_hover=BG, |
| button_secondary_text_color=TEXT_MID, |
| button_secondary_text_color_dark=CREAM, |
| button_secondary_border_color="transparent", |
|
|
| button_cancel_background_fill=f"linear-gradient(145deg, #e8c9a0, #d4b080)", |
| button_cancel_background_fill_dark=f"linear-gradient(145deg, #8a6140, #7a5130)", |
| button_cancel_text_color=TEXT_DARK, |
| button_cancel_text_color_dark=CREAM, |
|
|
| button_large_padding="10px 22px", |
| button_small_padding="5px 12px", |
|
|
| |
| table_even_background_fill=INPUT_BG, |
| table_even_background_fill_dark="#9a7040", |
| table_odd_background_fill=BG, |
| table_odd_background_fill_dark="#a07040", |
| table_border_color="transparent", |
| table_row_focus=ACCENT2, |
| table_row_focus_dark="#b8895a", |
|
|
| |
| body_text_color=TEXT_DARK, |
| body_text_color_dark=CREAM, |
| body_text_color_subdued=TEXT_MUTED, |
| body_text_color_subdued_dark="#d4c8a8", |
| link_text_color="#7a8a5a", |
| link_text_color_dark=ACCENT, |
| link_text_color_hover="#5a6a3a", |
| link_text_color_hover_dark=ACCENT2, |
|
|
| |
| code_background_fill=INPUT_BG, |
| code_background_fill_dark="#8a6140", |
|
|
| |
| error_background_fill="#e8c8a8", |
| error_background_fill_dark="#7a4820", |
| error_border_color="transparent", |
| error_text_color="#7a3820", |
| error_text_color_dark="#f5d0b0", |
| ) |
|
|
|
|
| |
| theme = SageAndSandNeumorphic() |
|
|
|
|
| |
| if __name__ == "__main__": |
| with gr.Blocks( |
| theme=SageAndSandNeumorphic(), |
| css=NEUMORPHIC_CSS, |
| title="Sage & Sand Neumorphic β Preview", |
| ) as demo: |
| gr.Markdown("## Sage & Sand Neumorphic") |
| gr.Markdown("A soft-extruded earthy theme for Stable Diffusion WebUI.") |
|
|
| with gr.Row(): |
| with gr.Column(): |
| prompt = gr.Textbox(label="Prompt", lines=3, placeholder="A serene landscapeβ¦") |
| neg_prompt = gr.Textbox(label="Negative prompt", lines=2, placeholder="blurry, watermarkβ¦") |
| with gr.Row(): |
| steps = gr.Slider(1, 150, value=20, step=1, label="Steps") |
| cfg = gr.Slider(1, 30, value=7, step=0.5, label="CFG Scale") |
| with gr.Row(): |
| sampler = gr.Dropdown(["Euler a", "DPM++ 2M", "DDIM"], value="Euler a", label="Sampler") |
| faces = gr.Checkbox(label="Restore faces") |
| with gr.Row(): |
| gr.Button("Generate", variant="primary") |
| gr.Button("Interrupt", variant="stop") |
|
|
| with gr.Column(): |
| gr.Image(label="Output", height=320) |
|
|
| with gr.Tabs(): |
| with gr.Tab("Generation"): |
| with gr.Row(): |
| gr.Slider(512, 2048, value=512, step=64, label="Width") |
| gr.Slider(512, 2048, value=512, step=64, label="Height") |
| gr.Slider(1, 8, value=1, step=1, label="Batch size") |
| with gr.Tab("ControlNet"): |
| gr.Textbox(label="ControlNet model", placeholder="control_v11p_sd15_openpose") |
| gr.Slider(0, 2, value=1, step=0.05, label="Control weight") |
| with gr.Tab("Extras"): |
| gr.Textbox(label="Notes", lines=4) |
|
|
| demo.launch() |
|
|