新特性:设计系统(Design System)构建 —— 建立团队的 Token 变量库
文章目录前言一、为什么需要 Token 变量库1.1 硬编码的四大问题1.2 Token 解决什么1.3 与现有 DSTokens.ets 的关系二、HarmonyOS 资源体系速览2.1 目录结构2.2 引用语法2.3 单位约定三、命名规范语义化而非描述色值3.1 推荐格式3.2 好的命名 vs 坏的命名3.3 完整 Token 清单Demo四、定义颜色 Token4.1 base/element/color.json4.2 dark/element/color.json4.3 对比一览五、定义间距与圆角 Token5.1 float.json 完整定义5.2 4vp 网格原则5.3 引用示例六、在组件中消费 Token6.1 迁移前 vs 迁移后6.2 Demo 组件画廊6.3 色板数据驱动七、暗色模式零代码适配7.1 原理7.2 开发注意事项7.3 容器色的暗色处理八、团队落地工作流8.1 设计 → 开发协作8.2 渐进式迁移策略8.3 与 Styles / Extend 结合8.4 Code Review 检查项九、常见误区9.1 「Token 就是常量类」9.2 「暗色模式在组件里判断」9.3 「Token 名用色值描述」9.4 「间距随意定」9.5 「只定义亮色」十、Token 规模参考十一、总结前言打开一个三个月前写的页面你可能会看到.fontColor(#6B7280).padding(12).borderRadius(10).backgroundColor(#2563EB)当时觉得「就几个魔法数字记得住」。三个月后品牌色从#2563EB换成#0A59F7设计师要求暗色模式适配间距从 12 统一调到 16——你需要在几十个文件里全文搜索替换漏一处就是视觉不一致。这就是没有设计系统的代价。Design System设计系统的核心产物之一是Token 变量库——把颜色、间距、圆角、字号等视觉属性抽象成语义化变量集中定义在resources目录组件通过$r(app.color.ds_text_primary)引用而非硬编码。HarmonyOS 的资源体系basedark双目录天然支持主题切换。本文结合项目 Demo从 Token 理念、资源定义、引用方式、暗色适配到组件落地做一次完整实战解析。读完后你应该能回答什么是语义化 Token和直接写色值有什么区别颜色、间距、圆角分别放在哪个 JSON 文件$r()如何在 ArkUI 组件中使用如何实现「改一处 resources全 App 换肤」一、为什么需要 Token 变量库1.1 硬编码的四大问题问题后果散落各处同一个#2563EB出现 80 次改品牌色要改 80 处语义缺失#6B7280是标题色还是提示色新人无法判断无法换肤暗色模式需在每个组件写if (isDark)协作断裂设计师给 Figma Token开发手写 hex两边对不上1.2 Token 解决什么Token 是设计决策的命名快照设计师说「主文本用 text-primary」 开发写 .fontColor($r(app.color.ds_text_primary)) 资源文件ds_text_primary #111827亮色/ #F9FAFB暗色改色只改color.json。加暗色只加dark/color.json同名条目。组件代码一行不动。1.3 与现有 DSTokens.ets 的关系项目中designsystem/tokens/DSTokens.ets用 TypeScript 常量定义 TokenexportclassDSColor{staticreadonlybrandPrimary#0A59F7}这适合纯逻辑层或跨 module 编译期引用但不支持系统级主题切换——暗色模式来了要手动判断。本文推荐的resources Token则由框架在运行时按主题自动选取是 HarmonyOS 官方推荐的视觉变量管理方式。两者可并存resources 管颜色/尺寸ETS 类管枚举和组件变体。二、HarmonyOS 资源体系速览2.1 目录结构entry/src/main/resources/ ├── base/ ← 默认亮色资源 │ └── element/ │ ├── color.json ← 颜色 Token │ ├── float.json ← 间距/圆角/字号 Token │ └── string.json └── dark/ ← 暗色覆写 └── element/ └── color.json ← 同名 Token暗色值系统根据当前主题自动选择base或dark目录下的同名资源。组件始终引用$r(app.color.ds_text_primary)无需关心当前是亮是暗。2.2 引用语法// 颜色.fontColor($r(app.color.ds_text_primary)).backgroundColor($r(app.color.ds_bg_surface))// 间距 / 圆角 / 字号.padding($r(app.float.ds_space_lg)).borderRadius($r(app.float.ds_radius_md)).fontSize($r(app.float.ds_font_body1))格式$r(app.type.name)其中type对应 JSON 文件名color、float、string。2.3 单位约定文件常用单位用途color.json#RRGGBB颜色值float.jsonvp间距、圆角随屏幕密度缩放float.jsonfp字号随系统字体缩放Demo 间距用vp字号用fp符合 HarmonyOS 适配规范。三、命名规范语义化而非描述色值3.1 推荐格式ds_category_role部分含义示例ds_设计系统前缀避免与业务资源冲突—category类别text、bg、space、radiusrole语义角色primary、secondary、md、lg3.2 好的命名 vs 坏的命名✅ 语义化❌ 描述性ds_text_primaryds_gray_900ds_brand_primaryds_blue_500ds_bg_surfaceds_whiteds_space_mdds_twelve_vp语义化命名的好处品牌色从蓝变绿Token 名ds_brand_primary不用改只改值。ds_blue_500则名实不符制造混乱。3.3 完整 Token 清单Demo颜色17 个类别Token品牌/语义ds_brand_primary,ds_brand_primary_container,ds_success,ds_success_container,ds_warning,ds_warning_container,ds_danger,ds_danger_container文本ds_text_primary,ds_text_secondary,ds_text_tertiary,ds_text_inverse表面ds_bg_page,ds_bg_surface,ds_bg_elevated,ds_border_default,ds_border_strong,ds_divider间距6 级4vp 网格Token值ds_space_xs4vpds_space_sm8vpds_space_md12vpds_space_lg16vpds_space_xl24vpds_space_xxl32vp圆角5 级Token值典型场景ds_radius_sm6vp小标签、徽标ds_radius_md10vp按钮、输入框ds_radius_lg16vp卡片、面板ds_radius_xl24vp底部弹窗ds_radius_full999vp胶囊、圆形字阶6 级Token值ds_font_caption10fpds_font_body212fpds_font_body114fpds_font_subtitle16fpds_font_title20fpds_font_headline24fp四、定义颜色 Token4.1 base/element/color.json{color:[{name:ds_brand_primary,value:#2563EB},{name:ds_text_primary,value:#111827},{name:ds_bg_page,value:#F9FAFB},{name:ds_bg_surface,value:#FFFFFF}]}容器色*_container是 Material Design 3 风格主色的低饱和背景用于标签、选中态、提示区块。Demo 中ds_brand_primary_container #EFF6FF与ds_brand_primary #2563EB配对使用。4.2 dark/element/color.json暗色目录中定义同名 Token赋予不同值{color:[{name:ds_text_primary,value:#F9FAFB},{name:ds_bg_page,value:#111827},{name:ds_bg_surface,value:#1F2937},{name:ds_brand_primary,value:#60A5FA}]}注意暗色模式下品牌色通常提高亮度#2563EB→#60A5FA而非简单反转以保证对比度和可读性。4.3 对比一览Token亮色 (base)暗色 (dark)ds_text_primary#111827#F9FAFBds_text_secondary#6B7280#9CA3AFds_bg_page#F9FAFB#111827ds_bg_surface#FFFFFF#1F2937ds_brand_primary#2563EB#60A5FADemo 在「色彩 Token」场景底部有暗色模式提示切换系统暗色后所有引用$r(app.color.ds_*)的组件自动更新无需任何代码修改。五、定义间距与圆角 Token5.1 float.json 完整定义{float:[{name:ds_space_xs,value:4vp},{name:ds_space_sm,value:8vp},{name:ds_space_md,value:12vp},{name:ds_space_lg,value:16vp},{name:ds_space_xl,value:24vp},{name:ds_space_xxl,value:32vp},{name:ds_radius_sm,value:6vp},{name:ds_radius_md,value:10vp},{name:ds_radius_lg,value:16vp},{name:ds_radius_xl,value:24vp},{name:ds_radius_full,value:999vp}]}5.2 4vp 网格原则间距刻度以4vp 为基数4、8、12、16、24、32。这保证所有间距是 4 的倍数视觉节奏统一设计师在 Figma 中用 8px 网格开发用 8vp心算对齐避免padding(13)这种「拍脑袋」数值Demo「间距 Token」场景用色条直观展示各级长度并附有 padding 实战卡片。5.3 引用示例// 统一内边距.padding($r(app.float.ds_space_lg))// 方向性内边距.padding({left:$r(app.float.ds_space_md),right:$r(app.float.ds_space_md),top:$r(app.float.ds_space_sm),bottom:$r(app.float.ds_space_sm)})// 圆角.borderRadius($r(app.float.ds_radius_lg))// Column 间距Column({space:12})// 注意space 参数目前只接受 number暂不能用 Resource最后一个注意点Column({ space })等布局参数的space在某些 API 版本只接受number不支持$r()。这类场景可暂时保留数字或在Builder封装层统一处理。颜色、padding、borderRadius、fontSize 等属性已广泛支持 Resource 类型。六、在组件中消费 Token6.1 迁移前 vs 迁移后// ❌ 硬编码Text(标题).fontSize(22).fontColor(#111827)Column().padding(16).backgroundColor(#FFFFFF).borderRadius(12)// ✅ Token 驱动Text(标题).fontSize($r(app.float.ds_font_headline)).fontColor($r(app.color.ds_text_primary))Column().padding($r(app.float.ds_space_lg)).backgroundColor($r(app.color.ds_bg_surface)).borderRadius($r(app.float.ds_radius_lg))6.2 Demo 组件画廊「组件应用」场景展示四类组件全部零硬编码按钮Text(主按钮).fontColor($r(app.color.ds_text_inverse)).backgroundColor($r(app.color.ds_brand_primary)).borderRadius($r(app.float.ds_radius_md)).padding({left:$r(app.float.ds_space_md),right:$r(app.float.ds_space_md),top:$r(app.float.ds_space_sm),bottom:$r(app.float.ds_space_sm)})卡片Column({space:8}).padding($r(app.float.ds_space_lg)).backgroundColor($r(app.color.ds_bg_surface)).borderRadius($r(app.float.ds_radius_lg)).border({width:1,color:$r(app.color.ds_border_default)})标签品牌/成功/警告/危险四色Text(成功).fontColor($r(app.color.ds_success)).backgroundColor($r(app.color.ds_success_container)).borderRadius($r(app.float.ds_radius_full))语义色 容器色配对是设计系统的标准做法文字用高饱和色背景用低饱和同色系。6.3 色板数据驱动Demo 将 Token 组织为可遍历数组便于文档化展示interfaceColorSwatch{id:stringtoken:stringlabel:stringcolor:ResourceColor}constBRAND_SWATCHES:ColorSwatch[][{id:c1,token:ds_brand_primary,label:主色,color:$r(app.color.ds_brand_primary)}]$r()返回值可直接存入常量数组ForEach 渲染色板——这也是团队内部 Token 文档页的常见实现。七、暗色模式零代码适配7.1 原理组件引用 $r(app.color.ds_bg_page) │ ▼ 系统判断当前主题 ╱ ╲ base/color.json dark/color.json #F9FAFB #111827组件层不出现任何isDarkMode判断。资源框架在运行时解析正确的值。7.2 开发注意事项所有颜色必须走 Token— 一个#FFFFFF漏网就会暗色下刺眼暗色值不是简单反转— 品牌色提亮、边框色减淡需设计稿校准对比度—ds_text_primary与ds_bg_surface在两端都需满足 WCAG AA对比度 ≥ 4.5:1测试方法— 系统设置切换暗色或在 DevEco 预览器选择 dark 主题7.3 容器色的暗色处理亮色ds_success_container #ECFDF5浅绿底暗色ds_success_container #064E3B深绿底容器色在暗色模式下反而加深避免大面积浅色刺眼同时保持语义色相。八、团队落地工作流8.1 设计 → 开发协作Figma Design Tokens │ ▼ 导出/对照 resources/color.json float.json │ ▼ 引用 $r(app.color.ds_*) 全 App 消费 │ ▼ 变更 只改 JSON → 全 App 生效建议在设计稿中直接使用与代码一致的 Token 名ds_text_primary减少翻译损耗。8.2 渐进式迁移策略不必一次性重构全 App。推荐顺序阶段范围做法Phase 1新页面强制使用 Token禁止硬编码Phase 2核心组件Button、Card、Input 封装 TokenPhase 3存量页面按模块逐步替换#hex→$r()Phase 4审查CI 脚本扫描硬编码色值8.3 与 Styles / Extend 结合对于重复样式组合可封装Extend(Text)functiondsBody1(){.fontSize($r(app.float.ds_font_body1)).fontColor($r(app.color.ds_text_primary))}Extend(Column)functiondsCard(){.padding($r(app.float.ds_space_lg)).backgroundColor($r(app.color.ds_bg_surface)).borderRadius($r(app.float.ds_radius_lg)).border({width:1,color:$r(app.color.ds_border_default)})}// 使用Text(正文).dsBody1()Column().dsCard()Token 定义「值」Extend 定义「组合」—— 两层分工避免每个组件重复写五六个属性。8.4 Code Review 检查项新增颜色是否定义为 Token组件是否使用$r()而非 hex暗色color.json是否有对应覆写间距是否为 4vp 网格倍数命名是否符合ds_category_role规范九、常见误区9.1 「Token 就是常量类」DSColor.brandPrimary #2563EB在 TypeScript 里编译期固定无法响应系统主题切换。resources Token 才是运行时主题感知的正解。常量类可作为补充不应替代 resources。9.2 「暗色模式在组件里判断」// 不推荐.fontColor(this.isDark?#F9FAFB:#111827)// 推荐.fontColor($r(app.color.ds_text_primary))前者每处都要写判断后者集中管理。9.3 「Token 名用色值描述」ds_blue_500在品牌换色后名不副实。用语义名ds_brand_primary值可以随便换。9.4 「间距随意定」padding(15)、margin(13)破坏网格节奏。Stick to the scalexs/sm/md/lg/xl/xxl。9.5 「只定义亮色」暗色适配不是可选项。从第一天就在dark/color.json维护同名 Token成本远低于事后补。十、Token 规模参考Demo 当前规模类型数量文件颜色17color.json × 2间距6float.json圆角5float.json字阶6float.json合计34—一个中等复杂度 App颜色 Token 通常在 20–40 个间距/圆角/字号各 5–8 个。不宜贪多——每个 Token 都应有明确使用场景否则团队记不住。扩展时优先加语义色如ds_info、ds_link而非加第 17 个灰色变体。十一、总结设计系统 Token 变量库的本质是把视觉决策从组件代码中抽离到 resources定义一次 → 处处引用 → 改一处全局生效HarmonyOS 落地三件套文件放什么引用方式base/element/color.json语义化颜色$r(app.color.ds_*)dark/element/color.json暗色覆写同名自动切换base/element/float.json间距/圆角/字号$r(app.float.ds_*)关键原则语义命名—ds_text_primary不是ds_gray_9004vp 网格— 间距刻度可预期零硬编码— 组件不出现#hex魔法数字双目录主题— base dark 同名 Token渐进迁移— 新代码强制 Token存量逐步替换本文 Demo 在四个场景中完整展示了色板、间距刻度、圆角阶梯和组件画廊。建议你在真机上运行后到系统设置切换暗色模式——观察ds_bg_page从浅灰变为深蓝灰而组件代码一行未改。这就是 Token 变量库的价值。设计系统不是设计师的文档摆设而是开发团队的视觉基础设施。从第一个ds_brand_primary开始你的 App 就拥有了统一的语言。相关文件Demo 页面entry/src/main/ets/pages/Index.ets亮色 Tokenentry/src/main/resources/base/element/color.json、float.json暗色 Tokenentry/src/main/resources/dark/element/color.json编译命令node/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js assembleHap-pproductdefault-pbuildModedebug --no-daemon

相关新闻

2026 低成本小程序商城平台实测:好用又安全的平台推荐

2026 低成本小程序商城平台实测:好用又安全的平台推荐

2026 低成本小程序商城平台实测:好用又安全的平台推荐做线上商城、私域带货的中小商家和个体创业者,大多都栽过平台选型的坑。要么平台年费高昂、隐性收费不断,拉高运营成本;要么后台操作复杂,零基础上手难度大&#x…

2026/7/14 18:55:55阅读更多 →
TB67H480FNG与PIC18F4620在工业运动控制中的优化方案

TB67H480FNG与PIC18F4620在工业运动控制中的优化方案

1. 项目概述:TB67H480FNG与PIC18F4620的黄金组合 在工业自动化和精密运动控制领域,电机驱动系统的稳定性和精度直接决定了整个项目的成败。我最近在一个自动化检测设备项目中采用了东芝TB67H480FNG驱动芯片与Microchip PIC18F4620微控制器的组合方案&…

2026/7/14 18:55:55阅读更多 →
【2013-11-19】【转】字符串匹配的Boyer-Moore算法

【2013-11-19】【转】字符串匹配的Boyer-Moore算法

[历史归档] 本文原发布于 cstriker1407.info 个人博客,内容为历史存档,仅供参考。 发布时间: 2013-11-19 | 标题:【转】字符串匹配的Boyer-Moore算法 | 分类: 编程 / 数据结构与算法 &…

2026/7/14 18:55:55阅读更多 →
AI日程管理实战:从信息结构化到系统工作流搭建

AI日程管理实战:从信息结构化到系统工作流搭建

上周我花了两小时整理下周的会议安排,结果还是漏掉了一个跨时区的客户电话。不是没记下来,而是手动排日程时,脑子一热把时区算错了。这种明明花了时间却因为细节出错的情况,估计不少人都遇到过。 这时候我开始认真琢磨&#xff1…

2026/7/14 20:11:01阅读更多 →
计算机毕业设计之基于springboot健身房会员管理系统设计与实现

计算机毕业设计之基于springboot健身房会员管理系统设计与实现

随着人们健康意识的不断提高,对健身的需求日益增长,健身房行业迎来了蓬勃发展的时期。越来越多的健身房如雨后春笋般涌现,规模也从小型工作室到大型连锁健身俱乐部不等。然而,行业的快速发展也带来了一系列管理上的挑战。传统的人…

2026/7/14 20:11:01阅读更多 →
TensorFlow Lite模型自动化验证:量化一致性与跨平台测试

TensorFlow Lite模型自动化验证:量化一致性与跨平台测试

1. 项目概述:为什么自动化测试 TensorFlow Lite 模型落地环节成了团队的“隐形瓶颈”在实际做边缘AI部署的三年里,我带过六支不同行业的模型落地小队——从工业质检的振动异常检测,到农业无人机的病虫害识别,再到智能硬件厂商的离…

2026/7/14 20:11:01阅读更多 →
YOLOv8固体废物识别检测系统:从环境配置到部署全流程指南

YOLOv8固体废物识别检测系统:从环境配置到部署全流程指南

固体废物识别检测是环境监测和垃圾分类领域的重要应用,基于深度学习的视觉检测技术能够有效提升识别效率和准确率。YOLOv8作为当前优秀的实时目标检测算法,在固体废物识别任务中表现出色。本文将详细介绍如何从零开始构建一个完整的YOLOv8固体废物识别检…

2026/7/14 20:11:01阅读更多 →
电量优化:后台任务管理的精细化控制(166)

电量优化:后台任务管理的精细化控制(166)

在 HarmonyOS 应用开发中,电量优化是保障用户体验的核心指标之一。应用退至后台后如果继续活动,极易导致设备耗电快、界面卡顿。为此,HarmonyOS 系统对后台应用实施了严格的管控机制(如进程挂起和终止),同时…

2026/7/14 20:11:01阅读更多 →
TLA2518 ADC与PIC18LF26K42 MCU接口设计指南

TLA2518 ADC与PIC18LF26K42 MCU接口设计指南

1. TLA2518 ADC芯片的核心特性解析TLA2518是德州仪器推出的一款12位精度、1MSPS采样率的8通道SAR型模数转换器。这款芯片在工业测量、医疗设备和自动化控制等领域有着广泛应用。其最突出的特点是每个通道可独立配置为模拟输入、数字输入或数字输出,这种灵活性使其成…

2026/7/14 20:06:01阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/14 4:56:14阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/14 2:55:05阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/14 6:17:41阅读更多 →
【Cursor数据库安全红线】:自动执行SQL前必须校验的6项权限策略,金融级项目已强制落地

【Cursor数据库安全红线】:自动执行SQL前必须校验的6项权限策略,金融级项目已强制落地

更多请点击: https://intelliparadigm.com 第一章:Cursor数据库安全红线概览 Cursor 作为一款基于 AI 的智能编程助手,其本地数据库(SQLite 存储)承载着用户代码片段、会话历史、自定义规则及敏感上下文信息。理解其安…

2026/7/14 0:03:18阅读更多 →
【Notion AI写作避坑白皮书】:基于127份真实用户失败案例,总结6大致命误用陷阱

【Notion AI写作避坑白皮书】:基于127份真实用户失败案例,总结6大致命误用陷阱

更多请点击: https://codechina.net 第一章:Notion AI写作辅助的底层能力边界认知 Notion AI 并非通用大语言模型的直接封装,而是基于 Llama 系列与自研微调模型构建的轻量化推理服务,其输入上下文窗口严格限制在 8192 token&…

2026/7/14 0:03:18阅读更多 →
AI Agent数据越界行为如何被精准溯源?——基于GDPR/CCPA双合规的5层审计框架实战指南

AI Agent数据越界行为如何被精准溯源?——基于GDPR/CCPA双合规的5层审计框架实战指南

更多请点击: https://kaifayun.com 第一章:AI Agent数据越界行为的合规性挑战与溯源必要性 AI Agent在自主执行任务过程中,可能因提示注入、上下文污染或权限配置缺陷,无意或有意访问、缓存、传输受保护数据(如PII、G…

2026/7/14 0:03:18阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/14 15:07:30阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/14 4:45:36阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/14 2:42:17阅读更多 →