# Sainsbury's 设计系统

## Sainsbury's 设计系统 界面预览

更改日期和时间，查看示例费用，然后检查本地选择结果。

## Sainsbury's 设计系统 设计参考

Sainsbury’s 公开的颜色、字号令牌、排版和组件指南。对于公开发布的断点表中相互冲突的内容，会分别标注，而不会将其合并。

### Sainsbury’s 调色板角色

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| Primary brand base | #F06C00 | 本示例中的品牌强调色和按钮填充色，搭配深色文字。 |
| Primary hover | #E55000 | 悬停填充色；不适合作为白色背景上的小字号文字颜色。 |
| Primary pressed | #C15100 | 按下状态。 |
| Primary light | #FEF5F0 | 选择项和摘要背景。 |
| Secondary base | #7F0442 | 少量使用的装饰性色彩。 |
| Monochrome darker | #262626 | 主要内容。 |
| Monochrome dark | #404040 | 辅助内容。 |
| Monochrome light | #D8D8D8 | 边界。 |
| Monochrome lighter | #F2F2F2 | 不可用选项的界面表面。 |
| Success lighter | #E6F4ED | 本地审核反馈界面表面。 |
| Success darker | #246127 | 反馈文字。 |
| Info darker | #135CA3 | 应用焦点轮廓。 |
### 已发布的字号令牌

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| xs | 2px | 公开字号令牌表中的间距值。 |
| sm | 4px | 公开字号令牌表中的间距值。 |
| md | 8px | 公开字号令牌表中的间距值。 |
| lg | 16px | 公开字号令牌表中的间距值。 |
| xl | 24px | 公开字号令牌表中的间距值。 |
| 2xl | 32px | 公开字号令牌表中的间距值。 |
| 3xl | 40px | 公开字号令牌表中的间距值。 |
| 4xl | 48px | 公开字号令牌表中的间距值。 |
| width-sm | 1px | 边框令牌；宽度和圆角名称应彼此区分。 |
| width-base | 2px | 边框令牌；宽度和圆角名称应彼此区分。 |
| radius-base | 2px | 边框令牌；宽度和圆角名称应彼此区分。 |
| radius-xl | 12px | 边框令牌；宽度和圆角名称应彼此区分。 |
| min-width-sm | 576px | 令牌断点；与编辑版式页面中的断点不同。 |
| min-width-md | 768px | 令牌断点；与编辑版式页面中的断点不同。 |
| min-width-lg | 992px | 令牌断点；与编辑版式页面中的断点不同。 |
| min-width-xl | 1200px | 令牌断点；与编辑版式页面中的断点不同。 |
### Sainsbury’s 字号比例

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| Body 1 | 1rem / 1.5 / Arial Regular | 正文。 |
| Caption | 0.875rem / 1.571 / Arial Regular | 辅助说明。 |
| Label 1 | 1rem / 1.5 / Arial Bold | 控件标签。 |
| Display 1 | 1.25rem / 1.3 | 官方指南中的品牌展示字体；此处使用后备字体。 |
| Display 2 | 1.5rem / 1.333 | 响应式替代方案为 Display 3。 |
| Display 3 | 1.875rem / 1.333 | 响应式替代方案为 Display 4。 |
| Display 4 | 2.375rem / 1.316 | 响应式替代方案为 Display 5。 |
| Display 5 | 3rem / 1.292 | 响应式替代方案为 Display 6。 |
| Display 6 | 3.75rem / 1.267 | 文档中规定的最大展示字号。 |
| Responsive type threshold | larger than 720px | 独立于 768px 的字号令牌断点。 |
## Sainsbury's 设计系统 完整指南

实用指南，介绍如何为真实界面选择、应用和评审此系统。

完整指导、实现起点和源代码链接。

## Sainsbury's 设计系统 Sainsbury's 设计系统 多品牌系统与访问权限

### 选择一个品牌主题

> Fable 通过共享组件结构和品牌专属值支持 Sainsbury’s、Argos、Nectar、Tu 和 Habitat。本指南仅使用 Sainsbury’s 调色板。在 Sainsbury’s 交互中混入 Nectar 紫色，或假定所有品牌使用相同字体，都会模糊系统中明确划定的边界。请将所选主题关联到应用入口。

