# DESIGN.md — 魔芋の松果 · 温馨像素设计规范 > 站名 **魔芋の松果**(Pinecone)。导航页和**所有游戏**共用这一套设计语言。 > 改 `design/tokens.css` 一处,全站一起变。 > > 可视化参考:打开 `design/styleguide.html`(也可以 `node tools/serve.mjs` 后访问 `/design/styleguide.html`)。 --- ## 1. 设计方向:温馨像素 Cozy Pixel 一句话:**傍晚的林间小屋,木桌上放着一台老式掌机。** | 关键词 | 落到什么上 | |---|---| | **像素** | 直角、2px 描边、无模糊硬阴影、整数网格、抖动网点。不用圆角、不用模糊、不用渐变过渡 | | **自然** | 暖土色系:赭石打底、陶土做主色、苔绿做辅色、蜜黄做点缀。背景不用纯白,文字不用纯黑 | | **温暖** | 低饱和暖色底 + 高对比暖墨字。留白充足,宁可空着也不塞满 | ### 1.1 为什么不用像素字体 中文没有可靠的像素字重,硬套会让正文难读、小字糊成一团。 所以像素感由**几何**承担——直角、2px 硬描边、无模糊投影、8px 网格、抖动网点、点阵图标; 正文保持系统无衬线的可读性,等宽字体只用在标签、数字、代码这类「机器感」的地方。 这条是硬约束:**不要为了「更像像素」去找像素字体**。 ### 1.2 品牌 **站名:魔芋の松果**(英文 **Pinecone**)。`` 写「魔芋の松果 · 小游戏合集」。 **Logo**:一颗点阵松果,32×32 网格。 | 文件 | 网格 | 用途 | |---|---|---| | `design/logo.svg` | 32×32,透明底 | 品牌位、规范页、大尺寸 | | `design/favicon.svg` | 16×16,奶油底 | 浏览器标签 | - 由 `tools/make-logo.mjs` **参数化生成**(菱形鳞片格 + 收尖轮廓 + 果柄)。 要换形状就改脚本里的 `CFG` 重跑,**不要手改 SVG**。 - 配色:轮廓 `#5c2c10` / 果身 `#c97330` / 受光 `#e9a860`——和 `--primary` 同族。 - **不做单色版**:单色会丢掉鳞片格,16px 下变成一颗没有纹理的蛋。 - **品牌位**(`.brand` / `.brand-mark` / `.brand-text`,在 `components.css`): 40×40 奶油底方块 + 2px 主色描边 + 32px logo。 奶油底是**固定色**(`--px-paper-100`),不随主题变——夜间主题下它像一扇亮着的窗。 --- ## 2. 设计原则 1. **一屏一个焦点**:最大的字、最亮的按钮、唯一的主色块,三者只出现在一处。 2. **主按钮只有一个**:其余降级为描边按钮,再次级用幽灵按钮。 3. **靠字号和颜色分层,不靠边框堆结构**:分割线只用在真正需要断开的地方。 4. **间距只用 4 的倍数**:`4 / 8 / 12 / 16 / 24 / 32 / 48 / 64`,组内 < 组间 < 区块。 5. **硬边优先**:能直角就直角,能硬阴影就不模糊,能纯色就不渐变。 6. **真实内容**:不写「标题一」「示例文字」,写像真游戏的文案。 --- ## 3. 色彩 ### 3.1 语义色(按角色取用,不要按色相取用) | 角色 | 变量 | 日间 | 夜间 | 用途 | |---|---|---|---|---| | 背景 | `--bg` | `#f4ecdc` | `#1f1813` | 页面底 | | 背景深 | `--bg-alt` | `#ece0c9` | `#17110d` | 代码块、键帽 | | 表面 | `--surface` | `#fdf8ee` | `#251d17` | 卡片、输入框、按钮 | | 表面深 | `--surface-alt` | `#f0e4cf` | `#2e241c` | 悬停态、标签底 | | 描边 | `--line` | `#d5c3a6` | `#3b2f25` | 2px 边框 | | 描边强 | `--line-strong` | `#b39d7c` | `#55443a` | 强调边框 | | 正文 | `--text` | `#2b2116` | `#f3e7d5` | 正文、标题 | | 次要 | `--text-muted` | `#6a5741` | `#b6a289` | 描述、说明 | | 弱化 | `--text-dim` | `#7a664c` | `#97846c` | 标签、元信息 | | 主色 | `--primary` | `#b84d24` | `#e8823f` | 主按钮、选中态、链接 | | 主色悬停 | `--primary-hover` | `#a8471f` | `#f2955a` | 悬停 | | 主色上的字 | `--on-primary` | `#fff8ee` | `#1a1410` | 主色块上的文字 | | 辅色 | `--accent` | `#527030` | `#8fb45c` | 次要强调、成功 | | 点缀 | `--honey` | `#d3961f` | `#e9b455` | 徽章、高亮(**只作填充**) | | 点缀上的字 | `--on-honey` | `#2b2116` | `#1a1410` | 蜜黄块上的文字 | | 危险 | `--danger` | `#a83a52` | `#d4617a` | 错误、删除 | | 信息 | `--info` | `#356a84` | `#6fa3bd` | 提示、链接备选 | ### 3.2 对比度(已实测,改色必须重测) | 组合 | 日间 | 夜间 | 要求 | |---|---|---|---| | `text` / `bg` | 13.4:1 | 14.4:1 | ≥ 4.5 | | `text-muted` / `bg` | 5.9:1 | 7.1:1 | ≥ 4.5 | | `text-dim` / `bg` | 4.7:1 | 4.9:1 | ≥ 4.5 | | `on-primary` / `primary` | 5.1:1 | 6.7:1 | ≥ 4.5 | | `primary` / `bg` | 4.3:1 | 6.4:1 | ≥ 3(大字 / 图形) | | `accent` / `bg` | 4.8:1 | 7.4:1 | ≥ 3 | | `info` / `bg` | 5.0:1 | 6.4:1 | ≥ 3 | | `danger` / `bg` | 5.3:1 | 4.8:1 | ≥ 3 | | `on-honey` / `honey` | 6.1:1 | 9.3:1 | ≥ 4.5 | ### 3.3 用色规则 - **主色只用在关键操作和少量点缀**,大致 60 中性 / 30 辅色 / 10 主色。 - **`--honey` 只能当填充**,不能当文字色(对背景只有 2.2:1)。它上面必须放 `--on-honey`。 - **中性色带褐色相**,不要用纯灰(`#888` 之类)。背景不用 `#fff`,文字不用 `#000`。 - **一个游戏最多引入一个自己的强调色**(`game.json` 的 `accent`),其余全部走语义色。 - 语义色在 `design/tokens.css` 里定义,**游戏里不要重新定义一套**。 --- ## 4. 字体 ```css --font-body: system-ui, -apple-system, "Segoe UI", "PingFang SC", "HarmonyOS Sans SC", "MiSans", "Microsoft YaHei", sans-serif; --font-mono: ui-monospace, "Cascadia Mono", "SFMono-Regular", "JetBrains Mono", Consolas, "Liberation Mono", monospace; ``` | 用途 | 字体 | 字号 | 字重 | 其他 | |---|---|---|---|---| | 页面大标题 | body | 32 / 24 | 700 | `letter-spacing: -.015em`,行高 1.15 | | 区块标题 | body | 20 | 700 | 行高 1.35 | | 卡片标题 | body | 16 | 700 | 行高 1.35 | | 正文 | body | 16 | 400 | 行高 1.65,一行 20–35 个中文 | | 次要说明 | body | 14 | 400 | `--text-muted` | | 卡片描述 | body | 13 | 400 | `--text-muted`,最多 2 行 | | 小标签 / 数字 / 代码 | **mono** | 12 | 600 | `letter-spacing: .14em`,大写 | **字号阶梯**:`12 / 13 / 14 / 16 / 20 / 24 / 32 / 40`。一屏最多用 4 档。 **中文排版**: - 不用斜体;字重用 400 / 500 / 600 / 700,标题最多 700。 - 中英文、数字之间加空格(「iPhone 16」「2048 分」)。 - 标点用全角。 - 标题不要只剩一两个字:调宽度或用 `text-wrap: balance`。 --- ## 5. 间距与网格 ```css --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-6: 24px; --sp-8: 32px; --sp-12: 48px; --sp-16: 64px; ``` - 只用这 8 档,**不要写 10px / 18px / 22px 这种**。 - 页面左右留白:桌面 24px,手机 16px。 - 内容最大宽度:导航页 1200px,游戏建议 520–720px。 - 背景是 **8px 像素网格**(`--grid-line`,透明度 3–5%),用来强化像素感,不要加粗。 --- ## 6. 形状与层次 ```css --radius: 0; /* 一律直角 */ --border-w: 2px; /* 一律 2px 描边 */ --shadow-off: 3px; /* 硬阴影偏移 */ --shadow: 3px 3px 0 var(--shadow-color); --shadow-lg: 5px 5px 0 var(--shadow-color); ``` - **圆角永远是 0**。需要「圆」的地方用点阵图标或方块拼。 - **阴影永远是硬边**(`blur = 0`),颜色是 `--shadow-color`,不是黑色半透明。 - **按下态**:元素向右下平移、阴影收掉——像真的按进板子里。 ```css .px-btn:active { transform: translate(2px, 2px); box-shadow: 0 0 0 var(--shadow-color); } ``` - **悬停态**:向左上浮 1–2px,阴影加深一档。 - 深色主题靠**表面色差**分层(`--surface` vs `--bg`),不靠阴影。 --- ## 7. 组件 全部在 `design/components.css`,类名统一 `px-` 前缀。 | 组件 | 类名 | 说明 | |---|---|---| | 按钮 | `.px-btn` | 基础;`.px-btn--primary` 主按钮;`.px-btn--ghost` 幽灵;`.px-btn--sm` 小号 | | 图标按钮 | `.px-icon-btn` | 40×40,放 24px 图标 | | 输入框 | `.px-input` | 聚焦时描边变主色;`.px-input--error` 错误态 | | 下拉框 | `.px-select` | | | 卡片 | `.px-card` | 内部结构见下 | | 徽章 | `.px-badge` | `.px-badge--honey` / `.px-badge--muted` | | 标签 | `.px-tag` | 分类用 | | 面板 | `.px-panel` | 带描边和阴影的容器 | | 状态栏 | `.px-statusbar` | 导航页和游戏页共用的顶栏,见 §7.3 | | 用户位 | `.px-user` | 顶栏右边:`登录` / `昵称 + 分数` | | 键帽 | `.px-kbd` | 快捷键提示 | | 空状态 | `.empty` | 含 `.steps` / `.step-n` | ### 7.1 卡片结构 ```html <a class="px-card" href="snake/index.html" style="--accent:#527030"> <div class="card-cover"><span class="card-emoji">🐍</span></div> <div class="card-body"> <h3 class="card-title"><span>贪吃蛇</span><em class="card-sub">Snake</em></h3> <p class="card-desc">一句话介绍,最多两行。</p> <div class="card-tags"><span class="px-tag">街机</span></div> </div> </a> ``` 封面的像素风景由 `--accent` 自动生成:三层天空色带 + 地平线抖动网点 + 地面色块 + 两颗像素星。 **不要**给封面加图片背景盖掉它;要自定义就整块换掉 `.card-cover` 的内容。 ### 7.2 交互状态 用户会点的地方都要有:悬停、按下、聚焦(`:focus-visible`)、禁用、空、错误。 禁用一律 `opacity: .45` + `cursor: not-allowed`,不要只改颜色。 ### 7.3 状态栏(导航页和游戏页同一条) 一条顶栏,左边「你在哪」,右边**用户位**。**同一个组件,两个位置**: | 位置 | 左边 | 右边(用户位) | |---|---|---| | 导航页 | 品牌 + 搜索 + 榜单 | 没登录 → `登录`;登录了 → `昵称 + 总 rating` | | 游戏页 | `← 合集` + 游戏名 | 没登录 → `登录`;登录了 → `昵称 + 这个游戏的 rating` | 导航页写在 `index.html` 里;游戏页不用手写,调 SDK: ```html <script src="../sdk/statusbar.js"></script> <script>KonjacStatusBar.mount({ title: '贪吃蛇' });</script> ``` 生成的结构: ```html <header class="px-statusbar"> <div class="px-statusbar-inner"> <a class="px-btn px-btn--sm px-btn--ghost" href="../index.html">← 合集</a> <span class="px-statusbar-title">贪吃蛇</span> <span class="px-statusbar-spacer"></span> <a class="px-user" href="../index.html#me" data-state="in" data-score="1"> <span class="px-user-name">魔芋</span> <b class="px-user-score">3540</b> </a> <button class="px-icon-btn" id="lg-theme" type="button">☾</button> </div> </header> ``` 规矩: - **用户位只有一个**(`.px-user`):没登录是「登录」,登录后是「昵称 + 分数」。 不要拆成两个控件,也不要只在游戏页放个孤零零的分数。 - 分数是蜜黄底 + 墨字,**全站唯一给分数上色的地方**。 - 分数是**对外分**(0–10000),不是 0–1。0–1 只在「我的」面板的小字里出现。 - **没成绩就不显示分数**。没身份 / 没配 endpoint / 还没玩过 → 只显示昵称,不要拿 0 冒充。 - 内容列宽度由 `--statusbar-max` 定:导航页 1200(默认),游戏页写 `--statusbar-max: 640px`。 - 游戏页**不要自己画顶栏**。要加东西就改 `sdk/statusbar.js`,所有游戏一起变。 - 主题键和导航页共用 `lg:nav:theme`:在首页切了夜间,进游戏也是夜间。 - 游戏自己的数值(得分、步数、血量)**不进状态栏**,放在下面的 `.hud` 一行里。 - 点游戏页的用户位会回到 `../index.html#me`,「我的」面板自动打开。 --- ## 8. 图标 - 点阵 **12×12**,显示 **24px**(整数 2 倍,像素才不糊)。 - 定义在 `design/pixel-icons.js`,用点阵字符串写,运行时转 SVG。 - 颜色跟随 `currentColor`,所以用 CSS 的 `color` 控制。 - **不要用 emoji 当界面图标**(游戏自己的角色 emoji 除外,那是内容不是图标)。 ```html <script src="../design/pixel-icons.js"></script> <script> el.innerHTML = LGPixel.icon('search', 24); </script> ``` 现有图标:`search` `sun` `moon` `dice` `gamepad` `arrow`。加新图标就往 `ICONS` 里加一行点阵。 --- ## 9. 动效 ```css --dur-fast: 120ms; --dur: 180ms; --ease: cubic-bezier(.2, .8, .2, 1); ``` - 只动 `transform` 和 `opacity`,不动 `width` / `top` / `margin`。 - 悬停 120ms,状态切换 180ms,超过 250ms 就嫌慢。 - **不要**做呼吸灯、粒子、持续闪烁——像素风靠静态画面立住。 - 必须尊重 `prefers-reduced-motion`(`tokens.css` 已全局兜底)。 --- ## 10. 可访问性 - 正文对比度 ≥ 4.5:1,大字和图标 ≥ 3:1(见 §3.2 实测表)。 - 可点区域至少 **44×44**(图标按钮 40×40 + 外围留白)。 - 焦点必须看得见:`box-shadow: var(--focus-ring)`,不要 `outline: none` 了事。 - 语义标签:`header` `nav` `main` `button` `a`;图片有 `alt`;纯装饰图标 `aria-hidden="true"`。 - 颜色不能是唯一的信息载体(选中态除了变色还要有 `aria-pressed`)。 --- ## 11. 怎么让游戏和站点统一(关键) 统一靠三件事,缺一不可: ### 11.1 引入同一套变量和组件 每个游戏的 `index.html` 的 `<head>` 里: ```html <link rel="stylesheet" href="../design/tokens.css"> <link rel="stylesheet" href="../design/components.css"> ``` - **不要**复制一份 token 到自己目录里改。要改就改 `design/tokens.css`,全站一起变。 - 游戏自己的样式写在 `<style>` 里,**只写布局**,颜色 / 字号 / 间距 / 圆角 / 阴影一律用 `var(--…)`。 ### 11.2 主题 ```html <html lang="zh-CN"> <!-- 跟随系统 --> <html lang="zh-CN" data-theme="light"> <!-- 强制日间 --> <html lang="zh-CN" data-theme="dark"> <!-- 强制夜间 --> ``` 游戏如果要做主题切换,用 `document.documentElement.dataset.theme = 'dark' | 'light'`, 并把选择存到 `lg:<id>:theme`。**不要**自己另起一套配色。 ### 11.3 用 px- 组件,不要重造 按钮用 `.px-btn`,输入框用 `.px-input`,卡片用 `.px-card`。 只有游戏特有的东西(棋盘、血条、摇杆)才自己写样式,且必须用 token 变量。 ### 11.4 最小骨架 ```html <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>贪吃蛇
得分 0
``` 完整骨架(带 `logic.js`、重放证据、触屏输入)见 `_template/`。 --- ## 12. 禁止事项 这些是「一眼 AI 味」和「一眼不像这个站」的通病,**不要做**: - ❌ 圆角卡片、毛玻璃、大模糊阴影、紫蓝渐变、霓虹发光 - ❌ 每个区块都居中、每段都是「图标 + 标题 + 一句话」三栏 - ❌ emoji 当界面图标 - ❌ 一屏五六种颜色;主色到处用 - ❌ 像素字体套中文正文 - ❌ 纯白背景 + 纯黑文字 - ❌ 写死色值(`#fff`、`rgb(...)`)而不走变量 - ❌ 复制一份 token 到自己目录里改 - ❌ 呼吸灯 / 粒子 / 持续闪烁的装饰动效 - ❌ 口号式空文案(「赋能」「一站式」「极致体验」) --- ## 13. 自查清单 做完一屏,逐条过: - [ ] 所有颜色 / 字号 / 间距 / 圆角都来自 `var(--…)`,没有写死的值 - [ ] 圆角是 0,描边是 2px,阴影无模糊 - [ ] 一屏只有一个主按钮、一个视觉焦点 - [ ] 间距都是 4 的倍数 - [ ] 正文对比度 ≥ 4.5:1(改了颜色就重测 §3.2) - [ ] 悬停 / 按下 / 聚焦 / 禁用 / 空 / 错误 六种状态都有 - [ ] 手机上 375px 宽不横向滚动、按钮点得到 - [ ] 日间和夜间都看过一眼 - [ ] 没有 §12 里的任何一条 - [ ] 图标是点阵 SVG,不是 emoji --- ## 附:文件位置 | 文件 | 作用 | |---|---| | `design/tokens.css` | 颜色 / 字体 / 间距 / 形状 / 动效变量 —— **唯一真源** | | `design/components.css` | `px-` 组件库 + 品牌位 | | `design/pixel-icons.js` | 点阵图标集 | | `design/logo.svg` | 松果 logo(32×32) | | `design/favicon.svg` | 标签页图标(16×16) | | `design/styleguide.html` | 可视化规范(本文件的配图版) | | `sdk/konjac-rating.js` | 计分接口(无需登录,纯在线) | | `sdk/statusbar.js` | 游戏页顶栏(和导航页同款,见 §7.3) | | `tools/make-logo.mjs` | 生成 logo 与 favicon | | `assets/nav.css` | 导航页布局(只有布局,不含组件) | | `assets/nav.js` | 导航页逻辑 |