mcpField
A
amsterdam-design-system

amsterdam-design-system

github.com/amsterdam设计与 UI26160 次安装16 周前更新
代码仓库

关于 amsterdam-design-system

amsterdam-design-system 是指导构建阿姆斯特丹市数字产品的设计技能,面向使用 React 等技术实现界面的开发者。它规定使用官方组件、设计令牌和布局模式,并提供 Tailwind CSS v4 集成指导。

主要功能

  1. 01官方 React 组件与图标
  2. 02Spacious 和 Compact 设计令牌
  3. 03响应式 4/8/12 列网格
  4. 04Tailwind CSS v4 集成指导
  5. 05Vite React 应用起始模板

适用场景

  1. 01为市政网站搭建页面
  2. 02为内部工具设计紧凑界面
  3. 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
.gitignore
bunfig.toml
eslint.config.js
index.html
package.json
README.md
tailwind.config.js
tsconfig.json
vite.config.ts
assets/starter-vite-react/src/
App.tsx
main.tsx
query.ts
router.tsx
assets/starter-vite-react/src/components/
StatCard.tsx
assets/starter-vite-react/src/layouts/
AppLayout.tsx
assets/starter-vite-react/src/lib/
cn.ts
assets/starter-vite-react/src/pages/
HomePage.tsx
NotFoundPage.tsx
assets/starter-vite-react/src/styles/
index.css
reveal.css
references/
aesthetic-discipline.md
components.md
icons.md
layout-patterns.md
tailwind-bridge.md
tokens.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 个检查问题:

  1. 主导色——哪个 --ams-color-* token 覆盖画布的 80% 以上?
  2. 醒目强调色——哪个 --ams-color-highlight-* token 最多用于 2–3 个元素?
  3. 节奏——哪些区域较密集(paddingVertical="large")?哪些区域较疏朗("2x-large")?
  4. 页面加载时刻——顶层 Grid 单元格上唯一的分阶段显现序列是什么?
  5. 令人难忘的元素——每个页面中,唯一的 <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-assetsAmsterdam Sans 字体文件
@amsterdam/design-system-cssBEM 组件样式(ams-* 类)
@amsterdam/design-system-tokensSpacious + Compact 模式下的 CSS 自定义属性(--ams-*)
@amsterdam/design-system-reactReact 组件(66 个组件,均使用 forwardRef)
@amsterdam/design-system-react-iconsAMS 图标集的图标组件

无需 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

然后:

  1. 阅读 references/aesthetic-discipline.md(5 个检查问题)。
  2. 将 src/pages/HomePage.tsx 替换为用户实际需要的页面,并确保已回答规范检查问题。
  3. 随着应用扩展,在 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)
中屏8576px – 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 固定
H132-48px 流式24-28px 流式
行高1.61.5
间距 m16-24px 流式12-16px 流式
间距 2xl48-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/uiADS 不提供这些组件——使用应用 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" />
图标属性
属性类型默认值说明
svgFunction | ReactNode必需图标包中的图标组件或自定义 SVG
size'small' | 'large' | 'heading-1' | 'heading-2' | 'heading-3' | 'heading-4' | 'heading-5'—与文本行高对齐的尺寸
color'inverse'—用于深色背景的白色图标
squarebooleanfalse正方形边界框
与其他组件搭配使用图标
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 / BellFillIconNotificationIcon / NotificationFillIcon
PersonCircleIcon / PersonCircleFillIconUserAccountIcon / UserAccountFillIcon
TrashBinIconDeleteIcon
CogwheelIconSettingsIcon
CheckMarkCircleIconSuccessIcon
自定义 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 紧凑模式。请参阅上方的“搭建新应用”部分。

评论

支持 Markdown