欢迎来到 ChatBBS! 这里是 ChatBBS 的社区,在这里可以获得很多关于 ChatBBS的资讯!玩得开心~

DeepSeek 研究 Flarum 的 UI~花费1 块钱左右,消耗33,403,400 tok

likeweixue V1 新手 发布于 2026-10-29 03:09 14 浏览 1 回复 0 赞 AI 开源
分享 教程 踩坑日记 论坛软件 讨论 Ai Flarum

起因是Flarum中文社区2群里面有佬说;

“你用 composer 把 flarum/frontend 卸载了”
“然后对着 ai 说”
“你按照 flarum/frontend 重构一个我喜欢的样子”
“flarum 的整个界面都是这个包负责的”
然后我就喊 DeepSeek 研究了~

Flarum 界面研究 & 本地 Demo

对 flarum-v2.0.2-php8.5 的前端源码做了一次通读,并用它自己的 LESS 源码编译出一套样式,
再按照 Mithril 组件的 DOM 结构手写了一份静态页面,用来直观地看 Flarum 的 UI 风格。

打开方式:双击 site/index.html 即可(纯静态,CSS / JS / 字体都在本地,无需联网、无需 PHP)。
如果浏览器对 file:// 比较严格,也可以在 site/ 里跑 python3 -m http.server 8000,然后访问
<http://localhost:8000>。

![首页](screenshots/home-light.png)


1. 一个前提:Flarum 2.x 已经没有 flarum/frontend 了

你听说的「用 composer 卸载 flarum/frontend,然后让 AI 照着重构」,是 Flarum 1.x 时代的做法。
1.x 里前端(JS + LESS)是独立 composer 包 flarum/frontend;到了 2.0,它被合并进了 flarum/core:

vendor/flarum/core/
├── js/                 # 前端源码(Mithril 组件,TypeScript)
│   ├── src/common/     # 通用组件:Button / Dropdown / Modal / Avatar / Badge ...
│   ├── src/forum/      # 论坛侧:IndexPage / DiscussionPage / PostStream / Composer ...
│   ├── src/admin/      # 管理后台
│   └── dist/           # 编译产物 forum.js / admin.js
├── less/               # ★ 设计系统就在这
│   ├── common/         # 变量、混入、按钮、下拉、表单、弹窗……
│   ├── forum/          # 首页、帖子、帖子流、Hero……
│   ├── admin/
│   ├── forum.less      # 论坛样式入口
│   └── admin.less
└── views/frontend/     # 服务端输出的 HTML 骨架(header / drawer / content)

所以「设计文件在 frontend 里」这个说法,在 2.x 应该改成「设计文件在 flarum/core/js + flarum/core/less 里」。
扩展(tags、likes、sticky……)各自还带自己的 less/ 和 js/src/,会往上叠加。
比如讨论列表右侧那一列彩色标签,就是 flarum/tags 扩展的 less/common/TagLabel.less 做的。

2. 这套 Demo 是怎么来的(没有手抄样式)

真实 Flarum 的 CSS 是这样产出的:PHP 里的 LessCompiler 把
less/common/variables.less → less/common/mixins.less → less/forum.less 依次编译。
这里用同样的顺序、同样的入口,用 lessc 直接编:

  • build/entry.less → site/assets/flarum.css(核心,约 4,000 条规则)
  • build/entry-tags.less → site/assets/tags.css(标签扩展)
  • Font Awesome 7 的字体直接从 vendor/flarum/font-awesome/webfonts 拷过来

也就是说:页面上的颜色、字号、间距、圆角、hover、动画,全部是真货,不是照着截图仿的。
HTML 部分则是照着 Mithril 组件的 view() 还原的 DOM,例如:

