Files
CyberStrikeAI/web/static/css/mobile.css
T
Sycun 8e00147d06 增加手机端使用ui的体验和整体优化 (#344)
* feat: integrate the mobile UI runtime layer from the migration package

迁移包给出的移动端改造是「运行时注入」架构:mobile.css 只写 767.98px 以下的
规则,mobile-ui.js 在末位加载并包装 switchPage、注入抽屉/底部标签栏/更多面板。
模板侧只有 4 处接线(viewport-fit + interactive-widget、样式层链接、ELK/SheetJS
移动端延迟加载、交互层末位脚本),其余保持上游原样,桌面端零影响。

* feat: 移动端注入层接入 i18n,并把触控下限统一到 44px

迁移包的交互层把标签栏、抽屉、更多面板的文案全写死成中文,en-US/ru-RU 下会中英
混杂;现在所有注入节点统一挂 data-i18n,交给 i18n.js 的 applyTranslations 在语言
切换时自动重译,本层只保留中文兜底。语言项改为按桌面端下拉里真实存在的语言循环
(原来只在 zh/en 之间来回,俄语用户点不到),面板里的 API 文档/GitHub 不再抄一份
URL,改为触发桌面端既有控件;iOS 上没有全屏 API 时给出提示而不是静默无反应;
不在四个标签里的页面点亮"更多",否则三分之一的页面底部栏无任何高亮。

同时补齐 PWA manifest、api-docs 页移动端样式层,并把通知铃铛与头像按钮从 38px
提到本层自己的 --m-touch(44px) 口径。配 web/static/js/mobile-ui-i18n.test.cjs:
键集三方一致、引用键必须三语都有译文、helper 区之外不许出现裸写文案的 API。

* style: 移动端外壳改 iOS 观感,并修掉抽屉页脚错位

观感:顶栏/底栏改透出材质+发丝分隔线,图标按钮一律正圆去底框,文字按钮一律胶囊,
卡片 18、面板顶角 22,按压给 scale 反馈并在 prefers-reduced-motion 下关掉;焦点环
从 2px 硬描边换成 3px 柔光环(forced-colors 下把硬描边还回去)。材质一律用
color-mix 从主题自己的 --bg-primary/--border-color 派生,浅色暗色都跟随。

两处不是"不好看"而是真错:
- 抽屉页脚被 insertBefore 插到菜单上面,"返回顶部"出现在列表上方;
- 顶栏材质在暗色下被 style.css 的 html[data-theme="dark"] header 不透明渐变压住,
  只写 header 选择器不生效,改成同特异性后暗色才真正透出。
圆角与胶囊走 --m-r-icon/--m-r-btn 令牌(24 节那批 !important 规则读的就是它们),
不再往下补选择器与 !important 对轰。

配 scripts/mobile-shot.mjs:手机视口下抓外壳图,--why 用 CDP 匹配样式表回答
"规则写了没生效是没命中还是被谁压住",--eval 量真实计算值。

* fix: 抽屉页脚与底部固定件被 Safari 工具栏压在后面

iOS 的 100dvh 是「工具栏收起时」的最大视口高度,工具栏展开时可见高度只有
innerHeight。两个全屏抽屉写死 height:100dvh,于是页脚那一截正好落在 Safari
底栏后面;bottom:0 的底栏与贴底面板同理。现在抽屉改用 mobile-ui.js 每次 resize
重写的 --m-app-height,贴底固定件按 max(0px, 100dvh - 可见高度) 抬升;不支持 dvh
或桌面端抬升量恒为 0,实测默认态底栏/抽屉位置零位移。

* fix: 去掉底部标签栏里重复的「更多」入口

顶栏的 ··· 与标签栏的「更多」打开的是同一个面板。按一个入口一份实现收掉底部那枚,
标签栏只留四个页面跳转;随之清掉 more2 图标、mobile.tabMore 键与门禁里的点击断言。
落点在四个标签之外的页面不再高亮任何标签——没有「更多」可点了。

* fix: 清掉另外两处重复入口,并把同类视口缺陷扫干净

顺着「底部更多与顶栏 ··· 重复」这条线扫了一遍,还有两处同类:
- 面板里的「退出登录」与头像菜单里的退出是同一个动作两个入口,头像菜单是桌面端
  一贯的位置,去掉面板那枚;连带删掉只有它能触发的 m-danger 样式与 logout 图标。
- 抽屉页脚的「返回顶部」与面板里的返回顶部是同一个动作两个入口,且抽屉是导航容器,
  动作按钮不该住在里面;去掉整个页脚(那句"移动端导航"文案也没有信息量)。

同类视口缺陷两处:
- #m-toast 的 bottom 只算到 tabbar-h + safe,底栏按 --m-bar-bottom 抬升时提示条不抬,
  工具栏展开时会压在底栏上;实测补前后间距 16px -> 16px。
- api-docs 页容器写 min-height:100dvh,恒定比可见区高出一截,最后一行落在 Safari
  底栏后面;改成 100svh(最小视口)。

i18n 契约测试同步:header.logout / mobile.navFooter 不再被引用,mobile 命名空间 18 键。

* style: 手机端对齐 new-api 的卡片密度与 iOS 的填充式表单

仪表盘那排 KPI 在 ≤480px 被桌面样式拍成一列 116px 高的长条,手机上要滚两屏才看完
四个数字。收成两列紧凑网格(奇数张末卡通栏不留空洞),数字 26px/600 + tabular-nums
做主角,标签 12px 弱化,图标块 28px。卡片表面统一成发丝边 + 16 圆角 + 几乎看不见的
投影——桌面的双层阴影在小屏上一屏四张会散。表单改 iOS 填充式(靠底色区分字段),
聚焦只留一圈柔光环(登录框实测叠过两圈),登录卡 20 圆角 + 满宽胶囊主按钮。

KPI 按 nth-child 分的淡彩渐变是产品自己的身份,这一节不跟它抢。

* fix: 补生产侧俄语、修掉白底白字的提示条,并把逐按钮门禁跑到 PASS

用户报的两件事:
- 面板里「切换语言」只在中英之间跳 —— 不是逻辑问题,是 ~/csai-生产版 缺俄语资产
  (模板没有 ru-RU 选项、i18n/ 只有两个 json、i18n.js 是不认 ru 的旧版)。补齐后
  真手指连点验通 中→英→俄→中,且该行文字自身跟着换。
- 点面板按钮时屏幕中间冒出一块白的 —— 是我自己造成的:25.6 把提示条改成浅色材质,
  字色还是基底的 #fff,等于白底白字。回到深色 HUD 胶囊,只留一层投影。

逐按钮门禁(需要登录态,本轮首次跑通全量 34 个界面 = 31 页 + 抽屉/面板/会话抽屉展开态):
- 硬缺陷 covered/offscreen/zero/pointer-events 全 0;11 条外壳真手指点击全过;
- 小于 44px 从 79 降到 3,残留是桌面密集表格里的控件,钉进基线只降不升;
- 修掉的真缺陷:子菜单项 42px(本层自己漏的下限)、.ai-channel-manager 的
  overflow:hidden 把 4 个通道选择按钮裁到滚不进来、会话抽屉里搜索框 42/分组按钮 24 宽/
  项目行 38/折叠头 30、工具条里 <a onclick> 只有 21px 高(这些链接既没 href 也没
  onclick 属性可依赖,判据要放宽到 <a>);复选框口径统一到 24.11 一处并放大到 30px。
- 门禁自己的两个错也修了:scrollIntoView 在「外层 hidden + 内层 auto」的嵌套里根本不滚,
   造成 59 个 offscreen 假红,改成逐层滚可滚祖先模拟手指;touch+mouse 双发会让一次点击
   变成两次(抽屉开了又关),只发 touch。
- 退出码口径写死:够不到/遮挡/零尺寸/pointer-events 与自家外壳必须 0,桌面遗留小命中区
   走基线;基线被压低时注入探针验红(RATCHET FAIL ×2)后撤销复验绿。

* style: 手机端玻璃质感升级,并补上对话上传的安卓适配

观感目标从"iOS 化的平面灰底"升到玻璃质感。平底上做半透明无物可透,玻璃读不出来,
所以先造画布再上材质(mobile.css 第 28 节):
- .container 铺环境光(浅色=近白底+右上 accent/左下紫柔光,暗色=深底+更亮柔光),
  .content-area / .chat-messages 交出实心底色,半透明表面从此有色可透;
- 模糊只给小面积/瞬时件:顶栏、底栏、底部面板、抽屉、登录卡、弹窗;常驻卡片
  只做半透明+发丝边+顶缘高光,不挂模糊——固定在滚动内容上的大面积模糊是掉帧主因
  (25 节实测口径不变);浅深色全用 color-mix 从主题令牌派生,只有顶缘高光按主题
  换向;prefers-reduced-transparency 与不支持模糊的环境退回实心。
- index.html 的 mobile.css 版本号递增,防 iPhone Safari 吃旧缓存。

安卓适配:对话上传的 accept="*" 不是合法 MIME 通配(各浏览器/WebView 解析不一致),
改为 "*/*";mobile-tap-audit.mjs 新增 --ua=iphone|android,同一套门禁可换安卓 UA 复跑
(壳层按视口宽度判定、与 UA 无关,换 UA 是给"安卓浏览器下同样合格"留可复现证据)。

验证(都在生产版文件夹跑):iPhone/Android 双 UA 端到端上传(按钮 44px → 触达
选择器 → POST /api/chat-uploads 200 落盘 → 附件条渲染)PASS;双 UA 全量门禁各 34 页
PASS(硬缺陷 0、点击失败 0、44px 基线无回潮)。
2026-10-08 10:44:42 +08:00

3629 lines
128 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ==========================================================================
CyberStrikeAI — Mobile UI Layer
--------------------------------------------------------------------------
目标:在不改动 style.css(1.1MB)与既有桌面布局的前提下,用一层独立的
响应式样式把平台改造成"手机优先可用"的形态。
设计语言参考 QuantumNous/new-api 的移动端实现:
- 断点 768px(useIsMobile: innerWidth < 768)
- 底部滑出式面板(rounded sheet + backdrop-blur 遮罩 + spring 动画)
- 100svh/dvh 视口单位、--app-header-height 变量驱动的布局高度
- 抽屉式导航 + 卡片化列表
本文件只在 max-width: 767.98px(以及平板/横屏区间)生效,
桌面端与 ≥768px 的既有表现完全不受影响。
========================================================================== */
/* --------------------------------------------------------------------------
1. 设计令牌
-------------------------------------------------------------------------- */
:root {
--m-appbar-h: 52px;
--m-tabbar-h: 58px;
--m-safe-top: env(safe-area-inset-top, 0px);
--m-safe-bottom: env(safe-area-inset-bottom, 0px);
--m-safe-left: env(safe-area-inset-left, 0px);
--m-safe-right: env(safe-area-inset-right, 0px);
--m-drawer-w: min(84vw, 320px);
--m-touch: 44px;
--m-radius-sheet: 18px;
--m-radius-card: 14px;
--m-gutter: 14px;
/* 24 节「按钮 / 控件视觉体系」的度量:全站按钮只有这一组尺寸口径。
改造前 style.css 里没有 .btn 基类,圆角 11 种、字号 9 种、字重 7 档、
同一个 .btn-secondary 出现 12 种签名(_mbtn.mjs 实测 645 个控件)。 */
--m-r-ctl: 12px;
/* 图标按钮一律正圆、文字按钮一律胶囊:24 节那批 !important 规则读的就是这两个
令牌,改令牌一处生效全站,不要再往下补选择器或跟 !important 对轰 */
--m-r-icon: 50%;
--m-r-btn: 999px;
--m-r-pill: 999px;
--m-ctl-h: var(--m-touch);
--m-ctl-pad-x: 16px;
--m-ctl-pad-x-sm: 12px;
--m-ctl-fs: 14px;
--m-ctl-fs-sm: 13px;
--m-ctl-fw: 500;
--m-ctl-fw-strong: 600;
--m-ctl-gap: 8px;
--m-ease: cubic-bezier(0.32, 0.72, 0, 1);
--m-scrim: rgba(15, 23, 42, 0.5);
--m-z-tabbar: 800;
--m-z-scrim: 1200;
--m-z-drawer: 1201;
--m-z-sheet: 1202;
--m-z-toast: 1300;
/* 运行时由 mobile-ui.js 写入,作为 100dvh 不可用时的兜底 */
--m-app-height: 100vh;
/* 贴底固定件(底栏 / 面板)的抬升量:iOS Safari 工具栏展开时,可见视口比
100dvh 矮一截,bottom:0 的固定件会被 Safari 底栏压住。JS 每次 resize 都会
重写 --m-app-height,所以这里能实时算出那一截是多少。默认 0,桌面与
不支持 dvh 的环境都不动。 */
--m-bar-bottom: 0px;
}
/* --------------------------------------------------------------------------
2. 视口与滚动基础
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
html {
height: 100%;
overflow: hidden;
}
body {
height: 100vh;
height: 100dvh;
height: var(--m-app-height, 100dvh);
overflow: hidden;
overscroll-behavior: none;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
.container {
height: 100%;
max-height: 100%;
border-radius: 0;
box-shadow: none;
overflow: hidden;
}
/* 触摸设备:去掉 300ms 点击延迟与高亮块 */
a,
button,
.nav-item-content,
.nav-submenu-item,
[onclick],
[role="button"] {
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
/* 双击缩放整体抑制(仍保留双指缩放),替代原先的全局 JS touchend 拦截 */
body {
touch-action: manipulation;
}
/* iOS 输入聚焦自动放大:所有表单控件至少 16px */
input,
select,
textarea {
font-size: 16px;
}
/* 滚动容器统一惯性滚动与边界收敛 */
.page-content,
.main-sidebar-nav,
.chat-messages,
.conversation-list,
.notification-list,
.m-sheet-body {
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
/* 触控目标下限 */
button,
.btn-primary,
.btn-secondary,
.btn-danger,
.btn-small,
.nav-item-content,
.nav-submenu-item,
.user-menu-item {
min-height: var(--m-touch);
}
button,
.btn-primary,
.btn-secondary,
.btn-danger {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
}
/* 桌面专属元素在手机上隐藏 */
.m-desktop-only {
display: none !important;
}
}
/* 手机专属元素默认隐藏,避免桌面端闪现 */
.m-mobile-only {
display: none;
}
@media (max-width: 767.98px) {
.m-mobile-only {
display: block;
}
.m-mobile-only.m-flex {
display: flex;
}
.m-mobile-only.m-inline-flex {
display: inline-flex;
}
}
/* --------------------------------------------------------------------------
3. 顶部应用栏
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
header {
height: var(--m-appbar-h);
min-height: var(--m-appbar-h);
padding: 0 var(--m-gutter);
padding-top: var(--m-safe-top);
height: calc(var(--m-appbar-h) + var(--m-safe-top));
min-height: calc(var(--m-appbar-h) + var(--m-safe-top));
display: flex;
align-items: center;
flex-shrink: 0;
position: relative;
z-index: 20;
background: var(--bg-primary);
border-bottom: 1px solid var(--border-color);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.header-content {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
height: 100%;
}
.header-logo-link {
flex: 1;
min-width: 0;
gap: 8px;
display: flex;
align-items: center;
}
.header-logo-link .brand-logo {
width: 28px;
height: 28px;
flex-shrink: 0;
}
.header-logo-link .brand-wordmark--lg {
font-size: 1.05rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.version-badge {
display: none;
}
.header-right {
gap: 4px;
flex-shrink: 0;
}
.header-actions {
gap: 4px;
align-items: center;
}
/* 桌面端的次要操作(API 文档 / GitHub / 主题 / 语言)收进"更多"面板 */
.header-actions > .openapi-doc-btn,
.header-actions > .lang-switcher,
.header-actions > .theme-toggle,
.header-actions > #theme-toggle-btn,
.header-actions > .theme-toggle-btn {
display: none !important;
}
.header-actions > .notification-menu-container,
.header-actions > .user-menu-container {
display: flex;
}
/* 汉堡按钮 / 关闭按钮 */
.m-menu-btn,
.m-more-btn,
.m-close-btn {
width: var(--m-touch);
height: var(--m-touch);
min-height: var(--m-touch);
padding: 0;
border: none;
border-radius: 10px;
background: transparent;
color: var(--text-primary);
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.m-menu-btn:active,
.m-more-btn:active,
.m-close-btn:active {
background: var(--bg-tertiary);
}
.m-menu-btn svg,
.m-more-btn svg,
.m-close-btn svg {
width: 22px;
height: 22px;
}
/* 通知与用户菜单是本层唯一漏掉 44px 下限的常驻控件:38px 在手指下是
「看得见但点不准」,与上面 --m-touch 的口径必须一致 */
.notification-btn,
.user-avatar-btn {
width: var(--m-touch);
height: var(--m-touch);
min-height: var(--m-touch);
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
}
}
/* --------------------------------------------------------------------------
4. 侧边栏 → 抽屉导航
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.main-layout {
position: relative;
overflow: hidden;
min-height: 0;
flex: 1;
}
.main-sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
width: var(--m-drawer-w) !important;
max-width: 86vw;
height: 100%;
height: 100dvh;
height: var(--m-app-height, 100dvh);
z-index: var(--m-z-drawer);
transform: translateX(-102%);
visibility: hidden;
transition: transform 0.3s var(--m-ease), visibility 0.3s linear;
box-shadow: none;
border-right: 1px solid var(--border-color);
border-radius: 0 var(--m-radius-sheet) var(--m-radius-sheet) 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.main-sidebar.m-open {
transform: translateX(0);
visibility: visible;
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
}
/* 折叠态在手机上无意义,强制展开 */
.main-sidebar.collapsed {
width: var(--m-drawer-w) !important;
}
.main-sidebar.collapsed .nav-item-content span,
.main-sidebar.collapsed .nav-submenu-item {
display: revert;
}
/* 桌面折叠按钮由抽屉头部关闭按钮取代 */
.sidebar-collapse-btn {
display: none !important;
}
/* 抽屉头部(由 mobile-ui.js 注入) */
.m-drawer-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 10px 10px 10px 14px;
padding-top: calc(10px + var(--m-safe-top));
border-bottom: 1px solid var(--border-color);
flex-shrink: 0;
}
.m-drawer-brand {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.m-drawer-brand img {
width: 26px;
height: 26px;
flex-shrink: 0;
}
.m-drawer-brand .brand-wordmark {
font-size: 1.02rem;
white-space: nowrap;
}
.m-drawer-search {
padding: 8px 12px;
border-bottom: 1px solid var(--border-color);
flex-shrink: 0;
}
.m-drawer-search input {
width: 100%;
box-sizing: border-box;
height: 38px;
padding: 0 12px;
border-radius: 10px;
border: 1px solid var(--border-color);
background: var(--input-bg);
color: var(--text-primary);
font-size: 15px;
outline: none;
}
.m-drawer-search input:focus {
border-color: var(--accent-color);
}
.m-nav-empty {
padding: 10px 4px 2px;
font-size: 13px;
color: var(--text-muted);
text-align: center;
}
.m-nav-empty[hidden] {
display: none;
}
/* 抽屉内的导航项:更大、更好点 */
.main-sidebar-nav {
flex: 1;
overflow-y: auto;
padding: 6px 8px calc(12px + var(--m-safe-bottom));
}
.nav-section-label {
padding: 12px 10px 6px;
font-size: 0.7rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-muted);
}
.nav-item-content {
padding: 11px 12px;
min-height: 46px;
font-size: 0.95rem;
border-radius: 10px;
gap: 12px;
}
.nav-item-content svg {
width: 20px;
height: 20px;
flex-shrink: 0;
}
.nav-submenu-item {
padding: 10px 12px 10px 44px;
/* 原来是 42px——本层自己定的 --m-touch(44px) 下限被自己漏掉了一处 */
min-height: var(--m-touch);
font-size: 0.9rem;
border-radius: 8px;
}
.nav-item.active > .nav-item-content,
.nav-item-content.active {
background: color-mix(in srgb, var(--accent-color) 14%, transparent);
color: var(--accent-color);
}
.nav-submenu-item.active {
background: color-mix(in srgb, var(--accent-color) 12%, transparent);
color: var(--accent-color);
}
/* 菜单搜索命中的项 */
.m-nav-hit > .nav-item-content,
.nav-submenu-item.m-nav-hit {
outline: 1px solid color-mix(in srgb, var(--accent-color) 45%, transparent);
}
.m-nav-hidden {
display: none !important;
}
/* 遮罩 */
.m-scrim {
position: fixed;
inset: 0;
z-index: var(--m-z-scrim);
/* 全屏 backdrop-filter 在无 GPU 加速环境代价极高,用不透明度替代 */
background: var(--m-scrim);
opacity: 0;
visibility: hidden;
transition: opacity 0.24s ease, visibility 0.24s linear;
}
.m-scrim.m-show {
opacity: 1;
visibility: visible;
}
}
/* --------------------------------------------------------------------------
5. 底部标签栏
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.content-area {
padding-bottom: calc(var(--m-tabbar-h) + var(--m-safe-bottom));
background: var(--bg-secondary);
}
#m-tabbar {
position: fixed;
left: 0;
right: 0;
bottom: var(--m-bar-bottom);
z-index: var(--m-z-tabbar);
display: flex;
align-items: stretch;
height: calc(var(--m-tabbar-h) + var(--m-safe-bottom));
padding-bottom: var(--m-safe-bottom);
padding-left: var(--m-safe-left);
padding-right: var(--m-safe-right);
/* 固定底栏带 backdrop-filter 时,软件渲染每帧都要重算模糊,是移动端掉帧主因 */
background: var(--bg-primary);
border-top: 1px solid var(--border-color);
box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.06);
transition: transform 0.24s var(--m-ease);
}
#m-tabbar.m-hidden {
transform: translateY(110%);
}
.m-tab {
flex: 1 1 0;
min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
padding: 6px 2px 4px;
border: none;
background: transparent;
color: var(--text-muted);
/* 原 0.66rem = 10.56px,是本层自己漏掉的"小字号下限 12px"(见 18.2),
底部标签栏是主导航,字号必须达标 */
font-size: 12px;
line-height: 1.15;
position: relative;
border-radius: var(--m-r-ctl);
transition: color 0.16s ease, transform 0.16s ease;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
.m-tab svg {
width: 23px;
height: 23px;
flex-shrink: 0;
}
.m-tab span {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.m-tab.m-active {
color: var(--accent-color);
}
.m-tab.m-active svg {
transform: translateY(-1px) scale(1.06);
}
.m-tab:active {
transform: scale(0.94);
}
.m-tab-badge {
position: absolute;
top: 3px;
left: 50%;
margin-left: 6px;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 9px;
background: var(--error-color, #ef4444);
color: #fff;
font-size: 11px;
line-height: 18px;
text-align: center;
font-weight: 600;
box-sizing: border-box;
}
.m-tab-dot {
position: absolute;
top: 6px;
left: 50%;
margin-left: 8px;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--error-color, #ef4444);
}
/* 键盘弹出时让出空间 */
body.m-keyboard #m-tabbar {
transform: translateY(110%);
}
body.m-keyboard .content-area {
padding-bottom: 0;
}
}
/* --------------------------------------------------------------------------
6. 底部滑出面板(更多 / 快捷操作)
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
#m-sheet {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: var(--m-z-sheet);
width: 95%;
margin: 0 auto;
bottom: calc(8px + var(--m-safe-bottom) + var(--m-bar-bottom));
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: var(--m-radius-sheet);
box-shadow: 0 -8px 40px rgba(0, 0, 0, 0.32);
transform: translateY(calc(100% + 24px));
opacity: 0;
visibility: hidden;
transition: transform 0.32s var(--m-ease), opacity 0.2s ease, visibility 0.32s linear;
max-height: 78vh;
max-height: 78dvh;
display: flex;
flex-direction: column;
overflow: hidden;
}
#m-sheet.m-show {
transform: translateY(0);
opacity: 1;
visibility: visible;
}
.m-sheet-grip {
width: 38px;
height: 4px;
border-radius: 2px;
background: var(--border-color);
margin: 8px auto 2px;
flex-shrink: 0;
}
.m-sheet-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 16px 10px;
flex-shrink: 0;
}
.m-sheet-title {
font-size: 0.95rem;
font-weight: 600;
color: var(--text-primary);
}
.m-sheet-body {
overflow-y: auto;
padding: 0 10px 12px;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 4px;
}
.m-sheet-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
padding: 12px 4px;
border: none;
background: transparent;
border-radius: 12px;
color: var(--text-secondary);
font-size: 0.72rem;
text-align: center;
line-height: 1.2;
min-height: 74px;
}
.m-sheet-item:active {
background: var(--bg-tertiary);
}
.m-sheet-item svg {
width: 22px;
height: 22px;
}
.m-sheet-item.m-on {
color: var(--accent-color);
background: color-mix(in srgb, var(--accent-color) 10%, transparent);
}
.m-sheet-sep {
grid-column: 1 / -1;
height: 1px;
background: var(--border-color);
margin: 8px 6px;
}
}
/* --------------------------------------------------------------------------
7. 页面头 / 内容区
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.page-header {
padding: 10px var(--m-gutter);
flex-direction: column;
flex-wrap: nowrap;
gap: 8px;
align-items: stretch;
border-radius: 0;
}
/* 标题独占一行,操作按钮区满宽换到下一行(避免 space-between 把按钮挤窄、
主按钮右端出屏被截)。按钮区内部仍可横向滚动,但占满整行、左对齐平齐。 */
.page-header h1,
.page-header h2,
.page-header h3 {
font-size: 1.05rem;
line-height: 1.3;
width: 100%;
}
.page-header-actions {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
padding-bottom: 2px;
flex-wrap: wrap;
}
.page-header-actions > * {
flex: 0 0 auto;
white-space: nowrap;
}
.page-content {
padding: var(--m-gutter) var(--m-gutter) calc(var(--m-gutter) + 8px);
overflow-x: hidden;
}
/* 页面切换的轻量过渡 */
.page.active {
animation: m-page-in 0.22s var(--m-ease);
}
@keyframes m-page-in {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: none;
}
}
/* 卡片化:常见容器统一圆角与内边距 */
.card,
.stat-card,
.metric-card,
.dashboard-card,
.panel,
.settings-section,
.info-card {
border-radius: var(--m-radius-card);
}
}
/* --------------------------------------------------------------------------
8. 表格 → 横向滚动 + 边缘渐隐
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.m-table-scroll {
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
overscroll-behavior-x: contain;
margin-left: calc(-1 * var(--m-gutter));
margin-right: calc(-1 * var(--m-gutter));
padding: 0 var(--m-gutter);
/* 左右渐隐阴影(Lea Verou 滚动阴影) */
background:
linear-gradient(to right, var(--bg-primary) 30%, rgba(255, 255, 255, 0)) left / 32px 100% no-repeat local,
linear-gradient(to left, var(--bg-primary) 30%, rgba(255, 255, 255, 0)) right / 32px 100% no-repeat local,
radial-gradient(farthest-side at 0 50%, rgba(15, 23, 42, 0.16), rgba(15, 23, 42, 0)) left / 12px 100% no-repeat scroll,
radial-gradient(farthest-side at 100% 50%, rgba(15, 23, 42, 0.16), rgba(15, 23, 42, 0)) right / 12px 100% no-repeat scroll;
}
.m-table-scroll > table {
min-width: 560px;
}
.m-table-scroll.m-tight > table {
min-width: 0;
}
table,
.data-table {
font-size: 0.82rem;
}
table th,
table td,
.data-table thead th,
.data-table tbody td {
padding: 9px 10px;
white-space: nowrap;
}
/* 表格内的按钮别撑破行 */
table .btn-primary,
table .btn-secondary,
table .btn-danger,
.data-table .btn-primary,
.data-table .btn-secondary,
.data-table .btn-danger {
min-height: 34px;
padding: 4px 10px;
font-size: 0.78rem;
}
/* 单元格里的长文本允许换行 */
.data-table td .m-wrap,
td.m-wrap {
white-space: normal;
min-width: 160px;
}
}
/* --------------------------------------------------------------------------
9. 表单与按钮
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.form-group,
.form-row {
margin-bottom: 12px;
}
.form-group label {
font-size: 0.85rem;
margin-bottom: 6px;
}
input[type="text"],
input[type="search"],
input[type="password"],
input[type="number"],
input[type="email"],
input[type="url"],
input[type="date"],
input[type="datetime-local"],
select,
textarea {
width: 100%;
box-sizing: border-box;
min-height: var(--m-touch);
padding: 10px 12px;
border-radius: 10px;
}
textarea {
min-height: 96px;
resize: vertical;
}
/* 一行多列的表单在手机上堆叠 */
.form-grid,
.form-row.multi,
.settings-grid {
grid-template-columns: 1fr !important;
display: grid;
gap: 12px;
}
.form-actions,
.page-header-actions.m-stack {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.form-actions > button,
.modal-footer > button {
flex: 1 1 auto;
}
}
/* --------------------------------------------------------------------------
10. 弹窗 / 下拉 / 浮层
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.modal {
padding: 0;
}
.modal-content {
width: 100%;
max-width: 100%;
height: 100%;
max-height: 100%;
margin: 0;
border-radius: 0;
border: none;
animation: m-sheet-up 0.26s var(--m-ease);
padding-top: var(--m-safe-top);
padding-bottom: var(--m-safe-bottom);
box-sizing: border-box;
}
@keyframes m-sheet-up {
from {
transform: translateY(16px);
opacity: 0.4;
}
to {
transform: none;
opacity: 1;
}
}
.modal-header {
padding: 12px var(--m-gutter);
flex-shrink: 0;
position: sticky;
top: 0;
background: var(--bg-primary);
z-index: 2;
border-bottom: 1px solid var(--border-color);
}
.modal-header h2,
.modal-header h3 {
font-size: 1.02rem;
}
.modal-body {
padding: var(--m-gutter);
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.modal-footer {
padding: 10px var(--m-gutter);
padding-bottom: calc(10px + var(--m-safe-bottom));
flex-shrink: 0;
position: sticky;
bottom: 0;
background: var(--bg-primary);
border-top: 1px solid var(--border-color);
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.modal-close,
.modal .close {
width: var(--m-touch);
height: var(--m-touch);
min-height: var(--m-touch);
}
/* 头部下拉:贴边全宽 */
.notification-dropdown,
.user-menu-dropdown,
.lang-dropdown {
position: fixed !important;
left: 8px !important;
right: 8px !important;
top: calc(var(--m-appbar-h) + var(--m-safe-top) - 4px) !important;
width: auto !important;
max-width: none !important;
max-height: 70vh;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
border-radius: var(--m-radius-card);
z-index: 1210;
}
/* 角色 / 项目 / 对话模式选择面板在手机上统一为底部抽屉,但**不盖输入框**:
YG 反馈「输入框和这三个功能点重叠」,实测面板 bottom:66px 恰好盖住输入条
(.chat-input-container 占据视口底部 186px)。现在 bottom 用 JS 测得的
--m-panel-offset(输入条顶部到视口底 + 8px 间距),面板始终弹在输入条正上方,
微信输入面板同款行为:输入条全程可见可点,点输入条即收起面板。
对话模式面板原先漏在这一组之外,仍按桌面的 position:absolute + left:0 定位,
在 320–412px 视口下以按钮包裹层为基准取全屏宽度,右边缘实测越界 198px。 */
.role-selection-panel,
.chat-project-panel,
.agent-mode-panel {
position: fixed !important;
left: 8px !important;
right: 8px !important;
bottom: var(--m-panel-offset, calc(var(--m-tabbar-h) + var(--m-safe-bottom) + 8px)) !important;
top: auto !important;
width: auto !important;
max-width: none !important;
max-height: var(--m-panel-maxh, 62vh);
border-radius: var(--m-radius-sheet);
z-index: 1210;
/* 面板与背景同色,上向投影把抽屉与聊天区明确分开 */
box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.16), 0 -1px 0 rgba(0, 0, 0, 0.06);
}
/* 轻提示 */
#m-toast {
position: fixed;
left: 50%;
/* 底栏按 --m-bar-bottom 抬升时提示条要一起抬,否则会压在底栏上 */
bottom: calc(var(--m-tabbar-h) + var(--m-safe-bottom) + 16px + var(--m-bar-bottom));
transform: translate(-50%, 12px);
z-index: var(--m-z-toast);
max-width: 88vw;
padding: 10px 16px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.92);
color: #fff;
font-size: 0.82rem;
line-height: 1.35;
text-align: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, transform 0.24s var(--m-ease), visibility 0.24s;
pointer-events: none;
}
#m-toast.m-show {
opacity: 1;
visibility: visible;
transform: translate(-50%, 0);
}
}
/* --------------------------------------------------------------------------
11. 对话页
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.chat-page-layout {
flex-direction: row;
}
/* 会话列表 → 左侧抽屉 */
.conversation-sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
width: var(--m-drawer-w) !important;
max-width: 86vw;
height: 100%;
height: 100dvh;
height: var(--m-app-height, 100dvh);
z-index: var(--m-z-drawer);
transform: translateX(-102%);
visibility: hidden;
transition: transform 0.3s var(--m-ease), visibility 0.3s linear;
border-radius: 0 var(--m-radius-sheet) var(--m-radius-sheet) 0;
box-shadow: none;
padding-top: var(--m-safe-top);
}
.conversation-sidebar.m-open {
transform: translateX(0);
visibility: visible;
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
}
.conversation-sidebar.collapsed {
width: var(--m-drawer-w) !important;
}
/* 桌面语义的「折叠」在手机上只会加一个无效 class,隐藏掉,让位给关闭按钮 */
.conversation-sidebar-collapse-btn {
display: none !important;
}
/* 抽屉只盖住左侧 320px,右侧露出的工具条按钮会误触,打开时整体隐藏 */
body.m-chat-drawer-open .m-chat-bar {
visibility: hidden;
}
.m-chat-drawer-close {
margin-left: auto;
flex-shrink: 0;
}
.m-chat-drawer-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 8px 10px 8px var(--m-gutter);
border-bottom: 1px solid var(--border-color);
background: var(--bg-primary);
flex-shrink: 0;
}
.m-chat-drawer-title {
font-size: 0.9375rem;
font-weight: 600;
color: var(--text-primary);
}
.conversation-header {
padding: 6px var(--m-gutter);
}
.chat-messages {
padding: 12px 12px 12px 12px;
}
/* 消息气泡占满宽度 */
.chat-messages .message,
.chat-messages .message-row,
.chat-messages .conversation-message {
max-width: 100%;
}
/* 轮次导航条在窄屏上太挤,直接收起 */
#chat-turn-rail {
display: none !important;
}
#chat-return-latest {
right: 10px;
bottom: 12px;
}
.chat-input-container {
padding: 8px 10px;
gap: 6px;
border-top: 1px solid var(--border-color);
}
.chat-input-primary-row {
gap: 6px;
}
.chat-input-leading {
gap: 6px;
}
/* 输入区里的次要按钮收成图标 */
.chat-input-container .role-selector-text {
max-width: 88px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 三个选择面板的关闭按钮桌面是 32×32,手机端放大到 44×44 满足触控 */
.role-selection-panel-close {
width: var(--m-touch) !important;
height: var(--m-touch) !important;
min-width: var(--m-touch) !important;
min-height: var(--m-touch) !important;
}
/* 发送按钮桌面是 40×40 圆形,手机端补足到 44×44(触控最小命中区) */
.chat-input-container .send-btn,
#chat-send-btn {
width: var(--m-touch) !important;
min-width: var(--m-touch) !important;
height: var(--m-touch) !important;
min-height: var(--m-touch) !important;
}
.chat-input-container .send-btn svg {
width: 20px;
height: 20px;
}
/* ------------------------------------------------------------------
19.1 触摸命中区统一补齐到 44×44(WCAG 2.5.8 / iOS HIG 最小可点区)
依据 _mtouch.mjs 实测:顶部栏图标 38×38、通用按钮高 40、
.btn-small 高 38、设置页导航项高 37、对话工具条高 40 —— 均低于 44。
只提升命中区高度/宽度,不改字号与配色。
------------------------------------------------------------------ */
.header-logo-link,
#notification-bell-btn,
#user-avatar-btn,
.m-chat-bar-btn,
.chat-upload-btn,
.btn-secondary,
.btn-primary,
.btn-small,
.btn-danger,
.settings-nav-item,
.settings-custom-select-trigger,
.page-header-actions button,
.page-header-actions a {
min-height: var(--m-touch) !important;
}
#notification-bell-btn,
#user-avatar-btn,
.chat-upload-btn {
width: var(--m-touch) !important;
min-width: var(--m-touch) !important;
}
/* 设置页导航 chip「C2」是最短项,实测只有 41px 宽 */
.settings-nav-item {
min-width: var(--m-touch) !important;
}
/* 复选框/单选框的尺寸见 24.11(已并入按钮体系,避免两处各说一套) */
.chat-input-field textarea,
.chat-input-field .chat-textarea {
max-height: 132px;
font-size: 16px;
}
#chat-hitl-approval-dock {
border-radius: var(--m-radius-card);
}
/* 附件列表横向滚动 */
.chat-file-list {
overflow-x: auto;
flex-wrap: nowrap;
-webkit-overflow-scrolling: touch;
}
/* 对话页移动端工具条(由 mobile-ui.js 注入到 .chat-container 首位) */
#m-chat-bar {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
padding: 6px 10px;
background: var(--bg-primary);
border-bottom: 1px solid var(--border-color);
}
.m-chat-bar-spacer {
flex: 1;
}
.m-chat-bar-btn {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 36px;
padding: 0 13px;
border-radius: 999px;
border: 1px solid var(--border-color);
background: var(--bg-tertiary);
color: var(--text-primary);
font-size: 0.8125rem;
font-weight: 500;
line-height: 1;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
transition: transform 0.12s var(--m-ease), background 0.15s;
}
.m-chat-bar-btn svg {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.m-chat-bar-btn:active {
transform: scale(0.95);
}
.m-chat-bar-btn.m-primary {
background: var(--accent-color);
border-color: var(--accent-color);
color: #ffffff;
}
/* _mchat12.mjs 实测:会话抽屉条目 38px、会话设置折叠头 39px、
HITL 审批人开关仅 30px——都藏在抽屉/折叠区,之前全站扫描扫不到。
这些是可点交互控件,统一提到 44px。 */
#conversation-sidebar .conversation-item,
#conversation-sidebar .project-conversation-item,
#conversation-reasoning-toggle,
.hitl-reviewer-toggle-btn,
.hitl-config-select,
.hitl-config-field .hitl-config-label {
min-height: var(--m-touch) !important;
}
.hitl-reviewer-toggle-btn {
display: inline-flex;
align-items: center;
}
}
/* --------------------------------------------------------------------------
12. 终端 / WebShell / C2
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.webshell-layout,
.c2-layout,
.terminal-layout {
flex-direction: column !important;
}
.webshell-sidebar,
.c2-sidebar,
.terminal-sidebar {
width: 100% !important;
max-width: none !important;
max-height: 40vh;
border-right: none;
border-bottom: 1px solid var(--border-color);
}
.terminal-container,
.xterm {
font-size: 12px;
}
.xterm-viewport {
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
/* 终端工具条横向滚动 */
.terminal-toolbar,
.webshell-toolbar,
.c2-toolbar {
overflow-x: auto;
flex-wrap: nowrap;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.terminal-toolbar::-webkit-scrollbar,
.webshell-toolbar::-webkit-scrollbar,
.c2-toolbar::-webkit-scrollbar {
display: none;
}
}
/* --------------------------------------------------------------------------
13. 登录页
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.login-overlay {
padding: 16px;
padding-top: calc(16px + var(--m-safe-top));
padding-bottom: calc(16px + var(--m-safe-bottom));
align-items: center;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.login-card {
width: 100%;
max-width: 420px;
padding: 22px 18px;
border-radius: var(--m-radius-sheet);
margin: auto;
}
.login-title {
font-size: 1.25rem;
}
.login-submit {
min-height: 48px;
font-size: 1rem;
width: 100%;
}
}
/* --------------------------------------------------------------------------
14. 网格 / 统计卡 / 图表
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
.stats-grid,
.kpi-grid,
.cards-grid,
.metrics-grid,
.dashboard-grid,
.overview-grid {
grid-template-columns: 1fr !important;
gap: 10px;
}
.stats-grid.m-two,
.kpi-grid.m-two {
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
}
/* 图表容器限高,避免 canvas 撑出屏幕 */
canvas {
max-width: 100% !important;
}
.chart-container,
.graph-container,
#fact-graph,
#asset-graph {
min-height: 260px;
max-height: 62vh;
}
/* 标签 / 徽章不换行溢出 */
.badge,
.tag,
.status-badge,
.pill {
white-space: nowrap;
}
}
/* --------------------------------------------------------------------------
15. 平板区间(768–1023.98px):保留桌面结构,仅做收敛
-------------------------------------------------------------------------- */
@media (min-width: 768px) and (max-width: 1023.98px) {
.main-sidebar {
width: 190px;
}
.page-content {
padding: 14px 16px;
}
.chat-messages {
padding: 16px 16px 16px 24px;
}
.conversation-sidebar {
width: 264px;
}
}
/* --------------------------------------------------------------------------
16. 横屏手机:把底部栏压薄,让出纵向空间
-------------------------------------------------------------------------- */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
:root {
--m-tabbar-h: 46px;
--m-appbar-h: 44px;
}
#m-tabbar .m-tab span {
display: none;
}
#m-tabbar .m-tab svg {
width: 21px;
height: 21px;
}
.main-sidebar {
width: min(70vw, 300px) !important;
}
}
/* --------------------------------------------------------------------------
17. 无障碍与性能
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
@media (prefers-reduced-motion: reduce) {
.main-sidebar,
.conversation-sidebar,
#m-tabbar,
#m-sheet,
.m-scrim,
#m-toast {
transition-duration: 0.01ms !important;
}
.page.active {
animation: none !important;
}
}
/* 焦点可见性(键盘 / 辅助设备) */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid var(--accent-color);
outline-offset: 2px;
}
}
/* 抽屉/面板打开时锁定背景滚动 */
body.m-locked {
overflow: hidden !important;
}
/* --------------------------------------------------------------------------
17.6 选择面板层级修正(关键)
--------------------------------------------------------------------------
.chat-input-container 带 container-type: inline-size,会创建独立的层叠
上下文,导致内部 position:fixed 的 .chat-project-panel / .role-selection-panel
/ .agent-mode-panel 的 z-index:1210 只在容器内部比较,无法与 body 级
#m-scrim(z-index:1200) 竞争 —— 实测 elementFromPoint 命中 .m-scrim,
面板收不到任何触摸事件,下滑关闭失效。
面板打开期间把容器提升到 1215(高于 scrim 1200 / drawer 1201 / sheet 1202,
低于 toast 1300),容器本身无 position,故需显式 position: relative。
-------------------------------------------------------------------------- */
body.m-panel-open .chat-input-container {
position: relative;
z-index: 1215;
}
/* --------------------------------------------------------------------------
18. 可读性 / 对比度 / 溢出修复
--------------------------------------------------------------------------
依据 _mvisual.mjs 实测:light 主题 --text-muted(#adb5bd) 在白底上只有
2.07:1,--text-secondary(#6c757d) 4.69:1,均不满足 WCAG AA(4.5:1);
11px 标签在手机上不可读;WebShell 工作区比视口宽 5px;设置页下拉选中值
与 AI 通道 chip 被 flex 压缩到 6~22px 宽。
以下仅在移动端生效,桌面端保持原样。
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
/* 18.1 文本色令牌提到 AA 级 */
html:not([data-theme="dark"]) {
--text-secondary: #525d6c;
/* 白底 6.6:1,灰底(≥#f8fafc)仍 ≥6.2:1 */
--text-muted: #5b6472;
/* 白底 6.0:1,浅灰底(#f1f3f5) 5.5:1 —— 原 #adb5bd 仅 2.07:1 */
/* 原 #3b82f6 作正文色在白底仅 3.68:1,深一档后 5.2:1 */
--accent-color: #2563eb;
}
html[data-theme="dark"] {
--text-muted: #9ca3af;
/* #111827 底 7.2:1 */
}
/* 18.2 小字号下限 12px */
.dashboard-severity-insight-score-label,
.dashboard-severity-insight-urgent-label,
.dashboard-severity-center-label,
.dashboard-severity-status-label,
.mcp-stats-kpi__label,
.mcp-stats-kpi__chip,
.pagination-info,
.pagination-page,
.hitl-tab-badge,
.version-badge,
/* 补漏(_mprobe.mjs 实测仍为 11px):projects 页的状态胶囊、统计 chip、
事实筛选标签,以及分页的「每页」标签 */
.projects-status-pill,
.projects-stat-chip,
.projects-fact-filter-label,
.pagination-page-size {
font-size: 12px !important;
}
/* 18.2b 第九轮(审计覆盖从假 14 页修成真实 29 页后)新暴露的小字。
asset-overview 的资产类型统计标签 11px、协议表头 9px;
agents-management 的角色徽章 11px;platform-rbac 的状态胶囊 11px。
图标字符(如 .tool-expand-icon 的 ▶ 10px)不属于文本,不在此列。 */
.asset-type-item span,
.asset-protocol-compact__header p,
.asset-panel-meta,
.asset-protocol-columns span,
.agent-role-badge,
.rbac-pill {
font-size: 12px !important;
}
/* 18.3 语义色徽章/数值加粗,补偿配色本身的低对比 */
.dashboard-severity-insight-risk-badge,
.dashboard-severity-status-value,
.dashboard-severity-insight-score-value {
font-weight: 700;
}
/* 18.4 WebShell / C2 / 终端工作区横向溢出 */
.webshell-main,
.webshell-workspace,
.c2-main,
.terminal-main {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
box-sizing: border-box;
overflow-x: hidden;
}
/* 18.5 设置页自定义下拉:选中值不再被压扁 */
*:has(> .settings-custom-select-value) {
min-width: 0;
}
.settings-custom-select-value {
flex: 1 1 max-content !important;
min-width: 0 !important;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 18.6 AI 通道 chip:容器占满整行并允许换行,chip 不再被压到不可读 */
*:has(> .ai-channel-editor-chip) {
flex: 1 1 100% !important;
flex-wrap: wrap !important;
width: 100%;
min-width: 0;
row-gap: 6px;
}
.ai-channel-editor-chip {
flex: 0 0 auto !important;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 18.7 对话页人工审批标签:原思路是"缩小 1px 就能完整显示",实测不成立
—— 见 24.14 的真实根因(max-width:142px 才是瓶颈,缩字号只是把裁切推迟)。 */
/* 18.8 dashboard 残留 11px 标签 */
.dashboard-batch-side-stat-label,
.dashboard-severity-progress-legend-item span,
.dashboard-severity-status-text span:not([id]) {
font-size: 12px !important;
}
/* 18.9 硬编码浅灰文本(不随令牌走)逐个纠正 */
#c2-sessions-count,
#page-knowledge-management .empty-state p,
#page-knowledge-management .empty-state div {
color: var(--text-secondary) !important;
}
/* 18.10 语义色徽章 / 状态数值:加深到 AA 级 */
.role-card-badge.enabled {
color: #157347;
background: rgba(40, 167, 69, 0.16);
}
#dashboard-status-fp {
color: #64748b !important;
}
/* 18.10b 第七轮补:语义色**文字**再次加深(原 2.09~2.88:1)
重算方式不是拿白底硬套,而是用 `_mcontrast.mjs` 逐层合成真实有效背景
(徽章自己还叠了 rgba(34,197,94,0.12)+0.04 两层底 → #dcf6e6),再对
"保持色相与饱和度、只压亮度"做二分求解,目标 5.0:1(比 AA 的 4.5 留一点
余量,避免不同背景合成算法得出不同结论)。
最要紧的是 dashboard 的「安全」徽章与四个状态计数——它们是页面主数据,
2.09:1 在手机户外基本读不出来。原则:**只动亮度,不动色相**,所以
「红=高危 / 绿=已修复 / 琥珀=待确认 / 灰=已忽略」的语义完整保留,
纠正的只是「把填充色直接当文字色用」这个反模式。
深色主题不受影响(同 18.14 的做法,见 23.4 的对称处理)。 */
html:not([data-theme="dark"]) #dashboard-severity-risk-level {
color: #107636 !important;
}
html:not([data-theme="dark"]) #dashboard-status-open {
color: #d81212 !important;
}
html:not([data-theme="dark"]) #dashboard-status-confirmed {
color: #986206 !important;
}
html:not([data-theme="dark"]) #dashboard-status-fixed {
color: #167e3c !important;
}
html:not([data-theme="dark"]) #dashboard-status-ignored {
color: #5a6d88 !important;
}
html:not([data-theme="dark"]) .dashboard-recommend-arrow {
color: #67707f !important;
}
html:not([data-theme="dark"]) .projects-fact-filter-label {
color: #5d718c !important;
}
/* 18.10c 必填项星号:样式表里有 4 种写法(`.required-mark` / `.form-required` /
`.required` / 内联 `style="color: red;"`),同一种语义 4 套颜色。
其中内联那种是纯 #ff0000,在白底上只有 4.0:1(星号也是文字,归 AA)。
统一到 18.11 已经在用的 #b02a37(≈6.0:1),一个语义一个颜色。
内联样式优先级高于普通规则,必须 !important 才压得住。 */
html:not([data-theme="dark"]) label > span[style*="color: red"],
html:not([data-theme="dark"]) label > span[style*="color:red"],
html:not([data-theme="dark"]) label > span.form-required,
html:not([data-theme="dark"]) label > span.required,
html:not([data-theme="dark"]) label > span.required-mark {
color: #b02a37 !important;
}
/* 18.11 白底红字按钮:原 #dc3545 仅 4.0:1 */
.btn-secondary.btn-danger,
.btn-secondary.btn-delete {
color: #b02a37 !important;
}
/* 18.12 C2 工具栏文字型按钮(无类名,继承 accent 色);
排除带内联背景的按钮,避免文字色与底色撞车 */
#page-c2-sessions button:not(.btn-primary):not(.btn-secondary):not(.btn-danger):not([style*="background"]) {
color: #1d4ed8 !important;
}
/* 18.13 dashboard / HITL / 知识库残留的低对比文本(原 3.3~4.5:1) */
#dashboard-fix-rate,
.dashboard-feed-tab {
color: var(--text-secondary) !important;
}
.dashboard-feed-tab.active {
color: var(--accent-color) !important;
}
.dashboard-empty-action,
#page-knowledge-management .empty-state button,
#page-knowledge-management .empty-state .btn,
#hitl-pending-count {
background-color: #2563eb !important;
border-color: #2563eb !important;
color: #ffffff !important;
font-weight: 600;
}
/* 18.14 硬编码灰/蓝文本再深一档(实测 4.0~4.34:1)
注意:这个深灰只在亮色主题下成立,深色主题下由区块 23.4 覆写。 */
html:not([data-theme="dark"]) #chat-hitl-shortcut-text,
html:not([data-theme="dark"]) #page-hitl .hitl-reviewer-toggle-btn,
html:not([data-theme="dark"]) #page-hitl .hitl-reviewer-toggle-btn span {
color: #4a5462 !important;
}
#projects-list-count {
color: #0052cc !important;
}
#page-settings .required-mark {
color: #b02a37 !important;
}
}
/* ==========================================================================
19. 设置页两栏布局在移动端塌陷(实测正文区仅剩 98px 宽)
--------------------------------------------------------------------------
根因:.page-content.settings-layout 是 flex-direction:row,
.settings-sidebar 固定 200px。390px 视口下 .settings-content 只剩 162px,
再扣 32px×2 内边距 → 正文 98px,下拉选中值与 AI 通道 chip 被压到 6~22px。
方案:移动端改单列,左侧导航变成顶部横向滚动的胶囊标签条。
========================================================================== */
@media (max-width: 767.98px) {
#page-settings .page-content.settings-layout {
flex-direction: column !important;
padding: 0 0 16px !important;
}
#page-settings .settings-sidebar {
width: 100% !important;
max-width: 100% !important;
height: auto !important;
flex: 0 0 auto !important;
border-right: 0 !important;
border-bottom: 1px solid var(--border-color);
background: var(--bg-primary);
position: sticky;
top: 0;
z-index: 5;
}
#page-settings .settings-nav {
display: flex !important;
flex-direction: row !important;
width: 100% !important;
height: auto !important;
min-height: 0 !important;
overflow-x: auto !important;
overflow-y: hidden !important;
gap: 6px;
padding: 8px 10px;
box-sizing: border-box;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
#page-settings .settings-nav::-webkit-scrollbar {
display: none;
}
#page-settings .settings-nav-item {
flex: 0 0 auto !important;
width: auto !important;
max-width: none !important;
white-space: nowrap;
padding: 8px 13px;
border-radius: 999px;
font-size: 13px;
border-left: 0 !important;
border-bottom: 0 !important;
}
#page-settings .settings-nav-item.active {
background: var(--accent-color);
color: #ffffff;
}
#page-settings .settings-content {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
flex: 1 1 auto !important;
padding: 14px 12px 24px !important;
box-sizing: border-box;
overflow-x: hidden;
}
/* 19.1 设置页内所有可伸缩容器放开宽度,禁止再被压缩 */
#page-settings .settings-section-content,
#page-settings .settings-subsection,
#page-settings .ai-channel-manager,
#page-settings .ai-channel-manager-body,
#page-settings .ai-channel-editor,
#page-settings .ai-channel-editor-head,
#page-settings .ai-channel-editor-meta,
#page-settings .ai-channel-switcher,
#page-settings .ai-channel-switcher-field,
#page-settings .settings-custom-select,
#page-settings .settings-custom-select-trigger {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
box-sizing: border-box;
}
/* 19.2 通道选择区原为 grid,移动端改为单列堆叠 */
#page-settings .ai-channel-switcher-field {
display: block !important;
}
/* 19.3 设置页宽表(审计日志 / 存储明细)自带 min-width:560px,
在 390px 视口直接撑破页面 → 改为容器内横向滚动 */
#page-settings *:has(> table.audit-log-table) {
overflow-x: auto !important;
max-width: 100% !important;
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
}
#page-settings table.audit-log-table {
max-width: none;
}
/* 19.4 审计筛选区:.audit-field 固定 min-width:150px,
320px 视口下第二个字段被挤出屏幕 → 允许压缩 + 整行换行 */
#page-settings .audit-field {
min-width: 0 !important;
flex: 1 1 140px !important;
}
#page-settings .audit-datetime-field,
#page-settings .audit-datetime-btn {
min-width: 0 !important;
}
#page-settings *:has(> .audit-field) {
flex-wrap: wrap !important;
}
}
/* --------------------------------------------------------------------------
20. 页面头功能按钮布局 / 工具条触控 / 底部安全区
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
/* 20.1 页面头功能按钮位置
根因:style.css:19132 的 @media (max-width: 768px) 把 .page-header-actions
设为 flex-direction: column; align-items: stretch;。本文件此前只覆盖了
align-items / flex-wrap / width,没有覆盖 flex-direction,于是
「刷新 / 批量导入 / 新增资产」这类按钮在手机上各占一行、各自居中。
改为横向换行自适应排列:能一行就一行(宽度 ≥444 左右),放不下自动换行,
任何设备宽度下按钮都完整可见、无需横向滑动。
注意:不要改回 nowrap + overflow-x —— 那会让主按钮(保存/新建)被推到
视口外需横滑才能看到,违背"自适应"诉求。 */
.page-header-actions,
.page-header > .page-header-actions,
.platform-rbac-page .page-header-actions,
.mcp-monitor-page .page-header-actions {
flex-direction: row !important;
flex-wrap: wrap !important;
align-items: center !important;
justify-content: flex-start !important;
gap: 8px !important;
width: 100%;
padding-bottom: 0;
}
/* 20.1b「已与服务端同步」状态块(tool-guard)84px 太占宽,会把主按钮挤出
一行。允许它在窄容器里收缩省略,留出空间给按钮。 */
.page-header-actions > .tool-guard-save-state {
flex: 0 1 auto;
min-width: 0;
max-width: 96px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 11px;
}
.page-header-actions > * {
flex: 0 0 auto;
white-space: nowrap;
}
/* 头部文字按钮统一到可点尺寸并留出横向内边距 */
.page-header-actions > button,
.page-header-actions > a.btn,
.page-header-actions > .btn-primary,
.page-header-actions > .btn-secondary,
.page-header-actions > .btn-danger {
min-height: var(--m-touch) !important;
min-width: 64px;
padding-left: 14px !important;
padding-right: 14px !important;
}
/* 20.2 图标型按钮(分页 ‹ › 等)原本只有 20~32px 宽,点不中 */
.btn-icon-pagination,
.btn-icon,
.table-icon-btn,
.page-size-select {
min-width: var(--m-touch) !important;
min-height: var(--m-touch) !important;
}
/* 20.3 工作流画布工具条:5 个按钮挤在 32px 宽里,文字被截断 */
.workflow-toolbar,
.workflow-toolbar-actions,
.workflow-canvas-toolbar,
.workflow-toolbar-group {
flex-wrap: wrap !important;
gap: 6px !important;
}
.workflow-toolbar-button,
.workflow-save-button {
min-width: 72px !important;
min-height: var(--m-touch) !important;
padding: 0 10px !important;
font-size: 12px;
}
/* 20.4 小号按钮保底触控高度(原 .btn-small 只有 30~36px 高) */
.btn-small {
min-height: var(--m-touch) !important;
padding: 0 12px !important;
}
/* 20.5 底部安全区:在标签栏之上再留 10px,
避免滚到底后最后一行控件仍贴住标签栏 */
.content-area {
padding-bottom: calc(var(--m-tabbar-h) + var(--m-safe-bottom) + 10px);
}
/* 20.6 页面内自定义下拉触发器高度不足 44px */
.settings-custom-select-trigger,
.monitor-filter-select-trigger,
.projects-filter-select-trigger {
min-height: var(--m-touch) !important;
}
/* 20.7 对话页顶部工具条按钮原为 36px 高 */
.m-chat-bar-btn {
min-height: var(--m-touch) !important;
padding: 0 12px !important;
}
/* 20.8 上传等纯图标按钮宽高都不足 */
.chat-upload-btn {
min-width: var(--m-touch) !important;
min-height: var(--m-touch) !important;
}
}
/* ==========================================================================
21. 内容区滚动模型修复(手机端局部滚动容器塌陷)
--------------------------------------------------------------------------
桌面端这些页面用「固定高度容器 + 内部 overflow:hidden」把滚动锁在局部,
例如 .asset-list-card 自己滚分页。390x664 的手机视口下父级 flex 空间不足,
这些局部滚动容器被压到 16~48px 高,内部控件被 overflow:hidden 裁掉,
而且无论怎么滑动都露不出来。改成「内容区整页滚动 + 内部容器按内容展开」。
========================================================================== */
@media (max-width: 767.98px) {
/* 21.1 资产库:.asset-list-card 实测仅 48px 高,里面的分页控件 183px 全被裁 */
.page-content.asset-content {
display: block !important;
overflow-y: auto !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch;
}
.asset-list-card {
display: block !important;
flex: none !important;
height: auto !important;
max-height: none !important;
min-height: 0 !important;
overflow: visible !important;
}
#asset-pagination,
.pagination-container.pagination-fixed {
position: static !important;
height: auto !important;
min-height: 0 !important;
}
/* 21.2 技能管理:#skills-list 实测仅 16px 高,5483px 内容挤成一条缝 */
.page-content.page-content-with-pagination {
display: block !important;
overflow-y: auto !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch;
}
#skills-list,
.skills-grid {
flex: none !important;
height: auto !important;
max-height: none !important;
min-height: 0 !important;
overflow: visible !important;
}
#skills-pagination {
position: static !important;
height: auto !important;
}
/* 21.3 平台 RBAC:.rbac-panel-detail 实测 2px、#rbac-tab-roles 28px */
.platform-rbac-page .page-content {
display: block !important;
overflow-y: auto !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch;
}
#rbac-view-users,
.rbac-layout {
display: block !important;
height: auto !important;
min-height: 0 !important;
overflow: visible !important;
}
.rbac-panel,
.rbac-tab-panel,
#rbac-tab-roles,
#rbac-tab-permissions,
#rbac-tab-resources,
#rbac-tab-audit {
flex: none !important;
height: auto !important;
max-height: none !important;
min-height: 0 !important;
overflow: visible !important;
}
/* 21.4 工作流:侧栏节点列表与画布工具条被 overflow:hidden 裁掉 */
.page-content.workflow-page-content {
display: block !important;
min-height: 0 !important;
overflow-y: auto !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch;
}
.workflow-sidebar {
height: auto !important;
max-height: none !important;
overflow: visible !important;
}
/* 画布本身需要一块固定高度,不能被内容压成 0 */
.workflow-main {
height: auto !important;
min-height: 380px !important;
max-height: none !important;
overflow: visible !important;
}
/* 21.5 C2 会话「重置」按钮实测 24x44,横向点不中 */
#page-c2-sessions button,
.c2-sessions-page button {
min-width: 44px !important;
}
}
/* --------------------------------------------------------------------------
22. 平板竖屏(768–1023.98px):对话页三个选择面板同样贴底
桌面规则 style.css:29402 的 @media (max-width:768px) 把 .role-selection-panel /
.agent-mode-panel 改成 width:calc(100vw - 16px) + left:-8px,但仍是
position:absolute,定位基准是各自在 100–200px 处的按钮包裹层;实测 768x1024
下两者右边缘分别越界 563px 与 654px,面板完全落在屏幕之外。
-------------------------------------------------------------------------- */
@media (min-width: 768px) and (max-width: 1023.98px) {
.role-selection-panel,
.chat-project-panel,
.agent-mode-panel {
position: fixed !important;
left: 16px !important;
right: 16px !important;
bottom: var(--m-panel-offset, 16px) !important;
top: auto !important;
width: auto !important;
max-width: none !important;
max-height: var(--m-panel-maxh, 62vh);
border-radius: var(--m-radius-sheet);
z-index: 1210;
box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.16), 0 -1px 0 rgba(0, 0, 0, 0.06);
}
}
/* --------------------------------------------------------------------------
22.2 触摸型横屏平板(1024–1439.98px + pointer:coarse):模式面板仍会越界
_mchat11.mjs 实测 1024x768 下 .agent-mode-panel 右边缘 1035 > 1024(offR=11)。
桌面鼠标保持原样(_mdesktop.mjs 已证明 1024x768 的桌面溢出为既有问题,不归本层管)。
-------------------------------------------------------------------------- */
@media (min-width: 1024px) and (max-width: 1439.98px) and (pointer: coarse) {
.role-selection-panel,
.chat-project-panel,
.agent-mode-panel {
position: fixed !important;
left: 16px !important;
right: 16px !important;
bottom: var(--m-panel-offset, 16px) !important;
top: auto !important;
width: auto !important;
max-width: none !important;
max-height: var(--m-panel-maxh, 62vh);
border-radius: var(--m-radius-sheet);
z-index: 1210;
box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.16), 0 -1px 0 rgba(0, 0, 0, 0.06);
}
}
/* --------------------------------------------------------------------------
23. 深色主题对比度修正(_mdark.mjs 全站实测出 4 类违规)
深色令牌里 --accent-color 是 #60a5fa(浅蓝),配白字只有 2.54:1;
语义绿 #15803d 在 #111f29 底上 3.33:1;危险红 #b02a37 在 #111827 底上 2.73:1。
统一只改文字色、不动背景,保证亮色主题与桌面端完全不受影响。
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
/* 23.1 浅蓝底(#60a5fa)上的白字 → 深墨蓝 #0b1120,实测 9.4:1 */
html[data-theme="dark"] .btn-primary,
html[data-theme="dark"] .btn-primary:hover,
html[data-theme="dark"] .btn-primary:focus,
html[data-theme="dark"] .m-chat-bar-btn.m-primary,
html[data-theme="dark"] .settings-nav-item.active,
html[data-theme="dark"] .chat-input-container .send-btn,
html[data-theme="dark"] .ai-channel-save-btn,
html[data-theme="dark"] #hitl-pending-count,
html[data-theme="dark"] .dashboard-empty-action,
html[data-theme="dark"] #page-knowledge-management .empty-state button,
html[data-theme="dark"] #page-knowledge-management .empty-state .btn {
color: #0b1120 !important;
}
html[data-theme="dark"] .btn-primary svg,
html[data-theme="dark"] .m-chat-bar-btn.m-primary svg,
html[data-theme="dark"] .chat-input-container .send-btn svg,
html[data-theme="dark"] .settings-nav-item.active svg {
color: #0b1120 !important;
fill: currentColor;
stroke: currentColor;
}
/* 23.2 语义绿在深底上提亮:#15803d → #4ade80,实测 8.9:1 */
html[data-theme="dark"] .is-success,
html[data-theme="dark"] .dashboard-kpi-sub-text.is-success,
html[data-theme="dark"] .mcp-stats-kpi__value--rate.is-success,
html[data-theme="dark"] .mcp-stats-kpi__status.is-success,
html[data-theme="dark"] .mcp-stats-tool-item__rate.is-success,
html[data-theme="dark"] .mcp-stats-rate.is-success {
color: #4ade80 !important;
}
/* 刷新闪烁态「上次更新」徽章同样是硬编码 #15803d */
html[data-theme="dark"] .dashboard-last-updated.is-flash {
color: #4ade80 !important;
background: rgba(74, 222, 128, 0.14);
}
/* 23.3 危险红在深底上提亮:#b02a37 → #f87171,实测 6.4:1 */
html[data-theme="dark"] {
--danger-color: #f87171;
--success-color: #4ade80;
}
html[data-theme="dark"] .required-mark,
html[data-theme="dark"] .btn-danger,
html[data-theme="dark"] .btn-secondary.btn-small.danger,
html[data-theme="dark"] .text-danger,
html[data-theme="dark"] .is-danger {
color: #f87171 !important;
}
/* 23.4 18.14 的硬编码深灰只对亮色成立,深色下交回 --text-muted(#9ca3af) */
html[data-theme="dark"] #chat-hitl-shortcut-text,
html[data-theme="dark"] #page-hitl .hitl-reviewer-toggle-btn,
html[data-theme="dark"] #page-hitl .hitl-reviewer-toggle-btn span {
color: var(--text-muted) !important;
}
}
/* ==========================================================================
24. 按钮 / 控件视觉体系统一
--------------------------------------------------------------------------
_mbtn.mjs 在 27 页上实测 645 个可点控件,改造前的客观问题:
· style.css 里**没有 .btn 基类**(`^\s*\.btn\s*\{` 零命中),因此没有尺寸
基准:.btn-danger 是唯一 10px 20px / 15px 的"大一号"按钮,.btn-search 与
.pagination-btn 固定 height:32px,.btn-icon-pagination 只有 20x20,
.settings-custom-select-trigger 同一页存在 32/38/42/48px 四种高度。
· 类语义被打破:.btn-secondary 一个类出现 **12 种签名**(205 个实例)——
高度 44/52/71、圆角 6px/8px、字号 13px/14px、横内边距 12/14/16/18px;
.btn-primary 6 种、.btn-small 4 种。
· 度量发散:圆角 11 种(0/4/5/6/7/8/10/12/14/999px/50%)、字号 9 种
(10.56~16px)、字重 7 档(400/500/520/560/600/650/700)。
· 同一行内尺寸互不相同(39 处容器):mcp-management 分页「上一页/下一页」
被 style.css:11867 的 `flex:1 + max-width:120px` 压到 53px 宽、中文换行
撑高成 53x71,而同行「首页/末页」是 44x52;资产库工具条三个按钮的横内边距
是 16/16/6;工作流工具条的圆角是 7px 与 0px。
· 手机主导航(底部标签栏)字号 10.56px、角标 9.6px,低于本层 18.2 自己
定的 12px 下限。
本节只做"收敛",不改任何布局结构与配色(配色仍由 18/23 节负责):
按钮高度 -> 44px(触控下限,也是唯一档)
圆角 -> 矩形控件 10px / 方形图标按钮 12px / 标签胶囊 999px
字号 -> 标准 14px、小号与胶囊 13px
字重 -> 常规 500、主要行动与选中态 600
横内边距 -> 标准 16px、小号 12px
分组间距 -> 8px
两条刻意不碰:`.btn-compact`(style.css:32833 显式 `min-height:0`,它是侧栏
分页的紧凑按钮,抬到 44px 会把侧栏撑破)与 `.chat-files-dropdown-item`
(是左对齐的菜单行,不是按钮,单独定义)。
========================================================================== */
@media (max-width: 767.98px) {
/* 24.1 基底:所有"按钮化控件"共用同一套度量,取代原先散落在
20.1 / 20.2 / 20.4 / 20.6 / 20.7 / 20.8 的逐类补丁。
只设左右内边距,不动上下——保持 style.css 里各族的垂直视觉重心。 */
:is(.btn-primary, .btn-secondary, .btn-danger, .btn-ghost,
.btn-small, .btn-sm, .btn-filter, .btn-search,
.mcp-detail-btn, .btn-delete, .btn-loading,
.page-header-actions > button, .page-header-actions > a.btn,
.tools-actions button, .attack-chain-action-btn,
.attack-chain-filters button, .robot-manager-toolbar button,
.robot-command-entry button, .asset-batch-menu button,
.workflow-toolbar-button, .workflow-save-button,
.m-chat-bar-btn, .chat-session-shortcut, .role-selector-btn,
.new-chat-btn, .ai-channel-save-btn, .dashboard-empty-action,
.dashboard-severity-status-filter-btn, .empty-state button,
.workflow-node-palette button, .c2-sessions-quick-links button,
.syntax-guide-toggle) {
min-height: var(--m-ctl-h) !important;
padding-left: var(--m-ctl-pad-x) !important;
padding-right: var(--m-ctl-pad-x) !important;
font-size: var(--m-ctl-fs) !important;
font-weight: var(--m-ctl-fw) !important;
border-radius: var(--m-r-btn) !important;
line-height: 1.25 !important;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* 24.2 小号档:横内边距收到 12px,字号 13px(高度仍是 44px 触控下限) */
:is(.btn-small, .btn-sm, .btn-filter, .btn-search,
.btn-icon-pagination, .mcp-detail-btn,
.tools-actions button, .workflow-toolbar-button,
.workflow-save-button, .m-chat-bar-btn, .chat-session-shortcut,
.workflow-node-palette button, .c2-sessions-quick-links button,
.syntax-guide-toggle, .robot-command-entry button) {
padding-left: var(--m-ctl-pad-x-sm) !important;
padding-right: var(--m-ctl-pad-x-sm) !important;
font-size: var(--m-ctl-fs-sm) !important;
}
/* 24.3 图标档:纯图标按钮一律 44x44 正方形,圆角略大于文字按钮,
让"图标块"与"文字条"在一起时不显单薄。
(原来 .chat-upload-btn 36x36/圆角 10、.btn-icon-pagination 20x20、
顶部图标 38x38,三种口径并存) */
:is(.btn-icon, .btn-icon-pagination, .chat-upload-btn,
.m-menu-btn, .m-more-btn, .m-close-btn, .projects-list-item-menu,
#notification-bell-btn, #user-avatar-btn, .header-actions .btn-icon) {
width: var(--m-ctl-h) !important;
min-width: var(--m-ctl-h) !important;
height: var(--m-ctl-h) !important;
min-height: var(--m-ctl-h) !important;
padding: 0 !important;
border-radius: var(--m-r-icon) !important;
font-size: var(--m-ctl-fs-sm) !important;
font-weight: var(--m-ctl-fw) !important;
}
:is(.btn-icon, .chat-upload-btn, .m-menu-btn, .m-more-btn, .m-close-btn,
#notification-bell-btn, #user-avatar-btn) svg {
width: 20px;
height: 20px;
}
/* 24.4 行内文字按钮(.btn-link):原来只有 32px 宽、圆角 0、横内边距 6px,
和旁边 6px 圆角按钮排在一行时基线都不齐。给它按钮的位置感,但保持无底无框。 */
.btn-link {
min-width: var(--m-touch) !important;
min-height: var(--m-ctl-h) !important;
padding-left: var(--m-ctl-pad-x-sm) !important;
padding-right: var(--m-ctl-pad-x-sm) !important;
font-size: var(--m-ctl-fs-sm) !important;
font-weight: var(--m-ctl-fw) !important;
border-radius: var(--m-r-btn) !important;
background: transparent !important;
box-shadow: none !important;
}
/* 24.5 标签 / 胶囊档:凡是"一组里选一个"的控件统一胶囊形,
原来 .rbac-tab 是 0 圆角 + 1px 内边距、.projects-tab 是 8px、
.hitl-page-tab 是 0 圆角、.asset-period-switch 是 11px 字号、
.c2-tasks-time-preset-btn 是 12px 字号。 */
:is(.rbac-tab, .projects-tab, .dashboard-feed-tab, .hitl-page-tab,
.rbac-workspace-tab, .settings-nav-item, .preset-chip,
.asset-period-switch button, .c2-tasks-time-preset-btn,
.hitl-reviewer-toggle-btn) {
min-height: var(--m-ctl-h) !important;
padding-left: 14px !important;
padding-right: 14px !important;
font-size: var(--m-ctl-fs-sm) !important;
font-weight: var(--m-ctl-fw) !important;
border-radius: var(--m-r-pill) !important;
line-height: 1.25 !important;
white-space: nowrap;
box-sizing: border-box;
}
:is(.rbac-tab, .projects-tab, .dashboard-feed-tab, .hitl-page-tab,
.rbac-workspace-tab, .settings-nav-item, .preset-chip,
.asset-period-switch button, .c2-tasks-time-preset-btn,
.hitl-reviewer-toggle-btn).is-active,
:is(.rbac-tab, .projects-tab, .dashboard-feed-tab, .hitl-page-tab,
.rbac-workspace-tab, .settings-nav-item, .preset-chip,
.asset-period-switch button, .c2-tasks-time-preset-btn,
.hitl-reviewer-toggle-btn).active {
font-weight: var(--m-ctl-fw-strong) !important;
}
/* 分段控件(周期切换)容器跟着胶囊化,3px 内衬保留 */
.asset-period-switch {
border-radius: var(--m-r-pill) !important;
padding: 3px !important;
gap: 2px !important;
display: inline-flex !important;
align-items: center;
}
/* 24.5b rbac-summary-count 是"查看明细 70"的**虚线文字按钮**,style.css 设计为
border-radius:0 + 虚线底边 + color:secondary。上面把它误纳入胶囊档,被强制
成全圆角、无背景的怪圈(实测 42×44、border-radius:999px)。排除它并还原
虚线文字按钮本义,仅给移动端 44px 命中高。 */
.rbac-summary-count--interactive {
border-radius: 0 !important;
background: none !important;
min-height: var(--m-touch) !important;
padding: 0 2px 1px !important;
display: inline-flex !important;
align-items: flex-end;
}
/* 24.6 下拉触发器:与按钮同高同圆角,但文字左对齐、右端留给箭头。
原来同一类在资产库有 32/38/42/48px 四种高度、在内距 9/11/12 之间跳动。 */
:is(.settings-custom-select-trigger, .monitor-filter-select-trigger,
.projects-filter-select-trigger, .vuln-filter-select-trigger,
.tasks-filter-select-trigger, .chat-files-filter-select-trigger,
.c2-form-select-trigger, .asset-custom-select .settings-custom-select-trigger) {
min-height: var(--m-ctl-h) !important;
padding-left: var(--m-ctl-pad-x-sm) !important;
padding-right: var(--m-ctl-pad-x-sm) !important;
font-size: var(--m-ctl-fs) !important;
font-weight: var(--m-ctl-fw) !important;
border-radius: var(--m-r-ctl) !important;
line-height: 1.25 !important;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
/* 24.7 分页结构修复
实测:mcp-management 分页里「上一页/下一页」53x71(中文被挤成两行),
同行「首页/末页」44x52 —— style.css:11867 在 ≤768px 给
`.pagination-fixed .pagination-controls .btn-secondary` 设了
`flex:1; min-width:60px; max-width:120px`,按钮被压窄后文字换行撑高。
这里取消伸缩、禁止换行、允许整行折行,四个按钮回到同一高度。 */
:is(.pagination-controls, .pagination-fixed .pagination-controls,
.pagination) {
display: flex !important;
flex-wrap: wrap !important;
align-items: center !important;
gap: 6px !important;
}
:is(.pagination-controls, .pagination-fixed .pagination-controls) > :is(button, .btn-secondary, .btn-primary) {
flex: 0 0 auto !important;
max-width: none !important;
min-width: var(--m-touch) !important;
min-height: var(--m-ctl-h) !important;
padding-left: var(--m-ctl-pad-x-sm) !important;
padding-right: var(--m-ctl-pad-x-sm) !important;
font-size: var(--m-ctl-fs-sm) !important;
border-radius: var(--m-r-ctl) !important;
white-space: nowrap !important;
}
/* 24.8 分组间距统一:控件成组出现时一律 8px,原来 6/8/10/12/16 混用 */
:is(.page-header-actions, .tools-actions, .rbac-tabs, .projects-tabs,
.dashboard-feed-tabs, .attack-chain-filters, .asset-toolbar-main,
.workflow-toolbar, .workflow-toolbar-group, .workflow-node-palette,
.chat-composer-context, .chat-composer-footer-leading,
.c2-tasks-time-presets, .c2-sessions-quick-links, .settings-nav,
.asset-saved-views, .header-actions, .hitl-reviewer-toggle,
.syntax-guide-actions, .rbac-summary-permissions, .empty-state) {
gap: var(--m-ctl-gap) !important;
}
/* 24.9 可点卡片 / 行的圆角归一到卡片档(原为 0/4/8/10/12 五种混用) */
:is(.dashboard-resource-item, .dashboard-severity-legend-item,
.dashboard-severity-status-cell, .dashboard-access-stat,
.dashboard-recommend-item, .tool-guard-rule-summary,
.rbac-user-row, .stat-card, .dashboard-kpi-card) {
border-radius: var(--m-radius-card) !important;
}
/* 24.10 层级:主要行动与选中态用 600,其余 500。
原来同一个界面里并存 400/500/520/560/600/650 六档字重。 */
:is(.btn-primary, .new-chat-btn, .ai-channel-save-btn,
.rbac-summary-count, .empty-state button,
.role-selector-btn.m-primary) {
font-weight: var(--m-ctl-fw-strong) !important;
}
.chat-input-container .send-btn {
font-weight: var(--m-ctl-fw-strong) !important;
}
/* 24.11 表单勾选类控件:视觉 24px,命中面 44x44(由整行 label 提供)
实测结论(_mchk2.mjs,运行时注入对照实验):Chrome 对
input[type=checkbox]/radio 这类替换元素**不采纳作者的 padding**——连运行时
注入 `input[type=checkbox]{padding:10px !important}` 都不生效(computed
padding 恒为 0、外框恒为 24x24)。按 CSS 层叠规范,!important 的优先级是
反的:UA 的 !important 高于作者的 !important,而 Blink 的 UA 样式表对勾选框
强制了内衬。所以"把本体撑到 44px"这条路在本平台上不成立(真撑大了视觉上也
是个巨大方框)。正确做法是:本体保持 24px 视觉,把 44px 命中面交给整行
label —— 实测该行 68x44,点行内任意位置都能切换。 */
input[type="checkbox"],
input[type="radio"] {
width: 30px !important;
height: 30px !important;
min-width: 30px !important;
min-height: 30px !important;
/* 仍需 !important:style.css 的 `.auto-refresh-toggle input[type=checkbox]`
(18871)等后代选择器特异性更高,会把它按回 18px */
}
label:has(> input[type="checkbox"]),
label:has(> input[type="radio"]) {
min-height: var(--m-touch);
display: inline-flex;
align-items: center;
gap: 8px;
}
/* 24.12 胶囊标签条:横向滚动,不换行、不裁切
_mprobe.mjs 实测 `.hitl-page-tabs` 在 320px 视口 clientW=292 / scrollW=371,
且 overflow-x 是 visible —— 第 4 个页签右边缘 385 > 视口 320,被祖先
`.page-content{overflow-x:hidden}` 直接切掉,**手指也滑不出来**:一个点不到
的页签。390px 下也已溢出 9px。这里沿用文件里既有的"横向滚动 + nowrap"惯例
(见 6.x 终端工具条、19.3 宽表),把溢出交还给标签条自己滚动。
只列真正单行排布的胶囊条,工作流工具条这类靠换行展示全部的容器不动。 */
:is(.hitl-page-tabs, .projects-tabs, .dashboard-feed-tabs, .rbac-tabs,
.rbac-workspace-tabs, .c2-tasks-time-presets) {
flex-wrap: nowrap !important;
overflow-x: auto !important;
overflow-y: hidden !important;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
}
:is(.hitl-page-tabs, .projects-tabs, .dashboard-feed-tabs, .rbac-tabs,
.rbac-workspace-tabs, .c2-tasks-time-presets)::-webkit-scrollbar {
display: none;
}
/* 条目不许被压扁(否则"横向滚动"会退化成"每个页签都被挤成省略号") */
:is(.hitl-page-tabs, .projects-tabs, .dashboard-feed-tabs, .rbac-tabs,
.rbac-workspace-tabs, .c2-tasks-time-presets) > * {
flex: 0 0 auto !important;
scroll-snap-align: start;
}
/* 24.13 projects 页残留小字号已并入 18.2 的下限清单(保持单一出处) */
/* 24.14 对话页「人工审批:关闭」按钮:文字不再被截断
实测根因(_mprobe.mjs):按钮被 style.css 的 max-width:142px 钉死,而内衬
16px 在图标+间距之外只剩 69px 给标签,标签实际需要 84px → 恒显省略号
(iPhone SE 上只剩 49px)。原 18.7 用"字号缩 1px"绕,属于治标。
底部这一行总宽 368px,上传键 44 + 发送键 44 之后仍有富余,放宽即可完整显示。 */
#chat-hitl-shortcut {
max-width: none !important;
padding-left: var(--m-ctl-pad-x-sm) !important;
padding-right: var(--m-ctl-pad-x-sm) !important;
font-size: var(--m-ctl-fs-sm) !important;
overflow: hidden !important;
}
/* 24.9 会话快捷按钮的文本由 JS 动态写入(如「Agent 审查:关闭 · OpenAI 协议模型 · qwen3-max」),
实测比按钮宽 30px+。原来强制 nowrap + visible 会被按钮容器裁掉尾部;
改为在按钮内收缩,超出显示省略号(回到 style.css:44875 的初衷)。 */
#chat-hitl-shortcut-text {
flex: 1 1 auto !important;
min-width: 0 !important;
font-size: var(--m-ctl-fs-sm) !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
text-align: left;
}
/* 24.15 自制开关:**不得**套用 24.11 的原生勾选框尺寸
(必须排在 24.11 之后,靠层叠接管)
tool-guard 页「调用拦截」把复选框做成了开关:tool-guard.css:68 定义
30×18 胶囊轨道 + 12px 圆形滑块(::after,left:2,选中 translateX(12));
tool-guard.js:205-218 同时还给这个 input 挂了 `.theme-checkbox` 类。
24.11 是 `input[type=checkbox]{width/height:24px !important}`——特异性
(0,1,1) 低于 `.tool-guard-page .tool-guard-switch input[type=checkbox]`
(0,2,1),但一加 !important 就反超,于是:
· 轨道被压成 24×24 方块(本该是 30×18 的胶囊)
· 滑块仍是 left2 + 12 + 位移12 = 右缘 26,**越出轨道**
· 未选中态:24 宽轨道里左侧一个 12px 圆点、右侧空 10px → 坏掉的控件
这里按原设计还原轨道与滑块,并用更高特异性接管两态位移。 */
.tool-guard-page .tool-guard-switch input[type="checkbox"] {
width: 30px !important;
height: 18px !important;
min-width: 30px !important;
min-height: 18px !important;
border-radius: 999px !important;
padding: 0 !important;
position: relative !important;
}
/* 未选中态:滑块归位。transform 必须显式声明,否则会从 24.16 的命中区
规则里继承 `translate(-50%,-50%)`,把滑块挪到轨道外 */
.tool-guard-page .tool-guard-switch input[type="checkbox"]::after {
content: '' !important;
position: absolute !important;
top: 2px !important;
left: 2px !important;
width: 12px !important;
height: 12px !important;
border-radius: 50% !important;
background: #fff !important;
transform: none !important;
}
/* 选中态:left2 + 滑块12 + 位移12 = 右缘 26,轨道内宽 28(30 减 1px 边框),
右侧留 2px —— 与未选中态左侧留 2px 对称 */
.tool-guard-page .tool-guard-switch input[type="checkbox"]:checked::after {
transform: translateX(12px) !important;
}
/* 裸开关:label 内只有 input、没有文字时宽度只有 24px,给它 44 命中面。
`justify-content` 在 auto 宽度下无副作用(内容即容器),所以对带文字的
「启用调用拦截」那行(104×44)不产生影响。 */
.tool-guard-page .tool-guard-switch {
min-height: var(--m-touch);
min-width: var(--m-touch);
justify-content: center;
}
/* 24.16 勾选框(.theme-checkbox):等比圆角 + 44px 命中面
(a) 等比圆角:24.11 把本体放大到 24px,但 `.theme-checkbox` 自己的
border-radius 是 4px(为默认 16px 设计),24px 下显得方。按钮体系是
10px/44px ≈ 23%,这里取 6px/24px = 25%——**按尺寸等比**才叫协调。
(b) 44px 命中面:24.11 的注释写着"命中面交给整行 label",但全站有 22 个
勾选框并没有可点的 label(mcp-management 工具列表 20 个、
asset-library 表头全选、info-collect 全选),本体 24×24 在手机上
很难点中。`.theme-checkbox` 是 appearance:none 的,此时 input 会渲染
伪元素、**且伪元素参与命中测试**;原生外观下则被 UA 样式表锁死——
这正是 24.11 里"padding 撑不开"的原因,两者是同一个机制的两面。
所以用一个透明 44×44 ::after 撑开命中面:不占布局、不改视觉。 */
.theme-checkbox {
position: relative !important;
border-radius: 6px !important;
}
.theme-checkbox::after {
content: '' !important;
position: absolute !important;
top: 50% !important;
left: 50% !important;
width: 44px !important;
height: 44px !important;
transform: translate(-50%, -50%) !important;
background: transparent !important;
}
/* 24.17 开关型隐形 input 必须排除在 24.11 之外
项目里其实有**三套**勾选/开关机制,24.11 只能套第一套:
1. `.theme-checkbox`(style.css:43032)——真·勾选框,appearance:none、
可见可点,24.16 已给它等比圆角与 44px 命中面;
2. `.modern-checkbox` + `.checkbox-custom`(style.css:11011)——开关,
input 本体是 `position:absolute; opacity:0; width:0; height:0;
pointer-events:none`,**根本不接收点击**,点击由外层 `.checkbox-label`
代收,视觉交给兄弟元素 `.checkbox-custom`(44×24 胶囊 + 16px 滑块,
选中 translateX(20):2+16+20 = 38 ≤ 40,左右各留 2px,设计本身正确);
3. `.tool-guard-page .tool-guard-switch input`——30×18 开关,见 24.15。
24.11 给第 2 套的 input 强推 24×24,等于在页面上多出一层 24×24 的隐形盒子
(因为 pointer-events:none 所以不拦点击,但没有任何存在理由,也会让
审计脚本把它误判成"点不中的勾选框")。这里恢复它的 0×0。 */
input.modern-checkbox {
width: 0 !important;
height: 0 !important;
min-width: 0 !important;
min-height: 0 !important;
}
/* 第 2 套开关的真实点击接收者是 `.checkbox-label`(实测 150×40,差 4px)。
这才是"点不中"的正确修复点——修 label,而不是修那个不可见的 input。
必须 !important:info-collect 有一条
`#info-collect-full-option .checkbox-label{min-height:40px}`(特异性 1,2,0)
压着这里的 (0,1,0);基础规则那边的 40px 则是 `padding:8px 0` + 24px 内容得来的。 */
.checkbox-label {
min-height: var(--m-touch) !important;
}
}
/* ==========================================================================
区块 25:审计覆盖被修正后(假 14 页 → 真实 29 页)新暴露的问题收口
--------------------------------------------------------------------------
起因:`_mtouch.mjs` / `_mdark.mjs` 的页表里有 6 个并不存在的页面 id,而
`switchPage()` 对无效 id 是静默 no-op —— 那 6 页一直在重复测上一页,
于是「14 页全 0」是假覆盖。修好页表(共用 `_pages.mjs` 的 29 个真实 id,
切换后回读 `.page.active` 断言)再测,下面这些问题才第一次被看见。
========================================================================== */
@media (max-width: 767.98px) {
/* 25.1 info-collect 表单在手机上仍是 4 列栅格。
基础规则(style.css:30644)的
grid-template-columns: minmax(160px,.7fr) 同 ×2
+ minmax(150px,.45fr) + minmax(280px,1fr)
最小合计 750px,390px 屏上第 3/4 列被直接推出视口;而
`@media (max-width:980px)` 里改为单列的那条(style.css:30627)被写在
**它之后**且特异性同为 (0,1,0) —— 顺序错误让这个覆盖永久失效。
实测「完整模式」选项 left=390 right=540(vw=390),整块不在屏内。 */
.info-collect-form-row {
grid-template-columns: 1fr !important;
}
/* 25.2 mcp-management 统计条用 flex-wrap:nowrap,
「📌 常驻: 49」right=409、「🔗 已连接: 0」right=468,都越过屏幕右缘。 */
#tools-stats,
.external-mcp-stats {
flex-wrap: wrap !important;
}
/* 25.3 语义色文字:沿用 18.10b 的同一套体系(一个语义一个色,只压亮度、
冻结色相与饱和度,目标 ≥4.5:1),限定亮色主题。注释里是修复前的实测值。 */
/* 中性灰 —— 与 .projects-fact-filter-label 取同值,全站灰标签一色到底 */
.c2-tasks-stat-label, /* 2.56 on #fff */
.c2-events-stat-label,
.c2-tasks-time-preset-btn, /* 2.45 on #f8fafc */
.c2-events-time-preset-btn {
color: #5d718c !important;
}
/* 信息蓝 */
#c2-events-stat-info, /* 3.68 */
.c2-tasks-time-preset-btn.is-active, /* 3.36 on #eff5fe */
.c2-events-time-preset-btn.is-active,
.agent-role-badge--orchestrator, /* 4.36 on #e0edff */
#rbac-metric-users, /* 4.34 on #e5ecf9,strong 是本体的 id */
.rbac-metric-users,
.rbac-metric-users span {
color: #1557e7 !important;
}
/* 待处理琥珀 */
#c2-tasks-stat-pending, /* 2.15 */
#c2-events-stat-warn {
color: #986206 !important;
}
/* 失败 / 严重 红 */
#c2-tasks-stat-failed, /* 3.76 */
#c2-events-stat-critical {
color: #d81212 !important;
}
/* 成功绿(.monitor-stat-value 是 28.8px 大字号,标准线是 3.0,实测 2.97 差 0.03) */
#c2-tasks-stat-success, /* 2.54 */
.monitor-stat-value.is-success {
color: #167e3c !important;
}
/* 授权橙 */
#rbac-metric-assignments, /* 3.00 on #f7e9e2,strong 是本体的 id */
.rbac-metric-assignments,
.rbac-metric-assignments span {
color: #ad4109 !important;
}
/* 浅粉底上的危险按钮文字:同属危险语义,但底色 #f6e9ec 比 dashboard 那处的
#fef8f8 更深,沿用同一个 #d81212 只有 4.42:1,所以按底色单独压暗。 */
#c2-tasks-batch-delete span, /* 3.84 */
#c2-events-batch-delete span {
color: #c22231 !important;
}
/* 25.4 c2 的主行动按钮:c2.css 拿 --c2-accent(#3b82f6)(c2.css:8)当按钮底色,
白字只有 3.68:1。同一个 #3b82f6 作正文色时已在 18.x 处理过,作按钮底色这条漏了。
压深到 #2563eb → 5.17:1。
**必须限定亮色主题**:深色主题下 `--c2-accent` 会变成浅蓝,按钮是"浅底+深字",
在深色里改背景会直接把文字对比度打下来(实测 3.64:1)。 */
html:not([data-theme="dark"]) .c2-payload-card .btn-primary,
html:not([data-theme="dark"]) #c2-build-btn {
background: #2563eb !important;
border-color: #2563eb !important;
}
/* 25.5 第九轮第二遍:新暴露页面里剩下的文本类小字也纳入 12px 下限。
`.tool-expand-icon` 的 ▶(10px)是图标字符、`svg text`(9px)是图表内标注,
两者属图形元素,不受文本字号下限约束,保持原尺寸。 */
.skill-card-tags span, /* 11.52px 技能标签 */
.c2-tasks-field span, /* 11px 筛选字段标题 */
.c2-events-field span,
#tools-stats strong, /* 11.2px 工具统计数字 */
.asset-protocol-empty p, /* 11px「暂无数据」 */
.asset-recent-badge, /* 10px「7 天」 */
.asset-recent-caption { /* 11px「占当前资产总量的 0%」 */
font-size: 12px !important;
}
/* 25.6 iPhone SE(320px 视口)下 info-collect 的结果工具栏被推出屏幕:
「列 / 导出 CSV / 导出 JSON」right=386 > 320。让它换行而不是溢出。
(同屏 `span.rbac-context-stat` 的 right=326 也越界 6px,但它的父容器
尚未定位,属边缘情况,先记录不动。) */
.info-collect-results-toolbar,
#fofa-selected-meta {
flex-wrap: wrap !important;
}
/* 25.6b header 是单行 space-between,左栏 min-width:240px 仍会把工具栏挤出;
改为上下两行:左栏占满一行,工具栏换到下一行右对齐。 */
.info-collect-results-header {
flex-wrap: wrap !important;
}
.info-collect-results-header-left {
min-width: 0 !important;
flex: 1 1 100% !important;
}
/* 25.6c rbac context 胶囊:max-width:min(25%,360px) 在 320px 下只有 80px,
两个统计("1个角色·3项授权")放不下,第二个 right=326 越界 6px。
窄屏放行换行且取消 25% 上限。 */
.rbac-metric-context {
max-width: none !important;
flex-wrap: wrap !important;
}
/* 25.7 输入控件高度兜底:区块 7 的 input/select{min-height:var(--m-touch)}
特异性 (0,1,1) 不够,被专项规则覆盖成 36/38/40/42px。
- c2.css `#page-c2-events .c2-events-field .form-control`(1,2,1) 锁 36px
- knowledge 的 `.knowledge-filters .search-box`(0,2,0) -> 38px
- rbac 的 `.rbac-search .form-input` -> 40px
- settings 的 `.form-group input`(settings 页) -> 42px
这里用 `#page-*` 前缀 + !important 压过,同时保留原 box-sizing/padding。
(form-control 设 height+min-height 都要改;.search-box input 与裸 input
走 min-height 即可。) */
#page-c2-tasks .c2-tasks-field .form-control,
#page-c2-events .c2-events-field .form-control,
#page-knowledge-management .knowledge-search-field input,
#page-platform-rbac .rbac-search .form-input,
#page-settings .form-group input,
#page-settings input[type="text"],
#page-settings input[type="password"],
#page-settings input[type="url"] {
height: var(--m-ctl-h) !important;
min-height: var(--m-ctl-h) !important;
}
}
/* ==========================================================================
区块 26:深色主题(html[data-theme="dark"])
--------------------------------------------------------------------------
这批违规同样是"审计覆盖修正后才被看见"的:`_mdark.mjs` 的页表此前含 6 个
不存在的页面 id,14 页里那 6 页一直在重复测上一页,所以"深色 0 违规"是假覆盖。
39 处的推荐色用 `_mcontrast.mjs` 的新 OFFLINE 模式离线求出 —— 它刻意复用与
在页面里跑时**同一份 solveFor()**,避免"离线一套算法、在线一套算法"分叉。
方向与亮色相反:深色下要**提亮**前景(搜索方向由前景/背景的明暗关系自动决定,
无须人工指定)。取值同样遵循"一个语义一个色",括号内是修复前的实测比值。
两处结论经**像素裁决**(`_mpixel.mjs` + THEME=dark)确认可信:CSS 计算的
`.mcp-stats-kpi__chip.is-ok` = #166534 与像素真值完全吻合(2.04:1),
`.role-card-badge.enabled` = #157347 同样吻合(2.43:1)——不是建模误差。
========================================================================== */
html[data-theme="dark"] {
/* 26.1 【已回滚的误判,留作记录】深色主题下 `.btn-primary` 的常规形态是
"浅蓝底 #60a5fa + 深墨蓝文字",实测 9.4:1 —— 与亮色主题的"深蓝底 + 白字"
互为反色,这是**有意设计**(正是 23.1 区块处理过的那套写法)。
我最初把它误判成"深字压蓝底",统一改成浅色文字,结果把 18 个页面里
可见的主按钮从 9.4:1 一次性压到 2.21:1 —— `_mdark.mjs` 重测后立刻暴露。
结论:**不要动 `.btn-primary` 的文字色**。
真正有问题的是登录按钮:`#login-overlay .btn-primary.login-submit` 上叠了
background-image: linear-gradient(135deg,#2563eb,#1d4ed8) 的深蓝渐变,
把浅蓝底盖掉了,文字仍是深墨蓝 → 实测 1:1 ~ 3.64:1。只修这一处。 */
#login-overlay .btn-primary,
#login-overlay .login-submit {
color: #eceff8 !important;
}
/* 26.2 中性灰 —— 深色下一律提亮到同一色:取各元素解里最亮的一档
(#848fa0 是"筛选按钮 / #1f2937 底"的解,对更暗的底只会比值更高)。
`.btn-secondary` 的分页按钮这里必须带上 `*`:按钮自身的文字其实是
子元素承载的,只改按钮本体会看不到任何变化(实测过)。 */
.c2-tasks-stat-label, /* 3.49 on #131a27 */
.c2-events-stat-label,
.c2-tasks-time-preset-btn, /* 3.75 on #0c1220 */
.c2-events-time-preset-btn,
#projects-detail-meta-time, /* 3.72 */
button.btn-secondary, /* 2.48 */
button.btn-secondary * {
color: #8c97a8 !important;
}
/* 26.3 信息蓝 */
#c2-events-stat-info, /* 2.95 */
.c2-tasks-time-preset-btn.is-active, /* 2.33 on #1e2d45 */
.c2-events-time-preset-btn.is-active,
.agent-role-badge--orchestrator, /* 2.69 */
.rbac-metric-users,
.rbac-metric-users span { /* 2.77 */
color: #6591f1 !important;
}
/* 26.4 紫 */
.rbac-metric-roles,
.rbac-metric-roles span { /* 2.90 */
color: #9c6af1 !important;
}
/* 26.5 绿 —— #24a655 是"启用徽章 / #142f2c 底"的解(对更暗的 chip 底只会更高) */
#c2-tasks-stat-success, /* 3.39 */
.mcp-stats-kpi__chip.is-ok, /* 2.04 ← 像素裁决过 */
.rbac-pill.is-ok, /* 2.27 */
.role-card-badge.enabled { /* 2.42 ← 像素裁决过 */
color: #24a655 !important;
}
/* dashboard 的「安全」徽章底色更亮(#163a36),需要更亮的绿 */
#dashboard-severity-risk-level {
color: #18b351 !important; /* 3.77 */
}
/* 26.6 红 —— #f04a4a 是"失败 chip / #271c2a 底"的解(#ef3e3e 对 #211a29 只有 4.35:1) */
#c2-tasks-stat-failed, /* 3.33 */
#c2-events-stat-critical,
#c2-tasks-batch-delete span, /* 2.86 */
#c2-events-batch-delete span,
.btn-secondary.btn-delete, /* 2.73 */
.mcp-stats-kpi__chip.is-fail { /* 1.97 */
color: #f25858 !important;
}
#dashboard-status-open {
color: #ef4646 !important; /* 4.45,差 0.05,单独按解取值 */
}
/* 26.7 琥珀 */
#c2-tasks-stat-pending, /* 3.39 */
#c2-events-stat-warn {
color: #b47407 !important;
}
/* 26.8 橙 */
.rbac-metric-assignments,
.rbac-metric-assignments span { /* 2.72 */
color: #e9580c !important;
}
/* 26.9 知识库「未启用」提示(rgb(102,102,102) 在深底上只有 3.28:1) */
#page-knowledge-management h3 {
color: #7c7c7c !important;
}
/* 26.10 mcp-management 的「全部」筛选按钮是白字压在浅蓝 #60a5fa 上:
冻结色相与饱和度后**无解**(白色没有可提亮的余量,求解器原样返回 #ffffff),
所以只能压深底色。 */
.btn-filter.active {
background: #2563eb !important;
border-color: #2563eb !important;
}
/* 26.11 c2 会话工具栏的文字型按钮:18.12 给的 #1d4ed8 在深底上只有 2.84:1。
那条规则的特异性是 (1,4,1)——四个 `:not()` 里的类与属性**都要计入**特异性,
所以这里必须原样带上同一个选择器才压得住(4 个 `:not()` 里的类与属性都要计入特异性)。
**踩过的坑**:这条规则原先我写成了 `html[data-theme="dark"][data-theme="dark"] #page-...`,
而它所在的位置是本文件末尾 `html[data-theme="dark"] { ... }` 块的**内部** ——
CSS 嵌套会把它解释成 `html[data-theme="dark"] html[data-theme="dark"][data-theme="dark"] #page-...`,
也就是"`html` 是 `html` 的后代",**结构性永不匹配**。表现为"改了毫无反应",
而且**连把特异性翻倍都毫无反应**——因为根本没有参与匹配。
块内规则已经自带 `html[data-theme="dark"]` 前缀,这里反而不能再写。 */
#page-c2-sessions button:not(.btn-primary):not(.btn-secondary):not(.btn-danger):not([style*="background"]) {
color: #6591f1 !important;
}
/* 26.12 空状态行动按钮:18.13 给它们铺了实心 #2563eb 底,
23.1 又按"浅蓝底(#60a5fa)"的假设把文字改成深墨蓝 —— 两轮修复前提不一致,
结果成了"深蓝底上的深蓝字"(实测 3.64:1)。底色既然固定是 #2563eb,
深色下文字就必须回转浅色(5.17:1)。前缀由所属的 `html[data-theme="dark"]` 块提供,
这里**不能**再手写一遍(原因见 26.11 的注释)。 */
.dashboard-empty-action,
#page-knowledge-management .empty-state button,
#page-knowledge-management .empty-state .btn,
#hitl-pending-count {
color: #eceff8 !important;
}
/* 26.13 未修项:asset-library 分页的「首页/上一页/下一页/尾页」在首页时是
`:disabled` 态,取 #475569 浅灰属有意设计 —— WCAG 1.4.3 明确豁免禁用控件,
不计入对比度要求。`_mdark.mjs` 会照报,属工具侧尚未过滤 `:disabled`。 */
}
/* --------------------------------------------------------------------------
27. C2 会话管理页移动端布局修正(第十轮)
实测(390x664 视口,登录 mtester):
- c2.css:4371 的 @media (max-width:768px) 给 .c2-session-layout 设
min-height:calc(100vh - 140px) —— 移动端该页可用内容区实测只有 445px 高,
而 100vh-140px = 524px,硬撑出 81px 垂直溢出 → .page-content 出现滚动条,
布局底部 B676 伸到 #m-tabbar(顶 T606) 之下 70px,内容被底部标签栏盖住;
- c2.css:4473 的 @media (max-width:1100px) 在样式表里位于 768px 断点**之后**,
同特异性下后声明胜 → .c2-session-sidebar-wrap 被卡回 width:280px,
390px 视口下会话列表只占 72% 宽,右侧直接露出深色背景;
- 会话主区 .c2-session-main 被 layout 的 auto 高度压到 266px,空态卡片
底部越界,下端内容被切。
修复方向:高度交给内容区实际算(flex:1 + min-height:0),侧栏手机占满全宽、
最高 260px 内部滚动,主区接管剩余空间并内部滚动。只作用于 ≤767.98px。
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
#page-c2-sessions .c2-session-layout {
min-height: 0 !important;
height: 100% !important;
max-height: none !important;
overflow: hidden !important;
}
#page-c2-sessions .c2-session-sidebar-wrap {
width: 100% !important;
min-width: 0 !important;
max-width: none !important;
max-height: 260px !important;
flex-shrink: 0 !important;
border-right: none !important;
}
#page-c2-sessions .c2-session-sidebar,
#page-c2-sessions #c2-session-list {
overflow-y: auto !important;
min-height: 0 !important;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
#page-c2-sessions .c2-session-main {
flex: 1 1 auto !important;
min-height: 0 !important;
overflow-y: auto !important;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
padding: 12px 14px 18px !important;
}
}
/* --------------------------------------------------------------------------
25. iOS 观感修订:材质 / 圆角 / 按压反馈
--------------------------------------------------------------------------
口径:
- 材质一律用 color-mix 从主题自己的 --bg-primary / --border-color 派生,
浅色与暗色自动跟随;写死 rgba 会在暗色下糊成一块白雾。
- backdrop-filter 只在浏览器支持时启用,不支持就退回实心背景(观感一致,
只是没有透出)。固定栏的模糊是软件渲染掉帧主因(见 5 节),所以全站只给
顶栏与底栏各一层 blur(20px),抽屉/面板用不透明材质不再加模糊。
- 圆角收成一个阶梯:控件胶囊、图标按钮正圆、卡片 18、面板顶角 22。
本层原有的 10/12/14/18 四种混用在手机上读起来是"没对齐",不是"设计过"。
- 按压反馈用 scale,且在 prefers-reduced-motion 下整体关掉。
- 所有改动只碰观感:44px 触控下限、层级、滚动容器一概不动。
-------------------------------------------------------------------------- */
:root {
--m-r-card: 18px;
--m-r-panel: 22px;
--m-material: color-mix(in srgb, var(--bg-primary) 82%, transparent);
--m-material-solid: color-mix(in srgb, var(--bg-primary) 96%, transparent);
--m-hairline: color-mix(in srgb, var(--border-color) 55%, transparent);
--m-fill-quaternary: color-mix(in srgb, var(--text-primary) 6%, transparent);
--m-fill-press: color-mix(in srgb, var(--text-primary) 10%, transparent);
--m-ring: color-mix(in srgb, var(--accent-color) 30%, transparent);
--m-shadow-panel: 0 -12px 40px rgba(0, 0, 0, 0.22);
--m-shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04);
}
@media (max-width: 767.98px) {
/* 25.1 顶栏 / 底栏:透出材质 + 发丝分隔线。
顶栏必须写成 html[data-theme] header:style.css 用同一条特异性给暗色塞了
不透明渐变,只写 header 会在暗色下丢掉材质(实测 background 被压成渐变)。 */
header,
html[data-theme] header {
background: var(--m-material-solid);
border-bottom: 1px solid var(--m-hairline);
box-shadow: none;
}
#m-tabbar {
background: var(--m-material-solid);
border-top: 1px solid var(--m-hairline);
box-shadow: none;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
header,
html[data-theme] header {
background: var(--m-material);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
#m-tabbar {
background: var(--m-material);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
}
/* 25.2 圆角阶梯:卡片、面板、控件、图标按钮 */
.card,
.stat-card,
.metric-card,
.dashboard-card,
.panel,
.settings-section,
.info-card {
border-radius: var(--m-r-card);
box-shadow: var(--m-shadow-card);
}
#m-sheet {
border-radius: var(--m-r-panel) var(--m-r-panel) 0 0;
box-shadow: var(--m-shadow-panel);
}
.main-sidebar {
border-radius: 0 var(--m-r-panel) var(--m-r-panel) 0;
border-right: none;
}
input:not([type=checkbox]):not([type=radio]),
select,
textarea {
border-radius: 12px;
}
button,
.btn-primary,
.btn-secondary,
.btn-danger,
.btn-small,
.btn-sm,
.m-chat-bar-btn {
border-radius: var(--m-r-pill);
}
/* 图标按钮去底去框:桌面用的是 .header-actions button(类+元素)两条特异性,
本层只写类名会被压住(实测 border 仍是 1px),所以这里必须同形写 */
.header-actions :is(.m-more-btn, .m-close-btn, .notification-btn, .user-avatar-btn, .btn-icon),
.m-menu-btn {
border: none;
background: transparent;
}
/* 次要按钮:iOS 的"填充"样式,去掉描边不再和主按钮抢层次 */
.btn-secondary,
.btn-small,
.btn-sm {
background: var(--m-fill-quaternary);
border-color: transparent;
}
/* 25.3 发丝线:抽屉 / 面板 / 会话条的内部分隔 */
.m-drawer-head,
.m-drawer-search,
.m-sheet-head,
.m-chat-bar {
border-bottom-color: var(--m-hairline);
}
.m-sheet-sep {
background: var(--m-hairline);
}
.m-sheet-grip {
width: 36px;
height: 5px;
border-radius: 999px;
background: color-mix(in srgb, var(--text-muted) 45%, transparent);
}
/* 25.4 按压反馈:可见的"点到了",不改变布局尺寸 */
.m-tab,
.m-sheet-item,
.m-chat-bar-btn,
.m-menu-btn,
.m-more-btn,
.m-close-btn,
.notification-btn,
.user-avatar-btn,
.btn-primary,
.btn-secondary,
.btn-danger,
.btn-small,
.nav-item-content,
.nav-submenu-item {
transition: transform 0.12s ease, background-color 0.12s ease, color 0.16s ease, opacity 0.12s ease;
}
.m-tab:active,
.m-sheet-item:active,
.m-chat-bar-btn:active,
.btn-primary:active,
.btn-secondary:active,
.btn-danger:active,
.btn-small:active {
transform: scale(0.96);
background-color: var(--m-fill-press);
}
.btn-primary:active {
/* 主按钮按压时保持强调色,只压暗,不变成灰底 */
background-color: color-mix(in srgb, var(--accent-color) 88%, #000);
}
/* 当前标签:图标后面一枚圆形底色,比整格变色更接近系统底栏 */
.m-tab.m-active {
color: var(--accent-color);
background: transparent;
}
.m-tab.m-active svg {
background: color-mix(in srgb, var(--accent-color) 12%, transparent);
border-radius: 50%;
padding: 2px;
box-sizing: content-box;
}
/* 25.5 焦点环:从 2px 硬描边换成 3px 柔光环,键盘可达性不降级 */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
outline: none;
box-shadow: 0 0 0 3px var(--m-ring);
}
.m-drawer-search input:focus {
border-color: color-mix(in srgb, var(--accent-color) 55%, transparent);
}
/* 强制高对比下柔光环会被抹掉,这里把硬描边还给它 */
@media (forced-colors: active) {
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid Highlight;
outline-offset: 2px;
}
}
/* 工具条里的 <a> 按钮(查看全部 / 拉取模型列表 / 视觉测试):它们是操作控件而不是
正文链接,桌面写成 21-29px 高的行内块,手指点不中。给到本层的触控下限,
字号与颜色不动。正文里的链接(.markdown-body 内)不在这些容器里,不受影响。 */
:is(.page-header-actions, .dashboard-section-actions, .dashboard-section-header,
.model-pick-row, .asset-toolbar, .settings-toolbar, .settings-form,
.form-group, .info-collect-toolbar, .settings-advanced-block > div)
a:not(.btn-primary):not(.btn-secondary):not(.btn-danger) {
display: inline-flex;
align-items: center;
min-height: var(--m-touch);
padding: 0 8px;
box-sizing: border-box;
}
/* 会话抽屉:本层把它做成全屏抽屉,里面的行就必须按 44 触控口径来
(实测搜索框 42、分组按钮 24 宽、项目行 38、"最近对话"折叠头 30) */
:is(#conversation-sidebar .conversation-search-box input,
#conversation-sidebar .add-group-btn,
#conversation-sidebar .project-folder-item,
#conversation-sidebar .recent-conversations-toggle,
#conversation-sidebar .project-folder-header) {
min-height: var(--m-touch);
}
#conversation-sidebar .add-group-btn {
min-width: var(--m-touch);
}
/* 调用拦截的开关:input 是 30x18 的滑块,命中区其实是包着它的 label。
把 label 撑到触控下限,手指点的就是整块开关而不是那条 18px 的芯。 */
.tool-guard-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: var(--m-touch);
min-height: var(--m-touch);
}
/* RBAC 概览里的计数胶囊:20px 宽点不中,补足到下限(文字居中,不改视觉密度) */
.rbac-summary-count {
min-width: var(--m-touch);
}
/* 桌面把 .ai-channel-manager 设成 overflow:hidden(style.css:4796,靠固定高度自截),
手机上它的内容比盒子高,超出的一截被裁掉——实测 4 个通道选择按钮 scrollIntoView
也拉不进视口,属于「看得见点不到」。这里交还给页面滚动容器。 */
.ai-channel-manager {
overflow: visible;
}
/* 25.6 遮罩与轻提示:遮罩不加模糊(全屏模糊是掉帧主因),提示条用材质 */
.m-scrim {
background: color-mix(in srgb, var(--m-scrim) 88%, transparent);
}
/* 提示条保持基底的深色 HUD 胶囊(1023 行):这里曾把它改成浅色材质,
但字色仍是 #fff,结果白底白字——用户看到的就是"屏幕中间一块白的"。
只补一层投影,底色与字色不动。 */
#m-toast {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
}
@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) {
/* 关掉按压缩放,只保留颜色反馈 */
.m-tab,
.m-sheet-item,
.m-chat-bar-btn,
.btn-primary,
.btn-secondary,
.btn-danger,
.btn-small {
transition: background-color 0.12s ease, color 0.16s ease;
}
.m-tab:active,
.m-sheet-item:active,
.m-chat-bar-btn:active,
.btn-primary:active,
.btn-secondary:active,
.btn-danger:active,
.btn-small:active {
transform: none;
}
}
@supports (height: 100dvh) {
:root {
--m-bar-bottom: max(0px, calc(100dvh - var(--m-app-height, 100dvh)));
}
}
/* --------------------------------------------------------------------------
26. 密度与表面:对齐 new-api 的卡片体系 + iOS 的分组列表与填充式表单
--------------------------------------------------------------------------
这一节只管"看起来挤不挤、齐不齐",不改配色语言:KPI 卡那套按 nth-child 分的
淡彩渐变是产品自己的身份(style.css:23789-23793),不跟它抢。
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
/* 26.1 KPI:桌面在 ≤480px 把 5 张卡拍成一列 116px 高的长条,手机上要滚
两屏才看完四个数字。改成两列紧凑网格,数字才是主角。 */
.dashboard-kpi-row {
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
gap: 10px;
margin-bottom: 16px;
}
/* 奇数张时末卡通栏,右下角不留空洞 */
.dashboard-kpi-row > .dashboard-kpi-card:last-child:nth-child(odd) {
grid-column: 1 / -1;
}
.dashboard-kpi-card {
padding: 12px 13px !important;
min-height: 0 !important;
gap: 0 !important;
}
.dashboard-kpi-label {
font-size: 12px;
line-height: 1.35;
font-weight: 500;
color: var(--text-muted);
}
.dashboard-kpi-value {
font-size: 26px;
line-height: 1.15;
font-weight: 600;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
margin-top: 6px;
}
.dashboard-kpi-sub,
.dashboard-kpi-sub-text {
font-size: 12px;
line-height: 1.4;
}
.dashboard-kpi-icon {
width: 28px;
height: 28px;
border-radius: 9px;
flex-shrink: 0;
}
.dashboard-kpi-icon svg {
width: 15px;
height: 15px;
}
.dashboard-access-strip {
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
gap: 10px;
}
/* 26.2 卡片表面统一:发丝边 + 16 圆角 + 几乎看不见的投影。
桌面的 0 2px 8px 双层阴影在小屏上会把每张卡都"抬起来",一屏四张就散了。 */
:is(.card, .stat-card, .metric-card, .dashboard-card, .panel,
.settings-section, .info-card) {
border: 1px solid var(--m-hairline);
border-radius: var(--m-r-card);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
}
/* 26.3 页面头:标题与操作行收成一组,不再各占一段空白 */
.page-header h2,
.page-header h1 {
font-size: 20px;
font-weight: 600;
letter-spacing: -0.01em;
}
.page-header {
gap: 8px;
margin-bottom: 12px;
}
/* 26.4 填充式表单(iOS 的写法:字段靠底色区分,不靠一圈灰边框) */
:is(.form-group, .settings-form, .modal-body, .m-drawer-search) :is(input, select, textarea) {
background: var(--m-fill-quaternary);
border-color: transparent;
border-radius: 12px;
}
/* 聚焦:只留一圈柔光环。再改 border 颜色会与 25.5 的环叠成两圈(登录框实测如此)*/
:is(.form-group, .settings-form, .modal-body, .m-drawer-search) :is(input, select, textarea):focus {
background: var(--bg-primary);
border-color: transparent;
box-shadow: 0 0 0 3px var(--m-ring);
}
/* 26.5 登录卡:满宽卡片 + 填充式字段 + 胶囊主按钮 */
.login-card {
border-radius: 20px;
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.12);
}
.login-form .btn-primary,
.login-card .btn-primary {
width: 100%;
border-radius: var(--m-r-pill);
}
/* 26.6 分组列表:卡片内的行之间只留发丝线,行高按触控下限走 */
:is(.settings-section, .card, .panel, .info-card) :is(.form-group, .settings-row, .list-row) + :is(.form-group, .settings-row, .list-row) {
border-top: 1px solid var(--m-hairline);
}
}
/* --------------------------------------------------------------------------
28. 玻璃质感:环境光画布 + 玻璃表面 + 顶缘高光
--------------------------------------------------------------------------
目标:把手机端从"平面灰底 + 分隔线"推进到 Apple 的玻璃材质语言。
玻璃要读得出来,前提是材料后面有东西可透,所以这一节先做两件事:
- 画布:.container 铺一层极淡的环境光(浅色 = 近白底 + 右上 accent / 左下紫的
柔光;暗色 = 深底 + 同色更亮的柔光),再把 .content-area / .chat-messages 的
实心底色交还给这层画布。此后所有半透明表面都有色可透,各页面自身的内容背景
一律不动。
- 表面分三档:模糊(backdrop-filter)只给"小面积或瞬时"的件 —— 顶栏、底栏
(25 节已有)、底部面板、抽屉、登录卡、弹窗;它们要么面积小,要么只在被打开
时才绘制,不挂在常驻滚动内容上。常驻卡片(.card/.stat-card/... 一列)只做
半透明 + 发丝边 + 顶缘高光,不挂模糊 —— 固定栏的大面积模糊是移动端掉帧
主因(25 节实测),这条口径不动。
- 浅色与暗色全部经 color-mix 从主题令牌派生;只有"顶缘高光"必须换向
(浅色 = 白线,暗色 = 极淡白线),单独覆写。
- prefers-reduced-transparency: reduce 时不问浏览器支持与否,全部退回实心。
-------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
:root {
/* 栏体材质从 82% 提到 62%:有模糊托底,透出的是画布而不是一块灰板 */
--m-material: color-mix(in srgb, var(--bg-primary) 62%, transparent);
/* 玻璃卡(常驻,不挂模糊):透出画布的一点色 */
--m-glass-card: color-mix(in srgb, var(--card-bg) 66%, transparent);
/* 玻璃面板(瞬时件,配模糊):抽屉 / 底部面板 / 登录卡 / 弹窗 */
--m-glass-panel: color-mix(in srgb, var(--bg-primary) 72%, transparent);
--m-glass-blur: saturate(180%) blur(20px);
/* 顶缘高光:玻璃的"口沿"。浅色下是白线,暗色下换极淡白(见下方覆写) */
--m-bevel: inset 0 1px 0 rgba(255, 255, 255, 0.55);
}
html[data-theme="dark"] {
--m-bevel: inset 0 1px 0 rgba(255, 255, 255, 0.07);
}
/* 28.1 环境光画布:整块 app 换底,卡片与栏体从这层透色 */
.container {
background:
radial-gradient(120% 88% at 88% -12%,
color-mix(in srgb, var(--accent-color) 14%, transparent), transparent 58%),
radial-gradient(95% 72% at -12% 106%,
color-mix(in srgb, #8b5cf6 11%, transparent), transparent 60%),
var(--bg-secondary);
}
html[data-theme="dark"] .container {
background:
radial-gradient(120% 88% at 88% -12%,
color-mix(in srgb, var(--accent-color) 18%, transparent), transparent 58%),
radial-gradient(95% 72% at -12% 106%,
color-mix(in srgb, #8b5cf6 13%, transparent), transparent 60%),
var(--bg-secondary);
}
/* 交还底色:画布负责"底",卡片负责"面" */
.content-area,
.chat-messages {
background: transparent;
}
/* 28.2 底栏补一道顶缘高光,顶栏保持 25 节的发丝线 */
#m-tabbar {
border-top-color: var(--m-hairline);
box-shadow: var(--m-bevel);
}
/* 28.3 卡片玻璃:半透明表面 + 发丝边 + 顶缘高光;投影保持 26 节的"几乎看不见" */
:is(.card, .stat-card, .metric-card, .dashboard-card, .panel,
.settings-section, .info-card) {
background: var(--m-glass-card);
border-color: var(--m-hairline);
box-shadow: var(--m-bevel), 0 1px 2px rgba(0, 0, 0, 0.03);
}
/* 28.4 底部面板:真玻璃(透色 + 模糊),只在打开时绘制 */
#m-sheet {
background: var(--m-glass-panel);
border-color: color-mix(in srgb, var(--border-color) 50%, transparent);
box-shadow: var(--m-bevel), var(--m-shadow-panel);
}
/* 28.5 抽屉(主导航 / 会话列表):同面板材质的真玻璃。
两套选择器:不带属性的管浅色与初始绘制,html[data-theme] 的管暗色
(style.css 的暗色抽屉是 (0,2,0) 特异性的不透明渐变,必须同形压回)。 */
.main-sidebar,
.conversation-sidebar,
html[data-theme] .main-sidebar,
html[data-theme] .conversation-sidebar {
background: var(--m-glass-panel);
}
.m-chat-drawer-head {
background: transparent;
}
.m-drawer-search input {
background: color-mix(in srgb, var(--input-bg) 78%, transparent);
}
/* 对话页顶部工具条:在流内、滚动内容不会跑到它后面,纯透色不挂模糊 */
#m-chat-bar {
background: color-mix(in srgb, var(--bg-primary) 66%, transparent);
border-bottom-color: var(--m-hairline);
}
/* 28.6 弹窗:与面板同档的玻璃 */
.modal-content {
background-color: var(--m-glass-panel);
border: 1px solid var(--m-hairline);
}
/* 28.7 登录:遮罩本身得先有"画布"(否则卡片后面只有一块灰),卡片是真玻璃 */
.login-overlay {
background:
radial-gradient(120% 88% at 85% -10%,
color-mix(in srgb, var(--accent-color) 13%, transparent), transparent 60%),
radial-gradient(95% 72% at -12% 108%,
color-mix(in srgb, #8b5cf6 10%, transparent), transparent 62%),
color-mix(in srgb, var(--bg-secondary) 92%, transparent);
}
html[data-theme="dark"] .login-overlay {
background:
radial-gradient(120% 88% at 85% -10%,
color-mix(in srgb, var(--accent-color) 17%, transparent), transparent 60%),
radial-gradient(95% 72% at -12% 108%,
color-mix(in srgb, #8b5cf6 12%, transparent), transparent 62%),
color-mix(in srgb, #0b1120 92%, transparent);
}
.login-card {
background: var(--m-glass-panel);
border-color: var(--m-hairline);
box-shadow: var(--m-bevel), 0 18px 44px rgba(0, 0, 0, 0.14);
}
/* 28.8 模糊:只给上述瞬时件。@supports 兜底逻辑与 25 节一致 */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
#m-sheet,
html[data-theme] .main-sidebar,
html[data-theme] .conversation-sidebar,
.login-card,
.modal-content {
-webkit-backdrop-filter: var(--m-glass-blur);
backdrop-filter: var(--m-glass-blur);
}
}
/* 28.9 降低透明度环境:退回实心材质(含画布) */
@media (prefers-reduced-transparency: reduce) {
.container,
.content-area,
.chat-messages,
.login-overlay {
background: var(--bg-secondary);
}
#m-sheet {
background: var(--bg-primary);
}
.main-sidebar,
.conversation-sidebar,
html[data-theme] .main-sidebar,
html[data-theme] .conversation-sidebar,
.login-card,
.modal-content {
background: var(--bg-primary);
}
#m-chat-bar {
background: var(--bg-primary);
}
:is(.card, .stat-card, .metric-card, .dashboard-card, .panel,
.settings-section, .info-card) {
background: var(--card-bg);
}
#m-sheet,
html[data-theme] .main-sidebar,
html[data-theme] .conversation-sidebar,
.login-card,
.modal-content {
-webkit-backdrop-filter: none;
backdrop-filter: none;
}
}
}