### 区分公开指南与私有软件包

公开的合作伙伴指南介绍了 React 组件、Fable Styles 和设计令牌包，但访问代码库需要凭据，需通过 Sainsbury’s 联系人申请。本示例无需任何凭据：它是基于已发布指南编写的完整原创 HTML。它不会假称自己运行了 Fable，也不会为无法访问的注册表编造安装命令。

## Sainsbury's 设计系统 Sainsbury's 设计系统 配色与对比度

### 橙色应搭配适当的文字颜色

源文档说明，基础橙色和深橙色在白色背景上均不符合普通正文的对比度要求。本示例将橙色用作填充色，并搭配黑色按钮文字，而不是在白底上使用小号橙色文字，或在橙底上使用小号白色文字。文档中的品牌例外不应被悄然解读为所有颜色组合都符合无障碍要求。

### 确保反馈语义清晰

查看结果使用绿色成功背景和深绿色文字。不可用时段采用中性背景并附有文字标签，选中的时段则同时显示已选中的单选按钮和橙色描边。颜色只是辅助提示，不能取代时段、可用状态或选择状态的文字说明。

## Sainsbury's 设计系统 Sainsbury's 设计系统 间距、圆角与断点

### 遵循已发布的尺寸比例

本示例在选项之间使用 8px 间距，每个时段内使用 16px 间距，主要内容组之间使用 24–32px 间距。控件采用 2px 圆角，摘要区则采用文档中规定的 12px 圆角。这些差异用于表达分组层级，而不是让所有区域都套用相同的圆角卡片样式。

### 注明文档中的差异

布局指南列出了 600px 和 900px 设备断点，而尺寸令牌表列出了 576px、768px 和 992px。排版指南还规定，显示文字的尺寸会在宽度超过 720px 时变化。这些断点并非同一比例的不同名称。本实现选择使用尺寸令牌中的 768px 断点来布局多列，并明确说明了这一选择。

## Sainsbury's 设计系统 Sainsbury's 设计系统 排版与实用内容

### 遵守品牌字体的使用范围

Sainsbury’s 官方显示文字样式使用 Mary Ann Extra Bold，普通正文则使用 Arial。由于本示例未包含品牌字体文件，因此从头到尾都使用 Arial。示例保留了字号比例和行高关系，但不会声称这种备用字体的视觉效果完全一致。语义标题级别与显示字号彼此独立。

### 清楚呈现取货选择

每个时段都标明时间范围和费用，或注明不可用。摘要会重复显示所选日期、时间和示例地点。主要操作按钮文案为“查看取货信息”，明确说明下一步操作。本示例不会编造倒计时、订单总额或预订成功信息来营造商业化效果。

## Sainsbury's 设计系统 Sainsbury's 设计系统 单选选项与本地状态

### 在同一组中使用关联选项

日期控件会更改可选时段，每个时段都是同一命名组中的原生单选按钮。更改日期后，系统会选中该日期的第一个可用时段并更新摘要。不可用选项仍会显示，但无法选中。原生单选按钮提供熟悉的键盘操作方式，无需用可点击的 div 重新实现。

### 避免显示过时的预订信息

点击“查看”后会显示本地确认信息，并注明尚未预订任何时段。更改日期或时间后，该结果会隐藏。生产环境流程必须重新检查可用状态并等待服务器响应，之后才能确认已预订。本示例将交互约定单独呈现，避免将缺失的后端工作误认为预订流程已完成。

## Sainsbury's 设计系统 Sainsbury's 设计系统 交互目标、焦点与响应式行为

### 为选项留出足够空间

公开的按钮指南建议触控目标高度为 48px。日期按钮和查看操作均达到这一高度，整个带标签的时段区域也都可以点击。蓝色焦点描边在橙色和浅色背景上始终清晰可见。装饰性购物篮图标对辅助技术隐藏。

### 允许较长内容换行

在窄屏布局中，摘要会排在选项列表后面，装饰性图标则会隐藏。时段标签和价格保留在正常文档流中，容器高度可灵活调整。翻译后的文字或较长的地点名称不应被固定高度的卡片裁切；本英文原型为后续本地化预留了足够空间。

