flutter-duskmoon-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Flutter DuskMoon UI — Design Principles & Usage Rules

Flutter DuskMoon UI — 设计原则与使用规则

Architecture Overview

架构概述

Package Dependency Graph (import direction →)

包依赖关系图(导入方向 →)

duskmoon-dev/design (YAML → codegen)
  → duskmoon_theme
      ├ DmDesignTokens (generated const data)
      ├ DmTheme (InheritedWidget)
      ├ DmPlatformStyle { material, cupertino, fluent }
      └ toMaterial() / toCupertino() / toFluent()

duskmoon_theme
  → duskmoon_widgets
      ├ DuskmoonApp (root shell)
      ├ DmAdaptiveWidget (base class)
      └ Dm* widgets (Button, TextField, Switch, etc.)

duskmoon_widgets
  → duskmoon_settings
  → duskmoon_feedback
  → duskmoon_ui (umbrella re-export)
Rule: Never import downstream.
duskmoon_theme
must never import from
duskmoon_widgets
.
duskmoon_widgets
must never import from
duskmoon_settings
.
duskmoon-dev/design (YAML → codegen)
  → duskmoon_theme
      ├ DmDesignTokens (generated const data)
      ├ DmTheme (InheritedWidget)
      ├ DmPlatformStyle { material, cupertino, fluent }
      └ toMaterial() / toCupertino() / toFluent()

duskmoon_theme
  → duskmoon_widgets
      ├ DuskmoonApp (root shell)
      ├ DmAdaptiveWidget (base class)
      └ Dm* widgets (Button, TextField, Switch, etc.)

duskmoon_widgets
  → duskmoon_settings
  → duskmoon_feedback
  → duskmoon_ui (umbrella re-export)
规则:禁止导入下游包。
duskmoon_theme
绝不能从
duskmoon_widgets
导入内容。
duskmoon_widgets
绝不能从
duskmoon_settings
导入内容。

Widget Tree (Runtime)

Widget树(运行时)

DuskmoonApp(tokens: .sunshine, darkTokens: .moonlight, platformStyle: .cupertino)
  └→ DmTheme (InheritedWidget — .of(context) available everywhere below)
       └→ CupertinoApp(theme: tokens.toCupertino())
            └→ user's widget tree
                 └→ DmButton(label: "Save")  // dispatches to CupertinoButton

DuskmoonApp(tokens: .sunshine, darkTokens: .moonlight, platformStyle: .cupertino)
  └→ DmTheme (InheritedWidget — .of(context) 可在下方所有节点调用)
       └→ CupertinoApp(theme: tokens.toCupertino())
            └→ 用户的Widget树
                 └→ DmButton(label: "Save")  // 调度到CupertinoButton

Rule 1: Theme Setup

规则1:主题配置

Correct App Root

正确的应用根节点

dart
// ✅ ALWAYS — use DuskmoonApp as the root widget
void main() {
  runApp(
    DuskmoonApp(
      tokens: DmDesignTokens.sunshine,
      darkTokens: DmDesignTokens.moonlight,
      themeMode: ThemeMode.system,
      platformStyle: DmPlatformStyle.material,
      home: const MyHomePage(),
    ),
  );
}
dart
// ✅ 必须使用 DuskmoonApp 作为根Widget
void main() {
  runApp(
    DuskmoonApp(
      tokens: DmDesignTokens.sunshine,
      darkTokens: DmDesignTokens.moonlight,
      themeMode: ThemeMode.system,
      platformStyle: DmPlatformStyle.material,
      home: const MyHomePage(),
    ),
  );
}

❌ NEVER — bypass DuskmoonApp

❌ 禁止绕过 DuskmoonApp

dart
// ❌ NEVER wrap MaterialApp/CupertinoApp directly
void main() {
  runApp(MaterialApp(
    theme: DmDesignTokens.sunshine.toMaterial(), // wrong — skips DmTheme
    home: MyHomePage(),
  ));
}
Why:
DuskmoonApp
injects
DmTheme
above the platform app. Without it,
DmTheme.of(context)
returns null and all Dm* widgets fail to resolve tokens.

dart
// ❌ 禁止直接包裹 MaterialApp/CupertinoApp
void main() {
  runApp(MaterialApp(
    theme: DmDesignTokens.sunshine.toMaterial(), // 错误 — 跳过了DmTheme
    home: MyHomePage(),
  ));
}
原因:
DuskmoonApp
会在平台应用上方注入
DmTheme
。如果没有它,
DmTheme.of(context)
会返回null,所有Dm* Widget都无法解析令牌。

Rule 2: Accessing Design Tokens

规则2:访问设计令牌

Always use
DmTheme.of(context)

始终使用
DmTheme.of(context)

dart
// ✅ Read tokens from the widget tree
Widget build(BuildContext context) {
  final tokens = DmTheme.of(context).tokens;
  return Container(
    color: tokens.primaryContainer,
    child: Text('Hello', style: TextStyle(color: tokens.onPrimaryContainer)),
  );
}
dart
// ✅ 从Widget树中读取令牌
Widget build(BuildContext context) {
  final tokens = DmTheme.of(context).tokens;
  return Container(
    color: tokens.primaryContainer,
    child: Text('Hello', style: TextStyle(color: tokens.onPrimaryContainer)),
  );
}

❌ NEVER reference generated constants directly in widget builds

❌ 禁止在Widget构建中直接引用生成的常量

dart
// ❌ This ignores dark mode, theme overrides, and subtree overrides
Widget build(BuildContext context) {
  return Container(color: DmDesignTokens.sunshine.primaryContainer);
}
Why: The resolved tokens depend on
ThemeMode
, platform brightness, and possible
DmThemeOverride
ancestors. Only
DmTheme.of(context)
returns the correct resolved set.
dart
// ❌ 这种写法忽略了深色模式、主题覆盖和子树覆盖
Widget build(BuildContext context) {
  return Container(color: DmDesignTokens.sunshine.primaryContainer);
}
原因: 解析后的令牌取决于
ThemeMode
、平台亮度以及可能存在的
DmThemeOverride
父节点。只有
DmTheme.of(context)
能返回正确的解析结果。

Exception — static adapter methods

例外情况 — 静态适配器方法

DuskmoonApp
itself must convert tokens to platform
ThemeData
before
DmTheme
exists in the tree. For this case only, use the static adapters:
dart
// Inside DuskmoonApp.build() — acceptable
MaterialApp(theme: DmTheme.staticToMaterial(resolvedTokens));

DuskmoonApp
自身必须在
DmTheme
加入树之前将令牌转换为平台
ThemeData
。仅在这种情况下,可以使用静态适配器:
dart
// 在 DuskmoonApp.build() 内部 — 允许使用
MaterialApp(theme: DmTheme.staticToMaterial(resolvedTokens));

Rule 3: Color System

规则3:颜色系统

Token Structure (61 color tokens per theme)

令牌结构(每个主题包含61个颜色令牌)

