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