## Sainsbury's 设计系统 Sainsbury's 设计系统 实现与 AI Agent 评审

### 运行独立示例

保存完整的 HTML 示例并在本地运行。示例中的三个日期和每天三个时段均为编写的样例数据，所有更改都只保存在内存中。源码包含 CSS、单选按钮创建、摘要更新和过时反馈清除逻辑，因此预览背后不存在缺失的应用层。

1. 将完整文件保存为 index.html。

2. 选择星期二，并确认无法选中上午的不可用时段。

3. 更改时段并检查摘要。

4. 点击“查看”，然后更改日期，确认旧结果会清除。

### 仅在访问权限得到确认后再迁移到 Fable

对于经授权的内部集成，请先获取实际的软件包访问权限，并检查当前主题和组件 API。在用受支持的组件替换原始标记时，应保留本示例的行为。确认已安装的令牌比例与公开参考资料一致后，才能将截图视为实现依据。

- 统一使用一种品牌主题。

- 确认软件包访问权限和当前 API。

- 分别检查文字对比度和品牌颜色还原度。

## Sainsbury's 设计系统 Sainsbury's 设计系统组件示例

### 完整的取货时段流程

独立的 HTML/CSS/JavaScript，包含日期选择、单选时段、不可用状态和实时摘要。

```
<!doctype html>
<html lang="en">
  <meta charset="utf-8" /><meta
    name="viewport"
    content="width=device-width,initial-scale=1"
  /><title>Collection slot study</title
  ><style>
    * {
      box-sizing: border-box;
    }
    body {
      margin: 0;
      background: #fff;
      color: #262626;
      font:
        16px/1.5 Arial,
        sans-serif;
      padding: 32px;
    }
    header {
      border-top: 8px solid #f06c00;
      padding-top: 24px;
      display: flex;
      justify-content: space-between;
      gap: 24px;
    }
    h1 {
      font-size: 38px;
      line-height: 1.316;
      margin: 8px 0 16px;
    }
    h2 {
      font-size: 24px;
      line-height: 1.333;
      margin: 0 0 16px;
    }
    .eyebrow,
    small {
      font-size: 14px;
      color: #404040;
    }
    p {
      margin: 8px 0 24px;
    }
    .basket {
      font-size: 48px;
      line-height: 1;
      background: #fef5f0;
      border-radius: 50%;
      width: 96px;
      height: 96px;
      display: grid;
      place-items: center;
      color: #7f0442;
    }
    main {
      display: grid;
      grid-template-columns: 2fr 1fr;
      gap: 32px;
      margin-top: 24px;
    }
    fieldset {
      border: 0;
      padding: 0;
      margin: 0 0 24px;
    }
    legend {
      font-weight: bold;
      margin-bottom: 16px;
    }
    .dates {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
    }
    button,
    select {
      font: inherit;
      min-height: 48px;
      padding: 12px 16px;
      border: 1px solid #737373;
      border-radius: 2px;
      background: white;
      color: #262626;
      cursor: pointer;
    }
    .dates button[aria-pressed="true"] {
      border: 2px solid #f06c00;
      background: #fef5f0;
      padding: 11px 15px;
    }
    .slots {
      display: grid;
      gap: 8px;
    }
    .slot {
      display: flex;
      gap: 12px;
      align-items: center;
      padding: 16px;
      border: 1px solid #d8d8d8;
      border-radius: 2px;
      cursor: pointer;
    }
    .slot:has(input:checked) {
      border: 2px solid #f06c00;
      padding: 15px;
      background: #fef5f0;
    }
    .slot:has(input:disabled) {
      background: #f2f2f2;
      cursor: default;
    }
    .slot input {
      accent-color: #c15100;
      width: 20px;
      height: 20px;
    }
    .slot strong {
      margin-left: auto;
    }
    .unavailable {
      color: #404040;
    }
    .primary {
      background: #f06c00;
      border: 2px solid #f06c00;
      font-weight: bold;
      color: #000;
    }
    .primary:hover {
      background: #e55000;
      border-color: #e55000;
    }
    .primary:active {
      background: #c15100;
      border-color: #c15100;
    }
    .summary {
      background: #fef5f0;
      border-radius: 12px;
      padding: 24px;
      align-self: start;
    }
    .summary dl {
      margin: 0;
    }
    .summary dt {
      font-size: 14px;
      margin-top: 16px;
    }
    .summary dd {
      margin: 4px 0;
      font-weight: bold;
    }
    #result {
      padding: 16px;
      background: #e6f4ed;
      color: #246127;
      margin-top: 24px;
    }
    button:focus-visible,
    input:focus-visible {
      outline: 3px solid #135ca3;
      outline-offset: 3px;
    }
    footer {
      border-top: 1px solid #d8d8d8;
      margin-top: 32px;
      padding-top: 16px;
      font-size: 14px;
      color: #404040;
    }
    [hidden] {
      display: none !important;
    }
    @media (max-width: 767px) {
      body {
        padding: 16px;
      }
      main {
        grid-template-columns: 1fr;
        gap: 24px;
      }
      h1 {
        font-size: 30px;
      }
      .basket {
        display: none;
      }
      .dates button {
        flex: 1;
      }
      .summary {
        padding: 16px;
      }
    }
  </style>
  <body>
    <header>
      <div>
        <span class="eyebrow">GROCERIES / ORIGINAL COLLECTION STUDY</span>
        <h1>Choose a collection slot</h1>
        <p>
          Select a sample day and time. No order or real availability is
          connected.
        </p>
      </div>
      <div class="basket" aria-hidden="true">▥</div>
    </header>
    <main>
      <section>
        <fieldset>
          <legend>Collection day</legend>
          <div class="dates" id="dates"></div>
        </fieldset>
        <fieldset>
          <legend id="slotLabel">Available times</legend>
          <div id="slots" class="slots"></div>
        </fieldset>
      </section>
      <aside class="summary">
        <h2>Collection details</h2>
        <dl>
          <dt>Sample location</dt>
          <dd>Riverside collection point</dd>
          <dt>Day and time</dt>
          <dd id="selection"></dd>
          <dt>Sample fee</dt>
          <dd id="fee"></dd>
        </dl>
        <p><small>All fees and slots are illustrative.</small></p>
        <button class="primary" id="review">Review collection</button>
        <div id="result" role="status" hidden></div>
      </aside>
    </main>
    <footer>
      Independent HTML interpretation of Sainsbury’s public guidance. Arial
      fallback; no private Fable runtime or brand font assets.
    </footer>
    <script>
      const days = [
        {
          label: "Monday",
          slots: [
            ["09:00–10:00", 1, true],
            ["12:00–13:00", 0, true],
            ["17:00–18:00", 2, false],
          ],
        },
        {
          label: "Tuesday",
          slots: [
            ["09:00–10:00", 0, false],
            ["12:00–13:00", 1, true],
            ["17:00–18:00", 1, true],
          ],
        },
        {
          label: "Wednesday",
          slots: [
            ["09:00–10:00", 0, true],
            ["12:00–13:00", 0, true],
            ["17:00–18:00", 2, true],
          ],
        },
      ];
      let day = 0,
        slot = 0;
      const $ = (id) => document.getElementById(id);
      function update() {
        const choice = days[day].slots[slot];
        $("selection").textContent = days[day].label + ", " + choice[0];
        $("fee").textContent =
          choice[1] === 0 ? "No collection fee" : "£" + choice[1].toFixed(2);
        $("result").hidden = true;
      }
      function render() {
        $("dates").replaceChildren(
          ...days.map((d, i) => {
            let b = document.createElement("button");
            b.textContent = d.label;
            b.setAttribute("aria-pressed", i === day);
            b.onclick = () => {
              day = i;
              slot = d.slots.findIndex((s) => s[2]);
              render();
            };
            return b;
          }),
        );
        $("slotLabel").textContent = days[day].label + " collection times";
        $("slots").replaceChildren(
          ...days[day].slots.map((s, i) => {
            let l = document.createElement("label");
            l.className = "slot";
            let input = document.createElement("input");
            input.type = "radio";
            input.name = "time";
            input.checked = i === slot;
            input.disabled = !s[2];
            input.onchange = () => {
              slot = i;
              update();
            };
            let text = document.createElement("span");
            text.textContent = s[0];
            let fee = document.createElement("strong");
            fee.textContent = s[2]
              ? s[1] === 0
                ? "No fee"
                : "£" + s[1].toFixed(2)
              : "Unavailable";
            l.append(input, text, fee);
            return l;
          }),
        );
        update();
      }
      $("review").onclick = () => {
        $("result").hidden = false;
        $("result").textContent =
          "Local selection ready: " +
          $("selection").textContent +
          ". No slot has been reserved.";
      };
      render();
    </script>
  </body>
</html>
```