GroupTokensUsage
Primary
primary
,
onPrimary
,
primaryContainer
,
onPrimaryContainer
Main brand actions, primary CTAs
Secondary
secondary
,
onSecondary
,
secondaryContainer
,
onSecondaryContainer
Supporting actions, alternative CTAs
Tertiary
tertiary
,
onTertiary
,
tertiaryContainer
,
onTertiaryContainer
Accent highlights, badges, special UI
Error
error
,
onError
,
errorContainer
,
onErrorContainer
Error states, destructive actions
Surface
surface
,
onSurface
,
surfaceDim
,
surfaceBright
,
surfaceContainerLowest
Highest
,
surfaceVariant
,
onSurfaceVariant
Backgrounds, cards, elevation
Outline
outline
,
outlineVariant
Borders, dividers
Inverse
inverseSurface
,
inverseOnSurface
,
inversePrimary
Snackbars, contrast overlays
Scrim/Shadow
scrim
(with alpha),
shadow
Modal overlays, elevation shadows
Semantic
info
,
success
,
warning
(+ content variants)
Status indicators
分组令牌用途
Primary
primary
,
onPrimary
,
primaryContainer
,
onPrimaryContainer
主品牌操作、主要CTA按钮
Secondary
secondary
,
onSecondary
,
secondaryContainer
,
onSecondaryContainer
辅助操作、备选CTA按钮
Tertiary
tertiary
,
onTertiary
,
tertiaryContainer
,
onTertiaryContainer
强调高亮、徽章、特殊UI元素
Error
error
,
onError
,
errorContainer
,
onErrorContainer
错误状态、破坏性操作
Surface
surface
,
onSurface
,
surfaceDim
,
surfaceBright
,
surfaceContainerLowest
Highest
,
surfaceVariant
,
onSurfaceVariant
背景、卡片、层级
Outline
outline
,
outlineVariant
边框、分隔线
Inverse
inverseSurface
,
inverseOnSurface
,
inversePrimary
Snackbar、对比遮罩
Scrim/Shadow
scrim
(带透明度),
shadow
模态遮罩、层级阴影
Semantic
info
,
success
,
warning
(含内容变体)
状态指示器

Color Format: OKLCH

颜色格式:OKLCH

All colors are defined in OKLCH in the source CSS. The Dart codegen converts to
Color
objects via inline OKLCH→sRGB math (zero external deps).
所有颜色在源CSS中以OKLCH格式定义。Dart代码生成器通过内置的OKLCH→sRGB转换逻辑将其转换为
Color
对象(无外部依赖)。

❌ NEVER hardcode color values

❌ 禁止硬编码颜色值

dart
// ❌ Hardcoded hex
Container(color: Color(0xFF60A5FA))

// ❌ Hardcoded Material color
Container(color: Colors.blue)

// ✅ Use design tokens
Container(color: DmTheme.of(context).tokens.primary)
dart
// ❌ 硬编码十六进制颜色
Container(color: Color(0xFF60A5FA))

// ❌ 硬编码Material颜色
Container(color: Colors.blue)

// ✅ 使用设计令牌
Container(color: DmTheme.of(context).tokens.primary)

Semantic color pairing rule

语义颜色配对规则

Every background token has a corresponding foreground token. Always pair them:
dart
// ✅ Correct pairing
Container(
  color: tokens.primaryContainer,
  child: Text('Label', style: TextStyle(color: tokens.onPrimaryContainer)),
)

// ❌ Mismatched — onSurface on primaryContainer may fail contrast
Container(
  color: tokens.primaryContainer,
  child: Text('Label', style: TextStyle(color: tokens.onSurface)),
)
BackgroundForeground
primary
onPrimary
primaryContainer
onPrimaryContainer
secondary
onSecondary
secondaryContainer
onSecondaryContainer
tertiary
onTertiary
tertiaryContainer
onTertiaryContainer
surface
onSurface
surfaceVariant
onSurfaceVariant
error
onError
errorContainer
onErrorContainer
inverseSurface
inverseOnSurface
每个背景令牌都有对应的前景令牌。 必须配对使用:
dart
// ✅ 正确配对
Container(
  color: tokens.primaryContainer,
  child: Text('Label', style: TextStyle(color: tokens.onPrimaryContainer)),
)

// ❌ 错误配对 — 在primaryContainer上使用onSurface可能导致对比度不足
Container(
  color: tokens.primaryContainer,
  child: Text('Label', style: TextStyle(color: tokens.onSurface)),
)
背景令牌前景令牌
primary
onPrimary
primaryContainer
onPrimaryContainer
secondary
onSecondary
secondaryContainer
onSecondaryContainer
tertiary
onTertiary
tertiaryContainer
onTertiaryContainer
surface
onSurface
surfaceVariant
onSurfaceVariant
error
onError
errorContainer
onErrorContainer
inverseSurface
inverseOnSurface

Surface elevation hierarchy

Surface层级结构

Use surface container tokens for visual depth, not opacity or shadows alone:
surfaceContainerLowest  →  bottom layer (behind everything)
surfaceContainerLow     →  low-elevation cards
surfaceContainer        →  standard cards/containers
surfaceContainerHigh    →  elevated cards, menus
surfaceContainerHighest →  dialogs, tooltips, top layer

使用surface容器令牌实现视觉深度,不要仅依赖透明度或阴影:
surfaceContainerLowest  →  最底层(在所有元素之后)
surfaceContainerLow     →  低层级卡片
surfaceContainer        →  标准卡片/容器
surfaceContainerHigh    →  高层级卡片、菜单
surfaceContainerHighest →  对话框、工具提示、最顶层

Rule 4: Available Themes

规则4:可用主题

5 themes defined in
duskmoon-dev/design
, codegen'd to Dart:
ThemeModePrimary Character
sunshine
lightWarm amber/gold
moonlight
darkCool blue/lavender
ocean
darkDeep blue/teal
forest
lightNatural green/earth
sunset
lightWarm orange/rose
Access via
DmDesignTokens.sunshine
,
DmDesignTokens.moonlight
, etc.

duskmoon-dev/design
中定义了5个主题,通过代码生成转换为Dart代码:
主题模式主色调特征
sunshine
浅色暖琥珀色/金色
moonlight
深色冷蓝色/淡紫色
ocean
深色深蓝色/蓝绿色
forest
浅色自然绿色/大地色
sunset
浅色暖橙色/玫瑰色
通过
DmDesignTokens.sunshine
DmDesignTokens.moonlight
等方式访问。

Rule 5: Platform Adaptive Widgets

规则5:平台自适应Widget

Resolution Stack (highest priority first)

优先级解析栈(优先级从高到低)

L1: Per-widget `platformOverride` parameter
L2: Nearest DmPlatformOverride ancestor (subtree override)
L3: DmTheme.of(context).platformStyle (from DuskmoonApp)
L4: defaultTargetPlatform (auto-detect)
L1: 单个Widget的`platformOverride`参数
L2: 最近的DmPlatformOverride父节点(子树覆盖)
L3: DmTheme.of(context).platformStyle(来自DuskmoonApp)
L4: defaultTargetPlatform(自动检测)

Writing an adaptive widget

编写自适应Widget

