Files
WorkBuddy f4e00b4d95 feat(miniapp): 首页 4×2 入口宫格图标改版为参考图 2 的渐变砖 + 白色图形
参考图 2 的视觉语言是「高饱和渐变圆角砖 + 白色描画图形 + 主标题/副标题双层文字」,
原实现是「浅色底砖 + 彩色线性 PNG + 单行标题」,两者差异较大,本轮按参考图重做。

- app.wxss:新增 8 个 --gradient-entry-* 渐变 token 与 --mini-entry-shadow。
  白图形的判据取**非文本图形 3:1**(WCAG 1.4.11);渐变是斜向的、图形又跨在两色之间,
  故以「较亮端」为准逐色实算(3.00~3.02:1,较暗端 3.54~4.08:1),实算值写在 token 注释里。
- index.wxss:.entry-icon 由 72rpx 浅色砖改为 96rpx 渐变砖(radius 28rpx + 落影);
  图形改用 data-uri SVG 背景层(几何图形统一 24×24 viewBox),砖面走 --gradient-entry-*;
  新增 .entry-sub(12px muted 副标题);删除已无用的 .entry-icon-image。
  小屏 @media(≤340px) 同步收紧,副标题不降到 micro —— micro 在本项目是「仅角标/装饰」档位,
  这行字承载语义,不属于装饰。
- index.wxml:宫格项去掉 <image>,图标改为纯 CSS 砖;补 aria-label 承接图形语义。
- index.ts:tone 名与色相语义对齐(rose/orange/violet/blue/purple/pink/green/amber),
  并补 subtitle;entry 不再持有 iconAsset(图标不再是图片资源)。
- preview-miniapp.mjs:首页 mock 同步 tone/subtitle,预览才能看到真实砖面与副标题。

渲染态实测(无头浏览器 + CDP,非真机渲染):
- 8 个 .entry-item 均渲染出 label + subtitle;.entry-icon 计算样式 48×48px / radius 14px /
  background-image 含 svg 且为两层(图形 26px + 渐变铺满);label 14px/600;sub 12px #7D839B。
- 证据截图:artifacts/ui-redesign/miniapp/home-entries-page.png 与 home-entries-grid-zoom.png。

门禁:UI 契约 23/23;语种审计 0 命中;tsc --noEmit 通过;wcc/wcsc 真编译 88 WXSS + 86 WXML / 0 失败。

未改(需产品确认):第 8 项业务名仍是「善缘服务站」(参考图 2 写作「万善缘地」),
副标题自拟为「就在身边」;渐变整体比参考图略深,以保证白图形对比度达标。
2026-09-23 15:22:23 +08:00

