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