All adaptive widgets extend
DmAdaptiveWidget
:
dart
class DmButton extends DmAdaptiveWidget {
  const DmButton({super.key, required this.label, super.platformOverride});
  final String label;

  
  Widget buildMaterial(BuildContext context, DmDesignTokens tokens) {
    return FilledButton(onPressed: () {}, child: Text(label));
  }

  
  Widget buildCupertino(BuildContext context, DmDesignTokens tokens) {
    return CupertinoButton.filled(onPressed: () {}, child: Text(label));
  }

  // buildFluent defaults to buildMaterial unless overridden
}
所有自适应Widget都继承自
DmAdaptiveWidget
dart
class DmButton extends DmAdaptiveWidget {
  const DmButton({super.key, required this.label, super.platformOverride});
  final String label;

  
  Widget buildMaterial(BuildContext context, DmDesignTokens tokens) {
    return FilledButton(onPressed: () {}, child: Text(label));
  }

  
  Widget buildCupertino(BuildContext context, DmDesignTokens tokens) {
    return CupertinoButton.filled(onPressed: () {}, child: Text(label));
  }

  // buildFluent默认继承buildMaterial,除非重写
}

❌ NEVER check platform manually

❌ 禁止手动判断平台

dart
// ❌ Manual platform switching
if (Platform.isIOS) {
  return CupertinoButton(...);
} else {
  return ElevatedButton(...);
}

// ✅ Use DmAdaptiveWidget dispatch or DmPlatformStyle resolution
class MyWidget extends DmAdaptiveWidget { ... }
dart
// ❌ 手动切换平台
if (Platform.isIOS) {
  return CupertinoButton(...);
} else {
  return ElevatedButton(...);
}

// ✅ 使用DmAdaptiveWidget调度或DmPlatformStyle解析
class MyWidget extends DmAdaptiveWidget { ... }

File structure for adaptive widgets

自适应Widget的文件结构

dm_button/
├── dm_button.dart            # Public API, extends DmAdaptiveWidget
├── dm_button_material.dart   # buildMaterial implementation
├── dm_button_cupertino.dart  # buildCupertino implementation
└── dm_button_fluent.dart     # buildFluent (optional, falls through to material)

dm_button/
├── dm_button.dart            # 公开API,继承自DmAdaptiveWidget
├── dm_button_material.dart   # buildMaterial实现
├── dm_button_cupertino.dart  # buildCupertino实现
└── dm_button_fluent.dart     # buildFluent(可选,默认回退到material)

Rule 6: Shared Design Enums

规则6:共享设计枚举

All Dm* widgets share these semantic enums. Use them consistently — never invent ad-hoc parameters.
dart
enum DmColorRole { primary, secondary, tertiary, error, neutral }
enum DmSize { xs, sm, md, lg, xl }
enum DmButtonVariant { filled, outlined, ghost, tonal }
enum DmInputVariant { outlined, filled, underlined }
所有Dm* Widget共享以下语义枚举。请统一使用,禁止自定义临时参数。
dart
enum DmColorRole { primary, secondary, tertiary, error, neutral }
enum DmSize { xs, sm, md, lg, xl }
enum DmButtonVariant { filled, outlined, ghost, tonal }
enum DmInputVariant { outlined, filled, underlined }

Color resolution from DmColorRole

通过DmColorRole解析颜色

Every widget that takes
DmColorRole
resolves tokens identically:
DmColorRoleBackgroundForegroundContainerOn Container
primary
tokens.primary
tokens.onPrimary
tokens.primaryContainer
tokens.onPrimaryContainer
secondary
tokens.secondary
tokens.onSecondary
tokens.secondaryContainer
tokens.onSecondaryContainer
tertiary
tokens.tertiary
tokens.onTertiary
tokens.tertiaryContainer
tokens.onTertiaryContainer
error
tokens.error
tokens.onError
tokens.errorContainer
tokens.onErrorContainer
neutral
tokens.surface
tokens.onSurface
tokens.surfaceContainerHigh
tokens.onSurface
所有接收
DmColorRole
的Widget解析令牌的逻辑一致:
DmColorRole背景前景容器容器前景
primary
tokens.primary
tokens.onPrimary
tokens.primaryContainer
tokens.onPrimaryContainer
secondary
tokens.secondary
tokens.onSecondary
tokens.secondaryContainer
tokens.onSecondaryContainer
tertiary
tokens.tertiary
tokens.onTertiary
tokens.tertiaryContainer
tokens.onTertiaryContainer
error
tokens.error
tokens.onError
tokens.errorContainer
tokens.onErrorContainer
neutral
tokens.surface
tokens.onSurface
tokens.surfaceContainerHigh
tokens.onSurface

Size scale

尺寸缩放

DmSizeHorizontal paddingVertical paddingFont scale
xs
840.75rem (12)
sm
1260.875rem (14)
md
1680.875rem (14)
lg
24121rem (16)
xl
32161.125rem (18)

DmSize水平内边距垂直内边距字体缩放
xs
840.75rem (12)
sm
1260.875rem (14)
md
1680.875rem (14)
lg
24121rem (16)
xl
32161.125rem (18)

Rule 7: Component Design — Actions

规则7:组件设计 — 操作类

DmButton

DmButton

Default:
variant: filled
,
color: primary
,
size: md
VariantBackgroundForegroundBorderUse case
filled
role coloronRolenonePrimary CTAs, main actions
outlined
transparentrole colorrole colorSecondary actions, cancel
ghost
transparentrole colornoneTertiary/inline actions, links
tonal
roleContaineronRoleContainernoneSoft emphasis, toggles
Color role assignment convention:
Action typeColor roleExample
Main CTA, save, submit, confirm
primary
"Save Changes"
Alternative action, secondary flow
secondary
"Export", "Share"
Accent action, special highlight
tertiary
"Watch Demo", "Premium"
Destructive, delete, remove
error
"Delete Account"
Neutral, dismiss, low emphasis
neutral
"Cancel", "Skip"
dart
// ✅ Typical action group
Row(children: [
  DmButton(variant: .ghost, color: .neutral, child: Text('Cancel')),
  DmButton(variant: .outlined, color: .secondary, child: Text('Save Draft')),
  DmButton(variant: .filled, color: .primary, child: Text('Publish')),
])
默认值:
variant: filled
,
color: primary
,
size: md
变体背景前景边框使用场景
filled
角色颜色onRole主要CTA、核心操作
outlined
透明角色颜色角色颜色次要操作、取消按钮
ghost
透明角色颜色三级/内联操作、链接
tonal
roleContaineronRoleContainer柔和强调、切换按钮
颜色角色分配约定:
操作类型颜色角色示例
主要CTA、保存、提交、确认
primary
"保存更改"
备选操作、次要流程
secondary
"导出"、"分享"
强调操作、特殊高亮
tertiary
"观看演示"、"高级版"
破坏性操作、删除、移除
error
"删除账户"
中性操作、关闭、低强调
neutral
"取消"、"跳过"
dart
// ✅ 典型操作组
Row(children: [
  DmButton(variant: .ghost, color: .neutral, child: Text('Cancel')),
  DmButton(variant: .outlined, color: .secondary, child: Text('Save Draft')),
  DmButton(variant: .filled, color: .primary, child: Text('Publish')),
])

DmIconButton

DmIconButton

