Spaces:
Running on Zero
Fix light mode by collapsing the theme, not hacking the DOM
Browse filesThe previous fix forced Gradio's .dark class via a <head> script +
MutationObserver. That fought Gradio's SSR hydration and triggered a
remount loop: the hero cards (gr.HTML) never rendered and the page was
left with three dead buttons.
Root cause of the original bug: Gradio's _get_theme_css() emits every
token under both :root (light) and :root.dark (dark). Our UI is
dark-native, so light mode looked washed out.
Real fix: build the theme with gr.themes.Base(), then copy every *_dark
token value onto its light counterpart. Gradio's :root block then
carries dark colors and the app renders dark for every visitor -- pure
server-rendered CSS, no client-side script, nothing to break hydration.
Verified against gradio 6.14.0: 84 tokens collapsed, server serves
theme.css with dark values in :root, build_app() and launch() clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@@ -1425,45 +1425,30 @@ footer { display: none !important; }
|
|
| 1425 |
"""
|
| 1426 |
|
| 1427 |
|
| 1428 |
-
# The UI is dark-mode-native (navy "MAISI Console" aesthetic
|
| 1429 |
-
#
|
| 1430 |
-
#
|
| 1431 |
-
#
|
| 1432 |
-
#
|
| 1433 |
-
|
| 1434 |
-
|
| 1435 |
-
|
| 1436 |
-
|
| 1437 |
-
|
| 1438 |
-
|
| 1439 |
-
|
| 1440 |
-
|
| 1441 |
-
|
| 1442 |
-
|
| 1443 |
-
|
| 1444 |
-
|
| 1445 |
-
|
| 1446 |
-
|
| 1447 |
-
|
| 1448 |
-
|
| 1449 |
-
|
| 1450 |
-
|
| 1451 |
-
|
| 1452 |
-
}
|
| 1453 |
-
if (document.readyState === 'loading') {
|
| 1454 |
-
document.addEventListener('DOMContentLoaded', watchBody);
|
| 1455 |
-
} else {
|
| 1456 |
-
watchBody();
|
| 1457 |
-
}
|
| 1458 |
-
"""
|
| 1459 |
-
|
| 1460 |
-
FORCE_DARK_HEAD = (
|
| 1461 |
-
'<meta name="color-scheme" content="dark">'
|
| 1462 |
-
"<script>(function () {" + _FORCE_DARK_BODY + "})();</script>"
|
| 1463 |
-
)
|
| 1464 |
-
|
| 1465 |
-
# Re-assert once more after the Gradio app finishes loading (belt-and-suspenders).
|
| 1466 |
-
FORCE_DARK_JS = "() => {" + _FORCE_DARK_BODY + "}"
|
| 1467 |
|
| 1468 |
|
| 1469 |
def build_app() -> gr.Blocks:
|
|
@@ -1499,7 +1484,6 @@ if __name__ == "__main__":
|
|
| 1499 |
server_port=int(os.environ.get("PORT", "7860")),
|
| 1500 |
show_error=True,
|
| 1501 |
css=CSS,
|
| 1502 |
-
theme=
|
| 1503 |
-
|
| 1504 |
-
head=FORCE_DARK_HEAD,
|
| 1505 |
)
|
|
|
|
| 1425 |
"""
|
| 1426 |
|
| 1427 |
|
| 1428 |
+
# The UI is dark-mode-native (navy "MAISI Console" aesthetic) — there is no
|
| 1429 |
+
# light variant. Rather than fight Gradio's light/dark toggle at the DOM level
|
| 1430 |
+
# (DOM/observer hacks break SSR hydration), we collapse the theme itself:
|
| 1431 |
+
# Gradio's _get_theme_css() emits every token twice — under `:root` (light) and
|
| 1432 |
+
# `:root.dark` (dark). By copying each token's dark value onto its light
|
| 1433 |
+
# counterpart, `:root` carries dark colors too, so the app renders dark
|
| 1434 |
+
# regardless of OS preference or the HF Spaces theme switch. Pure CSS output —
|
| 1435 |
+
# nothing client-side to break.
|
| 1436 |
+
def _all_dark_theme() -> gr.themes.Base:
|
| 1437 |
+
theme = gr.themes.Base()
|
| 1438 |
+
for attr in list(theme.__dict__):
|
| 1439 |
+
if attr.startswith("_") or not attr.endswith("_dark"):
|
| 1440 |
+
continue
|
| 1441 |
+
dark_val = theme.__dict__[attr]
|
| 1442 |
+
if dark_val is None: # None => dark inherits light; leave light as-is
|
| 1443 |
+
continue
|
| 1444 |
+
light_attr = attr[: -len("_dark")]
|
| 1445 |
+
if light_attr in theme.__dict__:
|
| 1446 |
+
theme.__dict__[light_attr] = dark_val
|
| 1447 |
+
return theme
|
| 1448 |
+
|
| 1449 |
+
|
| 1450 |
+
# Keep the browser's native chrome (scrollbars, form controls) dark too.
|
| 1451 |
+
DARK_HEAD = '<meta name="color-scheme" content="dark">'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1452 |
|
| 1453 |
|
| 1454 |
def build_app() -> gr.Blocks:
|
|
|
|
| 1484 |
server_port=int(os.environ.get("PORT", "7860")),
|
| 1485 |
show_error=True,
|
| 1486 |
css=CSS,
|
| 1487 |
+
theme=_all_dark_theme(),
|
| 1488 |
+
head=DARK_HEAD,
|
|
|
|
| 1489 |
)
|