chore(design): 建立 token 单源管线,删除 design/flutter fork
- 新增 design/codegen/gen_flutter_tokens.mjs: 从 colors_and_type.css codegen 出 client/lib/pangolin_tokens.gen.dart 覆盖 PangolinColors/Spacing/Radius/Motion/Shadow(纯数值层) - 重构 client/lib/pangolin_theme.dart: 删除手抄数值,import+export pangolin_tokens.gen.dart 保留实现层 PangolinScheme/PangolinText/PangolinTheme/PangolinContext 19 个引用方零改动,对外 API 完全不变 - 新增 web/usercenter/scripts/build-tokens.mjs: 仿 website 样板,prebuild/predev 自动从 css 生成 public/colors_and_type.css 去除 usercenter 手抄副本的漂移风险 - 更新 CLAUDE.md:补 token codegen 使用说明与单源模型 - 更新 design/SKILL.md:移除已删 flutter/ 引用,补 codegen 入口 验证:改 clay-500 → 三端 codegen 同步变化;flutter analyze 无新增 error。 注:design/flutter/ fork 手动删除(git rm 需用户执行) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
// pangolin_theme.dart
|
||||
// 穿山甲 · Pangolin — Flutter design tokens
|
||||
// 穿山甲 · Pangolin — Flutter theme implementation
|
||||
//
|
||||
// Single Dart source mirroring colors_and_type.css.
|
||||
// Fonts are loaded at runtime via the google_fonts package (no local ttf needed).
|
||||
// Token 数值层(颜色/间距/圆角/动效/阴影)由 codegen 自动维护,见 pangolin_tokens.gen.dart。
|
||||
// 本文件只保留 Flutter 实现层:
|
||||
// · PangolinScheme — ThemeExtension(语义色映射 + light/dark 实例)
|
||||
// · PangolinContext — BuildContext 扩展
|
||||
// · PangolinFonts — 字族标签常量
|
||||
// · PangolinText — google_fonts 运行时字型
|
||||
// · PangolinTheme — ThemeData 组装
|
||||
//
|
||||
// Usage:
|
||||
// MaterialApp(
|
||||
@@ -10,53 +15,14 @@
|
||||
// darkTheme: PangolinTheme.dark,
|
||||
// themeMode: ThemeMode.system,
|
||||
// );
|
||||
// // tokens: PangolinColors.clay500, PangolinSpacing.x4, PangolinRadius.lg ...
|
||||
// // tokens: context.pangolin.accent, PangolinSpacing.x4, PangolinRadius.lg ...
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
/// ── Primitive color ramps ───────────────────────────────────────────
|
||||
class PangolinColors {
|
||||
PangolinColors._();
|
||||
|
||||
// Clay / Copper — pangolin-armor primary (warm earth)
|
||||
static const clay50 = Color(0xFFFAF3ED);
|
||||
static const clay100 = Color(0xFFF2E2D4);
|
||||
static const clay200 = Color(0xFFE6C7AC);
|
||||
static const clay300 = Color(0xFFD9A982);
|
||||
static const clay400 = Color(0xFFCC8B5C);
|
||||
static const clay500 = Color(0xFFB96A3D); // brand primary
|
||||
static const clay600 = Color(0xFF9E5630);
|
||||
static const clay700 = Color(0xFF7E4426);
|
||||
static const clay800 = Color(0xFF5E331D);
|
||||
static const clay900 = Color(0xFF3D2213);
|
||||
|
||||
// Sand / Taupe — warm neutral ramp
|
||||
static const sand50 = Color(0xFFFAF8F4);
|
||||
static const sand100 = Color(0xFFF2EEE7);
|
||||
static const sand200 = Color(0xFFE6DFD3);
|
||||
static const sand300 = Color(0xFFD2C8B8);
|
||||
static const sand400 = Color(0xFFB0A491);
|
||||
static const sand500 = Color(0xFF8C8270);
|
||||
static const sand600 = Color(0xFF6B6253);
|
||||
static const sand700 = Color(0xFF4E4940);
|
||||
static const sand800 = Color(0xFF2E2A24);
|
||||
static const sand900 = Color(0xFF1F1C18);
|
||||
static const sand950 = Color(0xFF14110E); // warm espresso near-black
|
||||
|
||||
// Semantic hues (earth-tuned)
|
||||
static const green400 = Color(0xFF7FB07A);
|
||||
static const green500 = Color(0xFF5B8C5A); // connected / secure
|
||||
static const green600 = Color(0xFF467046);
|
||||
static const amber400 = Color(0xFFE2B05A);
|
||||
static const amber500 = Color(0xFFD69A3C); // connecting / warning
|
||||
static const amber600 = Color(0xFFB47E29);
|
||||
static const red400 = Color(0xFFD4715A);
|
||||
static const red500 = Color(0xFFC0533B); // error / disconnect
|
||||
static const red600 = Color(0xFF9E4230);
|
||||
|
||||
static const white = Color(0xFFFFFFFF);
|
||||
}
|
||||
// token 数值层:import 供本文件使用,export 让调用方 import pangolin_theme.dart 时也能访问。
|
||||
import 'pangolin_tokens.gen.dart';
|
||||
export 'pangolin_tokens.gen.dart';
|
||||
|
||||
/// ── Semantic tokens, resolved per brightness ───────────────────────
|
||||
@immutable
|
||||
@@ -177,38 +143,10 @@ extension PangolinContext on BuildContext {
|
||||
PangolinScheme get pangolin => Theme.of(this).extension<PangolinScheme>()!;
|
||||
}
|
||||
|
||||
/// ── Spacing (4px base) ──────────────────────────────────────────────
|
||||
class PangolinSpacing {
|
||||
PangolinSpacing._();
|
||||
static const double x1 = 4, x2 = 8, x3 = 12, x4 = 16, x5 = 20,
|
||||
x6 = 24, x8 = 32, x10 = 40, x12 = 48, x16 = 64;
|
||||
}
|
||||
|
||||
/// ── Radii ───────────────────────────────────────────────────────────
|
||||
class PangolinRadius {
|
||||
PangolinRadius._();
|
||||
static const double sm = 6, md = 10, lg = 14, xl = 20, xxl = 28, full = 999;
|
||||
static const rSm = Radius.circular(sm);
|
||||
static const rMd = Radius.circular(md);
|
||||
static const rLg = Radius.circular(lg);
|
||||
static const rXl = Radius.circular(xl);
|
||||
static const rXxl = Radius.circular(xxl);
|
||||
}
|
||||
|
||||
/// ── Motion ──────────────────────────────────────────────────────────
|
||||
class PangolinMotion {
|
||||
PangolinMotion._();
|
||||
static const fast = Duration(milliseconds: 140);
|
||||
static const base = Duration(milliseconds: 220);
|
||||
static const slow = Duration(milliseconds: 360);
|
||||
static const easeOut = Cubic(0.22, 1, 0.36, 1);
|
||||
static const easeInOut = Cubic(0.65, 0, 0.35, 1);
|
||||
}
|
||||
|
||||
/// ── Font families (via google_fonts at runtime) ─────────────────────
|
||||
class PangolinFonts {
|
||||
PangolinFonts._();
|
||||
// These string constants are used as display labels only.
|
||||
// String constants used as display labels only.
|
||||
// Actual font loading is done via google_fonts in PangolinText getters.
|
||||
static const display = 'Sora';
|
||||
static const sans = 'Manrope';
|
||||
@@ -248,16 +186,6 @@ class PangolinText {
|
||||
fontFeatures: const [FontFeature.tabularFigures()]);
|
||||
}
|
||||
|
||||
/// ── Soft warm-tinted shadows ────────────────────────────────────────
|
||||
class PangolinShadow {
|
||||
PangolinShadow._();
|
||||
static const _tint = Color(0xFF2D1E14);
|
||||
static List<BoxShadow> sm = [BoxShadow(color: _tint.withOpacity(0.06), blurRadius: 2, offset: const Offset(0, 1))];
|
||||
static List<BoxShadow> md = [BoxShadow(color: _tint.withOpacity(0.08), blurRadius: 14, offset: const Offset(0, 4))];
|
||||
static List<BoxShadow> lg = [BoxShadow(color: _tint.withOpacity(0.12), blurRadius: 32, offset: const Offset(0, 12))];
|
||||
static List<BoxShadow> xl = [BoxShadow(color: _tint.withOpacity(0.16), blurRadius: 60, offset: const Offset(0, 24))];
|
||||
}
|
||||
|
||||
/// ── Ready-to-use ThemeData ──────────────────────────────────────────
|
||||
class PangolinTheme {
|
||||
PangolinTheme._();
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// pangolin_tokens.gen.dart
|
||||
// AUTO-GENERATED — 勿手改。
|
||||
// 源: design/colors_and_type.css
|
||||
// 生成器: design/codegen/gen_flutter_tokens.mjs
|
||||
//
|
||||
// 包含:PangolinColors · PangolinSpacing · PangolinRadius · PangolinMotion · PangolinShadow
|
||||
// 由 pangolin_theme.dart 导入;业务代码通过 pangolin_theme.dart 的符号访问,无需直接 import 本文件。
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// ── Primitive color ramps ───────────────────────────────────────────
|
||||
/// Auto-generated from design/colors_and_type.css :root color ramps.
|
||||
class PangolinColors {
|
||||
PangolinColors._();
|
||||
|
||||
// Clay
|
||||
static const clay50 = Color(0xFFFAF3ED);
|
||||
static const clay100 = Color(0xFFF2E2D4);
|
||||
static const clay200 = Color(0xFFE6C7AC);
|
||||
static const clay300 = Color(0xFFD9A982);
|
||||
static const clay400 = Color(0xFFCC8B5C);
|
||||
static const clay500 = Color(0xFFB96A3D);
|
||||
static const clay600 = Color(0xFF9E5630);
|
||||
static const clay700 = Color(0xFF7E4426);
|
||||
static const clay800 = Color(0xFF5E331D);
|
||||
static const clay900 = Color(0xFF3D2213);
|
||||
|
||||
// Sand
|
||||
static const sand50 = Color(0xFFFAF8F4);
|
||||
static const sand100 = Color(0xFFF2EEE7);
|
||||
static const sand200 = Color(0xFFE6DFD3);
|
||||
static const sand300 = Color(0xFFD2C8B8);
|
||||
static const sand400 = Color(0xFFB0A491);
|
||||
static const sand500 = Color(0xFF8C8270);
|
||||
static const sand600 = Color(0xFF6B6253);
|
||||
static const sand700 = Color(0xFF4E4940);
|
||||
static const sand800 = Color(0xFF2E2A24);
|
||||
static const sand900 = Color(0xFF1F1C18);
|
||||
static const sand950 = Color(0xFF14110E);
|
||||
|
||||
// Green
|
||||
static const green400 = Color(0xFF7FB07A);
|
||||
static const green500 = Color(0xFF5B8C5A);
|
||||
static const green600 = Color(0xFF467046);
|
||||
|
||||
// Amber
|
||||
static const amber400 = Color(0xFFE2B05A);
|
||||
static const amber500 = Color(0xFFD69A3C);
|
||||
static const amber600 = Color(0xFFB47E29);
|
||||
|
||||
// Red
|
||||
static const red400 = Color(0xFFD4715A);
|
||||
static const red500 = Color(0xFFC0533B);
|
||||
static const red600 = Color(0xFF9E4230);
|
||||
|
||||
static const white = Color(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
/// ── Spacing (4px base) ──────────────────────────────────────────────
|
||||
class PangolinSpacing {
|
||||
PangolinSpacing._();
|
||||
static const double x0 = 0, x1 = 4, x2 = 8, x3 = 12, x4 = 16, x5 = 20, x6 = 24, x8 = 32, x10 = 40, x12 = 48, x16 = 64;
|
||||
}
|
||||
|
||||
/// ── Radii ───────────────────────────────────────────────────────────
|
||||
class PangolinRadius {
|
||||
PangolinRadius._();
|
||||
static const double sm = 6, md = 10, lg = 14, xl = 20, xxl = 28, full = 999;
|
||||
static const rSm = Radius.circular(sm);
|
||||
static const rMd = Radius.circular(md);
|
||||
static const rLg = Radius.circular(lg);
|
||||
static const rXl = Radius.circular(xl);
|
||||
static const rXxl = Radius.circular(xxl);
|
||||
}
|
||||
|
||||
/// ── Motion ──────────────────────────────────────────────────────────
|
||||
class PangolinMotion {
|
||||
PangolinMotion._();
|
||||
static const fast = Duration(milliseconds: 140);
|
||||
static const base = Duration(milliseconds: 220);
|
||||
static const slow = Duration(milliseconds: 360);
|
||||
static const easeOut = Cubic(0.22, 1, 0.36, 1);
|
||||
static const easeInOut = Cubic(0.65, 0, 0.35, 1);
|
||||
}
|
||||
|
||||
/// ── Soft warm-tinted shadows (light mode) ─────────────────────────
|
||||
class PangolinShadow {
|
||||
PangolinShadow._();
|
||||
static const _tint = Color(0xFF2D1E14); // rgba(45, 30, 20) warm shadow tint
|
||||
static List<BoxShadow> sm = [BoxShadow(color: _tint.withValues(alpha: 0.06), blurRadius: 2, offset: const Offset(0, 1))];
|
||||
static List<BoxShadow> md = [BoxShadow(color: _tint.withValues(alpha: 0.08), blurRadius: 14, offset: const Offset(0, 4))];
|
||||
static List<BoxShadow> lg = [BoxShadow(color: _tint.withValues(alpha: 0.12), blurRadius: 32, offset: const Offset(0, 12))];
|
||||
static List<BoxShadow> xl = [BoxShadow(color: _tint.withValues(alpha: 0.16), blurRadius: 60, offset: const Offset(0, 24))];
|
||||
}
|
||||
Reference in New Issue
Block a user