[{"data":1,"prerenderedAt":1747},["ShallowReactive",2],{"article-/topics/design/multi-brand-design-system-architecture":3,"related-design":459,"content-query-KfvCqJnyd0":1400},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":12,"image":18,"imageQuery":19,"pexelsPhotoId":20,"pexelsUrl":21,"featured":6,"readingTime":22,"body":23,"_type":453,"_id":454,"_source":455,"_file":456,"_stem":457,"_extension":458},"/topics/design/multi-brand-design-system-architecture","design",false,"","多品牌设计系统架构：怎样在统一基础上支持品牌差异，而不是复制多套组件库","多品牌设计系统最难的不是换一套颜色，而是如何管理共享基础、品牌差异和升级节奏。本文从分层、token、组件策略和失败案例出发，讲清多品牌设计系统的落地方法。","2026-04-16","HTMLPAGE 团队",[13,14,15,16,17],"Multi-brand Design System","Design System","Design Tokens","Component Architecture","Brand Consistency","/images/topics/design/multi-brand-design-system-architecture.jpg","design team reviewing brand style systems and interface tokens on large screen",14851420,"https://www.pexels.com/photo/close-up-of-man-pointing-at-a-large-touchscreen-14851420/",14,{"type":24,"children":25,"toc":441},"root",[26,34,49,54,60,65,83,88,106,111,117,122,141,146,152,157,162,193,198,203,233,238,244,249,254,267,272,278,283,301,306,311,316,321,339,344,362,367,395,400,405,410],{"type":27,"tag":28,"props":29,"children":30},"element","p",{},[31],{"type":32,"value":33},"text","只要一个团队开始同时服务多个品牌，就几乎一定会遇到同一个问题：",{"type":27,"tag":35,"props":36,"children":37},"ul",{},[38,44],{"type":27,"tag":39,"props":40,"children":41},"li",{},[42],{"type":32,"value":43},"所有品牌都想保留自己的视觉特征",{"type":27,"tag":39,"props":45,"children":46},{},[47],{"type":32,"value":48},"但团队又不想维护好几套完全独立的组件体系",{"type":27,"tag":28,"props":50,"children":51},{},[52],{"type":32,"value":53},"这就是多品牌设计系统要解决的核心矛盾。它不是简单地“换颜色换 Logo”，而是要在统一平台能力和品牌差异之间找到可长期维护的平衡点。",{"type":27,"tag":55,"props":56,"children":58},"h2",{"id":57},"多品牌不是多复制几套主题文件",[59],{"type":32,"value":57},{"type":27,"tag":28,"props":61,"children":62},{},[63],{"type":32,"value":64},"很多团队第一次做多品牌，会自然选择最直接的方式：",{"type":27,"tag":35,"props":66,"children":67},{},[68,73,78],{"type":27,"tag":39,"props":69,"children":70},{},[71],{"type":32,"value":72},"品牌 A 一套变量",{"type":27,"tag":39,"props":74,"children":75},{},[76],{"type":32,"value":77},"品牌 B 再复制一套变量",{"type":27,"tag":39,"props":79,"children":80},{},[81],{"type":32,"value":82},"品牌 C 继续复制",{"type":27,"tag":28,"props":84,"children":85},{},[86],{"type":32,"value":87},"这种方法短期能跑，但品牌一多，很快就会失控：",{"type":27,"tag":35,"props":89,"children":90},{},[91,96,101],{"type":27,"tag":39,"props":92,"children":93},{},[94],{"type":32,"value":95},"相同组件被维护出多个分支",{"type":27,"tag":39,"props":97,"children":98},{},[99],{"type":32,"value":100},"共性能力改一次要同步好几处",{"type":27,"tag":39,"props":102,"children":103},{},[104],{"type":32,"value":105},"谁是平台能力、谁是品牌差异越来越说不清",{"type":27,"tag":28,"props":107,"children":108},{},[109],{"type":32,"value":110},"多品牌设计系统真正需要的是分层，而不是复制。",{"type":27,"tag":55,"props":112,"children":114},{"id":113},"更稳的结构是基础层-品牌层-场景层",[115],{"type":32,"value":116},"更稳的结构是“基础层 + 品牌层 + 场景层”",{"type":27,"tag":28,"props":118,"children":119},{},[120],{"type":32,"value":121},"一个更容易长期维护的架构通常分成三层：",{"type":27,"tag":123,"props":124,"children":125},"ol",{},[126,131,136],{"type":27,"tag":39,"props":127,"children":128},{},[129],{"type":32,"value":130},"基础层：间距、排版、交互状态、组件 API 这些跨品牌共用能力",{"type":27,"tag":39,"props":132,"children":133},{},[134],{"type":32,"value":135},"品牌层：颜色、字体、圆角、阴影、品牌语气等可变资产",{"type":27,"tag":39,"props":137,"children":138},{},[139],{"type":32,"value":140},"场景层：营销页、后台、会员中心等具体业务落地规则",{"type":27,"tag":28,"props":142,"children":143},{},[144],{"type":32,"value":145},"这样做的意义在于：品牌变化不会轻易冲击组件结构，而基础升级也不必把每个品牌都重做一遍。",{"type":27,"tag":55,"props":147,"children":149},{"id":148},"token-体系决定多品牌扩展效率",[150],{"type":32,"value":151},"Token 体系决定多品牌扩展效率",{"type":27,"tag":28,"props":153,"children":154},{},[155],{"type":32,"value":156},"多品牌设计系统里，最容易被低估的不是组件，而是 token 命名。",{"type":27,"tag":28,"props":158,"children":159},{},[160],{"type":32,"value":161},"如果 token 直接写成：",{"type":27,"tag":35,"props":163,"children":164},{},[165,175,184],{"type":27,"tag":39,"props":166,"children":167},{},[168],{"type":27,"tag":169,"props":170,"children":172},"code",{"className":171},[],[173],{"type":32,"value":174},"blue-500",{"type":27,"tag":39,"props":176,"children":177},{},[178],{"type":27,"tag":169,"props":179,"children":181},{"className":180},[],[182],{"type":32,"value":183},"brand-red",{"type":27,"tag":39,"props":185,"children":186},{},[187],{"type":27,"tag":169,"props":188,"children":190},{"className":189},[],[191],{"type":32,"value":192},"button-green",{"type":27,"tag":28,"props":194,"children":195},{},[196],{"type":32,"value":197},"后面品牌一多，就会越来越难抽象。",{"type":27,"tag":28,"props":199,"children":200},{},[201],{"type":32,"value":202},"更稳的方式是先定义角色，再映射品牌：",{"type":27,"tag":35,"props":204,"children":205},{},[206,215,224],{"type":27,"tag":39,"props":207,"children":208},{},[209],{"type":27,"tag":169,"props":210,"children":212},{"className":211},[],[213],{"type":32,"value":214},"color-action-primary",{"type":27,"tag":39,"props":216,"children":217},{},[218],{"type":27,"tag":169,"props":219,"children":221},{"className":220},[],[222],{"type":32,"value":223},"color-surface-brand",{"type":27,"tag":39,"props":225,"children":226},{},[227],{"type":27,"tag":169,"props":228,"children":230},{"className":229},[],[231],{"type":32,"value":232},"color-text-emphasis",{"type":27,"tag":28,"props":234,"children":235},{},[236],{"type":32,"value":237},"这样品牌只是在映射层变化，而不是把组件语义改掉。",{"type":27,"tag":55,"props":239,"children":241},{"id":240},"组件层要控制品牌可变范围",[242],{"type":32,"value":243},"组件层要控制“品牌可变范围”",{"type":27,"tag":28,"props":245,"children":246},{},[247],{"type":32,"value":248},"并不是每个组件都应该开放同样程度的品牌自由度。",{"type":27,"tag":28,"props":250,"children":251},{},[252],{"type":32,"value":253},"例如：",{"type":27,"tag":35,"props":255,"children":256},{},[257,262],{"type":27,"tag":39,"props":258,"children":259},{},[260],{"type":32,"value":261},"Button、Badge、Banner 这类品牌感强的组件，可以放宽外观差异",{"type":27,"tag":39,"props":263,"children":264},{},[265],{"type":32,"value":266},"Table、Form、Drawer 这类结构性组件，更应该保持交互和信息结构稳定",{"type":27,"tag":28,"props":268,"children":269},{},[270],{"type":32,"value":271},"如果所有组件都被允许深度品牌化，最后多品牌系统往往会退化成“同名不同物”。",{"type":27,"tag":55,"props":273,"children":275},{"id":274},"失败案例三个品牌共用一个组件库但每次发布都要人工逐品牌验收",[276],{"type":32,"value":277},"失败案例：三个品牌共用一个组件库，但每次发布都要人工逐品牌验收",{"type":27,"tag":28,"props":279,"children":280},{},[281],{"type":32,"value":282},"这是多品牌系统很常见的隐性失败：",{"type":27,"tag":35,"props":284,"children":285},{},[286,291,296],{"type":27,"tag":39,"props":287,"children":288},{},[289],{"type":32,"value":290},"表面上是一个共享组件库",{"type":27,"tag":39,"props":292,"children":293},{},[294],{"type":32,"value":295},"实际上每个品牌都有一堆例外规则",{"type":27,"tag":39,"props":297,"children":298},{},[299],{"type":32,"value":300},"每次升级都要人工逐品牌检查视觉和交互",{"type":27,"tag":28,"props":302,"children":303},{},[304],{"type":32,"value":305},"问题不在“品牌太多”，而在没有定义清楚哪些能力应该共用，哪些差异应该被限制。",{"type":27,"tag":28,"props":307,"children":308},{},[309],{"type":32,"value":310},"一旦品牌差异没有边界，所谓共享就会慢慢失效。",{"type":27,"tag":55,"props":312,"children":314},{"id":313},"多品牌系统一定要把升级策略一起设计",[315],{"type":32,"value":313},{"type":27,"tag":28,"props":317,"children":318},{},[319],{"type":32,"value":320},"真正的难点不在接入第一个品牌，而在第六个月之后：",{"type":27,"tag":35,"props":322,"children":323},{},[324,329,334],{"type":27,"tag":39,"props":325,"children":326},{},[327],{"type":32,"value":328},"新品牌接入时，能否复用已有 token 结构",{"type":27,"tag":39,"props":330,"children":331},{},[332],{"type":32,"value":333},"共享组件升级后，旧品牌是否还能平滑跟进",{"type":27,"tag":39,"props":335,"children":336},{},[337],{"type":32,"value":338},"视觉规范调整时，是否能准确评估影响面",{"type":27,"tag":28,"props":340,"children":341},{},[342],{"type":32,"value":343},"多品牌设计系统要想稳定，必须同时具备：",{"type":27,"tag":35,"props":345,"children":346},{},[347,352,357],{"type":27,"tag":39,"props":348,"children":349},{},[350],{"type":32,"value":351},"可扩展的 token 层",{"type":27,"tag":39,"props":353,"children":354},{},[355],{"type":32,"value":356},"可约束的组件层",{"type":27,"tag":39,"props":358,"children":359},{},[360],{"type":32,"value":361},"可追踪的升级流程",{"type":27,"tag":55,"props":363,"children":365},{"id":364},"一份可直接复用的检查清单",[366],{"type":32,"value":364},{"type":27,"tag":35,"props":368,"children":369},{},[370,375,380,385,390],{"type":27,"tag":39,"props":371,"children":372},{},[373],{"type":32,"value":374},"设计系统是否拆分出基础层、品牌层和场景层",{"type":27,"tag":39,"props":376,"children":377},{},[378],{"type":32,"value":379},"token 是否以角色命名，而不是直接绑死颜色值",{"type":27,"tag":39,"props":381,"children":382},{},[383],{"type":32,"value":384},"组件是否明确了哪些外观可变、哪些结构不可变",{"type":27,"tag":39,"props":386,"children":387},{},[388],{"type":32,"value":389},"多品牌差异是否被限制在可控范围内",{"type":27,"tag":39,"props":391,"children":392},{},[393],{"type":32,"value":394},"共享组件升级时，是否能清楚识别各品牌影响面",{"type":27,"tag":55,"props":396,"children":398},{"id":397},"总结",[399],{"type":32,"value":397},{"type":27,"tag":28,"props":401,"children":402},{},[403],{"type":32,"value":404},"多品牌设计系统的目标，不是让每个品牌都“各做各的”，而是在统一基础能力上安全地表达差异。只要分层、token 和升级策略一起设计，它就会是平台资产，而不是多套组件库的维护陷阱。",{"type":27,"tag":28,"props":406,"children":407},{},[408],{"type":32,"value":409},"进一步阅读：",{"type":27,"tag":35,"props":411,"children":412},{},[413,423,432],{"type":27,"tag":39,"props":414,"children":415},{},[416],{"type":27,"tag":417,"props":418,"children":420},"a",{"href":419},"/topics/design/design-system-versioning-guide",[421],{"type":32,"value":422},"设计系统版本管理完全指南",{"type":27,"tag":39,"props":424,"children":425},{},[426],{"type":27,"tag":417,"props":427,"children":429},{"href":428},"/topics/design/design-system-performance-guide",[430],{"type":32,"value":431},"设计系统性能优化完整指南",{"type":27,"tag":39,"props":433,"children":434},{},[435],{"type":27,"tag":417,"props":436,"children":438},{"href":437},"/topics/design/design-tokens-engineering-practice",[439],{"type":32,"value":440},"Design Tokens 工程化实践",{"title":7,"searchDepth":442,"depth":442,"links":443},3,[444,446,447,448,449,450,451,452],{"id":57,"depth":445,"text":57},2,{"id":113,"depth":445,"text":116},{"id":148,"depth":445,"text":151},{"id":240,"depth":445,"text":243},{"id":274,"depth":445,"text":277},{"id":313,"depth":445,"text":313},{"id":364,"depth":445,"text":364},{"id":397,"depth":445,"text":397},"markdown","content:topics:design:multi-brand-design-system-architecture.md","content","topics/design/multi-brand-design-system-architecture.md","topics/design/multi-brand-design-system-architecture","md",[460,819,1121],{"_path":461,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":462,"description":463,"keywords":464,"image":470,"author":11,"date":471,"readingTime":472,"topic":5,"body":473,"_type":453,"_id":816,"_source":455,"_file":817,"_stem":818,"_extension":458},"/topics/design/button-component-design","按钮组件设计详解","学习按钮样式、交互状态、无障碍性和最佳实践",[465,466,467,468,469],"按钮设计","Button Component","交互状态","UI 组件","用户体验","/images/topics/button-design.jpg","2025-12-08",18,{"type":24,"children":474,"toc":798},[475,479,484,489,496,508,514,523,529,538,542,548,559,565,574,580,589,594,603,608,619,624,633,638,651,685,696,739,744],{"type":27,"tag":55,"props":476,"children":477},{"id":462},[478],{"type":32,"value":462},{"type":27,"tag":28,"props":480,"children":481},{},[482],{"type":32,"value":483},"按钮是 UI 中最重要的交互元素。优秀的按钮设计能够指导用户行为。",{"type":27,"tag":55,"props":485,"children":487},{"id":486},"按钮类型",[488],{"type":32,"value":486},{"type":27,"tag":490,"props":491,"children":493},"h3",{"id":492},"primary-button主按钮",[494],{"type":32,"value":495},"Primary Button（主按钮）",{"type":27,"tag":497,"props":498,"children":503},"pre",{"className":499,"code":501,"language":502,"meta":7},[500],"language-css",".btn-primary {\n  background-color: #0066cc;\n  color: #ffffff;\n  padding: 12px 24px;\n  border: none;\n  border-radius: 4px;\n  font-weight: 600;\n  font-size: 16px;\n  cursor: pointer;\n  transition: all 0.2s ease;\n}\n\n.btn-primary:hover {\n  background-color: #0052a3;\n  box-shadow: 0 4px 12px rgba(0, 102, 204, 0.2);\n}\n\n.btn-primary:active {\n  background-color: #003d7a;\n  transform: scale(0.98);\n}\n\n.btn-primary:disabled {\n  background-color: #cccccc;\n  cursor: not-allowed;\n  opacity: 0.6;\n}\n","css",[504],{"type":27,"tag":169,"props":505,"children":506},{"__ignoreMap":7},[507],{"type":32,"value":501},{"type":27,"tag":490,"props":509,"children":511},{"id":510},"secondary-button次按钮",[512],{"type":32,"value":513},"Secondary Button（次按钮）",{"type":27,"tag":497,"props":515,"children":518},{"className":516,"code":517,"language":502,"meta":7},[500],".btn-secondary {\n  background-color: transparent;\n  color: #0066cc;\n  border: 2px solid #0066cc;\n  padding: 10px 22px;\n  border-radius: 4px;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.2s;\n}\n\n.btn-secondary:hover {\n  background-color: rgba(0, 102, 204, 0.1);\n}\n\n.btn-secondary:active {\n  background-color: rgba(0, 102, 204, 0.2);\n}\n",[519],{"type":27,"tag":169,"props":520,"children":521},{"__ignoreMap":7},[522],{"type":32,"value":517},{"type":27,"tag":490,"props":524,"children":526},{"id":525},"danger-button危险按钮",[527],{"type":32,"value":528},"Danger Button（危险按钮）",{"type":27,"tag":497,"props":530,"children":533},{"className":531,"code":532,"language":502,"meta":7},[500],".btn-danger {\n  background-color: #cc0000;\n  color: #ffffff;\n  padding: 12px 24px;\n  border-radius: 4px;\n  cursor: pointer;\n  font-weight: 600;\n}\n\n.btn-danger:hover {\n  background-color: #990000;\n  box-shadow: 0 4px 12px rgba(204, 0, 0, 0.2);\n}\n",[534],{"type":27,"tag":169,"props":535,"children":536},{"__ignoreMap":7},[537],{"type":32,"value":532},{"type":27,"tag":55,"props":539,"children":540},{"id":467},[541],{"type":32,"value":467},{"type":27,"tag":490,"props":543,"children":545},{"id":544},"loading-状态",[546],{"type":32,"value":547},"Loading 状态",{"type":27,"tag":497,"props":549,"children":554},{"className":550,"code":552,"language":553,"meta":7},[551],"language-jsx","import { useState } from 'react';\n\nfunction Button({ children, onClick, loading, ...props }) {\n  const [isLoading, setIsLoading] = useState(false);\n  \n  const handleClick = async () => {\n    setIsLoading(true);\n    try {\n      await onClick();\n    } finally {\n      setIsLoading(false);\n    }\n  };\n  \n  return (\n    \u003Cbutton\n      onClick={handleClick}\n      disabled={isLoading || loading}\n      aria-busy={isLoading || loading}\n      {...props}\n    >\n      {isLoading ? (\n        \u003C>\n          \u003Cspan className=\"spinner\" aria-hidden=\"true\">\u003C/span>\n          {children}\n        \u003C/>\n      ) : (\n        children\n      )}\n    \u003C/button>\n  );\n}\n","jsx",[555],{"type":27,"tag":169,"props":556,"children":557},{"__ignoreMap":7},[558],{"type":32,"value":552},{"type":27,"tag":490,"props":560,"children":562},{"id":561},"disabled-状态",[563],{"type":32,"value":564},"Disabled 状态",{"type":27,"tag":497,"props":566,"children":569},{"className":567,"code":568,"language":502,"meta":7},[500],".btn:disabled {\n  opacity: 0.5;\n  cursor: not-allowed;\n  background-color: #cccccc;\n  color: #999999;\n}\n\n/* 禁用状态下隐藏指针光标 */\n.btn:disabled:hover {\n  box-shadow: none;\n  transform: none;\n}\n",[570],{"type":27,"tag":169,"props":571,"children":572},{"__ignoreMap":7},[573],{"type":32,"value":568},{"type":27,"tag":490,"props":575,"children":577},{"id":576},"focus-状态",[578],{"type":32,"value":579},"Focus 状态",{"type":27,"tag":497,"props":581,"children":584},{"className":582,"code":583,"language":502,"meta":7},[500],".btn:focus {\n  outline: none;\n  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1),\n              0 0 0 5px #0066cc;\n}\n\n/* 键盘导航焦点 */\n.btn:focus-visible {\n  outline: 2px solid #0066cc;\n  outline-offset: 2px;\n}\n",[585],{"type":27,"tag":169,"props":586,"children":587},{"__ignoreMap":7},[588],{"type":32,"value":583},{"type":27,"tag":55,"props":590,"children":592},{"id":591},"按钮大小",[593],{"type":32,"value":591},{"type":27,"tag":497,"props":595,"children":598},{"className":596,"code":597,"language":502,"meta":7},[500],"/* 小按钮 */\n.btn-sm {\n  padding: 6px 12px;\n  font-size: 12px;\n  min-height: 32px;\n  min-width: 32px;\n}\n\n/* 中等按钮（默认） */\n.btn-md {\n  padding: 12px 24px;\n  font-size: 16px;\n  min-height: 44px;\n  min-width: 44px;\n}\n\n/* 大按钮 */\n.btn-lg {\n  padding: 16px 32px;\n  font-size: 18px;\n  min-height: 56px;\n  min-width: 56px;\n}\n\n/* 全宽按钮 */\n.btn-block {\n  width: 100%;\n  display: block;\n}\n",[599],{"type":27,"tag":169,"props":600,"children":601},{"__ignoreMap":7},[602],{"type":32,"value":597},{"type":27,"tag":55,"props":604,"children":606},{"id":605},"无障碍性",[607],{"type":32,"value":605},{"type":27,"tag":497,"props":609,"children":614},{"className":610,"code":612,"language":613,"meta":7},[611],"language-html","\u003C!-- 语义正确 -->\n\u003Cbutton type=\"submit\" aria-label=\"提交表单\">\n  提交\n\u003C/button>\n\n\u003C!-- 加载状态 -->\n\u003Cbutton aria-busy=\"true\" disabled>\n  \u003Cspan aria-hidden=\"true\" class=\"spinner\">\u003C/span>\n  加载中...\n\u003C/button>\n\n\u003C!-- 图标按钮 -->\n\u003Cbutton aria-label=\"关闭\">\n  \u003Csvg aria-hidden=\"true\" width=\"24\" height=\"24\">\n    \u003Cline x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n    \u003Cline x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n  \u003C/svg>\n\u003C/button>\n\n\u003C!-- 切换按钮 -->\n\u003Cbutton aria-pressed=\"false\" aria-label=\"点赞\">\n  ♥\n\u003C/button>\n","html",[615],{"type":27,"tag":169,"props":616,"children":617},{"__ignoreMap":7},[618],{"type":32,"value":612},{"type":27,"tag":55,"props":620,"children":622},{"id":621},"完整组件示例",[623],{"type":32,"value":621},{"type":27,"tag":497,"props":625,"children":628},{"className":626,"code":627,"language":553,"meta":7},[551],"const Button = React.forwardRef((\n  {\n    children,\n    variant = 'primary',\n    size = 'md',\n    loading = false,\n    disabled = false,\n    icon,\n    className,\n    ...props\n  },\n  ref\n) => {\n  return (\n    \u003Cbutton\n      ref={ref}\n      className={`btn btn-${variant} btn-${size} ${className}`}\n      disabled={disabled || loading}\n      aria-busy={loading}\n      {...props}\n    >\n      {icon && \u003Cspan className=\"btn-icon\" aria-hidden=\"true\">{icon}\u003C/span>}\n      {loading ? (\n        \u003C>\n          \u003Cspan className=\"spinner\" aria-hidden=\"true\">\u003C/span>\n          {children}\n        \u003C/>\n      ) : (\n        children\n      )}\n    \u003C/button>\n  );\n});\n\nButton.displayName = 'Button';\n",[629],{"type":27,"tag":169,"props":630,"children":631},{"__ignoreMap":7},[632],{"type":32,"value":627},{"type":27,"tag":55,"props":634,"children":636},{"id":635},"最佳实践",[637],{"type":32,"value":635},{"type":27,"tag":28,"props":639,"children":640},{},[641,643,649],{"type":32,"value":642},"✅ ",{"type":27,"tag":644,"props":645,"children":646},"strong",{},[647],{"type":32,"value":648},"应该做的事",{"type":32,"value":650},":",{"type":27,"tag":35,"props":652,"children":653},{},[654,659,664,675,680],{"type":27,"tag":39,"props":655,"children":656},{},[657],{"type":32,"value":658},"最小触摸目标 44x44px",{"type":27,"tag":39,"props":660,"children":661},{},[662],{"type":32,"value":663},"清晰的视觉反馈",{"type":27,"tag":39,"props":665,"children":666},{},[667,669],{"type":32,"value":668},"使用语义 HTML ",{"type":27,"tag":169,"props":670,"children":672},{"className":671},[],[673],{"type":32,"value":674},"\u003Cbutton>",{"type":27,"tag":39,"props":676,"children":677},{},[678],{"type":32,"value":679},"提供加载状态反馈",{"type":27,"tag":39,"props":681,"children":682},{},[683],{"type":32,"value":684},"支持键盘导航",{"type":27,"tag":28,"props":686,"children":687},{},[688,690,695],{"type":32,"value":689},"❌ ",{"type":27,"tag":644,"props":691,"children":692},{},[693],{"type":32,"value":694},"不应该做的事",{"type":32,"value":650},{"type":27,"tag":35,"props":697,"children":698},{},[699,712,717,722,727],{"type":27,"tag":39,"props":700,"children":701},{},[702,704,710],{"type":32,"value":703},"使用 ",{"type":27,"tag":169,"props":705,"children":707},{"className":706},[],[708],{"type":32,"value":709},"\u003Cdiv>",{"type":32,"value":711}," 模拟按钮",{"type":27,"tag":39,"props":713,"children":714},{},[715],{"type":32,"value":716},"隐藏焦点指示器",{"type":27,"tag":39,"props":718,"children":719},{},[720],{"type":32,"value":721},"过多的按钮样式",{"type":27,"tag":39,"props":723,"children":724},{},[725],{"type":32,"value":726},"忽视禁用状态",{"type":27,"tag":39,"props":728,"children":729},{},[730,731,737],{"type":32,"value":703},{"type":27,"tag":169,"props":732,"children":734},{"className":733},[],[735],{"type":32,"value":736},"\u003Ca>",{"type":32,"value":738}," 代替按钮",{"type":27,"tag":55,"props":740,"children":742},{"id":741},"测试清单",[743],{"type":32,"value":741},{"type":27,"tag":35,"props":745,"children":748},{"className":746},[747],"contains-task-list",[749,762,771,780,789],{"type":27,"tag":39,"props":750,"children":753},{"className":751},[752],"task-list-item",[754,760],{"type":27,"tag":755,"props":756,"children":759},"input",{"disabled":757,"type":758},true,"checkbox",[],{"type":32,"value":761}," 在各种浏览器中测试",{"type":27,"tag":39,"props":763,"children":765},{"className":764},[752],[766,769],{"type":27,"tag":755,"props":767,"children":768},{"disabled":757,"type":758},[],{"type":32,"value":770}," 验证键盘导航",{"type":27,"tag":39,"props":772,"children":774},{"className":773},[752],[775,778],{"type":27,"tag":755,"props":776,"children":777},{"disabled":757,"type":758},[],{"type":32,"value":779}," 检查色彩对比度",{"type":27,"tag":39,"props":781,"children":783},{"className":782},[752],[784,787],{"type":27,"tag":755,"props":785,"children":786},{"disabled":757,"type":758},[],{"type":32,"value":788}," 测试触摸设备",{"type":27,"tag":39,"props":790,"children":792},{"className":791},[752],[793,796],{"type":27,"tag":755,"props":794,"children":795},{"disabled":757,"type":758},[],{"type":32,"value":797}," 屏幕阅读器兼容性",{"title":7,"searchDepth":442,"depth":442,"links":799},[800,801,806,811,812,813,814,815],{"id":462,"depth":445,"text":462},{"id":486,"depth":445,"text":486,"children":802},[803,804,805],{"id":492,"depth":442,"text":495},{"id":510,"depth":442,"text":513},{"id":525,"depth":442,"text":528},{"id":467,"depth":445,"text":467,"children":807},[808,809,810],{"id":544,"depth":442,"text":547},{"id":561,"depth":442,"text":564},{"id":576,"depth":442,"text":579},{"id":591,"depth":445,"text":591},{"id":605,"depth":445,"text":605},{"id":621,"depth":445,"text":621},{"id":635,"depth":445,"text":635},{"id":741,"depth":445,"text":741},"content:topics:design:button-component-design.md","topics/design/button-component-design.md","topics/design/button-component-design",{"_path":820,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":821,"description":822,"keywords":823,"image":828,"author":11,"date":471,"readingTime":829,"topic":5,"body":830,"_type":453,"_id":1118,"_source":455,"_file":1119,"_stem":1120,"_extension":458},"/topics/design/dark-mode-design","暗黑模式设计完整方案","学习暗黑模式实现、色彩方案、对比度管理和最佳实践",[824,825,826,827,469],"暗黑模式","Dark Mode","色彩系统","CSS 变量","/images/topics/dark-mode-design.jpg",20,{"type":24,"children":831,"toc":1101},[832,836,841,846,852,861,867,876,881,887,896,902,913,919,928,933,942,947,956,961,970,974,983,1011,1020,1048,1052],{"type":27,"tag":55,"props":833,"children":834},{"id":821},[835],{"type":32,"value":821},{"type":27,"tag":28,"props":837,"children":838},{},[839],{"type":32,"value":840},"暗黑模式已成为现代应用的标准功能。它能够减少眼睛疲劳、节省电池、改善用户体验。",{"type":27,"tag":55,"props":842,"children":844},{"id":843},"核心色彩系统",[845],{"type":32,"value":843},{"type":27,"tag":490,"props":847,"children":849},{"id":848},"light-mode-配色",[850],{"type":32,"value":851},"Light Mode 配色",{"type":27,"tag":497,"props":853,"children":856},{"className":854,"code":855,"language":502,"meta":7},[500],":root {\n  /* Light Mode */\n  --bg-primary: #ffffff;\n  --bg-secondary: #f5f5f5;\n  --bg-tertiary: #efefef;\n  \n  --text-primary: #1a1a1a;\n  --text-secondary: #666666;\n  --text-tertiary: #999999;\n  \n  --border-color: #e0e0e0;\n  --divider-color: #f0f0f0;\n}\n",[857],{"type":27,"tag":169,"props":858,"children":859},{"__ignoreMap":7},[860],{"type":32,"value":855},{"type":27,"tag":490,"props":862,"children":864},{"id":863},"dark-mode-配色",[865],{"type":32,"value":866},"Dark Mode 配色",{"type":27,"tag":497,"props":868,"children":871},{"className":869,"code":870,"language":502,"meta":7},[500],"@media (prefers-color-scheme: dark) {\n  :root {\n    /* Dark Mode */\n    --bg-primary: #1a1a1a;\n    --bg-secondary: #2d2d2d;\n    --bg-tertiary: #3a3a3a;\n    \n    --text-primary: #ffffff;\n    --text-secondary: #e0e0e0;\n    --text-tertiary: #a0a0a0;\n    \n    --border-color: #404040;\n    --divider-color: #2a2a2a;\n  }\n}\n",[872],{"type":27,"tag":169,"props":873,"children":874},{"__ignoreMap":7},[875],{"type":32,"value":870},{"type":27,"tag":55,"props":877,"children":879},{"id":878},"实现方案",[880],{"type":32,"value":878},{"type":27,"tag":490,"props":882,"children":884},{"id":883},"方案-1prefers-color-scheme",[885],{"type":32,"value":886},"方案 1：prefers-color-scheme",{"type":27,"tag":497,"props":888,"children":891},{"className":889,"code":890,"language":502,"meta":7},[500],"/* 自动跟随系统设置 */\n@media (prefers-color-scheme: light) {\n  :root {\n    --bg: #fff;\n    --text: #000;\n  }\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --bg: #1a1a1a;\n    --text: #fff;\n  }\n}\n\nbody {\n  background: var(--bg);\n  color: var(--text);\n}\n",[892],{"type":27,"tag":169,"props":893,"children":894},{"__ignoreMap":7},[895],{"type":32,"value":890},{"type":27,"tag":490,"props":897,"children":899},{"id":898},"方案-2javascript-切换",[900],{"type":32,"value":901},"方案 2：JavaScript 切换",{"type":27,"tag":497,"props":903,"children":908},{"className":904,"code":906,"language":907,"meta":7},[905],"language-javascript","// 检测和切换暗黑模式\nfunction initDarkMode() {\n  const isDark = localStorage.getItem('darkMode') === 'true' ||\n                 window.matchMedia('(prefers-color-scheme: dark)').matches;\n  \n  if (isDark) {\n    document.documentElement.setAttribute('data-theme', 'dark');\n  }\n}\n\nfunction toggleDarkMode() {\n  const isDark = document.documentElement.getAttribute('data-theme') === 'dark';\n  const newTheme = isDark ? 'light' : 'dark';\n  \n  document.documentElement.setAttribute('data-theme', newTheme);\n  localStorage.setItem('darkMode', newTheme === 'dark');\n}\n\n// CSS 应用\nhtml[data-theme='light'] {\n  color-scheme: light;\n}\n\nhtml[data-theme='dark'] {\n  color-scheme: dark;\n}\n","javascript",[909],{"type":27,"tag":169,"props":910,"children":911},{"__ignoreMap":7},[912],{"type":32,"value":906},{"type":27,"tag":490,"props":914,"children":916},{"id":915},"方案-3css-variables-javascript",[917],{"type":32,"value":918},"方案 3：CSS Variables + JavaScript",{"type":27,"tag":497,"props":920,"children":923},{"className":921,"code":922,"language":907,"meta":7},[905],"const themes = {\n  light: {\n    '--bg-primary': '#ffffff',\n    '--text-primary': '#000000',\n    '--accent': '#0066cc',\n    '--border': '#e0e0e0',\n  },\n  dark: {\n    '--bg-primary': '#1a1a1a',\n    '--text-primary': '#ffffff',\n    '--accent': '#4da3ff',\n    '--border': '#404040',\n  },\n};\n\nfunction applyTheme(themeName) {\n  const theme = themes[themeName];\n  Object.entries(theme).forEach(([key, value]) => {\n    document.documentElement.style.setProperty(key, value);\n  });\n  localStorage.setItem('theme', themeName);\n}\n",[924],{"type":27,"tag":169,"props":925,"children":926},{"__ignoreMap":7},[927],{"type":32,"value":922},{"type":27,"tag":55,"props":929,"children":931},{"id":930},"对比度管理",[932],{"type":32,"value":930},{"type":27,"tag":497,"props":934,"children":937},{"className":935,"code":936,"language":502,"meta":7},[500],"/* Light Mode 对比度 */\n:root {\n  --contrast-high: #000000;     /* 21:1 */\n  --contrast-medium: #333333;   /* 12.6:1 */\n  --contrast-low: #666666;      /* 5.1:1 */\n}\n\n/* Dark Mode 对比度 */\n@media (prefers-color-scheme: dark) {\n  :root {\n    --contrast-high: #ffffff;    /* 21:1 */\n    --contrast-medium: #e0e0e0;  /* 11.6:1 */\n    --contrast-low: #a0a0a0;     /* 4.5:1 */\n  }\n}\n\n/* 应用对比度 */\n.text-primary { color: var(--contrast-high); }\n.text-secondary { color: var(--contrast-medium); }\n.text-tertiary { color: var(--contrast-low); }\n",[938],{"type":27,"tag":169,"props":939,"children":940},{"__ignoreMap":7},[941],{"type":32,"value":936},{"type":27,"tag":55,"props":943,"children":945},{"id":944},"图片和图表处理",[946],{"type":32,"value":944},{"type":27,"tag":497,"props":948,"children":951},{"className":949,"code":950,"language":613,"meta":7},[611],"\u003C!-- 针对不同主题的图片 -->\n\u003Cpicture>\n  \u003Csource \n    media=\"(prefers-color-scheme: dark)\" \n    srcset=\"chart-dark.svg\"\n  />\n  \u003Cimg src=\"chart-light.svg\" alt=\"图表\" />\n\u003C/picture>\n\n\u003C!-- SVG 颜色适配 -->\n\u003Csvg class=\"icon\">\n  \u003Ccircle cx=\"50\" cy=\"50\" r=\"40\" fill=\"currentColor\" />\n\u003C/svg>\n\n\u003Cstyle>\n  .icon {\n    color: var(--text-primary);\n  }\n\u003C/style>\n",[952],{"type":27,"tag":169,"props":953,"children":954},{"__ignoreMap":7},[955],{"type":32,"value":950},{"type":27,"tag":55,"props":957,"children":959},{"id":958},"完整示例",[960],{"type":32,"value":958},{"type":27,"tag":497,"props":962,"children":965},{"className":963,"code":964,"language":553,"meta":7},[551],"import { useState, useEffect } from 'react';\n\nfunction ThemeProvider({ children }) {\n  const [theme, setTheme] = useState('light');\n  const [mounted, setMounted] = useState(false);\n  \n  useEffect(() => {\n    setMounted(true);\n    \n    // 获取保存的主题或系统偏好\n    const savedTheme = localStorage.getItem('theme');\n    const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches \n      ? 'dark' \n      : 'light';\n    \n    const initialTheme = savedTheme || systemTheme;\n    setTheme(initialTheme);\n    document.documentElement.setAttribute('data-theme', initialTheme);\n  }, []);\n  \n  const toggleTheme = () => {\n    const newTheme = theme === 'light' ? 'dark' : 'light';\n    setTheme(newTheme);\n    localStorage.setItem('theme', newTheme);\n    document.documentElement.setAttribute('data-theme', newTheme);\n  };\n  \n  // 防止闪烁\n  if (!mounted) {\n    return null;\n  }\n  \n  return (\n    \u003CThemeContext.Provider value={{ theme, toggleTheme }}>\n      {children}\n      \u003CThemeToggle theme={theme} onChange={toggleTheme} />\n    \u003C/ThemeContext.Provider>\n  );\n}\n\nfunction ThemeToggle({ theme, onChange }) {\n  return (\n    \u003Cbutton \n      onClick={onChange}\n      aria-label={`切换到${theme === 'light' ? '暗黑' : '亮色'}模式`}\n    >\n      {theme === 'light' ? '🌙' : '☀️'}\n    \u003C/button>\n  );\n}\n",[966],{"type":27,"tag":169,"props":967,"children":968},{"__ignoreMap":7},[969],{"type":32,"value":964},{"type":27,"tag":55,"props":971,"children":972},{"id":635},[973],{"type":32,"value":635},{"type":27,"tag":28,"props":975,"children":976},{},[977,978,982],{"type":32,"value":642},{"type":27,"tag":644,"props":979,"children":980},{},[981],{"type":32,"value":648},{"type":32,"value":650},{"type":27,"tag":35,"props":984,"children":985},{},[986,991,996,1001,1006],{"type":27,"tag":39,"props":987,"children":988},{},[989],{"type":32,"value":990},"支持系统偏好",{"type":27,"tag":39,"props":992,"children":993},{},[994],{"type":32,"value":995},"提供手动切换选项",{"type":27,"tag":39,"props":997,"children":998},{},[999],{"type":32,"value":1000},"确保足够的对比度",{"type":27,"tag":39,"props":1002,"children":1003},{},[1004],{"type":32,"value":1005},"优化图片和图表",{"type":27,"tag":39,"props":1007,"children":1008},{},[1009],{"type":32,"value":1010},"防止加载闪烁",{"type":27,"tag":28,"props":1012,"children":1013},{},[1014,1015,1019],{"type":32,"value":689},{"type":27,"tag":644,"props":1016,"children":1017},{},[1018],{"type":32,"value":694},{"type":32,"value":650},{"type":27,"tag":35,"props":1021,"children":1022},{},[1023,1028,1033,1038,1043],{"type":27,"tag":39,"props":1024,"children":1025},{},[1026],{"type":32,"value":1027},"强制单一模式",{"type":27,"tag":39,"props":1029,"children":1030},{},[1031],{"type":32,"value":1032},"忽视性能影响",{"type":27,"tag":39,"props":1034,"children":1035},{},[1036],{"type":32,"value":1037},"使用相同的颜色",{"type":27,"tag":39,"props":1039,"children":1040},{},[1041],{"type":32,"value":1042},"忘记保存用户偏好",{"type":27,"tag":39,"props":1044,"children":1045},{},[1046],{"type":32,"value":1047},"过度使用深色背景",{"type":27,"tag":55,"props":1049,"children":1050},{"id":741},[1051],{"type":32,"value":741},{"type":27,"tag":35,"props":1053,"children":1055},{"className":1054},[747],[1056,1065,1074,1083,1092],{"type":27,"tag":39,"props":1057,"children":1059},{"className":1058},[752],[1060,1063],{"type":27,"tag":755,"props":1061,"children":1062},{"disabled":757,"type":758},[],{"type":32,"value":1064}," 在浅色和深色模式下测试所有页面",{"type":27,"tag":39,"props":1066,"children":1068},{"className":1067},[752],[1069,1072],{"type":27,"tag":755,"props":1070,"children":1071},{"disabled":757,"type":758},[],{"type":32,"value":1073}," 检查颜色对比度符合 WCAG 标准",{"type":27,"tag":39,"props":1075,"children":1077},{"className":1076},[752],[1078,1081],{"type":27,"tag":755,"props":1079,"children":1080},{"disabled":757,"type":758},[],{"type":32,"value":1082}," 验证图片和图表在两种模式下清晰",{"type":27,"tag":39,"props":1084,"children":1086},{"className":1085},[752],[1087,1090],{"type":27,"tag":755,"props":1088,"children":1089},{"disabled":757,"type":758},[],{"type":32,"value":1091}," 测试主题切换的平滑性",{"type":27,"tag":39,"props":1093,"children":1095},{"className":1094},[752],[1096,1099],{"type":27,"tag":755,"props":1097,"children":1098},{"disabled":757,"type":758},[],{"type":32,"value":1100}," 检查用户偏好是否被保存",{"title":7,"searchDepth":442,"depth":442,"links":1102},[1103,1104,1108,1113,1114,1115,1116,1117],{"id":821,"depth":445,"text":821},{"id":843,"depth":445,"text":843,"children":1105},[1106,1107],{"id":848,"depth":442,"text":851},{"id":863,"depth":442,"text":866},{"id":878,"depth":445,"text":878,"children":1109},[1110,1111,1112],{"id":883,"depth":442,"text":886},{"id":898,"depth":442,"text":901},{"id":915,"depth":442,"text":918},{"id":930,"depth":445,"text":930},{"id":944,"depth":445,"text":944},{"id":958,"depth":445,"text":958},{"id":635,"depth":445,"text":635},{"id":741,"depth":445,"text":741},"content:topics:design:dark-mode-design.md","topics/design/dark-mode-design.md","topics/design/dark-mode-design",{"_path":1122,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1123,"description":1124,"keywords":1125,"image":1130,"author":1131,"date":471,"readingTime":829,"topic":5,"body":1132,"_type":453,"_id":1397,"_source":455,"_file":1398,"_stem":1399,"_extension":458},"/topics/design/form-controls-design","表单控件设计规范","学习输入框、选择框、复选框等表单控件的设计和实现",[1126,1127,1128,1129,469],"表单设计","Form Controls","输入框","验证反馈","/images/topics/form-controls-design.jpg","AI Content Team",{"type":24,"children":1133,"toc":1383},[1134,1138,1143,1148,1153,1162,1167,1176,1180,1189,1194,1203,1208,1217,1222,1231,1236,1245,1249,1258,1284,1293,1321,1325],{"type":27,"tag":55,"props":1135,"children":1136},{"id":1123},[1137],{"type":32,"value":1123},{"type":27,"tag":28,"props":1139,"children":1140},{},[1141],{"type":32,"value":1142},"优秀的表单设计能够提高用户完成率和满意度。",{"type":27,"tag":55,"props":1144,"children":1146},{"id":1145},"输入框设计",[1147],{"type":32,"value":1145},{"type":27,"tag":490,"props":1149,"children":1151},{"id":1150},"基础文本输入",[1152],{"type":32,"value":1150},{"type":27,"tag":497,"props":1154,"children":1157},{"className":1155,"code":1156,"language":502,"meta":7},[500],".input {\n  width: 100%;\n  padding: 12px 16px;\n  font-size: 16px;\n  border: 2px solid #e0e0e0;\n  border-radius: 4px;\n  font-family: inherit;\n  transition: border-color 0.2s;\n}\n\n.input:hover {\n  border-color: #bdbdbd;\n}\n\n.input:focus {\n  outline: none;\n  border-color: #0066cc;\n  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);\n}\n\n.input:disabled {\n  background-color: #f5f5f5;\n  color: #999999;\n  cursor: not-allowed;\n}\n\n.input.error {\n  border-color: #cc0000;\n}\n\n.input.success {\n  border-color: #00cc00;\n}\n",[1158],{"type":27,"tag":169,"props":1159,"children":1160},{"__ignoreMap":7},[1161],{"type":32,"value":1156},{"type":27,"tag":490,"props":1163,"children":1165},{"id":1164},"标签和提示",[1166],{"type":32,"value":1164},{"type":27,"tag":497,"props":1168,"children":1171},{"className":1169,"code":1170,"language":613,"meta":7},[611],"\u003Cdiv class=\"form-group\">\n  \u003Clabel for=\"email\" class=\"form-label\">\n    邮箱地址 \u003Cspan class=\"required\">*\u003C/span>\n  \u003C/label>\n  \u003Cinput\n    id=\"email\"\n    type=\"email\"\n    placeholder=\"user@example.com\"\n    class=\"input\"\n    aria-describedby=\"email-hint\"\n  />\n  \u003Cp id=\"email-hint\" class=\"form-hint\">\n    我们永远不会分享你的邮箱\n  \u003C/p>\n\u003C/div>\n",[1172],{"type":27,"tag":169,"props":1173,"children":1174},{"__ignoreMap":7},[1175],{"type":32,"value":1170},{"type":27,"tag":55,"props":1177,"children":1178},{"id":1129},[1179],{"type":32,"value":1129},{"type":27,"tag":497,"props":1181,"children":1184},{"className":1182,"code":1183,"language":553,"meta":7},[551],"function FormInput({ label, error, success, helperText, value, onChange, ...props }) {\n  return (\n    \u003Cdiv className=\"form-group\">\n      \u003Clabel className=\"form-label\">{label}\u003C/label>\n      \u003Cinput\n        className={`input ${\n          error ? 'error' : success ? 'success' : ''\n        }`}\n        value={value}\n        onChange={onChange}\n        {...props}\n      />\n      {error && (\n        \u003Cp className=\"form-error\" role=\"alert\">\n          {error}\n        \u003C/p>\n      )}\n      {success && (\n        \u003Cp className=\"form-success\">\n          ✓ {success}\n        \u003C/p>\n      )}\n      {helperText && (\n        \u003Cp className=\"form-hint\">{helperText}\u003C/p>\n      )}\n    \u003C/div>\n  );\n}\n",[1185],{"type":27,"tag":169,"props":1186,"children":1187},{"__ignoreMap":7},[1188],{"type":32,"value":1183},{"type":27,"tag":55,"props":1190,"children":1192},{"id":1191},"选择框设计",[1193],{"type":32,"value":1191},{"type":27,"tag":497,"props":1195,"children":1198},{"className":1196,"code":1197,"language":502,"meta":7},[500],".select {\n  appearance: none;\n  width: 100%;\n  padding: 12px 16px;\n  border: 2px solid #e0e0e0;\n  border-radius: 4px;\n  background-image: url('data:image/svg+xml;...');\n  background-repeat: no-repeat;\n  background-position: right 12px center;\n  padding-right: 40px;\n  font-size: 16px;\n  cursor: pointer;\n}\n\n.select:hover {\n  border-color: #bdbdbd;\n}\n\n.select:focus {\n  outline: none;\n  border-color: #0066cc;\n  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);\n}\n",[1199],{"type":27,"tag":169,"props":1200,"children":1201},{"__ignoreMap":7},[1202],{"type":32,"value":1197},{"type":27,"tag":55,"props":1204,"children":1206},{"id":1205},"复选框和单选按钮",[1207],{"type":32,"value":1205},{"type":27,"tag":497,"props":1209,"children":1212},{"className":1210,"code":1211,"language":502,"meta":7},[500],".checkbox-group {\n  display: flex;\n  gap: 12px;\n  align-items: center;\n}\n\n.checkbox-input {\n  width: 20px;\n  height: 20px;\n  cursor: pointer;\n  accent-color: #0066cc;\n}\n\n.checkbox-label {\n  cursor: pointer;\n  user-select: none;\n}\n\n/* 自定义复选框 */\n.custom-checkbox {\n  appearance: none;\n  width: 20px;\n  height: 20px;\n  border: 2px solid #e0e0e0;\n  border-radius: 4px;\n  cursor: pointer;\n  background-color: white;\n  transition: all 0.2s;\n}\n\n.custom-checkbox:checked {\n  background-color: #0066cc;\n  border-color: #0066cc;\n  background-image: url('data:image/svg+xml;...');\n}\n\n.custom-checkbox:focus {\n  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);\n}\n",[1213],{"type":27,"tag":169,"props":1214,"children":1215},{"__ignoreMap":7},[1216],{"type":32,"value":1211},{"type":27,"tag":55,"props":1218,"children":1220},{"id":1219},"文本区域",[1221],{"type":32,"value":1219},{"type":27,"tag":497,"props":1223,"children":1226},{"className":1224,"code":1225,"language":502,"meta":7},[500],".textarea {\n  width: 100%;\n  min-height: 120px;\n  padding: 12px 16px;\n  border: 2px solid #e0e0e0;\n  border-radius: 4px;\n  font-family: inherit;\n  font-size: 16px;\n  resize: vertical;\n  transition: border-color 0.2s;\n}\n\n.textarea:focus {\n  outline: none;\n  border-color: #0066cc;\n  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);\n}\n",[1227],{"type":27,"tag":169,"props":1228,"children":1229},{"__ignoreMap":7},[1230],{"type":32,"value":1225},{"type":27,"tag":55,"props":1232,"children":1234},{"id":1233},"完整表单示例",[1235],{"type":32,"value":1233},{"type":27,"tag":497,"props":1237,"children":1240},{"className":1238,"code":1239,"language":553,"meta":7},[551],"function SignupForm() {\n  const [formData, setFormData] = useState({\n    name: '',\n    email: '',\n    password: '',\n    confirmPassword: '',\n    subscribe: false,\n    terms: false,\n  });\n  \n  const [errors, setErrors] = useState({});\n  const [touched, setTouched] = useState({});\n  const [submitted, setSubmitted] = useState(false);\n  \n  const handleChange = (e) => {\n    const { name, value, type, checked } = e.target;\n    setFormData(prev => ({\n      ...prev,\n      [name]: type === 'checkbox' ? checked : value,\n    }));\n    \n    // 实时验证\n    if (touched[name]) {\n      validateField(name, type === 'checkbox' ? checked : value);\n    }\n  };\n  \n  const handleBlur = (e) => {\n    const { name } = e.target;\n    setTouched(prev => ({ ...prev, [name]: true }));\n    validateField(name, formData[name]);\n  };\n  \n  const validateField = (name, value) => {\n    const newErrors = { ...errors };\n    \n    switch (name) {\n      case 'name':\n        if (!value) newErrors.name = '名字不能为空';\n        else delete newErrors.name;\n        break;\n      case 'email':\n        if (!value) newErrors.email = '邮箱不能为空';\n        else if (!/^[^\\\\s@]+@[^\\\\s@]+\\\\.[^\\\\s@]+$/.test(value)) {\n          newErrors.email = '请输入有效的邮箱';\n        } else {\n          delete newErrors.email;\n        }\n        break;\n      case 'password':\n        if (!value) newErrors.password = '密码不能为空';\n        else if (value.length \u003C 8) newErrors.password = '密码至少 8 位';\n        else delete newErrors.password;\n        break;\n      case 'confirmPassword':\n        if (value !== formData.password) {\n          newErrors.confirmPassword = '两次密码输入不一致';\n        } else {\n          delete newErrors.confirmPassword;\n        }\n        break;\n      case 'terms':\n        if (!value) newErrors.terms = '必须同意服务条款';\n        else delete newErrors.terms;\n        break;\n      default:\n        break;\n    }\n    \n    setErrors(newErrors);\n  };\n  \n  const validate = () => {\n    const newErrors = {};\n    \n    if (!formData.name) newErrors.name = '名字不能为空';\n    if (!formData.email) newErrors.email = '邮箱不能为空';\n    if (formData.password.length \u003C 8) newErrors.password = '密码至少 8 位';\n    if (formData.password !== formData.confirmPassword) {\n      newErrors.confirmPassword = '两次密码输入不一致';\n    }\n    if (!formData.terms) newErrors.terms = '必须同意服务条款';\n    \n    return newErrors;\n  };\n  \n  const handleSubmit = async (e) => {\n    e.preventDefault();\n    \n    // 标记所有字段已触碰\n    setTouched({\n      name: true,\n      email: true,\n      password: true,\n      confirmPassword: true,\n      terms: true,\n    });\n    \n    const newErrors = validate();\n    \n    if (Object.keys(newErrors).length === 0) {\n      setSubmitted(true);\n      // 提交表单\n      console.log('Form submitted:', formData);\n      // 重置表单\n      setFormData({\n        name: '',\n        email: '',\n        password: '',\n        confirmPassword: '',\n        subscribe: false,\n        terms: false,\n      });\n    } else {\n      setErrors(newErrors);\n    }\n  };\n  \n  return (\n    \u003Cform onSubmit={handleSubmit} noValidate>\n      {submitted && (\n        \u003Cdiv className=\"form-success-message\" role=\"alert\">\n          注册成功！\n        \u003C/div>\n      )}\n      \n      \u003CFormInput\n        label=\"姓名\"\n        name=\"name\"\n        value={formData.name}\n        onChange={handleChange}\n        onBlur={handleBlur}\n        error={touched.name && errors.name}\n        helperText=\"请输入你的全名\"\n      />\n      \n      \u003CFormInput\n        label=\"邮箱\"\n        name=\"email\"\n        type=\"email\"\n        value={formData.email}\n        onChange={handleChange}\n        onBlur={handleBlur}\n        error={touched.email && errors.email}\n      />\n      \n      \u003CFormInput\n        label=\"密码\"\n        name=\"password\"\n        type=\"password\"\n        value={formData.password}\n        onChange={handleChange}\n        onBlur={handleBlur}\n        error={touched.password && errors.password}\n        helperText=\"至少 8 个字符\"\n      />\n      \n      \u003CFormInput\n        label=\"确认密码\"\n        name=\"confirmPassword\"\n        type=\"password\"\n        value={formData.confirmPassword}\n        onChange={handleChange}\n        onBlur={handleBlur}\n        error={touched.confirmPassword && errors.confirmPassword}\n      />\n      \n      \u003Cdiv className=\"form-group\">\n        \u003Clabel className=\"checkbox-label\">\n          \u003Cinput\n            type=\"checkbox\"\n            name=\"subscribe\"\n            checked={formData.subscribe}\n            onChange={handleChange}\n            className=\"checkbox-input\"\n          />\n          订阅我们的新闻通讯\n        \u003C/label>\n      \u003C/div>\n      \n      \u003Cdiv className=\"form-group\">\n        \u003Clabel className=\"checkbox-label\">\n          \u003Cinput\n            type=\"checkbox\"\n            name=\"terms\"\n            checked={formData.terms}\n            onChange={handleChange}\n            onBlur={handleBlur}\n            className=\"checkbox-input\"\n          />\n          我同意\n          \u003Ca href=\"/terms\" target=\"_blank\" rel=\"noopener noreferrer\">\n            服务条款\n          \u003C/a>\n          和\n          \u003Ca href=\"/privacy\" target=\"_blank\" rel=\"noopener noreferrer\">\n            隐私政策\n          \u003C/a>\n        \u003C/label>\n        {touched.terms && errors.terms && (\n          \u003Cp className=\"form-error\">{errors.terms}\u003C/p>\n        )}\n      \u003C/div>\n      \n      \u003Cbutton type=\"submit\" className=\"btn btn-primary btn-block\">\n        注册\n      \u003C/button>\n    \u003C/form>\n  );\n}\n",[1241],{"type":27,"tag":169,"props":1242,"children":1243},{"__ignoreMap":7},[1244],{"type":32,"value":1239},{"type":27,"tag":55,"props":1246,"children":1247},{"id":635},[1248],{"type":32,"value":635},{"type":27,"tag":28,"props":1250,"children":1251},{},[1252,1253,1257],{"type":32,"value":642},{"type":27,"tag":644,"props":1254,"children":1255},{},[1256],{"type":32,"value":648},{"type":32,"value":650},{"type":27,"tag":35,"props":1259,"children":1260},{},[1261,1266,1271,1276,1280],{"type":27,"tag":39,"props":1262,"children":1263},{},[1264],{"type":32,"value":1265},"使用正确的输入类型",{"type":27,"tag":39,"props":1267,"children":1268},{},[1269],{"type":32,"value":1270},"提供实时验证反馈",{"type":27,"tag":39,"props":1272,"children":1273},{},[1274],{"type":32,"value":1275},"清晰的标签和提示",{"type":27,"tag":39,"props":1277,"children":1278},{},[1279],{"type":32,"value":658},{"type":27,"tag":39,"props":1281,"children":1282},{},[1283],{"type":32,"value":684},{"type":27,"tag":28,"props":1285,"children":1286},{},[1287,1288,1292],{"type":32,"value":689},{"type":27,"tag":644,"props":1289,"children":1290},{},[1291],{"type":32,"value":694},{"type":32,"value":650},{"type":27,"tag":35,"props":1294,"children":1295},{},[1296,1301,1306,1311,1316],{"type":27,"tag":39,"props":1297,"children":1298},{},[1299],{"type":32,"value":1300},"隐藏标签",{"type":27,"tag":39,"props":1302,"children":1303},{},[1304],{"type":32,"value":1305},"过度使用占位符",{"type":27,"tag":39,"props":1307,"children":1308},{},[1309],{"type":32,"value":1310},"验证后立即提交",{"type":27,"tag":39,"props":1312,"children":1313},{},[1314],{"type":32,"value":1315},"忽视无障碍性",{"type":27,"tag":39,"props":1317,"children":1318},{},[1319],{"type":32,"value":1320},"复杂的验证规则",{"type":27,"tag":55,"props":1322,"children":1323},{"id":741},[1324],{"type":32,"value":741},{"type":27,"tag":35,"props":1326,"children":1328},{"className":1327},[747],[1329,1338,1347,1356,1365,1374],{"type":27,"tag":39,"props":1330,"children":1332},{"className":1331},[752],[1333,1336],{"type":27,"tag":755,"props":1334,"children":1335},{"disabled":757,"type":758},[],{"type":32,"value":1337}," 所有控件都可用键盘导航",{"type":27,"tag":39,"props":1339,"children":1341},{"className":1340},[752],[1342,1345],{"type":27,"tag":755,"props":1343,"children":1344},{"disabled":757,"type":758},[],{"type":32,"value":1346}," 标签与输入框关联",{"type":27,"tag":39,"props":1348,"children":1350},{"className":1349},[752],[1351,1354],{"type":27,"tag":755,"props":1352,"children":1353},{"disabled":757,"type":758},[],{"type":32,"value":1355}," 验证消息清晰",{"type":27,"tag":39,"props":1357,"children":1359},{"className":1358},[752],[1360,1363],{"type":27,"tag":755,"props":1361,"children":1362},{"disabled":757,"type":758},[],{"type":32,"value":1364}," 色彩对比度足够",{"type":27,"tag":39,"props":1366,"children":1368},{"className":1367},[752],[1369,1372],{"type":27,"tag":755,"props":1370,"children":1371},{"disabled":757,"type":758},[],{"type":32,"value":1373}," 屏幕阅读器兼容",{"type":27,"tag":39,"props":1375,"children":1377},{"className":1376},[752],[1378,1381],{"type":27,"tag":755,"props":1379,"children":1380},{"disabled":757,"type":758},[],{"type":32,"value":1382}," 移动设备测试",{"title":7,"searchDepth":442,"depth":442,"links":1384},[1385,1386,1390,1391,1392,1393,1394,1395,1396],{"id":1123,"depth":445,"text":1123},{"id":1145,"depth":445,"text":1145,"children":1387},[1388,1389],{"id":1150,"depth":442,"text":1150},{"id":1164,"depth":442,"text":1164},{"id":1129,"depth":445,"text":1129},{"id":1191,"depth":445,"text":1191},{"id":1205,"depth":445,"text":1205},{"id":1219,"depth":445,"text":1219},{"id":1233,"depth":445,"text":1233},{"id":635,"depth":445,"text":635},{"id":741,"depth":445,"text":741},"content:topics:design:form-controls-design.md","topics/design/form-controls-design.md","topics/design/form-controls-design",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":1401,"image":18,"imageQuery":19,"pexelsPhotoId":20,"pexelsUrl":21,"featured":6,"readingTime":22,"body":1402,"_type":453,"_id":454,"_source":455,"_file":456,"_stem":457,"_extension":458},[13,14,15,16,17],{"type":24,"children":1403,"toc":1737},[1404,1408,1419,1423,1427,1431,1446,1450,1465,1469,1473,1477,1492,1496,1500,1504,1508,1535,1539,1543,1570,1574,1578,1582,1586,1597,1601,1605,1609,1624,1628,1632,1636,1640,1655,1659,1674,1678,1701,1705,1709,1713],{"type":27,"tag":28,"props":1405,"children":1406},{},[1407],{"type":32,"value":33},{"type":27,"tag":35,"props":1409,"children":1410},{},[1411,1415],{"type":27,"tag":39,"props":1412,"children":1413},{},[1414],{"type":32,"value":43},{"type":27,"tag":39,"props":1416,"children":1417},{},[1418],{"type":32,"value":48},{"type":27,"tag":28,"props":1420,"children":1421},{},[1422],{"type":32,"value":53},{"type":27,"tag":55,"props":1424,"children":1425},{"id":57},[1426],{"type":32,"value":57},{"type":27,"tag":28,"props":1428,"children":1429},{},[1430],{"type":32,"value":64},{"type":27,"tag":35,"props":1432,"children":1433},{},[1434,1438,1442],{"type":27,"tag":39,"props":1435,"children":1436},{},[1437],{"type":32,"value":72},{"type":27,"tag":39,"props":1439,"children":1440},{},[1441],{"type":32,"value":77},{"type":27,"tag":39,"props":1443,"children":1444},{},[1445],{"type":32,"value":82},{"type":27,"tag":28,"props":1447,"children":1448},{},[1449],{"type":32,"value":87},{"type":27,"tag":35,"props":1451,"children":1452},{},[1453,1457,1461],{"type":27,"tag":39,"props":1454,"children":1455},{},[1456],{"type":32,"value":95},{"type":27,"tag":39,"props":1458,"children":1459},{},[1460],{"type":32,"value":100},{"type":27,"tag":39,"props":1462,"children":1463},{},[1464],{"type":32,"value":105},{"type":27,"tag":28,"props":1466,"children":1467},{},[1468],{"type":32,"value":110},{"type":27,"tag":55,"props":1470,"children":1471},{"id":113},[1472],{"type":32,"value":116},{"type":27,"tag":28,"props":1474,"children":1475},{},[1476],{"type":32,"value":121},{"type":27,"tag":123,"props":1478,"children":1479},{},[1480,1484,1488],{"type":27,"tag":39,"props":1481,"children":1482},{},[1483],{"type":32,"value":130},{"type":27,"tag":39,"props":1485,"children":1486},{},[1487],{"type":32,"value":135},{"type":27,"tag":39,"props":1489,"children":1490},{},[1491],{"type":32,"value":140},{"type":27,"tag":28,"props":1493,"children":1494},{},[1495],{"type":32,"value":145},{"type":27,"tag":55,"props":1497,"children":1498},{"id":148},[1499],{"type":32,"value":151},{"type":27,"tag":28,"props":1501,"children":1502},{},[1503],{"type":32,"value":156},{"type":27,"tag":28,"props":1505,"children":1506},{},[1507],{"type":32,"value":161},{"type":27,"tag":35,"props":1509,"children":1510},{},[1511,1519,1527],{"type":27,"tag":39,"props":1512,"children":1513},{},[1514],{"type":27,"tag":169,"props":1515,"children":1517},{"className":1516},[],[1518],{"type":32,"value":174},{"type":27,"tag":39,"props":1520,"children":1521},{},[1522],{"type":27,"tag":169,"props":1523,"children":1525},{"className":1524},[],[1526],{"type":32,"value":183},{"type":27,"tag":39,"props":1528,"children":1529},{},[1530],{"type":27,"tag":169,"props":1531,"children":1533},{"className":1532},[],[1534],{"type":32,"value":192},{"type":27,"tag":28,"props":1536,"children":1537},{},[1538],{"type":32,"value":197},{"type":27,"tag":28,"props":1540,"children":1541},{},[1542],{"type":32,"value":202},{"type":27,"tag":35,"props":1544,"children":1545},{},[1546,1554,1562],{"type":27,"tag":39,"props":1547,"children":1548},{},[1549],{"type":27,"tag":169,"props":1550,"children":1552},{"className":1551},[],[1553],{"type":32,"value":214},{"type":27,"tag":39,"props":1555,"children":1556},{},[1557],{"type":27,"tag":169,"props":1558,"children":1560},{"className":1559},[],[1561],{"type":32,"value":223},{"type":27,"tag":39,"props":1563,"children":1564},{},[1565],{"type":27,"tag":169,"props":1566,"children":1568},{"className":1567},[],[1569],{"type":32,"value":232},{"type":27,"tag":28,"props":1571,"children":1572},{},[1573],{"type":32,"value":237},{"type":27,"tag":55,"props":1575,"children":1576},{"id":240},[1577],{"type":32,"value":243},{"type":27,"tag":28,"props":1579,"children":1580},{},[1581],{"type":32,"value":248},{"type":27,"tag":28,"props":1583,"children":1584},{},[1585],{"type":32,"value":253},{"type":27,"tag":35,"props":1587,"children":1588},{},[1589,1593],{"type":27,"tag":39,"props":1590,"children":1591},{},[1592],{"type":32,"value":261},{"type":27,"tag":39,"props":1594,"children":1595},{},[1596],{"type":32,"value":266},{"type":27,"tag":28,"props":1598,"children":1599},{},[1600],{"type":32,"value":271},{"type":27,"tag":55,"props":1602,"children":1603},{"id":274},[1604],{"type":32,"value":277},{"type":27,"tag":28,"props":1606,"children":1607},{},[1608],{"type":32,"value":282},{"type":27,"tag":35,"props":1610,"children":1611},{},[1612,1616,1620],{"type":27,"tag":39,"props":1613,"children":1614},{},[1615],{"type":32,"value":290},{"type":27,"tag":39,"props":1617,"children":1618},{},[1619],{"type":32,"value":295},{"type":27,"tag":39,"props":1621,"children":1622},{},[1623],{"type":32,"value":300},{"type":27,"tag":28,"props":1625,"children":1626},{},[1627],{"type":32,"value":305},{"type":27,"tag":28,"props":1629,"children":1630},{},[1631],{"type":32,"value":310},{"type":27,"tag":55,"props":1633,"children":1634},{"id":313},[1635],{"type":32,"value":313},{"type":27,"tag":28,"props":1637,"children":1638},{},[1639],{"type":32,"value":320},{"type":27,"tag":35,"props":1641,"children":1642},{},[1643,1647,1651],{"type":27,"tag":39,"props":1644,"children":1645},{},[1646],{"type":32,"value":328},{"type":27,"tag":39,"props":1648,"children":1649},{},[1650],{"type":32,"value":333},{"type":27,"tag":39,"props":1652,"children":1653},{},[1654],{"type":32,"value":338},{"type":27,"tag":28,"props":1656,"children":1657},{},[1658],{"type":32,"value":343},{"type":27,"tag":35,"props":1660,"children":1661},{},[1662,1666,1670],{"type":27,"tag":39,"props":1663,"children":1664},{},[1665],{"type":32,"value":351},{"type":27,"tag":39,"props":1667,"children":1668},{},[1669],{"type":32,"value":356},{"type":27,"tag":39,"props":1671,"children":1672},{},[1673],{"type":32,"value":361},{"type":27,"tag":55,"props":1675,"children":1676},{"id":364},[1677],{"type":32,"value":364},{"type":27,"tag":35,"props":1679,"children":1680},{},[1681,1685,1689,1693,1697],{"type":27,"tag":39,"props":1682,"children":1683},{},[1684],{"type":32,"value":374},{"type":27,"tag":39,"props":1686,"children":1687},{},[1688],{"type":32,"value":379},{"type":27,"tag":39,"props":1690,"children":1691},{},[1692],{"type":32,"value":384},{"type":27,"tag":39,"props":1694,"children":1695},{},[1696],{"type":32,"value":389},{"type":27,"tag":39,"props":1698,"children":1699},{},[1700],{"type":32,"value":394},{"type":27,"tag":55,"props":1702,"children":1703},{"id":397},[1704],{"type":32,"value":397},{"type":27,"tag":28,"props":1706,"children":1707},{},[1708],{"type":32,"value":404},{"type":27,"tag":28,"props":1710,"children":1711},{},[1712],{"type":32,"value":409},{"type":27,"tag":35,"props":1714,"children":1715},{},[1716,1723,1730],{"type":27,"tag":39,"props":1717,"children":1718},{},[1719],{"type":27,"tag":417,"props":1720,"children":1721},{"href":419},[1722],{"type":32,"value":422},{"type":27,"tag":39,"props":1724,"children":1725},{},[1726],{"type":27,"tag":417,"props":1727,"children":1728},{"href":428},[1729],{"type":32,"value":431},{"type":27,"tag":39,"props":1731,"children":1732},{},[1733],{"type":27,"tag":417,"props":1734,"children":1735},{"href":437},[1736],{"type":32,"value":440},{"title":7,"searchDepth":442,"depth":442,"links":1738},[1739,1740,1741,1742,1743,1744,1745,1746],{"id":57,"depth":445,"text":57},{"id":113,"depth":445,"text":116},{"id":148,"depth":445,"text":151},{"id":240,"depth":445,"text":243},{"id":274,"depth":445,"text":277},{"id":313,"depth":445,"text":313},{"id":364,"depth":445,"text":364},{"id":397,"depth":445,"text":397},1776916083330]