Same color/size system as DmButton. Must always have
semanticLabel
.
dart
DmIconButton(
  icon: Icons.delete,
  color: DmColorRole.error,
  semanticLabel: 'Delete item',
  onPressed: () {},
)
与DmButton使用相同的颜色/尺寸系统。必须始终设置
semanticLabel
dart
DmIconButton(
  icon: Icons.delete,
  color: DmColorRole.error,
  semanticLabel: 'Delete item',
  onPressed: () {},
)

DmFab (Floating Action Button)

DmFab(浮动操作按钮)

  • Default color:
    primary
    — the single most important action on the screen
  • Surface:
    primaryContainer
    background,
    onPrimaryContainer
    icon
  • Rule: Maximum one FAB per screen. If you need multiple actions, use
    DmActionList
    .
dart
DmFab(
  onPressed: () {},
  icon: Icons.add,
  // FAB always uses primaryContainer/onPrimaryContainer — no color param
)
  • 默认颜色:
    primary
    — 屏幕上最重要的单个操作
  • 表面:
    primaryContainer
    背景,
    onPrimaryContainer
    图标
  • 规则: 每个屏幕最多一个FAB。如果需要多个操作,请使用
    DmActionList
dart
DmFab(
  onPressed: () {},
  icon: Icons.add,
  // FAB始终使用primaryContainer/onPrimaryContainer — 无颜色参数
)

DmActionList

DmActionList

Adapts rendering to available space:
BreakpointRendering
Small (< 600)Popup menu (overflow)
Medium (600–1200)Icon buttons in row
Large (> 1200)Text buttons with icons
dart
DmActionList(
  actions: [
    DmAction(icon: Icons.edit, label: 'Edit', onPressed: ...),
    DmAction(icon: Icons.share, label: 'Share', onPressed: ...),
    DmAction(icon: Icons.delete, label: 'Delete', color: DmColorRole.error, onPressed: ...),
  ],
)

根据可用空间自适应渲染:
断点渲染方式
小屏(< 600)弹出菜单(溢出)
中屏(600–1200)行内图标按钮
大屏(> 1200)带图标的文本按钮
dart
DmActionList(
  actions: [
    DmAction(icon: Icons.edit, label: 'Edit', onPressed: ...),
    DmAction(icon: Icons.share, label: 'Share', onPressed: ...),
    DmAction(icon: Icons.delete, label: 'Delete', color: DmColorRole.error, onPressed: ...),
  ],
)

Rule 8: Component Design — Navigation

规则8:组件设计 — 导航类

DmAppBar

DmAppBar

Default token mapping:
ElementTokenRationale
Background
primary
Brand presence, top-level identity
Title text
onPrimary
Contrast on primary
Icon buttons
onPrimary
Consistent with primary surface
Bottom bordernone (primary fills)Clean branded bar
Scrolled/elevated state: Background transitions to
primaryContainer
, text to
onPrimaryContainer
.
dart
DmAppBar(
  title: Text('Settings'),
  leading: DmIconButton(icon: Icons.arrow_back, semanticLabel: 'Back'),
  actions: [
    DmIconButton(icon: Icons.search, semanticLabel: 'Search'),
    DmIconButton(icon: Icons.more_vert, semanticLabel: 'More options'),
  ],
)
Neutral variant: For screens where the app bar should not compete with content (e.g., content-heavy reading views), pass
color: DmColorRole.neutral
to fall back to
surface
/
onSurface
.
默认令牌映射:
元素令牌设计理由
背景
primary
品牌展示、顶层身份标识
标题文本
onPrimary
在primary背景上保证对比度
图标按钮
onPrimary
与primary表面保持一致
底部边框无(primary填充整个栏)简洁的品牌栏
滚动/悬浮状态: 背景过渡为
primaryContainer
,文本过渡为
onPrimaryContainer
dart
DmAppBar(
  title: Text('Settings'),
  leading: DmIconButton(icon: Icons.arrow_back, semanticLabel: 'Back'),
  actions: [
    DmIconButton(icon: Icons.search, semanticLabel: 'Search'),
    DmIconButton(icon: Icons.more_vert, semanticLabel: 'More options'),
  ],
)
中性变体: 对于AppBar不应与内容竞争的屏幕(如内容密集的阅读视图),传递
color: DmColorRole.neutral
回退到
surface
/
onSurface

DmBottomNav

DmBottomNav

ElementToken
Background
primary
Selected icon/label
onPrimary
Unselected icon/label
onPrimary
at 70% opacity
Selected indicator
primaryContainer
(pill behind icon)
Top bordernone (primary fills)
Rule: 3–5 destinations maximum. Labels always visible (not icon-only).
元素令牌
背景
primary
选中的图标/标签
onPrimary
未选中的图标/标签
onPrimary
(70%透明度)
选中指示器
primaryContainer
(图标后方的胶囊形状)
顶部边框无(primary填充整个栏)
规则: 最多3–5个目标页面。标签始终可见(不支持仅图标模式)。

DmTabBar

DmTabBar

ElementToken
Background
surface
Selected tab
primary
(indicator + text)
Unselected tab
onSurfaceVariant
Indicator
primary
(bottom line in Material, pill in Cupertino)
元素令牌
背景
surface
选中标签
primary
(指示器+文本)
未选中标签
onSurfaceVariant
指示器
primary
(Material为底线,Cupertino为胶囊)

DmDrawer

DmDrawer

ElementToken
Background
secondary
Header area
secondaryContainer
Selected item bg
onSecondary
at 15% opacity
Selected item text
onSecondary
Unselected text
onSecondary
at 70% opacity
Dividers
onSecondary
at 20% opacity
Scrim (overlay behind drawer)
scrim
with alpha
Side menus and drawers use the secondary color family to visually distinguish navigation chrome from the primary-branded top bar.
元素令牌
背景
secondary
头部区域
secondaryContainer
选中项背景
onSecondary
(15%透明度)
选中项文本
onSecondary
未选中文本
onSecondary
(70%透明度)
分隔线
onSecondary
(20%透明度)
遮罩(Drawer后方的覆盖层)
scrim
(带透明度)
侧边菜单和Drawer使用secondary色系,在视觉上将导航控件与primary品牌的顶部栏区分开。

DmBreadcrumbs

DmBreadcrumbs

ElementToken
Active (current)
onSurface
(no link)
Ancestors (links)
primary
Separator
onSurfaceVariant

元素令牌
激活项(当前页面)
onSurface
(无链接)
祖先项(链接)
primary
分隔符
onSurfaceVariant

Rule 9: Component Design — Layout & Cards

规则9:组件设计 — 布局与卡片

DmCard

DmCard