组件文件本 Demo 对应
PageStructurejs/src/forum/components/PageStructure.tsx.Page > .Page-main > .Page-hero + .Page-container
HeaderSecondary / HeaderDropdownjs/src/forum/components/*顶栏搜索 / 主题 / 通知 / 头像
IndexPage + IndexSidebarjs/src/forum/components/IndexPage.tsx首页侧栏、排序工具条、讨论列表
DiscussionListItemjs/src/forum/components/DiscussionListItem.tsx列表行:头像、置顶徽章、标题、标签、回复数
DiscussionHero / Herojs/src/forum/components/DiscussionHero.tsx帖子页顶部彩色横幅
PostStream / CommentPost / PostUser / PostMeta同上楼层、头像列、时间下拉、悬停操作
PostStreamScrubber同上右侧「第一条 / 正在查看 1 / 4 / 最后一条」

重新编译样式:

cd build && ./compile.sh      # 会自动 npm i less,然后覆盖 site/assets/*.css

3. Flarum 的设计语言(从源码里量出来的)

颜色

主题色只有一个入口:@config-primary-color / @config-secondary-color(默认都是 #536F90,
后台「外观」里改的就是这两个值),其余颜色全部由它们推导:

Token亮色暗色
--body-bg#fffhsl(212.46, 20%, 10%) ≈ #14191f
--text-color#111#ddd
--control-bghsl(212.46, 26.87%, 93%)hsl(212.46, 20%, 13%)
--control-colorhsl(212.46, 20%, 50%)同左
--muted-colorhsl(212.46, 20%, 50%)hsl(212.46, 15%, 50%)
--link-colorsaturate(#536F90, 10%)同左
--discussion-title-colormix(#111, #fff, 55%)mix(#ddd, #14191f, 55%)
--shadow-colorrgba(0,0,0,.35)rgba(0,0,0,.5)

暗色不是「反色」,而是重新取一组 HSL:饱和度压到 20% 以下、亮度压到 10%,
所以蓝灰的主色在暗色下会自然变成低饱和的深蓝黑。
主题通过 <html data-theme="light|dark|light-hc|dark-hc"> 切换,auto 跟随系统;
本 Demo 右上角的「太阳/月亮」下拉就是这套机制(选择会存进 localStorage)。

尺寸 / 节奏

Token值说明
基础字号13px,行高 1.5body;正文(.Post-body)是 14px / 1.7,≥1100px 时 15px
讨论标题16px(手机 14px)未读 600,已读为 --discussion-title-color
顶栏52px(手机 46px)--header-height,所有吸顶元素都对齐它
圆角4px--border-radius,按钮、标签、输入框、代码块统一
间距20px--post-padding、.Post 内边距
头像列85px--avatar-column-width(桌面 64px 头像 + 21px 间隙)
侧栏190px → 260px(HD)→ 280px(XXL)--sidebar-width,主内容间距 50px(HD 56px)
断点768 / 992 / 1100 / 1600 / 2000 / 3000容器宽 1200 / 1300 / 1500
层级1000 → 1070header → pane → composer → dropdown → modal → alert → tooltip

几个「很 Flarum」的细节(Demo 里都能看到)

  • 头像:没有上传头像时,用用户名的首字母 + 由用户名哈希出的颜色(stringToColor()),
    所以「林小满」永远是那颗淡粉色的「林」。
  • 按钮:默认是淡灰底(--control-bg)+ 4px 圆角;主按钮只改底色为 --primary-color,
    并加粗、左右内边距加到 20px。
  • 徽章:22px 的圆形,--badge-bg 由群组颜色给;置顶帖的图钉、管理员的扳手都在这。
  • 标签:列表里靠右浮在标题行(position:absolute; right:80px),帖子里变成白底彩字的 pill。
  • 讨论页 Hero:有标签时,第一条标签的颜色会成为整个 Hero 的背景色(--hero-bg),
    文字自动选反色 —— Demo 里那条蓝底就是「讨论」标签的 #4A6FA5。
  • 右侧 Scrubber:桌面端不是滚动条,是一条 300px 高的竖向轨道 + 蓝色把手,
    上下各有「第一条 / 最后一条」。
  • 悬停才出现的操作:列表行的 ⋮、楼层右下角的「赞 / ⋮」默认 opacity: 0,
    只有 hover / :focus-within 时才浮出来 —— 鼠标不在时才干净。

4. Demo 里能点什么

操作结果
点列表标题进入帖子页(#/d/2),该条自动变为已读
右上角太阳/月亮跟随系统 / 浅色 / 深色 / 两种高对比度,立即生效
铃铛通知面板(3 条未读,可「全部标为已读」)
头像用户菜单(个人主页 / 设置 / 后台 / 退出)
侧栏标签直接筛选列表(再点「全部讨论」恢复)
排序下拉切换「最新回复 / 最新发布 / 最多回复 / 最早发布」
工具条的对勾全部标为已读(角标一起消失)
列表行 hover出现 ⋮ 菜单,可「标为已读」
「开始讨论」打开 Flarum 风格的弹窗表单
帖子页「写下你的回复…」弹窗提交后,真的会插入一个新楼层,并更新 Scrubber 计数
楼层时间展开 #编号 / 完整时间 / IP / 永久链接
手机宽度(<768px)顶栏变汉堡菜单 + 居中标题 + 右侧主按钮,抽屉、横向侧栏都按 Flarum 的断点走

5. 目录

flarum-ui/
├── site/                     # 演示站点(打开 index.html)
│   ├── index.html            # 首页 + 帖子页(hash 路由)
│   ├── assets/
│   │   ├── flarum.css        # ← 由 vendor/flarum/core/less 编译
│   │   ├── tags.css          # ← 由 vendor/flarum/tags/less 编译
│   │   ├── demo.css          # 只补了 demo 脚手架(hidden、抽屉遮罩等)
│   │   ├── app.js            # 主题 / 下拉 / 路由 / 弹窗 / 发回复
│   │   └── avatars/          # 两张示例头像
│   └── webfonts/             # Font Awesome 7(来自 vendor)
├── build/                    # 重新编译 CSS 用
│   ├── entry.less
│   ├── entry-tags.less
│   └── compile.sh
└── screenshots/              # 首页/帖子页/暗色/手机/弹窗

6. 已知的简化

  • 没有后端:搜索是占位弹窗,分页是「加载更多」按钮,登录状态直接写死为「林小满」。
  • 回复用的是 Flarum 的 Modal 样式;官方是贴底悬浮的 Composer(需要编辑器、自动保存等,
    属于另一套交互,这里没有搬)。
  • 没有引入 jQuery / Bootstrap / Mithril —— 只复用了 CSS 和 DOM 约定,交互由 100 多行原生 JS 完成。
  • 通知面板、悬停菜单的条目是示意数据。
2 楼

Flarum UI 设计指南

基于 Flarum 2.0.2 源码整理(vendor/flarum/core/less + vendor/flarum/core/js/src),
文中所有数值、类名、变量名都能在源码里逐条对上。
配图来自按源码还原的本地 Demo 与组件速查表(样式是 Flarum 原版 LESS 的编译产物)。

适合谁看:要基于 Flarum 做主题 / 扩展 / 二创的设计师与前端;
想理解「为什么 Flarum 长这样」的产品同学。


目录

  1. 设计原则
  2. 颜色系统
  3. 排版
  4. 形状与节奏
  5. 布局与响应式
  6. 组件规范
  7. 图标
  8. 动效
  9. 可访问性
  10. 改主题、写扩展
  11. 附录:速查表

1. 设计原则

Flarum 的界面代码里有几条非常稳定的「隐性规则」。理解了它们,几乎不需要背具体数值。

1.1 Token 先行

界面上出现的每个颜色、圆角、层级都是 CSS 变量,组件只引用变量:

.Button {
  color: var(--button-color);
  background: var(--button-bg);
  border-radius: var(--border-radius);
}

主题、暗色模式、高对比度都只是换一套变量值,不碰组件样式。

1.2 一个主色,推导全站

后台「外观」里只让你填两个色值(primary / secondary),
剩下的控制色、链接色、悬停色、遮罩色全部由它们推导出来:

@primary-color:    @config-primary-color;   // #536F90
@secondary-color:  @config-secondary-color; // #536F90
@link-color:       saturate(@primary-color, 10%);          // #486e9b
@control-bg:       hsl(@secondary-hue, min(50%, @secondary-sat), 93%);
@muted-color:      hsl(@secondary-hue, min(20%, @secondary-sat), 50%);
@body-bg-dark:     hsl(@secondary-hue, min(20%, @secondary-sat), 10%);

1.3 语义命名,而不是外观命名

✅ --control-bg   --muted-color   --discussion-title-color
❌ --grey-100     --blue-500      --title-grey

好处是同一个 token 在亮色 / 暗色下可以取完全不同的值,而组件毫无感知。

1.4 克制的层级

  • 全局只有一种圆角:4px(--border-radius)。
  • 全局只有三种阴影:下拉/顶栏 0 2px 6px、弹窗 0 7px 15px、徽章 0 2px 4px。
  • 层级主要靠字号、字重、留白表达,不靠边框和卡片。
  • 控件高度统一 36px(按钮、输入框、下拉触发器都一样高)。

1.5 内容优先,操作后置

  • 讨论列表没有卡片、没有分割线,行与行只靠间距分开。
  • 每行的 ⋮ 菜单、每个楼层的「赞 / ⋮」默认 opacity: 0,hover 或 :focus-within 才出现。
  • 顶栏在滚动前没有阴影,滚动后才加。
.Post-actions {
  transition: opacity .2s;
  @media @tablet-up { .no-touch & { opacity: 0; } }
  .Post:hover &, .Post:focus-within &, &.openWithin { opacity: 1; }
}

1.6 暗色是「重新调色」,不是反色

暗色不是 filter: invert(),也不是把亮色简单压暗,而是每个 token 重新取一组 HSL:
饱和度压到 20% 以下、亮度压到 10%~16%。所以主色在暗色里会「沉」下去,而不是跳出来。

1.7 可访问性是内建的

  • 所有可交互元素都有键盘焦点环(outline: 5px auto Highlight,只在 :focus-visible 出现)。
  • 图标按钮强制带 aria-label;纯装饰图标带 aria-hidden。
  • 页面有 skip link(跳到主内容);弹窗有 focus trap、role="dialog"、aria-modal。

2. 颜色系统

2.1 推导链

config-secondary ──┬── control-bg      (控件底色:按钮、输入框、代码块)
                   ├── muted-color     (次要文字:时间、说明、占位)
                   ├── body-bg-dark    (暗色背景)
                   └── overlay-bg      (遮罩:弹窗、抽屉)
config-primary ────┼── link-color      (链接、可点击文字)
                   ├── button-primary  (主按钮)
                   └── badge/hero 等强调色

2.2 核心 Token(亮色 / 暗色)

Token亮色暗色用途
--body-bg#ffffff#14191f页面底色
--body-bg-shaded#f7f7f7#0e1215比底色更沉的区域
--body-bg-light#ffffff#1f262e比底色更亮的区域
--control-bg#e8edf2#1b2128按钮、输入框、代码块底色
--control-bg-shaded#dbe2ea#12171c控件按下态 / 分隔线
--control-color#667d99#6c7e93控件上的文字
--muted-color#667d99#6c7e93次要文字
--muted-more-color#aaaaaa#5c6570元信息(时间、回复数说明)
--text-color#111111#dddddd正文
--link-color#486e9b#486e9b链接(主色饱和 +10%)
--discussion-title-color#7c7c7c#838587已读讨论标题
--primary-color#536F90#536F90主色
--shadow-colorrgba(0,0,0,.35)rgba(0,0,0,.5)阴影
--overlay-bgrgba(83,111,144,.9)hsla(212,20%,5%,.9)弹窗 / 抽屉遮罩
--code-bg / --code-color#f7f7f7 / #5e5e5e#0e1215 / #fff行内代码、代码块
--highlight-color#FFE300#FFE300搜索命中高亮

2.3 语义色(不随主题变化)

语义背景文字用途
Success#B4F1AF#33722D成功按钮、成功提示
Warning#fff2ae#ad6c00普通提示条(Alert 默认色)
Error#d83e3e#fff错误提示、危险按钮、表单错误
Online#7FBA00—在线小绿点

注意:这三个语义色在暗色模式下不反色,因为它们本身是「带白/黑文字的高饱和块」。
若你的主题要重定义,记得同时检查块内文字对比度。

2.4 主题切换机制

主题挂在 <html> 上,一行属性切换整套变量:

<html data-theme="light">     <!-- 亮色 -->
<html data-theme="dark">      <!-- 暗色 -->
<html data-theme="light-hc">  <!-- 亮色高对比度 -->
<html data-theme="dark-hc">   <!-- 暗色高对比度 -->

对应选择器:

[data-theme^=light] { .scheme(light); }
[data-theme^=dark]  { .scheme(dark);  }
[data-theme=light-hc] { ... }  // 在 light 基础上加深文字、加强对比
[data-theme=dark-hc]  { ... }

auto 不是一套变量,而是由 JS 解析成 light / dark 后写入 data-theme。
所以任何 Theme 只要提供了这四套变量,就自动支持主题切换。
![深色模式(同一套组件,重新取色)](screenshots/home-dark.png)

2.5 用色规则

  • 不要在组件里写 hex,一律 var(--token)。
  • 背景用 --body-bg / --control-bg;文字用 --text-color / --muted-color。
  • 需要「比底色深一点」时用 --body-bg-shaded,不要手动 darken()。
  • 彩色块(标签、徽章)必须同时设置前景,参照 text-contrast--light/dark 的做法。

对比度参考(默认主题实测):

组合对比度说明
--text-color on --body-bg18.9 : 1正文,非常安全
白字 on --primary-color5.19 : 1主按钮,达标
--muted-color on 白4.24 : 1次要文字,接近 AA 边界
--muted-more-color on 白2.32 : 1⚠️ 仅用于时间、计数等元信息,别放正文

![组件速查 · 颜色与排版](screenshots/styleguide-light-1.png)


3. 排版

3.1 字体栈

font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
             Ubuntu, Cantarell, Oxygen, Roboto, Helvetica, Arial, sans-serif;

不引入 Web Font(除图标字体),保证首屏零字体请求。
中文由系统字体(macOS 苹方 / Windows 微软雅黑)接管,数字与英文走系统 UI 字体。

3.2 字号阶梯

角色字号字重行高颜色
Hero 标题22px(手机 16px)4001.5em继承(彩色 Hero 下自动反色)
讨论标题(未读)16px(手机 14px)6001.3--heading-color
讨论标题(已读)16px4001.3--discussion-title-color
正文(帖子)14px,≥1100px 为 15px4001.7--text-color
界面文本13px4001.5--text-color
表单标签14px7001.5--text-color
辅助说明12px4001.5--muted-color
列表元信息11px4001.5--muted-more-color
按钮13px400 / 700(主按钮)20px--button-color
行内代码90%4001.3--code-color

帖子正文内部的标题(Markdown 渲染):

h1 225% · h2 175% · h3 150% · h4 125% · h5/h6 100%

3.3 行长与留白

  • 宽屏下讨论正文被限制在 100ch(--discussion-content-max-width),
    容器可以继续变宽,但一行文字不会变长。
  • 段落间距 1em,列表、引用块也是 margin-bottom: 1em。
  • 帖子在桌面端有 64px 的头像列(--avatar-column-width: 85px),正文从列右侧开始。

3.4 链接与强调

a { cursor: pointer; color: var(--link-color); text-decoration: none; }
a:hover { text-decoration: underline; }
  • 正文里的链接额外加 1px 底边,hover 时底边变主色(替代下划线,中文阅读更干净)。
  • 强调用 <strong> / <em>,不要用颜色区分语义。
  • 搜索命中用 <mark>:黄底 + 4px 圆角 + 1px 内阴影。

4. 形状与节奏

4.1 圆角

值用途
4px(--border-radius)默认:按钮、输入框、下拉、标签、提示条、代码块
18pxButton--rounded、Button--flat(胶囊按钮)
14pxSwitch 滑块
50%头像、徽章

4.2 控件高度

控件高度
按钮 / 输入框 / 下拉触发器36px
紧凑按钮 Button--compact28px(4px 上下内边距 + 20px 行高)
图标按钮 Button--icon36 × 36
顶栏52px(手机 46px)
标签约 18px(85% 字号 + 0.1em 上下内边距)
徽章22px 圆形

4.3 间距刻度

实际用到的值基本落在这一组上:

4 · 8 · 10 · 12 · 15 · 20 · 30 · 50(· 55 讨论页栏间距)

常见组合:

  • 页面容器左右 padding:15px
  • 内容区顶部间距:30px(手机 15px)
  • 帖子内边距:20px(--post-padding)
  • 下拉菜单项:8px 15px
  • 表单组之间:10px 起,区块之间 24px

4.4 阴影

场景值
顶栏滚动后、下拉菜单、抽屉0 2px 6px var(--shadow-color)
弹窗0 7px 15px var(--shadow-color)
徽章、头像、Tooltip 箭头0 2px 4px var(--shadow-color)

阴影颜色本身是 token(亮色 .35、暗色 .5 黑),所以暗色下阴影更重但不会发灰。

4.5 层级(z-index)

层值
顶栏1000
讨论列表抽屉(pane)1010
编辑器1020
下拉菜单1030
弹窗遮罩1040(+ modal-count)
弹窗1050(+ modal-number)
通知条1060
Tooltip1070

新增浮层时不要写 9999,从这张表里挑一个相邻的层级。


5. 布局与响应式

5.1 断点

名称范围主要变化
small-phone≤ 320px列表信息更窄
phone< 768px抽屉导航;顶栏 46px;标题居中
tablet768 – 991px侧边栏变为横向导航条;顶栏显示头像不显示用户名
desktop992 – 1099px双栏布局(侧栏 + 内容)
desktop-hd≥ 1100px正文 15px、正文封顶 100ch;顶栏搜索常驻(手机移入工具条)
desktop-xl≥ 1600px容器 1300px
desktop-xxl≥ 2000px容器 1500px

5.2 容器宽度

断点.container 宽度
< 1100px流式(左右 15px 内边距)
≥ 1100pxmin(1200px, 100%)
≥ 1600pxmin(1300px, 100%)
≥ 2000pxmin(1500px, 100%)

5.3 页面骨架

<div class="Page IndexPage">
  <div class="Page-pane"></div>              <!-- 可选:讨论列表抽屉 -->
  <div class="Page-main">
    <a class="sr-only sr-only-focusable-custom">跳至主要内容</a>
    <div class="Page-hero">                   <!-- 通栏 Hero -->
      <header class="Hero WelcomeHero">…</header>
    </div>
    <div class="Page-container container">    <!-- 侧栏 + 内容 -->
      <div class="Page-sidebar">…</div>
      <div class="Page-content" id="main-content">…</div>
    </div>
  </div>
</div>
  • .Page-container 是 flex:侧栏宽度 190px → 260px(HD) → 280px(XXL),间距 50px → 56px(HD)。
  • 讨论页把 flex-direction 反转,侧栏跑到右边,并给内容加 100ch 上限。
  • 手机 / 平板下侧栏不占位,而是变成内容上方的横向导航条(sideNav--horizontal)。

5.4 顶栏

<header class="App-header">
  <div class="container">
    <div class="Header-title">…logo / 站名…</div>
    <div class="Header-primary">…导航项…</div>
    <div class="Header-secondary">…搜索 / 主题 / 通知 / 头像…</div>
  </div>
</header>
  • 固定定位,高 52px(手机 46px),z-index: 1000。
  • .Header-secondary 内的控件间距只有 2px:图标按钮固定 36px 宽,文字按钮靠自身 13px 内边距撑开。
  • 平板区间会自动隐藏用户名、只留头像;搜索按钮收成图标。
  • 手机:汉堡(左)+ 页面标题下拉(中)+ 主操作(右,如「开始讨论」),
    顶栏内容整体移入左侧抽屉。

5.5 首页

Page.IndexPage
├─ Page-hero ──── WelcomeHero(通栏,--hero-bg 底 + 一句话简介)
└─ Page-main
   └─ Page-container.container
      ├─ Page-sidebar ── 开始讨论 / 全部讨论 / 标签 / ─── / 标签列表
      └─ Page-content ── 工具条(排序 · 刷新 · 全部已读)
                       └─ DiscussionList(行与行之间没有卡片、没有分割线)
                       └─ 加载更多

5.6 讨论页

Page.DiscussionPage
├─ Page-hero ──── DiscussionHero(背景取第一条标签的颜色,文字自动反色)
└─ Page-container.container(flex-direction: row-reverse → 侧栏在右)
   ├─ Page-content ── PostStream
   │                 ├─ 楼层(64px 头像列|用户名·徽章·时间|正文|赞·⋮)
   │                 ├─ 时间间隔(超过 4 天)
   │                 ├─ 楼层 …
   │                 └─ 回复占位
   └─ Page-sidebar ── 回复 / ⋮ · 第一条 · Scrubber · 最后一条

帖子内部的结构:

区域内容
.Post-header用户名、群组徽章、时间(点击展开编号 / 完整时间 / IP / 永久链接)
.Post-side64px 头像(手机端隐藏,头像改放在标题行)
.Post-body正文:14px / 1.7,代码块、引用、列表都有独立样式
.Post-actions赞、⋮,默认透明,hover / focus 才出现
.Post-footer编辑记录、扩展信息(默认为空)

5.7 响应式要点

元素手机平板桌面
侧栏移入顶栏(标题下拉 / 主按钮)横向导航条左侧固定列
讨论列表单行:头像 + 标题 + 回复数气泡完整两行完整两行 + 右侧标签
帖子头像32px,放在标题行64px 左列64px 左列
帖子操作常显hover 显示hover 显示
弹窗全屏覆盖居中 600px居中 600px

![首页(桌面)](screenshots/home-light.png)
![帖子页(桌面)](screenshots/discussion-light.png)
![首页(手机)](screenshots/phone-home.png)


6. 组件规范

![组件速查 · 组件](screenshots/styleguide-light-2.png)

6.1 按钮 Button

类名外观用在哪
.Button淡灰底、深灰字、4px 圆角默认操作
.Button--primary主色底、白字、加粗、左右 20px页面唯一主操作(开始讨论、发布)
.Button--link透明底、链接色文字型操作、菜单项
.Button--flat透明底、18px 圆角顶栏图标按钮
.Button--icon36×36、只有图标刷新、更多、关闭
.Button--compact4px 10px,更矮表格行内、次级操作
.Button--rounded18px 圆角胶囊型按钮
.Button--danger / --success / --warning语义色删除、确认、警告
.Button--dashed / --outline虚线 / 描边「添加」类空位
[disabled] / .disabledopacity: .65不可用

结构:

<button class="Button Button--primary hasIcon" type="button">
  <i class="icon fa-solid fa-pen-to-square Button-icon" aria-hidden="true"></i>
  <span class="Button-label">
    <span class="Button-labelText">开始讨论</span>
    <span class="Button-helperText">可选副标题</span>
  </span>
</button>

规则:

  • 一个区域只放一个 Button--primary。
  • 纯图标按钮必须给 aria-label。
  • 按钮里不要塞 <div>,文案用 .Button-labelText 包裹,方便截断。
  • 危险操作永远不要和主按钮并排等宽。

6.2 表单

元素规格
.FormControl高 36px、2px 透明边框、8px 13px 内边距、4px 圆角
聚焦边框变 --primary-color,底色变 --body-bg,文字变 --text-color
占位符用 --control-color,不要用浏览器默认灰
标签14px / 700,和控件间距 10px
帮助文本 .helpText12px / --muted-color
错误 .ValidationError0.9em / 700 / --validation-error-color
禁用opacity: .5 + cursor: not-allowed
<div class="Form">
  <div class="Form-group">
    <label>标题</label>
    <input class="FormControl" type="text" placeholder="一句话说清楚你想讨论什么">
    <div class="helpText">发布后仍可编辑。</div>
  </div>
</div>
  • 手机端输入框字号强制 16px,防止 iOS 聚焦时页面放大。
  • 勾选框不用方框,直接显示 ✓ / ✗(.Checkbox-display),开关才是滑块。
  • 表单里「字段级错误」用红色边框盒子(.Form-group--danger)+ 红色说明,
    「整页错误」用 .Alert--error,两者不要混用。

6.3 下拉菜单

<div class="ButtonGroup Dropdown dropdown">
  <button class="Button Dropdown-toggle" data-toggle="dropdown">排序</button>
  <ul class="Dropdown-menu dropdown-menu">
    <li><a class="Button" href="#">最新回复</a></li>
    <li class="Dropdown-separator"></li>
    <li><button class="Button">最多回复</button></li>
  </ul>
</div>
规格值
菜单最小宽度160px
内边距8px 0
与触发器间距7px
菜单项8px 15px,图标与文字间隔 9px
分隔线1px --control-bg,上下 8px
出现方式无动画,直接显示(避免菜单抖动)
  • 靠近屏幕右侧时加 Dropdown-menu--right,靠底时加 Dropdown-menu--top。
  • 菜单项里放 <a> / <button> / <span>,样式自动对齐,不要自己加 padding。

6.4 弹窗 Modal

规格值
宽度默认 600px,Modal--small 375px,Modal--large 800px
位置距顶部 120px,水平居中
圆角4px
阴影0 7px 15px
遮罩--overlay-bg(主色 90% 透明)
结构header(25px 内边距,20px 标题)/ body(--control-bg 底)/ footer
动画transform: scale(.9) → 1 + 淡入,200ms
<div class="ModalManager modal">
  <div class="Modal Modal--small modal-dialog fade in">
    <div class="Modal-content">
      <div class="Modal-close App-backControl"><button class="Button Button--icon Button--link">×</button></div>
      <div class="Modal-header"><h3 class="App-titleControl App-titleControl--text">标题</h3></div>
      <div class="Modal-body">…</div>
      <div class="Modal-footer">…</div>
    </div>
  </div>
  <div class="ModalManager-invisibleBackdrop"></div>
</div>
<div class="Modal-backdrop backdrop" data-showing></div>

注意:遮罩是弹窗容器的兄弟节点而不是子节点,靠 --modal-count / --modal-number
两个变量排队。手机端弹窗变成全屏页。

6.5 头像、徽章、标签

头像 Avatar:没有上传头像时用「用户名首字符 + 用户名哈希色」。

场景尺寸
顶栏用户菜单24px
手机帖子头部32px
讨论列表36px
默认 / 用户卡片48px
桌面帖子64px

哈希规则:用户名所有字符的 charCode 之和 % 360 作为色相,s=0.3, v=0.9。
所以同一个用户名永远是同一种颜色。
徽章 Badge:22px 圆、白/黑字自动对比,颜色来自群组色。

徽章例子
管理员#B72A2A + fa-wrench
版主#80349E + fa-bolt
置顶主色 + fa-thumbtack
默认--muted-color + 任意图标

标签 TagLabel:85% 字号、0.1em 0.5em 内边距、4px 圆角。
多个标签相邻时中间圆角为 0,拼成一个整体;子标签在导航里会缩进并隐藏色块。

6.6 反馈

组件规格
.Alert12px 16px 内边距、警告黄底;--error 红底白字;--success 绿底
.Alert-title粗体 + 16px 图标 + 8px 间距
.LoadingIndicator18 / 24 / 32px 圆环,750ms 匀速旋转,颜色取当前文字色
.Placeholder空状态:40px 上边距,1.4em 居中的 --muted-more-color
.Tooltip12px 字号、最大 200px、深色底 rgba(0,0,0,.9)、7×12 内边距
.Pill4px 圆角的组合小标签(如「已赞 · 3」)

6.7 分页与标签页

  • 分页:首 / 上 / 页码输入框 / 下 / 末 五列,间距 8px;页码输入框最大 80px。
    首页的讨论列表用「加载更多」而不是分页。
  • 标签页 Tabs:15px 字号、12px 8px 内边距、底部 2px 主色下划线标记当前项。

6.8 帖子流

元素规格
楼层 .Post上下 20px 内边距,圆角 4px
头像列桌面 85px(64px 头像 + 21px 空隙)
用户名校验14px / 700 / --heading-color
时间13px / --muted-color,点击展开「编号 / 完整时间 / IP / 永久链接」
正文14px / 1.7(宽屏 15px)
楼层分隔1px --control-bg,右侧留出 20px
时间间隔12px 粗体大写 --muted-color,左缩进一个头像列
回复占位2px 虚线圆角 10px,hover 时才显示边框

7. 图标

  • 图标库:Font Awesome 7 Free(vendor/flarum/font-awesome),本地托管。
  • 统一写法:
<i class="icon fa-solid fa-reply Button-icon" aria-hidden="true"></i>
位置尺寸
按钮内 .Button-icon14px
图标按钮 .Button--icon16px
侧边导航15px
下拉菜单项16px(固定宽度,保证左对齐)
徽章内0.56 × 徽章尺寸 ≈ 12px
帖子页「⋮」16px(Button--icon)

常用图标语义(保持全站一致):

语义图标
回复fa-reply
新讨论fa-edit(= fa-pen-to-square)
更多操作fa-ellipsis-v / fa-ellipsis-h
搜索fa-magnifying-glass
通知fa-bell
主题fa-sun / fa-moon / fa-circle-half-stroke
置顶fa-thumbtack
已读fa-check
刷新fa-rotate
标签fa-tag / fa-table-cells-large

规则:

  • 装饰性图标必须 aria-hidden="true",语义靠文字或 aria-label。
  • 不要用 emoji 代替图标(跨平台渲染不一致),emoji 只出现在用户内容里。
  • 图标颜色跟随文字色,不单独设色;需要强调时改父级。

8. 动效

场景时长曲线
输入框边框/底色150msease
Tooltip 淡入150mslinear
列表行 hover 底色200msease
弹窗缩放出现200msease-out
抽屉滑出200msease
新楼层淡入400msease-in-out
加载转圈750mslinear 无限

原则:

  • 只为状态变化加动画,不为装饰加;列表 hover 是底色渐变,不做位移。
  • 下拉菜单、通知面板不做进出动画——打开要即时。
  • 动画时长不超过 400ms,超过会让人觉得卡。
  • Flarum 源码里没有处理 prefers-reduced-motion;如果你做主题,建议补一条:
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

9. 可访问性

9.1 焦点环

所有可交互元素统一使用浏览器原生焦点环,只在键盘操作时出现:

outline: 5px auto Highlight;
outline: 5px auto -webkit-focus-ring-color;
.my-button { .add-keyboard-focus-ring(); }          // 自己获得焦点
.my-input  { .add-keyboard-focus-ring(":focus-within"); }

不要为了「好看」去掉 outline,也不要用 box-shadow 自制焦点环——高对比度模式下会消失。

9.2 语义与 ARIA

场景做法
纯图标按钮必须 aria-label(如「刷新」「全部标为已读」)
装饰图标aria-hidden="true"
图标徽章role="img" + aria-label(如「管理员」)
下拉aria-haspopup="menu",当前项 aria-current="true"
弹窗role="dialog" + aria-modal="true" + focus trap
仅屏幕阅读器可见.sr-only;数字类信息再补一个 .visually-hidden 的完整描述
跳转页面顶部 skip link,指向 #main-content

9.3 颜色与对比

  • 正文和背景的对比度非常充足(亮色 18.9:1)。
  • --muted-color 约 4.2:1,适合次要文字,不建议放长段落。
  • --muted-more-color 只有 2.3~3:1,只用于元信息(时间、回复数说明)。
  • 彩色标签 / 徽章必须用 YIQ 判断前景色(源码里的 isDark() + text-contrast--*),
    不要凭感觉选白字或黑字。

9.4 触控与键盘

  • 主要操作的可点击区域不小于 36 × 36(图标按钮即 36px)。
  • 手机端标题 + 信息区合成一个大点击区域(.DiscussionListItem-main),右侧回复数是独立按钮。
  • 弹窗打开时锁滚动、Esc 关闭、点击遮罩关闭。
  • 下拉菜单支持 Esc / 失焦关闭;打开时焦点不跳走。

10. 改主题、写扩展

10.1 只改主色

后台 → 外观 → 主色 / 次色。等价于在 LESS 里改:

@config-primary-color:   #2F6F5E;
@config-secondary-color: #2F6F5E;

重新编译后,链接色、按钮、遮罩、暗色背景、控制色会一起变。
不要逐个组件覆盖颜色。

10.2 覆盖单个 token

在「自定义 LESS」(后台)或主题扩展里:

:root {
  --border-radius: 8px;          /* 全局圆角 */
  --post-font-size: 16px;        /* 帖子正文字号 */
}
[data-theme^=dark] {
  --primary-color: #7EA2C9;      /* 暗色下用更亮的主色 */
}

