摘要

给三个 Gradio 应用做品牌化改版(注入 CSS/JS 换皮),本来以为只是贴几张样式,结果连着踩了三个坑:1920 宽的浏览器右侧空出 320 像素、移动端样式怎么注入都不生效、顶栏 logo 和右边按钮视觉上永远不对称。更魔幻的是中途还虚惊一场”后端挂了”。本文把四个问题的根因、定位过程和修法全部记录在案,版本相关坑请以 Gradio 6.19 为准(6.20 行为不同,见文中对照)。文中内网地址与端口均已脱敏。

封面:一个宽屏页面上右侧大片空洞的示意

坑一:宽屏右侧空白 —— max-width 配了,margin:auto 没配

症状:语音站在 1920 宽的浏览器里,内容全部贴左,右侧空出一条约 320 像素的荒原;配音站却居中得很好。两站代码风格几乎一致,唯一差别:一个用 Gradio 6.19,一个用 6.20。

定位:量 DOM 而不是看截图——

# Playwright 里直接量容器几何
el = page.locator(".gradio-container")
print(el.evaluate("e => { const r = e.getBoundingClientRect();"
                  " return [r.left, r.right, innerWidth]; }"))
# 1920 视口下:[0, 1223, 1920]   ← 左0右697,偏了

再翻 Gradio 自带样式表,6.19 的 .gradio-container 有 max-width:1600px 却没有 margin-left/right:auto(6.20 补上了)。同时这个容器的 width 按内容收缩,实际只有 1223 像素——两个缺陷叠加,就是那条荒原。

修法(品牌注入层,两行解决):

.gradio-container {
  max-width: 1600px !important;
  width: 100% !important;
  margin-left: auto !important;
  margin-right: auto !important;
}

注意是 100% 加 max-width,不是 min-width:100%——后者会击穿 1600 上限,在 2K/4K 屏上又变成另一种丑。

坑二:移动端样式注入不生效 —— Gradio 的选择器加前缀把祖先前代改了

症状:想给手机端调一个 .main { padding: ... },CSS 明明白白写进了 launch(css=...),浏览器里也能看到这条规则,但就是不生效。DevTools 里那条规则甚至显示“被更高优先级覆盖”,可优先级写得已经是 !important。

根因:Gradio 对前端注入的样式会做一层作用域处理,实际落盘的选择器被改写成了带内部前缀的形态,例如 .contain .main。问题在于 .main 并不是 .contain 的后代——DOM 结构里 .main 是 .contain 的祖先:

三栏对比:你以为的匹配 / 实际落盘的选择器 / 解法

.main          ← 你想改这个
  └─ .wrap
       └─ .contain   ← Gradio 把选择器锚在这里
            └─ 组件们

.contain .main 永远匹配不到任何东西,规则成为死码。这不是缓存、不是优先级,是匹配集为空。

修法:绕开 Gradio 的注入通道,把 <style> 直接塞进页面 <head>。launch(css=) 走的是组件级注入(会被加前缀),而 demo.load(js=...) 动态写 head,或者对可改静态资源的部署直接在 index.html 模板里注一段,都不经过前缀化。两条通道的差别值得记住:

# ❌ 会被 .contain 前缀化,改祖先类选择器必死
demo.launch(css=".main{padding:8px!important}")

# ✅ 运行时注入 head,原样生效
demo.load(js="""() => {
  const s = document.createElement('style');
  s.textContent = '.main{padding:8px!important}';
  document.head.appendChild(s);
}""")

坑三:顶栏 logo 与按钮“不对称” —— 幻影左缩进

症状:手机端顶栏左边 logo、右边圆钮,视觉测量发现 logo 距屏幕左缘比按钮距右缘多出约 20 像素,明明左右 padding 一样。

根因:logo 那条链接是块级元素,外层还套着一个居中的排版容器。display:block 的元素在居中容器里会被施加文本缩进类的副作用,产生一段看不见的左空隙——而右侧按钮是 flex item,不吃这一套。两边参照系不同,padding 相同也白搭。

修法:把 logo 链接改成 flex item 并清掉继承的内边距:

.brandlink { display:flex !important; align-items:center; padding-left:0 !important; margin:0 !important; }

改完再量:左右 delta = 0。经验:布局“不对称”先分清两侧元素的 display 类型和参照容器,很多时候不是间距错了,是两边根本不在同一个坐标系里。

坑四(附赠):“后端没起来”其实是按需加载

症状:语音站页面顶部红字 server 未运行 @ http://127.0.0.1:8000,直觉是推理服务崩了。

真相:端口 8000 上跑的是一个按需启动的本地推理后端,只有点击“加载模型”才会拉起并监听;平时不监听是设计行为。Web UI 端口和推理后端端口不是一回事,UI 活着 ≠ 后端必须活着。

动作:把页面提示文案从“server 未运行”改成“模型未加载(点击加载模型)”,一场事故就地消灭。这条不算 Gradio 的坑,但它是同一轮排查里最贵的一分钟——先看架构是不是按需,再谈故障。

验收纪律:宽屏必须测 1920 以上

最后立一条规矩,源自坑一的教训:窄视口下两站表现一致,1920 才暴露差异。所有布局类修复的验收必须覆盖 ≥1920 宽视口,窄屏截图作废;移动端另外双验 390/360 两个宽度。度量方式用 DOM 几何(getBoundingClientRect 差值)而不是目测截图,误差小一个量级。

for vw in (1920, 2560):
    page.set_viewport_size({"width": vw, "height": 1080})
    left, right = geom(page)             # 容器左右空隙
    assert abs(left - right) < 2, f"宽屏 {vw} 不对称: {left}/{right}"

小结

坑 根因 修法
宽屏右侧荒原 6.19 容器缺 margin:auto 且按内容收缩 补 width:100% + max-width + auto 边距
移动端注入失效 Gradio 前缀化选择器,祖先类匹配集为空 改走 head <style> 注入
顶栏不对称 块级 logo 在居中容器产生幻影左缩进 logo 改 flex item,清 padding
“后端挂了” 推理后端按需启动 改文案,别改代码

三个 Gradio 应用最后共用一份品牌注入层(CSS + JS),升级只动一处;版本差异(6.19 / 6.20)全部由注入层吸收,不改任何业务代码。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注