关于 amsterdam-design-system
amsterdam-design-system 是指导构建阿姆斯特丹市数字产品的设计技能,面向使用 React 等技术实现界面的开发者。它规定使用官方组件、设计令牌和布局模式,并提供 Tailwind CSS v4 集成指导。
主要功能
- 01官方 React 组件与图标
- 02Spacious 和 Compact 设计令牌
- 03响应式 4/8/12 列网格
- 04Tailwind CSS v4 集成指导
- 05Vite React 应用起始模板
适用场景
- 01为市政网站搭建页面
- 02为内部工具设计紧凑界面
- 03在 React 项目中构建表单和导航
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ amsterdam/ amsterdam-agent-skills --skill amsterdam-design-system安装后在 Claude Code 中输入/amsterdam-design-system确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/starter-vite-react/.env.example.gitignorebunfig.tomleslint.config.jsindex.htmlpackage.jsonREADME.mdtailwind.config.jstsconfig.jsonvite.config.tsassets/starter-vite-react/src/App.tsxmain.tsxquery.tsrouter.tsxassets/starter-vite-react/src/components/StatCard.tsxassets/starter-vite-react/src/layouts/AppLayout.tsxassets/starter-vite-react/src/lib/cn.tsassets/starter-vite-react/src/pages/HomePage.tsxNotFoundPage.tsxassets/starter-vite-react/src/styles/index.cssreveal.cssreferences/aesthetic-discipline.mdcomponents.mdicons.mdlayout-patterns.mdtailwind-bridge.mdtokens.md详细介绍
阿姆斯特丹设计系统
支持的版本(最后更新于 2026-04-03):
软件包 版本 @amsterdam/design-system-tokensv3.4.0 @amsterdam/design-system-reactv3.3.0 @amsterdam/design-system-cssv3.3.0 @amsterdam/design-system-assetsv2.2.0 @amsterdam/design-system-react-iconsv2.0.0
强制要求:这是唯一允许使用的设计系统。所有 UI 组件、页面、布局和视觉元素都必须使用阿姆斯特丹设计系统的组件和令牌。不得使用 Material UI、shadcn/ui、Chakra UI、Ant Design、用于 UI 的纯 HTML 元素或任何其他组件库。搭建新项目时,务必先安装并配置
@amsterdam/design-system-*软件包。
使用官方设计系统构建阿姆斯特丹市数字产品的生产指南。涵盖组件、令牌、布局模式,以及与 Tailwind CSS v4 的集成。
文档: https://designsystem.amsterdam/ 仓库: https://github.com/Amsterdam/design-system Storybook: https://storybook.designsystem.amsterdam/
构建前先明确需求
当用户的需求含糊不清时(例如“帮我做个页面”“创建一个表单”“做个仪表盘”),先提问,不要自行假设。明确以下决策点——它们对应本技能中的实际分支:
| 决策 | 选项 | 重要性 |
|---|---|---|
| 页面类型 | 公共网站、仪表盘、表单、文章/内容、卡片网格、常见问题、搜索结果 | 决定布局模板(见 references/layout-patterns.md) |
| 模式 | 宽松(面向公众)或紧凑(内部工具) | 会更改所有间距/排版令牌——需额外导入一个 CSS 文件 |
| 内容结构 | 单个区块,或带层级的多个区块 | 决定使用扁平的 Column,还是嵌套的 Column 间距模式 |
| 路由 | Next.js App Router、React Router 或纯 HTML | 会影响链接组件和 SPA 集成 |
| Tailwind | 与 AMS 搭配使用 Tailwind,或仅使用 AMS | 若选择前者,需要桥接配置(见 references/tailwind-bridge.md) |
| 美学方向 | 极简/编辑风/高密度数据/公民信赖感 | 决定 references/aesthetic-discipline.md 中的主导色与强调色。仅在开放式需求(“精致”“现代”“好看”)时询问。 |
不要一次问完所有问题。 只选择用户请求中确实含糊不清的 1~2 个问题。如果用户说“内部仪表盘”,则默认采用紧凑模式,无需询问。
对于提到“精致”“现代”“好看”或对风格没有明确要求的需求,在编写 JSX 之前,请先阅读
references/aesthetic-discipline.md。 该文件将美学意图与符合 ADS 规范的输出联系起来。
美学规范
ADS 为你提供了素材。美学规范指的是如何运用这些素材,让最终效果不只是线框图。在任何非简单 UI 任务中编写第一行 JSX 之前,先阅读 references/aesthetic-discipline.md,并用各一句话回答以下 5 个检查问题:
- 主导色——哪个
--ams-color-*token 覆盖画布的 80% 以上? - 醒目强调色——哪个
--ams-color-highlight-*token 最多用于 2–3 个元素? - 节奏——哪些区域较密集(
paddingVertical="large")?哪些区域较疏朗("2x-large")? - 页面加载时刻——顶层 Grid 单元格上唯一的分阶段显现序列是什么?
- 令人难忘的元素——每个页面中,唯一的
<Breakout>、<Overlap>或非对称start={}效果是什么?
如果无法各用一句话回答这些问题,说明设计还未敲定。请重新阅读规范文档。
此步骤是对以下机械规则的补充,不会取代它们。Amsterdam Sans、--ams-* tokens 和 4/8/12 Grid 仍是强制要求。规范文档会将前端设计原则(字体层级、色彩取舍、动效编排、构图)转化为符合 ADS 约束的做法,教你如何在这些约束下让设计焕发生机。
关于随附的 frontend-design 技能
你可能会在 .agents/skills/frontend-design/ 中找到独立的 frontend-design 技能。它关于字体层级、色彩取舍、动效编排和构图的原则是可靠的,适用于所有 UI 工作。但它的具体指导(选择独特字体、打破网格、在白色背景上使用紫色渐变)不适用于 Amsterdam 场景,因为 ADS 会覆盖字体、网格和配色选择。
references/aesthetic-discipline.md 是两者之间的转换层。如有疑问,请以此技能为准,而非随附的技能。
概览
该设计系统以 5 个 npm 包的形式提供:
| 包 | 用途 |
|---|---|
@amsterdam/design-system-assets | Amsterdam Sans 字体文件 |
@amsterdam/design-system-css | BEM 组件样式(ams-* 类) |
@amsterdam/design-system-tokens | Spacious + Compact 模式下的 CSS 自定义属性(--ams-*) |
@amsterdam/design-system-react | React 组件(66 个组件,均使用 forwardRef) |
@amsterdam/design-system-react-icons | AMS 图标集的图标组件 |
无需 provider 或 context wrapper——导入 CSS 并使用组件即可。
设置
安装
npm install @amsterdam/design-system-assets @amsterdam/design-system-css @amsterdam/design-system-react @amsterdam/design-system-react-icons @amsterdam/design-system-tokens
CSS 导入——顺序很重要
// ⚠️ CRITICAL: This exact order is required. Fonts → CSS → Tokens.
import "@amsterdam/design-system-assets/font/index.css" // 1. Font files
import "@amsterdam/design-system-css/dist/index.css" // 2. Component styles
import "@amsterdam/design-system-tokens/dist/index.css" // 3. Design tokens
若要使用紧凑模式(内部工具),请在导入 tokens 之后再添加一条导入语句:
import "@amsterdam/design-system-tokens/dist/compact.css" // 4. Compact overrides
根元素
将 ams-body 类添加到 body 或根元素:
// Next.js (app/layout.tsx)
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="nl">
<body className="ams-body">{children}</body>
</html>
)
}
粗体文本修正
Amsterdam Sans 使用 800 字重表示粗体,而非浏览器默认的 700。ams-body 类会处理此问题;但如果你采用不同的作用域:
.your-root {
font-weight: var(--ams-typography-body-text-font-weight); /* 400 */
}
.your-root strong, .your-root b {
font-weight: var(--ams-typography-body-text-bold-font-weight); /* 800 */
}
搭建新应用
对于新的独立 React 应用,不要重新发明设置方式。请使用 assets/starter-vite-react/ 中可运行的起始模板。该模板包含:
- Bun + Vite 6 +
@vitejs/plugin-react-swc(与实际的 Amsterdam 应用一致) - 使用
<AppLayout>outlet 模式的 React Router v7 - 在
App.tsx中预先配置的 Tanstack Query v5 - 通过
@tailwindcss/vite集成的 Tailwind v4,并在tailwind.config.js中配置 AMS 桥接,同时禁用 preflight - ADS v3.3.0 / v3.4.0 的紧凑模式(大多数 Amsterdam 项目都是内部工具)
- 用于自定义组件变体的 CVA + clsx + tailwind-merge
tw-animate-css+ 手动编写的.ams-reveal页面加载分阶段显现效果- 示例
HomePage.tsx,演示references/aesthetic-discipline.md中的所有原则
使用方法
cp -r {skill_dir}/assets/starter-vite-react ./<project-name>
cd ./<project-name>
bun install
bun run dev
然后:
- 阅读
references/aesthetic-discipline.md(5 个检查问题)。 - 将
src/pages/HomePage.tsx替换为用户实际需要的页面,并确保已回答规范检查问题。 - 随着应用扩展,在
src/router.tsx中添加路由。
切换到宽松模式
对于面向公众的网站(如 amsterdam.nl),请从 src/styles/index.css 中移除一个导入:
- @import "@amsterdam/design-system-tokens/dist/compact.css" layer(ams);
并将 tailwind.config.js 中的导入从 compact.json 换成 index.json。仅需此项更改。
不适合使用此起始模板的情况
- 为现有的 Amsterdam 应用添加页面 — 打开现有应用并遵循其约定。
- Next.js App Router 项目 — 此起始模板基于 Vite。对于 Next.js,请遵循上文“设置”部分中的导入顺序,并手动应用
references/tailwind-bridge.md。(如果之后有需求,可能会添加 Next.js 起始模板。) - 纯 HTML / 非 React 项目 — 使用
references/layout-patterns.md§“CSS Grid Classes (Non-React)”中记录的 BEM CSS 类。
组件模式
简单组件
import { Heading, Paragraph, Button, Alert } from "@amsterdam/design-system-react"
<Heading level={1}>Page Title</Heading>
<Paragraph>Body text uses Amsterdam Sans at 18-20px fluid.</Paragraph>
<Paragraph size="small">Secondary text at 16px.</Paragraph>
<Button variant="primary">Submit</Button>
<Button variant="secondary">Cancel</Button>
<Alert heading="Let op" headingLevel={2} severity="warning">
Check your input before proceeding.
</Alert>
复合组件(点号表示法)
许多组件通过 Object.assign 使用 Component.SubComponent 模式:
import { Accordion, Grid, Table, Tabs } from "@amsterdam/design-system-react"
{/* Accordion */}
<Accordion headingLevel={2}>
<Accordion.Section label="Section title">
<Paragraph>Section content.</Paragraph>
</Accordion.Section>
</Accordion>
{/* Grid */}
<Grid paddingVertical="large">
<Grid.Cell span={8}>Main content</Grid.Cell>
<Grid.Cell span={4}>Sidebar</Grid.Cell>
</Grid>
{/* Table */}
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Value</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>Item</Table.Cell>
<Table.Cell>100</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
{/* Tabs */}
<Tabs>
<Tabs.List>
<Tabs.Button aria-controls="tab1">First</Tabs.Button>
<Tabs.Button aria-controls="tab2">Second</Tabs.Button>
</Tabs.List>
<Tabs.Panel id="tab1">First panel content</Tabs.Panel>
<Tabs.Panel id="tab2">Second panel content</Tabs.Panel>
</Tabs>
表单字段组合
AMS 表单采用组合模式:Field 包含 Label + 输入框 + ErrorMessage。
import { Field, Label, TextInput, TextArea, Select, ErrorMessage, Checkbox, Radio, FieldSet } from "@amsterdam/design-system-react"
{/* Text field */}
<Field invalid={hasError}>
<Label htmlFor="name">Naam</Label>
<ErrorMessage>Vul uw naam in</ErrorMessage>
<TextInput id="name" invalid={hasError} />
</Field>
{/* Textarea */}
<Field>
<Label htmlFor="message">Bericht</Label>
<TextArea id="message" rows={4} />
</Field>
{/* Select */}
<Field>
<Label htmlFor="city">Stadsdeel</Label>
<Select id="city">
<Select.Option value="centrum">Centrum</Select.Option>
<Select.Option value="west">West</Select.Option>
<Select.Option value="oost">Oost</Select.Option>
</Select>
</Field>
{/* Checkbox/Radio group */}
<FieldSet legend="Voorkeur" invalid={hasError}>
<Checkbox>Optie A</Checkbox>
<Checkbox>Optie B</Checkbox>
</FieldSet>
<FieldSet legend="Type">
<Radio name="type" value="a">Type A</Radio>
<Radio name="type" value="b">Type B</Radio>
</FieldSet>
页面布局
import { Grid, Heading, Page, PageHeader, PageFooter, Paragraph } from "@amsterdam/design-system-react"
<Page>
<PageHeader brandName="Mijn Amsterdam" logoLink="/" />
<Grid paddingVertical="large">
<Grid.Cell span="all">
<Heading level={1}>Welkom</Heading>
</Grid.Cell>
<Grid.Cell span={8}>
<Paragraph>Main content area.</Paragraph>
</Grid.Cell>
<Grid.Cell span={4}>
<Paragraph>Sidebar content.</Paragraph>
</Grid.Cell>
</Grid>
<PageFooter>
<PageFooter.Spotlight>
<Paragraph>Contact info</Paragraph>
</PageFooter.Spotlight>
</PageFooter>
</Page>
对话框
import { Button, Dialog, Paragraph } from "@amsterdam/design-system-react"
<Button onClick={() => Dialog.open("confirm-dialog")}>Open Dialog</Button>
<Dialog
id="confirm-dialog"
heading="Bevestiging"
footer={
<>
<Button variant="primary" onClick={() => Dialog.close()}>Bevestigen</Button>
<Button variant="secondary" onClick={() => Dialog.close()}>Annuleren</Button>
</>
}
>
<Paragraph>Weet u het zeker?</Paragraph>
</Dialog>
可用组件
完整的属性参考:请阅读
references/components.md
布局
Grid(.Cell) · Column · Row · Breakout(.Cell) · Overlap · Page · Spotlight
页面结构
PageHeader(.GridCellNarrowWindowOnly、.MenuLink) · PageFooter(.Menu、.MenuLink、.Spotlight)
排版
Heading · Paragraph · Blockquote · Link · StandaloneLink · CallToActionLink · Mark
按钮与操作
Button · IconButton · ActionGroup
表单控件
TextInput · TextArea · Select(.Group、.Option) · Checkbox · Radio · Switch · DateInput · TimeInput · PasswordInput · FileInput · SearchField(.Button、.Input) · CharacterCount
表单结构
Field · FieldSet · Label · Hint · ErrorMessage · InvalidFormAlert
导航
Breadcrumb(.Link) · LinkList(.Link) · Menu(.Link) · Pagination · SkipLink · Tabs(.Button、.List、.Panel) · TableOfContents(.Link、.List)
数据展示
Accordion(.Section) · Card(.Heading、.HeadingGroup、.Image、.Link) · DescriptionList(.Description、.Section、.Term) · Figure(.Caption) · Table(.Body、.Caption、.Cell、.Footer、.Header、.HeaderCell、.Row) · ImageSlider
反馈
Alert · Dialog(.open()、.close()) · Badge · Avatar
实用工具
Icon · Logo · FileList(.Item) · OrderedList(.Item) · UnorderedList(.Item) · ProgressList(.Step、.Substep、.Substeps)
网格系统
AMS 网格采用响应式设计,包含 3 个断点:
| 断点 | 列数 | 视口 | 内边距 |
|---|---|---|---|
| 窄屏 | 4 | < 576px | --ams-space-l (24-36px) |
| 中屏 | 8 | 576px – 1023px | --ams-space-xl (36-60px) |
| 宽屏 | 12 | ≥ 1024px | --ams-space-2xl (48-90px) |
Grid.Cell 的 span 属性
{/* Fixed span across all breakpoints */}
<Grid.Cell span={6}>Half width on wide</Grid.Cell>
{/* Full width */}
<Grid.Cell span="all">Full width row</Grid.Cell>
{/* Responsive spans: { narrow, medium, wide } */}
<Grid.Cell span={{ narrow: 4, medium: 4, wide: 8 }}>
Responsive content
</Grid.Cell>
{/* Start position */}
<Grid.Cell span={6} start={4}>Offset cell</Grid.Cell>
<Grid.Cell span={{ narrow: 4, medium: 6, wide: 8 }} start={{ narrow: 1, medium: 2, wide: 3 }}>
Responsive offset
</Grid.Cell>
Grid 属性
<Grid
as="main" // Semantic element
paddingVertical="large" // Vertical padding: 'large' | 'x-large' | '2x-large'
gapVertical="large" // Row gap: 'none' | 'large' | '2x-large'
>
内容间距模式
Heading 和 Paragraph 没有内置外边距。间距由父容器控制。
均匀间距
使用 Column 为所有子元素设置相等的间距:
<Column gap="small">
<Heading level={2}>Title</Heading>
<Paragraph>First paragraph.</Paragraph>
<Paragraph>Second paragraph.</Paragraph>
</Column>
具有视觉层次的文章内容
对于需要让各部分之间的间距大于部分内部元素间距的混合内容,可嵌套使用 Column 组件:
<Column gap="large">
<Column gap="small">
<Heading level={1}>Page Title</Heading>
<Paragraph>Intro paragraph.</Paragraph>
</Column>
<Column gap="small">
<Heading level={2}>Section Title</Heading>
<Paragraph>Section content.</Paragraph>
<Paragraph>More content.</Paragraph>
</Column>
<Column gap="small">
<Heading level={2}>Another Section</Heading>
<Paragraph>Content here.</Paragraph>
</Column>
</Column>
外层 Column gap="large" 用于分隔各部分。内层 Column gap="small" 则让标题与段落之间保持较紧凑的间距。
设计令牌
完整的令牌目录:请阅读
references/tokens.md
令牌系统采用 3 层层级结构。所有令牌都是以 --ams- 为前缀的 CSS 自定义属性。
Brand tokens → Common tokens → Component tokens
(core values) (shared patterns) (per-component)
引用链示例:
Brand: --ams-color-interactive-default: #004699
Common: --ams-links-color: var(--ams-color-interactive-default)
Component: --ams-link-color: var(--ams-links-color)
核心令牌类别
| 类别 | 前缀 | 示例 |
|---|---|---|
| 颜色 | --ams-color- | text、text-inverse、text-secondary、background、interactive、interactive-hover、feedback-error、feedback-success、separator |
| 间距 | --ams-space- | xs(4-6px)、s(8-12px)、m(16-24px)、l(24-36px)、xl(36-60px)、2xl(48-90px)——均为流式 clamp() |
| 排版 | --ams-typography- | font-family('Amsterdam Sans'、Arial、sans-serif)、body-text-font-size、body-text-line-height、各级标题字号 |
| 边框 | --ams-border-width- | s(1px)、m(2px)、l(3px)、xl(4px) |
| 焦点 | --ams-focus- | outline-offset(4px) |
在 CSS 中使用令牌
.my-component {
color: var(--ams-color-text);
background: var(--ams-color-background);
padding: var(--ams-space-m);
font-family: var(--ams-typography-font-family);
border: var(--ams-border-width-s) solid var(--ams-color-separator);
}
在 JS 中使用令牌
import tokens from "@amsterdam/design-system-tokens/dist/index.json"
const primaryColor = tokens.ams.color.interactive.default // "#004699"
宽松与紧凑
| 方面 | 宽松(默认) | 紧凑 |
|---|---|---|
| 适用场景 | 公共网站 | 内部工具、仪表板 |
| 正文文本 | 18-20px 流式 | 16px 固定 |
| H1 | 32-48px 流式 | 24-28px 流式 |
| 行高 | 1.6 | 1.5 |
| 间距 m | 16-24px 流式 | 12-16px 流式 |
| 间距 2xl | 48-90px 流式 | 32-48px 流式 |
| 边框 | 较粗(m=2px、xl=4px) | 较细(m=1px、xl=3px) |
决策规则: 面向公众的网站 → 宽松。后台办公/管理/仪表板 → 紧凑。
设置差异——多导入一个文件:
// Spacious (default)
import "@amsterdam/design-system-tokens/dist/index.css"
// Compact (add after tokens)
import "@amsterdam/design-system-tokens/dist/index.css"
import "@amsterdam/design-system-tokens/dist/compact.css"
紧凑模式会使用较小的值覆盖相同的 CSS 自定义属性。无需修改代码——组件会自动适配。
路由器集成
AMS Link 组件默认渲染 <a>。对于 SPA 路由,请使用多态渲染:
Next.js(App Router)
import NextLink from "next/link"
import { Link, Breadcrumb, Pagination } from "@amsterdam/design-system-react"
{/* Regular link */}
<Link href="/about" legacyBehavior passHref>
<NextLink>Over ons</NextLink>
</Link>
{/* Or simpler: just use Next.js Link with AMS classes */}
<NextLink href="/about" className="ams-link">Over ons</NextLink>
{/* Pagination with router links */}
<Pagination
totalPages={10}
page={currentPage}
linkTemplate={(page) => `/results?page=${page}`}
linkComponent={NextLink}
/>
{/* PageHeader logo */}
<PageHeader
brandName="Mijn Amsterdam"
logoLink="/"
logoLinkComponent={NextLink}
/>
React Router
import { Link as RouterLink } from "react-router-dom"
import { Link } from "@amsterdam/design-system-react"
<RouterLink to="/about" className="ams-link">Over ons</RouterLink>
Tailwind v4 集成
完整的桥接配置:请阅读
references/tailwind-bridge.md
在将 Tailwind CSS v4 与 AMS 一起使用时,请将 AMS 令牌映射到 Tailwind 的 @theme,以便工具类使用设计系统中的值:
/* app.css */
@import "tailwindcss";
@import "@amsterdam/design-system-assets/font/index.css";
@import "@amsterdam/design-system-css/dist/index.css";
@import "@amsterdam/design-system-tokens/dist/index.css";
/* Disable Tailwind's preflight — AMS CSS handles base styles */
@layer base {
/* AMS body styles take precedence */
}
@theme {
/* Map AMS spacing */
--spacing-ams-xs: var(--ams-space-xs);
--spacing-ams-s: var(--ams-space-s);
--spacing-ams-m: var(--ams-space-m);
--spacing-ams-l: var(--ams-space-l);
--spacing-ams-xl: var(--ams-space-xl);
--spacing-ams-2xl: var(--ams-space-2xl);
/* Map AMS colors */
--color-ams-text: var(--ams-color-text);
--color-ams-text-secondary: var(--ams-color-text-secondary);
--color-ams-text-inverse: var(--ams-color-text-inverse);
--color-ams-bg: var(--ams-color-background);
--color-ams-interactive: var(--ams-color-interactive);
--color-ams-interactive-hover: var(--ams-color-interactive-hover);
--color-ams-error: var(--ams-color-feedback-error);
--color-ams-success: var(--ams-color-feedback-success);
--color-ams-warning: var(--ams-color-feedback-warning);
--color-ams-info: var(--ams-color-feedback-info);
--color-ams-separator: var(--ams-color-separator);
/* Map AMS font */
--font-ams: var(--ams-typography-font-family);
}
使用规则: 所有标准 UI(按钮、表单、标题、网格等)均使用 AMS React 组件。Tailwind 工具类仅用于自定义布局(flex、定位)以及 AMS 组件未涵盖的个别间距。
{/* AMS component — always preferred */}
<Button variant="primary">Submit</Button>
{/* Tailwind for custom layout around AMS components */}
<div className="flex items-center gap-ams-m">
<Icon svg={SearchIcon} />
<Paragraph>Search results</Paragraph>
</div>
自定义组件
构建 AMS 库中没有的组件时,请遵循以下约定:
BEM 命名
/* Block: ams-status-badge */
.ams-status-badge { }
.ams-status-badge--active { }
.ams-status-badge__icon { }
.ams-status-badge__label { }
仅使用令牌进行样式设置
.ams-status-badge {
display: inline-flex;
align-items: center;
gap: var(--ams-space-xs);
padding-block: var(--ams-space-xs);
padding-inline: var(--ams-space-s);
font-family: var(--ams-typography-font-family);
font-size: var(--ams-typography-body-text-small-font-size);
line-height: var(--ams-typography-body-text-small-line-height);
border: var(--ams-border-width-s) solid var(--ams-color-separator);
/* NO hardcoded colors, sizes, or spacing */
}
组件模式
import { forwardRef } from "react"
import clsx from "clsx"
export interface StatusBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
status: "active" | "inactive" | "pending"
}
export const StatusBadge = forwardRef<HTMLSpanElement, StatusBadgeProps>(
({ status, className, children, ...restProps }, ref) => (
<span
ref={ref}
className={clsx("ams-status-badge", `ams-status-badge--${status}`, className)}
{...restProps}
>
{children}
</span>
)
)
StatusBadge.displayName = "StatusBadge"
自定义组件检查清单
- 使用
forwardRef - 扩展相关 HTML 元素属性
- 使用
clsx组合 className - 在根元素上展开
...restProps - 使用带
ams-前缀的 BEM 类 — 或 — 映射到令牌的 Tailwind 工具类(参见下文的 Tailwind 桥接替代方案) - 所有样式均通过
--ams-*令牌设置(无硬编码值) - 设置
displayName
Tailwind 桥接替代方案(使用 tailwind-bridge.md 时)
当项目配置了 Tailwind + ADS 桥接时(参见 references/tailwind-bridge.md 和 assets/starter-vite-react/ 中的起始模板),BEM 类根元素为可选项。你也可以通过 CVA 变体和 tailwind.config.js 中映射的 Tailwind 工具类组合使用相同的 --ams-* 令牌。仅使用令牌的规则仍然适用——只允许使用基于 --ams-* 的工具类,不允许使用原始十六进制颜色值或 px 值。
可运行示例为 assets/starter-vite-react/src/components/StatCard.tsx:这是一个使用 forwardRef 的组件,通过 cva() 定义变体矩阵,并使用 cn()(clsx + tailwind-merge)组合类;其中所有颜色/间距工具类(bg-ams-bg、border-ams-separator、p-ams-m、border-l-ams-magenta 等)都由 tailwind.config.js 中的 --ams-* 令牌提供支持。若需要 CVA 变体 API,请选择此方案;若需要在 React 之外共享样式,或希望通过手写样式表来理解样式,则选择 BEM/CSS 方案。
TypeScript 模式
Prop 类型
// Intersect with HTML attributes
interface MyComponentProps extends React.HTMLAttributes<HTMLDivElement> {
variant: "primary" | "secondary"
}
// For form elements
interface MyInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
invalid?: boolean
}
复合组件导出
// Following AMS pattern with Object.assign
const ListRoot = forwardRef<HTMLUListElement, ListProps>(/* ... */)
const ListItem = forwardRef<HTMLLIElement, ListItemProps>(/* ... */)
export const List = Object.assign(ListRoot, { Item: ListItem })
// Usage: <List><List.Item>...</List.Item></List>
常见错误
| 错误 | 修正方法 |
|---|---|
| 导入顺序错误 | 字体 → CSS → 令牌(始终如此) |
缺少 ams-body 类 | 添加到 <body> 或根元素 |
颜色硬编码(#004699) | 使用 var(--ams-color-interactive) |
间距硬编码(16px) | 使用 var(--ams-space-m) |
使用 font-weight: 700 表示粗体 | 使用 800 或 var(--ams-typography-body-text-bold-font-weight) |
在 html 上设置 font-size: 62.5% | 不要这样做——AMS 使用以 16px 为基准校准的 rem 值 |
Field 和 input 都缺少 invalid 属性 | <Field invalid> 和 <TextInput invalid> 都需要设置 |
使用 <h1> 而不是 <Heading level={1}> | 始终使用 AMS Heading 组件 |
Tabs.Button 缺少 aria-controls | 必需的属性——必须与 Tabs.Panel 的 id 匹配 |
使用 Tailwind 的 bg-blue-500 而不是 AMS 令牌 | 使用 bg-ams-interactive 或 AMS 组件 |
使用已弃用的 PageHeading | 使用 <Heading level={1}>——PageHeading 已弃用 |
| 标题和段落使用相同的间距 | 将内容区块包裹在嵌套的 Column 中——区块之间使用 gap="large",区块内部使用 gap="small" |
| 输出看起来像线框图 | 阅读 references/aesthetic-discipline.md,并在编写 JSX 前选定一个主导令牌和一个强调令牌 |
每个区块都使用相同的 paddingVertical 和 span | 调整节奏——交替使用紧凑(large)和宽松(x-large/2x-large)的布局;使用 start={} 让跨度不对称 |
| 使用 Next.js 或纯 Vite 从头搭建脚手架 | 使用 assets/starter-vite-react/——Bun + Vite + React Router + Tanstack Query 是面向真实场景的目标 |
为 Popover/DropdownMenu/Tooltip 选择 shadcn/ui | ADS 不提供这些组件——使用应用 AMS 令牌样式的 @radix-ui/react-* 无头基础组件(参见 references/components.md §“ADS 不提供的组件”) |
图标用法
完整的图标目录和命名规范:阅读
references/icons.md
图标是用于快速传达信息的视觉符号。为确保尺寸和对齐方式一致,必须始终将图标包裹在 Icon 组件中。图标集包含在 @amsterdam/design-system-react-icons 中(345 个以上图标,v2.0.0+)。
基本用法
import { Icon, Button, IconButton } from "@amsterdam/design-system-react"
import { SearchIcon, CloseIcon, NotificationIcon } from "@amsterdam/design-system-react-icons"
{/* Standalone decorative icon — hidden from assistive tech by default */}
<Icon svg={SearchIcon} />
{/* Sized to match text */}
<Icon svg={SearchIcon} size="large" /> {/* matches large body text */}
<Icon svg={SearchIcon} size="heading-3" /> {/* matches heading level 3 */}
{/* Inverse color for dark backgrounds */}
<Icon svg={SearchIcon} color="inverse" />
{/* Square bounding box (useful for alignment in grids) */}
<Icon svg={SearchIcon} square />
{/* Button with icon (icon appears after text by default) */}
<Button icon={SearchIcon}>Zoeken</Button>
<Button icon={SearchIcon} iconBefore>Zoeken</Button>
{/* Icon-only button — label is REQUIRED for accessibility */}
<IconButton svg={CloseIcon} label="Sluiten" />
图标属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
svg | Function | ReactNode | 必需 | 图标包中的图标组件或自定义 SVG |
size | 'small' | 'large' | 'heading-1' | 'heading-2' | 'heading-3' | 'heading-4' | 'heading-5' | — | 与文本行高对齐的尺寸 |
color | 'inverse' | — | 用于深色背景的白色图标 |
square | boolean | false | 正方形边界框 |
与其他组件搭配使用图标
import { StandaloneLink, Badge } from "@amsterdam/design-system-react"
import { SearchIcon, StarIcon } from "@amsterdam/design-system-react-icons"
<StandaloneLink href="/search" icon={SearchIcon}>Zoek op de website</StandaloneLink>
<Badge label="Nieuw" icon={StarIcon} color="azure" />
v2.0.0 重命名(破坏性变更)
以下图标在 v2.0.0 中已重命名——请使用新名称:
| 旧名称 | 新名称 |
|---|---|
BellIcon / BellFillIcon | NotificationIcon / NotificationFillIcon |
PersonCircleIcon / PersonCircleFillIcon | UserAccountIcon / UserAccountFillIcon |
TrashBinIcon | DeleteIcon |
CogwheelIcon | SettingsIcon |
CheckMarkCircleIcon | SuccessIcon |
自定义 SVG
{/* Must use viewBox="0 0 24 24" and fill="currentColor" */}
<Icon svg={
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2L2 7l10 5 10-5-10-5z" />
</svg>
} />
指南
- 按钮和链接中的图标应与文本搭配使用——仅在通用约定中(汉堡菜单、搜索、播放控件)单独使用图标
- 默认颜色:黑色/白色,与容器匹配。交互状态:蓝色。禁用状态:灰色
- 图标位于文本左侧,并与第一行垂直居中对齐
Icon组件会在<span>上设置hidden——图标默认是装饰性的。对于有实际含义的图标,请使用带有label属性的IconButton- WCAG 对比度要求同样适用于图标和排版
参考文件
如需查看详细的 API 文档、令牌目录和模板,请阅读 references/ 中的参考文件:
aesthetic-discipline.md— 如何在 ADS 的限制范围内运用前端设计原则(字体层级、色彩运用、动态效果、构图、纵深)。在任何非简单 UI 任务中编写 JSX 之前,请先阅读此文件。components.md— 每个组件的完整属性和代码示例,以及 ADS 未提供组件时可用的 Radix 基元部分tokens.md— 完整的--ams-*令牌目录,包含两种模式下的取值layout-patterns.md— 页面布局模板(公共网站、仪表盘、表单页面、React Router + AppLayout outlet)tailwind-bridge.md— 完整的 Tailwind v4 + AMS 集成指南(Vite 插件、紧凑令牌、动态效果)icons.md— 来自@amsterdam/design-system-react-icons的图标目录
资源:
assets/starter-vite-react/— 可运行的 Bun + Vite + React + TS 入门项目,已预先配置 ADS 紧凑模式。请参阅上方的“搭建新应用”部分。