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