[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6GSY0G9SCXGSNR427H788XJ":3,"skill-same:01M6GSY0G9SCXGSNR427H788XJ":76},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":42,"linkOnly":43,"markdownContent":44,"markdownBody":45,"frontMatter":46,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":14,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":55,"install":56,"resources":69},"01M6GSY0G9SCXGSNR427H788XJ","web-best-practices",null,"Apply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".","japurcell\u002Fskills","japurcell","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F10947458?v=4","01M6GSJK1YAG95R6VHF47V30KY","Adam Purcell","01M6GSJK6817NVR5FVDMFFNJ6H",0,"MIT","https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdefault\u002Fweb-best-practices\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"web","best practices","web best practices","quality","code quality review","modern web development","when","apply","for","modernize","quality review","compatibility","use","check","review","web development","security audit","audit","code","development","code quality","vulnerabilities","security","2026-11-10 10:12:30",false,"---\nname: web-best-practices\ndescription: Apply modern web development best practices for security, compatibility, and code quality. Use when asked to \"apply best practices\", \"security audit\", \"modernize code\", \"code quality review\", or \"check for vulnerabilities\".\nlicense: MIT\nmetadata:\n  author: web-quality-skills\n  version: \"2.0\"\n---\n\n# Best practices\n\nModern web development standards based on Lighthouse best practices audits. Covers security, browser compatibility, and code quality patterns.\n\n## Evidence-led audit workflow\n\nWhen a rendered page is available:\n\n1. Run a live Lighthouse Best Practices audit when that capability is available; with Chrome DevTools MCP, use `lighthouse_audit`. Use navigation mode for a normal page load or snapshot mode when the current state must be preserved.\n2. Inspect the listed console and network failures and fetch individual details only when they support a finding.\n3. Supplement runtime evidence with dependency, header, configuration, and source inspection; Lighthouse is not a complete security assessment.\n4. Fix the implicated code, re-run the same audit, and keep security findings separate from style preferences.\n\nIf live tools are unavailable, use the Lighthouse CLI plus focused dependency and header checks. Never report a high Lighthouse score as proof that the application is secure.\n\n## Security\n\nRead [the security reference](references\u002FSECURITY.md) when security is in scope or a live audit surfaces a related failure. It covers HTTPS\u002FHSTS, CSP and Trusted Types, Subresource Integrity, headers, dependencies, sanitization, and cookies.\n\nAt minimum:\n\n- **Use HTTPS without mixed content.** Add HSTS only after confirming every relevant subdomain supports HTTPS.\n- **Treat a strict CSP as defense in depth.** Prefer nonces or hashes and test with report-only before enforcement.\n- **Sanitize untrusted HTML and protect DOM XSS sinks.** Prefer text APIs when markup is not required.\n- **Pin and review third-party code.** Use SRI where the delivery model supports it and keep dependencies patched.\n- **Verify response headers at runtime.** Source configuration alone does not prove what the deployed page sends.\n\n## Browser compatibility\n\n### Doctype declaration\n\n```html\n\u003C!-- ❌ Missing or invalid doctype -->\n\u003Chtml>\n  \u003C!DOCTYPE html PUBLIC \"-\u002F\u002FW3C\u002F\u002FDTD HTML 4.01\u002F\u002FEN\">\n\n  \u003C!-- ✅ HTML5 doctype -->\n  \u003C!DOCTYPE html>\n  \u003Chtml lang=\"en\">\u003C\u002Fhtml>\n\u003C\u002Fhtml>\n```\n\n### Character encoding\n\n```html\n\u003C!-- ❌ Missing or late charset -->\n\u003Chtml>\n  \u003Chead>\n    \u003Ctitle>Page\u003C\u002Ftitle>\n    \u003Cmeta charset=\"UTF-8\" \u002F>\n  \u003C\u002Fhead>\n\n  \u003C!-- ✅ Charset as first element in head -->\n  \u003Chtml>\n    \u003Chead>\n      \u003Cmeta charset=\"UTF-8\" \u002F>\n      \u003Ctitle>Page\u003C\u002Ftitle>\n    \u003C\u002Fhead>\n  \u003C\u002Fhtml>\n\u003C\u002Fhtml>\n```\n\n### Viewport meta tag\n\n```html\n\u003C!-- ❌ Missing viewport -->\n\u003Chead>\n  \u003Ctitle>Page\u003C\u002Ftitle>\n\u003C\u002Fhead>\n\n\u003C!-- ✅ Responsive viewport -->\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\" \u002F>\n  \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \u002F>\n  \u003Ctitle>Page\u003C\u002Ftitle>\n\u003C\u002Fhead>\n```\n\n### Feature detection\n\n```javascript\n\u002F\u002F ❌ Browser detection (brittle)\nif (navigator.userAgent.includes('Chrome')) {\n  \u002F\u002F Chrome-specific code\n}\n\n\u002F\u002F ✅ Feature detection\nif ('IntersectionObserver' in window) {\n  \u002F\u002F Use IntersectionObserver\n} else {\n  \u002F\u002F Fallback\n}\n\n\u002F\u002F ✅ Using @supports in CSS\n@supports (display: grid) {\n  .container {\n    display: grid;\n  }\n}\n\n@supports not (display: grid) {\n  .container {\n    display: flex;\n  }\n}\n```\n\n### Polyfills (when needed)\n\nPrefer **bundling polyfills at build time** (Babel\u002FSWC + `core-js`, or `@vitejs\u002Fplugin-legacy`) targeted by your supported-browsers list. This eliminates the runtime check entirely and avoids shipping polyfill bytes to modern browsers.\n\nIf you must load a polyfill at runtime, append a script element — never use `document.write` (it blocks the parser and is broken in async\u002Fdeferred contexts):\n\n```html\n\u003Cscript>\n  if (!(\"fetch\" in window)) {\n    const s = document.createElement(\"script\");\n    s.src = \"\u002Fpolyfills\u002Ffetch.js\";\n    s.defer = true;\n    document.head.appendChild(s);\n  }\n\u003C\u002Fscript>\n```\n\n**Never load polyfills from a third-party CDN you don't control.** The `polyfill.io` service was [compromised in mid-2024](https:\u002F\u002Fsansec.io\u002Fresearch\u002Fpolyfill-supply-chain-attack) in a supply-chain attack and used to serve malware to ~100k sites. Self-host, or use a vetted mirror (e.g. [Cloudflare's `cdnjs` polyfill build](https:\u002F\u002Fblog.cloudflare.com\u002Fpolyfill-io-now-available-on-cdnjs-reduce-your-supply-chain-risk\u002F)) — and pin the version with [Subresource Integrity](#subresource-integrity-sri-for-third-party-scripts).\n\n---\n\n## Deprecated APIs\n\n### Avoid these\n\n```javascript\n\u002F\u002F ❌ document.write (blocks parsing)\ndocument.write('\u003Cscript src=\"...\">\u003C\u002Fscript>');\n\n\u002F\u002F ✅ Dynamic script loading\nconst script = document.createElement('script');\nscript.src = '...';\ndocument.head.appendChild(script);\n\n\u002F\u002F ❌ Synchronous XHR (blocks main thread)\nconst xhr = new XMLHttpRequest();\nxhr.open('GET', url, false); \u002F\u002F false = synchronous\n\n\u002F\u002F ✅ Async fetch\nconst response = await fetch(url);\n\n\u002F\u002F ❌ Application Cache (deprecated)\n\u003Chtml manifest=\"cache.manifest\">\n\n\u002F\u002F ✅ Service Workers\nif ('serviceWorker' in navigator) {\n  navigator.serviceWorker.register('\u002Fsw.js');\n}\n```\n\n### Event listener passive\n\n```javascript\n\u002F\u002F ❌ Non-passive touch\u002Fwheel (may block scrolling)\nelement.addEventListener(\"touchstart\", handler);\nelement.addEventListener(\"wheel\", handler);\n\n\u002F\u002F ✅ Passive listeners (allows smooth scrolling)\nelement.addEventListener(\"touchstart\", handler, { passive: true });\nelement.addEventListener(\"wheel\", handler, { passive: true });\n\n\u002F\u002F ✅ If you need preventDefault, be explicit\nelement.addEventListener(\"touchstart\", handler, { passive: false });\n```\n\n---\n\n## Console & errors\n\n### No console errors\n\n```javascript\n\u002F\u002F ❌ Errors in production\nconsole.log(\"Debug info\"); \u002F\u002F Remove in production\nthrow new Error(\"Unhandled\"); \u002F\u002F Catch all errors\n\n\u002F\u002F ✅ Proper error handling\ntry {\n  riskyOperation();\n} catch (error) {\n  \u002F\u002F Log to error tracking service\n  errorTracker.captureException(error);\n  \u002F\u002F Show user-friendly message\n  showErrorMessage(\"Something went wrong. Please try again.\");\n}\n```\n\n### Error boundaries (React)\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n\n  static getDerivedStateFromError(error) {\n    return { hasError: true };\n  }\n\n  componentDidCatch(error, info) {\n    errorTracker.captureException(error, { extra: info });\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return \u003CFallbackUI \u002F>;\n    }\n    return this.props.children;\n  }\n}\n\n\u002F\u002F Usage\n\u003CErrorBoundary>\n  \u003CApp \u002F>\n\u003C\u002FErrorBoundary>;\n```\n\n### Global error handler\n\n```javascript\n\u002F\u002F Catch unhandled errors\nwindow.addEventListener(\"error\", (event) => {\n  errorTracker.captureException(event.error);\n});\n\n\u002F\u002F Catch unhandled promise rejections\nwindow.addEventListener(\"unhandledrejection\", (event) => {\n  errorTracker.captureException(event.reason);\n});\n```\n\n---\n\n## Source maps\n\n### Production configuration\n\n```javascript\n\u002F\u002F ❌ Source maps exposed in production\n\u002F\u002F webpack.config.js\nmodule.exports = {\n  devtool: \"source-map\", \u002F\u002F Exposes source code\n};\n\n\u002F\u002F ✅ Hidden source maps (uploaded to error tracker)\nmodule.exports = {\n  devtool: \"hidden-source-map\",\n};\n\n\u002F\u002F ✅ Or no source maps in production\nmodule.exports = {\n  devtool: process.env.NODE_ENV === \"production\" ? false : \"source-map\",\n};\n```\n\n**Strip `sourcesContent` from production maps** when uploading to your error tracker. By default, bundlers embed the full original source inside the `.map` file — anyone who obtains the map (including via a misconfigured upload step) gets your unminified code. Configure your bundler to omit `sourcesContent`, or use a Sentry\u002FBugsnag CLI flag that does so when uploading.\n\nFor Vite, prefer `sourcemap: 'hidden'` over `'true'` so the `\u002F\u002F# sourceMappingURL=` comment isn't emitted into the bundle.\n\n---\n\n## Performance best practices\n\n### Avoid blocking patterns\n\n```javascript\n\u002F\u002F ❌ Blocking script\n\u003Cscript src=\"heavy-library.js\">\u003C\u002Fscript>\n\n\u002F\u002F ✅ Deferred script\n\u003Cscript defer src=\"heavy-library.js\">\u003C\u002Fscript>\n\n\u002F\u002F ❌ Blocking CSS import\n@import url('other-styles.css');\n\n\u002F\u002F ✅ Link tags (parallel loading)\n\u003Clink rel=\"stylesheet\" href=\"styles.css\">\n\u003Clink rel=\"stylesheet\" href=\"other-styles.css\">\n```\n\n### Efficient event handlers\n\n```javascript\n\u002F\u002F ❌ Handler on every element\nitems.forEach((item) => {\n  item.addEventListener(\"click\", handleClick);\n});\n\n\u002F\u002F ✅ Event delegation\ncontainer.addEventListener(\"click\", (e) => {\n  if (e.target.matches(\".item\")) {\n    handleClick(e);\n  }\n});\n```\n\n### Memory management\n\n```javascript\n\u002F\u002F ❌ Memory leak (never removed)\nconst handler = () => {\n  \u002F* ... *\u002F\n};\nwindow.addEventListener(\"resize\", handler);\n\n\u002F\u002F ✅ Cleanup when done\nconst handler = () => {\n  \u002F* ... *\u002F\n};\nwindow.addEventListener(\"resize\", handler);\n\n\u002F\u002F Later, when component unmounts:\nwindow.removeEventListener(\"resize\", handler);\n\n\u002F\u002F ✅ Using AbortController\nconst controller = new AbortController();\nwindow.addEventListener(\"resize\", handler, { signal: controller.signal });\n\n\u002F\u002F Cleanup:\ncontroller.abort();\n```\n\n---\n\n## Code quality\n\n### Valid HTML\n\n```html\n\u003C!-- ❌ Invalid HTML -->\n\u003Cdiv id=\"header\">\n  \u003Cdiv id=\"header\">\n    \u003C!-- Duplicate ID -->\n\n    \u003Cul>\n      \u003Cdiv>Item\u003C\u002Fdiv>\n      \u003C!-- Invalid child -->\n    \u003C\u002Ful>\n\n    \u003Ca href=\"\u002F\">\u003Cbutton>Click\u003C\u002Fbutton>\u003C\u002Fa>\n    \u003C!-- Invalid nesting -->\n\n    \u003C!-- ✅ Valid HTML -->\n    \u003Cheader id=\"site-header\">\u003C\u002Fheader>\n\n    \u003Cul>\n      \u003Cli>Item\u003C\u002Fli>\n    \u003C\u002Ful>\n\n    \u003Ca href=\"\u002F\" class=\"button\">Click\u003C\u002Fa>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n```\n\n### Semantic HTML\n\n```html\n\u003C!-- ❌ Non-semantic -->\n\u003Cdiv class=\"header\">\n  \u003Cdiv class=\"nav\">\n    \u003Cdiv class=\"nav-item\">Home\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n\u003Cdiv class=\"main\">\n  \u003Cdiv class=\"article\">\n    \u003Cdiv class=\"title\">Headline\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n\n\u003C!-- ✅ Semantic HTML5 -->\n\u003Cheader>\n  \u003Cnav>\n    \u003Ca href=\"\u002F\">Home\u003C\u002Fa>\n  \u003C\u002Fnav>\n\u003C\u002Fheader>\n\u003Cmain>\n  \u003Carticle>\n    \u003Ch1>Headline\u003C\u002Fh1>\n  \u003C\u002Farticle>\n\u003C\u002Fmain>\n```\n\n### Image aspect ratios\n\n```html\n\u003C!-- ❌ Distorted images -->\n\u003Cimg src=\"photo.jpg\" width=\"300\" height=\"100\" \u002F>\n\u003C!-- If actual ratio is 4:3, this squishes the image -->\n\n\u003C!-- ✅ Preserve aspect ratio -->\n\u003Cimg src=\"photo.jpg\" width=\"300\" height=\"225\" \u002F>\n\u003C!-- Actual 4:3 dimensions -->\n\n\u003C!-- ✅ CSS object-fit for flexibility -->\n\u003Cimg src=\"photo.jpg\" style=\"width: 300px; height: 200px; object-fit: cover;\" \u002F>\n```\n\n---\n\n## Permissions & privacy\n\n### Request permissions properly\n\n```javascript\n\u002F\u002F ❌ Request on page load (bad UX, often denied)\nnavigator.geolocation.getCurrentPosition(success, error);\n\n\u002F\u002F ✅ Request in context, after user action\nfindNearbyButton.addEventListener(\"click\", async () => {\n  \u002F\u002F Explain why you need it\n  if (await showPermissionExplanation()) {\n    navigator.geolocation.getCurrentPosition(success, error);\n  }\n});\n```\n\n### Permissions policy\n\n```html\n\u003C!-- Restrict powerful features -->\n\u003Cmeta\n  http-equiv=\"Permissions-Policy\"\n  content=\"geolocation=(), camera=(), microphone=()\"\n\u002F>\n\n\u003C!-- Or allow for specific origins -->\n\u003Cmeta\n  http-equiv=\"Permissions-Policy\"\n  content=\"geolocation=(self 'https:\u002F\u002Fmaps.example.com')\"\n\u002F>\n```\n\n---\n\n## Audit checklist\n\n### Security (critical)\n\n- [ ] HTTPS enabled, no mixed content\n- [ ] No vulnerable dependencies (`npm audit`)\n- [ ] CSP headers configured (with `frame-ancestors`, `base-uri`, `form-action`)\n- [ ] `require-trusted-types-for 'script'` enforced (or report-only during rollout)\n- [ ] Third-party `\u003Cscript>`\u002F`\u003Clink rel=\"stylesheet\">` pinned with SRI hashes\n- [ ] Security headers present (HSTS, X-Content-Type-Options, Referrer-Policy)\n- [ ] No exposed source maps (and `sourcesContent` stripped from uploaded ones)\n\n### Compatibility\n\n- [ ] Valid HTML5 doctype\n- [ ] Charset declared first in head\n- [ ] Viewport meta tag present\n- [ ] No deprecated APIs used\n- [ ] Passive event listeners for scroll\u002Ftouch\n\n### Code quality\n\n- [ ] No console errors\n- [ ] Valid HTML (no duplicate IDs)\n- [ ] Semantic HTML elements used\n- [ ] Proper error handling\n- [ ] Memory cleanup in components\n\n### UX\n\n- [ ] No intrusive interstitials\n- [ ] Permission requests in context\n- [ ] Clear error messages\n- [ ] Appropriate image aspect ratios\n\n## Tools\n\n| Tool                                                            | Purpose                                   |\n| --------------------------------------------------------------- | ----------------------------------------- |\n| `npm audit`                                                     | Dependency vulnerabilities                |\n| [SecurityHeaders.com](https:\u002F\u002Fsecurityheaders.com)              | Header analysis                           |\n| [W3C Validator](https:\u002F\u002Fvalidator.w3.org)                       | HTML validation                           |\n| Live Lighthouse audit (Chrome DevTools MCP: `lighthouse_audit`) | Rendered Best Practices checks for agents |\n| Lighthouse CLI                                                  | Best Practices audit fallback             |\n| [Observatory](https:\u002F\u002Fobservatory.mozilla.org)                  | Security scan                             |\n\n## References\n\n- [MDN Web Security](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FSecurity)\n- [OWASP Top 10](https:\u002F\u002Fowasp.org\u002Fwww-project-top-ten\u002F)\n- [Web Quality Audit](..\u002Fweb-quality-audit\u002FSKILL.md)\n","# Best practices\n\nModern web development standards based on Lighthouse best practices audits. Covers security, browser compatibility, and code quality patterns.\n\n## Evidence-led audit workflow\n\nWhen a rendered page is available:\n\n1. Run a live Lighthouse Best Practices audit when that capability is available; with Chrome DevTools MCP, use `lighthouse_audit`. Use navigation mode for a normal page load or snapshot mode when the current state must be preserved.\n2. Inspect the listed console and network failures and fetch individual details only when they support a finding.\n3. Supplement runtime evidence with dependency, header, configuration, and source inspection; Lighthouse is not a complete security assessment.\n4. Fix the implicated code, re-run the same audit, and keep security findings separate from style preferences.\n\nIf live tools are unavailable, use the Lighthouse CLI plus focused dependency and header checks. Never report a high Lighthouse score as proof that the application is secure.\n\n## Security\n\nRead [the security reference](references\u002FSECURITY.md) when security is in scope or a live audit surfaces a related failure. It covers HTTPS\u002FHSTS, CSP and Trusted Types, Subresource Integrity, headers, dependencies, sanitization, and cookies.\n\nAt minimum:\n\n- **Use HTTPS without mixed content.** Add HSTS only after confirming every relevant subdomain supports HTTPS.\n- **Treat a strict CSP as defense in depth.** Prefer nonces or hashes and test with report-only before enforcement.\n- **Sanitize untrusted HTML and protect DOM XSS sinks.** Prefer text APIs when markup is not required.\n- **Pin and review third-party code.** Use SRI where the delivery model supports it and keep dependencies patched.\n- **Verify response headers at runtime.** Source configuration alone does not prove what the deployed page sends.\n\n## Browser compatibility\n\n### Doctype declaration\n\n```html\n\u003C!-- ❌ Missing or invalid doctype -->\n\u003Chtml>\n  \u003C!DOCTYPE html PUBLIC \"-\u002F\u002FW3C\u002F\u002FDTD HTML 4.01\u002F\u002FEN\">\n\n  \u003C!-- ✅ HTML5 doctype -->\n  \u003C!DOCTYPE html>\n  \u003Chtml lang=\"en\">\u003C\u002Fhtml>\n\u003C\u002Fhtml>\n```\n\n### Character encoding\n\n```html\n\u003C!-- ❌ Missing or late charset -->\n\u003Chtml>\n  \u003Chead>\n    \u003Ctitle>Page\u003C\u002Ftitle>\n    \u003Cmeta charset=\"UTF-8\" \u002F>\n  \u003C\u002Fhead>\n\n  \u003C!-- ✅ Charset as first element in head -->\n  \u003Chtml>\n    \u003Chead>\n      \u003Cmeta charset=\"UTF-8\" \u002F>\n      \u003Ctitle>Page\u003C\u002Ftitle>\n    \u003C\u002Fhead>\n  \u003C\u002Fhtml>\n\u003C\u002Fhtml>\n```\n\n### Viewport meta tag\n\n```html\n\u003C!-- ❌ Missing viewport -->\n\u003Chead>\n  \u003Ctitle>Page\u003C\u002Ftitle>\n\u003C\u002Fhead>\n\n\u003C!-- ✅ Responsive viewport -->\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\" \u002F>\n  \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \u002F>\n  \u003Ctitle>Page\u003C\u002Ftitle>\n\u003C\u002Fhead>\n```\n\n### Feature detection\n\n```javascript\n\u002F\u002F ❌ Browser detection (brittle)\nif (navigator.userAgent.includes('Chrome')) {\n  \u002F\u002F Chrome-specific code\n}\n\n\u002F\u002F ✅ Feature detection\nif ('IntersectionObserver' in window) {\n  \u002F\u002F Use IntersectionObserver\n} else {\n  \u002F\u002F Fallback\n}\n\n\u002F\u002F ✅ Using @supports in CSS\n@supports (display: grid) {\n  .container {\n    display: grid;\n  }\n}\n\n@supports not (display: grid) {\n  .container {\n    display: flex;\n  }\n}\n```\n\n### Polyfills (when needed)\n\nPrefer **bundling polyfills at build time** (Babel\u002FSWC + `core-js`, or `@vitejs\u002Fplugin-legacy`) targeted by your supported-browsers list. This eliminates the runtime check entirely and avoids shipping polyfill bytes to modern browsers.\n\nIf you must load a polyfill at runtime, append a script element — never use `document.write` (it blocks the parser and is broken in async\u002Fdeferred contexts):\n\n```html\n\u003Cscript>\n  if (!(\"fetch\" in window)) {\n    const s = document.createElement(\"script\");\n    s.src = \"\u002Fpolyfills\u002Ffetch.js\";\n    s.defer = true;\n    document.head.appendChild(s);\n  }\n\u003C\u002Fscript>\n```\n\n**Never load polyfills from a third-party CDN you don't control.** The `polyfill.io` service was [compromised in mid-2024](https:\u002F\u002Fsansec.io\u002Fresearch\u002Fpolyfill-supply-chain-attack) in a supply-chain attack and used to serve malware to ~100k sites. Self-host, or use a vetted mirror (e.g. [Cloudflare's `cdnjs` polyfill build](https:\u002F\u002Fblog.cloudflare.com\u002Fpolyfill-io-now-available-on-cdnjs-reduce-your-supply-chain-risk\u002F)) — and pin the version with [Subresource Integrity](#subresource-integrity-sri-for-third-party-scripts).\n\n---\n\n## Deprecated APIs\n\n### Avoid these\n\n```javascript\n\u002F\u002F ❌ document.write (blocks parsing)\ndocument.write('\u003Cscript src=\"...\">\u003C\u002Fscript>');\n\n\u002F\u002F ✅ Dynamic script loading\nconst script = document.createElement('script');\nscript.src = '...';\ndocument.head.appendChild(script);\n\n\u002F\u002F ❌ Synchronous XHR (blocks main thread)\nconst xhr = new XMLHttpRequest();\nxhr.open('GET', url, false); \u002F\u002F false = synchronous\n\n\u002F\u002F ✅ Async fetch\nconst response = await fetch(url);\n\n\u002F\u002F ❌ Application Cache (deprecated)\n\u003Chtml manifest=\"cache.manifest\">\n\n\u002F\u002F ✅ Service Workers\nif ('serviceWorker' in navigator) {\n  navigator.serviceWorker.register('\u002Fsw.js');\n}\n```\n\n### Event listener passive\n\n```javascript\n\u002F\u002F ❌ Non-passive touch\u002Fwheel (may block scrolling)\nelement.addEventListener(\"touchstart\", handler);\nelement.addEventListener(\"wheel\", handler);\n\n\u002F\u002F ✅ Passive listeners (allows smooth scrolling)\nelement.addEventListener(\"touchstart\", handler, { passive: true });\nelement.addEventListener(\"wheel\", handler, { passive: true });\n\n\u002F\u002F ✅ If you need preventDefault, be explicit\nelement.addEventListener(\"touchstart\", handler, { passive: false });\n```\n\n---\n\n## Console & errors\n\n### No console errors\n\n```javascript\n\u002F\u002F ❌ Errors in production\nconsole.log(\"Debug info\"); \u002F\u002F Remove in production\nthrow new Error(\"Unhandled\"); \u002F\u002F Catch all errors\n\n\u002F\u002F ✅ Proper error handling\ntry {\n  riskyOperation();\n} catch (error) {\n  \u002F\u002F Log to error tracking service\n  errorTracker.captureException(error);\n  \u002F\u002F Show user-friendly message\n  showErrorMessage(\"Something went wrong. Please try again.\");\n}\n```\n\n### Error boundaries (React)\n\n```jsx\nclass ErrorBoundary extends React.Component {\n  state = { hasError: false };\n\n  static getDerivedStateFromError(error) {\n    return { hasError: true };\n  }\n\n  componentDidCatch(error, info) {\n    errorTracker.captureException(error, { extra: info });\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return \u003CFallbackUI \u002F>;\n    }\n    return this.props.children;\n  }\n}\n\n\u002F\u002F Usage\n\u003CErrorBoundary>\n  \u003CApp \u002F>\n\u003C\u002FErrorBoundary>;\n```\n\n### Global error handler\n\n```javascript\n\u002F\u002F Catch unhandled errors\nwindow.addEventListener(\"error\", (event) => {\n  errorTracker.captureException(event.error);\n});\n\n\u002F\u002F Catch unhandled promise rejections\nwindow.addEventListener(\"unhandledrejection\", (event) => {\n  errorTracker.captureException(event.reason);\n});\n```\n\n---\n\n## Source maps\n\n### Production configuration\n\n```javascript\n\u002F\u002F ❌ Source maps exposed in production\n\u002F\u002F webpack.config.js\nmodule.exports = {\n  devtool: \"source-map\", \u002F\u002F Exposes source code\n};\n\n\u002F\u002F ✅ Hidden source maps (uploaded to error tracker)\nmodule.exports = {\n  devtool: \"hidden-source-map\",\n};\n\n\u002F\u002F ✅ Or no source maps in production\nmodule.exports = {\n  devtool: process.env.NODE_ENV === \"production\" ? false : \"source-map\",\n};\n```\n\n**Strip `sourcesContent` from production maps** when uploading to your error tracker. By default, bundlers embed the full original source inside the `.map` file — anyone who obtains the map (including via a misconfigured upload step) gets your unminified code. Configure your bundler to omit `sourcesContent`, or use a Sentry\u002FBugsnag CLI flag that does so when uploading.\n\nFor Vite, prefer `sourcemap: 'hidden'` over `'true'` so the `\u002F\u002F# sourceMappingURL=` comment isn't emitted into the bundle.\n\n---\n\n## Performance best practices\n\n### Avoid blocking patterns\n\n```javascript\n\u002F\u002F ❌ Blocking script\n\u003Cscript src=\"heavy-library.js\">\u003C\u002Fscript>\n\n\u002F\u002F ✅ Deferred script\n\u003Cscript defer src=\"heavy-library.js\">\u003C\u002Fscript>\n\n\u002F\u002F ❌ Blocking CSS import\n@import url('other-styles.css');\n\n\u002F\u002F ✅ Link tags (parallel loading)\n\u003Clink rel=\"stylesheet\" href=\"styles.css\">\n\u003Clink rel=\"stylesheet\" href=\"other-styles.css\">\n```\n\n### Efficient event handlers\n\n```javascript\n\u002F\u002F ❌ Handler on every element\nitems.forEach((item) => {\n  item.addEventListener(\"click\", handleClick);\n});\n\n\u002F\u002F ✅ Event delegation\ncontainer.addEventListener(\"click\", (e) => {\n  if (e.target.matches(\".item\")) {\n    handleClick(e);\n  }\n});\n```\n\n### Memory management\n\n```javascript\n\u002F\u002F ❌ Memory leak (never removed)\nconst handler = () => {\n  \u002F* ... *\u002F\n};\nwindow.addEventListener(\"resize\", handler);\n\n\u002F\u002F ✅ Cleanup when done\nconst handler = () => {\n  \u002F* ... *\u002F\n};\nwindow.addEventListener(\"resize\", handler);\n\n\u002F\u002F Later, when component unmounts:\nwindow.removeEventListener(\"resize\", handler);\n\n\u002F\u002F ✅ Using AbortController\nconst controller = new AbortController();\nwindow.addEventListener(\"resize\", handler, { signal: controller.signal });\n\n\u002F\u002F Cleanup:\ncontroller.abort();\n```\n\n---\n\n## Code quality\n\n### Valid HTML\n\n```html\n\u003C!-- ❌ Invalid HTML -->\n\u003Cdiv id=\"header\">\n  \u003Cdiv id=\"header\">\n    \u003C!-- Duplicate ID -->\n\n    \u003Cul>\n      \u003Cdiv>Item\u003C\u002Fdiv>\n      \u003C!-- Invalid child -->\n    \u003C\u002Ful>\n\n    \u003Ca href=\"\u002F\">\u003Cbutton>Click\u003C\u002Fbutton>\u003C\u002Fa>\n    \u003C!-- Invalid nesting -->\n\n    \u003C!-- ✅ Valid HTML -->\n    \u003Cheader id=\"site-header\">\u003C\u002Fheader>\n\n    \u003Cul>\n      \u003Cli>Item\u003C\u002Fli>\n    \u003C\u002Ful>\n\n    \u003Ca href=\"\u002F\" class=\"button\">Click\u003C\u002Fa>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n```\n\n### Semantic HTML\n\n```html\n\u003C!-- ❌ Non-semantic -->\n\u003Cdiv class=\"header\">\n  \u003Cdiv class=\"nav\">\n    \u003Cdiv class=\"nav-item\">Home\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n\u003Cdiv class=\"main\">\n  \u003Cdiv class=\"article\">\n    \u003Cdiv class=\"title\">Headline\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n\n\u003C!-- ✅ Semantic HTML5 -->\n\u003Cheader>\n  \u003Cnav>\n    \u003Ca href=\"\u002F\">Home\u003C\u002Fa>\n  \u003C\u002Fnav>\n\u003C\u002Fheader>\n\u003Cmain>\n  \u003Carticle>\n    \u003Ch1>Headline\u003C\u002Fh1>\n  \u003C\u002Farticle>\n\u003C\u002Fmain>\n```\n\n### Image aspect ratios\n\n```html\n\u003C!-- ❌ Distorted images -->\n\u003Cimg src=\"photo.jpg\" width=\"300\" height=\"100\" \u002F>\n\u003C!-- If actual ratio is 4:3, this squishes the image -->\n\n\u003C!-- ✅ Preserve aspect ratio -->\n\u003Cimg src=\"photo.jpg\" width=\"300\" height=\"225\" \u002F>\n\u003C!-- Actual 4:3 dimensions -->\n\n\u003C!-- ✅ CSS object-fit for flexibility -->\n\u003Cimg src=\"photo.jpg\" style=\"width: 300px; height: 200px; object-fit: cover;\" \u002F>\n```\n\n---\n\n## Permissions & privacy\n\n### Request permissions properly\n\n```javascript\n\u002F\u002F ❌ Request on page load (bad UX, often denied)\nnavigator.geolocation.getCurrentPosition(success, error);\n\n\u002F\u002F ✅ Request in context, after user action\nfindNearbyButton.addEventListener(\"click\", async () => {\n  \u002F\u002F Explain why you need it\n  if (await showPermissionExplanation()) {\n    navigator.geolocation.getCurrentPosition(success, error);\n  }\n});\n```\n\n### Permissions policy\n\n```html\n\u003C!-- Restrict powerful features -->\n\u003Cmeta\n  http-equiv=\"Permissions-Policy\"\n  content=\"geolocation=(), camera=(), microphone=()\"\n\u002F>\n\n\u003C!-- Or allow for specific origins -->\n\u003Cmeta\n  http-equiv=\"Permissions-Policy\"\n  content=\"geolocation=(self 'https:\u002F\u002Fmaps.example.com')\"\n\u002F>\n```\n\n---\n\n## Audit checklist\n\n### Security (critical)\n\n- [ ] HTTPS enabled, no mixed content\n- [ ] No vulnerable dependencies (`npm audit`)\n- [ ] CSP headers configured (with `frame-ancestors`, `base-uri`, `form-action`)\n- [ ] `require-trusted-types-for 'script'` enforced (or report-only during rollout)\n- [ ] Third-party `\u003Cscript>`\u002F`\u003Clink rel=\"stylesheet\">` pinned with SRI hashes\n- [ ] Security headers present (HSTS, X-Content-Type-Options, Referrer-Policy)\n- [ ] No exposed source maps (and `sourcesContent` stripped from uploaded ones)\n\n### Compatibility\n\n- [ ] Valid HTML5 doctype\n- [ ] Charset declared first in head\n- [ ] Viewport meta tag present\n- [ ] No deprecated APIs used\n- [ ] Passive event listeners for scroll\u002Ftouch\n\n### Code quality\n\n- [ ] No console errors\n- [ ] Valid HTML (no duplicate IDs)\n- [ ] Semantic HTML elements used\n- [ ] Proper error handling\n- [ ] Memory cleanup in components\n\n### UX\n\n- [ ] No intrusive interstitials\n- [ ] Permission requests in context\n- [ ] Clear error messages\n- [ ] Appropriate image aspect ratios\n\n## Tools\n\n| Tool                                                            | Purpose                                   |\n| --------------------------------------------------------------- | ----------------------------------------- |\n| `npm audit`                                                     | Dependency vulnerabilities                |\n| [SecurityHeaders.com](https:\u002F\u002Fsecurityheaders.com)              | Header analysis                           |\n| [W3C Validator](https:\u002F\u002Fvalidator.w3.org)                       | HTML validation                           |\n| Live Lighthouse audit (Chrome DevTools MCP: `lighthouse_audit`) | Rendered Best Practices checks for agents |\n| Lighthouse CLI                                                  | Best Practices audit fallback             |\n| [Observatory](https:\u002F\u002Fobservatory.mozilla.org)                  | Security scan                             |\n\n## References\n\n- [MDN Web Security](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FSecurity)\n- [OWASP Top 10](https:\u002F\u002Fowasp.org\u002Fwww-project-top-ten\u002F)\n- [Web Quality Audit](..\u002Fweb-quality-audit\u002FSKILL.md)\n",{"name":5,"license":15,"metadata":47,"description":7},{"author":48,"version":49},"web-quality-skills","2.0","main","57541c0eee379b047ef924f47432ce8ffa5130e8","skills\u002Fdefault\u002Fweb-best-practices","https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills","https:\u002F\u002Fgithub.com\u002Fjapurcell","2026-11-11 08:59:34",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add japurcell\u002Fskills --skill web-best-practices",{"tool":62,"command":63},"bunx","bunx skills add japurcell\u002Fskills --skill web-best-practices",{"tool":65,"command":66},"pnpm","pnpm dlx skills add japurcell\u002Fskills --skill web-best-practices","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdefault\u002Fweb-best-practices\u002FSKILL.md",true,[70],{"path":71,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":74,"sourceHtmlUrl":75},"references\u002FSECURITY.md","REFERENCE","参考文档",6705,"https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdefault\u002Fweb-best-practices\u002Freferences\u002FSECURITY.md",[77,105,127,154,176,197],{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":88,"publishedAt":42},"01M6PT0ZTYCN4DHK3SXWS79WSY","spec-create","Create or revise a portable behavioral SPEC for a feature or whole product. Use when specifying a new idea, documenting an existing system for reimplementation, or defining a change while preserving compatibility. Implementation planning is a separate task.","https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdefault\u002Fspec-create\u002FSKILL.md",[83],{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000031","产品经理","PROFESSION","适用职业",[89,90,91,92,93,27,94,31,95,96,97,98,99,100,101,30,102,103,104],"create","spec","spec create","feature","planning","system","Existing","task","output","change","product","implementation","revise","behavioral","idea","path",{"skillNo":106,"name":107,"nameZh":6,"description":108,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":112,"publishedAt":42},"01M6GSVHQ4MY4R4SMS8RY6324Q","prd-ralph","Complete at most one eligible unfinished PRD task, verify it, log progress, and optionally create one scoped commit.","https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdefault\u002Fprd-ralph\u002FSKILL.md",[111],{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},[113,114,115,89,116,117,118,119,120,121,96,122,123,124,125,126,104],"prd ralph","ralph","prd","progress","verify","at","complete","commit","it","log","scoped","false","file","id",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":136,"publishedAt":42},"01M6GSYMGHE2FEGEFKD926EJH8","fsdp-style-guide","Mandatory for all UI\u002FUX design, implementation, review, audit, or visual regression tasks. Always use for any work that affects user-facing appearance, behavior, accessibility, branding, layout, navigation, forms, dialogs, alerts, tables, or visual design.","https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fforest-service\u002Ffsdp-style-guide\u002FSKILL.md",[133],{"categoryNo":134,"categoryName":135,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000032","设计师",[137,138,139,140,141,142,143,144,31,145,146,33,147,148,149,150,151,36,152,153,27],"fsdp style guide","style guide","FSDP","style","guide","visual regression","regression","design","visual","tasks","visual design","alerts","work","branding","layout","ux","behavior",{"skillNo":155,"name":156,"nameZh":6,"description":157,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":158,"categories":159,"keywords":160,"publishedAt":42},"01M6GSYHEQARNM7WN0RJA7K7T2","agency-priorities","Provide self-contained USDA strategic goals and Forest Service priorities for agency briefings, proposals, and work-plan alignment. Use when explaining agency direction or evaluating how a project contributes to USDA and Forest Service priorities.","https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fforest-service\u002Fagency-priorities\u002FSKILL.md",[],[161,162,163,164,165,149,166,167,168,169,170,171,172,173,31,27,25,174,175],"agency priorities","priorities","agency","self","alignment","plan","goals","proposals","direction","how","project","strategic","service","briefings","self-contained",{"skillNo":177,"name":178,"nameZh":6,"description":179,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":180,"categories":181,"keywords":185,"publishedAt":42},"01M6GSY9VVN8RSJ5QYM540ZJ1D","accomplishment-report","Create a concise Forest Service executive accomplishment report when all work in an ExecPlan is completed, or update its existing report. Use at ExecPlan closeout and when asked to report completed work, agency alignment, and estimated AI-assisted versus human-only time and cost savings.","https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fforest-service\u002Faccomplishment-report\u002FSKILL.md",[182],{"categoryNo":183,"categoryName":184,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000034","行政与办公人员",[186,187,165,188,89,149,189,190,191,192,118,163,193,173,31,194,195,196,95,25],"report","accomplishment report","cost","concise","human","time","update","only","closeout","savings","execplan",{"skillNo":198,"name":199,"nameZh":6,"description":200,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":201,"categories":202,"keywords":211,"publishedAt":42},"01M6GSY7J8ZMCE319P8Y6JZ432","writing-for-agents","Writing documents for agents. Use when creating or editing skills, or modifying AGENTS.md or CLAUDE.md.","https:\u002F\u002Fgithub.com\u002Fjapurcell\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdefault\u002Fwriting-for-agents\u002FSKILL.md",[203,208],{"categoryNo":204,"categoryName":205,"taxonomyType":206,"taxonomyTypeDesc":207},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":209,"categoryName":210,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000036","内容创作者",[212,213,27,31,214,215,25],"writing","writing for agents","documents","editing"]