Elevation hierarchy via surface tokens:
Card styleBackground tokenUse case
Flat
surface
Inline content, no separation
Outlined
surface
+
outlineVariant
border
List items, settings rows
Elevated
surfaceContainerLow
Standard cards
Filled
surfaceContainerHigh
Emphasized/grouped content
Interior layout convention:
┌─────────────────────────────────┐
│ [optional media/image]          │
├─────────────────────────────────┤
│ Title         (onSurface)       │
│ Subtitle      (onSurfaceVariant)│
│                                 │
│ Body text     (onSurface)       │
│                                 │
│ ┌─────────────────────────────┐ │
│ │ Actions: ghost/outlined btns│ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘
dart
DmCard(
  style: DmCardStyle.elevated,
  child: Column(children: [
    Image(...),
    Padding(
      padding: EdgeInsets.all(16),
      child: Column(children: [
        Text('Title', style: TextStyle(color: tokens.onSurface)),
        Text('Subtitle', style: TextStyle(color: tokens.onSurfaceVariant)),
        Row(children: [
          DmButton(variant: .ghost, child: Text('Cancel')),
          DmButton(variant: .filled, child: Text('Confirm')),
        ]),
      ]),
    ),
  ]),
)
❌ NEVER put a
filled
primary card background with
onPrimary
text for regular content cards. Primary/secondary/tertiary containers are for interactive highlights (selected state, feature callout), not default card backgrounds.
通过surface令牌实现层级结构:
卡片样式背景令牌使用场景
Flat
surface
内联内容,无需分隔
Outlined
surface
+
outlineVariant
边框
列表项、设置行
Elevated
surfaceContainerLow
标准卡片
Filled
surfaceContainerHigh
强调/分组内容
内部布局约定:
┌─────────────────────────────────┐
│ [可选媒体/图片]                  │
├─────────────────────────────────┤
│ 标题         (onSurface)       │
│ 副标题       (onSurfaceVariant)│
│                                 │
│ 正文文本     (onSurface)       │
│                                 │
│ ┌─────────────────────────────┐ │
│ │ 操作区:ghost/outlined按钮  │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘
dart
DmCard(
  style: DmCardStyle.elevated,
  child: Column(children: [
    Image(...),
    Padding(
      padding: EdgeInsets.all(16),
      child: Column(children: [
        Text('Title', style: TextStyle(color: tokens.onSurface)),
        Text('Subtitle', style: TextStyle(color: tokens.onSurfaceVariant)),
        Row(children: [
          DmButton(variant: .ghost, child: Text('Cancel')),
          DmButton(variant: .filled, child: Text('Confirm')),
        ]),
      ]),
    ),
  ]),
)
❌ 禁止 为常规内容卡片使用
filled
的primary卡片背景搭配
onPrimary
文本。Primary/secondary/tertiary容器仅用于交互高亮(选中状态、功能标注),而非默认卡片背景。

DmDivider

DmDivider

VariantTokenUse case
Default
outlineVariant
Section separation
Strong
outline
Major section breaks
变体令牌使用场景
默认
outlineVariant
区块分隔
加粗
outline
主要区块分隔

DmScaffold

DmScaffold

Responsive layout dispatch:
BreakpointNavigation style
Compact (< 600)
DmBottomNav
Medium (600–1200)
NavigationRail
(collapsed)
Expanded (> 1200)
NavigationRail
(expanded with labels)
Page body background:
surface
. Rail/side nav background:
secondary
.

响应式布局调度:
断点导航样式
紧凑屏(< 600)
DmBottomNav
中屏(600–1200)
NavigationRail
(折叠状态)
扩展屏(> 1200)
NavigationRail
(展开带标签)
页面主体背景:
surface
。侧边导航背景:
secondary

Rule 10: Component Design — Data Display (Bricks)

规则10:组件设计 — 数据展示(基础块)

DmBadge

DmBadge

Small status/count indicator. Takes
DmColorRole
.
VariantBackgroundForegroundUse case
Filledrole coloronRoleNotification count, status dot
TonalroleContaineronRoleContainerSoft label, category tag
Default:
color: error
(notification convention),
size: sm
dart
DmBadge(count: 3)                                  // red notification dot
DmBadge(label: 'New', color: .tertiary, variant: .tonal)  // soft accent tag
DmBadge(label: 'Draft', color: .neutral, variant: .tonal) // muted status
小型状态/计数指示器。接收
DmColorRole
参数。
变体背景前景使用场景
Filled角色颜色onRole通知计数、状态点
TonalroleContaineronRoleContainer柔和标签、分类标记
默认值:
color: error
(通知约定),
size: sm
dart
DmBadge(count: 3)                                  // 红色通知点
DmBadge(label: 'New', color: .tertiary, variant: .tonal)  // 柔和强调标签
DmBadge(label: 'Draft', color: .neutral, variant: .tonal) // 低强调状态

DmChip

DmChip

Selectable/filterable labels. Takes
DmColorRole
.
StateBackgroundForegroundBorder
Unselected
surface
onSurfaceVariant
outline
Selected
secondaryContainer
onSecondaryContainer
none
Disabled
surface
at 38% opacity
onSurface
at 38%
outline
at 12%
Default selection color:
secondary
— secondary containers are for selection states.
可选/可过滤的标签。接收
DmColorRole
参数。
状态背景前景边框
未选中
surface
onSurfaceVariant
outline
选中
secondaryContainer
onSecondaryContainer
禁用
surface
(38%透明度)
onSurface
(38%透明度)
outline
(12%透明度)
默认选中颜色:
secondary
— secondary容器用于选中状态。

DmAvatar

DmAvatar

VariantBackgroundForeground
With image
Initials (default)
primaryContainer
onPrimaryContainer
Initials (group variety)Cycle through
primary
/
secondary
/
tertiary
containers
Matching
onContainer
Sizes follow
DmSize
enum. Default:
md
(40dp diameter).
变体背景前景
带图片
首字母(默认)
primaryContainer
onPrimaryContainer
首字母(群组变体)循环使用
primary
/
secondary
/
tertiary
容器
对应的onContainer
尺寸遵循
DmSize
枚举。默认值:
md
(40dp直径)。

DmStat (Data Brick)

DmStat(数据块)

