| """Guild-dark theme: dark green + gold, matching the original backstory forge. |
| |
| Exports the CSS string and a head-HTML builder (meta tags + JSON-LD). |
| In Gradio 6, css/head are passed to demo.launch(), not gr.Blocks(). |
| """ |
|
|
| import html as _html |
| import json |
|
|
| |
| BG = "#2A4032" |
| PANEL = "#3D5A47" |
| GOLD = "#D4AF37" |
| CREAM = "#F2E6CE" |
| CREAM_DIM = "rgba(242, 230, 206, 0.85)" |
| RED = "#A7343B" |
| RED_DARK = "#7E2429" |
|
|
| CSS = """ |
| @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Inter:wght@400;500;600;700&display=swap'); |
| |
| :root { |
| --lf-bg: #2A4032; |
| --lf-panel: #3D5A47; |
| --lf-panel-deep: #24382C; |
| --lf-gold: #D4AF37; |
| --lf-cream: #F2E6CE; |
| --lf-cream-dim: rgba(242, 230, 206, 0.85); |
| --lf-red: #A7343B; |
| --lf-red-dark: #7E2429; |
| } |
| |
| body, gradio-app, .gradio-container { |
| background-color: var(--lf-bg) !important; |
| color: var(--lf-cream); |
| font-family: 'Inter', system-ui, sans-serif; |
| } |
| |
| .gradio-container { max-width: 72rem !important; margin: 0 auto !important; } |
| |
| /* ---------- typography ---------- */ |
| /* !important: Gradio's dark-theme prose styles (?__theme=dark on the HF |
| embed) otherwise override heading colors to white. */ |
| .lf-page h1 { |
| font-family: 'Press Start 2P', monospace; |
| color: var(--lf-gold) !important; |
| font-size: 1.35rem; |
| line-height: 1.6; |
| margin: 1.25rem 0 0.75rem 0; |
| -webkit-font-smoothing: none; |
| word-wrap: break-word; |
| } |
| .lf-page h2 { |
| font-family: 'Press Start 2P', monospace; |
| color: var(--lf-gold) !important; |
| font-size: 0.95rem; |
| line-height: 1.6; |
| margin: 2rem 0 0.75rem 0; |
| -webkit-font-smoothing: none; |
| } |
| .lf-page h3 { color: var(--lf-gold) !important; } |
| |
| /* native control accents (radios, checkboxes) — gold, not Gradio orange */ |
| .gradio-container input[type="radio"], |
| .gradio-container input[type="checkbox"] { accent-color: var(--lf-gold); } |
| .gradio-container input[type="radio"]:checked, |
| .gradio-container input[type="checkbox"]:checked { |
| background-color: var(--lf-gold) !important; |
| border-color: var(--lf-gold) !important; |
| } |
| .lf-page h3 { color: var(--lf-gold); font-size: 1.05rem; margin: 1.25rem 0 0.5rem 0; font-weight: 700; } |
| .lf-page p, .lf-page li { color: var(--lf-cream-dim); line-height: 1.7; font-size: 1rem; } |
| .lf-page ul, .lf-page ol { padding-left: 1.25rem; margin: 0.75rem 0; } |
| .lf-page li::marker { color: var(--lf-gold); } |
| .lf-page a { color: var(--lf-gold); text-decoration: underline; } |
| .lf-page strong { color: var(--lf-gold); } |
| .lf-tagline { font-size: 1.1rem !important; color: var(--lf-cream) !important; } |
| |
| /* ---------- panels the tool lives in ---------- */ |
| .lf-panel, .lf-page .block, .gradio-container .form { |
| background-color: var(--lf-panel) !important; |
| border: 1px solid rgba(212, 175, 55, 0.35) !important; |
| border-radius: 8px; |
| } |
| .lf-page label span, .lf-page .label-wrap span { color: var(--lf-cream) !important; } |
| .lf-page input, .lf-page textarea, .lf-page select { |
| background-color: var(--lf-panel-deep) !important; |
| color: var(--lf-cream) !important; |
| border-color: rgba(212, 175, 55, 0.35) !important; |
| } |
| |
| /* primary action buttons — gold everywhere, not Gradio's default orange */ |
| .gradio-container button.primary, .lf-page .lf-generate button { |
| background: var(--lf-gold) !important; |
| color: #21301F !important; |
| border: none !important; |
| font-weight: 700; |
| } |
| .gradio-container button.primary:hover { filter: brightness(1.08); } |
| |
| /* wide content (stat tables, battle boards) scrolls inside its container */ |
| .lf-output { overflow-x: auto; } |
| .lf-output table { min-width: 480px; } |
| |
| /* ---------- Loreify CTA blocks ---------- */ |
| .lf-cta { |
| background: linear-gradient(160deg, rgba(167, 52, 59, 0.16), rgba(167, 52, 59, 0.05)); |
| border: 1px solid rgba(212, 175, 55, 0.45); |
| border-radius: 8px; |
| padding: 1.25rem 1.5rem; |
| margin: 1.5rem 0; |
| text-align: center; |
| } |
| .lf-cta p { color: var(--lf-cream); font-size: 1.05rem; margin: 0 0 0.9rem 0; } |
| .lf-cta .lf-cta-btn { |
| display: inline-block; |
| background: var(--lf-red); |
| color: #FFFBE0 !important; |
| border: 2px solid var(--lf-red-dark); |
| border-radius: 6px; |
| padding: 0.7rem 1.5rem; |
| font-weight: 700; |
| font-size: 1rem; |
| text-decoration: none !important; |
| box-shadow: 1px 2px 4px rgba(0,0,0,0.25); |
| } |
| .lf-cta .lf-cta-btn:hover { background: var(--lf-red-dark); } |
| .lf-cta .lf-cta-sub { font-size: 0.85rem; color: var(--lf-cream-dim); margin-top: 0.6rem; margin-bottom: 0; } |
| |
| /* ---------- output / example cards ---------- */ |
| .lf-output, .lf-example { |
| background: var(--lf-panel); |
| border: 1px solid rgba(212, 175, 55, 0.45); |
| border-radius: 8px; |
| padding: 1.25rem 1.5rem; |
| margin: 0.75rem 0; |
| } |
| .lf-output h3, .lf-example h3 { margin-top: 0.75rem; } |
| |
| /* related tools + footer */ |
| .lf-related { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; margin: 1rem 0; } |
| .lf-related a.lf-related-card { |
| display: block; background: var(--lf-panel); |
| border: 1px solid rgba(212, 175, 55, 0.4); border-radius: 8px; |
| padding: 1rem; text-decoration: none; |
| } |
| .lf-related a.lf-related-card:hover { border-color: var(--lf-gold); } |
| .lf-related .lf-related-title { color: var(--lf-gold); font-weight: 700; margin-bottom: 0.35rem; } |
| .lf-related .lf-related-desc { color: var(--lf-cream-dim); font-size: 0.9rem; } |
| .lf-disclaimer { font-size: 0.82rem; color: rgba(242, 230, 206, 0.6); margin: 2rem 0 1rem 0; line-height: 1.6; } |
| |
| /* FAQ */ |
| .lf-faq details { |
| background: var(--lf-panel); border: 1px solid rgba(212, 175, 55, 0.35); |
| border-radius: 8px; padding: 0.85rem 1.1rem; margin: 0.6rem 0; |
| } |
| .lf-faq summary { color: var(--lf-gold); font-weight: 700; cursor: pointer; font-size: 1rem; } |
| .lf-faq details p { margin: 0.7rem 0 0.2rem 0; } |
| |
| /* mobile */ |
| @media (max-width: 640px) { |
| .lf-page h1 { font-size: 1.05rem; } |
| .lf-page h2 { font-size: 0.85rem; } |
| .lf-cta { padding: 1rem; } |
| } |
| |
| footer { display: none !important; } |
| |
| /* offscreen-but-in-DOM (e.g. clipboard source textarea) */ |
| .lf-offscreen { |
| position: absolute !important; |
| left: -99999px !important; |
| top: 0 !important; |
| height: 1px !important; |
| width: 1px !important; |
| overflow: hidden !important; |
| opacity: 0; |
| pointer-events: none; |
| } |
| """ |
|
|
|
|
| def build_head(config, jsonld_extra: dict | None = None) -> str: |
| """Meta description + WebApplication JSON-LD for the page head.""" |
| desc = _html.escape(config.tagline, quote=True) |
| data = { |
| "@context": "https://schema.org", |
| "@type": "WebApplication", |
| "name": config.h1, |
| "description": config.tagline, |
| "url": config.space_url, |
| "applicationCategory": "GameApplication", |
| "operatingSystem": "Any", |
| "offers": {"@type": "Offer", "price": "0", "priceCurrency": "USD"}, |
| } |
| if jsonld_extra: |
| data.update(jsonld_extra) |
| return ( |
| f'<meta name="description" content="{desc}">\n' |
| f'<script type="application/ld+json">{json.dumps(data)}</script>' |
| ) |
|
|