10.3 扩展的 UI 约定

  • 新增界面优先复用 .Button、.FormControl、.Dropdown、.Modal,不要另起一套。
  • 需要往现有界面塞东西时,用 ItemList + 优先级,而不是用 CSS 定位硬贴:
items.add('myButton', <Button>自定义</Button>, 50);  // 数字越大越靠前
  • 类名沿用 BEM 风格:Block / Block-element / Block--modifier。
  • 组件类名与 JS 组件名一致(CommentPost、DiscussionHero),方便反查。

10.4 十条军规

  1. 不写死颜色,一律 var(--token)。
  2. 不覆盖 !important,先找对应的 token 或 mixin。
  3. 不改控件高度(36px),否则整页的垂直节奏会崩。
  4. 圆角只用 4px / 18px / 50% 三种。
  5. 层级用 z-index 表里的值,不写 9999。
  6. 一个区域只有一个主按钮。
  7. 纯图标按钮必须可被读出名字(aria-label)。
  8. 新组件要有 hover / focus-visible / disabled / 暗色 四种状态。
  9. 装饰性动画一律可关(prefers-reduced-motion)。
  10. 中文文案不硬编码在 JS 里,走翻译 key,方便换语言。

11. 附录:速查表

11.1 变量全表(常用)

/* 布局 */
--border-radius: 4px;
--header-height: 52px;
--header-height-phone: 46px;
--drawer-width: 270px;
--pane-width: 400px;
--avatar-column-width: 85px;
--post-padding: 20px;
--container-hd: 1200px;
--container-xl: 1300px;
--container-xxl: 1500px;
--discussion-content-max-width: 100ch;
--post-font-size: 14px;        /* ≥1100px 时 15px */
/* 颜色(会随 data-theme 变化) */
--body-bg  --body-bg-shaded  --body-bg-light
--text-color  --heading-color
--muted-color  --muted-color-light  --muted-more-color
--control-bg  --control-bg-light  --control-bg-shaded  --control-color
--button-bg  --button-bg-hover  --button-bg-active
--button-primary-bg  --button-primary-color
--link-color  --primary-color  --secondary-color
--shadow-color  --overlay-bg  --code-bg  --code-color
--discussion-title-color  --hero-bg  --hero-color
--badge-bg  --avatar-bg  --tag-bg  --tag-color

