---
title: "图标"
description: "每个设计系统一套图标集，一种字重，一条规则——未选中时用线框，选中时用填充。"
url: "https://tanrei.chnotchy.com/zh-cn/foundations/icons/"
last_updated: 2026-10-26T22:31:27.000Z
---

# 图标

> 每个设计系统一套图标集，一种字重，一条规则——未选中时用线框，选中时用填充。

Tanrei 的默认图标是 [Material Symbols](https://fonts.google.com/icons) 的 **Rounded** 样式，**字重 300**。和文本字体一样，这款字体只按名称引用，不随包分发：由产品自行加载。

图标怎样绘制，由一小组令牌决定。图标框有三种尺寸：`sys.icon.small`、`medium` 和 `large`（默认为 16、20 和 24px），字体的光学尺寸跟随图标框。

- **`ref.typeface.icon`**: The icon font. Material Symbols Rounded: a variable font whose weight and fill are set from sys.icon.
- **`sys.icon.family`**: The icon font by selection. A variable icon font (Material Symbols) is one family and tells selected from unselected with its FILL axis; a static icon font tells them apart with two families (Material Icons Outlined / Material Icons). Both default to ref.typeface.icon.
- **`sys.icon.weight`**: The stroke weight of every icon (the \`wght\` axis of the icon font).
- **`sys.icon.fill`**: The fill of an icon by selection (the \`FILL\` axis of the icon font): 0 is outlined, 1 is filled.

生成自 `tokens/ref/typeface.tokens.json`, `tokens/sys/measure.tokens.json`

## 填充规则

**未选中用线框，选中用填充，没有例外。** 导航项、标签页、切换按钮、纸片、收藏：图标始终是同一个字形，只有填充随状态变化。填充图标和线框图标从不为了装饰而混用，也从不手动挑选——在 Web 上，填充跟随图标所在控件的状态。

```js
import '@tanrei/web/icons.css';
```

```html
<button aria-pressed="true"><span class="tanrei-icon" aria-hidden="true">favorite</span> 收藏</button>
```

`.tanrei-icon` 会读取任意祖先元素上的 `aria-pressed`、`aria-selected`、`aria-checked` 和 `aria-current`（Tanrei 控件会把它们移到 `data-aria-*`，在这类控件上读取的是 `data-aria-pressed`、`data-aria-selected`、`data-aria-checked` 和 `data-aria-current`），或者图标自身及其祖先上的 `data-selected`（`data-selected="false"` 可以让单个图标不参与）。图标旁边的文本已经表达了同样的意思时，给图标加上 `aria-hidden="true"`；没有这样的文本时，给控件一个无障碍名称。

名称也可以写在 `data-icon` 里，元素本身留空：

```html
<span class="tanrei-icon" data-icon="folder"></span>
```

绘制出的字形相同，但名称不再是页面上的文本：它不会出现在 `textContent` 或 `innerText` 中，不会出现在用户复制的内容里，也不会进入外层控件的无障碍名称。凡是名称会被当作文本读取的地方，都可以用这种写法，例如菜单或列表的一行、表格的单元格，或测试要比对的文本。尺寸、选中状态以及从右到左布局中的镜像，都与把名称写成文本时完全一样。如果图标旁边的文本已经表达了同样的意思，仍然要加上 `aria-hidden="true"`：Firefox 128 之前的版本会把这个名称读进无障碍名称。

在原生平台上，同样的取值随设计系统一起提供（`system.icon`），另有一个小的辅助函数，把“是否选中”和尺寸转换成字体所需的参数。Tanrei 目前还不提供图标组件。

## 每个设计系统一套图标集

图标集属于设计系统，所以[层](https://tanrei.chnotchy.com/zh-cn/foundations/design-systems/)可以更换它。各内置系统解析出的结果如下：

| 设计系统 | `family.unselected` | `family.selected` | `weight` | `fill.unselected` | `fill.selected` |
| --- | --- | --- | --- | --- | --- |
| `tanrei` | Material Symbols Rounded | Material Symbols Rounded | 300 | 0 | 1 |
| `cupertino` | Material Symbols Rounded | Material Symbols Rounded | 300 | 0 | 1 |
| `m3-expressive-like` | Material Symbols Outlined | Material Symbols Outlined | 400 | 0 | 1 |
| `m3-like` | Material Symbols Outlined | Material Symbols Outlined | 400 | 0 | 1 |

生成自 `tokens/systems/*.system.json`, `design/tokens/<system>/tanrei.light.tokens.json`

图标字体区分选中与未选中的图标，有两种办法，这组令牌两种都能覆盖。选中的图标总是同时取 `family.selected` **和** `fill.selected`；两者之中究竟哪一个改变了画出来的结果，取决于图标集。

### 一个可变字体族

可变图标字体（例如 Material Symbols）是带有 `FILL` 轴的单个字体族。两个字体族令牌默认都指向 `ref.typeface.icon`，所以只要重新指向这一个令牌就能更换图标集，再由 `fill.unselected` / `fill.selected` 区分两种状态。`m3-like` 写的全部内容就是这些：

```json
"tokens": {
  "tanrei.ref.typeface.icon": { "$value": ["Material Symbols Outlined"], "reason": "Material Design 3 以 Outlined 样式绘制 Material Symbols。" },
  "tanrei.sys.icon.weight":   { "$value": 400, "reason": "Material Symbols 在 Material Design 3 中的默认字重。" }
}
```

### 两个静态字体族

静态图标字体没有轴。它把线框和填充两种画法作为两个独立的字体族提供，所以层要为每种状态各指定一个：

```json
"tokens": {
  "tanrei.sys.icon.family.unselected": { "$value": ["Material Icons Outlined"], "reason": "Acme 使用静态的 Material Icons 图标集：未选中时用线框。" },
  "tanrei.sys.icon.family.selected":   { "$value": ["Material Icons"], "reason": "Acme 使用静态的 Material Icons 图标集：选中时用填充。" },
  "tanrei.sys.icon.weight":            { "$value": 400, "reason": "静态图标字体只有一种字重；Material Icons 按 400 绘制。" }
}
```

填充和字重令牌保持原样。静态字体会忽略这些轴，所以它们没有害处，组件也不需要知道当前启用的是哪一种图标集。这个层就是仓库自己的测试样例（`packages/tokens-build/test/fixtures/acme.system.json`）。

### 图标名称由产品负责

Tanrei 决定的是图标怎样绘制，而不是有哪些图标。各图标集对字形的命名不同，一套图标集也可能缺少另一套里有的字形。更换图标集的产品需要核对自己用到的名称。不是字体的符号集（例如 SF Symbols）无法用字体族令牌来指定；这一点已作为 `platform-symbol-sets` 记入模仿差距登记表。

### React Native

React Native 的 `<Text>` 无法设置字体变体轴，所以使用可变图标字体时，`FILL` 到不了字形，图标在两种状态下看起来一样。要在那里守住这条规则，可以像上面那样使用两个静态字体族，或者用 SVG 绘制图标。

四个原生软件包的图标辅助函数正在迁移到“每种状态一个字体族”的模型；在某个软件包的 README 说明这一点之前，应当认为该软件包只读取单个字体族。
