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