11.2 尺寸表

项值
顶栏52 / 46px
控件高度36px
图标按钮36 × 36
徽章22px
头像24 / 32 / 36 / 48 / 64
标签字号85%
正文14px / 1.7(HD 15px)
界面文本13px / 1.5

11.3 源码索引

下表路径默认省略前缀 vendor/flarum/core/;tags 扩展单独标注。

想知道去看
颜色、尺寸、断点less/common/variables.less
亮 / 暗 / 高对比度变量less/common/root.less
按钮less/common/Button.less
表单less/common/FormControl.less Form.less Checkbox.less
下拉less/common/Dropdown.less
弹窗less/common/Modal.less
头像 / 徽章less/common/Avatar.less Badge.less
首页 / 侧栏less/forum/IndexPage.less less/common/sideNav.less
讨论列表less/forum/DiscussionList.less DiscussionListItem.less
帖子 / 帖子流less/forum/Post.less PostStream.less Scrubber.less
标签vendor/flarum/tags/less/common/TagLabel.less
组件 DOMjs/src/**/components/*.tsx
页面骨架views/frontend/forum.blade.php

11.4 相关文件

  • 本地 Demo:site/index.html
  • 组件速查表:site/styleguide.html
  • 重新编译样式:build/compile.sh

本文档基于 Flarum 2.0.2 整理;不同版本之间数值可能略有调整,以源码为准。

更多操作
请 登录 后参与讨论。