diff --git a/backend/data/alone/cache/plugin-resources/webside/default/favicon.ico b/backend/data/alone/cache/plugin-resources/webside/default/favicon.ico new file mode 100644 index 000000000..280094d5d Binary files /dev/null and b/backend/data/alone/cache/plugin-resources/webside/default/favicon.ico differ diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md new file mode 100644 index 000000000..92d77b7ed --- /dev/null +++ b/frontend/AGENTS.md @@ -0,0 +1,238 @@ +# AGENTS.md + +本文件为 AI 编码代理在 open-cdm 前端工程(`frontend/`)内工作时的规则说明。内容按 CloudDM Web 前端的实际工程组织和团队规则调整。 + +## 作用范围 + +- 本文件适用于以 `frontend/` 为工作根目录的前端开发任务。 +- 仓库根目录的 `AGENTS.md` 提供全仓通用规则;本文件在其基础上补充前端专属约束。 +- 若子目录存在更深层 `AGENTS.md`,则该子目录及其后代以内层文件为准。 +- 本文件约束源码、配置、脚本和测试变更;默认不要修改生成产物和依赖目录,例如 `node_modules/`、`dist/`。 + +## 项目定位 + +- `frontend/` 是 CloudDM 的 Web 前端工程,npm 包名为 `clouddm-web`,同时作为后端 Gradle 模块 `cgdm-web` 被打包进安装包。 +- 面向团队数据库管理,覆盖 SQL 查询、数据源管理、权限控制、脱敏、工单协作、系统配置等能力。 +- 主要技术栈: + - Vue 3、Vue CLI 5、Vue Router 4、Vuex 4。 + - JavaScript 为主,局部 TypeScript;Node.js 22.22.1。 + - UI:View UI Plus(主)、Ant Design Vue(局部)、Tailwind CSS、Less。 + - 编辑器:Monaco Editor;图表/监控:Highcharts、自研 monitor 面板。 + - 国际化:vue-i18n;HTTP:axios;实时:reconnecting-websocket。 + +## 项目结构 + +- `src/main.js`:应用入口,注册插件、全局组件、主题和 i18n。 +- `src/App.vue`:根组件。 +- `src/router/`:路由定义;`index.js` 为主路由,`system.js` 为系统管理子路由。 +- `src/store/`:Vuex 全局状态(用户、权限、主题等)。 +- `src/views/`:页面视图,按业务域划分: + - `sql/`:SQL 工作台 + - `system/`:系统管理(数据源、权限、脱敏、子账号等) + - `ticket/`:工单 + - `project/`:项目 / 变更 + - `login/`、`initialization/`:登录与初始化 + - `consoleJob/`、`worker/`、`devops/` 等运维相关页面 +- `src/components/`:可复用组件: + - `ui/`、`widgets/`:基础 UI 与业务小组件 + - `function/`:功能型组件(数据源、监控、编辑器等) + - `layout/`:布局(侧边栏、品牌等) + - `modal/`、`form/`、`editor/`:弹窗、表单、编辑器 +- `src/services/`:接口与通信层: + - `http/`:面向 DM Console 的 REST API(`request.js` 为 axios 封装) + - `cc/`:面向 RDP / CloudCanal 相关 API + - `socket.js`:WebSocket 连接 +- `src/locales/`:国际化文案(`en.json`、`zh.json`)。 +- `src/i18n.js`:i18n 初始化与语言切换。 +- `src/styles/`:全局样式、主题(`themes/`)、Less 变量与 mixin。 +- `DESIGN.md`:UI 设计风格规范(色彩、字体、间距、圆角、组件形态);新增或改版页面/组件时以此为准。 +- `src/utils/`:通用工具函数。 +- `src/mixins/`:Vue mixin(鉴权、数据源、弹窗等)。 +- `src/directives/`:自定义指令。 +- `src/const/`:常量与枚举。 +- `public/`:静态资源与 HTML 模板。 +- `vue.config.js`:Vue CLI / Webpack 配置;开发代理默认指向 `http://localhost:8222`。 +- `scripts/check-i18n.js`:国际化 key 一致性检查脚本。 + +## 工作方式 + +- 以最小且正确的改动解决问题。 +- 优先选择直接、清晰、可读的实现,不追求花哨抽象。 +- 当多条规则冲突时,优先保证行为正确、状态一致、契约清晰。 +- 不乱猜业务语义;拿不到明确数据时,不要擅自补业务对象、状态、内容或其他业务含义。 +- 不胡乱兼容旧逻辑;已经明确删除的字段、分支、路径要清干净。 +- 先阅读相关模块的现有实现,再决定改法;优先沿用本仓库已有模式、命名、工具类和基础设施。 +- 工作区可能存在用户未提交改动;不要回滚、覆盖或重排与当前任务无关的改动。 + +## 构建与验证命令 + +### 环境要求 + +- Node.js 22.22.1 +- 本地联调需后端单机模式运行于 `http://localhost:8222`(`DmAloneLauncher`) + +### 常用命令 + +```bash +cd frontend && npm install +cd frontend && npm run serve:dm +cd frontend && npm run build:dm +cd frontend && npm run lint +cd frontend && npm run lint-fix +cd frontend && npm run test:unit +cd frontend && npm run check-i18n +``` + +- 使用 `package-lock.json`,默认使用 `npm`,不要擅自切换到其他包管理器。 +- 构建产物输出到 `dist/templates/`,由后端 Gradle `cgdm-web` 模块打包。 +- 全量构建前端资源也可通过 `cd package && ./all_build.sh web` 触发。 + +### 本地联调 + +- `npm run serve:dm` 启动开发服务器,`vue.config.js` 中 `devServer.proxy` 将 API 代理到后端。 +- 后端未启动时,页面接口调用会失败;优先确认 `localhost:8222` 可访问。 + +## 编码规则 + +- 不过度防御,不为了极低概率场景写复杂兜底逻辑。 +- 允许基于模块边界、配置约束和框架契约建立合理信任;不要不看上下文就把所有值都当成任意脏输入。 +- 不要为实际不可能出现的 `null`、空值或非法状态写复杂防御分支;防御逻辑只放在真实边界和不可信输入处。 +- 避免长条件和层层 `if` 堆叠导致代码难读;如果必须校验,优先让边界、契约和数据结构保持清晰。 +- 代码要干净直接,好读优先,不要为了抽象而抽象。 +- 尽量不写三元表达式,能用 `if` 表达清楚的逻辑,优先使用 `if`。 +- 不写没必要的小 helper,只有复用明显且能降低理解成本时才抽。 +- 没用到的字段、方法、分支、返回值要删掉。 +- 注释只解释不明显的业务约束、协议边界或复杂流程,不写重复代码字面含义的注释。 + +## 前端规则 + +### 架构与目录约定 + +- 以 Vue 3 + Vue CLI 为主,沿用 `components/`、`services/`、`store/`、`router/`、`views/` 的既有分层。 +- 新增页面:在 `views/` 对应业务目录下创建,并在 `router/` 注册路由与 `meta.requiredAuth` 权限。 +- 新增接口:在 `services/http/api/` 或 `services/cc/api/` 下按现有模块拆分,通过 `services/http/index.js` 聚合导出。 +- 全局状态放 Vuex;页面局部状态用组件 `data` / `setup`;跨组件通信用 `eventBus`(`utils/eventBus.js`)或 Vuex。 +- 不要擅自引入新的 UI 框架、状态管理库或构建工具。 + +### 设计风格 + +完整规范见 `DESIGN.md`;以下为代理落地时必须遵守的摘要。 + +**设计基调** + +- 编辑型工作流界面:白底画布(`#ffffff`)+ 深墨文字(`#181d26`),留白充足,不靠渐变或背景装饰抢注意力。 +- 品牌张力来自**全幅签名色块**(coral `#aa2d00`、forest `#0a2e0e`、dark navy `#181d26`、cream `#f5e9d4` 等),用于阶段性强调,不作为小元素点缀色。 +- 深度优先靠**色块对比**,阴影极少;输入框、次级按钮用 1px 发丝线边框(`#dddddd`)。 + +**色彩角色** + +| 角色 | Token | 用途 | +|------|-------|------| +| 主色 / 墨字 | `primary` / `ink` `#181d26` | 主按钮背景、标题、强调文字 | +| 正文 | `body` `#333840` | 默认段落 | +| 弱化 | `muted` `#41454d` | 页脚、面包屑、说明 | +| 画布 | `canvas` `#ffffff` | 页面默认背景 | +| 浅表面 | `surface-soft` `#f8fafc` | 卡片、选中层 | +| 链接 | `link` `#1b61c9` | 行内链接;**不是**主按钮色 | +| 语义 | `info` / `success` 等 | 提示、成功态 | + +- 主按钮用近黑(`primary`),不是链接蓝。链接蓝仅用于 `text-link`。 +- 签名色(coral、forest、peach、mint 等)只用于整块表面,不拆成小 badge 或 icon 底色。 + +**字体** + +- 主系统:Haas / Haas Groot Disp;无授权字体时用 Inter Display 或 `system-ui` 替代。 +- 展示标题(h1/h2)用 400–500 字重,**不靠加粗强调**;强调靠字号和色块对比。 +- 正文固定 14px / 400;按钮与标签 16px / 500。 +- 定价子系统单独用 Inter Display + `rounded.pill` 药丸按钮,不与主编辑系统混用。 + +**间距与圆角** + +- 间距以 4px 为基准:`xs` 8 · `md` 16 · `lg` 24 · `xl` 32 · `xxl` 48 · `section` 96。 +- 大区块上下内边距优先 `section`(96px);卡片内边距 24–48px 按层级选用。 +- 圆角层级:`sm` 6px 输入框 · `md` 10px 内容卡片 · `lg` 12px 主按钮与签名卡片 · `pill` 仅定价页。 + +**组件形态(对照 `DESIGN.md` components 节)** + +- **主按钮** `button-primary`:近黑底、白字、12px 圆角、16×24px 内边距;每视口仅一个主 CTA。 +- **次按钮** `button-secondary`:白底、墨字、发丝线描边;与主按钮成对出现。 +- **输入框** `text-input`:高 44px、6px 圆角、发丝线边框;聚焦用 `info-border`。 +- **签名卡片** `signature-coral-card` / `hero-card-dark`:全幅色块 + 48px 内边距 + 12px 圆角。 +- **功能卡片** `feature-card-tabbed` / `demo-grid-card`:浅底或 pastel 底,网格内高度可刻意错落。 + +**Do / Don't(来自 DESIGN.md)** + +- Do:主按钮保持近黑;hero 区信任留白、不加渐变;签名色块打断长页面单调节奏;间距对齐 4px 网格。 +- Don't:把链接蓝当主按钮色;展示标题加粗到 600/700;hero 加渐变/光晕背景;在定价子系统外使用 pill 圆角;连续两个相同表面模式(如两段纯白无变化);自行发明签名色以外的 accent 色。 +- 状态只文档化 Default 与 Active/Pressed,不额外设计 hover 变体。 + +**与现有代码的关系** + +- 样式实现落在 `styles/variables.less`、`styles/themes/` 和组件 Less 中;新 UI 应以 `DESIGN.md` token 为目标,逐步对齐,不沿用与规范冲突的旧色值(如把链接绿当主色)。 +- 暗色主题遵循 `styles/themes/dark-theme.less`,语义角色与亮色一致,色相按主题映射。 + +**响应式** + +- 断点:Mobile `<768` · Tablet `768–1024` · Desktop `1024–1440` · Wide `>1440`(内容最大宽约 1280px 居中)。 +- 触控目标:主按钮最小 48×48px;输入框高 44px。 +- 窄屏优先减列数而非缩小卡片;表格改为横向滚动。 + +### 组件与样式 + +- 复用 `components/ui/`、`components/widgets/` 中已有基础组件;功能型复用看 `components/function/`。 +- 只有现有组件无法表达真实交互或可访问性需求时,才新增组件。 +- 样式优先用 Less,全局变量和 mixin 在 `styles/variables.less`、`styles/mixins.less`;主题通过 `store` 的 `initTheme` 和 `styles/themes/` 管理。 +- 新增或改版 UI 前先读 `DESIGN.md`,颜色、字号、间距、圆角、按钮层级按设计 token 落地。 +- 已混用 View UI Plus、Ant Design Vue、Tailwind;新增 UI 优先与当前页面所在模块保持一致,避免同一区域混用多套组件库;覆盖组件库默认样式时对齐 `DESIGN.md` 而非组件库原生色。 +- 修改样式时检查移动端和常见桌面宽度,避免文案溢出、控件遮挡和布局跳动。 + +### 国际化 + +- 用户可见文案必须维护在 `src/locales/`,不要在组件、服务或 store 中硬编码展示文案。 +- 新增 key 时同步维护 `en.json` 和 `zh.json`;提交前运行 `npm run check-i18n`。 +- 路由 `meta.title`、表格列名、按钮、提示、错误展示均走 i18n。 + +### API 与数据契约 + +- 修改接口字段、枚举、状态或错误码时,同步检查后端 VO / API、前端 service、页面逻辑和测试。 +- 后端已删除的字段,前端不要继续保留 fallback 行为。 +- 在增加兼容逻辑前,先确认后端真实协议。 +- 接口响应、状态机、权限判断、国际化 key 和错误码以当前真实实现为准,不要凭命名猜业务含义。 +- HTTP 错误统一走 `services/formatError.js` 和 `utils/errorQueueModal` 机制,不要各页面自行弹窗处理同类错误。 + +### 权限与路由 + +- 路由 `meta.requiredAuth` 声明页面所需权限码;侧边栏菜单通过 `utils/buildSidebarMenu.js` 按权限过滤。 +- 按钮级权限参考现有页面的 `v-if` / mixin 模式(如 `authMixin`),保持与后端权限码一致。 + +## 前后端契约 + +- 前后端契约必须保持一致。 +- 字段删除时,要同时删除后端、前端和测试中的对应逻辑。 +- 前端不要为后端已删除字段继续保留 fallback 行为。 +- DM Console API 通常走 `/console/api/` 或 `/rdp/console/api/` 前缀;具体路径以 `services/http/request.js` 和现有 api 文件为准。 + +## 实时连接与异步 + +- WebSocket 使用 `services/socket.js`(reconnecting-websocket);关注重连、终态和资源释放。 +- SQL 执行、结果导出、异步任务等长连接场景,明确 loading、取消、失败恢复和组件卸载时的清理。 +- 不要把阻塞操作放在主线程导致页面卡顿;大列表注意分页或虚拟滚动。 + +## 测试要求 + +- 默认不要新增无用测试;除非用户明确要求补测试,否则不要新增测试类。 +- 前端改动优先运行 `npm run lint`、`npm run check-i18n` 和相关单测(`npm run test:unit`)。 +- 用户可见流程变更要做浏览器级检查。 +- 测试覆盖真实风险路径:权限边界、接口异常、WebSocket 断开重连、表单校验与状态一致性。 + +## Review 规则 + +- Review 只提真实 bug 和明确 concern。 +- 不要堆风格噪音,除非它确实影响正确性、可维护性或契约清晰度。 +- 优先关注状态一致性、协议破坏、权限绕过、前后端字段不一致、i18n 遗漏、样式回归和偏离 `DESIGN.md` 的色彩/字重/圆角。 + +## PR 与提交 + +- PR 说明要清楚描述改了什么、为什么改、如何验证。 +- 提交信息使用 Conventional Commits,例如 `feat(frontend): add SSO settings page` 或 `fix(sql): fix operator panel overflow`。 +- 提交前确认没有把 `node_modules/`、`dist/`、日志、临时文件或无关格式化改动带入 diff。 diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md new file mode 100644 index 000000000..eef4bd20c --- /dev/null +++ b/frontend/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md \ No newline at end of file diff --git a/frontend/DESIGN.md b/frontend/DESIGN.md new file mode 100644 index 000000000..d885f4984 --- /dev/null +++ b/frontend/DESIGN.md @@ -0,0 +1,554 @@ +--- +version: alpha +name: Airtable-design-analysis +description: A sober, editorial workflow-software interface anchored on white canvas and dark-ink type, where brand voltage comes from full-bleed signature cards in coral, dark green, peach, and dark navy that punctuate long-scroll explainer pages. Primary actions use a near-black pill CTA; secondary actions sit in a white outlined button. Type runs Haas Grotesk in modest weights — never bold for its own sake. + +colors: + primary: "#181d26" + primary-active: "#0d1218" + ink: "#181d26" + body: "#333840" + muted: "#41454d" + hairline: "#dddddd" + border-strong: "#9297a0" + canvas: "#ffffff" + surface-soft: "#f8fafc" + surface-strong: "#e0e2e6" + surface-dark: "#181d26" + surface-dark-elevated: "#1d1f25" + signature-coral: "#aa2d00" + signature-forest: "#0a2e0e" + signature-cream: "#f5e9d4" + signature-peach: "#fcab79" + signature-mint: "#a8d8c4" + signature-yellow: "#f4d35e" + signature-mustard: "#d9a441" + on-primary: "#ffffff" + on-dark: "#ffffff" + link: "#1b61c9" + link-active: "#1a3866" + info: "#254fad" + info-border: "#458fff" + success: "#006400" + success-border: "#39bf45" + pricing-ink: "#1d1f25" + +typography: + display-xl: + fontFamily: "Haas Groot Disp, Haas, sans-serif" + fontSize: 48px + fontWeight: 500 + lineHeight: 1.1 + letterSpacing: 0 + display-lg: + fontFamily: "Haas Groot Disp, Haas, sans-serif" + fontSize: 40px + fontWeight: 400 + lineHeight: 1.2 + letterSpacing: 0 + display-md: + fontFamily: "Haas Groot Disp, Haas, sans-serif" + fontSize: 32px + fontWeight: 400 + lineHeight: 1.2 + letterSpacing: 0 + title-lg: + fontFamily: "Haas, sans-serif" + fontSize: 24px + fontWeight: 400 + lineHeight: 1.35 + letterSpacing: 0.12px + title-md: + fontFamily: "Haas Groot Disp, Haas, sans-serif" + fontSize: 20px + fontWeight: 400 + lineHeight: 1.5 + letterSpacing: 0 + title-sm: + fontFamily: "Haas, sans-serif" + fontSize: 18px + fontWeight: 500 + lineHeight: 1.4 + letterSpacing: 0 + label-md: + fontFamily: "Haas, sans-serif" + fontSize: 16px + fontWeight: 500 + lineHeight: 1.4 + letterSpacing: 0 + button: + fontFamily: "Haas, sans-serif" + fontSize: 16px + fontWeight: 500 + lineHeight: 1.4 + letterSpacing: 0 + body-md: + fontFamily: "Haas, sans-serif" + fontSize: 14px + fontWeight: 400 + lineHeight: 1.25 + letterSpacing: 0 + caption: + fontFamily: "Haas, sans-serif" + fontSize: 14px + fontWeight: 500 + lineHeight: 1.35 + letterSpacing: 0.16px + legal: + fontFamily: "Haas, sans-serif" + fontSize: 13.12px + fontWeight: 600 + lineHeight: 1.2 + letterSpacing: 0 + pricing-display: + fontFamily: "Inter Display, system-ui, sans-serif" + fontSize: 44.8px + fontWeight: 475 + lineHeight: 1.1 + letterSpacing: 0 + pricing-section: + fontFamily: "Inter Display, system-ui, sans-serif" + fontSize: 28px + fontWeight: 475 + lineHeight: 1.2 + letterSpacing: 0 + pricing-card-title: + fontFamily: "Inter Display, system-ui, sans-serif" + fontSize: 20px + fontWeight: 475 + lineHeight: 1.3 + letterSpacing: 0 + +rounded: + xs: 2px + sm: 6px + md: 10px + lg: 12px + pill: 9999px + full: 9999px + +spacing: + xxs: 4px + xs: 8px + sm: 12px + md: 16px + lg: 24px + xl: 32px + xxl: 48px + section: 96px + +components: + button-primary: + backgroundColor: "{colors.primary}" + textColor: "{colors.on-primary}" + typography: "{typography.button}" + rounded: "{rounded.lg}" + padding: 16px 24px + button-primary-active: + backgroundColor: "{colors.primary-active}" + textColor: "{colors.on-primary}" + rounded: "{rounded.lg}" + button-secondary: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.button}" + rounded: "{rounded.lg}" + padding: 16px 24px + button-secondary-on-dark: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.button}" + rounded: "{rounded.lg}" + padding: 16px 24px + button-legal: + backgroundColor: "{colors.link}" + textColor: "{colors.on-primary}" + typography: "{typography.legal}" + rounded: "{rounded.xs}" + padding: 12px 10px + button-icon-circular: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + rounded: "{rounded.full}" + size: 40px + button-pricing-pill: + backgroundColor: "{colors.canvas}" + textColor: "{colors.pricing-ink}" + typography: "{typography.button}" + rounded: "{rounded.pill}" + padding: 12px 24px + text-link: + backgroundColor: transparent + textColor: "{colors.link}" + typography: "{typography.body-md}" + top-nav: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.body-md}" + height: 64px + hero-band: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.display-lg}" + padding: 96px + signature-coral-card: + backgroundColor: "{colors.signature-coral}" + textColor: "{colors.on-primary}" + typography: "{typography.display-md}" + rounded: "{rounded.lg}" + padding: 48px + signature-forest-card: + backgroundColor: "{colors.signature-forest}" + textColor: "{colors.on-primary}" + typography: "{typography.display-md}" + rounded: "{rounded.lg}" + padding: 48px + hero-card-dark: + backgroundColor: "{colors.surface-dark}" + textColor: "{colors.on-dark}" + typography: "{typography.display-md}" + rounded: "{rounded.lg}" + padding: 48px + feature-card-tabbed: + backgroundColor: "{colors.surface-soft}" + textColor: "{colors.ink}" + typography: "{typography.title-lg}" + rounded: "{rounded.lg}" + padding: 32px + cream-callout-card: + backgroundColor: "{colors.signature-cream}" + textColor: "{colors.ink}" + typography: "{typography.title-lg}" + rounded: "{rounded.md}" + padding: 24px + demo-grid-card: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.label-md}" + rounded: "{rounded.md}" + padding: 16px + logo-strip: + backgroundColor: "{colors.canvas}" + textColor: "{colors.muted}" + typography: "{typography.body-md}" + padding: 32px + article-card: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.title-sm}" + rounded: "{rounded.md}" + padding: 16px + topic-filter-rail: + backgroundColor: "{colors.canvas}" + textColor: "{colors.body}" + typography: "{typography.body-md}" + width: 240px + text-input: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.body-md}" + rounded: "{rounded.sm}" + padding: 12px 16px + height: 44px + text-input-focus: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + rounded: "{rounded.sm}" + pricing-tier-card: + backgroundColor: "{colors.canvas}" + textColor: "{colors.pricing-ink}" + typography: "{typography.pricing-card-title}" + rounded: "{rounded.md}" + padding: 32px + pricing-tier-card-featured: + backgroundColor: "{colors.surface-soft}" + textColor: "{colors.pricing-ink}" + typography: "{typography.pricing-card-title}" + rounded: "{rounded.md}" + padding: 32px + pricing-comparison-row: + backgroundColor: "{colors.canvas}" + textColor: "{colors.body}" + typography: "{typography.body-md}" + padding: 12px + cta-band-light: + backgroundColor: "{colors.surface-strong}" + textColor: "{colors.ink}" + typography: "{typography.display-md}" + rounded: "{rounded.lg}" + padding: 48px + footer: + backgroundColor: "{colors.canvas}" + textColor: "{colors.body}" + typography: "{typography.body-md}" + padding: 64px +--- + +## Overview + +Airtable's marketing surfaces are quietly editorial. The base atmosphere is white canvas, dark ink type, generous whitespace, and a near-black pill CTA — nothing is fighting for attention until a section needs to. The brand voltage doesn't come from gradient washes or accent walls; it comes from **full-bleed signature cards** in `{colors.signature-coral}`, `{colors.signature-forest}`, and `{colors.surface-dark}` that punctuate long-scroll explainer pages every two or three screens. Between those signature bands, the page reads like a print magazine: a headline, supporting copy, a small image cluster, then breathing room. + +Type voice is Haas Grotesk at modest weights (400 for display, 500 for sub-titles and buttons). Display headlines never go bolder than 500 — emphasis comes from size and color contrast, not from weight. Body copy stays at 14px / 400 throughout. The pricing surface runs its own dialect: **Inter Display** at unusual mid-weights (475 / 575) and **pill-shaped buttons** (`{rounded.pill}`) that don't appear on any other page — a deliberate sub-system signaling "this page is about commercial precision." + +**Key Characteristics:** +- Primary CTA is `{colors.primary}` (near-black ink) with white text and a `{rounded.lg}` (12px) corner — it reads as confident and final, never decorative. +- Secondary CTA is a `{colors.canvas}` button with `{colors.ink}` text and a hairline outline. The two together form Airtable's signature button pair. +- Hero is white canvas. There is no atmospheric gradient, no mesh, no background flourish. The brand strength comes from the type and the buttons sitting in clean whitespace. +- Brand voltage lives in **signature surface cards**: `{colors.signature-coral}`, `{colors.signature-forest}`, and `{colors.surface-dark}` carry full-bleed product callouts every few screens. +- Demo-card grids carry product UI fragments on `{colors.signature-peach}`, `{colors.signature-mint}`, `{colors.signature-cream}` and other warm pastel surfaces. +- Section rhythm: white canvas → coral signature card → white body → cream callout band → dark navy CTA → light gray CTA banner → footer. The canvas resets between every signature surface. +- Border radius is hierarchical: `{rounded.lg}` (12px) for primary CTAs and large signature cards, `{rounded.md}` (10px) for content cards and demo grids, `{rounded.sm}` (6px) for inputs, `{rounded.full}` for icon buttons. Pricing buttons jump to `{rounded.pill}` to mark themselves as a separate dialect. +- Vertical rhythm is `{spacing.section}` (96px) between major bands — universal across every page. + +## Colors + +### Brand & Accent +- **Primary** (`{colors.primary}` — #181d26): The dominant brand color. Used for the primary CTA background, h1/h2 display type, and the `{component.surface-dark}` band. Not "blue, then black" — black IS the primary throughout the marketing system. +- **Primary Active** (`{colors.primary-active}` — #0d1218): The press state on primary buttons. + +### Surface +- **Canvas** (`{colors.canvas}` — #ffffff): The default page surface; the floor of every editorial body. +- **Surface Soft** (`{colors.surface-soft}` — #f8fafc): Tabbed feature cards and the featured pricing tier. +- **Surface Strong** (`{colors.surface-strong}` — #e0e2e6): The light gray "Start building with Airtable" CTA banner near the footer. +- **Surface Dark** (`{colors.surface-dark}` — #181d26): The dark navy CTA cards used mid-page (for example "The path to 10× every person in your organization"). +- **Surface Dark Elevated** (`{colors.surface-dark-elevated}` — #1d1f25): The articles-page hero base behind the rainbow-stripe overlay. +- **Hairline** (`{colors.hairline}` — #dddddd): The 1px border tone for input outlines, table dividers, secondary-button outlines. + +### Text +- **Ink** (`{colors.ink}` — #181d26): The strongest text — h1/h2 display type and primary button text-on-light. Same hex as `{colors.primary}` because they are the same role expressed at type and button layers. +- **Body** (`{colors.body}` — #333840): The default running-text color. +- **Muted** (`{colors.muted}` — #41454d): Footer links, breadcrumbs, captions. +- **Border Strong** (`{colors.border-strong}` — #9297a0): The 1px outline color on disabled secondary buttons. +- **On Primary / On Dark** (`{colors.on-primary}` — #ffffff): The text color on primary buttons and dark surfaces. + +### Signature Card Surfaces +These are the colors that carry Airtable's brand voltage. They appear as full-bleed, full-card surfaces — never as accents on a small element. +- **Coral** (`{colors.signature-coral}` — #aa2d00): The largest signature card on the homepage ("Production apps in prototype speed"). Full-bleed dark coral with white type. +- **Forest** (`{colors.signature-forest}` — #0a2e0e): A deep-green signature card used in the homepage demo-grid cluster. +- **Cream** (`{colors.signature-cream}` — #f5e9d4): The cream callout band ("The path to 10× every person in your organization") — a soft beige surface holding dark type and product UI fragments. +- **Peach** (`{colors.signature-peach}` — #fcab79), **Mint** (`{colors.signature-mint}` — #a8d8c4), **Yellow** (`{colors.signature-yellow}` — #f4d35e), **Mustard** (`{colors.signature-mustard}` — #d9a441): Demo-card surfaces that carry small product UI fragments inside the multi-card grid sections. + +### Semantic +- **Link** (`{colors.link}` — #1b61c9): Inline body links and anchor text. Darker on press to `{colors.link-active}` (#1a3866). Despite the `--theme_button-background-primary` CSS-variable name, this color is **not** the primary button color — it is the link color. +- **Info** (`{colors.info}` — #254fad) and **Info Border** (`{colors.info-border}` — #458fff): Inline info badges and focused-input outline. +- **Success** (`{colors.success}` — #006400) and **Success Border** (`{colors.success-border}` — #39bf45): Confirmation states. + +## Typography + +### Font Family +The system runs **Haas / Haas Groot Disp** (Airtable's licensed display + text type). Haas Groot Disp covers display sizes (h1 / h2); Haas Grotesk covers everything 24px and below. The fallback stack walks `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif`. + +The pricing surface runs a separate **Inter Display** stack at mid-weights (475 / 575) — a deliberate sub-system signaling commercial precision. + +### Hierarchy + +| Token | Size | Weight | Line Height | Letter Spacing | Use | +|---|---|---|---|---|---| +| `{typography.display-xl}` | 48px | 500 | 1.1 | 0 | Articles page h2 — second-tier editorial headline | +| `{typography.display-lg}` | 40px | 400 | 1.2 | 0 | Homepage h1 hero | +| `{typography.display-md}` | 32px | 400 | 1.2 | 0 | Platform-page h2 — feature-section headlines | +| `{typography.title-lg}` | 24px | 400 | 1.35 | 0.12px | Section titles | +| `{typography.title-md}` | 20px | 400 | 1.5 | 0 | Sub-section titles in tabbed feature cards | +| `{typography.title-sm}` | 18px | 500 | 1.4 | 0 | Article-card titles | +| `{typography.label-md}` | 16px | 500 | 1.4 | 0 | Demo-card titles, list labels | +| `{typography.button}` | 16px | 500 | 1.4 | 0 | Standard CTA button labels | +| `{typography.body-md}` | 14px | 400 | 1.25 | 0 | Body copy, footer links, top-nav items | +| `{typography.caption}` | 14px | 500 | 1.35 | 0.16px | Light captions and meta text | +| `{typography.legal}` | 13.12px | 600 | 1.2 | 0 | Cookie/legal CTA buttons | +| `{typography.pricing-display}` | 44.8px | 475 | 1.1 | 0 | Pricing-page h1 | +| `{typography.pricing-section}` | 28px | 475 | 1.2 | 0 | Pricing-page section heads | +| `{typography.pricing-card-title}` | 20px | 475 | 1.3 | 0 | Pricing tier card plan name | + +### Principles +The Haas system prefers weight 400 for display sizes — a 40px h1 is **not** bold. Visual emphasis is delegated to size, color contrast, and the signature surface cards. Where the system does want weight, it pivots to 500 (sub-titles, buttons, article titles), never 600 or 700 in the editorial body. The only true bold (600) lives in `{typography.legal}` — a sign that boldness is reserved for terms-of-service surfaces, not marketing. + +The pricing-page sub-system uses Inter Display at `font-weight: 475` — a custom mid-weight between regular (400) and medium (500), shipped as a variable font. + +### Note on Font Substitutes +If Haas Groot Disp and Haas Grotesk are unavailable, **Inter Display** (variable) is the closest open-source substitute for both — adjust line-height down by ~5% to match Haas's tighter cap-height. For the pricing sub-system, use Inter Display directly. On macOS / iOS, **system-ui** is sufficient; on Windows, the chain falls through to Segoe UI, which is a usable but slightly cooler substitute. + +## Layout + +### Spacing System +- **Base unit:** 4px (all spacing snaps to 4-multiples). +- **Tokens:** `{spacing.xxs}` 4px · `{spacing.xs}` 8px · `{spacing.sm}` 12px · `{spacing.md}` 16px · `{spacing.lg}` 24px · `{spacing.xl}` 32px · `{spacing.xxl}` 48px · `{spacing.section}` 96px. +- **Section padding (vertical):** `{spacing.section}` (96px) is the universal vertical rhythm constant — every major editorial band on every page uses 96px top + 96px bottom internal padding. +- **Card internal padding:** `{spacing.xl}` (32px) for tabbed feature cards and pricing tier cards; `{spacing.xxl}` (48px) inside signature coral / forest / dark cards; `{spacing.lg}` (24px) for cream callouts and demo-grid cards. +- **Gutters:** `{spacing.lg}` (24px) between cards in 3-up grids; `{spacing.md}` (16px) inside denser logo strips and footer column gutters. + +### Grid & Container +- **Max content width:** ~1280px centered, with `{spacing.xxl}` (48px) horizontal breathing room. +- **Editorial body:** Single 8/12-column at large breakpoints, collapsing to single-column on mobile. +- **Demo-card grids:** 3 or 4 columns at desktop, 2 at tablet, 1 at mobile. Card sizes are deliberately uneven within the grid to dodge a uniform "spec sheet" feel. +- **Logo strip:** 6 monochrome partner logos in a single row at desktop; wraps to 3-up on mobile. + +### Whitespace Philosophy +Airtable uses whitespace as the dominant atmospheric tool. Hero sections sit in 96px+ of pure whitespace above and below the headline + sub-headline pair, with no decoration in that whitespace. The hero is intentionally calm — there is no gradient, no aurora, no atmospheric mesh behind the type. The system trusts whitespace alone to do the framing. + +## Elevation & Depth + +| Level | Treatment | Use | +|---|---|---| +| Flat | No shadow, no border | Body sections, top nav, footer | +| Soft hairline | 1px `{colors.hairline}` border | Inputs, sub-nav rails, comparison-table dividers, secondary buttons | +| Button rest | Soft drop with subtle blue-tinted glow at low alpha | Primary CTA buttons (the blue tint is a holdover from the link color and reads as a faint accent under the dark button) | +| Button focus | Outer 2px blue ring at higher alpha | Keyboard focus state on primary buttons | +| Card flat | No shadow; relies on color contrast against the surface band | Signature coral / forest / dark cards, cream callouts, demo-grid cards | + +The elevation philosophy is **color-block first, shadow second**. Shadows are minimal; depth is delegated to the contrast between white canvas and signature surface cards. There is no soft-glow / atmospheric-shadow / heavy-elevation language anywhere in the marketing system. + +### Decorative Depth +- **Vertical rainbow stripes** appear on the articles hero only — multi-color vertical bands sitting on `{colors.surface-dark-elevated}`. This is a single-page treatment, not a system-wide signature. +- **Photography-as-depth** in the demo-card grid: every card carries a real product UI screenshot or mockup, contributing depth through legible artifact density rather than decorative effects. + +## Shapes + +### Border Radius Scale + +| Token | Value | Use | +|---|---|---| +| `{rounded.xs}` | 2px | Cookie-consent and legal CTA buttons — system-required surfaces | +| `{rounded.sm}` | 6px | Text inputs, small inline buttons | +| `{rounded.md}` | 10px | Secondary content cards, article cards, cream callouts | +| `{rounded.lg}` | 12px | Primary CTA buttons, signature surface cards, tabbed feature cards | +| `{rounded.pill}` | 9999px | Pricing-page CTA buttons (sub-system only) | +| `{rounded.full}` | 9999px / 50% | Circular icon buttons, avatar surfaces | + +### Photography Geometry +Product UI screenshots inside demo-card grids retain native aspect ratios (typically 4:3 or 16:10) and crop into `{rounded.md}` containers. Hero illustrations bleed full-width with no rounding. Article-card thumbnails use 16:9 with `{rounded.md}` corners. Avatars in testimonials use `{rounded.full}` (perfect circles). Pricing comparison table images stay rectangular with no rounding. + +## Components + +> **No hover states documented.** Per the global no-hover policy (Step 6), every component spec below documents only Default and Active/Pressed states. Variants live as separate entries in the `components:` front matter. + +**`top-nav`** — A 64px-tall white bar pinned to the top of every page. Airtable wordmark sits at left; primary horizontal menu (Platform, Solutions, Resources, Enterprise, Pricing) sits center-left in `{typography.body-md}`; the right cluster carries a "Book Demo" outline link, "Sign up for free" `{component.button-primary}`, and "Log In" text link. The nav stays light on every page — Airtable does not invert the nav over dark sections. + +### Buttons + +**`button-primary`** — The signature primary CTA. Background `{colors.primary}` (near-black), text `{colors.on-primary}`, type `{typography.button}`, padding 16px × 24px, rounded `{rounded.lg}` (12px). This is the "Get started for free" / "Sign up for free" button visible on every hero. It reads as confident and final — not decorative — which is why the system uses it sparingly (one per viewport). +- Active state: `button-primary-active` darkens to `{colors.primary-active}` (#0d1218). + +**`button-secondary`** — White outline button (e.g. "Book demo"). Background `{colors.canvas}`, text `{colors.ink}`, type `{typography.button}`, rounded `{rounded.lg}` (12px), 1px hairline outline. Sits next to `{component.button-primary}` as the "less-committed" choice. + +**`button-secondary-on-dark`** — Same shape as `{component.button-secondary}` but used on signature coral / forest / dark surfaces. Background `{colors.canvas}`, text `{colors.ink}` — the white button stays white over dark surfaces because the system never inverts to a translucent on-dark style on the marketing site. + +**`button-pricing-pill`** — The pricing-page CTA family. Background `{colors.canvas}`, text `{colors.pricing-ink}`, rounded `{rounded.pill}` (9999px), padding 12px × 24px. The only place pill-shape appears in the marketing system. Treat it as part of the pricing sub-system signaling. + +**`button-legal`** — Cookie-consent and legal-banner CTAs. Background `{colors.link}`, text `{colors.on-primary}`, type `{typography.legal}` (13.12px / 600), rounded `{rounded.xs}` (2px), padding 12px × 10px. The 2px corner radius and 600 weight signal "this is a required system surface," not a designed brand surface. + +**`button-icon-circular`** — 40px × 40px circular button with `{colors.canvas}` background, hairline border, and `{colors.ink}` icon. Used for carousel controls, "share", and "back" affordances. + +**`text-link`** — Inline body links in `{colors.link}` (#1b61c9, the actual link blue). No underline by default. Type inherits `{typography.body-md}`. + +### Cards & Containers + +**`hero-band`** — The full-page-width white-canvas hero. No surface card, no border, no shadow, no atmospheric gradient — just the headline, sub-headline, and primary + secondary button pair sitting in 96px of whitespace. Vertical padding `{spacing.section}` (96px). + +**`signature-coral-card`** — The large full-bleed coral card on the homepage ("Production apps in prototype speed"). Background `{colors.signature-coral}` (#aa2d00, a dark coral / oxide red), text `{colors.on-primary}`, rounded `{rounded.lg}` (12px), internal padding `{spacing.xxl}` (48px). Carries an h2 in `{typography.display-md}`, supporting copy in `{typography.body-md}`, and `{component.button-secondary-on-dark}` as the CTA. + +**`signature-forest-card`** — A deep green signature card (`{colors.signature-forest}` — #0a2e0e) used as a demo-grid sibling to the coral card on the homepage. + +**`hero-card-dark`** — The dark navy mid-page CTA card (e.g. "The path to 10× every person in your organization"). Background `{colors.surface-dark}` (#181d26), text `{colors.on-dark}`, rounded `{rounded.lg}` (12px), internal padding `{spacing.xxl}` (48px). The same color as `{colors.primary}` because the system uses ink as both type color and signature dark surface. + +**`feature-card-tabbed`** — Light-cream cards (e.g. the "Coke / Pelosi / Conde Nast / Time Inc" tabbed feature card on the homepage). Background `{colors.surface-soft}`, rounded `{rounded.lg}` (12px), internal padding `{spacing.xl}` (32px). Left rail carries vertically-stacked tab labels in `{typography.title-md}`; right pane shows the active tab's content (illustration + body copy + small CTA). + +**`cream-callout-card`** — Beige callout cards (`{colors.signature-cream}`). Rounded `{rounded.md}` (10px), internal padding `{spacing.lg}` (24px). Carry product UI fragments or stat callouts — softer than the dark/coral signature cards but still a deliberate brand surface. + +**`demo-grid-card`** — Used in multi-card grids that punctuate every page. Background `{colors.canvas}` or one of the demo-grid surfaces (`{colors.signature-peach}`, `{colors.signature-mint}`, `{colors.signature-yellow}`, `{colors.signature-mustard}`), rounded `{rounded.md}` (10px), internal padding `{spacing.md}` (16px). Each card frames a product UI fragment. Card heights vary deliberately to dodge a uniform "spec sheet" feel. + +**`logo-strip`** — Horizontal monochrome partner-logo row (HBO, Netflix, Amazon, Time, Conde Nast). Logos render in `{colors.muted}`, surface is `{colors.canvas}`, vertical padding `{spacing.xl}` (32px). 6 logos at desktop, 3 at mobile. + +**`article-card`** — The trending-stories grid on the articles page. Background `{colors.canvas}`, rounded `{rounded.md}` (10px), internal padding `{spacing.md}` (16px). Each card carries a colorful illustrated thumbnail (16:9), a small uppercase category tag, an `{typography.title-sm}` title, and a meta line. 3-up at desktop. + +**`topic-filter-rail`** — The left rail on the articles page. 240px wide, `{colors.canvas}` background, `{typography.body-md}`, vertically grouped category headings ("Marketing", "Product", "Project management", "Operations") with sub-bullets. Active item carries a small numeric count badge. + +### Inputs & Forms + +**`text-input`** — Standard text input. Background `{colors.canvas}`, text `{colors.ink}`, type `{typography.body-md}`, rounded `{rounded.sm}` (6px), padding 12px × 16px, height 44px. 1px hairline border in `{colors.hairline}`. + +**`text-input-focus`** — Focus state. Border thickens or recolors to `{colors.info-border}`. + +### Pricing Sub-System + +**`pricing-tier-card`** — Standard tier card. Background `{colors.canvas}`, text `{colors.pricing-ink}`, type `{typography.pricing-card-title}` for the plan name, rounded `{rounded.md}` (10px), internal padding `{spacing.xl}` (32px). Carries the plan name, a price block in `{typography.pricing-display}` (44.8px / 475), feature checklist, and a `{component.button-pricing-pill}` at the bottom. + +**`pricing-tier-card-featured`** — The featured tier (typically "Team" or "Business"). Background shifts to `{colors.surface-soft}`. No accent border, no badge — the background tone shift is the only signal. + +**`pricing-comparison-row`** — Each row of the long comparison table at the bottom of the pricing page. Labels in the left column; checkmarks or values across 4 plan columns. 12px vertical padding per row, hairline divider between rows. + +### Navigation Variants + +**`footer`** — Light surface (`{colors.canvas}`), 6-column link list at desktop covering Platform / Solutions / Resources / Learn / Company sub-trees. Vertical padding `{spacing.section}` divided across upper link block and lower legal row. Type `{typography.body-md}`. + +**`cta-band-light`** — The light gray "Start building with Airtable" CTA strip near the footer. Background `{colors.surface-strong}` (#e0e2e6), text `{colors.ink}`, rounded `{rounded.lg}` (12px), padding `{spacing.xxl}` (48px). Carries an h2 in `{typography.display-md}` and a `{component.button-primary}`. + +### Signature Components + +**Articles Vertical Rainbow Stripe Hero** — The articles-page hero treatment. Multi-color vertical bands at varying widths sitting on `{colors.surface-dark-elevated}`. The h1 + sub-head + CTA cluster sits center-left on top of the stripes. This is a single-page hero treatment, not a system-wide signature — do not promote it to a multi-page pattern. + +## Do's and Don'ts + +### Do +- Keep `{component.button-primary}` near-black. The brand's primary CTA is `{colors.primary}`, not the link blue. Mixing them up turns a confident hero into a confused one. +- Reserve `{component.button-primary}` for one primary action per viewport. The system is designed for scarcity at the brand-action layer. +- Use `{component.button-secondary}` (white with hairline outline) as the natural pair with `{component.button-primary}`. The two together form Airtable's signature button row. +- Trust whitespace as the hero atmosphere. Hero bands are intentionally calm — no gradient, no mesh, no atmospheric backdrop. Going against this reads as off-brand. +- Use `{component.signature-coral-card}`, `{component.signature-forest-card}`, and `{component.hero-card-dark}` to break editorial monotony. These are the brand's voltage moments. +- Keep `{component.demo-grid-card}` heights uneven within a grid. Uniform heights feel like a spec sheet. +- Treat the pricing surface as its own dialect: keep `{typography.pricing-display}`, `{typography.pricing-card-title}`, and `{component.button-pricing-pill}` together. Mixing them with Haas Grotesk button type breaks the sub-system's voice. +- Anchor every editorial band with `{spacing.section}` (96px) vertical padding. + +### Don't +- Don't make `{colors.link}` (#1b61c9) the primary button color. It is the link color. The primary button is `{colors.primary}` (#181d26, near-black). Treating link-blue as the brand action is the most common mistake when reading Airtable's CSS variables. +- Don't add a gradient backdrop to the hero. Airtable's hero is white, full stop. Mesh, aurora, spotlight gradients all read as "another SaaS template" — not Airtable. +- Don't bold display-weight type. `{typography.display-xl}` and `{typography.display-lg}` are intentionally weight 400 / 500 — going to 700 reads as marketing-page-template. +- Don't use `{rounded.pill}` outside the pricing surface. It's a sub-system signal, not a general radius option. +- Don't repeat the same surface mode in two consecutive bands. The editorial pacing depends on rhythm: white → signature card → white → cream → dark → white. Two whites in a row read as a typography blog. +- Don't add hover state styling beyond what the system already encodes. The system documents Default and Active/Pressed only. +- Don't introduce additional accent colors beyond the documented signature card palette. The system's voltage already uses coral, forest, dark navy, cream, peach, mint, yellow, and mustard. + +## Responsive Behavior + +### Breakpoints + +| Name | Width | Key Changes | +|---|---|---| +| Mobile | < 768px | Single-column body; top nav collapses to hamburger; demo-grid drops to 1-up; signature cards stay full-bleed; logo strip wraps to 2 rows; footer collapses to single-column | +| Tablet | 768–1024px | 2-up demo-grid; top nav stays horizontal but tightens; cream-callout cards stack 2-up; pricing comparison table becomes horizontally scrollable | +| Desktop | 1024–1440px | 3-up demo-grid (and 4-up for tighter content); full top-nav with all menu items visible; pricing tier cards render 4-across | +| Wide | > 1440px | Same as Desktop with more outer breathing room; max content width caps at ~1280px and the page adds outer margin rather than scaling type up | + +### Touch Targets +- `{component.button-primary}` and siblings render at 48 × 48px minimum (16px vertical padding + 16px line-height) — comfortably above WCAG AAA's 44 × 44. +- `{component.button-icon-circular}` is exactly 40 × 40px — slightly under WCAG's recommended 44, but the centered icon and dot-radius compensate visually. +- `{component.text-input}` height is 44px. + +### Collapsing Strategy +- Top nav collapses to a hamburger at < 768px; the menu opens as a full-screen sheet rather than a dropdown. +- Card grids reduce columns rather than scaling cards down. +- The `{component.feature-card-tabbed}` re-stacks the tab rail above the content pane on mobile. +- The pricing comparison table converts to horizontally-scrollable swipe at < 1024px; the four plan headers stay visible while body rows scroll. + +### Image Behavior +- Demo-card UI screenshots crop to fit their container rather than scaling up. +- Hero illustrations bleed full-width on mobile, losing horizontal margin. +- Signature card images (inside coral / forest / dark cards) compress to their card width without cropping. + +## Iteration Guide + +1. Focus on ONE component at a time. Reference its YAML key directly (`{component.button-primary}`, `{component.signature-coral-card}`). +2. When adding a new component, decide first which sub-system it belongs to: the main editorial system (Haas, `{rounded.lg}`/`{rounded.md}`) or the pricing sub-system (Inter Display, `{rounded.pill}`). +3. Variants of an existing component (`-active`, `-disabled`, `-focus`) live as separate entries in `components:` — never as nested state objects. +4. Use `{token.refs}` everywhere prose mentions a color, a radius, a typography role, or a spacing value. Hex codes appear at most once next to the reference. +5. Never document hover. The system documents Default and Active/Pressed states only. +6. Run `npx @google/design.md lint DESIGN.md` after edits — `broken-ref`, `contrast-ratio`, and `orphaned-tokens` warnings flag issues automatically. +7. When in doubt about emphasis: bigger type before bolder type, signature surface card before solid accent. + +## Known Gaps + +- The exact hex values of pastel demo-grid surfaces (`{colors.signature-peach}`, `{colors.signature-mint}`, `{colors.signature-yellow}`, `{colors.signature-mustard}`) are inferred from screenshot pixel sampling. Some product launches may swap these surfaces seasonally. +- Hover behavior across all components is not documented (per global no-hover policy). +- Animation and transition timings are not in scope. +- Form validation states beyond `text-input-focus` are not extracted — error and success states for inputs would need a dedicated form page to confirm. +- The pricing comparison table's checkmark glyph and column-divider widths are described structurally but not formalized as tokens. +- The CSS variable `--theme_button-background-primary: #1b61c9` exists at `:root` but is not used as the primary CTA color anywhere on the marketing site. It maps to the link/info color role instead. Documented here so future extractions don't re-trip over the misleading variable name. diff --git a/frontend/src/App.vue b/frontend/src/App.vue index adbf68404..91746ce18 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,5 +1,5 @@