@imfusion/web-ui 0.5.1-dev.33.gadde98d1 → 0.5.1-dev.39.gfc64049e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +18 -10
  2. package/bin/install.js +96 -42
  3. package/bin/install.test.ts +150 -64
  4. package/dist/llms/gen-tokens.d.ts +7 -0
  5. package/package.json +6 -3
  6. package/src/llms/install-templates/codex-hooks.json +44 -0
  7. package/src/llms/install-templates/hooks/session-start.sh +5 -0
  8. package/src/llms/install-templates/hooks/stop.sh +18 -0
  9. package/src/llms/install-templates/hooks/subagent-start.sh +5 -0
  10. package/src/llms/install-templates/hooks/user-prompt-submit.sh +5 -0
  11. package/src/llms/{skills/imf-web-ui-agent-setup/templates → install-templates}/settings.json +14 -6
  12. package/src/llms/skills/imf-web-ui/SKILL.md +8 -11
  13. package/src/llms/skills/imf-web-ui-audit/SKILL.md +87 -0
  14. package/src/llms/skills/imf-web-ui-components/SKILL.md +1 -1
  15. package/src/llms/skills/imf-web-ui-conventions/SKILL.md +57 -0
  16. package/src/llms/skills/imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md +141 -0
  17. package/src/llms/skills/{imf-web-ui-frontend-setup/templates/FRONTEND_SETUP_REPORT.md → imf-web-ui-conventions/templates/REPORT.md} +4 -4
  18. package/src/llms/skills/imf-web-ui-conventions/topics/agent-tooling.md +82 -0
  19. package/src/llms/skills/imf-web-ui-conventions/topics/assets.md +27 -0
  20. package/src/llms/skills/imf-web-ui-conventions/topics/authentication.md +65 -0
  21. package/src/llms/skills/imf-web-ui-conventions/topics/class-names.md +50 -0
  22. package/src/llms/skills/imf-web-ui-conventions/topics/components.md +101 -0
  23. package/src/llms/skills/imf-web-ui-conventions/topics/data.md +212 -0
  24. package/src/llms/skills/imf-web-ui-conventions/topics/docs-structure.md +40 -0
  25. package/src/llms/skills/imf-web-ui-conventions/topics/git.md +34 -0
  26. package/src/llms/skills/imf-web-ui-conventions/topics/library-boundary.md +33 -0
  27. package/src/llms/skills/imf-web-ui-conventions/topics/library-setup.md +26 -0
  28. package/src/llms/skills/{imf-web-ui-frontend-conventions/references → imf-web-ui-conventions/topics}/npm-project.md +14 -18
  29. package/src/llms/skills/imf-web-ui-conventions/topics/project-structure.md +44 -0
  30. package/src/llms/skills/imf-web-ui-conventions/topics/react.md +107 -0
  31. package/src/llms/skills/imf-web-ui-conventions/topics/styling.md +88 -0
  32. package/src/llms/skills/imf-web-ui-conventions/topics/testing.md +25 -0
  33. package/src/llms/skills/imf-web-ui-conventions/topics/tokens.md +7 -0
  34. package/src/llms/skills/imf-web-ui-conventions/topics/tooling.md +116 -0
  35. package/src/llms/skills/{imf-web-ui-frontend-conventions/references → imf-web-ui-conventions/topics}/typescript.md +3 -4
  36. package/src/llms/skills/imf-web-ui-conventions/topics/validation.md +62 -0
  37. package/src/llms/skills/imf-web-ui-setup/SKILL.md +71 -0
  38. package/src/llms/skills/imf-web-ui-update/SKILL.md +9 -3
  39. package/src/llms/skills/imf-web-ui-ux/SKILL.md +4 -4
  40. package/src/llms/skills/imf-web-ui-ux/references/forms.md +2 -2
  41. package/src/llms/tokens.gen.json +887 -0
  42. package/src/llms/skills/imf-web-ui-agent-setup/SKILL.md +0 -82
  43. package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/post-tool-use.sh +0 -33
  44. package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/session-start.sh +0 -4
  45. package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/user-prompt-submit.sh +0 -4
  46. package/src/llms/skills/imf-web-ui-frontend-conventions/SKILL.md +0 -46
  47. package/src/llms/skills/imf-web-ui-frontend-conventions/references/assets.md +0 -23
  48. package/src/llms/skills/imf-web-ui-frontend-conventions/references/class-names.md +0 -42
  49. package/src/llms/skills/imf-web-ui-frontend-conventions/references/components.md +0 -63
  50. package/src/llms/skills/imf-web-ui-frontend-conventions/references/data.md +0 -201
  51. package/src/llms/skills/imf-web-ui-frontend-conventions/references/docs-structure.md +0 -44
  52. package/src/llms/skills/imf-web-ui-frontend-conventions/references/git.md +0 -33
  53. package/src/llms/skills/imf-web-ui-frontend-conventions/references/library-boundary.md +0 -37
  54. package/src/llms/skills/imf-web-ui-frontend-conventions/references/project-structure.md +0 -21
  55. package/src/llms/skills/imf-web-ui-frontend-conventions/references/react.md +0 -110
  56. package/src/llms/skills/imf-web-ui-frontend-conventions/references/stack.md +0 -39
  57. package/src/llms/skills/imf-web-ui-frontend-conventions/references/styling.md +0 -91
  58. package/src/llms/skills/imf-web-ui-frontend-conventions/references/testing.md +0 -18
  59. package/src/llms/skills/imf-web-ui-frontend-conventions/references/tooling.md +0 -76
  60. package/src/llms/skills/imf-web-ui-frontend-conventions/references/validation.md +0 -88
  61. package/src/llms/skills/imf-web-ui-frontend-setup/SKILL.md +0 -66
  62. package/src/llms/skills/imf-web-ui-frontend-setup/templates/README.md +0 -27
  63. package/src/llms/skills/imf-web-ui-library-setup/SKILL.md +0 -36
  64. /package/src/llms/{skills/imf-web-ui-frontend-setup/templates → install-templates}/AGENTS.md +0 -0
  65. /package/src/llms/{skills/imf-web-ui-agent-setup/templates → install-templates}/hooks/baseline-staleness.sh +0 -0
