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