379 lines
18 KiB
Plaintext
Raw Permalink 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.
/**
* 丹阳公益婚恋服务平台 - 全局 Design Token
*
* v2(2026-09-22 设计系统夯实)
* ---------------------------------------------------------------
* 本次修订针对审计发现的系统性问题,逐条给出依据:
*
* 1) 品牌色不能承载文字 → 引入「基础色 / -strong」分级
* #E96B6B on 白 = 3.10:1,白字 on #E96B6B = 3.10:1,on #FFF3F2 = 2.85:1,
* 三条路径都不满足正文 4.5:1。故:
* --color-primary 仅用于大色块、渐变、光晕、装饰(非文字)
* --color-primary-strong 用于文字、小图标、纯文字链接(5.33:1 ✓)
* --color-primary-solid 实心按钮底(白字 ≥4.5:1 ✓)
* 功能色(success/warning/danger/info)同此分级。
*
* 2) 次要文字对比度过低
* 旧 --color-text-tertiary(#777) = 4.48(on 白,勉强)/ #999 实测 2.85 却被用了 71 次。
* 现将三级文字压到 #646C85 = 5.22:1,占位符保留浅色(占位符非正文)。
*
* 3) 颜色碎片化
* 审计前全站 182 种 hex、770 次硬编码(54 种灰、60 种蓝紫、40 种红橙)。
* 本文件是唯一的颜色事实源;页面层禁止再写 hex,一律 var(--token)。
*
* 4) 触摸目标
* 组件库默认按钮只有 40px、小号 28px、"喜欢"按钮 31px。
* 统一以 --tap-min(88rpx=44px) 为下限,--tap-gap 为最小间距。
*
* 5) 动效几乎未落地(--motion-* 全站仅 5 次引用)
* 补充 --ease-spring 与 --press-scale,让点击有即时反馈。
*
* 6) 夜间氛围层缺失(参考图有明显的光晕/星点,代码只有纯渐变)
* 新增 --mini-glow-* / --mini-star / --mini-glass / --mini-hairline。
*
* 单位说明:rpx(750rpx = 屏幕宽度,1rpx ≈ 0.5px @375pt)
* 旧 token 名全部保留(88 个 wxss 共 909 处引用),只更新取值,避免大范围失效。
*/
page {
/* ============ 品牌主色(分级:色块 / 文字 / 实心按钮) ============ */
--color-primary: #E96B6B; /* 基础色:渐变、光晕、装饰、非文字图形 */
--color-primary-strong: #B8434A; /* 文字与小图标(5.33:1 on 白,4.91:1 on 浅粉底) */
--color-primary-solid: #C24C50; /* 实心按钮底(白字 4.62:1) */
--color-primary-active: #D95F5F; /* 按压态(配合 --color-primary 使用) */
--color-primary-light: #FFF3F2;
--color-primary-lighter: #FFF8F7;
/* ============ 小程序参考图品牌主题(夜间紫蓝) ============ */
--mini-night-start: #172A72;
--mini-night-end: #25104F;
--mini-night-soft: #3445A1;
--mini-accent-pink: #FF4F91; /* 点缀色:仅用于色块/装饰,不作正文 */
--mini-accent-coral: #FF6A78;
--mini-card-bg: rgba(255, 255, 255, 0.98);
--mini-text: #1F2744;
--mini-muted: #7D839B;
--mini-card-radius: 24rpx;
--mini-text-on-night: #FFFFFF; /* on 深底 13.04:1 ✓ */
--mini-text-on-night-muted: rgba(255, 255, 255, 0.72); /* on 深底 ≈8:1 ✓ */
--mini-shadow: 0 16rpx 40rpx rgba(28, 30, 88, 0.14);
/* ---- 渐变(集中定义:此前 4 个页面各写了一份「橙红渐变」按钮) ---- */
/* --gradient-brand 明亮活泼,但白字仅 2.77:1 —— 只能用于无文字的装饰填充。 */
--gradient-brand: linear-gradient(135deg, #FF6A78 0%, #E96B6B 100%);
/* 承载白字的 CTA 渐变:两端 #C24C50(4.73:1) / #B8434A(5.33:1) 均达标。 */
--gradient-brand-solid: linear-gradient(135deg, #C24C50 0%, #B8434A 100%);
--gradient-night: linear-gradient(160deg, #172A72 0%, #25104F 100%);
--gradient-night-soft: linear-gradient(160deg, #3445A1 0%, #172A72 100%);
/* ---- 首页 4×2 入口宫格图标渐变(参考图 2:高饱和圆角砖 + 白色描画图标) ----
白图标的对比度判据是**非文本图形 3:1**(WCAG 1.4.11)。渐变是斜向的、图标又跨在
两色之间,所以**较亮的一端也必须达标** —— 下表注释给的是 0% 处实算值,100% 更暗只会更高。
色相按业务语义固定(不随 tone 名漂移):找对象/婚前辅导=玫瑰粉系、公益红娘/服务站=暖橙系、
公益活动=紫罗兰、婚恋咨询=蓝、婚恋课堂=紫、婚恋反诈=绿。 */
--gradient-entry-rose: linear-gradient(135deg, #F56287 0%, #F34671 100%); /* 3.01:1 / 3.54:1 */
--gradient-entry-orange: linear-gradient(135deg, #F66A06 0%, #D95E06 100%); /* 3.01:1 / 3.78:1 */
--gradient-entry-violet: linear-gradient(135deg, #A380F5 0%, #8F64F2 100%); /* 3.02:1 / 3.98:1 */
--gradient-entry-blue: linear-gradient(135deg, #5195F6 0%, #3484F4 100%); /* 3.01:1 / 3.65:1 */
--gradient-entry-purple: linear-gradient(135deg, #B27BF0 0%, #A260ED 100%); /* 3.01:1 / 3.87:1 */
--gradient-entry-pink: linear-gradient(135deg, #F2619E 0%, #F0458C 100%); /* 3.01:1 / 3.54:1 */
--gradient-entry-green: linear-gradient(135deg, #18A979 0%, #148F66 100%); /* 3.00:1 / 4.08:1 */
--gradient-entry-amber: linear-gradient(135deg, #E9740C 0%, #CC650B 100%); /* 3.01:1 / 3.86:1 */
/* 图标砖落影:比 --mini-shadow 更小,避免 4 列宫格里相邻砖块的投影互相压边 */
--mini-entry-shadow: 0 8rpx 18rpx rgba(28, 30, 88, 0.18);
/* ---- 夜间氛围层(v2 新增;参考图有光晕与星点,此前完全没有) ---- */
--mini-glow-coral: rgba(255, 79, 145, 0.40);
--mini-glow-indigo: rgba(94, 124, 255, 0.32);
--mini-glow-soft: rgba(255, 255, 255, 0.10);
--mini-star: rgba(255, 255, 255, 0.70);
--mini-glass: rgba(255, 255, 255, 0.10); /* 深底玻璃面 */
--mini-glass-strong: rgba(255, 255, 255, 0.16);
--mini-hairline: rgba(255, 255, 255, 0.16); /* 深底分隔线 */
--mini-glass-success: rgba(90, 226, 184, 0.16); /* 深底上的成功态玻璃面 */
/* 星点层:多个小径向渐变叠在夜空渐变之上。用 radial-gradient 而非图片,
避免额外请求,也便于随主题换色(index 页脚注:装饰层不承载信息)。 */
--mini-starfield:
radial-gradient(circle at 12% 18%, var(--mini-star) 0, transparent 2.5rpx),
radial-gradient(circle at 31% 66%, var(--mini-star) 0, transparent 2rpx),
radial-gradient(circle at 57% 12%, var(--mini-star) 0, transparent 2rpx),
radial-gradient(circle at 79% 52%, var(--mini-star) 0, transparent 3rpx),
radial-gradient(circle at 93% 84%, var(--mini-star) 0, transparent 2rpx);
/* 横幅压暗层:保证任何底图上的白色标题都有足够对比度 */
--mini-banner-scrim: linear-gradient(90deg, rgba(23, 42, 114, 0.84) 0%, rgba(37, 16, 79, 0.40) 56%, rgba(255, 79, 145, 0.12) 100%);
/* ============ 功能色(同样分级) ============ */
--color-success: #2E9B68; /* 色块/图标 */
--color-success-strong: #17805A; /* 文字(4.92:1 ✓) */
--color-success-light: #ECF8F2;
--color-warning: #E59A32; /* 色块/图标(作文字仅 2.33:1) */
--color-warning-strong: #9C5F14; /* 文字(5.17:1 ✓) */
--color-warning-light: #FFF7E9;
--color-danger: #D94A4A; /* 色块/图标 */
--color-danger-strong: #C62828; /* 文字(5.62:1 ✓) */
--color-danger-light: #FFF0F0;
--color-info: #4A7CD4; /* 色块/图标 */
--color-info-strong: #2A5DB0; /* 文字(6.38:1 ✓) */
--color-info-light: #EEF4FF;
/* ============ 强调紫(参考图的标签语言;此前散落成 6 种写法) ============ */
--color-accent-purple: #8B63BC; /* 色块/装饰 */
--color-accent-purple-strong: #7C60BC; /* 文字(5.03:1 ✓) */
--color-accent-purple-light: #F3EEFF;
/* 深色夜空底上的强调色(不可用于浅色底) */
--color-success-on-night: #A6F5D7; /* on #172A72 = 10.35:1 ✓ */
/* ============ 文字色(实测对比度 on 白 / on 页面底) ============ */
--color-text-strong: #1A1F36; /* 标题 16.24 / 14.90 ✓ */
--color-text-primary: #2A3050; /* 正文 12.83 / 11.77 ✓ */
--color-text-secondary: #4A5169; /* 次要正文 7.86 / 7.21 ✓ */
--color-text-tertiary: #646C85; /* 说明文字 5.22 / 4.79 ✓(旧 #777 为 4.48) */
--color-text-placeholder: #9AA1B4; /* 占位符(非正文,可浅) */
--color-text-disabled: #B8BDC9;
--color-text-inverse: #FFFFFF; /* 深色背景上的文字 */
/* ============ 边框与分割线 ============ */
--color-border: #E6E8EF;
--color-border-strong: #D8DBE5;
--color-divider: #EFF0F5;
--color-border-focus: #B8434A; /* 焦点环用 strong,保证 3:1 可见度 */
--color-border-error: #C62828;
/* ============ 背景(避免纯白/纯灰平铺,页面底为暖白) ============ */
--color-bg-page: #F6F5F3;
--color-bg-card: #FFFFFF;
--color-bg-subtle: #F5F6FA; /* 次级区块、内嵌列表 */
--color-bg-muted: #EEF0F6; /* 更弱的填充 */
--color-bg-input: #F7F8FB; /* 表单输入框底(此前被引用但从未定义,导致背景失效) */
--color-bg-disabled: #F3F4F7;
--color-bg-overlay: rgba(0, 0, 0, 0.45);
/* ============ 字体 ============ */
/* 小程序无法可靠加载 Web Font(wx.loadFontFace 依赖 HTTPS 白名单且有闪动风险),
故使用中文系统字栈:iOS 走苹方、Android 走思源/系统黑体。
排版辨识度靠字号阶梯与字重建立,而非依赖字体本身。 */
--font-family-cn: -apple-system, "PingFang SC", "Noto Sans SC", "Hiragino Sans GB", "Microsoft YaHei", system-ui, sans-serif;
--font-family-number: "SF Pro Display", -apple-system, "DIN Alternate", "PingFang SC", sans-serif;
/* 字号阶梯 —— 正文 32rpx(16px) 起,元信息下限 24rpx(12px) */
--font-size-micro: 22rpx; /* 11px 仅角标/装饰,禁用于正文 */
--font-size-xs: 24rpx; /* 12px 元信息下限 */
--font-size-sm: 26rpx; /* 13px 次要说明 */
--font-size-md: 28rpx; /* 14px 次要正文 */
--font-size-base: 32rpx; /* 16px 正文 */
--font-size-lg: 36rpx; /* 18px 小标题 */
--font-size-xl: 40rpx; /* 20px 区块标题 */
--font-size-2xl: 48rpx; /* 24px 页面标题 */
--font-size-3xl: 56rpx; /* 28px 大标题 */
--font-size-display: 64rpx; /* 32px Hero */
/* 字重:只用 4 档。禁止 550/650/750 等非百位值 —— Android 无对应字重会回退,
导致同一设计在不同机型上粗细不一致(审计发现 30 处)。 */
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.65;
--letter-spacing-tight: 0;
--letter-spacing-wide: 1rpx;
--letter-spacing-wider: 2rpx;
/* ============ 间距(4px 栅格) ============ */
--spacing-1: 8rpx;
--spacing-2: 16rpx;
--spacing-3: 24rpx;
--spacing-4: 32rpx;
--spacing-5: 40rpx;
--spacing-6: 48rpx;
--spacing-8: 64rpx;
--spacing-10: 80rpx;
--spacing-12: 96rpx;
--spacing-page-x: 32rpx;
--spacing-section: 48rpx;
--spacing-card-inner: 32rpx;
--spacing-tight: 16rpx;
/* ============ 圆角 ============ */
--radius-sm: 16rpx;
--radius-md: 24rpx;
--radius-lg: 32rpx;
--radius-xl: 40rpx;
--radius-2xl: 48rpx;
--radius-full: 9999rpx;
/* ============ 阴影(统一为一套,此前 30+ 种近似值) ============ */
--shadow-card: 0 8rpx 28rpx rgba(35, 35, 95, 0.07);
--shadow-raised: 0 12rpx 36rpx rgba(35, 35, 95, 0.10);
--shadow-popup: 0 16rpx 64rpx rgba(0, 0, 0, 0.10);
--shadow-night: 0 16rpx 40rpx rgba(28, 30, 88, 0.18);
/* ============ 边框宽度 ============ */
--border-width: 1rpx;
--border-width-strong: 2rpx;
/* ============ 触摸目标(无障碍下限 44×44px) ============ */
--tap-min: 88rpx; /* 44px 最小可点区域 */
--tap-comfortable: 96rpx; /* 48px 舒适尺寸:主操作 */
--tap-gap: 16rpx; /* 8px 相邻目标最小间距 */
/* ============ 动效 ============ */
--motion-fast: 120ms;
--motion-normal: 200ms;
--motion-slow: 300ms;
--ease-standard: cubic-bezier(0.32, 0.72, 0, 1);
--ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
--press-scale: 0.97;
--press-opacity: 0.88;
/* ============ 层级 z-index ============ */
--z-base: 1;
--z-sticky: 100;
--z-tabbar: 200;
--z-dropdown: 500;
--z-modal: 1000;
--z-toast: 2000;
--z-ambient: 0;
/* ============ 页面基础样式 ============ */
background-color: var(--color-bg-page);
font-family: var(--font-family-cn);
font-size: var(--font-size-base);
color: var(--color-text-primary);
line-height: var(--line-height-normal);
}
/* ==========================================================================
通用工具类
========================================================================== */
.container {
padding: var(--spacing-page-x);
}
.flex { display: flex; }
.flex-column { display: flex; flex-direction: column; }
.flex-center { display: flex; align-items: center; justify-content: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.flex-start { display: flex; align-items: center; justify-content: flex-start; }
.flex-1 { flex: 1; min-width: 0; }
.flex-shrink-0 { flex-shrink: 0; }
.wrap { flex-wrap: wrap; }
/* 4 列宫格:用 flex + 百分比宽度实现。
注:display:grid 在 WebView 渲染下可用,但 Skyline 不支持,故统一改为 flex 以免疫渲染引擎迁移。*/
.grid-4 {
display: flex;
flex-wrap: wrap;
}
.grid-4 > .grid-item {
width: 25%;
box-sizing: border-box;
}
.text-strong { color: var(--color-text-strong); }
.text-primary { color: var(--color-text-primary); }
.text-secondary { color: var(--color-text-secondary); }
.text-tertiary { color: var(--color-text-tertiary); }
.text-placeholder { color: var(--color-text-placeholder); }
.text-white { color: var(--color-text-inverse); }
.text-brand { color: var(--color-primary-strong); }
/* 品牌色作文字只能用 -strong 变体:基础色 #E96B6B 在白底仅 3.10:1。
保留 .text-brand-soft 这个类名是为了不破坏既有引用,取值已统一到 -strong,
如需「更亮的品牌色」请用于背景/图形而非文字。 */
.text-brand-soft { color: var(--color-primary-strong); }
.text-success { color: var(--color-success-strong); }
.text-warning { color: var(--color-warning-strong); }
.text-danger { color: var(--color-danger-strong); }
.text-info { color: var(--color-info-strong); }
.font-micro { font-size: var(--font-size-micro); }
.font-xs { font-size: var(--font-size-xs); }
.font-sm { font-size: var(--font-size-sm); }
.font-md { font-size: var(--font-size-md); }
.font-base { font-size: var(--font-size-base); }
.font-lg { font-size: var(--font-size-lg); }
.font-xl { font-size: var(--font-size-xl); }
.font-2xl { font-size: var(--font-size-2xl); }
.font-3xl { font-size: var(--font-size-3xl); }
.font-display { font-size: var(--font-size-display); }
.font-regular { font-weight: var(--font-weight-regular); }
.font-medium { font-weight: var(--font-weight-medium); }
.font-semibold { font-weight: var(--font-weight-semibold); }
.font-bold { font-weight: var(--font-weight-bold); }
.mt-1 { margin-top: var(--spacing-1); }
.mt-2 { margin-top: var(--spacing-2); }
.mt-3 { margin-top: var(--spacing-3); }
.mt-4 { margin-top: var(--spacing-4); }
.mt-5 { margin-top: var(--spacing-5); }
.mt-6 { margin-top: var(--spacing-6); }
.mb-1 { margin-bottom: var(--spacing-1); }
.mb-2 { margin-bottom: var(--spacing-2); }
.mb-3 { margin-bottom: var(--spacing-3); }
.mb-4 { margin-bottom: var(--spacing-4); }
.mb-5 { margin-bottom: var(--spacing-5); }
.mb-6 { margin-bottom: var(--spacing-6); }
/* 文本省略 */
.ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ellipsis-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
/* 安全区底部留白(避开 Home Indicator) */
.safe-area-bottom {
padding-bottom: env(safe-area-inset-bottom);
}
/* 页面卡片基础样式 */
.card-base {
background: var(--color-bg-card);
border-radius: var(--radius-lg);
padding: var(--spacing-card-inner);
box-shadow: var(--shadow-card);
}
/* 分割线 */
.divider {
height: var(--border-width);
background: var(--color-divider);
}
/* ==========================================================================
可访问性:只用 token,不用工具类(2026-09-23 修订)
--------------------------------------------------------------------------
v2 曾在此处新增 .tap-min / .tap-gap / .focusable / .pressable 四个工具类。
复检发现全部零引用(等价能力已各自写在组件样式内,例如 button/index.wxss
直接使用 min-height:var(--tap-min)),且存在两个硬问题:
1) `.tap-gap > * + *` 用了通配符与兄弟选择器 —— WXSS 不支持,
wcc 编译期直接报 `error at token '*'`,导致整个小程序编译失败。
(这是编译错误,不是静默失效,代价最高。)
2) `.pressable:active` / `.focusable:focus-visible` 的伪类在 view 上
不生效(小程序须用 hover-class),属于"写了等于没写"。
故整块移除,保留 token。需要热区/间距时直接引用 token:
min-height: var(--tap-min); → 88rpx = 44px,最小可点区域
gap: var(--tap-gap); → 16rpx = 8px,相邻目标间距(flex gap)
【WXSS 禁用清单】星号通配符、兄弟选择器、属性选择器。
【注释约束】WXSS 注释**不可嵌套**:内层注释的结束标记会提前终止外层注释,
其后的文字会被当成真实样式解析,导致编译失败或样式被污染。
(2026-09-23 实证:本块注释初版即踩此坑。)
========================================================================== */