Design system
Swiss Ledger 与 Ledger Motion
九个 App 和这个网站共用的一套设计语言。下面每个 token 都能看到、重播,旁边是 SwiftUI 与 CSS 两种写法,数值一一对应。
模拟「减弱动态效果」
Principles
- 01纸与墨页面与面板同色,层级只靠 1px 发丝描边和留白。不用浮起的卡片、阴影、毛玻璃、渐变底。
- 02只有一种颜色会说话全家族唯一强调色是印刷红。一屏之内有颜色的元素通常只有墨与红两种。
- 03直角面板与控件 2pt,标签与进度条 0pt,开关是方形滑块。
- 04数字是英雄所有数字等宽,最重要的那个数字是全屏最大最重的元素。
- 05动效是确认,不是表演每个动效都回答「我的操作生效了吗」或「它从哪来、到哪去」。
Color
| Token | 浅色 | 深色 | 对比度(纸 / 墨) | 用途 |
|---|---|---|---|---|
| paperpageBackground | #FAFAF7 | #0B0B0A | — | 页面底,面板与页面同色 |
| inkink | #0B0B0A | #F5F4F0 | 18.83 / 17.89 | 正文、主图形、选中态、主按钮 |
| ink-softinkSoft | #6E6E68 | #9E9E99 | 4.91 / 7.32 | 次要文本、元信息、分组标题 |
| ink-faintinkFaint | #74736D | #7A7A75 | 4.55 / 4.56 | 禁用、占位符、装饰图形 |
| accentaccent | #C4321F | #E2503A | 5.25 / 5.10 | 全家族唯一强调色:当前选中、需要处理的一项 |
| successsuccess | #2F7A63 | #3F9E82 | 4.92 / 6.02 | 描边标签、收入金额 |
| warningwarning | #8A6100 | #C9962A | 5.30 / 7.39 | 待处理、偏高 |
| dangerdanger | #9E1B12 | #F2684F | 7.67 / 6.44 | 只用于不可逆操作 |
| borderpanelBorder | 黑 14% | 白 14% | 1.38 | 面板与控件的 1px 描边,只负责分组 |
| dividerdivider | 黑 10% | 白 10% | — | 面板内的行分隔线 |
强调色预算:每屏强调色区块 ≤ 1 处、标签与小字 ≤ 3 处、图标 0。支出永远不用红色,红色留给「超支」这个判断。
Type
¥3,222
一眼看清本月还剩多少。
账单导入
把过去几个月的账一次带进来
最少路径只要金额和分类,其余字段收进「更多选项」。
统计于 2026-10-02,只计入 git 已跟踪的文件。
Budget left · 9 月
Archivo 400 / 700 / 800,与 App 打包的是同一份字体文件;中文回落到苹方。所有数字等宽:1,111.11 / 8,888.88。
Shape
Five actions
账本上只会发生五种事,所以界面上只允许五种动效。
午餐 · 沙县小吃−¥38.00
地铁−¥4.00
咖啡−¥22.00
午餐 · 沙县小吃−¥38.00
超市−¥64.50
房租−¥2,820.00
¥3,222
Cleared
已删除 午餐 ¥38撤销
Motion tokens
instant0Tab 内容切换、减弱动态降级
- SwiftUI
AppMotion.instant无动画- CSS
--ledger-instant0ms
press100ms按下态
- SwiftUI
AppMotion.press.easeOut(duration: 0.10)- CSS
--ledger-press100ms cubic-bezier(0, 0, .58, 1)
quick120ms数值交叉淡化、含义变化
- SwiftUI
AppMotion.quick.easeInOut(duration: 0.12)- CSS
--ledger-quick120ms cubic-bezier(.42, 0, .58, 1)
standard180ms展开收起、选中、筛选、开关
- SwiftUI
AppMotion.standard.easeInOut(duration: 0.18)- CSS
--ledger-standard180ms cubic-bezier(.42, 0, .58, 1)
exit180ms收笔
- SwiftUI
AppMotion.exit.easeOut(duration: 0.18)- CSS
--ledger-exit180ms cubic-bezier(0, 0, .58, 1)
enter240ms落笔
- SwiftUI
AppMotion.enter.timingCurve(0.2, 0, 0, 1, duration: 0.24)- CSS
--ledger-enter240ms cubic-bezier(.2, 0, 0, 1)
tick400ms刻度
- SwiftUI
AppMotion.tick.timingCurve(0.2, 0, 0, 1, duration: 0.40)- CSS
--ledger-tick400ms cubic-bezier(.2, 0, 0, 1)
stamp220ms盖章(临界阻尼,零过冲)
- SwiftUI
AppMotion.stamp.spring(response: 0.22, dampingFraction: 1.0)- CSS
--ledger-stamp220ms cubic-bezier(.2, 0, 0, 1)
banner320ms横幅入场,唯一允许轻微过冲
- SwiftUI
AppMotion.banner.spring(response: 0.35, dampingFraction: 0.74)- CSS
--ledger-banner320ms cubic-bezier(.3, 1.1, .6, 1)
上面的示例放慢 4 倍播放,便于看清曲线。编排常量:落笔上移 6,逐行间隔 32ms,最多 8 行参与;单个动效 ≤ 0.5s,一段编排 ≤ 0.9s。例外只有三个:开篇 ≤ 1.0s、FOOTY 点亮 ≤ 2.6s、网站签名演示 ≤ 1.6s。
Reduced motion
| 动作 | 正常 | 减弱动态效果 |
|---|---|---|
| enter / 逐行 | 淡入 + 上移 6,逐行 | 120ms 淡入,不逐行 |
| tick | 数字滚动、线生长 | 直接显示最终值 |
| stamp | 0.92 → 1 缩放 | 120ms 淡入 |
| banner | 从顶部滑入 | 120ms 淡入 |
| standard / exit / press | 不变 | 不变 |
| 签名演示 | 完整编排 | 直接显示终态 |
位移和缩放消失,只保留最短的淡化,信息一项不少。网站读取 prefers-reduced-motion,App 读取系统的同名设置。