代码参考

- 无需私有软件包或凭据。

## Sainsbury's 设计系统 AI Agent 设置

### 实现起点

Fable 软件包访问权限与这份公开且独立的示例无关。

```
# Save the complete HTML example as index.html
python3 -m http.server 8080
# Open http://localhost:8080
```

### 适用于你项目的提示词

```
根据这份完整的 HTML 示例，构建一个遵循 Sainsbury’s 指南的取货时段选择器。使用单一品牌配色、橙色操作按钮上的黑色文字、带原生标签的单选按钮、48px 点击目标，以及明确采用的 768px 布局方案。每次选择变化时都清除审核反馈。使用虚构的可用时段，不要声称使用了 Fable 运行时或成功预订。
```

## Sainsbury's 设计系统 来源与范围

对 Sainsbury’s 公开指南的 OpenDesign 应用研究。这是原创的 HTML，并非 Fable 私有运行时。使用 Arial 字体，而非重新分发的 Mary Ann 资源；取货时段和费用均为虚构。

- Sainsbury’s 配色 配色值及文档中说明的橙色对比度限制。

- 尺寸令牌 边框、间距和断点值。

- 排版 品牌字体、字号比例与响应式行为。

- 单选按钮 选择、标签与分组。

- Button 操作层级、标签与点击目标。