Statistics display block:
┌───────────────┐
│ 1,234         │  ← value: onSurface, large/bold
│ Active Users  │  ← label: onSurfaceVariant, small
│ ▲ 12.5%       │  ← trend: success or error token
└───────────────┘
ElementToken
Value
onSurface
Label
onSurfaceVariant
Positive trend
success
(or
tokens.success
)
Negative trend
error
Card background
surfaceContainerLow
(when in card)
统计信息展示块:
┌───────────────┐
│ 1,234         │  ← 数值:onSurface,大字号/加粗
│ Active Users  │  ← 标签:onSurfaceVariant,小字号
│ ▲ 12.5%       │  ← 趋势:success或error令牌
└───────────────┘
元素令牌
数值
onSurface
标签
onSurfaceVariant
正向趋势
success
(或
tokens.success
负向趋势
error
卡片背景
surfaceContainerLow
(当包含在卡片中时)

DmTable / Data Grid

DmTable / 数据网格

ElementToken
Header row bg
surfaceContainerHigh
Header text
onSurface
(bold)
Body row bg (even)
surface
Body row bg (odd)
surfaceContainerLowest
Body text
onSurface
Row hover
surfaceContainerLow
Selected row
secondaryContainer
Border/grid lines
outlineVariant
Sort indicator
primary

元素令牌
表头行背景
surfaceContainerHigh
表头文本
onSurface
(加粗)
表体行背景(偶数)
surface
表体行背景(奇数)
surfaceContainerLowest
表体文本
onSurface
行悬停
surfaceContainerLow
选中行
secondaryContainer
边框/网格线
outlineVariant
排序指示器
primary

Rule 11: Component Design — Feedback

规则11:组件设计 — 反馈类

DmAlert

DmAlert

SemanticBackgroundForegroundIcon color
Info
infoContainer
(or
surfaceContainerHigh
+ info icon)
onSurface
info
Success
successContainer
onSurface
success
Warning
warningContainer
onSurface
warning
Error
errorContainer
onErrorContainer
error
Convention: Alerts use semantic container tokens with full-width layout. For inline indicators, use
DmBadge
.
语义类型背景前景图标颜色
信息
infoContainer
(或
surfaceContainerHigh
+ 信息图标)
onSurface
info
成功
successContainer
onSurface
success
警告
warningContainer
onSurface
warning
错误
errorContainer
onErrorContainer
error
约定: Alert使用语义容器令牌和全宽布局。对于内联指示器,请使用
DmBadge

DmDialog

DmDialog

ElementToken
Scrim (backdrop)
scrim
with alpha
Dialog surface
surfaceContainerHighest
Title
onSurface
Body
onSurfaceVariant
Confirm button
DmButton(variant: .filled, color: .primary)
Cancel button
DmButton(variant: .ghost, color: .neutral)
Destructive confirm
DmButton(variant: .filled, color: .error)
元素令牌
遮罩(背景)
scrim
(带透明度)
对话框表面
surfaceContainerHighest
标题
onSurface
正文
onSurfaceVariant
确认按钮
DmButton(variant: .filled, color: .primary)
取消按钮
DmButton(variant: .ghost, color: .neutral)
破坏性确认按钮
DmButton(variant: .filled, color: .error)

DmSnackbar

DmSnackbar

Uses inverse tokens for contrast against current theme:
ElementToken
Background
inverseSurface
Text
inverseOnSurface
Action button
inversePrimary
使用inverse令牌以在当前主题下保证对比度:
元素令牌
背景
inverseSurface
文本
inverseOnSurface
操作按钮
inversePrimary

DmProgress

DmProgress

Linear and circular variants. Default color:
primary
.
VariantTrackIndicator
Default
surfaceContainerHighest
primary
With color role
roleContainer
(at low opacity)
role color
线性和圆形变体。默认颜色:
primary
变体轨道指示器
默认
surfaceContainerHighest
primary
带颜色角色
roleContainer
(低透明度)
角色颜色

DmSkeleton

DmSkeleton

Loading placeholder. Uses
surfaceContainerHigh
with shimmer animation toward
surfaceContainerLow
.

加载占位符。使用
surfaceContainerHigh
并向
surfaceContainerLow
过渡的闪烁动画。

Rule 12: Component Design — Inputs

规则12:组件设计 — 输入类

DmTextField

DmTextField

VariantIdleFocusedError
outlined
outlineVariant
border
primary
border (2px)
error
border
filled
surfaceContainerHighest
bg
primary
bottom indicator
error
indicator
underlined
outlineVariant
bottom line
primary
bottom line (2px)
error
line
ElementToken
Input text
onSurface
Placeholder/hint
onSurfaceVariant
Label (floating)
onSurfaceVariant
primary
when focused
Helper text
onSurfaceVariant
Error text
error
Prefix/suffix icon
onSurfaceVariant
Default variant:
outlined
变体空闲状态聚焦状态错误状态
outlined
outlineVariant
边框
primary
边框(2px)
error
边框
filled
surfaceContainerHighest
背景
primary
底部指示器
error
指示器
underlined
outlineVariant
底线
primary
底线(2px)
error
底线
元素令牌
输入文本
onSurface
占位符/提示文本
onSurfaceVariant
标签(浮动)
onSurfaceVariant
→ 聚焦时变为
primary
辅助文本
onSurfaceVariant
错误文本
error
前缀/后缀图标
onSurfaceVariant
默认变体:
outlined

DmCheckbox / DmSwitch / DmSlider

DmCheckbox / DmSwitch / DmSlider

StateToken
Unchecked/off
onSurfaceVariant
(border),
surface
(fill)
Checked/on
primary
(fill),
onPrimary
(checkmark)
Track (switch off)
surfaceContainerHighest
Track (switch on)
primary
at 50% →
primaryContainer
Thumb
outline
(off) →
onPrimary
(on, over primary track)
Slider active track
primary
Slider inactive track
surfaceContainerHighest
Slider thumb
primary
DisabledAll at 38% opacity

状态令牌
未选中/关闭
onSurfaceVariant
(边框),
surface
(填充)
选中/开启
primary
(填充),
onPrimary
(勾选标记)
轨道(开关关闭)
surfaceContainerHighest
轨道(开关开启)
primary
(50%透明度)→
primaryContainer
滑块
outline
(关闭)→
onPrimary
(开启,在primary轨道上)
滑块激活轨道
primary
滑块未激活轨道
surfaceContainerHighest
滑块手柄
primary
禁用所有元素38%透明度

Rule 13: Visual Design Principles

规则13:视觉设计原则

Hierarchy through token roles, not through ad-hoc colors

通过令牌角色实现层级,而非自定义颜色

Primary   → THE action (one per screen section)
Secondary → supporting actions, selection states
Tertiary  → accents, highlights, special callouts
Surface   → everything else (backgrounds, text, structure)
If you need emphasis, promote the token role — don't invent a color.
Primary   → 核心操作(每个屏幕区块一个)
Secondary → 辅助操作、选中状态
Tertiary  → 强调、高亮、特殊标注
Surface   → 其他所有元素(背景、文本、结构)
如果需要强调,提升令牌角色 — 不要自定义颜色。

Density and spacing

密度与间距

DuskMoon follows MD3 density: default padding 16dp, compact 12dp, comfortable 24dp. Widget padding follows the
DmSize
scale.
DuskMoon遵循MD3密度:默认内边距16dp,紧凑模式12dp,舒适模式24dp。Widget内边距遵循
DmSize
缩放规则。

Elevation = surface tokens, not shadows

层级 = surface令牌,而非阴影

Use
surfaceContainerLowest
surfaceContainerHighest
for visual hierarchy. Shadows (
shadow
token) are supplementary, not the primary depth cue.
dart
// ✅ Surface-token elevation
Container(color: tokens.surfaceContainerHigh)  // elevated
Container(color: tokens.surface)                // base level

// ❌ Shadow-only elevation
Container(
  decoration: BoxDecoration(
    color: tokens.surface,
    boxShadow: [BoxShadow(blurRadius: 8)],  // shadow without surface distinction
  ),
)
使用
surfaceContainerLowest
surfaceContainerHighest
实现视觉层级。阴影(
shadow
令牌)仅作为补充,而非主要深度提示。
dart
// ✅ 使用surface令牌实现层级
Container(color: tokens.surfaceContainerHigh)  // 高层级
Container(color: tokens.surface)                // 基础层级

// ❌ 仅使用阴影实现层级
Container(
  decoration: BoxDecoration(
    color: tokens.surface,
    boxShadow: [BoxShadow(blurRadius: 8)],  // 仅用阴影,无surface区分
  ),
)

Dark mode is not "invert everything"

深色模式不是“反转一切”

Each theme has its own curated token set. The codegen produces distinct values per theme. Never compute dark colors by inverting or dimming light colors at runtime.
dart
// ❌ Never compute dark variants
final darkBg = Color.lerp(tokens.surface, Colors.black, 0.3);

// ✅ Use the dark theme's own tokens
DuskmoonApp(tokens: .sunshine, darkTokens: .moonlight)  // moonlight has its own curated values

每个主题都有自己的精心设计的令牌集合。代码生成器会为每个主题生成不同的值。禁止在运行时通过反转或调暗浅色颜色来计算深色颜色。
dart
// ❌ 禁止计算深色变体
final darkBg = Color.lerp(tokens.surface, Colors.black, 0.3);

// ✅ 使用深色主题自身的令牌
DuskmoonApp(tokens: .sunshine, darkTokens: .moonlight)  // moonlight有自己的精心设计的值

Rule 14: Package Boundaries

规则14:包边界

(Architecture rules — same as above, renumbered for continuity)
(架构规则 — 与前文一致,重新编号以保持连续性)

What goes where

内容划分

PackageContainsDoes NOT contain
duskmoon_theme
DmDesignTokens
,
DmTheme
,
DmPlatformStyle
,
toMaterial()
/
toCupertino()
/
toFluent()
adapters
Any widgets, any
BuildContext
-dependent rendering
duskmoon_widgets
DuskmoonApp
,
DmAdaptiveWidget
, all
Dm*
widgets
Token definitions, theme adapters
duskmoon_theme_bloc
DmThemeBloc
,
DmThemeCubit
for runtime theme switching
Widget implementations
duskmoon_settings
Settings UI widgets built on adaptive dispatchTheme internals
duskmoon_feedback
Feedback/bug-report widgetsTheme internals
duskmoon_ui
Umbrella — re-exports all aboveNo unique code
包含内容不包含内容
duskmoon_theme
DmDesignTokens
,
DmTheme
,
DmPlatformStyle
,
toMaterial()
/
toCupertino()
/
toFluent()
适配器
任何Widget、任何依赖
BuildContext
的渲染逻辑
duskmoon_widgets
DuskmoonApp
,
DmAdaptiveWidget
, 所有
Dm*
Widget
令牌定义、主题适配器
duskmoon_theme_bloc
DmThemeBloc
,
DmThemeCubit
(用于运行时主题切换)
Widget实现
duskmoon_settings
基于自适应调度的设置UI Widget主题内部逻辑
duskmoon_feedback
反馈/错误报告Widget主题内部逻辑
duskmoon_ui
聚合包 — 重新导出所有上述包无独有代码

❌ NEVER add duskmoon_widgets as dependency of duskmoon_theme

❌ 禁止将duskmoon_widgets作为duskmoon_theme的依赖

This creates a circular dependency. If
duskmoon_theme
needs to reference a widget concept, use an abstract interface or callback, not a concrete widget import.

这会导致循环依赖。如果
duskmoon_theme
需要引用Widget相关概念,请使用抽象接口或回调,而非具体的Widget导入。

Rule 15: Code Engine Integration

规则15:代码引擎集成

duskmoon_code_engine
has zero dependency on
duskmoon_theme
. The theme adapter lives as an extension method in
duskmoon_theme
:
dart
// In duskmoon_theme — NOT in duskmoon_code_engine
extension DmCodeEngineTheme on DmDesignTokens {
  CodeEditorTheme toCodeEditorTheme() => CodeEditorTheme(
    background: surface,
    foreground: onSurface,
    // ...
  );
}

duskmoon_code_engine
duskmoon_theme
零依赖。主题适配器作为扩展方法存在于
duskmoon_theme
中:
dart
// 在duskmoon_theme中 — 不在duskmoon_code_engine中
extension DmCodeEngineTheme on DmDesignTokens {
  CodeEditorTheme toCodeEditorTheme() => CodeEditorTheme(
    background: surface,
    foreground: onSurface,
    // ...
  );
}

Rule 16: Codegen Pipeline

规则16:代码生成流程

duskmoon-dev/design YAML
  → Bun/TypeScript emitter
    → CSS (duskmoonui consumption)
    → TypeScript (duskmoonui/duskmoon-elements)
    → Dart (flutter_duskmoon_ui — committed, CI never needs Bun)
    → JSON (documentation/tooling)
Generated Dart files are committed to git. CI must never require Bun or Node to build the Flutter packages.
duskmoon-dev/design YAML
  → Bun/TypeScript生成器
    → CSS(供duskmoonui使用)
    → TypeScript(供duskmoonui/duskmoon-elements使用)
    → Dart(flutter_duskmoon_ui — 已提交到Git,CI无需Bun)
    → JSON(文档/工具)
生成的Dart文件已提交到Git。 CI构建Flutter包时绝不需要Bun或Node。

❌ NEVER hand-edit generated files

❌ 禁止手动编辑生成的文件

Files in
packages/duskmoon_theme/lib/src/generated/
are produced by codegen. Edit the YAML source in
duskmoon-dev/design
and re-run the pipeline.

packages/duskmoon_theme/lib/src/generated/
目录下的文件由代码生成器生成。请编辑
duskmoon-dev/design
中的YAML源文件并重新运行生成流程。

Rule 17: Accessibility

规则17:无障碍访问

  • All color pairings must meet WCAG 2.1 AA contrast (4.5:1 normal text, 3:1 large text)
  • Every interactive Dm* widget must support keyboard navigation
  • Semantic labels required on all icon-only buttons
  • Focus indicators must be visible on all themes

  • 所有颜色配对必须符合WCAG 2.1 AA对比度标准(普通文本4.5:1,大文本3:1)
  • 所有交互式Dm* Widget必须支持键盘导航
  • 所有仅图标按钮必须设置语义标签
  • 在所有主题下,焦点指示器必须可见

Rule 18: Testing Patterns

规则18:测试模式

Widget tests must verify all three platforms

Widget测试必须覆盖所有三个平台

dart
for (final style in DmPlatformStyle.values) {
  testWidgets('DmButton renders on $style', (tester) async {
    await tester.pumpWidget(
      DuskmoonApp(
        tokens: DmDesignTokens.sunshine,
        platformStyle: style,
        home: const DmButton(label: 'Test'),
      ),
    );
    expect(find.text('Test'), findsOneWidget);
  });
}
dart
for (final style in DmPlatformStyle.values) {
  testWidgets('DmButton renders on $style', (tester) async {
    await tester.pumpWidget(
      DuskmoonApp(
        tokens: DmDesignTokens.sunshine,
        platformStyle: style,
        home: const DmButton(label: 'Test'),
      ),
    );
    expect(find.text('Test'), findsOneWidget);
  });
}

Theme tests must verify token resolution

主题测试必须验证令牌解析

dart
testWidgets('DmTheme.of resolves correct tokens', (tester) async {
  late DmDesignTokens resolved;
  await tester.pumpWidget(
    DuskmoonApp(
      tokens: DmDesignTokens.sunshine,
      home: Builder(builder: (context) {
        resolved = DmTheme.of(context).tokens;
        return const SizedBox();
      }),
    ),
  );
  expect(resolved.primary, equals(DmDesignTokens.sunshine.primary));
});

dart
testWidgets('DmTheme.of resolves correct tokens', (tester) async {
  late DmDesignTokens resolved;
  await tester.pumpWidget(
    DuskmoonApp(
      tokens: DmDesignTokens.sunshine,
      home: Builder(builder: (context) {
        resolved = DmTheme.of(context).tokens;
        return const SizedBox();
      }),
    ),
  );
  expect(resolved.primary, equals(DmDesignTokens.sunshine.primary));
});

Quick Reference: Anti-Patterns

速查:反模式

❌ Don't✅ Do
MaterialApp(theme: tokens.toMaterial())
as root
DuskmoonApp(tokens: ...)
as root
DmDesignTokens.sunshine.primary
in widget build
DmTheme.of(context).tokens.primary
Color(0xFF...)
or
Colors.blue
tokens.primary
/
tokens.secondary
Platform.isIOS
for widget dispatch
Extend
DmAdaptiveWidget
Hand-edit
generated/
Dart files
Edit YAML source, re-run codegen
Import
duskmoon_widgets
from
duskmoon_theme
Keep dependency direction strict
Put theme adapter in
duskmoon_code_engine
Extension method in
duskmoon_theme
primaryContainer
bg +
onSurface
text
Pair
primaryContainer
+
onPrimaryContainer
AppBar background =
surface
AppBar background =
primary
(DuskMoon convention)
Drawer/side menu bg =
primary
Drawer/side menu bg =
secondary
(navigation chrome distinction)
Card default bg =
primaryContainer
Card bg =
surfaceContainerLow
/
surface
+ outline
Shadows as primary depth cueSurface container tokens for elevation hierarchy
Color.lerp(x, Colors.black, 0.3)
for dark mode
Use the dark theme's own curated tokens
Multiple FABs on one screenOne FAB max; use
DmActionList
for multiple
Icon button without
semanticLabel
Always provide
semanticLabel
on
DmIconButton
Selection highlight with
primary
Selection states use
secondaryContainer
Inventing colors outside the token systemPromote token role (primary→secondary→tertiary)

❌ 禁止做法✅ 正确做法
使用
MaterialApp(theme: tokens.toMaterial())
作为根节点
使用
DuskmoonApp(tokens: ...)
作为根节点
在Widget构建中使用
DmDesignTokens.sunshine.primary
使用
DmTheme.of(context).tokens.primary
使用
Color(0xFF...)
Colors.blue
使用
tokens.primary
/
tokens.secondary
使用
Platform.isIOS
进行Widget调度
继承
DmAdaptiveWidget
手动编辑
generated/
目录下的Dart文件
编辑YAML源文件,重新运行代码生成
duskmoon_theme
导入
duskmoon_widgets
严格保持依赖方向
将主题适配器放在
duskmoon_code_engine
duskmoon_theme
中使用扩展方法
使用
primaryContainer
背景 +
onSurface
文本
配对使用
primaryContainer
+
onPrimaryContainer
AppBar背景使用
surface
AppBar背景使用
primary
(DuskMoon约定)
Drawer/侧边菜单背景使用
primary
Drawer/侧边菜单背景使用
secondary
(区分导航控件)
卡片默认背景使用
primaryContainer
卡片背景使用
surfaceContainerLow
/
surface
+ 边框
仅使用阴影作为主要深度提示使用surface容器令牌实现层级结构
使用
Color.lerp(x, Colors.black, 0.3)
实现深色模式
使用深色主题自身的精心设计的令牌
一个屏幕上使用多个FAB最多一个FAB;使用
DmActionList
处理多个操作
图标按钮不设置
semanticLabel
始终为
DmIconButton
提供
semanticLabel
使用
primary
作为选中高亮
使用
secondaryContainer
作为选中状态
在令牌系统外自定义颜色提升令牌角色(primary→secondary→tertiary)

Checklist for Code Review

代码审查检查清单

When reviewing Flutter code that uses DuskMoon UI, verify:
Architecture:
  • App root is
    DuskmoonApp
    , not
    MaterialApp
    /
    CupertinoApp
    directly
  • Package imports flow downstream only (theme → widgets → settings)
  • No generated files were hand-edited
  • No
    duskmoon_theme
    dependency in
    duskmoon_code_engine
  • Adaptive widgets extend
    DmAdaptiveWidget
    , not manual platform checks
Color & Tokens:
  • All color values come from
    DmTheme.of(context).tokens
    , not hardcoded
  • Background/foreground token pairs match (primary↔onPrimary, etc.)
  • No
    Colors.*
    or
    Color(0x...)
    literals
  • Dark mode uses separate theme tokens, no runtime color computation
Component Design:
  • Buttons use
    DmColorRole
    convention (primary=main CTA, error=destructive, etc.)
  • AppBar and BottomNav use
    primary
    /
    onPrimary
    defaults
  • Drawer and side menu use
    secondary
    /
    onSecondary
    defaults
  • Cards use surface container tokens, not primary/secondary containers for default bg
  • Selection states use
    secondaryContainer
    tokens
  • Surface elevation via container tokens, not shadow-only
  • Maximum one FAB per screen section
  • DmActionList
    for multiple actions, not ad-hoc button rows
  • Snackbar uses inverse tokens
  • Dialog scrim uses
    scrim
    token with alpha
Accessibility:
  • Semantic labels on all icon-only buttons
  • Focus indicators visible on all themes
  • Widget tests cover all three
    DmPlatformStyle
    values
  • WCAG AA contrast on all bg/fg pairings
审查使用DuskMoon UI的Flutter代码时,请验证:
架构:
  • 应用根节点是
    DuskmoonApp
    ,而非直接使用
    MaterialApp
    /
    CupertinoApp
  • 包导入仅向下游流动(theme → widgets → settings)
  • 没有手动编辑生成的文件
  • duskmoon_code_engine
    中没有
    duskmoon_theme
    依赖
  • 自适应Widget继承自
    DmAdaptiveWidget
    ,而非手动判断平台
颜色与令牌:
  • 所有颜色值均来自
    DmTheme.of(context).tokens
    ,而非硬编码
  • 背景/前景令牌配对正确(primary↔onPrimary等)
  • 没有
    Colors.*
    Color(0x...)
    字面量
  • 深色模式使用独立的主题令牌,无运行时颜色计算
组件设计:
  • 按钮遵循
    DmColorRole
    约定(primary=主要CTA,error=破坏性操作等)
  • AppBar和BottomNav使用
    primary
    /
    onPrimary
    默认值
  • Drawer和侧边菜单使用
    secondary
    /
    onSecondary
    默认值
  • 卡片使用surface容器令牌,默认背景不使用primary/secondary容器
  • 选中状态使用
    secondaryContainer
    令牌
  • 使用容器令牌实现Surface层级,而非仅依赖阴影
  • 每个屏幕区块最多一个FAB
  • 使用
    DmActionList
    处理多个操作,而非自定义按钮行
  • Snackbar使用inverse令牌
  • Dialog遮罩使用带透明度的
    scrim
    令牌
无障碍访问:
  • 所有仅图标按钮都有语义标签
  • 在所有主题下焦点指示器可见
  • Widget测试覆盖所有三个
    DmPlatformStyle
  • 所有背景/前景配对符合WCAG AA对比度标准