@@ -1,110 +0,0 @@
1
- # React
2
-
3
- The house defaults for the React code around `@imfusion/web-ui`, in full. The links throughout are for **you, the agent**:
4
- consult them while building — they are the authoritative source when a case here is ambiguous. Hand them to the human only if
5
- asked.
6
-
7
- ## Component roles
8
-
9
- Dumb/smart separation is standard React practice (it traces back to Dan Abramov's
10
- ["Presentational and Container Components"](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) and
11
- survives in [Thinking in React](https://react.dev/learn/thinking-in-react)). The house version has three roles:
12
-
13
- - **Dumb components** own how things _look_. They style and compose library primitives, receive plain data and callbacks as
14
- props, and know nothing about fetching, routing, or business logic. All non-layout styling lives here — and only here.
15
- - **Layout components** own _arrangement_ — and nothing else. `Stack`- and `Row`-based wrappers with token gaps, a page grid,
16
- a section frame. They exist because smart containers are styleless: when a container needs two panels side by side, that
17
- arrangement is a layout component, not an inline style.
18
- - **Smart containers** own how things _work_. Routes (or explicit container components) fetch data, hold orchestration logic,
19
- and wire the other two together. Zero styling — the moment a container wants CSS, extract a layout component.
20
-
21
- ```tsx
22
- // Dumb — renders what it's given
23
- function UserCard({ name, role, onEdit }: { name: string; role: string; onEdit: () => void }) {
24
- return (
25
- <Card.Root>
26
- <Card.Content>
27
- <Typo>{name}</Typo>
28
- <Chip>{role}</Chip>
29
- </Card.Content>
30
- <Card.Footer>
31
- <Button onClick={onEdit}>Edit</Button>
32
- </Card.Footer>
33
- </Card.Root>
34
- );
35
- }
36
-
37
- // Smart — knows where data comes from, renders the dumb component
38
- function UserCardContainer({ userId }: { userId: string }) {
39
- const { data } = useUserQuery(userId);
40
- const openEditor = useEditorNavigation(userId);
41
- return <UserCard name={data.name} role={data.role} onEdit={openEditor} />;
42
- }
43
- ```
44
-
45
- Why it matters here: dumb components are the layer where `@imfusion/web-ui` lives. Keeping them free of logic keeps every
46
- screen restylable, testable with plain props, and resilient to library updates. When and how to wrap the library's primitives
47
- in app-level dumb components is [library-boundary.md](library-boundary.md).
48
-
49
- ## Compose, don't configure
50
-
51
- Build screen-level pieces by composing primitives (`Stack`, `Row`, `Card`, your dumb components) rather than growing one
52
- component with a dozen boolean props. If a component's prop list reads like a settings page, it wanted to be two or three
53
- components. When state must be shared between siblings, lift it to the nearest common parent —
54
- [Sharing State Between Components](https://react.dev/learn/sharing-state-between-components) — rather than syncing copies.
55
-
56
- ## Put state where its truth lives
57
-
58
- State comes in kinds, and each kind has an owner. Work down this list and stop at the first match:
59
-
60
- 1. **URL state** — shareable via the address bar (filters, sort, pagination, active tab) → TanStack Router search params.
61
- Back button and copied links are UX features you get for free. URL values cross an external boundary; parse them according
62
- to [validation.md](validation.md).
63
- 2. **Server state** — comes from an API → TanStack Query's cache ([data.md](data.md)). Never copy server data into `useState`
64
- — that's how stale-UI bugs are born.
65
- 3. **Subtree state** — scoped to a subtree, resets on leave (wizard progress) → React context.
66
- 4. **Client state** — app-wide and persistent → TanStack Store, and only now.
67
- 5. **Local state** — one component's own (input value, open/closed) → `useState`.
68
-
69
- `useState` is the right tool for local UI state, and most state is local: whether a panel is open, which tab is active, a
70
- draft value being typed, a hover flag. Keep those in the component and don't reach for a library. When a form turns drafts
71
- into a submitted domain value, validate that boundary as described in [validation.md](validation.md).
72
-
73
- Most frontends need far less of tier 4 than they think; tiers 1–2 usually dissolve the "we need a store" instinct. For
74
- structuring the state itself, [Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure) is the
75
- reference — especially its rules on avoiding redundant and duplicated state.
76
-
77
- ## Effects: last resort, and named
78
-
79
- Before writing `useEffect`, check: derived values belong in render (or `useMemo`), responses to user actions belong in the
80
- event handler, and server synchronization belongs in the data-fetching layer. Effects are for synchronizing with systems
81
- _outside_ React. The definitive catalog of effect misuses — read it before every effect you're tempted to write — is
82
- [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect).
83
-
84
- When an effect is genuinely needed, extract it into a custom hook named for its purpose — `useSyncedScroll`,
85
- `useDocumentTitle`, `useHotkey` — never an anonymous `useEffect` block inline in a component. The name documents intent, the
86
- hook isolates the dependency array, and the component body stays declarative. Pattern reference:
87
- [Reusing Logic with Custom Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks).
88
-
89
- The naming rule holds even for the rare effect that stays inline: give the callback a name, so the intent survives without a
90
- comment —
91
-
92
- ```tsx
93
- useEffect(
94
- function syncDocumentTitle() {
95
- document.title = title;
96
- },
97
- [title]
98
- );
99
- ```
100
-
101
- ## Reading list
102
-
103
- Consult while building; each is the authority for its topic:
104
-
105
- - [Thinking in React](https://react.dev/learn/thinking-in-react) — decomposition and one-way data flow
106
- - [Keeping Components Pure](https://react.dev/learn/keeping-components-pure) — why dumb components stay dumb
107
- - [Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure) — shaping state without duplication
108
- - [Sharing State Between Components](https://react.dev/learn/sharing-state-between-components) — lifting state
109
- - [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect) — the effect misuse catalog
110
- - [Reusing Logic with Custom Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) — named effects live here
@@ -1,39 +0,0 @@
1
- # Stack
2
-
3
- The topic→tool map for an ImFusion frontend. Check this before adding a dependency; check the TanStack suite before adding a
4
- non-TanStack one.
5
-
6
- | Topic | Tool |
7
- | --------------------- | ----------------------------------------------------------------------------------------------------- |
8
- | Build | [Vite](https://vite.dev) |
9
- | Routing, URL state | [TanStack Router](https://tanstack.com/router) — file-based, type-safe search params |
10
- | Server state | [TanStack Query](https://tanstack.com/query) — the pattern around it is [data.md](data.md) |
11
- | Forms | [TanStack Form](https://tanstack.com/form) |
12
- | Data grids | [TanStack Table](https://tanstack.com/table) + web-ui's styled `Table` parts |
13
- | App-wide client state | [TanStack Store](https://tanstack.com/store) — last resort in the state ladder ([react.md](react.md)) |
14
- | Schema validation | [Zod](https://zod.dev) — at the network boundary ([data.md](data.md)) |
15
- | Styling | CSS Modules ([styling.md](styling.md)) |
16
- | Test | [Vitest](https://vitest.dev) |
17
- | Format | [Prettier](https://prettier.io) — values in [tooling.md](tooling.md) |
18
- | Lint | [ESLint](https://eslint.org) flat config ([tooling.md](tooling.md)) |
19
- | Types | `tsc --noEmit` — own script, own CI step |
20
- | Staged files | [lint-staged](https://github.com/lint-staged/lint-staged) (or nano-staged, drop-in) |
21
- | Dead code | [knip](https://knipjs.dev) — needs per-repo config |
22
-
23
- ## Devtools come with the library
24
-
25
- Every TanStack library that ships a devtools package gets it as a dev dependency, mounted in development only. Router is a
26
- given, so `@tanstack/react-router-devtools` is a given too; Query's goes in when Query does. Look for a `-devtools` sibling
27
- whenever you add a TanStack dependency — the set grows, and not every library has one yet. Once a project has several,
28
- `@tanstack/devtools` hosts them in one panel.
29
-
30
- ## When a library owns a layer
31
-
32
- A library owns the layer once you find yourself rebuilding what it does: validation timing and cross-field rules (Form),
33
- caching and refetching (Query), URL as the source of truth (Router), sorting and pagination over rows (Table). Adding the
34
- library mid-project is normal and cheap; unpicking a hand-rolled version later is not.
35
-
36
- ## Docs over memory
37
-
38
- `npx @tanstack/cli` for TanStack docs — never work from memory. Where a project has wired up `@tanstack/intent`, use it to
39
- reach the Agent Skills its TanStack dependencies ship, and read those too.
@@ -1,91 +0,0 @@
1
- # Styling
2
-
3
- **CSS Modules with native CSS by default** — nesting and custom properties are the DRY mechanism; no Sass, no CSS-in-JS, no
4
- utility-class framework. Where the stylesheet lives and who owns one is [components.md](components.md).
5
-
6
- - **Nest pseudo-elements, states, and child selectors under the root** so a selector prefix is written once:
7
-
8
- ```css
9
- .root {
10
- transition: clip-path var(--ease);
11
- &:focus-visible {
12
- outline: var(--imf-ui-border-size-2) solid var(--imf-ui-color-fg-support);
13
- }
14
- &[data-disabled] {
15
- opacity: 0.5;
16
- }
17
- }
18
- ```
19
-
20
- - **Lift a repeated literal into a custom property** (an easing, a colour-math result) and reference it. Custom properties
21
- also carry per-state/per-variant values down the tree — the parameterisation a mixin would provide, done natively.
22
- - Don't "merge" rules that only look similar. Structurally different output (three distinct `clip-path` polygons) isn't
23
- repetition a mixin can remove. Keep it explicit.
24
-
25
- ## Build custom UI from tokens
26
-
27
- Anything you build that the library doesn't cover — a stat widget, a custom panel — uses `--imf-ui-*` variables for color,
28
- spacing, radius, and type instead of hardcoded values. That's what makes custom UI look native next to library components,
29
- and what keeps it correct when the theme changes. A hex code or a magic `px` next to a concept the tokens already name is a
30
- defect.
31
-
32
- Geometry that can't be a token (a clip-path percentage, a hairline `1px`) lives in a named custom property at the top of the
33
- stylesheet, with the invariant written next to it. Don't silently approximate to the nearest token.
34
-
35
- ## Override through the sanctioned seams
36
-
37
- All library styles live in the `imf-ui.components` CSS layer, so **any plain selector you write wins** — that's the whole
38
- override contract:
39
-
40
- - Target the stable hooks: `data-imf-ui-component` attributes and your own classes/wrappers.
41
- - Never target the library's internal class names — they are generated and change without notice.
42
- - Never `!important` — if you think you need it, you're targeting the wrong thing.
43
- - **Style against state via data attributes** (`[data-checked]`, `[data-disabled]`, `[data-popup-open]`) — components expose
44
- their state there, so you never maintain your own state classes. `className` is purely a styling surface:
45
-
46
- ```css
47
- [data-imf-ui-component="Switch"][data-checked] {
48
- outline: 2px solid var(--imf-ui-color-bg-positive);
49
- }
50
- ```
51
-
52
- ## The color system
53
-
54
- The color roles you can name: **surfaces** (`main` the canvas, `support` panels, `minor` popovers), **brand** (identity, full
55
- saturation) vs **primary** (contrast-tuned, CTAs — distinct roles on purpose), **status** (`negative`, `warning`, `positive`,
56
- `info`), and three **accent** slots.
57
-
58
- Two layers:
59
-
60
- - **Controls** are the 80/20 customization surface — override one and every derived token shifts:
61
-
62
- ```css
63
- :root {
64
- --imf-ui-color-primary-hue: 30; /* every primary semantic token follows */
65
- }
66
- ```
67
-
68
- - **Semantic tokens** are what you consume in your own CSS: `--imf-ui-color-bg-{name}` per role, a flat
69
- `--imf-ui-color-fg-{main|support|minor|oncolor|…}` ladder for text. Borders and rings draw from the `fg-*` space.
70
-
71
- Rules of thumb: if you push a hue control into a pale corner, you own overriding the matching `fg-*` token; the color scheme
72
- is `<html data-imf-ui-color-scheme="light|dark">` (the provider sets it) — scheme-specific styling selects via that
73
- attribute.
74
-
75
- ## Responsive styling
76
-
77
- - **Tune components per breakpoint by overriding their `--imf-ui-*` variables inside your own media queries** — custom
78
- properties cross the `@media` boundary, props don't. That's why components expose sizing as variables instead of a `width`
79
- prop:
80
-
81
- ```css
82
- @media (min-width: 768px) {
83
- .my-shell {
84
- --imf-ui-appshell-navbar-width: 22rem;
85
- }
86
- }
87
- ```
88
-
89
- - **Write literal breakpoint values.** The library's `@custom-media` aliases are build-internal and don't ship.
90
- - **JS only for behaviour** (render a burger menu on mobile): `useMediaQuery(minWidth("md"))`. Never for styling CSS can do.
91
- - **No responsive props.** `size={{ sm: … }}` objects are deliberately not offered — write the media query.
@@ -1,18 +0,0 @@
1
- # Testing
2
-
3
- Test the **decisions**, not the rendering.
4
-
5
- - **Pure logic gets unit tests** — pricing, permissions, date math, parsing, reducers. These are cheap, fast, and the place
6
- bugs actually hide.
7
- - **Presentational components generally don't.** A component that maps props onto web-ui primitives has no logic of its own;
8
- asserting that it rendered a `<Button>` tests React, not your code.
9
- - **Behaviour a user performs gets an interaction test** — a form that validates, a flow with steps. Test it through the
10
- interface the user has (roles, labels, visible text), not through internals.
11
- - **Extract the decision out of a hook and test that.** A hook whose interesting part is a plain function is easier to test
12
- as a plain function than through a render harness. A hook that only wraps a browser API has no decision to extract.
13
-
14
- The data layer has its own recipe — stub `fetch`, run the real query client — in [data.md](data.md). A schema whose
15
- constraints or transforms encode product behavior gets focused accepted, rejected, and transformed cases; see
16
- [validation.md](validation.md).
17
-
18
- The measure isn't coverage percentage. It's whether a test failing tells you something you didn't already know.
@@ -1,76 +0,0 @@
1
- # Tooling config
2
-
3
- The config baselines for the tools in [stack.md](stack.md). Prefer typed config (`.ts` over `.json`) where the tool supports
4
- it.
5
-
6
- ## Prettier
7
-
8
- Config file shape is free (`.prettierrc`, `prettier.config.ts`); the values are not:
9
-
10
- ```ts
11
- import type { Config } from "prettier";
12
-
13
- const config: Config = {
14
- printWidth: 125,
15
- tabWidth: 2,
16
- useTabs: false,
17
- trailingComma: "none",
18
- arrowParens: "avoid",
19
- semi: true,
20
- singleQuote: false,
21
- proseWrap: "always"
22
- };
23
- ```
24
-
25
- No config file means Prettier runs on defaults — the values silently differ.
26
-
27
- ## ESLint
28
-
29
- Flat config (`eslint.config.ts`):
30
-
31
- - `strictTypeChecked` + `stylisticTypeChecked`, `projectService: true`
32
- - `as` and `!` banned outside tests (`consistent-type-assertions`)
33
- - `.gitignore` as the ignore source (`includeIgnoreFile` from `@eslint/compat`) — ignores aren't maintained twice
34
- - The `#/` alias enforced via `no-restricted-imports` banning the `../*` pattern — parent-relative paths error, siblings
35
- (`./`) stay relative ([project-structure.md](project-structure.md))
36
-
37
- ## tsconfig
38
-
39
- ```jsonc
40
- {
41
- "compilerOptions": {
42
- "strict": true,
43
- "moduleResolution": "bundler",
44
- "verbatimModuleSyntax": true, // import type stays import type
45
- "noUnusedLocals": true,
46
- "noUnusedParameters": true,
47
- "noFallthroughCasesInSwitch": true,
48
- "noUncheckedSideEffectImports": true,
49
- "skipLibCheck": true,
50
- "paths": { "#/*": ["./src/*"] }
51
- }
52
- }
53
- ```
54
-
55
- The `#/` → `src/` alias and its rationale: [project-structure.md](project-structure.md).
56
-
57
- ## Staged files
58
-
59
- Runner config (lint-staged or nano-staged) applying eslint `--fix` and prettier `--write` to staged files only.
60
-
61
- ## CSS class names
62
-
63
- Generated CSS Module class names are readable in the DOM, `{prefix}-{file}-{local}`, never the default hash. A legible DOM is
64
- what makes devtools and browser automation usable. The library ships the plugin that produces them; pass your own short,
65
- app-scoped prefix:
66
-
67
- ```ts
68
- import { readableCssModuleNames } from "@imfusion/web-ui/build/vite-css-module-names";
69
-
70
- export default defineConfig({
71
- plugins: [react(), readableCssModuleNames({ prefix: "acme" })]
72
- });
73
- ```
74
-
75
- One pattern for dev, Storybook, and production. Register the plugin in **every** tool that compiles the CSS — a compiler left
76
- out generates different names for the same source file, and its styles silently don't apply.
@@ -1,88 +0,0 @@
1
- # Validation
2
-
3
- Runtime validation belongs where data crosses from an untrusted representation into frontend-owned values. The schema is the
4
- single source of truth for both the runtime check and the TypeScript type.
5
-
6
- ## Boundaries
7
-
8
- Validate once, at the edge:
9
-
10
- - network responses when they enter the frontend;
11
- - URL path and search parameters before business logic uses them;
12
- - persisted browser data when it is read;
13
- - user input when it becomes a submitted domain value or request value.
14
-
15
- Code inside that boundary receives parsed values and does not repeat defensive shape checks. An outgoing request built from
16
- an already parsed domain value is serialized, not validated a second time. A typed client returning a handwritten generic is
17
- not validation: it only asserts that an untrusted response has the requested type.
18
-
19
- ## Schema first, type derived
20
-
21
- Use Zod for the runtime schema and derive the type with `z.infer`:
22
-
23
- ```ts
24
- import { z } from "zod";
25
-
26
- export const userSchema = z.object({
27
- id: z.string(),
28
- email: z.email()
29
- });
30
-
31
- export type User = z.infer<typeof userSchema>;
32
- ```
33
-
34
- Do not maintain a handwritten `User` beside `userSchema`. Request and response shapes follow the same rule when the frontend
35
- owns or consumes their runtime representation.
36
-
37
- ## TanStack Router search params
38
-
39
- TanStack Router v1 accepts a Zod v4 schema directly in `validateSearch`; no adapter or parsing wrapper is needed:
40
-
41
- ```tsx
42
- import { createFileRoute } from "@tanstack/react-router";
43
- import { z } from "zod";
44
-
45
- const searchSchema = z.object({
46
- page: z.number().int().positive().catch(1),
47
- filter: z.string().catch("")
48
- });
49
-
50
- type UserSearch = z.infer<typeof searchSchema>;
51
-
52
- export const Route = createFileRoute("/users")({
53
- validateSearch: searchSchema,
54
- component: Users
55
- });
56
-
57
- function Users() {
58
- const search = Route.useSearch();
59
- return <UserList page={search.page} filter={search.filter} />;
60
- }
61
- ```
62
-
63
- `Route.useSearch()` returns `UserSearch` by inference from `validateSearch`. Use `.catch()` when malformed URL input should
64
- fall back without interrupting navigation. Use `.default()` only when a missing value gets a default while malformed values
65
- should still follow the route's validation error path.
66
-
67
- ## Failure handling
68
-
69
- Use `schema.parse(value)` when invalid data is a contract failure that should follow the normal error path, such as a
70
- malformed backend response reaching a route error boundary. Use `schema.safeParse(value)` when failure is expected and the
71
- caller must render or otherwise handle validation issues, such as submitted user input.
72
-
73
- Transforms and coercion belong in the boundary schema when they are part of entering the domain. Do not scatter trimming,
74
- number conversion, or defaulting through downstream components.
75
-
76
- ## Audit
77
-
78
- A boundary is aligned when:
79
-
80
- - the untrusted source is represented as `unknown` until parsed;
81
- - a Zod schema parses it at the point of entry;
82
- - exported TypeScript types use `z.infer<typeof schema>`;
83
- - downstream code consumes the parsed value without duplicate checks or assertions;
84
- - parse failures reach the intended error or user-feedback path;
85
- - behavior-changing schemas have focused tests for accepted, rejected, and transformed values.
86
-
87
- TanStack Query placement is in [data.md](data.md), general type derivation in [typescript.md](typescript.md), URL and form
88
- ownership in [react.md](react.md), and test selection in [testing.md](testing.md).
@@ -1,66 +0,0 @@
1
- ---
2
- name: imf-web-ui-frontend-setup
3
- description:
4
- "Assess an ImFusion frontend against the project baseline and write a reviewable FRONTEND_SETUP_REPORT.md: new-project
5
- setup needs, existing-project gaps, alignment migrations, or one named topic (for example data, lifecycle hooks, CSS class
6
- names, or Prettier). Covers stack, package scripts, tooling, verification, project shape, data, docs, and agent wiring.
7
- House conventions, not industry standards. This skill inspects and plans; approved implementation is a separate task. Not
8
- for wiring the library itself (imf-web-ui-library-setup)."
9
- argument-hint: "[new|audit|align|<topic>]"
10
- allowed-tools: Read Glob Grep
11
- ---
12
-
13
- # imf-web-ui-frontend-setup
14
-
15
- You are the frontend setup auditor. Investigate the repository statically, record every supported conclusion in
16
- `FRONTEND_SETUP_REPORT.md`, then stop for human review. You must follow the applicable `imf-web-ui-frontend-conventions`
17
- references, cite repository evidence, distinguish defects from working deviations, and never present the baseline as
18
- universal best practice.
19
-
20
- ## Workflow
21
-
22
- 1. Resolve the requested mode and scope.
23
- 2. Read every applicable convention reference below. For agent tooling, also read the vendored
24
- `imf-web-ui-agent-setup/SKILL.md` and its templates.
25
- 3. Inspect the repository and write or refresh `FRONTEND_SETUP_REPORT.md` from
26
- [`templates/FRONTEND_SETUP_REPORT.md`](templates/FRONTEND_SETUP_REPORT.md). The template is the report contract. Preserve
27
- everything under `## Reviewer notes` verbatim.
28
- 4. Return the report path and a short verdict. Change nothing else; implementation is a separate, approved task.
29
-
30
- ## Safety
31
-
32
- Use only static inspection: Read, Glob, Grep, and equivalent non-executing search tools. Do not use a shell or invoke Node,
33
- npm, npx, package scripts, hooks, config imports, linters, tests, builds, Git commands, or project binaries. Read config as
34
- text and report runtime or machine-local state that cannot be established statically as unverified.
35
-
36
- Only `FRONTEND_SETUP_REPORT.md` may be written. Write is intentionally not pre-approved in `allowed-tools`; the report
37
- follows the host's ordinary write approval. Host-managed hooks may run after that write; the skill neither invokes nor
38
- suppresses them, but it does report broken or unexpected hook behavior found during static inspection.
39
-
40
- ## Modes
41
-
42
- - **New** — record what exists and what setup work is needed.
43
- - **Audit** — report broken and missing pieces. A working project convention wins; differences are deviations, not defects.
44
- - **Align** — use the same evidence, but make deviations explicit migration proposals.
45
- - **One topic** — resolve any other argument to matching rows below and assess only those. If none match, list the available
46
- rows instead of guessing or widening scope.
47
-
48
- ## References
49
-
50
- | Reference | Assess |
51
- | --------------------------------- | ------------------------------------------------------------------------- |
52
- | `stack.md` | dependencies, dead-code detection, and matching devtools |
53
- | `npm-project.md` | package metadata, scripts, pins, npm and Node config |
54
- | `tooling.md` | Prettier, ESLint, TypeScript, staged files, CSS class names |
55
- | `git.md` | tracked hooks, verification scopes, staleness wiring |
56
- | `project-structure.md` | source tree, naming, imports |
57
- | `components.md` | component folders and colocation |
58
- | `styling.md` | CSS Modules, tokens, prohibited styling systems |
59
- | `validation.md` | runtime schemas, boundary parsing, derived types |
60
- | `data.md` | transport, query/mutation options, keys, invalidation |
61
- | `docs-structure.md` | README, AGENTS, docs index and content boundaries |
62
- | `imf-web-ui-agent-setup/SKILL.md` | installed skills, AGENTS fence, lifecycle hooks, registrations, staleness |
63
-
64
- Knip is required for dead-code detection. `eslint-plugin-jsx-a11y` remains a recommendation when the project is user-facing;
65
- its absence is not a finding. CI, env and secrets, error tracking, deploy, dependency updates, and library wiring are out of
66
- scope.
@@ -1,27 +0,0 @@
1
- # <App name>
2
-
3
- <One paragraph: what the app is, the stack in one line — e.g. "Vite + React 19 + TanStack Router (file-based, CSR) + TanStack
4
- Query + @imfusion/web-ui".>
5
-
6
- ## Install
7
-
8
- ```bash
9
- npm install
10
- npm run git:config # once per clone: hooks path, pull.rebase, merge.ff
11
- ```
12
-
13
- <Registry tokens, required services, or other one-time setup. Delete if none.>
14
-
15
- ## Usage
16
-
17
- ```bash
18
- npm run dev
19
- ```
20
-
21
- <Environment specifics: proxies, .env files, ports. Delete if none.>
22
-
23
- `npm run` lists every script; `package.json` is the source of truth. `verify:full` is the CI gate.
24
-
25
- ## Docs
26
-
27
- [`docs/index.md`](./docs/index.md) registers them.
@@ -1,36 +0,0 @@
1
- ---
2
- name: imf-web-ui-library-setup
3
- description:
4
- "One-time wiring of a consumer project: the @imfusion/web-ui styles import and WebUIProvider wrapper. Load when installing
5
- the library for the first time, or when components render unstyled or without theme context."
6
- ---
7
-
8
- # imf-web-ui-library-setup
9
-
10
- This is library wiring: the styles import and the provider. It applies to anyone using `@imfusion/web-ui`.
11
-
12
- If the project is an **ImFusion** frontend and this is first-time setup, mention once that `imf-web-ui-frontend-setup` sets
13
- up or audits the repo's tooling (formatting, linting, hooks, scripts) against the ImFusion baseline, and let the human
14
- decide. Offer it; never run it uninvited, and don't raise it again if they pass — the library works fine without any of it.
15
-
16
- Every consumer entry point needs exactly two lines, in this order:
17
-
18
- ```tsx
19
- import "@imfusion/web-ui/styles.css";
20
- import { WebUIProvider, Button } from "@imfusion/web-ui";
21
- ```
22
-
23
- Wrap the app root in `<WebUIProvider>` once. Components rendered outside it won't have the theme/CSS-variable context they
24
- expect.
25
-
26
- Never import a Base UI (or other upstream) stylesheet or component directly — everything a web-ui component needs is already
27
- inside `styles.css` and the package's own exports; reaching around web-ui to the upstream library is always wrong, even if
28
- the upstream docs show it that way.
29
-
30
- ## Symptoms of a broken setup
31
-
32
- - **Components render but look unstyled** — the `styles.css` import is missing from the entry point.
33
- - **Components render but ignore the theme (wrong colors, no CSS variables resolving)** — they're mounted outside
34
- `<WebUIProvider>`.
35
- - **An integration component throws on import** — its optional peer dependency isn't installed; check the component's
36
- description in the docgen index (`imf-web-ui-components`) for which peer to add to your `package.json`.