- 合作伙伴访问权限 私有代码库访问权限与实现路径。

- 布局 编辑布局网格；与尺寸令牌中的断点不同。

## Sainsbury's 设计系统 相关资源

### Salesforce Lightning

一份 Lightning 蓝图指南，适用于密集的记录处理场景：真实的间距、字体与语义色，一个可编辑的案例工作区、字段验证，以及能反映本地变更的活动历史记录。

### Samsung One UI

通过完整的原创阅读偏好预览，了解 One UI 的浏览与交互结构、24dp 边距建议、200% 文本缩放要求，以及深色外观和明确的重置恢复方式。

### SAP Fiori

通过 UI5 Web Components 使用 SAP Fiori 的 Horizon 主题：采用真实的主题参数、明确的内容密度、语义化的值状态，以及带本地验证的采购申请布局。


- [实现参考](https://design-systems.sainsburys.co.uk/components/radio-button/)
- [来源](https://design-systems.sainsburys.co.uk/foundations/colour/sainsburys-colours/)
- [来源](https://design-systems.sainsburys.co.uk/foundations/design-tokens/size-tokens/)
- [来源](https://design-systems.sainsburys.co.uk/foundations/typography/)
- [代码参考](https://design-systems.sainsburys.co.uk/components/radio-button/)
- [Sainsbury’s 配色 配色值及文档中说明的橙色对比度限制。](https://design-systems.sainsburys.co.uk/foundations/colour/sainsburys-colours/)
- [尺寸令牌 边框、间距和断点值。](https://design-systems.sainsburys.co.uk/foundations/design-tokens/size-tokens/)
- [排版 品牌字体、字号比例与响应式行为。](https://design-systems.sainsburys.co.uk/foundations/typography/)
- [单选按钮 选择、标签与分组。](https://design-systems.sainsburys.co.uk/components/radio-button/)
- [Button 操作层级、标签与点击目标。](https://design-systems.sainsburys.co.uk/components/button/)
- [合作伙伴访问权限 私有代码库访问权限与实现路径。](https://design-systems.sainsburys.co.uk/getting-started/for-third-party-partners/)
- [布局 编辑布局网格；与尺寸令牌中的断点不同。](https://design-systems.sainsburys.co.uk/foundations/layout/)
