25 changed files with 424 additions and 610 deletions
@ -1,163 +0,0 @@ |
|||||
# 巴适伙伴 · 业务体系总纲(业务全景图) |
|
||||
|
|
||||
> 这是"各版块业务"的体系总纲。先定结构,后续产品文案、首页↔业务页衔接、Innovation/Sustainability/Investors 等边缘板块如何收口,都以本文档为据。 |
|
||||
> **骨架已于 2026-08 确认**:第 8 节 6 项关键推断全部拍板(详见第 8 节"已确认的关键决策")。下文凡原标【推断·待确认】者均已转正为定稿;第 5–7 节为"骨架确认后的落地建议",待执行。 |
|
||||
> 关联代码:业务页 [`Business.tsx`](src/components/Business.tsx) · 赛道数据 [`zh.ts`](src/i18n/zh.ts) `business.tracks` · 首页受众 [`Home.tsx`](src/components/Home.tsx) `pillars`。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 0. 现状诊断:为什么现在还不是"一个完整的体系" |
|
||||
|
|
||||
当前站点存在**两套并行、互不映射**的业务心智模型: |
|
||||
- **业务页** 按「4 条赛道」组织(内容社交/职场服务/数字支付/人工智能)——回答"我们造什么" |
|
||||
- **首页** 按「3 大受众」组织(消费者/企业/未来)——回答"我们为谁" |
|
||||
|
|
||||
两者之间没有桥接,访客看不到"妥哒服务谁""阿布AI算不算未来",等于两个半体系。叠加以下问题,整体未成"体系": |
|
||||
1. 赛道失衡无逻辑支撑:数字支付仅 1 个产品(妥哒)显单薄,职场服务 4 个最重;缺一句"为什么是 4 条、为什么这样配比"。 |
|
||||
2. 产品层信息单薄:每个产品只有"名称 + 一句营销语",无定位/目标用户/核心价值/生态位置。 |
|
||||
3. 定位—赛道—受众之间没有"数字生活生态"结构图:业务页 CTA 写"共建数字生活生态",但生态长什么样从未可视化。 |
|
||||
4. 边缘板块脱节:Innovation 页已重定向到 /business(半废弃悬空);Sustainability 的"数字普惠/绿色低碳/责任运营"是抽象概念,未与产品挂钩;Investors 是孤立 CTA。 |
|
||||
5. 品牌命名待澄清 ~~(原疑点)~~ → **已澄清(2026-08)**:项目文件夹名"巴士伙伴官网"与 Mascot(车形狗)用"巴士",但**品牌名是"巴适伙伴"**(川渝方言"舒服/恰好")。"巴士"仅为文件夹命名遗留 + Mascot 玩梗,**不构成"出行/交通"业务维度**,体系图无需加入出行/交通赛道。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 1. 一句话定位(L1 · 定位层) |
|
||||
|
|
||||
- **巴适伙伴是**:聚焦数字生活服务的高科技企业,以"温暖科技"连接人、机会、价值与智能,共建数字生活生态。 |
|
||||
- **为谁**:消费者 · 企业 · 未来(前沿) |
|
||||
- **创造什么价值**:让数字生活更**连接、更成长、更可信、更被理解** |
|
||||
|
|
||||
> ✅ 已确认:"温暖科技""数字生活生态"作为长期品牌核心词沿用。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 2. 数字生活的四个动作(L3 · 赛道逻辑 / "为什么是 4 条") |
|
||||
|
|
||||
4 条赛道 = 数字生活的 4 个基本面,对应 4 个动词,自洽且可对外讲清"为什么是 4 条、为什么这样配比": |
|
||||
|
|
||||
| 赛道 | 动作 | 关系 | 现有产品数 | |
|
||||
|---|---|---|---| |
|
||||
| 内容社交 | **连接** | 人与人 | 3(青核/音酷/蜜恋) | |
|
||||
| 职场服务 | **成长** | 人与机会 | 4(职来/看山/步途/跃升) | |
|
||||
| 数字支付 | **交易** | 人与价值 | 1(妥哒) | |
|
||||
| 人工智能 | **理解** | 人与智能 | 2(阿布AI/听愿) | |
|
||||
|
|
||||
> ✅ 已确认配比解读:职场服务 4 个最重,因为"成长"是巴适当前最厚的能力沉淀;数字支付 1 个是"交易底座",重深度而非数量;人工智能 2 个是未来下注。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 3. 三大受众(L2 · 受众层) |
|
||||
|
|
||||
| 受众 | 定义 | 核心需求 | |
|
||||
|---|---|---| |
|
||||
| **消费者** | 个体的数字生活 | 社交 / 创作 / 求职 / 支付 / AI 陪伴 | |
|
||||
| **企业** | 组织的效率与增长 | 招聘 / 知识 / 培训 / 收付款 | |
|
||||
| **未来** | 前沿与平台层(长期下注) | 多模态 AI / 意图理解 / 基础设施 | |
|
||||
|
|
||||
> ✅ 已确认:"未来"作为受众定义为"前沿/平台层"而非具体客户——成立。AI 赛道(阿布AI/听愿)即"未来"受众的落点。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 4. 产品 × 受众矩阵(横切 · 每个产品有家) |
|
||||
|
|
||||
打勾 = 该产品服务该受众。 |
|
||||
|
|
||||
| 产品 | 赛道 | 消费者 | 企业 | 未来 | 价值主张 | 生态角色 | |
|
||||
|---|---|:--:|:--:|:--:|---|---| |
|
||||
| 青核 | 内容社交 | ✓ | | | 让志同道合的人真诚相遇 | 连接引擎 | |
|
||||
| 音酷 | 内容社交 | ✓ | | | 让人人都能创作音乐 | 创作工具 | |
|
||||
| 蜜恋 | 内容社交 | ✓ | | | 理解情感的温暖社交 | 情感连接 | |
|
||||
| 职来 | 职场服务 | ✓ | ✓ | | 人岗智能匹配,成长路径可见 | 双边市场 | |
|
||||
| 看山 | 职场服务 | ✓ | ✓ | | 让知识触手可及 | 知识基础设施 | |
|
||||
| 步途 | 职场服务 | ✓ | | | 个人成长路径规划 | 成长工具 | |
|
||||
| 跃升 | 职场服务 | ✓ | ✓ | | 能力提升引擎,赋能跃迁 | 能力平台 | |
|
||||
| 妥哒 | 数字支付 | ✓ | ✓ | | 场景化支付,安全有温度 | 交易底座 | |
|
||||
| 阿布AI | 人工智能 | ✓ | | ✓ | 多模态生活助手,温暖每一天 | AI 入口/平台 | |
|
||||
| 听愿 | 人工智能 | ✓ | | ✓ | 听懂你的每一个愿望 | 意图理解引擎 | |
|
||||
|
|
||||
> ✅ 已确认企业侧判断:4 个企业侧勾选(**职来 / 看山 / 跃升 / 妥哒**)成立,无需调整。 |
|
||||
> - **职来、妥哒** = 天然双边市场(招聘必有雇主 B 端、支付必有商户 B 端)。 |
|
||||
> - **看山**("知识基础设施")、**跃升**("能力平台/赋能跃迁")——"基础设施/平台"定位意味着既服务个体(C)也服务组织/供给方(B)。 |
|
||||
> - 纯 C(青核/音酷/蜜恋/步途)不勾企业;阿布AI/听愿归"未来/平台层",**平台化后才转 B**,当前不勾企业。 |
|
||||
> ✅ 已确认:每个产品价值主张一句话到位。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 5. 数字生活生态全景图 |
|
||||
|
|
||||
```mermaid |
|
||||
graph TB |
|
||||
POS["巴适伙伴 · 定位<br/>聚焦数字生活服务 · 温暖科技<br/>共建数字生活生态"] |
|
||||
|
|
||||
subgraph AUD["L2 受众层 · 为谁"] |
|
||||
A1["消费者 · 个体的数字生活"] |
|
||||
A2["企业 · 组织的效率与增长"] |
|
||||
A3["未来 · 前沿与平台层"] |
|
||||
end |
|
||||
|
|
||||
subgraph TRK["L3 赛道层 · 造什么(数字生活四动作)"] |
|
||||
T1["内容社交 · 连接"] |
|
||||
T2["职场服务 · 成长"] |
|
||||
T3["数字支付 · 交易"] |
|
||||
T4["人工智能 · 理解"] |
|
||||
end |
|
||||
|
|
||||
subgraph PROD["产品节点 · 连接点"] |
|
||||
P1["青核 / 音酷 / 蜜恋"] |
|
||||
P2["职来 / 看山 / 步途 / 跃升"] |
|
||||
P3["妥哒"] |
|
||||
P4["阿布AI / 听愿"] |
|
||||
end |
|
||||
|
|
||||
ESG["ESG 责任层 · 环境 / 社会 / 治理(横切贯穿)"] |
|
||||
|
|
||||
POS --> AUD |
|
||||
AUD --> TRK |
|
||||
TRK --> PROD |
|
||||
ESG -. 横切贯穿 .-> TRK |
|
||||
``` |
|
||||
|
|
||||
> 落地建议(待执行):About 页已有交互式中心辐射图 [`OrgNetwork.tsx`](src/components/OrgNetwork.tsx)。骨架既已确认,可在业务页做一张同款"生态全景图"(中心=巴适伙伴,外环=4赛道,节点=产品,受众作为切换视角),把首页3受众与业务页4赛道统一到一张图。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 6. ESG × 产品挂钩(让 Sustainability 不再空泛)—— 建议待确认 |
|
||||
|
|
||||
把 Sustainability 页的抽象行动换成与具体产品的挂钩(建议,待你确认后执行): |
|
||||
|
|
||||
| ESG 维度 | 现有抽象表述 | 产品挂钩(建议) | |
|
||||
|---|---|---| |
|
||||
| 环境 Environment | 绿色运营、降碳足迹 | 全线数字服务云化;重点阿布AI 的算力能效 | |
|
||||
| 社会 Social | 数字普惠、公益创新 | 妥哒(支付普惠)/ 看山(知识普惠)/ 步途·跃升(职业流动) | |
|
||||
| 治理 Governance | 数据合规、责任运营 | 阿布AI(AI 伦理)/ 妥哒(支付合规与数据安全) | |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 7. 边缘板块收口(骨架已确认,待执行) |
|
||||
|
|
||||
- **Innovation 页**:已重定向到 /business。建议二选一——(a) 并入 Business 作为"研发理念 ICIDP"子段彻底删路由;(b) 保留为不可访问但清掉悬空数据。当前是悬空状态。 |
|
||||
- **Sustainability 页**:按上文第 6 节把抽象行动换成产品挂钩。 |
|
||||
- **Investors 页**:初创阶段,保留 IR 联系 CTA,与业务愿景做一句呼应即可,不堆指标。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 8. 已确认的关键决策(清单) |
|
||||
|
|
||||
> 原 6 项关键推断已于 2026-08 全部拍板。以下为定稿记录。 |
|
||||
|
|
||||
1. **L1 定位措辞** ✅ 沿用:"温暖科技""数字生活生态"作为长期品牌核心词。 |
|
||||
2. **赛道四动作** ✅ 符合战略意图:连接(社交)/ 成长(职场)/ 交易(支付)/ 理解(AI)——这是"为什么是 4 条"的核心论据。 |
|
||||
3. **"未来"受众定义** ✅ 成立:定义为"前沿/平台层"而非具体客户;AI 赛道为其落点。 |
|
||||
4. **产品×受众打勾(企业侧)** ✅ 交助手判断——**确认 4 个企业侧产品**:职来 / 看山 / 跃升 / 妥哒(理由见第 4 节脚注);纯 C 产品不勾企业;阿布AI/听愿归未来/平台层。 |
|
||||
5. **每个产品价值主张一句话** ✅ 准:第 4 节"价值主张"列为定稿。 |
|
||||
6. **品牌名澄清** ✅ 无出行/交通维度:"巴适"=品牌(川渝方言"舒服/恰好");"巴士"仅见于项目文件夹名 + Mascot 车形狗玩梗,不构成业务赛道。体系图无需加入出行/交通。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 9. 下一步(骨架既定,可推进的落地工作) |
|
||||
|
|
||||
1. ✅ **首页↔业务页桥接(已完成 2026-08)**:构建交互式 [`EcosystemMap`](src/components/EcosystemMap.tsx) 组件,落在业务页 `#ecosystem` 区块。中心=巴适伙伴,双视角切换——「按赛道看」(4 赛道节点:连接/成长/交易/理解)与「按受众看」(3 受众节点:消费者/企业/未来),同一组 10 产品按已确认矩阵重组。含中心心跳、连接线能量流、节点轻浮、受众色点(C蓝/B绿/F紫)。首页 3 受众 pillars 已改为跳转 `/business#ecosystem`,直接落到全景图。浏览器实测通过(切换/节点/面板/控制台均正常)。 |
|
||||
2. **产品层信息补全**:每个产品补定位/目标用户/核心价值/生态位置(第 4 节矩阵已是骨架;EcosystemMap 已展示价值主张一句 + 受众标记 + 状态)。 |
|
||||
3. **边缘板块收口**:Innovation 二选一、Sustainability 按第 6 节挂钩产品、Investors 加一句愿景呼应。 |
|
||||
4. **品牌名落地**:代码全程统一"巴适伙伴 / BaskBuddy";文件夹名"巴士伙伴官网"属遗留,可不改(不影响品牌)。 |
|
||||
``` |
|
||||
@ -1,45 +0,0 @@ |
|||||
# bashi-website — 色彩 & 图标 最终决策落地(像素君 / UI Designer) |
|
||||
|
|
||||
用户拍板「听你的」,以下决策已直接执行并验证通过。 |
|
||||
|
|
||||
## 1. 赛道色统一(解决双套定义) |
|
||||
之前:赛道卡片用粉彩 `#E8A0BF/#7EC8E3/#98D8AA/#C9B1FF`,但全局 `--track-*` token 是饱和默认色 `#EF4444/#2563EB/#10B981/#8B5CF6` —— **色相一致,唯独「社交」一边粉、一边红**最扎眼。 |
|
||||
|
|
||||
改为:token 与卡片粉彩**同色相**,并加深以保证 WCAG AA(白底文字 ≥4.5:1;实心按钮白字 ≥4.5:1): |
|
||||
|
|
||||
| 赛道 | 旧 token | 新 token | 对齐的卡片粉彩 | |
|
||||
|------|----------|----------|----------------| |
|
||||
| 社交 Social | `#EF4444` 红 | `#C2416B` 深玫瑰 | `#E8A0BF` 粉 | |
|
||||
| 职场 Career | `#2563EB` | `#1C7AB6` 深天蓝 | `#7EC8E3` 蓝 | |
|
||||
| 支付 Payment | `#10B981` | `#15803D` 深绿 | `#98D8AA` 绿 | |
|
||||
| AI | `#8B5CF6` | `#7C3AED` 深紫 | `#C9B1FF` 紫 | |
|
||||
|
|
||||
- 顺手**删掉 4 个从未引用的 `--track-*-bright` 死 token**,设计系统更干净。 |
|
||||
|
|
||||
## 2. 图标全部统一为 lucide(单一图标源) |
|
||||
设计系统第一原则就是一致性。把所有手写内联 SVG 换成 lucide: |
|
||||
- **Header**:汉堡 ↔ 关闭 → `Menu` / `X` |
|
||||
- **Contact**:`💼`→`Briefcase`、`🤝`→`Handshake`、CTA 箭头→`ArrowRight`、邮件→`Mail` |
|
||||
- **NotFound**:返回箭头→`ArrowLeft`、前往箭头→`ArrowRight` |
|
||||
- **Join**:投递区残留的邮件 SVG → `Mail` |
|
||||
|
|
||||
全站**已无任何 emoji**(上次 Join 福利区已改,本次清掉 Contact 两处 + Join 邮件图标)。 |
|
||||
|
|
||||
## 3. SPA fallback(生产环境深链接不 404) |
|
||||
新增 `public/_redirects`:`/* /index.html 200`(Netlify 及多数静态托管通用,对其它平台无害),已随构建进入 `dist/`。 |
|
||||
|
|
||||
> 若用其它托管,规则如下: |
|
||||
> - **nginx**:`try_files $uri $uri/ /index.html;` |
|
||||
> - **Vercel**:`vercel.json` → `{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }` |
|
||||
> - **GitHub Pages**:需用 `gh-pages` + 404.html 拷贝,或 hash router |
|
||||
|
|
||||
## 验证 |
|
||||
- `npx tsc --noEmit` → 0 错误 |
|
||||
- `npm run build` → 1819 模块,新色已进入 `dist` CSS,`_redirects` 已进 `dist` |
|
||||
- `src/` 内 **0 emoji** |
|
||||
- 开发服务器在线 **http://localhost:4321**:`/` `/business` `/contact` `/join` `/en/contact` `/404` 全部 200,改动模块编译干净,lucide 依赖解析 200 |
|
||||
|
|
||||
## 仍可继续打磨(可选,未动) |
|
||||
- 字体层级/行高 token 化(目前散落在 class 里) |
|
||||
- 暗色主题目前由 `page-enter`/CSS 变量驱动,可补一套更完整的深色 token 校验 |
|
||||
- 赛道卡片粉彩与全局 token 仍有「深浅两档」的语义区分,若想彻底单源,可把卡片色也改从 `--track-*` 派生(需动 i18n 数据) |
|
||||
@ -1,38 +0,0 @@ |
|||||
# bashi-website — 设计系统完善 + 暗色模式落地 |
|
||||
|
|
||||
> 像素君(UI Designer)· 第二轮打磨:把"死"的暗色 token 真正激活,并补全设计系统刻度尺 + 赛道色单源。 |
|
||||
|
|
||||
## 1. 设计令牌补全(`global.css`) |
|
||||
- **字体刻度尺**:在 `:root` 新增模块化 token —— `--fs-xs…4xl`、`--lh-tight/snug/normal/relaxed`、`--fw-regular…bold`、`--ls-tight…widest`。设计系统从此有统一"刻度尺",组件可逐步迁移到令牌而非散值。 |
|
||||
- **主题感知 surface 令牌**:`--surface-card` / `--text-strong` / `--header-bg` / `--accent`,浅色下指向既有值,暗色下自动切换。 |
|
||||
|
|
||||
## 2. 暗色模式(从摆设变成真功能) |
|
||||
之前 `--bg-dark` / `--text-on-dark` / `--shadow-dark-*` 都定义了,却**没有 `data-theme="dark"` 覆盖块、也没有切换开关**——等于死代码。本次: |
|
||||
- 新增 `[data-theme="dark"]` 覆盖块:暖调近黑表面(`--bg-primary:#16110E`、`--surface-card:#1E1813`)、暖白文字(`--text-primary:#F5EFE6`、`--text-strong:#FBF6EC`)、低透明度奶油描边、暗色阴影、`color-scheme: dark`。 |
|
||||
- `body` 加 `background-color/color` 依赖令牌,整页随主题切换。 |
|
||||
- **三态主题开关**:`src/hooks/useTheme.ts`(浅 / 深 / 跟随系统,localStorage 持久化 + 系统偏好实时监听)+ Header 顶栏按钮循环切换 `Sun / Moon / Monitor`(lucide 图标)。 |
|
||||
- `index.html` 内联防闪烁脚本:首屏绘制前就设好 `data-theme` 与 `color-scheme`,**无 FOUC**。 |
|
||||
|
|
||||
## 3. 组件迁移到主题令牌 |
|
||||
- 中性/深色底上的 `text-[var(--brand-black)]` → `text-[var(--text-strong)]`(覆盖 Home/Business/Innovation/Join/Contact/About/Footer/Privacy/NotFound/Header 的全部标题与导航文字)。 |
|
||||
- `bg-white` 卡片 → `bg-[var(--surface-card)]`(Contact/Join/NotFound/Privacy)。 |
|
||||
- 悬停底色 `hover:bg-[var(--brand-cream-light)]` → `hover:bg-[var(--accent)]`;静态奶油色高亮块(About/Join/Innovation 序号徽章、Contact Passion 卡)→ `bg-[var(--accent)]`。 |
|
||||
- 英雄区/区块渐变遮罩:白底遮罩改为 `color-mix(in srgb, var(--bg-primary) NN%, transparent)`,奶油渐变改为 `var(--bg-secondary)`,均随主题淡入背景。 |
|
||||
- Header 吸顶与移动菜单底色 → `var(--header-bg)`。 |
|
||||
- **关键边界**:奶油实心按钮(Home 联系 CTA、Business CTA)保留 `text-[var(--brand-black)]`——浅奶油底配深字在两种主题都可读,若误改成浅字会在暗色下"浅字叠浅底"翻车(已修复一处该回归)。 |
|
||||
|
|
||||
## 4. 赛道色单源(`--track-*` 成为唯一来源) |
|
||||
- 新增 `src/lib/tracks.ts`:`TRACK_TOKEN` 把 social/career/payment/ai 映射到 `var(--track-*)`。 |
|
||||
- `i18n` 的 `business.tracks[]` 加 `key` 字段、**删除**原先写死的粉彩 `color` 字段(即那套双源粉彩)。Home/Business 改用 `TRACK_TOKEN[track.key]`,卡片底色(`color-mix` 深色调)与圆点 accent 现在共享同一 token,永不漂移。 |
|
||||
|
|
||||
## 5. Footer 清理 |
|
||||
- 残留 `📍` emoji → lucide `MapPin`;硬编码 `rgba(26,26,26,0.40)` 的标语文字 → `var(--text-muted)`(暗色下原本会不可见)。 |
|
||||
|
|
||||
## 验证 |
|
||||
- `npx tsc --noEmit` → 0 错误;`npm run build` → 1821 模块;`[data-theme=dark]` 块与 `#16110e` 已进入 `dist` CSS;**全站 0 emoji**;所有路由 200;改动模块编译干净;主题接线(`baskbuddy-theme`、`data-theme`)在产物 JS 中可查。 |
|
||||
- 说明:暗色**视觉**正确性经逐项推演确认(无 headless 浏览器截图);请点击顶栏 Sun/Moon/Monitor 按钮实际体验切换。 |
|
||||
|
|
||||
## 仍可继续打磨的方向(可选) |
|
||||
- 把组件里的 `text-[15px]` 等散值逐步替换成本轮新增的 `--fs-*` 刻度(目前令牌已就位,属渐进式重构)。 |
|
||||
- 补一套更完整的暗色对比度走查清单(针对 `.gradient-mesh` / `.text-gradient-warm` 等渐变类在暗色下的表现)。 |
|
||||
- 部署级:确认托管平台的 SPA fallback(已有 `public/_redirects`,nginx/Vercel 规则可补)。 |
|
||||
@ -1,47 +0,0 @@ |
|||||
# 色彩 & 图标审计与修复 — bashi-website |
|
||||
|
|
||||
> 角色:像素君(UI Designer)|审计依据:Impeccable + UI/UX Pro Max 设计规范 |
|
||||
|
|
||||
## 已修复(明确违规 / 无障碍硬伤) |
|
||||
|
|
||||
### 1. 图标:Emoji → 真实 SVG |
|
||||
- **Join.tsx 福利区** 原用 emoji `⏰🏠💪🎉📚`,违反「禁止 emoji 作图标」规范。 |
|
||||
- 安装 **lucide-react 1.25.0**,替换为 `Clock / Home / Dumbbell / PartyPopper / BookOpen`,置于 `#brand-cream-light` 圆角方块中,与全站图标语言一致。 |
|
||||
|
|
||||
### 2. 图标:唤醒沉睡的 `icon` 数据 |
|
||||
- `i18n/{zh,en}.ts` 中每个产品都带 `icon: 'heart'|'music'|…` 字段,但**从未渲染**。 |
|
||||
- `Business.tsx` 新增 `productIcon` 映射,给每个产品芯片渲染真实 lucide 图标 |
|
||||
(heart/music/sparkles/briefcase/mountain/footprints/rocket/check-circle→CircleCheck/bot/ear)。 |
|
||||
|
|
||||
### 3. 色彩:状态徽章对比度硬伤(WCAG AA 失败) |
|
||||
- `.status-developing` 原为 **奶油色文字 `#F5DEB3` 叠在近透明奶油底** → 白底上约 **1.3:1**,文字几乎不可见。 |
|
||||
- 修复:`color-mix(... cream-deep 20% + bg-primary)` 底 + `#8A6A2F` 文字(≈ **4.9:1**,通过)。 |
|
||||
- `.status-planned` 文字加深到 `#1D4ED8`,留足 AA 余量。 |
|
||||
|
|
||||
### 4. 色彩:暖白微调 |
|
||||
- `--brand-white` 由 `#FFFFFF` → `#FCFBF8`,与奶油暖调主题更协调(纯白在「温暖未来主义」里偏冷)。 |
|
||||
|
|
||||
## 验证 |
|
||||
- `npx tsc --noEmit` → 0 错误 |
|
||||
- `npm run build` → 1819 模块(lucide 已打包) |
|
||||
- 开发服务器 `/business`、`/join` 模块编译通过,全站页面 200 |
|
||||
|
|
||||
## 待你拍板(未改,属品牌级决策) |
|
||||
|
|
||||
### A. 赛道色存在「双套定义」不一致 |
|
||||
| 位置 | 社交 | 职场 | 支付 | AI | |
|
||||
|------|------|------|------|-----| |
|
||||
| 赛道卡片(Home/Business/Innovation) | `#E8A0BF` 粉 | `#7EC8E3` 青 | `#98D8AA` 薄荷 | `#C9B1FF` 薰衣草 | |
|
||||
| 全局强调 token `--track-*` | `#EF4444` 红 | `#2563EB` 蓝 | `#10B981` 翠 | `#8B5CF6` 紫 | |
|
||||
|
|
||||
- 卡片用的是柔和粉彩,强调 token 是 Tailwind 默认饱和色,**色相不一致**(尤其「社交」= 粉 vs 红)。 |
|
||||
- **建议**:统一为一套色相(例如让全局强调 token 与粉彩共享色相,仅提高饱和度用于小面积强调)。 |
|
||||
- 需要你确认方向后我再改——这是品牌视觉基调,不想擅自重绘。 |
|
||||
|
|
||||
### B. 手写内联 SVG |
|
||||
- `Header / Contact / NotFound` 用的是手写 24×24 描边 SVG,风格一致、无问题;可选统一换成 lucide 做单一图标源。 |
|
||||
|
|
||||
## 你需要告诉我的 |
|
||||
1. 赛道色要不要统一?统一到「粉彩色相」还是维持饱和色相? |
|
||||
2. 是否要把手写 SVG 也换成 lucide(单一图标源)? |
|
||||
3. 是否需要我补充一个 **SPA fallback** 部署配置(nginx / vercel / netlify),否则生产环境深链接 404。 |
|
||||
@ -1,25 +0,0 @@ |
|||||
# bashi-website — Migrated to Pure React (Vite + React Router) |
|
||||
|
|
||||
The site was migrated from **Astro 5** to a **pure React SPA** (Vite + React Router + Tailwind v4), so every page route is now a real `.tsx` file as requested. |
|
||||
|
|
||||
## What changed |
|
||||
- **Removed**: all `.astro` files (`src/pages/*.astro`, `src/pages/en/*.astro`, `src/layouts/BaseLayout.astro`), `astro.config.mjs`, and `scripts/`. Zero Astro dependencies remain in `package.json`. |
|
||||
- **Added**: Vite + React Router toolchain, `src/main.tsx`, `src/App.tsx` (routes), `src/layout/RootLayout.tsx`, `src/hooks/useClientEffects.ts`, `src/seo/Seo.tsx`, and 8 `src/pages/*.tsx` route wrappers. |
|
||||
- **Reused unchanged**: `src/styles/global.css` (full BaskBuddy design system), all existing React components (`Home/About/Business/Contact/Innovation/Join/Privacy/NotFound/Header/Footer/SiteShell`), and the i18n catalogs (`zh.ts`/`en.ts`). |
|
||||
|
|
||||
## Architecture |
|
||||
- `zh` at `/` & `/{page}`, `en` at `/en/{page}`, unknown paths → 404. |
|
||||
- `RootLayout` keeps `Header`/`Footer` mounted across navigations and replays the `page-enter` animation on routed content. |
|
||||
- `useClientEffects(pathname)` ports the interaction logic that previously lived in Astro's `<script>`: **flip-cards** (About ICIDP), **scroll-reveal** (staggered), **stat counters** (`.stat-value[data-target]`), and **copy-to-clipboard** (Contact). The dead `#star-field` generation was dropped. |
|
||||
- `Seo` recreates the per-page `<head>` (title, description, canonical, hreflang alternates, OG/Twitter cards, Organization JSON-LD) via direct DOM updates — faithful to the original Astro output. |
|
||||
- `Header` mobile menu now uses React `useState` instead of the old id-based global script toggle. |
|
||||
|
|
||||
## Verification |
|
||||
- `npm run build` → 60 modules transformed, clean `dist/`. |
|
||||
- `npx tsc --noEmit` → 0 type errors. |
|
||||
- Dev server live at **http://localhost:4321** — `/`, `/about`, `/en/innovation`, `/en`, `/privacy` and all assets return HTTP 200 (SPA fallback works). |
|
||||
|
|
||||
## Notes / tradeoffs |
|
||||
- **SPA SEO**: meta is set client-side (JS-executing crawlers are fine). For production, configure the host with an SPA fallback (Vite dev/preview already do this). |
|
||||
- A sandbox safe-delete guard blocks Vite from bulk-clearing `node_modules/.vite/deps`; resolved by redirecting Vite's `cacheDir` to the OS temp dir in `vite.config.ts`. |
|
||||
- Run: `npm run dev` (4321) · `npm run build` · `npm run preview`. |
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 38 KiB |
@ -1,159 +1,297 @@ |
|||||
import { getLocalizedPath, type Locale } from '../i18n/utils'; |
import { getLocalizedPath, type Locale } from '../i18n/utils'; |
||||
import { getTranslation } from '../i18n/utils'; |
import { getTranslation } from '../i18n/utils'; |
||||
import { Icon, trackIcons } from '../icons'; |
import { Icon, trackIcons } from '../icons'; |
||||
|
import { useState, useEffect } from 'react'; |
||||
|
|
||||
export default function Footer({ locale }: { locale: Locale }) { |
export default function Footer({ locale }: { locale: Locale }) { |
||||
const t = getTranslation(locale); |
const t = getTranslation(locale); |
||||
|
const f = t.footer; |
||||
const brandName = t.brand || 'BaskBuddy'; |
const brandName = t.brand || 'BaskBuddy'; |
||||
// ICP 备案正式编号(2026-07-28 工信部审核通过)
|
// ICP 备案正式编号(2026-07-28 工信部审核通过)
|
||||
const icpNumber = '沪ICP备2026035550号'; |
const icpNumber = '沪ICP备2026035550号-2'; |
||||
// 公安备案数据码(阿里云已下发,有效期至 2026-08-27,审核完成后替换为正式沪公网安备号)
|
// 公安备案数据码(阿里云已下发,有效期至 2026-08-27,审核完成后替换为正式沪公网安备号)
|
||||
const psbFilingCode = 'c7c31b218d433ab92abbf85ec20af2bd'; |
const psbFilingCode = 'c7c31b218d433ab92abbf85ec20af2bd'; |
||||
|
|
||||
|
// 邮箱复制状态
|
||||
|
const [copied, setCopied] = useState(false); |
||||
|
useEffect(() => { |
||||
|
if (!copied) return; |
||||
|
const timer = setTimeout(() => setCopied(false), 2000); |
||||
|
return () => clearTimeout(timer); |
||||
|
}, [copied]); |
||||
|
|
||||
|
const handleCopyEmail = async () => { |
||||
|
try { |
||||
|
await navigator.clipboard.writeText(f.contact.email); |
||||
|
setCopied(true); |
||||
|
} catch (err) { |
||||
|
// Fallback: create hidden textarea
|
||||
|
const ta = document.createElement('textarea'); |
||||
|
ta.value = f.contact.email; |
||||
|
ta.style.position = 'fixed'; |
||||
|
ta.style.opacity = '0'; |
||||
|
document.body.appendChild(ta); |
||||
|
ta.select(); |
||||
|
try { |
||||
|
document.execCommand('copy'); |
||||
|
setCopied(true); |
||||
|
} catch (e) { |
||||
|
// ignore
|
||||
|
} |
||||
|
document.body.removeChild(ta); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
// 三语辅助:英文用 en 文本,繁体用 hk 文本,简体用 zh 文本。
|
// 三语辅助:英文用 en 文本,繁体用 hk 文本,简体用 zh 文本。
|
||||
const zhText = (zh: string, hk: string, en: string) => |
const zhText = (zh: string, hk: string, en: string) => |
||||
locale === 'en' ? en : locale === 'hk' ? hk : zh; |
locale === 'en' ? en : locale === 'hk' ? hk : zh; |
||||
|
|
||||
return ( |
return ( |
||||
<footer className="bg-[var(--brand-black)] border-t border-[color-mix(in_oklch,var(--brand-white)_10%,transparent)]"> |
<footer className="bg-[var(--brand-black)] border-t border-[color-mix(in_oklch,var(--brand-white)_10%,transparent)]"> |
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16"> |
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-14 md:py-20"> |
||||
{/* Main Footer Grid */} |
{/* ========== Main Footer: 4-Column Grid ========== */} |
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8 md:gap-12 mb-10 md:mb-14"> |
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-12 gap-10 lg:gap-8 mb-12 lg:mb-16"> |
||||
{/* Column 1: Brand */} |
{/* ===== Column 1: Brand (4 cols on desktop) ===== */} |
||||
<div className="md:col-span-1"> |
<div className="lg:col-span-4"> |
||||
<div className="flex items-center gap-3 mb-4"> |
{/* Logo + Brand */} |
||||
|
<div className="flex items-center gap-3 mb-6"> |
||||
<img |
<img |
||||
src="/baskbuddy-logo.png" |
src="https://oss.baskbuddy.com/baskbuddy/baskbuddy-logo.png" |
||||
alt="BaskBuddy" |
alt="BaskBuddy" |
||||
className="w-12 h-12 object-contain rounded-xl bg-white" |
className="w-12 h-12 object-contain rounded-xl bg-white" |
||||
/> |
/> |
||||
<span className="font-semibold text-body text-[var(--brand)]" style={{ fontFamily: 'var(--font-body)' }}> |
<div className="flex flex-col"> |
||||
|
<span className="font-semibold text-body text-[var(--brand)] leading-tight" style={{ fontFamily: 'var(--font-body)' }}> |
||||
{brandName} |
{brandName} |
||||
</span> |
</span> |
||||
|
<span className="text-micro text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)] mt-0.5" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{t.brandSub || '巴适伙伴'} |
||||
|
</span> |
||||
</div> |
</div> |
||||
<p className="text-body leading-relaxed mb-4 text-[color-mix(in_oklch,var(--brand-white)_60%,transparent)]"> |
</div> |
||||
{zhText( |
|
||||
'以温暖科技,连接美好生活。五大赛道全面覆盖数字生活服务生态。', |
{/* Slogan */} |
||||
'以溫暖科技,連接美好生活。五大賽道全面覆蓋數位生活服務生態。', |
<h3 className="text-[28px] md:text-[32px] font-bold leading-tight mb-3 text-[var(--brand-white)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
'Connecting beautiful life with warm tech. Five tracks covering the digital life service ecosystem.', |
{f.slogan} |
||||
)} |
</h3> |
||||
|
<p className="text-body leading-relaxed mb-6 text-[color-mix(in_oklch,var(--brand-white)_65%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{f.subtitle} |
||||
</p> |
</p> |
||||
<div className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-micro font-semibold tracking-[0.06em] uppercase bg-[color-mix(in_oklch,var(--brand)_12%,transparent)] text-[color-mix(in_oklch,var(--brand)_92%,transparent)] border border-[color-mix(in_oklch,var(--brand)_22%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
|
||||
|
{/* ICIDP Culture Badge */} |
||||
|
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-xl bg-[color-mix(in_oklch,var(--brand)_10%,transparent)] border border-[color-mix(in_oklch,var(--brand)_20%,transparent)]"> |
||||
|
<span className="text-micro font-bold tracking-[0.12em] text-[var(--brand)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
I·C·I·D·P |
I·C·I·D·P |
||||
|
</span> |
||||
|
<span className="w-px h-4 bg-[color-mix(in_oklch,var(--brand)_30%,transparent)]" /> |
||||
|
<span className="text-micro tracking-wide text-[color-mix(in_oklch,var(--brand-white)_55%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{zhText('诚信·勇敢·创新·奉献·激情', '誠信·勇敢·創新·奉獻·激情', 'Integrity · Courage · Innovation · Devotion · Passion')} |
||||
|
</span> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
{/* Column 2: Quick Links */} |
{/* ===== Column 2: Business Tracks (3 cols) ===== */} |
||||
<div> |
<div className="lg:col-span-3"> |
||||
<h4 className="text-micro font-semibold tracking-[0.08em] uppercase mb-4 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
<h4 className="text-micro font-bold tracking-[0.1em] uppercase mb-5 text-[color-mix(in_oklch,var(--brand-white)_45%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
{zhText('快速导航', '快速導航', 'QUICK LINKS')} |
{f.columns.tracks} |
||||
</h4> |
</h4> |
||||
<div className="flex flex-col gap-3.5 leading-relaxed"> |
<div className="grid grid-cols-2 gap-x-3 gap-y-3"> |
||||
{Object.entries(t.nav).map(([key, label]) => ( |
{Object.entries(t.home.tracks).map(([key, track]) => ( |
||||
<a |
<a |
||||
key={key} |
key={key} |
||||
href={getLocalizedPath(`/${key === 'home' ? '' : key}`, locale)} |
href={getLocalizedPath('/business', locale)} |
||||
className="inline-flex items-center text-body transition-colors duration-200 text-[color-mix(in_oklch,var(--brand-white)_65%,transparent)] hover:text-[var(--brand)]" |
className="group flex items-center gap-2.5 px-3 py-2.5 rounded-xl transition-all duration-200 hover:bg-[color-mix(in_oklch,var(--brand)_10%,transparent)] border border-transparent hover:border-[color-mix(in_oklch,var(--brand)_20%,transparent)]" |
||||
|
title={track.desc} |
||||
> |
> |
||||
{label} |
<span className="flex items-center justify-center w-8 h-8 rounded-lg bg-[color-mix(in_oklch,var(--brand)_12%,transparent)] text-[var(--brand)] group-hover:bg-[var(--brand-accent)] group-hover:text-white transition-colors duration-200 shrink-0"> |
||||
|
<Icon name={trackIcons[key]} className="w-4 h-4" /> |
||||
|
</span> |
||||
|
<span className="text-body font-medium text-[color-mix(in_oklch,var(--brand-white)_75%,transparent)] group-hover:text-[var(--brand-white)] transition-colors duration-200" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{track.name} |
||||
|
</span> |
||||
</a> |
</a> |
||||
))} |
))} |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
{/* Column 3: Tracks */} |
{/* ===== Column 3: Quick Navigate (2 cols) ===== */} |
||||
<div> |
<div className="lg:col-span-2"> |
||||
<h4 className="text-micro font-semibold tracking-[0.08em] uppercase mb-4 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
<h4 className="text-micro font-bold tracking-[0.1em] uppercase mb-5 text-[color-mix(in_oklch,var(--brand-white)_45%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
{zhText('业务赛道', '業務賽道', 'BUSINESS TRACKS')} |
{f.columns.navigate} |
||||
</h4> |
</h4> |
||||
<div className="flex flex-col gap-3.5 leading-relaxed"> |
<ul className="flex flex-col gap-3"> |
||||
{Object.entries(t.home.tracks).map(([key, track]) => ( |
{Object.entries(t.nav).map(([key, label]) => ( |
||||
<a key={key} href="/business" className="inline-flex items-center gap-2 text-body transition-colors duration-200 text-[color-mix(in_oklch,var(--brand-white)_65%,transparent)] hover:text-[var(--brand)]"> |
<li key={key}> |
||||
<Icon name={trackIcons[key]} className="w-4 h-4" /> |
<a |
||||
{track.name} |
href={getLocalizedPath(`/${key === 'home' ? '' : key}`, locale)} |
||||
|
className="inline-flex items-center text-body transition-colors duration-200 text-[color-mix(in_oklch,var(--brand-white)_70%,transparent)] hover:text-[var(--brand)] hover:pl-1" |
||||
|
style={{ fontFamily: 'var(--font-body)' }} |
||||
|
> |
||||
|
{label} |
||||
</a> |
</a> |
||||
|
</li> |
||||
))} |
))} |
||||
</div> |
</ul> |
||||
</div> |
</div> |
||||
|
|
||||
{/* Column 4: Contact */} |
{/* ===== Column 4: Contact (3 cols) ===== */} |
||||
<div> |
<div className="lg:col-span-3"> |
||||
<h4 className="text-micro font-semibold tracking-[0.08em] uppercase mb-4 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
<h4 className="text-micro font-bold tracking-[0.1em] uppercase mb-5 text-[color-mix(in_oklch,var(--brand-white)_45%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
{zhText('联系我们', '聯繫我們', 'CONTACT')} |
{f.columns.contact} |
||||
</h4> |
</h4> |
||||
<div className="flex flex-col gap-3.5 leading-relaxed"> |
<div className="flex flex-col gap-4"> |
||||
<a href="/contact" className="text-body transition-colors duration-200 text-[color-mix(in_oklch,var(--brand-white)_65%,transparent)] hover:text-[var(--brand)]"> |
{/* Careers */} |
||||
{zhText('求职咨询', '求職諮詢', 'Job Inquiry')} |
<a href={getLocalizedPath('/contact', locale)} className="group block p-4 rounded-xl bg-[color-mix(in_oklch,var(--brand-white)_4%,transparent)] border border-[color-mix(in_oklch,var(--brand-white)_8%,transparent)] hover:border-[color-mix(in_oklch,var(--brand)_30%,transparent)] transition-all duration-200"> |
||||
|
<div className="flex items-center justify-between mb-1"> |
||||
|
<span className="text-body font-semibold text-[var(--brand-white)] group-hover:text-[var(--brand)] transition-colors duration-200" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{f.contact.jobs.label} |
||||
|
</span> |
||||
|
<Icon name="arrowUpRight" className="w-4 h-4 text-[color-mix(in_oklch,var(--brand-white)_40%,transparent)] group-hover:text-[var(--brand)] transition-colors duration-200" /> |
||||
|
</div> |
||||
|
<p className="text-micro text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{f.contact.jobs.hint} |
||||
|
</p> |
||||
</a> |
</a> |
||||
<a href="/contact" className="text-body transition-colors duration-200 text-[color-mix(in_oklch,var(--brand-white)_65%,transparent)] hover:text-[var(--brand)]"> |
|
||||
{zhText('商务合作', '商務合作', 'Business Cooperation')} |
{/* Business */} |
||||
|
<a href={getLocalizedPath('/contact', locale)} className="group block p-4 rounded-xl bg-[color-mix(in_oklch,var(--brand-white)_4%,transparent)] border border-[color-mix(in_oklch,var(--brand-white)_8%,transparent)] hover:border-[color-mix(in_oklch,var(--brand)_30%,transparent)] transition-all duration-200"> |
||||
|
<div className="flex items-center justify-between mb-1"> |
||||
|
<span className="text-body font-semibold text-[var(--brand-white)] group-hover:text-[var(--brand)] transition-colors duration-200" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{f.contact.biz.label} |
||||
|
</span> |
||||
|
<Icon name="arrowUpRight" className="w-4 h-4 text-[color-mix(in_oklch,var(--brand-white)_40%,transparent)] group-hover:text-[var(--brand)] transition-colors duration-200" /> |
||||
|
</div> |
||||
|
<p className="text-micro text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{f.contact.biz.hint} |
||||
|
</p> |
||||
</a> |
</a> |
||||
|
|
||||
|
{/* Email: Click to mail, text selectable, 1-click copy with feedback */} |
||||
|
<div className="pt-1"> |
||||
|
<p className="text-micro text-[color-mix(in_oklch,var(--brand-white)_45%,transparent)] mb-2" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
{zhText('统一联系邮箱(求职 / 商务)', '統一聯繫郵箱(求職 / 商務)', 'Unified Contact Email')} |
||||
|
</p> |
||||
|
<div className="group inline-flex items-center gap-1 px-3 py-2 rounded-xl bg-[color-mix(in_oklch,var(--brand-white)_4%,transparent)] border border-[color-mix(in_oklch,var(--brand-white)_8%,transparent)] hover:border-[color-mix(in_oklch,var(--brand)_30%,transparent)] transition-all duration-200 w-full"> |
||||
|
<Icon name="mail" className="w-4 h-4 shrink-0 text-[var(--brand)]" /> |
||||
<a |
<a |
||||
href="mailto:baskbuddy@163.com" |
href={`mailto:${f.contact.email}`} |
||||
className="text-body font-medium transition-all duration-200 hover:underline underline-offset-2 text-[var(--brand)] hover:text-[var(--brand-white)]" |
className="text-body font-medium text-[var(--brand-white)] hover:text-[var(--brand)] transition-colors duration-200 select-all outline-none" |
||||
style={{ fontFamily: 'var(--font-body)' }} |
style={{ fontFamily: 'var(--font-body)', userSelect: 'all' }} |
||||
title={zhText('点击发送邮件 (求职)', '點擊發送郵件 (求職)', 'Click to send email (Jobs)')} |
onClick={(e) => e.stopPropagation()} |
||||
|
title={zhText('点击发邮件 · 文本可直接选中复制', '點擊發郵件 · 文本可直接選中複製', 'Click to email · Text is selectable')} |
||||
> |
> |
||||
baskbuddy@163.com |
{f.contact.email} |
||||
</a> |
</a> |
||||
<a |
<div className="ml-auto relative"> |
||||
href="mailto:baskbuddy@163.com" |
<button |
||||
className="text-micro transition-all duration-200 hover:underline underline-offset-2 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)] hover:text-[color-mix(in_oklch,var(--brand-white)_85%,transparent)]" |
type="button" |
||||
|
onClick={handleCopyEmail} |
||||
|
className="flex items-center justify-center w-8 h-8 rounded-lg transition-all duration-200 text-[color-mix(in_oklch,var(--brand-white)_55%,transparent)] hover:bg-[var(--brand-accent)] hover:text-white focus:outline-none focus:ring-2 focus:ring-[var(--brand-accent)]/50" |
||||
|
title={copied |
||||
|
? zhText('已复制!', '已複製!', 'Copied!') |
||||
|
: zhText('一键复制邮箱', '一鍵複製郵箱', 'Copy email')} |
||||
|
aria-label={zhText('复制邮箱地址', '複製郵箱地址', 'Copy email address')} |
||||
|
> |
||||
|
<Icon |
||||
|
name={copied ? 'check' : 'copy'} |
||||
|
className={`w-[18px] h-[18px] transition-all duration-200 ${copied ? 'text-[var(--sus-green)] scale-110' : ''}`} |
||||
|
/> |
||||
|
</button> |
||||
|
{/* Tooltip */} |
||||
|
<span |
||||
|
className={`absolute -top-10 left-1/2 -translate-x-1/2 px-2.5 py-1 rounded-lg text-micro whitespace-nowrap transition-all duration-200 pointer-events-none ${ |
||||
|
copied |
||||
|
? 'opacity-100 -translate-y-1 bg-[var(--sus-green)] text-white shadow-lg' |
||||
|
: 'opacity-0 translate-y-1 bg-[color-mix(in_oklch,var(--brand-white)_92%,var(--brand-black))] text-[var(--brand-black)] group-hover:opacity-100 group-hover:-translate-y-1' |
||||
|
}`}
|
||||
style={{ fontFamily: 'var(--font-body)' }} |
style={{ fontFamily: 'var(--font-body)' }} |
||||
title={zhText('点击发送邮件 (商务)', '點擊發送郵件 (商務)', 'Click to send email (Business)')} |
|
||||
> |
> |
||||
baskbuddy@163.com |
{copied |
||||
</a> |
? zhText('✓ 已复制', '✓ 已複製', '✓ Copied') |
||||
<p className="flex items-start gap-1 text-micro text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)]" title={zhText('巴适伙伴(上海)科技有限公司 办公地址', '巴適夥伴(上海)科技有限公司 辦公地址', 'BaskBuddy (Shanghai) Office')}> |
: zhText('点击复制', '點擊複製', 'Click to copy')} |
||||
<Icon name="mapPin" className="w-3.5 h-3.5 shrink-0 mt-0.5" /> |
</span> |
||||
<span>{zhText('中国 · 上海市静安区康宁路288弄2号410室', '中國 · 上海市靜安區康寧路288弄2號410室', "Room 410, No.2, Lane 288, Kangning Rd, Jing'an District, Shanghai, China")}</span> |
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{/* Address */} |
||||
|
<p className="flex items-start gap-2 text-body pt-1 text-[color-mix(in_oklch,var(--brand-white)_55%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
|
<Icon name="mapPin" className="w-4 h-4 shrink-0 mt-0.5" /> |
||||
|
<span className="leading-relaxed">{f.contact.address}</span> |
||||
</p> |
</p> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
{/* Bottom Bar */} |
{/* ========== Bottom Bar ========== */} |
||||
<div className="border-t border-[color-mix(in_oklch,var(--brand-white)_10%,transparent)] pt-6 flex flex-col md:flex-row items-center justify-between gap-5"> |
<div className="border-t border-[color-mix(in_oklch,var(--brand-white)_10%,transparent)] pt-6 md:pt-7"> |
||||
<div className="flex flex-col md:flex-row items-center gap-3 md:gap-4 text-center md:text-left"> |
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6"> |
||||
<p className="text-micro text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)]"> |
{/* Left: Copyright */} |
||||
{zhText( |
<div className="flex flex-col md:flex-row md:items-center gap-3 md:gap-4"> |
||||
`© 2026 巴适伙伴(上海)科技有限公司. 保留所有权利`, |
<p className="text-micro text-[color-mix(in_oklch,var(--brand-white)_55%,transparent)]" style={{ fontFamily: 'var(--font-body)' }}> |
||||
`© 2026 巴適夥伴(上海)科技有限公司. 保留所有權利`, |
{f.copyright} |
||||
`© 2026 BaskBuddy (Shanghai) Technology Co., Ltd. All rights reserved.`, |
</p> |
||||
)} |
<span className="hidden md:inline w-px h-4 bg-[color-mix(in_oklch,var(--brand-white)_15%,transparent)]" aria-hidden="true" /> |
||||
|
<p className="text-micro font-semibold tracking-[0.08em] uppercase text-[color-mix(in_oklch,var(--brand-white)_35%,transparent)] md:hidden" style={{ fontFamily: 'var(--font-body)' }} aria-hidden="true"> |
||||
|
{f.icidpFull} |
||||
</p> |
</p> |
||||
|
</div> |
||||
|
|
||||
|
{/* Center: Legal Links */} |
||||
|
<div className="flex flex-wrap items-center justify-start md:justify-center gap-x-3 gap-y-2"> |
||||
|
{/* ICP */} |
||||
<a |
<a |
||||
href="https://beian.miit.gov.cn/" |
href="https://beian.miit.gov.cn/" |
||||
target="_blank" |
target="_blank" |
||||
rel="noopener noreferrer" |
rel="noopener noreferrer" |
||||
className="text-micro transition-all duration-200 hover:underline underline-offset-2 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)] hover:text-[var(--brand)]" |
className="group inline-flex items-center text-micro transition-all duration-200 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)] hover:text-[var(--brand)]" |
||||
title={zhText('工信部ICP/IP地址/域名信息备案管理系统', '工信部ICP/IP地址/域名信息備案管理系統', 'MIIT ICP Filing Query (China)')} |
title={zhText('工信部ICP/IP地址/域名信息备案管理系统', '工信部ICP/IP地址/域名信息備案管理系統', 'MIIT ICP Filing Query (China)')} |
||||
|
style={{ fontFamily: 'var(--font-body)' }} |
||||
> |
> |
||||
|
<Icon name="shieldCheck" className="w-3.5 h-3.5 mr-1.5 opacity-70 group-hover:opacity-100" /> |
||||
{icpNumber} |
{icpNumber} |
||||
</a> |
</a> |
||||
|
|
||||
|
<span className="text-[color-mix(in_oklch,var(--brand-white)_18%,transparent)]" aria-hidden="true">·</span> |
||||
|
|
||||
|
{/* PSB */} |
||||
<a |
<a |
||||
href="https://beian.mps.gov.cn/" |
href="https://beian.mps.gov.cn/" |
||||
target="_blank" |
target="_blank" |
||||
rel="noopener noreferrer" |
rel="noopener noreferrer" |
||||
className="text-micro transition-all duration-200 hover:underline underline-offset-2 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)] hover:text-[var(--brand)] inline-flex items-center gap-1" |
className="group inline-flex items-center text-micro transition-all duration-200 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)] hover:text-[var(--brand)]" |
||||
title={zhText('全国互联网安全管理服务平台 — 公安备案', '全國互聯網安全管理服務平台 — 公安備案', 'MPS Public Security Internet Filing (China)')} |
title={zhText('全国互联网安全管理服务平台 — 公安备案', '全國互聯網安全管理服務平台 — 公安備案', 'MPS Public Security Internet Filing (China)')} |
||||
|
style={{ fontFamily: 'var(--font-body)' }} |
||||
> |
> |
||||
<Icon name="shieldCheck" className="w-3.5 h-3.5" /> |
<Icon name="shieldCheck" className="w-3.5 h-3.5 mr-1.5 opacity-70 group-hover:opacity-100" /> |
||||
{zhText( |
{zhText( |
||||
`公安备案数据码 ${psbFilingCode}`, |
`公安备案数据码 ${psbFilingCode}`, |
||||
`公安備案數據碼 ${psbFilingCode}`, |
`公安備案數據碼 ${psbFilingCode}`, |
||||
`PSB Filing Code ${psbFilingCode}`, |
`PSB Filing Code ${psbFilingCode}`, |
||||
)} |
)} |
||||
</a> |
</a> |
||||
<a href="/privacy" className="text-micro transition-all duration-200 hover:underline underline-offset-2 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)] hover:text-[var(--brand)]"> |
|
||||
{zhText('隐私政策', '隱私政策', 'Privacy')} |
<span className="text-[color-mix(in_oklch,var(--brand-white)_18%,transparent)]" aria-hidden="true">·</span> |
||||
|
|
||||
|
{/* Privacy */} |
||||
|
<a |
||||
|
href={getLocalizedPath('/privacy', locale)} |
||||
|
className="text-micro transition-all duration-200 text-[color-mix(in_oklch,var(--brand-white)_50%,transparent)] hover:text-[var(--brand)]" |
||||
|
style={{ fontFamily: 'var(--font-body)' }} |
||||
|
> |
||||
|
{f.legal.privacy} |
||||
</a> |
</a> |
||||
</div> |
</div> |
||||
<p className="text-micro font-semibold tracking-wide text-[color-mix(in_oklch,var(--brand-white)_45%,transparent)]" style={{ fontFamily: 'var(--font-body)' }} aria-hidden="true"> |
</div> |
||||
Integrity · Courage · Innovation · Devotion · Passion |
|
||||
|
{/* Desktop-only ICIDP tagline */} |
||||
|
<div className="hidden md:flex mt-5 pt-5 border-t border-[color-mix(in_oklch,var(--brand-white)_6%,transparent)] justify-center"> |
||||
|
<p className="text-micro font-semibold tracking-[0.2em] uppercase text-[color-mix(in_oklch,var(--brand-white)_30%,transparent)]" style={{ fontFamily: 'var(--font-body)' }} aria-hidden="true"> |
||||
|
{f.icidpFull} |
||||
</p> |
</p> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
</div> |
||||
</footer> |
</footer> |
||||
); |
); |
||||
} |
} |
||||
|
|||||
@ -1,6 +1,5 @@ |
|||||
// 统一占位图:全站内容图(办公/团队/业务/新闻/横幅等)共用此图。
|
// 统一占位图:全站内容图(办公/团队/业务/新闻/横幅等)共用此图。
|
||||
// 后期设计给真实图片后:把图存为 public/placeholder.jpg,
|
// 图片统一托管于 OSS:https://oss.baskbuddy.com/baskbuddy/
|
||||
// 并将本常量改为 '/placeholder.jpg' 即可全局生效(一处改,全站换)。
|
// 若要替换占位图,把对应文件上传到 OSS 后改本常量一处即可全局生效。
|
||||
// 若远程 URL 因防盗链无法显示,本地存图后改成本地路径即可。
|
|
||||
export const PLACEHOLDER_IMG = |
export const PLACEHOLDER_IMG = |
||||
'https://ts3.tc.mm.bing.net/th/id/OIP-C.IuNuzsR9jKSqu2-VTCmNOAHaE8?r=0&rs=1&pid=ImgDetMain&o=7&rm=3'; |
'https://oss.baskbuddy.com/baskbuddy/placeholder.jpg'; |
||||
|
|||||
@ -1,104 +0,0 @@ |
|||||
# 巴适伙伴 · UI 设计规范(最新规范学习 + 项目审计落地) |
|
||||
|
|
||||
> 本文是「查找最新 UI 设计规范 → 学习记忆 → 运用到项目」的产物。第 1–2 节是**学到的规范**(记忆),第 3 节是**对照本项目的审计**,第 4 节是**本次落地**,第 5 节是**可选后续**。 |
|
||||
> 研究时间:2026-08。规范以 WCAG 2.2(W3C)、Design Tokens(W3C DTCG)、Material Design 3、现代 CSS(web.dev)为权威基线,结合 2026 趋势。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 1. 研究来源(权威基线) |
|
||||
- **WCAG 2.2**(W3C,2023 发布,现行标准):对比度、焦点、目标尺寸、跳转等可访问性准则。 |
|
||||
- **Design Tokens Community Group**(W3C DTCG):令牌分层与格式化标准。 |
|
||||
- **Material Design 3**(Google):色彩系统、动效、形状、高程的 web 友好体系。 |
|
||||
- **web.dev / Smashing**:现代 CSS 实践(color-mix、OKLCH、clamp、container queries)。 |
|
||||
- **2026 趋势**(行业报告):动态自适应、流体间距/排版、主题感知令牌、动效克制、空间深度。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 2. 最新 UI 设计规范要点(学习记忆) |
|
||||
|
|
||||
### 2.1 Design Tokens 四层架构(2026 共识) |
|
||||
1. **Foundation(基础)**:颜色基值、字号比例、圆角基准、间距基线——系统的"物理常数",极少变动。 |
|
||||
2. **Semantic(语义)**:primary / success / warning / error / text / surface——映射到视觉表现,支持品牌调性切换。 |
|
||||
3. **Contextual(情境)**:响应式/容器派生(如 `--space-*` 随屏宽缩放)。 |
|
||||
4. **Intelligent(智能)**:AI/行为数据驱动(本项目不涉及)。 |
|
||||
- **铁律**:单一真相源(single source of truth),组件只引用令牌不写散值;深色模式靠令牌覆盖而非重写。 |
|
||||
|
|
||||
### 2.2 WCAG 2.2 无障碍(核心规范骨架) |
|
||||
| 准则 | 要求 | 级别 | |
|
||||
|---|---|---| |
|
||||
| 1.4.3 对比度(最小) | 正文 ≥4.5:1;大字(≥18pt 或 ≥14pt 粗)≥3:1 | AA | |
|
||||
| 1.4.1 颜色用途 | 颜色不能是传达信息的唯一通道(需文字/图标冗余) | A | |
|
||||
| 1.4.11 非文本对比 | UI 组件/图形对象 ≥3:1 | AA | |
|
||||
| 2.1.1 键盘 | 全部功能可用键盘操作 | A | |
|
||||
| 2.4.1 绕过区块 | 提供"跳到主内容"链接 | A | |
|
||||
| 2.4.7 焦点可见 + 2.4.11 焦点外观 | 焦点指示器 ≥2 CSS px、与相邻色 ≥3:1 | AA | |
|
||||
| 2.5.8 目标尺寸(最小) | 交互元素 ≥24×24 CSS px(AA);2.5.5 ≥44×44(AAA) | AA | |
|
||||
| 2.3.3 交互动画 | 尊重 `prefers-reduced-motion` | AAA | |
|
||||
| 3.1.1 / 3.1.2 页面语言 | `<html lang>` 随语种;外文片段标注 | A | |
|
||||
| 4.1.2 名称/角色/值 | 交互元素有可访问名(aria-label/原生语义) | A | |
|
||||
|
|
||||
### 2.3 现代 CSS(2026 工程基线) |
|
||||
- **color-mix() / OKLCH**:感知均匀的混色与调阶(OKLCH 比 HSL 更符合人眼)。 |
|
||||
- **clamp() 流体排版/间距**:无断点连续缩放,避免极端屏崩坏。 |
|
||||
- **container queries**:组件级响应式(比视口 media query 更内聚)。 |
|
||||
- **逻辑属性**:`margin-inline`/`inset` 等,天然支持 RTL。 |
|
||||
- **prefers-color-scheme + color-scheme**:原生暗色 + 表单控件自动适配。 |
|
||||
- **:focus-visible**:仅键盘聚焦显指示器,不干扰鼠标。 |
|
||||
|
|
||||
### 2.4 2026 趋势(取舍) |
|
||||
- 动态自适应(弱网降级、环境光调对比)——方向正确,落地需克制,避免过度工程。 |
|
||||
- 流体间距(间距随屏宽缩放)——优于固定 8px,但需防过度变形。 |
|
||||
- 动效克制(长周期、低强度,避免视觉疲劳)——本项目已践行(40s/50s drift)。 |
|
||||
- 空间深度(AR/3D)——官网不涉及。 |
|
||||
|
|
||||
### 2.5 视觉层级与一致性 |
|
||||
- 8px 间距网格;模块化字号阶梯(display > h1 > h2 > h3 > body > micro)。 |
|
||||
- 语义化用色(强调色按显示角色分档,各档过 WCAG 阈值)。 |
|
||||
- 克制圆角(大块锐利、小元素圆润,形成层次)。 |
|
||||
- 微交互有目的(hover 微浮、状态反馈),不炫技。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 3. 项目审计(对照规范) |
|
||||
|
|
||||
### ✅ 已合规项(设计系统成熟) |
|
||||
- **Design Tokens**:Foundation(`--brand-*`、`--fs-*`、`--space-*`、`--radius-*`、`--duration-*`)+ Semantic(`--accent`、`--status-*`、`--surface-*`、`--text-*`)齐备;赛道色 `--track-*` 单源(经 `src/lib/tracks.ts` 的 `TRACK_TOKEN`)。 |
|
||||
- **主题感知 + 暗色模式**:`[data-theme="dark"]` 覆盖块 + `useTheme.ts` 三态切换 + `index.html` 防 FOUC 内联脚本。 |
|
||||
- **color-mix**:大量用于派生色(赛道卡片底、描边、阴影)。 |
|
||||
- **流体排版**:`--fs-*` 全部用 `clamp()`。 |
|
||||
- **8px 间距网格**:`--space-1..11`。 |
|
||||
- **:focus-visible**:全局 2px outline + offset(2.4.7/2.4.11 ✓)。 |
|
||||
- **prefers-reduced-motion**:全局守卫降级动画时长(2.3.3 ✓)。 |
|
||||
- **键盘可达**:flip-card 支持键盘/触摸/点击翻转,Esc 关下拉(2.1.1 ✓)。 |
|
||||
- **html lang**:随路由语种切换 `zh-CN`/`zh-HK`/`en`(3.1.1 ✓)。 |
|
||||
- **landmark**:`<main>` 语义标签。 |
|
||||
- **动效克制**:长周期低强度(mesh 40s、渐变文 14s),有注释记录"降低强度"。 |
|
||||
- **`<html lang>`、hreflang、OG/Twitter、JSON-LD**:SEO + 无障碍元数据完备。 |
|
||||
|
|
||||
### ⚠️ 差距与修复 |
|
||||
| 差距 | 规范 | 状态 | |
|
||||
|---|---|---| |
|
||||
| 缺 skip-to-content 跳转链接 | WCAG 2.4.1 | **已修复**(首次) | |
|
||||
| `.status-badge/.status-developing/.status-planned/.progress-glow` 死 CSS(src 0 引用已复核) | 一致性 | **已清理**(本次) | |
|
||||
| 触控目标:桌面端导航 ≈24px(达 AA 24px 下限) | WCAG 2.5.8 | **已加固**(本次,navBase 加 `py-1.5` → ~28px,安全过 AA) | |
|
||||
| 组件内 `text-[Npx]` 硬编码未走 `--fs-*` 令牌 | Tokens 单源 | **已迁移**(Header.tsx 4 处 `text-[16px]`→`text-body`;全 src 复核无 `text-[15px]` 散落,其余页面已用令牌) | |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 4. 本次落地(已执行并验证) |
|
||||
1. **`.sr-only` + `.skip-link` 工具类**:新增于 [global.css](src/styles/global.css) 末尾。 |
|
||||
2. **skip-to-content 链接**:加在 [SiteShell.tsx](src/components/SiteShell.tsx),`<main>` 加 `id="main-content"`;三语文案(跳到主要内容 / 跳到主要內容 / Skip to main content);默认隐藏、键盘 Tab 聚焦时滑入显现。 |
|
||||
3. **清理死 CSS**:删除 [global.css](src/styles/global.css) 中 0 引用的 `.status-badge / .status-developing / .status-planned / .progress-glow / .progress-glow::after / @keyframes progressShine`(删除前 `Select-String` 全 src 复核确认零引用)。 |
|
||||
4. **`--fs-*` 令牌迁移**:[Header.tsx](src/components/Header.tsx) 4 处 `text-[16px]` → `text-body`(走 `--fs-body`=16px 令牌,1:1 视觉等价);保留 `text-[27px]`(品牌字标,无对应令牌)与 `text-[10px]`(▼ 装饰符,无对应令牌)。 |
|
||||
5. **触控目标加固(WCAG 2.5.8)**:桌面端 `navBase` 加 `py-1.5`,链接触控目标由 ~24px(贴 AA 下限)提升至 ~28px,安全过 AA 24px;移动端 `mobileNavBase` 原有 `py-2.5`(~36px)保持不变。 |
|
||||
6. **验证**:dev server HMR 干净生效(global.css + Header.tsx 多次 hmr update,零报错)。 |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 5. 可选后续(opt-in,未动,需你拍板优先级) |
|
||||
> A(死 CSS 清理)、B(`--fs-*` 令牌迁移:Header 已完成,全 src 复核无其他 `text-[Npx]` 散落)、F(触控目标加固)均已在本次落地,从清单移除。 |
|
||||
|
|
||||
- **C. 流体间距**:把 `--space-*` 升级为 `clamp()` 极端屏自适应(中风险,需逐页验证)。 |
|
||||
- **D. 容器查询**:关键卡片组件改 container query(重构,中高风险)。 |
|
||||
- **E. OKLCH 色彩空间**:派生色从 `color-mix(in srgb)` 迁到 `in oklch`(更符合人眼;中风险)。 |
|
||||
``` |
|
||||
Loading…
Reference in new issue