UI 视觉设计规范
本规范基于已完成的设计页面(
static/ui/fap/、static/ui/lsp/)提取,参考客户原始需求文档制定。
平台概览
| 平台 | 技术栈 | 设备尺寸 | 用户角色 | 设计特点 |
|---|---|---|---|---|
| Family App | UniApp(iOS + Android) | 390×844px(iPhone 16 Pro) | 子女/家属(中年用户) | 传统触控界面,适老化要求:字号 ≥ 16pt |
| Luma App | 嵌入式 Linux + Web 前端 | 1280×800px(音箱屏幕) | 老年人(65岁以上) | 语音优先 + 触控辅助,适老化要求:字号 ≥ 18pt |
1. Family App(儿女端)
1.1 设计令牌(Design Tokens)
所有 Family App 页面共享 __design_tokens.css 中的 CSS 变量:
/* ═══════════════════════════════════════════
1. 墨色系列(文字)
═══════════════════════════════════════════ */
--ink: #1A1814; /* 主墨色/深色文字 */
--ink-mid: #4A4540; /* 中等墨色 */
--ink-soft: #7A7570; /* 柔和墨色 */
--ink-dim: #B0AAA5; /* 暗淡墨色 */
/* ═══════════════════════════════════════════
2. 纸张系列(背景)
═══════════════════════════════════════════ */
--paper: #F8F6F2; /* 主背景色 */
--paper2: #F0EDE8; /* 次背景色 */
--paper3: #E8E4DE; /* 第三背景色 */
--white: #FFFFFF; /* 纯白 */
/* ═══════════════════════════════════════════
3. 绿色系(安全/正常状态)
═══════════════════════════════════════════ */
--green: #2D6A4F; /* 深绿色(规范定义) */
--green: #4A8A5A; /* 实际使用值 */
--green-light: #EAF4EF; /* 浅绿色背景 */
--green-border: rgba(74,138,90,0.2);
/* ═══════════════════════════════════════════
4. 琥珀色系(警告状态)
═══════════════════════════════════════════ */
--amber: #92400E; /* 深琥珀色(规范定义) */
--amber: #8A7040; /* 实际使用值 */
--amber-light: #FEF3C7; /* 浅琥珀色背景 */
--amber-border: rgba(138,112,64,0.2);
/* ═══════════════════════════════════════════
5. 红色系(紧急/错误状态)
═══════════════════════════════════════════ */
--red: #991B1B; /* 深红色(规范定义) */
--red: #9A5040; /* 实际使用值 */
--red-light: #FEF2F2; /* 浅红色背景 */
--red-border: rgba(154,80,64,0.2);
/* ═══════════════════════════════════════════
6. 蓝色系(信息状态)
═══════════════════════════════════════════ */
--blue: #1E3A8A; /* 深蓝色(规范定义) */
--blue: #3D6B9A; /* 实际使用值 */
--blue-light: #EFF6FF; /* 浅蓝色背景 */
--blue-border: rgba(61,106,154,0.2);
/* ═══════════════════════════════════════════
7. 边框色
═══════════════════════════════════════════ */
--border: rgba(26,24,20,0.1);
--border2: rgba(26,24,20,0.06);
/* ═══════════════════════════════════════════
8. 阴影
═ ══════════════════════════════════════════ */
--shadow-soft: 0 2px 8px rgba(0,0,0,0.04);
--shadow-medium: 0 4px 16px rgba(0,0,0,0.06);
/* ═══════════════════════════════════════════
9. 字体
═══════════════════════════════════════════ */
--font-display: 'Fraunces', Georgia, serif; /* 品牌/标题 */
--font-body: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif; /* 界面正文 */
--font-mono: 'JetBrains Mono', 'SF Mono', monospace; /* 等宽字体 */
/* ═══════════════════════════════════════════
10. 动画缓动函数
═══════════════════════════════════════════ */
--ease-spring: cubic-bezier(0.22,1,0.36,1);
--ease-out-expo: cubic-bezier(0.16,1,0.3,1);
1.2 颜色语义
| 状态 | 背景色 | 强调色 | 说明 |
|---|---|---|---|
| CALM 安全 | #EAF4EF | #4A8A5A | 首页安全、正常消息 |
| REMINDER 提醒 | #FEF3C7 | #8A7040 | 待确认提醒 |
| ALERT 关注 | #F8EDE9 | #9A5040 | 预警横幅、紧急 CTA |
| HELP 求助 | #F8EDE9 | #9A5040 | 紧急横幅、求助 CTA |
| FAMILY 家人 | #E8F0F8 | #3D6B9A | 家人消息通知 |
| OFFLINE 离线 | #E8E4DE | #5A6060 | 设备离线状态 |
1.3 字体规范
| 元素 | 字号 | 字体 | 字重 | 说明 |
|---|---|---|---|---|
| 老人姓名 | 30px | Fraunces display | 600 | 衬线字体,温暖感 |
| Hero 标题 | 28px | Fraunces display | 600 | 状态主标题 |
| 品牌名 | 36px | Lora | 600 | 登录页品牌展示 |
| 正文 | 14px | DM Sans | 400 | 适老化底线 |
| Section 标签 | 11px | DM Sans | 600 | text-transform: uppercase; letter-spacing: 1px |
| 按钮文字 | 15-16px | DM Sans | 600 | 主 CTA 按钮 |
| 底部 Tab | 10px | DM Sans | 600 | 无障碍支持 |
| 徽章文字 | 9-10px | DM Sans | 700 | 状态徽章 |
1.4 按钮规范
主按钮(.btn-primary):
.btn-primary {
width: 100%;
padding: 17px 24px; /* 高度 ≥56dp */
font-size: 16px;
font-weight: 600;
border: none;
border-radius: 14px;
background: var(--green); /* CALM 态 */
color: #fff;
box-shadow: 0 4px 16px rgba(74,138,90,0.28);
transition: all 0.25s var(--ease-spring);
}
.btn-primary:hover:not(:disabled) {
filter: brightness(1.08);
transform: translateY(-2px);
box-shadow: 0 8px 28px rgba(74,138,90,0.35);
}
.btn-primary:active:not(:disabled) { transform: scale(0.98); }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
次按钮(.btn-secondary):
.btn-secondary {
background: var(--paper);
color: var(--ink-soft);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 16px;
font-size: 11px;
font-weight: 600;
}
危险按钮(.btn-danger):
.btn-danger {
background: var(--red);
color: #fff;
border: none;
border-radius: 10px;
padding: 10px 16px;
font-size: 11px;
font-weight: 600;
}
1.5 表单输入规范
输入框(.form-input / .input):
.form-input {
width: 100%;
padding: 15px 18px;
font-size: 16px; /* 防止 iOS 自动缩放 */
font-family: var(--font-body);
border: 1.5px solid var(--border-subtle);
border-radius: 14px;
background: var(--white);
color: var(--ink);
transition: border-color 0.22s ease, box-shadow 0.22s ease;
-webkit-appearance: none;
}
.form-input:focus {
outline: none;
border-color: var(--green);
box-shadow: 0 0 0 3px rgba(74,138,90,0.1);
}
.form-input.is-error { border-color: #C0392B; box-shadow: 0 0 0 3px rgba(192,57,43,0.1); }
.form-input::placeholder { color: var(--ink-dim); }
表单标签(.form-label):
.form-label {
display: block;
font-size: 11px;
font-weight: 700;
color: var(--ink-soft);
text-transform: uppercase;
letter-spacing: 1.2px;
margin-bottom: 9px;
}
字段错误(.field-error):
.field-error { font-size: 12px; color: #C0392B; margin-top: 6px; display: none; }
.field-error.visible { display: block; }
1.6 卡片组件规范
Hero 卡片(首页状态卡片):
.hero-card {
background: var(--green-light);
border: 1.5px solid var(--green-border);
border-radius: 24px;
padding: 26px 24px;
margin-bottom: 16px;
position: relative;
overflow: hidden;
}
.hero-card::before {
content: '';
position: absolute; top: 0; left: 0; right: 0; height: 3px;
background: linear-gradient(90deg, var(--green), transparent 80%);
opacity: 0.8;
}
.hero-card::after {
content: '';
position: absolute; top: -50px; right: -60px;
width: 180px; height: 180px; border-radius: 50%;
background: radial-gradient(circle, var(--green) 0%, transparent 70%);
opacity: 0.06;
}
通用卡片(.card):
.card {
background: var(--white);
border: 1px solid var(--border2);
border-radius: 12px;
padding: 16px;
}
.card-hero {
background: var(--green-light);
border: 1px solid rgba(45,106,79,0.15);
border-radius: 16px;
padding: 14px;
}
.card-hero.warning { background: var(--amber-light); border-color: rgba(180,83,9,0.15); }
.card-hero.danger { background: var(--red-light); border-color: rgba(153,27,27,0.15); }
Recent 卡片(消息列表卡片):
.timeline-card {
background: var(--bg-card);
border: 1px solid var(--border-subtle);
border-radius: 18px;
padding: 14px 16px;
cursor: pointer;
transition: all 0.2s var(--ease-spring);
}
.timeline-card:hover { background: var(--bg-card-subtle); transform: translateX(3px); box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
1.7 徽章规范
.badge {
display: inline-flex;
align-items: center;
padding: 3px 8px;
border-radius: 20px;
font-size: 9px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.badge-safe { background: var(--green); color: #fff; }
.badge-warning { background: var(--amber-mid); color: #fff; }
.badge-danger { background: var(--red); color: #fff; }
.badge-info { background: var(--ink); color: #fff; }
消息类型徽章(Timeline Card):
.card-type { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.6px; padding: 3px 8px; border-radius: 10px; }
.card-type.checkin { background: var(--green-light); color: var(--green); }
.card-type.voice { background: var(--amber-light); color: var(--amber); }
.card-type.text { background: var(--blue-light); color: var(--blue); }
.card-type.reminder { background: var(--amber-light); color: var(--amber); }
.card-type.alert { background: var(--red-light); color: var(--red); }
.card-type.video { background: var(--teal-light); color: var(--teal); }
.card-type.safe { background: var(--green-light); color: var(--green); }
1.8 老人头像规范
.elder-avatar-wrapper { position: relative; }
.elder-avatar {
width: 50px; height: 50px; border-radius: 50%;
background: linear-gradient(135deg, var(--green), color-mix(in srgb, var(--green) 75%, #000));
display: flex; align-items: center; justify-content: center;
color: #fff; font-family: var(--font-display); font-weight: 600; font-size: 20px;
box-shadow: 0 4px 14px rgba(0,0,0,0.1);
}
.status-ring {
position: absolute; top: -4px; left: -4px; right: -4px; bottom: -4px;
border-radius: 50%; border: 2px solid var(--green);
opacity: 0.35;
animation: ringPulse 3.5s ease-in-out infinite; /* CALM 态 */
}
.status-dot {
position: absolute; bottom: -1px; right: -1px;
width: 13px; height: 13px; border-radius: 50%;
background: var(--green); border: 2.5px solid var(--bg-cloud);
animation: dotBlink 2.5s ease-in-out infinite;
}
1.9 导航规范
底部导航(.bottom-nav):
.bottom-nav {
position: fixed; bottom: 0; left: 0; right: 0;
display: flex;
border-top: 1px solid var(--border-subtle);
padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
background: rgba(255,255,255,0.95);
backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
box-shadow: 0 -2px 12px rgba(0,0,0,0.04);
z-index: 100;
}
@media (min-width: 768px) {
.bottom-nav { left: 50%; transform: translateX(-50%); width: 414px; max-width: 100%; border-radius: 24px 24px 0 0; }
}
.nav-item {
flex: 1; display: flex; flex-direction: column; align-items: center;
padding: 12px 4px 8px;
color: var(--text-tertiary); text-decoration: none;
font-size: 10px; font-weight: 600; gap: 4px;
cursor: pointer; transition: all 0.25s ease;
position: relative;
}
.nav-item::before {
content: ''; position: absolute; top: -1px; left: 50%;
transform: translateX(-50%) scaleX(0);
width: 28px; height: 3px;
background: var(--green);
border-radius: 0 0 3px 3px;
transition: transform 0.3s var(--ease-spring);
}
.nav-item.active { color: var(--green); }
.nav-item.active::before { transform: translateX(-50%) scaleX(1); }
.nav-item svg { width: 22px; height: 22px; fill: currentColor; }
1.10 动画规范
| 动画 | CSS 类 | 时长 | 缓动 | 触发时机 |
|---|---|---|---|---|
| 页面入场 | fadeIn | 0.6s | var(--ease-out) | 页面加载 |
| 内容块入场 | slideUp | 0.5s | var(--ease-spring) | 延迟 0.05-0.3s 依次入场 |
| 卡片脉冲 | cardPulse | 2.5s | ease-in-out | ALERT 状态持续循环 |
| 状态环脉冲 | ringPulse | 1.5-3.5s | ease-in-out | 老人头像状态环(ALERT=1.5s, CALM=3.5s) |
| 状态点闪烁 | dotBlink | 1-2.5s | ease-in-out | 老人头像状态点 |
| 未读脉冲 | unreadPulse | 2s | ease-in-out | 消息未读点 |
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
@keyframes cardPulse { 0%,100% { box-shadow: 0 4px 20px rgba(154,80,64,0.15); } 50% { box-shadow: 0 8px 36px rgba(154,80,64,0.28), 0 0 0 2px rgba(154,80,64,0.08); } }
@keyframes ringPulse { 0%,100% { transform: scale(1); opacity: 0.35; } 50% { transform: scale(1.06); opacity: 0.15; } }
@keyframes dotBlink { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
@keyframes unreadPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
1.11 Toast 通知
.toast {
position: fixed;
top: calc(22px + env(safe-area-inset-top, 0px));
left: 50%;
transform: translateX(-50%) translateY(-16px);
padding: 13px 24px;
border-radius: 12px;
font-size: 14px; font-weight: 500;
box-shadow: 0 8px 32px rgba(0,0,0,0.14);
opacity: 0; pointer-events: none; z-index: 2000; white-space: nowrap;
}
.toast.toast-show { animation: toastSlide 3.2s var(--ease-out) forwards; }
.toast-error { background: #C0392B; color: #fff; }
.toast-success { background: var(--green); color: #fff; }
@keyframes toastSlide {
0% { transform: translateX(-50%) translateY(-16px); opacity: 0; }
12% { transform: translateX(-50%) translateY(0); opacity: 1; }
88% { transform: translateX(-50%) translateY(0); opacity: 1; }
100% { transform: translateX(-50%) translateY(-16px); opacity: 0; }
}
1.12 登录页特殊样式
登录页有独特的环境光球(Ambient Orbs)效果:
body {
background: var(--bg-mist);
background-image:
radial-gradient(ellipse at 25% 6%, rgba(232,244,235,0.75) 0%, transparent 50%),
radial-gradient(ellipse at 78% 92%, rgba(232,244,235,0.5) 0%, transparent 50%),
radial-gradient(ellipse at 55% 50%, rgba(74,138,90,0.04) 0%, transparent 60%);
}
.orb {
position: fixed; border-radius: 50%; pointer-events: none; z-index: 0;
}
.orb-1 { width: 320px; height: 320px; background: radial-gradient(circle, rgba(232,244,235,0.8) 0%, transparent 68%); top: -100px; left: -90px; animation: orbFloat 8s ease-in-out infinite; }
@keyframes orbFloat {
0%, 100% { transform: translateY(0) scale(1); opacity: 1; }
50% { transform: translateY(-14px) scale(1.05); opacity: 0.85; }
}
1.13 触控规范
| 指标 | 最小值 | 说明 |
|---|---|---|
| 按钮高度 | ≥56dp | 主 CTA 使用 padding: 16-17px 24px |
| 触控区域 | ≥44dp × 44dp | 卡片最小触控面积 |
| 按钮间距 | ≥8-12dp | 避免误触 |
| 圆角 | ≥12px | 卡片和按钮统一 border-radius |
2. Luma App(老人端)
2.1 设计令牌(Design Tokens)
Luma App 页面无共享 CSS 文件,每个 HTML 独立定义 :root 变量。按状态分类:
CALM / 倾听态:
:root {
--bg-app: #FAFAF7; /* 暖白背景 */
--bg-card: #FFFFFF;
--text-primary: #1A1A1A;
--text-secondary: #374151;
--text-tertiary: #4B5563;
--accent-primary: #8A6A00; /* 琥珀强调 */
--accent-secondary: #B45309; /* 琥珀次强调 */
--border: #E5E4DF;
--surface-warm: rgba(138, 106, 0, 0.04);
}
FAMILY / 家人态:
:root {
--bg: #0D1117; /* 深黑背景 */
--family-color: #3D6A9A; /* 家庭蓝 */
--family-glow: rgba(61, 106, 154, 0.15);
--card-bg: #1D2D3D;
--text-primary: rgba(255, 255, 255, 0.90);
--text-secondary: rgba(255, 255, 255, 0.65);
--text-dim: rgba(255, 255, 255, 0.35);
}
OFFLINE / 离线态:
:root {
--gray-color: #5A6060; /* 灰色 */
--gray-bg: #2D2D2D;
--device-bg: #0D1117; /* 深黑 */
--amber-bg: rgba(124, 107, 63, 0.16);
--amber-border: rgba(124, 107, 63, 0.22);
--glow-outer: rgba(90, 96, 96, 0.15);
--glow-inner: rgba(90, 96, 96, 0.08);
}
ALERT / HELP / 紧急态:
:root {
--bg-app: #FAFAF7;
--state-danger: #B91C1C; /* 深红 */
--surface-warm: rgba(180, 69, 9, 0.06); /* ALERT 氛围光 */
}
:root {
--state-help: #8A3030; /* HELP 深红 */
--state-help-mid: rgba(138, 48, 48, 0.35);
--state-help-soft: rgba(138, 48, 48, 0.15);
--state-help-glow: rgba(138, 48, 48, 0.22);
--bg-dark: #0D1117;
--bg-help-deep: #2A0E0E;
--green-confirm: #4A8A5A; /* 已接听确认色 */
}
2.2 颜色语义
| 状态 | 背景色 | 强调色 | 说明 |
|---|---|---|---|
| CALM 平静 | #FAFAF7(暖白) | #8A6A00(琥珀) | 正常陪伴界面 |
| ALERT 关注 | #FAFAF7(暖白) | #B91C1C(深红) | 跌倒询问,保持克制 |
| HELP 求助 | #0D1117(深黑) | #8A3030(深红) | 求助界面 |
| OFFLINE 离线 | #0D1117(深黑) | #5A6060(灰色) | 诚实但温和 |
| FAMILY 家人 | #0D1117(深黑) | #3D6A9A(家庭蓝) | 语音消息卡片 |
Luma App 所有深色状态均使用
#0D1117作为基础背景,配合氛围光渐变。
2.3 字体规范
| 元素 | 字号 | 字体 | 字重 | 说明 |
|---|---|---|---|---|
| 时间显示 | ≥52px | Libre Baskerville | 400 | 远距离可读,衬线字体 |
| 日期 | 18-24px | DM Sans | 300 | 大写字母间距 |
| 问候语 | ≥48px | Libre Baskerville | 400 | 情感连接核心元素 |
| 状态信息 | 26-28px | DM Sans | 300 | 浅色文字 |
| 存在感文字 | 20-24px | DM Sans | 400 italic | 引导下一步操作 |
| 帮助提示 | 16px | DM Sans | 300 | 最淡层级的提示 |
| 紧急标题 | 44px | Fraunces | 400 | HELP 态主标题 |
| 紧急副文本 | 19px | Fraunces | 300 | HELP 态说明文字 |
2.4 屏幕结构规范
<!-- Luma App 固定尺寸容器 -->
<div class="screen"> <!-- 1280×800 固定尺寸,响应式缩放 -->
<div class="device-screen"> <!-- 等效根容器(离线/HELP 态) -->
<!-- CALM/FAMILY 态分层结构 -->
<div class="layer-time"> <!-- 时间层:时间 + 日期 -->
<div class="layer-card"> <!-- 内容层:状态卡片/消息 -->
<div class="layer-presence"> <!-- 存在感层:底部提示 -->
<!-- 辅助层 -->
<div class="ambient-glow"> <!-- 径向渐变氛围光 -->
<div class="grain-layer"> <!-- SVG 颗粒纹理叠加 -->
<div class="grain-overlay"> <!-- 等效颗粒层(HELP 态) -->
2.5 倾听动画 规范
Wave 动画(5 根声波条,CALM/Home/Listening 共用):
.listening-indicator {
display: flex; align-items: center; gap: 8-10px;
margin-top: 36-40px;
}
.wave {
width: 6-7px;
border-radius: 3-4px;
background: var(--accent-primary);
animation: wave 1.3-1.4s ease-in-out infinite;
}
/* 5 根波条,不同高度 + 不同 delay */
.wave:nth-child(1) { height: 18-22px; animation-delay: 0s; }
.wave:nth-child(2) { height: 30-36px; animation-delay: 0.12-0.13s; }
.wave:nth-child(3) { height: 22-28px; animation-delay: 0.24-0.26s; }
.wave:nth-child(4) { height: 36-40px; animation-delay: 0.36-0.39s; }
.wave:nth-child(5) { height: 18-22px; animation-delay: 0.48-0.52s; }
@keyframes wave {
0%, 100% { transform: scaleY(0.5); opacity: 0.45-0.5; }
50% { transform: scaleY(1); opacity: 1; }
}
Voice Pulse 动画(FAMILY 态消息卡片的语音点):
.voice-dot {
width: 10px; height: 10px; border-radius: 50%;
background: var(--family-color); opacity: 0.6;
animation: voicePulse 1.5s ease-in-out infinite;
}
.voice-dot:nth-child(2) { animation-delay: 0.2s; }
.voice-dot:nth-child(3) { animation-delay: 0.4s; }
.voice-dot:nth-child(4) { animation-delay: 0.6s; }
.voice-dot:nth-child(5) { animation-delay: 0.8s; }
@keyframes voicePulse {
0%, 100% { transform: scale(1); opacity: 0.4; }
50% { transform: scale(1.3); opacity: 0.8; }
}
Amber Pulse 动画(ALERT 态倾听点):
.dot {
width: 10px; height: 10px; border-radius: 50%;
background-color: var(--accent-secondary);
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.4; transform: scale(1); }
50% { opacity: 1; transform: scale(1.35); }
}
2.6 氛围光规范
CALM 状态(暖白背景):
.screen::before {
background: radial-gradient(
ellipse 70% 50% at 20% 40%,
rgba(138, 106, 0, 0.04) 0%,
transparent 60%
);
}
ALERT 状态(暖白 + 琥珀/红氛围):
body {
background-image:
radial-gradient(ellipse 60% 50% at 30% 55%, rgba(138,106,0,0.10) 0%, transparent 70%),
radial-gradient(ellipse 40% 35% at 70% 40%, rgba(180,83,9,0.06) 0%, transparent 60%);
}
HELP 状态(深色 + 深红氛围呼吸):
.device-screen::before {
background: radial-gradient(
ellipse 80% 70% at 50% 50%,
rgba(138,48,48,0.22) 0%,
rgba(138,48,48,0.08) 35%,
transparent 65%
);
animation: ambientBreath 7s ease-in-out infinite;
}
@keyframes ambientBreath {
0%, 100% { opacity: 0.7; transform: scale(1); }
50% { opacity: 1; transform: scale(1.03); }
}
FAMILY 状态(深色 + 家庭蓝氛围):
.screen::before {
background: radial-gradient(
ellipse 60% 40% at 50% 40%,
rgba(61,106,154,0.15) 0%,
transparent 65%
);
}
OFFLINE 状态(深黑 + 颗粒纹理 + 中心光晕):
.device-screen {
background: #0D0D0D;
box-shadow: 0 0 80px rgba(90,96,96,0.15), 0 0 40px rgba(90,96,96,0.08);
}
.grain-layer {
/* SVG feTurbulence 颗粒纹理,opacity: 0.045,animation: grainShift 8s */
opacity: 0.045;
background-size: 300px 300px;
animation: grainShift 8s steps(1) infinite;
}
@keyframes grainShift {
0% { transform: translate(0, 0); }
10% { transform: translate(-1%, -1%); }
20% { transform: translate(1%, 0%); }
30% { transform: translate(0%, 1%); }
/* ... 10% 步进,共 10 步 */
100% { transform: translate(0, 0); }
}
.ambient-glow {
/* 中心圆形光晕 rgba(90,96,96,0.10) */
position: absolute; top: 38%; left: 50%;
transform: translate(-50%, -50%);
width: 600px; height: 600px; border-radius: 50%;
background: radial-gradient(circle,
rgba(90,96,96,0.10) 0%,
rgba(90,96,96,0.04) 45%,
transparent 70%
);
}
2.7 SOS 紧急动画
SOS 脉冲环(HELP 态):
.sos-ring::before {
content: ''; position: absolute; inset: 0; border-radius: 50%;
border: 2px solid rgba(138,48,48,0.55);
animation: sosPulse 2.2s ease-out infinite;
}
.sos-ring::after {
content: ''; position: absolute; inset: 12px; border-radius: 50%;
border: 1px solid rgba(138,48,48,0.30);
animation: sosPulse 2.2s ease-out infinite 0.5s;
}
@keyframes sosPulse {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(1.8); opacity: 0; }
}
Spin Ring 动画(OFFLINE 态图标环):
.spin-ring {
position: absolute; inset: 0; border-radius: 50%;
border: 1.5px solid rgba(90,96,96,0.18);
animation: spinRing 9s linear infinite;
}
@keyframes spinRing {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.cloud-icon { animation: opacityPulse 5s ease-in-out infinite; }
@keyframes opacityPulse {
0%, 100% { opacity: 0.55; }
50% { opacity: 1; }
}
2.8 布局规范
左对齐布局(常态 Home):
.layout {
padding: 60px 100px 60px 160px; /* 左侧留白更大(屏 幕宽广) */
text-align: left;
align-items: flex-start;
}
居中布局(Care Card、倾听态):
/* 消息卡片居中 */
.layer-card {
display: flex; align-items: center; justify-content: center;
padding: 40px 100px;
}
.card {
background: var(--card-bg);
border-radius: 24px;
padding: 48px 56px;
max-width: 800px;
text-align: center;
box-shadow: 0 0 80px rgba(61,106,154,0.12), 0 0 40px rgba(61,106,154,0.06), 0 16px 48px rgba(0,0,0,0.4);
}
2.9 安全与紧急规范
OFFLINE 安全提示条:
.safety-strip {
background: var(--amber-bg);
border-top: 1px solid var(--amber-border);
padding: 18px 80px;
display: flex; align-items: center; justify-content: center; gap: 12px;
}
/* 固定文案:"If you need help right now, please call 911" */
.safety-text {
font-family: 'Fraunces', Georgia, serif;
font-size: 15px; font-weight: 400;
color: rgba(255,255,255,0.78);
}
存在感文字(最底部,淡色):
.presence-line {
background: #0D0D0D;
padding: 14px 80px 26px;
text-align: center;
}
.presence-text {
font-family: 'Fraunces', Georgia, serif;
font-size: 12px; font-weight: 300; font-style: italic;
color: rgba(255,255,255,0.28);
letter-spacing: 0.6px;
}
2.10 响应式缩放
Luma App 屏幕按宽度分级缩放:
| 屏幕宽度 | 行为 |
|---|---|
≥1280px | 原始尺寸居中 |
900px-1279px | 按比例缩放(width: 100vw; height: 56.25vw) |
<900px | 字号逐级缩小(time 52→40→22px,greeting 48→38→22px) |
@media (max-width: 1280px) {
.screen { width: 100vw; height: 56.25vw; max-height: 100vh; }
}
@media (max-width: 900px) {
.time { font-size: 40px; }
.greeting { font-size: 38px; }
.info { font-size: 22px; }
.presence { font-size: 20px; }
}