@imfusion/web-ui 0.6.1-dev.12.ge86ac0a1 → 0.6.1-dev.14.g8fac1dfb
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.
- package/README.md +102 -170
- package/dist/{code-Blo48PGr.js → code-C_56u-Vk.js} +2 -2
- package/dist/{icons-wBmF0U2x.js → icons-Cy1HAosO.js} +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.js +31 -31
- package/dist/integrations/code-highlight.js +2 -2
- package/dist/integrations/image-display-options.js +1 -1
- package/package.json +1 -1
- package/src/llms/install-templates/AGENTS.md +15 -18
- package/src/llms/llms.gen.txt +33 -33
- package/src/llms/skills/imf-web-ui/SKILL.md +29 -39
- package/src/llms/skills/imf-web-ui-audit/SKILL.md +50 -102
- package/src/llms/skills/imf-web-ui-components/SKILL.md +47 -104
- package/src/llms/skills/imf-web-ui-conventions/SKILL.md +39 -52
- package/src/llms/skills/imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md +1 -0
- package/src/llms/skills/imf-web-ui-conventions/topics/agent-tooling.md +40 -62
- package/src/llms/skills/imf-web-ui-conventions/topics/assets.md +11 -12
- package/src/llms/skills/imf-web-ui-conventions/topics/authentication.md +31 -46
- package/src/llms/skills/imf-web-ui-conventions/topics/class-names.md +18 -23
- package/src/llms/skills/imf-web-ui-conventions/topics/components.md +20 -69
- package/src/llms/skills/imf-web-ui-conventions/topics/data.md +50 -146
- package/src/llms/skills/imf-web-ui-conventions/topics/docs-structure.md +17 -23
- package/src/llms/skills/imf-web-ui-conventions/topics/git.md +15 -20
- package/src/llms/skills/imf-web-ui-conventions/topics/library-boundary.md +28 -19
- package/src/llms/skills/imf-web-ui-conventions/topics/library-setup.md +20 -16
- package/src/llms/skills/imf-web-ui-conventions/topics/npm-project.md +28 -42
- package/src/llms/skills/imf-web-ui-conventions/topics/project-structure.md +28 -30
- package/src/llms/skills/imf-web-ui-conventions/topics/react.md +28 -74
- package/src/llms/skills/imf-web-ui-conventions/topics/styling.md +65 -62
- package/src/llms/skills/imf-web-ui-conventions/topics/testing.md +12 -14
- package/src/llms/skills/imf-web-ui-conventions/topics/tokens.md +9 -4
- package/src/llms/skills/imf-web-ui-conventions/topics/tooling.md +40 -68
- package/src/llms/skills/imf-web-ui-conventions/topics/typescript.md +26 -50
- package/src/llms/skills/imf-web-ui-conventions/topics/validation.md +19 -25
- package/src/llms/skills/imf-web-ui-setup/SKILL.md +43 -64
- package/src/llms/skills/imf-web-ui-update/SKILL.md +48 -114
- package/src/llms/skills/imf-web-ui-ux/SKILL.md +64 -92
- package/src/llms/skills/imf-web-ui-ux/references/forms.md +16 -36
- package/src/llms/skills/imf-web-ui-ux/references/usability-heuristics.md +14 -27
- package/src/llms/skills/imf-web-ui-ux/references/visual-design.md +22 -38
|
@@ -1,82 +1,60 @@
|
|
|
1
1
|
# Agent tooling
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
`npx web-ui-install` manages the Web UI skills and optional lifecycle hooks in a consumer project. This topic explains the
|
|
4
|
+
choices around that installer.
|
|
5
5
|
|
|
6
6
|
## The skill bundle
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
Run:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npx web-ui-install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
It installs or refreshes the `imf-web-ui-*` skills, updates the managed `AGENTS.md` fence, and removes skills that the
|
|
15
|
+
package no longer ships. The installer remembers `.claude/skills/`, `.agents/skills/`, or both. Use `--target claude|agents`
|
|
16
|
+
to choose explicitly and `--reconfigure` to choose again.
|
|
17
|
+
|
|
18
|
+
Skill version markers (`.imf-web-ui-skill-version.json`) show which package version installed each skill. Refresh them with
|
|
19
|
+
the installer; do not hand-edit the vendored files.
|
|
14
20
|
|
|
15
21
|
## The hooks
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
entries the installer didn't write.
|
|
22
|
-
|
|
23
|
-
- **SessionStart** — once per session: points the agent at the `imf-web-ui` skills router.
|
|
24
|
-
- **SubagentStart** — the same line, byte for byte, for each spawned subagent; subagents don't reliably inherit the parent
|
|
25
|
-
session's context.
|
|
26
|
-
- **UserPromptSubmit** — one line per prompt naming the companion skills to consult.
|
|
27
|
-
- **Stop** — the verify gate: when the turn edited source files, it blocks the agent from finishing once, with the
|
|
28
|
-
instruction to run the project's verification and fix what it reports. Re-entry is detected from the payload, so the gate
|
|
29
|
-
can never loop; a turn that edited nothing passes untouched.
|
|
30
|
-
|
|
31
|
-
```mermaid
|
|
32
|
-
flowchart LR
|
|
33
|
-
SS(["SessionStart<br/>once per session"]) --> ss["session-start.sh"]
|
|
34
|
-
SA(["SubagentStart<br/>per spawned subagent"]) --> sa["subagent-start.sh"]
|
|
35
|
-
UP(["UserPromptSubmit<br/>every prompt"]) --> up["user-prompt-submit.sh"]
|
|
36
|
-
ST(["Stop<br/>turn ends after edits"]) --> st["stop.sh"]
|
|
37
|
-
ss --> router["injects the router pointer:<br/>start at imf-web-ui"]
|
|
38
|
-
sa --> router
|
|
39
|
-
up --> hints["injects the per-task hints:<br/>components · conventions · ux"]
|
|
40
|
-
st --> gate["blocks once:<br/>run verification first"]
|
|
41
|
-
router --> agent["agent routes to the right<br/>companion skill"]
|
|
42
|
-
hints --> agent
|
|
43
|
-
gate --> agent2["agent verifies,<br/>then finishes"]
|
|
23
|
+
Install the optional lifecycle hooks with:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npx web-ui-install --hooks
|
|
44
27
|
```
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
|
|
56
|
-
|
|
29
|
+
The installer owns the scripts under `.agents/hooks/imf-web-ui/` and merges its registrations into Claude Code and Codex
|
|
30
|
+
settings. It refreshes scripts and removes retired registrations without replacing unrelated entries.
|
|
31
|
+
|
|
32
|
+
The shipped events are:
|
|
33
|
+
|
|
34
|
+
- `SessionStart`: point the agent at the Web UI router once per session.
|
|
35
|
+
- `SubagentStart`: provide the same pointer to spawned agents.
|
|
36
|
+
- `UserPromptSubmit`: name the companion skills relevant to the prompt.
|
|
37
|
+
- `Stop`: ask for project verification after a turn edits source files.
|
|
38
|
+
|
|
39
|
+
Read existing registrations first. If the project already covers an event, do not stack a second hook; adapt the existing
|
|
40
|
+
registration and keep the missing behavior.
|
|
41
|
+
|
|
42
|
+
`baseline-staleness.sh` is a pre-commit check, not an agent hook. It compares installed skill markers with the installed
|
|
43
|
+
package version.
|
|
57
44
|
|
|
58
45
|
## Codex trust
|
|
59
46
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
- Until then hooks are skipped, and skipped silently — from outside, a skipped hook and a hook that ran and said nothing look
|
|
63
|
-
identical.
|
|
64
|
-
- After installing or updating hooks, tell the user to trust the project and review `/hooks` in Codex, and again after any
|
|
65
|
-
hook script changes.
|
|
66
|
-
- When a hint doesn't show up, check trust before debugging the script.
|
|
67
|
-
- Claude Code has no trust gate for project hooks.
|
|
47
|
+
Codex runs project hooks only after the project is trusted and each script is approved through `/hooks`. Revisit that review
|
|
48
|
+
after a hook script changes. Claude Code has no equivalent project-hook trust gate.
|
|
68
49
|
|
|
69
50
|
## Dependency-shipped skills
|
|
70
51
|
|
|
71
|
-
|
|
72
|
-
allowlist.
|
|
52
|
+
Dependencies can ship their own skills. TanStack is one example; its docs and allowlist determine how it is used.
|
|
73
53
|
|
|
74
54
|
## Hook docs
|
|
75
55
|
|
|
76
|
-
|
|
77
|
-
host and per event.
|
|
56
|
+
Read the current host documentation before adding or adapting a hook:
|
|
78
57
|
|
|
79
|
-
- [Claude Code
|
|
80
|
-
|
|
81
|
-
- [Codex
|
|
82
|
-
- [Codex: config reference](https://learn.chatgpt.com/docs/config-file/config-reference) — `[features]` and `[hooks.state]`
|
|
58
|
+
- [Claude Code hooks](https://code.claude.com/docs/en/hooks)
|
|
59
|
+
- [Codex hooks](https://learn.chatgpt.com/docs/hooks)
|
|
60
|
+
- [Codex configuration](https://learn.chatgpt.com/docs/config-file/config-reference)
|
|
@@ -2,26 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## Importing
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Import files from `src/assets/` so the bundler fingerprints and includes them. Do not reference an asset through a
|
|
6
|
+
public-path string.
|
|
7
7
|
|
|
8
|
-
## Photographs
|
|
8
|
+
## Photographs: WebP
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Convert photographs to WebP before adding them:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
11
13
|
magick source.png -resize 2000x -quality 80 -define webp:method=6 src/assets/name.webp
|
|
12
14
|
```
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
- `method=6` — densest encoding; a one-off cost at conversion time, so take the smaller file.
|
|
16
|
-
- Long edge ≤ 2000px — nothing on the market resolves more in a content image.
|
|
17
|
-
- No `<picture>` fallback — WebP is supported everywhere since 2020.
|
|
16
|
+
Keep the long edge at 2000px or less. WebP is supported by the browser floor.
|
|
18
17
|
|
|
19
18
|
## Other formats
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
Use PNG when the image needs alpha or exact pixels. Use an inline SVG component for icons, logos, and line art so it inherits
|
|
21
|
+
`currentColor` and follows the theme.
|
|
23
22
|
|
|
24
23
|
## Scope
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
Apply these rules to assets you add or touch. Existing files do not need a format migration just because they use another
|
|
26
|
+
format.
|
|
@@ -1,65 +1,50 @@
|
|
|
1
1
|
# Authentication
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Use one route boundary for authentication. The provider, session mechanism, endpoint paths, and login/logout transport stay
|
|
4
|
+
owned by the application.
|
|
5
5
|
|
|
6
6
|
## Starter shape
|
|
7
7
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
lib/auth/
|
|
16
|
-
login-url.ts # pure login navigation helper; not an API topic file
|
|
17
|
-
login-url.test.ts # focused helper tests
|
|
18
|
-
routes/
|
|
19
|
-
__root.tsx # global Outlet and error/not-found boundaries; no auth guard
|
|
20
|
-
_public/route.tsx # anonymous route group
|
|
21
|
-
_app/route.tsx # authenticated route group
|
|
22
|
-
```
|
|
8
|
+
- Keep public routes under a pathless `_public/` group.
|
|
9
|
+
- Keep authenticated routes under a pathless `_app/` group.
|
|
10
|
+
- Guard `_app/` before its children render.
|
|
11
|
+
- Keep the root route neutral: it owns the outlet and global error/not-found boundaries.
|
|
12
|
+
- A greenfield app starts `_app/` with a minimal `AppShell` unless it has no persistent authenticated navigation.
|
|
13
|
+
|
|
14
|
+
The file layout is in [project-structure.md](project-structure.md).
|
|
23
15
|
|
|
24
16
|
## One current-user query
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
- `
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
Use one server-backed current-user query as the identity source:
|
|
19
|
+
|
|
20
|
+
- Put it in `api/auth/queries.ts` and define its key and schema with the [data.md](data.md) pattern.
|
|
21
|
+
- Expose query options as `context.api.auth.getUser()`.
|
|
22
|
+
- Call `ensureQueryData` in a loader or a query hook in a component.
|
|
23
|
+
- Keep credentials and access tokens out of React state.
|
|
24
|
+
- Do not infer authentication from local storage, decoded tokens, route flags, or permission-gated UI.
|
|
25
|
+
|
|
26
|
+
The server response, parsed at the network boundary, is the source of truth.
|
|
35
27
|
|
|
36
28
|
## The two route groups
|
|
37
29
|
|
|
38
|
-
Both resolve the current-user query in `beforeLoad
|
|
30
|
+
Both groups may resolve the current-user query in `beforeLoad`. They handle a 401 differently:
|
|
39
31
|
|
|
40
|
-
| Group | 401
|
|
41
|
-
| ---------- |
|
|
42
|
-
| `_app/` |
|
|
43
|
-
| `_public/` |
|
|
32
|
+
| Group | 401 result |
|
|
33
|
+
| ---------- | -------------------------------------------- |
|
|
34
|
+
| `_app/` | Navigate to the server-owned login endpoint. |
|
|
35
|
+
| `_public/` | Continue as an anonymous user. |
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- The root route stays neutral: global `Outlet` and error/not-found boundaries, no public/authenticated decision.
|
|
37
|
+
The `_app/` guard should navigate only for the authentication 401. Rethrow redirects, server failures, connection failures,
|
|
38
|
+
and schema errors so the normal error boundary can handle them. A public route may redirect an already-authenticated user
|
|
39
|
+
into `_app/`.
|
|
49
40
|
|
|
50
41
|
## Login and logout
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- Never hard-code an endpoint shape or provider into shared frontend conventions.
|
|
56
|
-
- No intermediate login route when the server owns the flow.
|
|
57
|
-
- A failed API request is an auth failure only when its typed error is specifically a 401.
|
|
43
|
+
Follow the application's documented server flow. Login is a browser navigation when the server owns the session; it is not a
|
|
44
|
+
Query fetch. Logout uses the server's documented state-changing action. Preserve a validated same-origin return path when the
|
|
45
|
+
server supports one. Do not invent an endpoint shape in this shared baseline.
|
|
58
46
|
|
|
59
47
|
## App shell
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`_app/` outlet.
|
|
64
|
-
- An explicit no-persistent-navigation decision may omit the shell; the `_app/` guard stays.
|
|
65
|
-
- An established project keeps its working choice; public pages may use a small branded header.
|
|
49
|
+
`AppShell` is authenticated chrome, not the authentication mechanism. It can hold the logo, navigation, and session action
|
|
50
|
+
around the `_app/` outlet. An established project keeps its working shell or header choice.
|
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
# Class names in components
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
goes in the stylesheet: [styling.md](styling.md).
|
|
3
|
+
Use CVA to turn design props into CSS Module classes, then merge the caller's `className`.
|
|
5
4
|
|
|
6
5
|
## CVA is the only tool
|
|
7
6
|
|
|
8
|
-
- `class-variance-authority`
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
- Defaults live in the props destructuring, not CVA's `defaultVariants`: react-docgen-typescript reads the destructuring, so
|
|
14
|
-
defaults declared in CVA don't reach the generated docs.
|
|
7
|
+
- Use `class-variance-authority` for variant axes. Do not add `clsx`, `tailwind-merge`, or a local `cn` helper.
|
|
8
|
+
- Import `cx` from `@imfusion/web-ui` when a component only needs concatenation.
|
|
9
|
+
- Pass CSS Module references to CVA, not string class names.
|
|
10
|
+
- Represent a boolean axis with `false: null` and `true: classes.axisOn`.
|
|
11
|
+
- Put defaults in function destructuring. CVA `defaultVariants` does not reach the generated docs.
|
|
15
12
|
|
|
16
13
|
```tsx
|
|
17
|
-
interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
18
|
-
appearance?: "outline" | "solid";
|
|
19
|
-
inline?: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
14
|
const chip = cva(classes.root, {
|
|
23
15
|
variants: {
|
|
24
|
-
appearance: {
|
|
16
|
+
appearance: {
|
|
17
|
+
outline: classes.appearanceOutline,
|
|
18
|
+
solid: classes.appearanceSolid
|
|
19
|
+
},
|
|
25
20
|
inline: { false: null, true: classes.inline }
|
|
26
21
|
}
|
|
27
22
|
});
|
|
@@ -33,18 +28,18 @@ export function Chip({ appearance = "outline", inline = false, className, ...pro
|
|
|
33
28
|
|
|
34
29
|
## Merging `className`
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- Resolve a function-form `className` before merging — components built on a library that passes render state
|
|
41
|
-
(`className={state => …}`) receive either shape:
|
|
31
|
+
A component that accepts `className` must merge it. Put the incoming value in CVA's `className` slot so it is appended after
|
|
32
|
+
the component classes. If there is no CVA configuration, use `cx`.
|
|
33
|
+
|
|
34
|
+
A library render prop may make `className` a function. Resolve it with the render state before merging:
|
|
42
35
|
|
|
43
36
|
```tsx
|
|
44
37
|
className={state => cx(classes.root, typeof className === "function" ? className(state) : className)}
|
|
45
38
|
```
|
|
46
39
|
|
|
40
|
+
A closed component should omit `className`, not accept and ignore it.
|
|
41
|
+
|
|
47
42
|
## Shared CVA modules
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
When two components share the same visual variant axes, put their CVA call in a `{name}.cva.ts` module next to them. This
|
|
45
|
+
keeps the visual contract in one place.
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
# Components
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Keep consumer components small and easy to replace. React roles are described in [react.md](react.md); CSS ownership is in
|
|
4
4
|
[styling.md](styling.md).
|
|
5
5
|
|
|
6
6
|
## As dumb as possible
|
|
7
7
|
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
8
|
+
- Feed components data and callbacks through props.
|
|
9
|
+
- Keep fetching, routing, and business logic in the page or feature layer.
|
|
10
|
+
- Local UI state such as an open state or draft value is fine.
|
|
11
|
+
- Formatting and display-only decisions are fine; owning server state is not.
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
|
-
|
|
15
|
-
interface Props {
|
|
14
|
+
interface UserCardProps {
|
|
16
15
|
name: string;
|
|
17
16
|
onEdit: () => void;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
export function UserCard({ name, onEdit }:
|
|
19
|
+
export function UserCard({ name, onEdit }: UserCardProps) {
|
|
21
20
|
return (
|
|
22
21
|
<Card.Root>
|
|
23
|
-
<Typo>{name}</Typo>
|
|
22
|
+
<Typo.P>{name}</Typo.P>
|
|
24
23
|
<Button onClick={onEdit}>Edit</Button>
|
|
25
24
|
</Card.Root>
|
|
26
25
|
);
|
|
@@ -29,73 +28,25 @@ export function UserCard({ name, onEdit }: Props) {
|
|
|
29
28
|
|
|
30
29
|
## Grouping
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- Flat is fine while there are few; let grouping follow what the project has, don't impose it up front.
|
|
31
|
+
Group by component role, such as layout or domain UI, according to the project structure. Do not create a hierarchy before
|
|
32
|
+
the project needs one.
|
|
35
33
|
|
|
36
34
|
## One file or a folder
|
|
37
35
|
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
- Imports read `#/components/data-table`, so the inside can be restructured without touching call sites.
|
|
36
|
+
A one-file component can stay in its group. A component with parts, styles, or tests gets a folder with an `index.ts` barrel.
|
|
37
|
+
Call sites import the folder path:
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```
|
|
39
|
+
```text
|
|
45
40
|
components/data-table/
|
|
46
|
-
data-table.tsx
|
|
47
|
-
data-table
|
|
48
|
-
data-table.
|
|
49
|
-
|
|
50
|
-
index.ts # re-exports only
|
|
41
|
+
data-table.tsx
|
|
42
|
+
data-table.module.css
|
|
43
|
+
data-table.test.tsx
|
|
44
|
+
index.ts
|
|
51
45
|
```
|
|
52
46
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```tsx
|
|
56
|
-
// data-table.tsx — CVA even with one variant axis, so future axes slot in (class-names.md)
|
|
57
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
58
|
-
import classes from "./data-table.module.css";
|
|
59
|
-
|
|
60
|
-
const dataTable = cva(classes.root, {
|
|
61
|
-
variants: { density: { comfortable: classes.densityComfortable, compact: classes.densityCompact } }
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Intersection, not `interface extends` — VariantProps is an object type. Defaults live in the destructuring.
|
|
65
|
-
type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof dataTable> & { rows: TableRow[] };
|
|
66
|
-
|
|
67
|
-
export function DataTable({ className, density = "comfortable", rows, ...props }: Props) {
|
|
68
|
-
return (
|
|
69
|
-
<div {...props} className={dataTable({ density, className })}>
|
|
70
|
-
{/* … */}
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
```css
|
|
77
|
-
/* data-table.module.css — native nesting + library tokens, no preprocessor, no @layer (styling.md) */
|
|
78
|
-
.root {
|
|
79
|
-
border-radius: var(--imf-ui-border-radius-2);
|
|
80
|
-
font-size: var(--imf-ui-font-size-1);
|
|
81
|
-
|
|
82
|
-
&:focus-visible {
|
|
83
|
-
/* states nest under the root */
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.densityCompact {
|
|
88
|
-
/* one class per CVA variant value */
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
```ts
|
|
93
|
-
// index.ts — re-exports only; callers import #/components/data-table
|
|
94
|
-
export { DataTable } from "./data-table";
|
|
95
|
-
```
|
|
47
|
+
The barrel only re-exports the public pieces.
|
|
96
48
|
|
|
97
49
|
## Colocation
|
|
98
50
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- Tests and local types sit next to their subject: a file hunted for in a parallel tree gets edited less carefully.
|
|
51
|
+
Keep a component's stylesheet, tests, and local types beside the component. A container that needs styling usually wants to
|
|
52
|
+
be a layout or dumb component instead.
|