[{"data":1,"prerenderedAt":1643},["ShallowReactive",2],{"article-/topics/design/design-system-2026-annual-review":3,"related-design":405,"content-query-udBkHvEXNi":1346},{"_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":399,"_id":400,"_source":401,"_file":402,"_stem":403,"_extension":404},"/topics/design/design-system-2026-annual-review","design",false,"","2026 设计系统年度总结：从组件库扩张回到治理、协作与更新成本","2026 年设计系统建设的重心，已经从“做更多组件”逐步转向“让系统真正被持续使用”。本文从治理、文档、协作和发布视角回顾这一年的关键变化。","2026-05-02","HTMLPAGE 团队",[13,14,15,16,17],"Design System","Annual Review","Design Ops","Collaboration","Component Library","/images/topics/design/design-system-2026-annual-review.jpg","design system planning board and laptop workspace",7180972,"https://www.pexels.com/photo/a-person-discussing-with-a-man-a-drawing-on-a-white-board-7180972/",15,{"type":24,"children":25,"toc":387},"root",[26,34,39,48,55,60,85,90,113,118,123,128,133,151,156,162,167,190,195,201,206,211,229,234,240,245,268,273,279,284,308,313,341,346,351,356],{"type":27,"tag":28,"props":29,"children":30},"element","p",{},[31],{"type":32,"value":33},"text","如果说前几年设计系统建设的主旋律还是“做起来”，那么 2026 年更明显的变化是：",{"type":27,"tag":28,"props":35,"children":36},{},[37],{"type":32,"value":38},"大家开始更认真地面对另一个问题。",{"type":27,"tag":28,"props":40,"children":41},{},[42],{"type":27,"tag":43,"props":44,"children":45},"strong",{},[46],{"type":32,"value":47},"设计系统做出来之后，怎样才能持续被使用，而不是持续被绕开？",{"type":27,"tag":49,"props":50,"children":52},"h2",{"id":51},"_2026-年最大的变化设计系统从资产建设转向运营治理",[53],{"type":32,"value":54},"2026 年最大的变化：设计系统从资产建设转向运营治理",{"type":27,"tag":28,"props":56,"children":57},{},[58],{"type":32,"value":59},"过去很多团队衡量设计系统进展，会先看：",{"type":27,"tag":61,"props":62,"children":63},"ul",{},[64,70,75,80],{"type":27,"tag":65,"props":66,"children":67},"li",{},[68],{"type":32,"value":69},"有多少组件",{"type":27,"tag":65,"props":71,"children":72},{},[73],{"type":32,"value":74},"覆盖了多少页面",{"type":27,"tag":65,"props":76,"children":77},{},[78],{"type":32,"value":79},"token 是否齐全",{"type":27,"tag":65,"props":81,"children":82},{},[83],{"type":32,"value":84},"有没有配套文档",{"type":27,"tag":28,"props":86,"children":87},{},[88],{"type":32,"value":89},"这些依然重要，但今年更核心的衡量标准开始变化：",{"type":27,"tag":61,"props":91,"children":92},{},[93,98,103,108],{"type":27,"tag":65,"props":94,"children":95},{},[96],{"type":32,"value":97},"团队是否真的按规则使用",{"type":27,"tag":65,"props":99,"children":100},{},[101],{"type":32,"value":102},"变更是否能稳定发布",{"type":27,"tag":65,"props":104,"children":105},{},[106],{"type":32,"value":107},"设计与开发是否共享同一套判断",{"type":27,"tag":65,"props":109,"children":110},{},[111],{"type":32,"value":112},"系统是否在降低协作成本",{"type":27,"tag":28,"props":114,"children":115},{},[116],{"type":32,"value":117},"这意味着设计系统越来越像长期运营产品，而不是一次性建设项目。",{"type":27,"tag":49,"props":119,"children":121},{"id":120},"组件数量不再是最有说服力的成果",[122],{"type":32,"value":120},{"type":27,"tag":28,"props":124,"children":125},{},[126],{"type":32,"value":127},"2026 年很多团队开始意识到，组件越多不一定代表系统越成熟。",{"type":27,"tag":28,"props":129,"children":130},{},[131],{"type":32,"value":132},"更常见的问题反而是：",{"type":27,"tag":61,"props":134,"children":135},{},[136,141,146],{"type":27,"tag":65,"props":137,"children":138},{},[139],{"type":32,"value":140},"组件名义上很多，实际重复严重",{"type":27,"tag":65,"props":142,"children":143},{},[144],{"type":32,"value":145},"边界模糊，选择成本高",{"type":27,"tag":65,"props":147,"children":148},{},[149],{"type":32,"value":150},"维护者越来越少，变更越来越慢",{"type":27,"tag":28,"props":152,"children":153},{},[154],{"type":32,"value":155},"因此系统成熟度的讨论，越来越从“数量”转向“是否有清晰边界和维护机制”。",{"type":27,"tag":49,"props":157,"children":159},{"id":158},"文档评审和协作流程的地位明显提升",[160],{"type":32,"value":161},"文档、评审和协作流程的地位明显提升",{"type":27,"tag":28,"props":163,"children":164},{},[165],{"type":32,"value":166},"设计系统在今年一个很重要的变化，是大家开始更重视非组件层资产：",{"type":27,"tag":61,"props":168,"children":169},{},[170,175,180,185],{"type":27,"tag":65,"props":171,"children":172},{},[173],{"type":32,"value":174},"文档结构",{"type":27,"tag":65,"props":176,"children":177},{},[178],{"type":32,"value":179},"设计走查",{"type":27,"tag":65,"props":181,"children":182},{},[183],{"type":32,"value":184},"设计与开发交付标准",{"type":27,"tag":65,"props":186,"children":187},{},[188],{"type":32,"value":189},"更新记录与废弃策略",{"type":27,"tag":28,"props":191,"children":192},{},[193],{"type":32,"value":194},"原因很现实：组件库本身只是产物，真正影响团队效率的是使用过程中的判断一致性。",{"type":27,"tag":49,"props":196,"children":198},{"id":197},"token-与规范依然重要但更强调变更管理",[199],{"type":32,"value":200},"Token 与规范依然重要，但更强调变更管理",{"type":27,"tag":28,"props":202,"children":203},{},[204],{"type":32,"value":205},"2026 年设计系统的难点不再只是“怎么统一”，而是“统一之后怎么继续演进”。",{"type":27,"tag":28,"props":207,"children":208},{},[209],{"type":32,"value":210},"例如：",{"type":27,"tag":61,"props":212,"children":213},{},[214,219,224],{"type":27,"tag":65,"props":215,"children":216},{},[217],{"type":32,"value":218},"token 变更如何影响现有页面",{"type":27,"tag":65,"props":220,"children":221},{},[222],{"type":32,"value":223},"组件 API 调整如何平滑迁移",{"type":27,"tag":65,"props":225,"children":226},{},[227],{"type":32,"value":228},"规范升级如何通知并推动落地",{"type":27,"tag":28,"props":230,"children":231},{},[232],{"type":32,"value":233},"这说明设计系统工作越来越偏向治理与版本管理，而不仅仅是视觉规则制定。",{"type":27,"tag":49,"props":235,"children":237},{"id":236},"一个常见失败案例系统在扩张团队信任却在下降",[238],{"type":32,"value":239},"一个常见失败案例：系统在扩张，团队信任却在下降",{"type":27,"tag":28,"props":241,"children":242},{},[243],{"type":32,"value":244},"这类项目通常不是没有投入，而是治理机制跟不上扩张速度：",{"type":27,"tag":61,"props":246,"children":247},{},[248,253,258,263],{"type":27,"tag":65,"props":249,"children":250},{},[251],{"type":32,"value":252},"文档更新慢于实现变化",{"type":27,"tag":65,"props":254,"children":255},{},[256],{"type":32,"value":257},"开发与设计理解出现偏差",{"type":27,"tag":65,"props":259,"children":260},{},[261],{"type":32,"value":262},"组件质量不稳定",{"type":27,"tag":65,"props":264,"children":265},{},[266],{"type":32,"value":267},"边界问题长期靠口头沟通解决",{"type":27,"tag":28,"props":269,"children":270},{},[271],{"type":32,"value":272},"结果就是系统表面变大了，团队反而更愿意绕开它。",{"type":27,"tag":49,"props":274,"children":276},{"id":275},"_2026-年最值得保留的几条经验",[277],{"type":32,"value":278},"2026 年最值得保留的几条经验",{"type":27,"tag":28,"props":280,"children":281},{},[282],{"type":32,"value":283},"如果把这一年的经验压缩成更稳的判断，大概是：",{"type":27,"tag":285,"props":286,"children":287},"ol",{},[288,293,298,303],{"type":27,"tag":65,"props":289,"children":290},{},[291],{"type":32,"value":292},"先保证边界清晰，再继续扩组件",{"type":27,"tag":65,"props":294,"children":295},{},[296],{"type":32,"value":297},"先让文档和变更机制稳定，再扩大覆盖范围",{"type":27,"tag":65,"props":299,"children":300},{},[301],{"type":32,"value":302},"先解决使用过程中的判断一致性，再追求形式完整",{"type":27,"tag":65,"props":304,"children":305},{},[306],{"type":32,"value":307},"设计系统的真正价值在协作，而不在库存",{"type":27,"tag":49,"props":309,"children":311},{"id":310},"一份可直接复用的年度复盘清单",[312],{"type":32,"value":310},{"type":27,"tag":61,"props":314,"children":315},{},[316,321,326,331,336],{"type":27,"tag":65,"props":317,"children":318},{},[319],{"type":32,"value":320},"团队今年最常绕开设计系统的原因是什么",{"type":27,"tag":65,"props":322,"children":323},{},[324],{"type":32,"value":325},"文档、评审和更新机制是否真正进入工作流",{"type":27,"tag":65,"props":327,"children":328},{},[329],{"type":32,"value":330},"组件边界和 token 变更是否越来越可预测",{"type":27,"tag":65,"props":332,"children":333},{},[334],{"type":32,"value":335},"系统建设是在降低还是增加协作成本",{"type":27,"tag":65,"props":337,"children":338},{},[339],{"type":32,"value":340},"明年最值得继续投入的是新组件、治理机制还是协作联动",{"type":27,"tag":49,"props":342,"children":344},{"id":343},"总结",[345],{"type":32,"value":343},{"type":27,"tag":28,"props":347,"children":348},{},[349],{"type":32,"value":350},"2026 年设计系统最有价值的变化，不是组件库变得更大，而是越来越多团队开始把它当成需要长期治理的协作系统。真正成熟的设计系统，不靠数量说话，而靠持续可用性建立信任。",{"type":27,"tag":28,"props":352,"children":353},{},[354],{"type":32,"value":355},"进一步阅读：",{"type":27,"tag":61,"props":357,"children":358},{},[359,369,378],{"type":27,"tag":65,"props":360,"children":361},{},[362],{"type":27,"tag":363,"props":364,"children":366},"a",{"href":365},"/topics/design/design-system-documentation-best-practices",[367],{"type":32,"value":368},"设计系统文档化最佳实践",{"type":27,"tag":65,"props":370,"children":371},{},[372],{"type":27,"tag":363,"props":373,"children":375},{"href":374},"/topics/design/design-development-collaboration-workflow",[376],{"type":32,"value":377},"设计与开发协作流程",{"type":27,"tag":65,"props":379,"children":380},{},[381],{"type":27,"tag":363,"props":382,"children":384},{"href":383},"/topics/design/storybook-7-complete-practice",[385],{"type":32,"value":386},"Storybook 7 完整实践",{"title":7,"searchDepth":388,"depth":388,"links":389},3,[390,392,393,394,395,396,397,398],{"id":51,"depth":391,"text":54},2,{"id":120,"depth":391,"text":120},{"id":158,"depth":391,"text":161},{"id":197,"depth":391,"text":200},{"id":236,"depth":391,"text":239},{"id":275,"depth":391,"text":278},{"id":310,"depth":391,"text":310},{"id":343,"depth":391,"text":343},"markdown","content:topics:design:design-system-2026-annual-review.md","content","topics/design/design-system-2026-annual-review.md","topics/design/design-system-2026-annual-review","md",[406,765,1067],{"_path":407,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":408,"description":409,"keywords":410,"image":416,"author":11,"date":417,"readingTime":418,"topic":5,"body":419,"_type":399,"_id":762,"_source":401,"_file":763,"_stem":764,"_extension":404},"/topics/design/button-component-design","按钮组件设计详解","学习按钮样式、交互状态、无障碍性和最佳实践",[411,412,413,414,415],"按钮设计","Button Component","交互状态","UI 组件","用户体验","/images/topics/button-design.jpg","2025-12-08",18,{"type":24,"children":420,"toc":744},[421,425,430,435,442,455,461,470,476,485,489,495,506,512,521,527,536,541,550,555,566,571,580,585,597,631,642,685,690],{"type":27,"tag":49,"props":422,"children":423},{"id":408},[424],{"type":32,"value":408},{"type":27,"tag":28,"props":426,"children":427},{},[428],{"type":32,"value":429},"按钮是 UI 中最重要的交互元素。优秀的按钮设计能够指导用户行为。",{"type":27,"tag":49,"props":431,"children":433},{"id":432},"按钮类型",[434],{"type":32,"value":432},{"type":27,"tag":436,"props":437,"children":439},"h3",{"id":438},"primary-button主按钮",[440],{"type":32,"value":441},"Primary Button（主按钮）",{"type":27,"tag":443,"props":444,"children":449},"pre",{"className":445,"code":447,"language":448,"meta":7},[446],"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",[450],{"type":27,"tag":451,"props":452,"children":453},"code",{"__ignoreMap":7},[454],{"type":32,"value":447},{"type":27,"tag":436,"props":456,"children":458},{"id":457},"secondary-button次按钮",[459],{"type":32,"value":460},"Secondary Button（次按钮）",{"type":27,"tag":443,"props":462,"children":465},{"className":463,"code":464,"language":448,"meta":7},[446],".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",[466],{"type":27,"tag":451,"props":467,"children":468},{"__ignoreMap":7},[469],{"type":32,"value":464},{"type":27,"tag":436,"props":471,"children":473},{"id":472},"danger-button危险按钮",[474],{"type":32,"value":475},"Danger Button（危险按钮）",{"type":27,"tag":443,"props":477,"children":480},{"className":478,"code":479,"language":448,"meta":7},[446],".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",[481],{"type":27,"tag":451,"props":482,"children":483},{"__ignoreMap":7},[484],{"type":32,"value":479},{"type":27,"tag":49,"props":486,"children":487},{"id":413},[488],{"type":32,"value":413},{"type":27,"tag":436,"props":490,"children":492},{"id":491},"loading-状态",[493],{"type":32,"value":494},"Loading 状态",{"type":27,"tag":443,"props":496,"children":501},{"className":497,"code":499,"language":500,"meta":7},[498],"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",[502],{"type":27,"tag":451,"props":503,"children":504},{"__ignoreMap":7},[505],{"type":32,"value":499},{"type":27,"tag":436,"props":507,"children":509},{"id":508},"disabled-状态",[510],{"type":32,"value":511},"Disabled 状态",{"type":27,"tag":443,"props":513,"children":516},{"className":514,"code":515,"language":448,"meta":7},[446],".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",[517],{"type":27,"tag":451,"props":518,"children":519},{"__ignoreMap":7},[520],{"type":32,"value":515},{"type":27,"tag":436,"props":522,"children":524},{"id":523},"focus-状态",[525],{"type":32,"value":526},"Focus 状态",{"type":27,"tag":443,"props":528,"children":531},{"className":529,"code":530,"language":448,"meta":7},[446],".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",[532],{"type":27,"tag":451,"props":533,"children":534},{"__ignoreMap":7},[535],{"type":32,"value":530},{"type":27,"tag":49,"props":537,"children":539},{"id":538},"按钮大小",[540],{"type":32,"value":538},{"type":27,"tag":443,"props":542,"children":545},{"className":543,"code":544,"language":448,"meta":7},[446],"/* 小按钮 */\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",[546],{"type":27,"tag":451,"props":547,"children":548},{"__ignoreMap":7},[549],{"type":32,"value":544},{"type":27,"tag":49,"props":551,"children":553},{"id":552},"无障碍性",[554],{"type":32,"value":552},{"type":27,"tag":443,"props":556,"children":561},{"className":557,"code":559,"language":560,"meta":7},[558],"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",[562],{"type":27,"tag":451,"props":563,"children":564},{"__ignoreMap":7},[565],{"type":32,"value":559},{"type":27,"tag":49,"props":567,"children":569},{"id":568},"完整组件示例",[570],{"type":32,"value":568},{"type":27,"tag":443,"props":572,"children":575},{"className":573,"code":574,"language":500,"meta":7},[498],"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",[576],{"type":27,"tag":451,"props":577,"children":578},{"__ignoreMap":7},[579],{"type":32,"value":574},{"type":27,"tag":49,"props":581,"children":583},{"id":582},"最佳实践",[584],{"type":32,"value":582},{"type":27,"tag":28,"props":586,"children":587},{},[588,590,595],{"type":32,"value":589},"✅ ",{"type":27,"tag":43,"props":591,"children":592},{},[593],{"type":32,"value":594},"应该做的事",{"type":32,"value":596},":",{"type":27,"tag":61,"props":598,"children":599},{},[600,605,610,621,626],{"type":27,"tag":65,"props":601,"children":602},{},[603],{"type":32,"value":604},"最小触摸目标 44x44px",{"type":27,"tag":65,"props":606,"children":607},{},[608],{"type":32,"value":609},"清晰的视觉反馈",{"type":27,"tag":65,"props":611,"children":612},{},[613,615],{"type":32,"value":614},"使用语义 HTML ",{"type":27,"tag":451,"props":616,"children":618},{"className":617},[],[619],{"type":32,"value":620},"\u003Cbutton>",{"type":27,"tag":65,"props":622,"children":623},{},[624],{"type":32,"value":625},"提供加载状态反馈",{"type":27,"tag":65,"props":627,"children":628},{},[629],{"type":32,"value":630},"支持键盘导航",{"type":27,"tag":28,"props":632,"children":633},{},[634,636,641],{"type":32,"value":635},"❌ ",{"type":27,"tag":43,"props":637,"children":638},{},[639],{"type":32,"value":640},"不应该做的事",{"type":32,"value":596},{"type":27,"tag":61,"props":643,"children":644},{},[645,658,663,668,673],{"type":27,"tag":65,"props":646,"children":647},{},[648,650,656],{"type":32,"value":649},"使用 ",{"type":27,"tag":451,"props":651,"children":653},{"className":652},[],[654],{"type":32,"value":655},"\u003Cdiv>",{"type":32,"value":657}," 模拟按钮",{"type":27,"tag":65,"props":659,"children":660},{},[661],{"type":32,"value":662},"隐藏焦点指示器",{"type":27,"tag":65,"props":664,"children":665},{},[666],{"type":32,"value":667},"过多的按钮样式",{"type":27,"tag":65,"props":669,"children":670},{},[671],{"type":32,"value":672},"忽视禁用状态",{"type":27,"tag":65,"props":674,"children":675},{},[676,677,683],{"type":32,"value":649},{"type":27,"tag":451,"props":678,"children":680},{"className":679},[],[681],{"type":32,"value":682},"\u003Ca>",{"type":32,"value":684}," 代替按钮",{"type":27,"tag":49,"props":686,"children":688},{"id":687},"测试清单",[689],{"type":32,"value":687},{"type":27,"tag":61,"props":691,"children":694},{"className":692},[693],"contains-task-list",[695,708,717,726,735],{"type":27,"tag":65,"props":696,"children":699},{"className":697},[698],"task-list-item",[700,706],{"type":27,"tag":701,"props":702,"children":705},"input",{"disabled":703,"type":704},true,"checkbox",[],{"type":32,"value":707}," 在各种浏览器中测试",{"type":27,"tag":65,"props":709,"children":711},{"className":710},[698],[712,715],{"type":27,"tag":701,"props":713,"children":714},{"disabled":703,"type":704},[],{"type":32,"value":716}," 验证键盘导航",{"type":27,"tag":65,"props":718,"children":720},{"className":719},[698],[721,724],{"type":27,"tag":701,"props":722,"children":723},{"disabled":703,"type":704},[],{"type":32,"value":725}," 检查色彩对比度",{"type":27,"tag":65,"props":727,"children":729},{"className":728},[698],[730,733],{"type":27,"tag":701,"props":731,"children":732},{"disabled":703,"type":704},[],{"type":32,"value":734}," 测试触摸设备",{"type":27,"tag":65,"props":736,"children":738},{"className":737},[698],[739,742],{"type":27,"tag":701,"props":740,"children":741},{"disabled":703,"type":704},[],{"type":32,"value":743}," 屏幕阅读器兼容性",{"title":7,"searchDepth":388,"depth":388,"links":745},[746,747,752,757,758,759,760,761],{"id":408,"depth":391,"text":408},{"id":432,"depth":391,"text":432,"children":748},[749,750,751],{"id":438,"depth":388,"text":441},{"id":457,"depth":388,"text":460},{"id":472,"depth":388,"text":475},{"id":413,"depth":391,"text":413,"children":753},[754,755,756],{"id":491,"depth":388,"text":494},{"id":508,"depth":388,"text":511},{"id":523,"depth":388,"text":526},{"id":538,"depth":391,"text":538},{"id":552,"depth":391,"text":552},{"id":568,"depth":391,"text":568},{"id":582,"depth":391,"text":582},{"id":687,"depth":391,"text":687},"content:topics:design:button-component-design.md","topics/design/button-component-design.md","topics/design/button-component-design",{"_path":766,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":767,"description":768,"keywords":769,"image":774,"author":11,"date":417,"readingTime":775,"topic":5,"body":776,"_type":399,"_id":1064,"_source":401,"_file":1065,"_stem":1066,"_extension":404},"/topics/design/dark-mode-design","暗黑模式设计完整方案","学习暗黑模式实现、色彩方案、对比度管理和最佳实践",[770,771,772,773,415],"暗黑模式","Dark Mode","色彩系统","CSS 变量","/images/topics/dark-mode-design.jpg",20,{"type":24,"children":777,"toc":1047},[778,782,787,792,798,807,813,822,827,833,842,848,859,865,874,879,888,893,902,907,916,920,929,957,966,994,998],{"type":27,"tag":49,"props":779,"children":780},{"id":767},[781],{"type":32,"value":767},{"type":27,"tag":28,"props":783,"children":784},{},[785],{"type":32,"value":786},"暗黑模式已成为现代应用的标准功能。它能够减少眼睛疲劳、节省电池、改善用户体验。",{"type":27,"tag":49,"props":788,"children":790},{"id":789},"核心色彩系统",[791],{"type":32,"value":789},{"type":27,"tag":436,"props":793,"children":795},{"id":794},"light-mode-配色",[796],{"type":32,"value":797},"Light Mode 配色",{"type":27,"tag":443,"props":799,"children":802},{"className":800,"code":801,"language":448,"meta":7},[446],":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",[803],{"type":27,"tag":451,"props":804,"children":805},{"__ignoreMap":7},[806],{"type":32,"value":801},{"type":27,"tag":436,"props":808,"children":810},{"id":809},"dark-mode-配色",[811],{"type":32,"value":812},"Dark Mode 配色",{"type":27,"tag":443,"props":814,"children":817},{"className":815,"code":816,"language":448,"meta":7},[446],"@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",[818],{"type":27,"tag":451,"props":819,"children":820},{"__ignoreMap":7},[821],{"type":32,"value":816},{"type":27,"tag":49,"props":823,"children":825},{"id":824},"实现方案",[826],{"type":32,"value":824},{"type":27,"tag":436,"props":828,"children":830},{"id":829},"方案-1prefers-color-scheme",[831],{"type":32,"value":832},"方案 1：prefers-color-scheme",{"type":27,"tag":443,"props":834,"children":837},{"className":835,"code":836,"language":448,"meta":7},[446],"/* 自动跟随系统设置 */\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",[838],{"type":27,"tag":451,"props":839,"children":840},{"__ignoreMap":7},[841],{"type":32,"value":836},{"type":27,"tag":436,"props":843,"children":845},{"id":844},"方案-2javascript-切换",[846],{"type":32,"value":847},"方案 2：JavaScript 切换",{"type":27,"tag":443,"props":849,"children":854},{"className":850,"code":852,"language":853,"meta":7},[851],"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",[855],{"type":27,"tag":451,"props":856,"children":857},{"__ignoreMap":7},[858],{"type":32,"value":852},{"type":27,"tag":436,"props":860,"children":862},{"id":861},"方案-3css-variables-javascript",[863],{"type":32,"value":864},"方案 3：CSS Variables + JavaScript",{"type":27,"tag":443,"props":866,"children":869},{"className":867,"code":868,"language":853,"meta":7},[851],"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",[870],{"type":27,"tag":451,"props":871,"children":872},{"__ignoreMap":7},[873],{"type":32,"value":868},{"type":27,"tag":49,"props":875,"children":877},{"id":876},"对比度管理",[878],{"type":32,"value":876},{"type":27,"tag":443,"props":880,"children":883},{"className":881,"code":882,"language":448,"meta":7},[446],"/* 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",[884],{"type":27,"tag":451,"props":885,"children":886},{"__ignoreMap":7},[887],{"type":32,"value":882},{"type":27,"tag":49,"props":889,"children":891},{"id":890},"图片和图表处理",[892],{"type":32,"value":890},{"type":27,"tag":443,"props":894,"children":897},{"className":895,"code":896,"language":560,"meta":7},[558],"\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",[898],{"type":27,"tag":451,"props":899,"children":900},{"__ignoreMap":7},[901],{"type":32,"value":896},{"type":27,"tag":49,"props":903,"children":905},{"id":904},"完整示例",[906],{"type":32,"value":904},{"type":27,"tag":443,"props":908,"children":911},{"className":909,"code":910,"language":500,"meta":7},[498],"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",[912],{"type":27,"tag":451,"props":913,"children":914},{"__ignoreMap":7},[915],{"type":32,"value":910},{"type":27,"tag":49,"props":917,"children":918},{"id":582},[919],{"type":32,"value":582},{"type":27,"tag":28,"props":921,"children":922},{},[923,924,928],{"type":32,"value":589},{"type":27,"tag":43,"props":925,"children":926},{},[927],{"type":32,"value":594},{"type":32,"value":596},{"type":27,"tag":61,"props":930,"children":931},{},[932,937,942,947,952],{"type":27,"tag":65,"props":933,"children":934},{},[935],{"type":32,"value":936},"支持系统偏好",{"type":27,"tag":65,"props":938,"children":939},{},[940],{"type":32,"value":941},"提供手动切换选项",{"type":27,"tag":65,"props":943,"children":944},{},[945],{"type":32,"value":946},"确保足够的对比度",{"type":27,"tag":65,"props":948,"children":949},{},[950],{"type":32,"value":951},"优化图片和图表",{"type":27,"tag":65,"props":953,"children":954},{},[955],{"type":32,"value":956},"防止加载闪烁",{"type":27,"tag":28,"props":958,"children":959},{},[960,961,965],{"type":32,"value":635},{"type":27,"tag":43,"props":962,"children":963},{},[964],{"type":32,"value":640},{"type":32,"value":596},{"type":27,"tag":61,"props":967,"children":968},{},[969,974,979,984,989],{"type":27,"tag":65,"props":970,"children":971},{},[972],{"type":32,"value":973},"强制单一模式",{"type":27,"tag":65,"props":975,"children":976},{},[977],{"type":32,"value":978},"忽视性能影响",{"type":27,"tag":65,"props":980,"children":981},{},[982],{"type":32,"value":983},"使用相同的颜色",{"type":27,"tag":65,"props":985,"children":986},{},[987],{"type":32,"value":988},"忘记保存用户偏好",{"type":27,"tag":65,"props":990,"children":991},{},[992],{"type":32,"value":993},"过度使用深色背景",{"type":27,"tag":49,"props":995,"children":996},{"id":687},[997],{"type":32,"value":687},{"type":27,"tag":61,"props":999,"children":1001},{"className":1000},[693],[1002,1011,1020,1029,1038],{"type":27,"tag":65,"props":1003,"children":1005},{"className":1004},[698],[1006,1009],{"type":27,"tag":701,"props":1007,"children":1008},{"disabled":703,"type":704},[],{"type":32,"value":1010}," 在浅色和深色模式下测试所有页面",{"type":27,"tag":65,"props":1012,"children":1014},{"className":1013},[698],[1015,1018],{"type":27,"tag":701,"props":1016,"children":1017},{"disabled":703,"type":704},[],{"type":32,"value":1019}," 检查颜色对比度符合 WCAG 标准",{"type":27,"tag":65,"props":1021,"children":1023},{"className":1022},[698],[1024,1027],{"type":27,"tag":701,"props":1025,"children":1026},{"disabled":703,"type":704},[],{"type":32,"value":1028}," 验证图片和图表在两种模式下清晰",{"type":27,"tag":65,"props":1030,"children":1032},{"className":1031},[698],[1033,1036],{"type":27,"tag":701,"props":1034,"children":1035},{"disabled":703,"type":704},[],{"type":32,"value":1037}," 测试主题切换的平滑性",{"type":27,"tag":65,"props":1039,"children":1041},{"className":1040},[698],[1042,1045],{"type":27,"tag":701,"props":1043,"children":1044},{"disabled":703,"type":704},[],{"type":32,"value":1046}," 检查用户偏好是否被保存",{"title":7,"searchDepth":388,"depth":388,"links":1048},[1049,1050,1054,1059,1060,1061,1062,1063],{"id":767,"depth":391,"text":767},{"id":789,"depth":391,"text":789,"children":1051},[1052,1053],{"id":794,"depth":388,"text":797},{"id":809,"depth":388,"text":812},{"id":824,"depth":391,"text":824,"children":1055},[1056,1057,1058],{"id":829,"depth":388,"text":832},{"id":844,"depth":388,"text":847},{"id":861,"depth":388,"text":864},{"id":876,"depth":391,"text":876},{"id":890,"depth":391,"text":890},{"id":904,"depth":391,"text":904},{"id":582,"depth":391,"text":582},{"id":687,"depth":391,"text":687},"content:topics:design:dark-mode-design.md","topics/design/dark-mode-design.md","topics/design/dark-mode-design",{"_path":1068,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1069,"description":1070,"keywords":1071,"image":1076,"author":1077,"date":417,"readingTime":775,"topic":5,"body":1078,"_type":399,"_id":1343,"_source":401,"_file":1344,"_stem":1345,"_extension":404},"/topics/design/form-controls-design","表单控件设计规范","学习输入框、选择框、复选框等表单控件的设计和实现",[1072,1073,1074,1075,415],"表单设计","Form Controls","输入框","验证反馈","/images/topics/form-controls-design.jpg","AI Content Team",{"type":24,"children":1079,"toc":1329},[1080,1084,1089,1094,1099,1108,1113,1122,1126,1135,1140,1149,1154,1163,1168,1177,1182,1191,1195,1204,1230,1239,1267,1271],{"type":27,"tag":49,"props":1081,"children":1082},{"id":1069},[1083],{"type":32,"value":1069},{"type":27,"tag":28,"props":1085,"children":1086},{},[1087],{"type":32,"value":1088},"优秀的表单设计能够提高用户完成率和满意度。",{"type":27,"tag":49,"props":1090,"children":1092},{"id":1091},"输入框设计",[1093],{"type":32,"value":1091},{"type":27,"tag":436,"props":1095,"children":1097},{"id":1096},"基础文本输入",[1098],{"type":32,"value":1096},{"type":27,"tag":443,"props":1100,"children":1103},{"className":1101,"code":1102,"language":448,"meta":7},[446],".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",[1104],{"type":27,"tag":451,"props":1105,"children":1106},{"__ignoreMap":7},[1107],{"type":32,"value":1102},{"type":27,"tag":436,"props":1109,"children":1111},{"id":1110},"标签和提示",[1112],{"type":32,"value":1110},{"type":27,"tag":443,"props":1114,"children":1117},{"className":1115,"code":1116,"language":560,"meta":7},[558],"\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",[1118],{"type":27,"tag":451,"props":1119,"children":1120},{"__ignoreMap":7},[1121],{"type":32,"value":1116},{"type":27,"tag":49,"props":1123,"children":1124},{"id":1075},[1125],{"type":32,"value":1075},{"type":27,"tag":443,"props":1127,"children":1130},{"className":1128,"code":1129,"language":500,"meta":7},[498],"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",[1131],{"type":27,"tag":451,"props":1132,"children":1133},{"__ignoreMap":7},[1134],{"type":32,"value":1129},{"type":27,"tag":49,"props":1136,"children":1138},{"id":1137},"选择框设计",[1139],{"type":32,"value":1137},{"type":27,"tag":443,"props":1141,"children":1144},{"className":1142,"code":1143,"language":448,"meta":7},[446],".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",[1145],{"type":27,"tag":451,"props":1146,"children":1147},{"__ignoreMap":7},[1148],{"type":32,"value":1143},{"type":27,"tag":49,"props":1150,"children":1152},{"id":1151},"复选框和单选按钮",[1153],{"type":32,"value":1151},{"type":27,"tag":443,"props":1155,"children":1158},{"className":1156,"code":1157,"language":448,"meta":7},[446],".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",[1159],{"type":27,"tag":451,"props":1160,"children":1161},{"__ignoreMap":7},[1162],{"type":32,"value":1157},{"type":27,"tag":49,"props":1164,"children":1166},{"id":1165},"文本区域",[1167],{"type":32,"value":1165},{"type":27,"tag":443,"props":1169,"children":1172},{"className":1170,"code":1171,"language":448,"meta":7},[446],".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",[1173],{"type":27,"tag":451,"props":1174,"children":1175},{"__ignoreMap":7},[1176],{"type":32,"value":1171},{"type":27,"tag":49,"props":1178,"children":1180},{"id":1179},"完整表单示例",[1181],{"type":32,"value":1179},{"type":27,"tag":443,"props":1183,"children":1186},{"className":1184,"code":1185,"language":500,"meta":7},[498],"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",[1187],{"type":27,"tag":451,"props":1188,"children":1189},{"__ignoreMap":7},[1190],{"type":32,"value":1185},{"type":27,"tag":49,"props":1192,"children":1193},{"id":582},[1194],{"type":32,"value":582},{"type":27,"tag":28,"props":1196,"children":1197},{},[1198,1199,1203],{"type":32,"value":589},{"type":27,"tag":43,"props":1200,"children":1201},{},[1202],{"type":32,"value":594},{"type":32,"value":596},{"type":27,"tag":61,"props":1205,"children":1206},{},[1207,1212,1217,1222,1226],{"type":27,"tag":65,"props":1208,"children":1209},{},[1210],{"type":32,"value":1211},"使用正确的输入类型",{"type":27,"tag":65,"props":1213,"children":1214},{},[1215],{"type":32,"value":1216},"提供实时验证反馈",{"type":27,"tag":65,"props":1218,"children":1219},{},[1220],{"type":32,"value":1221},"清晰的标签和提示",{"type":27,"tag":65,"props":1223,"children":1224},{},[1225],{"type":32,"value":604},{"type":27,"tag":65,"props":1227,"children":1228},{},[1229],{"type":32,"value":630},{"type":27,"tag":28,"props":1231,"children":1232},{},[1233,1234,1238],{"type":32,"value":635},{"type":27,"tag":43,"props":1235,"children":1236},{},[1237],{"type":32,"value":640},{"type":32,"value":596},{"type":27,"tag":61,"props":1240,"children":1241},{},[1242,1247,1252,1257,1262],{"type":27,"tag":65,"props":1243,"children":1244},{},[1245],{"type":32,"value":1246},"隐藏标签",{"type":27,"tag":65,"props":1248,"children":1249},{},[1250],{"type":32,"value":1251},"过度使用占位符",{"type":27,"tag":65,"props":1253,"children":1254},{},[1255],{"type":32,"value":1256},"验证后立即提交",{"type":27,"tag":65,"props":1258,"children":1259},{},[1260],{"type":32,"value":1261},"忽视无障碍性",{"type":27,"tag":65,"props":1263,"children":1264},{},[1265],{"type":32,"value":1266},"复杂的验证规则",{"type":27,"tag":49,"props":1268,"children":1269},{"id":687},[1270],{"type":32,"value":687},{"type":27,"tag":61,"props":1272,"children":1274},{"className":1273},[693],[1275,1284,1293,1302,1311,1320],{"type":27,"tag":65,"props":1276,"children":1278},{"className":1277},[698],[1279,1282],{"type":27,"tag":701,"props":1280,"children":1281},{"disabled":703,"type":704},[],{"type":32,"value":1283}," 所有控件都可用键盘导航",{"type":27,"tag":65,"props":1285,"children":1287},{"className":1286},[698],[1288,1291],{"type":27,"tag":701,"props":1289,"children":1290},{"disabled":703,"type":704},[],{"type":32,"value":1292}," 标签与输入框关联",{"type":27,"tag":65,"props":1294,"children":1296},{"className":1295},[698],[1297,1300],{"type":27,"tag":701,"props":1298,"children":1299},{"disabled":703,"type":704},[],{"type":32,"value":1301}," 验证消息清晰",{"type":27,"tag":65,"props":1303,"children":1305},{"className":1304},[698],[1306,1309],{"type":27,"tag":701,"props":1307,"children":1308},{"disabled":703,"type":704},[],{"type":32,"value":1310}," 色彩对比度足够",{"type":27,"tag":65,"props":1312,"children":1314},{"className":1313},[698],[1315,1318],{"type":27,"tag":701,"props":1316,"children":1317},{"disabled":703,"type":704},[],{"type":32,"value":1319}," 屏幕阅读器兼容",{"type":27,"tag":65,"props":1321,"children":1323},{"className":1322},[698],[1324,1327],{"type":27,"tag":701,"props":1325,"children":1326},{"disabled":703,"type":704},[],{"type":32,"value":1328}," 移动设备测试",{"title":7,"searchDepth":388,"depth":388,"links":1330},[1331,1332,1336,1337,1338,1339,1340,1341,1342],{"id":1069,"depth":391,"text":1069},{"id":1091,"depth":391,"text":1091,"children":1333},[1334,1335],{"id":1096,"depth":388,"text":1096},{"id":1110,"depth":388,"text":1110},{"id":1075,"depth":391,"text":1075},{"id":1137,"depth":391,"text":1137},{"id":1151,"depth":391,"text":1151},{"id":1165,"depth":391,"text":1165},{"id":1179,"depth":391,"text":1179},{"id":582,"depth":391,"text":582},{"id":687,"depth":391,"text":687},"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":1347,"image":18,"imageQuery":19,"pexelsPhotoId":20,"pexelsUrl":21,"featured":6,"readingTime":22,"body":1348,"_type":399,"_id":400,"_source":401,"_file":402,"_stem":403,"_extension":404},[13,14,15,16,17],{"type":24,"children":1349,"toc":1633},[1350,1354,1358,1365,1369,1373,1392,1396,1415,1419,1423,1427,1431,1446,1450,1454,1458,1477,1481,1485,1489,1493,1508,1512,1516,1520,1539,1543,1547,1551,1570,1574,1597,1601,1605,1609],{"type":27,"tag":28,"props":1351,"children":1352},{},[1353],{"type":32,"value":33},{"type":27,"tag":28,"props":1355,"children":1356},{},[1357],{"type":32,"value":38},{"type":27,"tag":28,"props":1359,"children":1360},{},[1361],{"type":27,"tag":43,"props":1362,"children":1363},{},[1364],{"type":32,"value":47},{"type":27,"tag":49,"props":1366,"children":1367},{"id":51},[1368],{"type":32,"value":54},{"type":27,"tag":28,"props":1370,"children":1371},{},[1372],{"type":32,"value":59},{"type":27,"tag":61,"props":1374,"children":1375},{},[1376,1380,1384,1388],{"type":27,"tag":65,"props":1377,"children":1378},{},[1379],{"type":32,"value":69},{"type":27,"tag":65,"props":1381,"children":1382},{},[1383],{"type":32,"value":74},{"type":27,"tag":65,"props":1385,"children":1386},{},[1387],{"type":32,"value":79},{"type":27,"tag":65,"props":1389,"children":1390},{},[1391],{"type":32,"value":84},{"type":27,"tag":28,"props":1393,"children":1394},{},[1395],{"type":32,"value":89},{"type":27,"tag":61,"props":1397,"children":1398},{},[1399,1403,1407,1411],{"type":27,"tag":65,"props":1400,"children":1401},{},[1402],{"type":32,"value":97},{"type":27,"tag":65,"props":1404,"children":1405},{},[1406],{"type":32,"value":102},{"type":27,"tag":65,"props":1408,"children":1409},{},[1410],{"type":32,"value":107},{"type":27,"tag":65,"props":1412,"children":1413},{},[1414],{"type":32,"value":112},{"type":27,"tag":28,"props":1416,"children":1417},{},[1418],{"type":32,"value":117},{"type":27,"tag":49,"props":1420,"children":1421},{"id":120},[1422],{"type":32,"value":120},{"type":27,"tag":28,"props":1424,"children":1425},{},[1426],{"type":32,"value":127},{"type":27,"tag":28,"props":1428,"children":1429},{},[1430],{"type":32,"value":132},{"type":27,"tag":61,"props":1432,"children":1433},{},[1434,1438,1442],{"type":27,"tag":65,"props":1435,"children":1436},{},[1437],{"type":32,"value":140},{"type":27,"tag":65,"props":1439,"children":1440},{},[1441],{"type":32,"value":145},{"type":27,"tag":65,"props":1443,"children":1444},{},[1445],{"type":32,"value":150},{"type":27,"tag":28,"props":1447,"children":1448},{},[1449],{"type":32,"value":155},{"type":27,"tag":49,"props":1451,"children":1452},{"id":158},[1453],{"type":32,"value":161},{"type":27,"tag":28,"props":1455,"children":1456},{},[1457],{"type":32,"value":166},{"type":27,"tag":61,"props":1459,"children":1460},{},[1461,1465,1469,1473],{"type":27,"tag":65,"props":1462,"children":1463},{},[1464],{"type":32,"value":174},{"type":27,"tag":65,"props":1466,"children":1467},{},[1468],{"type":32,"value":179},{"type":27,"tag":65,"props":1470,"children":1471},{},[1472],{"type":32,"value":184},{"type":27,"tag":65,"props":1474,"children":1475},{},[1476],{"type":32,"value":189},{"type":27,"tag":28,"props":1478,"children":1479},{},[1480],{"type":32,"value":194},{"type":27,"tag":49,"props":1482,"children":1483},{"id":197},[1484],{"type":32,"value":200},{"type":27,"tag":28,"props":1486,"children":1487},{},[1488],{"type":32,"value":205},{"type":27,"tag":28,"props":1490,"children":1491},{},[1492],{"type":32,"value":210},{"type":27,"tag":61,"props":1494,"children":1495},{},[1496,1500,1504],{"type":27,"tag":65,"props":1497,"children":1498},{},[1499],{"type":32,"value":218},{"type":27,"tag":65,"props":1501,"children":1502},{},[1503],{"type":32,"value":223},{"type":27,"tag":65,"props":1505,"children":1506},{},[1507],{"type":32,"value":228},{"type":27,"tag":28,"props":1509,"children":1510},{},[1511],{"type":32,"value":233},{"type":27,"tag":49,"props":1513,"children":1514},{"id":236},[1515],{"type":32,"value":239},{"type":27,"tag":28,"props":1517,"children":1518},{},[1519],{"type":32,"value":244},{"type":27,"tag":61,"props":1521,"children":1522},{},[1523,1527,1531,1535],{"type":27,"tag":65,"props":1524,"children":1525},{},[1526],{"type":32,"value":252},{"type":27,"tag":65,"props":1528,"children":1529},{},[1530],{"type":32,"value":257},{"type":27,"tag":65,"props":1532,"children":1533},{},[1534],{"type":32,"value":262},{"type":27,"tag":65,"props":1536,"children":1537},{},[1538],{"type":32,"value":267},{"type":27,"tag":28,"props":1540,"children":1541},{},[1542],{"type":32,"value":272},{"type":27,"tag":49,"props":1544,"children":1545},{"id":275},[1546],{"type":32,"value":278},{"type":27,"tag":28,"props":1548,"children":1549},{},[1550],{"type":32,"value":283},{"type":27,"tag":285,"props":1552,"children":1553},{},[1554,1558,1562,1566],{"type":27,"tag":65,"props":1555,"children":1556},{},[1557],{"type":32,"value":292},{"type":27,"tag":65,"props":1559,"children":1560},{},[1561],{"type":32,"value":297},{"type":27,"tag":65,"props":1563,"children":1564},{},[1565],{"type":32,"value":302},{"type":27,"tag":65,"props":1567,"children":1568},{},[1569],{"type":32,"value":307},{"type":27,"tag":49,"props":1571,"children":1572},{"id":310},[1573],{"type":32,"value":310},{"type":27,"tag":61,"props":1575,"children":1576},{},[1577,1581,1585,1589,1593],{"type":27,"tag":65,"props":1578,"children":1579},{},[1580],{"type":32,"value":320},{"type":27,"tag":65,"props":1582,"children":1583},{},[1584],{"type":32,"value":325},{"type":27,"tag":65,"props":1586,"children":1587},{},[1588],{"type":32,"value":330},{"type":27,"tag":65,"props":1590,"children":1591},{},[1592],{"type":32,"value":335},{"type":27,"tag":65,"props":1594,"children":1595},{},[1596],{"type":32,"value":340},{"type":27,"tag":49,"props":1598,"children":1599},{"id":343},[1600],{"type":32,"value":343},{"type":27,"tag":28,"props":1602,"children":1603},{},[1604],{"type":32,"value":350},{"type":27,"tag":28,"props":1606,"children":1607},{},[1608],{"type":32,"value":355},{"type":27,"tag":61,"props":1610,"children":1611},{},[1612,1619,1626],{"type":27,"tag":65,"props":1613,"children":1614},{},[1615],{"type":27,"tag":363,"props":1616,"children":1617},{"href":365},[1618],{"type":32,"value":368},{"type":27,"tag":65,"props":1620,"children":1621},{},[1622],{"type":27,"tag":363,"props":1623,"children":1624},{"href":374},[1625],{"type":32,"value":377},{"type":27,"tag":65,"props":1627,"children":1628},{},[1629],{"type":27,"tag":363,"props":1630,"children":1631},{"href":383},[1632],{"type":32,"value":386},{"title":7,"searchDepth":388,"depth":388,"links":1634},[1635,1636,1637,1638,1639,1640,1641,1642],{"id":51,"depth":391,"text":54},{"id":120,"depth":391,"text":120},{"id":158,"depth":391,"text":161},{"id":197,"depth":391,"text":200},{"id":236,"depth":391,"text":239},{"id":275,"depth":391,"text":278},{"id":310,"depth":391,"text":310},{"id":343,"depth":391,"text":343},1777707271343]