[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C4NY0FTYGTS7THKRAC20T3":3,"skill-same:01M6C4NY0FTYGTS7THKRAC20T3":91},{"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":26,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":42,"commitSha":43,"skillDirectory":44,"forksCount":45,"repositoryHtmlUrl":46,"repositoryHomepageUrl":6,"ownerHtmlUrl":47,"ownerBlogUrl":48,"ownerDescription":49,"updatedAt":50,"install":51,"resources":64},"01M6C4NY0FTYGTS7THKRAC20T3","frontend-testing",null,"USE WHEN testing non-React UI (vanilla JavaScript or TypeScript DOM code, a web component, or a Vue, Svelte or Angular component) with DOM Testing Library in Vitest or Jest with jsdom: writing, fixing or reviewing a test, or a request to add an end-to-end journey or a layout check to that suite; NOT WHEN the code or project uses React or React Testing Library (use web:react-testing), for XState actors (web:xstate), or for Playwright, Cypress or screenshot-comparison work in a real browser; covers queries, accessible names, userEvent, async results, network stubs and assertions that pass for the wrong reason, and declines what jsdom cannot prove while offering the part that fits.","mintuz\u002Fskills","mintuz","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F447031?v=4","01M6BVWJYSVR4XQC9AQCEMWQRG","Adam Bulmer","01M6C4DCXKNYZB281YB3PZDJJG",35,"MIT","https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ffrontend-testing\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[27,28,29,30,31,32,33,34,35,36],"testing","frontend","frontend testing","browser","first","regression","behavior","library","testing library","ui","2026-11-10 10:12:30",false,"---\nname: frontend-testing\ndescription: \"USE WHEN testing non-React UI (vanilla JavaScript or TypeScript DOM code, a web component, or a Vue, Svelte or Angular component) with DOM Testing Library in Vitest or Jest with jsdom: writing, fixing or reviewing a test, or a request to add an end-to-end journey or a layout check to that suite; NOT WHEN the code or project uses React or React Testing Library (use web:react-testing), for XState actors (web:xstate), or for Playwright, Cypress or screenshot-comparison work in a real browser; covers queries, accessible names, userEvent, async results, network stubs and assertions that pass for the wrong reason, and declines what jsdom cannot prove while offering the part that fits.\"\n---\n\n# Front-end testing with DOM Testing Library\n\nWrite behaviour-first DOM Testing Library tests for UI built without React. For React components, hooks and providers, load `web:react-testing`. For the TDD workflow, load `tdd`.\n\n## Scope\n\nThis skill covers tests that run against a DOM inside a test runner, such as Vitest or Jest with jsdom. Decline these parts of a request:\n\n- **End-to-end and cross-page journeys:** cross-document navigation, redirects, real servers, downloads, extra browser tabs, and real cookie behaviour such as `HttpOnly`, `Secure` and `SameSite`. Reasons: jsdom does not navigate between documents or open real tabs. A real server sets an `HttpOnly` cookie, and page scripts cannot read it, so `document.cookie` in jsdom cannot prove it. Exception: jsdom holds a URL and a cookie jar, so a client-side URL or `document.cookie` check inside one document is in scope.\n- **Visual appearance:** computed layout, real fonts, screenshots and visual-regression diffs. Reason: jsdom does not lay out or paint, so it cannot report a size, a position or a rendered colour.\n\nTo decline (`principle-state-the-scope`):\n\n1. For each declined part, write its reason from the list above. A named replacement tool is not a reason.\n2. Name the replacement: Playwright or Cypress for a journey; a screenshot or visual-regression comparison in a real browser for appearance.\n3. Offer the part of the request that fits in the DOM, if any.\n\nDo not offer a jsdom test that fakes the navigation, the server or the rendering as an equivalent.\n\n`@testing-library\u002Fdom` exports no `render`. Mount the markup yourself. When a framework wrapper such as `@testing-library\u002Fvue` supplies `render`, mount through it; this skill still governs the queries and the interactions.\n\n## Use the project's test libraries\n\nUse the runner, matchers, network stub and helpers the project already has. Do not add a testing dependency or a lint plugin unasked (`principle-reuse-before-building`).\n\n- **Interactions:** Use `userEvent` from `@testing-library\u002Fuser-event`, created with `userEvent.setup()` in each test. Use `fireEvent` only for an event that `userEvent` does not support, or when the project shows that `user-event` is not installed. In the second case, say that `user-event` would model the interaction more closely.\n- **Matchers:** Use jest-dom matchers such as `toBeInTheDocument()` and `toBeDisabled()`. Only when the project shows that its setup does not register jest-dom, assert absence with `toBeNull()`, text with `textContent`, and state with the element's own property, such as `disabled`.\n- **Network:** Use the project's existing stub: its MSW `server` and handlers, otherwise its own stub helper. Override an MSW handler for one test with `server.use(...)`. When the project has an MSW server, do not add a `fetch` or `axios` spy. When the code receives its request function by injection, pass a mock for that function.\n- **Lint:** When `eslint-plugin-testing-library` and `eslint-plugin-jest-dom` are missing, recommend them. Install them only when asked.\n\n## Test behaviour, not implementation\n\nTest what users see and do through the DOM: clicks, typing and visible text (`principle-test-behavior-not-implementation`). For each assertion, ask which plausible wrong implementation would still pass. Write the assertion that fails on it.\n\nMount the real component under test. Let it own its markup and event wiring. Do not read or set its internal state. Do not write your own listener and then assert that it ran. Return a runnable test that imports the code under test. When the task does not give the module path, import from an assumed path beside the test file and say that the path is assumed.\n\n## Queries and interactions\n\n- Choose the variant by timing: `getBy*` for state already present, `findBy*` for state that appears later, `queryBy*` for absence.\n- Query through `screen`. Use `within(element)` to scope a query to one region. Do not bind queries to a container, for example with `getQueriesForElement`. Do not use a CSS class selector when an accessible query reaches the element.\n- Re-query an element by its new role or accessible name when you must prove it is discoverable under that identity, or when the update can replace the node. An existing reference stays valid when the same node is reused.\n- Interact once, outside `waitFor` and every other retrying callback. Put only assertions inside a retrying callback.\n- Wait for an observable state with `findBy*` or `waitFor`. Do not put a fixed delay in place of that wait, such as `setTimeout`, a sleep promise or a fake-timer advance.\n- Reset markup that you mounted into `document.body` yourself between tests, for example with `document.body.innerHTML = ''` in `afterEach`. Do not call `cleanup()` in a test file.\n\n## Accessible names come from the markup\n\nA role query's `name` option matches the accessible name, not the text. These roles take a name only from `aria-label` or `aria-labelledby`, never from their content: `status`, `alert`, `region`, `dialog`, `navigation` and `form`. Without those attributes they have no name, and `{ name: \u002Forder placed\u002Fi }` finds nothing. Find such an element by role alone, then assert its text:\n\n```typescript\nconst status = await screen.findByRole(\"status\");\nexpect(status).toHaveTextContent(\u002Forder placed\u002Fi);\n```\n\nUse this form only when the role first appears in its terminal state. When an earlier state uses the same role, such as a pending `status`, `findByRole` returns that earlier element at once. Then retry on the terminal text:\n\n```typescript\nawait waitFor(() => expect(screen.getByRole(\"status\")).toHaveTextContent(\u002Forder placed\u002Fi));\n```\n\nUse `{ name }` for roles named by their content, such as `button`, `link`, `heading` and `option`. `\u003Cinput type=\"password\">` has no ARIA role, so `getByRole('textbox')` does not find it. Find it with `getByLabelText`.\n\n## When no accessible query fits\n\nWork down the priority order in [queries.md](references\u002Fqueries.md). When an element exposes no role, label, text, alt text or title:\n\n1. Name the queries you tried and say why each fails on the supplied markup.\n2. When you own the markup, fix it. Prefer semantic HTML, then a `\u003Clabel>`, then an ARIA attribute.\n3. When you cannot change the markup, for example a third-party widget, use `getByTestId`. Record the reason in a comment beside the query.\n4. Read raw attributes and descendants with plain DOM access on the element you already hold, such as `element.querySelector('canvas')` and `getAttribute('data-render-count')`. `screen` has no attribute query; `screen.getByAttribute` does not exist. `queryHelpers` and `buildQueries` can build one; do this only for a pattern repeated across many tests.\n\nNever assume a role, label or accessible name that the supplied markup lacks; the test then fails on the real component. Never invent a query name. The built-in queries on `screen` are the eight `*By*` families in [queries.md](references\u002Fqueries.md), each with its `get`, `query`, `find` and `All` forms.\n\n## Prove the whole rule\n\nA test proves a rule only when it observes both sides of that rule.\n\n- **Call counts:** Assert `toHaveBeenCalledTimes(n)`. `toHaveBeenCalledWith(...)` proves the arguments of one call, not how many calls happened.\n- **Conditional enablement:** Assert that the control is disabled while its condition is unmet, and enabled once the condition is met.\n- **Repeated interactions:** Assert the full state cycle for every repetition. A rising call count does not prove that the second attempt reached the same pending and terminal states.\n- **Absence:** First await a state that exists only after the async work finished, such as the success text. Then assert absence. An absence assertion straight after an interaction can run before the component reacts, so it passes whether the component is correct or broken. Assert absence with `queryBy*`, which returns null when nothing matches; never with a throwing `getBy*` inside `expect(...).toThrow()`. `queryBy*` still throws when several elements match. Only when the supplied markup can render several matches, use `queryAllBy*` and assert an empty array. `waitForElementToBeRemoved` throws when the element is already gone at call time; use it only for a removal that has not happened yet.\n\n## References\n\nRead a reference when the task needs its detail:\n\n| Reference | Read for |\n| --- | --- |\n| [queries.md](references\u002Fqueries.md) | Query priority, variants, query mistakes |\n| [user-events.md](references\u002Fuser-events.md) | `userEvent` against `fireEvent`, `userEvent.setup()`, clicking, typing, keyboard, fake timers |\n| [async-testing.md](references\u002Fasync-testing.md) | `findBy*`, `waitFor`, `waitForElementToBeRemoved`; loading states, API responses, debounced inputs |\n| [msw.md](references\u002Fmsw.md) | MSW `setupServer`, per-test handler overrides |\n| [anti-patterns.md](references\u002Fanti-patterns.md) | Mistakes and fixes, cleanup, ESLint plugin configuration |\n| [accessibility-first-testing.md](references\u002Faccessibility-first-testing.md) | ARIA against semantic HTML |\n","# Front-end testing with DOM Testing Library\n\nWrite behaviour-first DOM Testing Library tests for UI built without React. For React components, hooks and providers, load `web:react-testing`. For the TDD workflow, load `tdd`.\n\n## Scope\n\nThis skill covers tests that run against a DOM inside a test runner, such as Vitest or Jest with jsdom. Decline these parts of a request:\n\n- **End-to-end and cross-page journeys:** cross-document navigation, redirects, real servers, downloads, extra browser tabs, and real cookie behaviour such as `HttpOnly`, `Secure` and `SameSite`. Reasons: jsdom does not navigate between documents or open real tabs. A real server sets an `HttpOnly` cookie, and page scripts cannot read it, so `document.cookie` in jsdom cannot prove it. Exception: jsdom holds a URL and a cookie jar, so a client-side URL or `document.cookie` check inside one document is in scope.\n- **Visual appearance:** computed layout, real fonts, screenshots and visual-regression diffs. Reason: jsdom does not lay out or paint, so it cannot report a size, a position or a rendered colour.\n\nTo decline (`principle-state-the-scope`):\n\n1. For each declined part, write its reason from the list above. A named replacement tool is not a reason.\n2. Name the replacement: Playwright or Cypress for a journey; a screenshot or visual-regression comparison in a real browser for appearance.\n3. Offer the part of the request that fits in the DOM, if any.\n\nDo not offer a jsdom test that fakes the navigation, the server or the rendering as an equivalent.\n\n`@testing-library\u002Fdom` exports no `render`. Mount the markup yourself. When a framework wrapper such as `@testing-library\u002Fvue` supplies `render`, mount through it; this skill still governs the queries and the interactions.\n\n## Use the project's test libraries\n\nUse the runner, matchers, network stub and helpers the project already has. Do not add a testing dependency or a lint plugin unasked (`principle-reuse-before-building`).\n\n- **Interactions:** Use `userEvent` from `@testing-library\u002Fuser-event`, created with `userEvent.setup()` in each test. Use `fireEvent` only for an event that `userEvent` does not support, or when the project shows that `user-event` is not installed. In the second case, say that `user-event` would model the interaction more closely.\n- **Matchers:** Use jest-dom matchers such as `toBeInTheDocument()` and `toBeDisabled()`. Only when the project shows that its setup does not register jest-dom, assert absence with `toBeNull()`, text with `textContent`, and state with the element's own property, such as `disabled`.\n- **Network:** Use the project's existing stub: its MSW `server` and handlers, otherwise its own stub helper. Override an MSW handler for one test with `server.use(...)`. When the project has an MSW server, do not add a `fetch` or `axios` spy. When the code receives its request function by injection, pass a mock for that function.\n- **Lint:** When `eslint-plugin-testing-library` and `eslint-plugin-jest-dom` are missing, recommend them. Install them only when asked.\n\n## Test behaviour, not implementation\n\nTest what users see and do through the DOM: clicks, typing and visible text (`principle-test-behavior-not-implementation`). For each assertion, ask which plausible wrong implementation would still pass. Write the assertion that fails on it.\n\nMount the real component under test. Let it own its markup and event wiring. Do not read or set its internal state. Do not write your own listener and then assert that it ran. Return a runnable test that imports the code under test. When the task does not give the module path, import from an assumed path beside the test file and say that the path is assumed.\n\n## Queries and interactions\n\n- Choose the variant by timing: `getBy*` for state already present, `findBy*` for state that appears later, `queryBy*` for absence.\n- Query through `screen`. Use `within(element)` to scope a query to one region. Do not bind queries to a container, for example with `getQueriesForElement`. Do not use a CSS class selector when an accessible query reaches the element.\n- Re-query an element by its new role or accessible name when you must prove it is discoverable under that identity, or when the update can replace the node. An existing reference stays valid when the same node is reused.\n- Interact once, outside `waitFor` and every other retrying callback. Put only assertions inside a retrying callback.\n- Wait for an observable state with `findBy*` or `waitFor`. Do not put a fixed delay in place of that wait, such as `setTimeout`, a sleep promise or a fake-timer advance.\n- Reset markup that you mounted into `document.body` yourself between tests, for example with `document.body.innerHTML = ''` in `afterEach`. Do not call `cleanup()` in a test file.\n\n## Accessible names come from the markup\n\nA role query's `name` option matches the accessible name, not the text. These roles take a name only from `aria-label` or `aria-labelledby`, never from their content: `status`, `alert`, `region`, `dialog`, `navigation` and `form`. Without those attributes they have no name, and `{ name: \u002Forder placed\u002Fi }` finds nothing. Find such an element by role alone, then assert its text:\n\n```typescript\nconst status = await screen.findByRole(\"status\");\nexpect(status).toHaveTextContent(\u002Forder placed\u002Fi);\n```\n\nUse this form only when the role first appears in its terminal state. When an earlier state uses the same role, such as a pending `status`, `findByRole` returns that earlier element at once. Then retry on the terminal text:\n\n```typescript\nawait waitFor(() => expect(screen.getByRole(\"status\")).toHaveTextContent(\u002Forder placed\u002Fi));\n```\n\nUse `{ name }` for roles named by their content, such as `button`, `link`, `heading` and `option`. `\u003Cinput type=\"password\">` has no ARIA role, so `getByRole('textbox')` does not find it. Find it with `getByLabelText`.\n\n## When no accessible query fits\n\nWork down the priority order in [queries.md](references\u002Fqueries.md). When an element exposes no role, label, text, alt text or title:\n\n1. Name the queries you tried and say why each fails on the supplied markup.\n2. When you own the markup, fix it. Prefer semantic HTML, then a `\u003Clabel>`, then an ARIA attribute.\n3. When you cannot change the markup, for example a third-party widget, use `getByTestId`. Record the reason in a comment beside the query.\n4. Read raw attributes and descendants with plain DOM access on the element you already hold, such as `element.querySelector('canvas')` and `getAttribute('data-render-count')`. `screen` has no attribute query; `screen.getByAttribute` does not exist. `queryHelpers` and `buildQueries` can build one; do this only for a pattern repeated across many tests.\n\nNever assume a role, label or accessible name that the supplied markup lacks; the test then fails on the real component. Never invent a query name. The built-in queries on `screen` are the eight `*By*` families in [queries.md](references\u002Fqueries.md), each with its `get`, `query`, `find` and `All` forms.\n\n## Prove the whole rule\n\nA test proves a rule only when it observes both sides of that rule.\n\n- **Call counts:** Assert `toHaveBeenCalledTimes(n)`. `toHaveBeenCalledWith(...)` proves the arguments of one call, not how many calls happened.\n- **Conditional enablement:** Assert that the control is disabled while its condition is unmet, and enabled once the condition is met.\n- **Repeated interactions:** Assert the full state cycle for every repetition. A rising call count does not prove that the second attempt reached the same pending and terminal states.\n- **Absence:** First await a state that exists only after the async work finished, such as the success text. Then assert absence. An absence assertion straight after an interaction can run before the component reacts, so it passes whether the component is correct or broken. Assert absence with `queryBy*`, which returns null when nothing matches; never with a throwing `getBy*` inside `expect(...).toThrow()`. `queryBy*` still throws when several elements match. Only when the supplied markup can render several matches, use `queryAllBy*` and assert an empty array. `waitForElementToBeRemoved` throws when the element is already gone at call time; use it only for a removal that has not happened yet.\n\n## References\n\nRead a reference when the task needs its detail:\n\n| Reference | Read for |\n| --- | --- |\n| [queries.md](references\u002Fqueries.md) | Query priority, variants, query mistakes |\n| [user-events.md](references\u002Fuser-events.md) | `userEvent` against `fireEvent`, `userEvent.setup()`, clicking, typing, keyboard, fake timers |\n| [async-testing.md](references\u002Fasync-testing.md) | `findBy*`, `waitFor`, `waitForElementToBeRemoved`; loading states, API responses, debounced inputs |\n| [msw.md](references\u002Fmsw.md) | MSW `setupServer`, per-test handler overrides |\n| [anti-patterns.md](references\u002Fanti-patterns.md) | Mistakes and fixes, cleanup, ESLint plugin configuration |\n| [accessibility-first-testing.md](references\u002Faccessibility-first-testing.md) | ARIA against semantic HTML |\n",{"name":5,"description":7},"main","0e0a9088ef87c2efb762c587ce6e949289321143","src\u002Fweb\u002Fskills\u002Ffrontend-testing",7,"https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills","https:\u002F\u002Fgithub.com\u002Fmintuz","http:\u002F\u002Fwww.mintuz.com","Working at @monzo, Previously worked at Moonpig, @booking and @bbc","2026-11-13 19:21:11",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add mintuz\u002Fskills --skill frontend-testing",{"tool":57,"command":58},"bunx","bunx skills add mintuz\u002Fskills --skill frontend-testing",{"tool":60,"command":61},"pnpm","pnpm dlx skills add mintuz\u002Fskills --skill frontend-testing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ffrontend-testing\u002FSKILL.md",true,[65,71,75,79,83,87],{"path":66,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":69,"sourceHtmlUrl":70},"references\u002Faccessibility-first-testing.md","REFERENCE","参考文档",1308,"https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ffrontend-testing\u002Freferences\u002Faccessibility-first-testing.md",{"path":72,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":73,"sourceHtmlUrl":74},"references\u002Fanti-patterns.md",3792,"https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ffrontend-testing\u002Freferences\u002Fanti-patterns.md",{"path":76,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":77,"sourceHtmlUrl":78},"references\u002Fasync-testing.md",4242,"https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ffrontend-testing\u002Freferences\u002Fasync-testing.md",{"path":80,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":81,"sourceHtmlUrl":82},"references\u002Fmsw.md",2525,"https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ffrontend-testing\u002Freferences\u002Fmsw.md",{"path":84,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":85,"sourceHtmlUrl":86},"references\u002Fqueries.md",2631,"https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ffrontend-testing\u002Freferences\u002Fqueries.md",{"path":88,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":89,"sourceHtmlUrl":90},"references\u002Fuser-events.md",1966,"https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ffrontend-testing\u002Freferences\u002Fuser-events.md",[92,108,130,143,163,180],{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":100,"publishedAt":37},"01M6C4PDGB6ZZWBS35N3DF7PZZ","xstate","WHEN React state changes in response to an external answer — async submit, in-flight or loading flags, retries, cancellation, polling, optimistic updates, stale responses — or to an interaction with an interruptible middle, such as a drag or a gesture; or WHEN building, reviewing, testing, or visualising XState 5 actors and @xstate\u002Freact integrations; NOT for a value one event sets completely, such as a text draft, a focus flag, or a plain open and closed toggle; models typed lifecycles and renders legal Mermaid stateDiagram-v2 charts.","https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Fxstate\u002FSKILL.md",[98,99],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[94,101,102,103,104,105,106,27,107],"react","text","state","integrations","Charts","mermaid","open",{"skillNo":109,"name":110,"nameZh":6,"description":111,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":112,"categories":113,"keywords":122,"publishedAt":37},"01M6C4PB6VVC2X19B63B7YNH0T","web-design","WHEN refining UI layout, typography, color, or polish; NOT code implementation; provides concise principles for intentional, legible design.","https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Fweb-design\u002FSKILL.md",[114,119],{"categoryNo":115,"categoryName":116,"taxonomyType":117,"taxonomyTypeDesc":118},"01JBSEEDCAT000000000000032","设计师","PROFESSION","适用职业",{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",[123,124,125,126,127,36,128,129],"web","design","web design","code implementation","polish","typography","code",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":137,"publishedAt":37},"01M6C4P8D92GJDA49V7X1CZXEV","tailwind","USE WHEN writing, fixing or reviewing styling in a project that uses Tailwind CSS, even a one-class change or a request that does not name Tailwind: colours, focus rings, spacing, class names chosen by a prop, map or CVA variants, theme tokens and dark mode in the Tailwind stylesheet or tailwind.config, setup or upgrades (if unsure whether the project uses Tailwind, check package.json or the main stylesheet first); NOT WHEN writing plain CSS or Sass in a project without Tailwind; produces class names Tailwind can find, syntax for the installed major version, visible focus indicators, and no unrequested dependency or invented brand value.","https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Ftailwind\u002FSKILL.md",[136],{"categoryNo":120,"categoryName":121,"taxonomyType":21,"taxonomyTypeDesc":22},[138,139,140,141,124,142],"Tailwind","libraries","component","css","design systems",{"skillNo":144,"name":145,"nameZh":6,"description":146,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":147,"categories":148,"keywords":152,"publishedAt":37},"01M6C4P5VWQ69Q32DA0634GDJY","refactoring","WHEN you need a plan to refactor, restructure, or clean up working code and the test result is known, either green or a recorded failing set; NOT for new feature delivery or any behavior change; reversible-baseline safety with prioritized improvements.","https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Frefactoring\u002FSKILL.md",[149],{"categoryNo":150,"categoryName":151,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000007","代码审查",[145,153,154,155,33,156,157,158,159,160,129,161,162],"Safety","baseline","refactor","change","plan","new feature","SET","up","feature delivery","test",{"skillNo":164,"name":101,"nameZh":6,"description":165,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":166,"categories":167,"keywords":170,"publishedAt":37},"01M6C4P345APEFEX00J8T7QMXQ","USE WHEN changing the logic, data, props, state, Effects or rendered text of a React component, hook or page, even a one-line change; NOT WHEN the request is styling only (class names, Tailwind, CSS, dark mode, spacing, shadows) or tests only, or when it models submit, in-flight, loading, retry or failure states, even for a named React form (xstate covers that, including its React wiring); applies React 19 composition, state, Effect, structure, API and performance rules with only the project's installed libraries.","https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Freact\u002FSKILL.md",[168,169],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[101,129,141,138,171,27,172,173,174,175,176,103,162,177,178,179],"performance","hooks","components","architecture","api","react components","state management","styling","management",{"skillNo":181,"name":182,"nameZh":6,"description":183,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":184,"categories":185,"keywords":188,"publishedAt":37},"01M6C4P0DK385QBG3B3S86TQV2","react-testing","USE WHEN writing, fixing or reviewing a React Testing Library test for a component, hook or context provider, even one small test, or when asked for an end-to-end journey inside a Vitest or React Testing Library suite; NOT WHEN changing the component itself (react), testing XState actors or @xstate\u002Freact (xstate), or writing a Playwright or Cypress test; covers renderHook, providers, async results, forms and false-pass anti-patterns, and declines end-to-end journeys with the testable slices.","https:\u002F\u002Fgithub.com\u002Fmintuz\u002Fskills\u002Fblob\u002Fmain\u002Fsrc\u002Fweb\u002Fskills\u002Freact-testing\u002FSKILL.md",[186,187],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[189,27,101,190,172,191,34,35,173,192,193,176],"react testing","anti-patterns","e2e","Providers","context"]