@imfusion/web-ui 0.5.1-dev.15.g0b0aa0c5 → 0.5.1-dev.17.g60f2cb91

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.
@@ -0,0 +1,20 @@
1
+ import { Plugin } from 'vite';
2
+ export interface ReadableCssModuleNamesOptions {
3
+ /** Namespace for every generated class name. Short, stable, app-scoped (`imf-ui`, `acme`). */
4
+ prefix: string;
5
+ }
6
+ /**
7
+ * Generates readable CSS Module class names instead of the default hash, so the DOM
8
+ * stays legible in devtools and browser automation. `button.module.css` yields
9
+ * `{prefix}-button-module-root` — `[name]` is the filename, which keeps its `.module`
10
+ * suffix.
11
+ *
12
+ * Register it in every tool that compiles the CSS (the app build AND Storybook). A
13
+ * compiler left out generates different class names for the same source file and its
14
+ * styles silently don't apply.
15
+ *
16
+ * Defaults to opting into Lightning CSS, which drops `css.modules` options it has no
17
+ * equivalent for (`localsConvention`). Set `css.transformer: "postcss"` to stay on
18
+ * the PostCSS pipeline.
19
+ */
20
+ export declare function readableCssModuleNames({ prefix }: ReadableCssModuleNamesOptions): Plugin;
@@ -0,0 +1,17 @@
1
+ //#region src/build/vite-css-module-names/index.ts
2
+ function e(e, t) {
3
+ let { lightningcss: n, modules: r, transformer: i } = e.css ?? {}, a = n?.cssModules;
4
+ if (!(typeof a == "boolean" || r === !1) && !a?.pattern && !(typeof r == "object" && r.generateScopedName)) return i === "postcss" ? { css: { modules: { generateScopedName: t } } } : { css: {
5
+ transformer: "lightningcss",
6
+ lightningcss: { cssModules: { pattern: t } }
7
+ } };
8
+ }
9
+ function t({ prefix: t }) {
10
+ let n = `${t}-[name]-[local]`;
11
+ return {
12
+ name: "imf-ui:readable-css-module-names",
13
+ config: (t) => e(t, n)
14
+ };
15
+ }
16
+ //#endregion
17
+ export { t as readableCssModuleNames };
package/dist/index.js CHANGED
@@ -301,7 +301,7 @@ function Jn({ value: e, label: t = "Copy", copiedLabel: n = "Copied", variant: i
301
301
  "data-copied": c || void 0,
302
302
  "aria-label": c ? n : t,
303
303
  onClick: () => l(e),
304
- className: r(Kn.root, typeof o == "string" ? o : void 0),
304
+ className: (e) => r(Kn.root, typeof o == "function" ? o(e) : o),
305
305
  children: /* @__PURE__ */ X(u, {
306
306
  size: 15,
307
307
  strokeWidth: 2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imfusion/web-ui",
3
- "version": "0.5.1-dev.15.g0b0aa0c5",
3
+ "version": "0.5.1-dev.17.g60f2cb91",
4
4
  "description": "The official Web UI component library for ImFusion web apps",
5
5
  "author": "ImFusion GmbH",
6
6
  "license": "UNLICENSED",
@@ -30,6 +30,10 @@
30
30
  "types": "./dist/integrations/*/index.d.ts",
31
31
  "import": "./dist/integrations/*.js"
32
32
  },
33
+ "./build/*": {
34
+ "types": "./dist/build/*/index.d.ts",
35
+ "import": "./dist/build/*.js"
36
+ },
33
37
  "./styles.css": "./dist/style.css",
34
38
  "./docgen.json": "./src/docgen/doc.gen.json",
35
39
  "./llms.txt": "./src/llms/llms.gen.txt",
@@ -129,6 +133,7 @@
129
133
  "globals": "15.15.0",
130
134
  "jiti": "2.7.0",
131
135
  "knip": "6.14.2",
136
+ "lightningcss": "1.32.0",
132
137
  "nano-staged": "1.0.2",
133
138
  "playwright": "1.60.0",
134
139
  "prettier": "3.8.3",
@@ -24,21 +24,22 @@ changed until the human approves.
24
24
 
25
25
  Each topic lives in one reference. Read the one whose moment you're in; starting a new feature usually wants several.
26
26
 
27
- | Reference | Covers | Read when |
28
- | ------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------- |
29
- | [library-boundary.md](references/library-boundary.md) | staying behind `@imfusion/web-ui`, wrappers, type derivation, peers | touching anything that renders library components |
30
- | [react.md](references/react.md) | component roles, state kinds and owners, effects discipline | writing new screens, components, or wrappers |
31
- | [components.md](references/components.md) | component anatomy on disk: grouping, folders, colocation | adding a component file |
32
- | [typescript.md](references/typescript.md) | functional style, types, naming | writing any code |
33
- | [styling.md](references/styling.md) | native CSS Modules, tokens, the override contract | writing CSS or styling beyond the defaults |
34
- | [data.md](references/data.md) | `api/`+`http/` shape, Zod boundary, query/mutation patterns | adding an API topic, a fetch, or a mutation |
35
- | [project-structure.md](references/project-structure.md) | the `src/` tree, file naming, imports | adding files rather than editing existing ones |
36
- | [testing.md](references/testing.md) | what's worth testing and what isn't | writing or reviewing tests |
37
- | [stack.md](references/stack.md) | the topic→tool map, when a library owns a layer | choosing or adding any dependency |
38
- | [npm-project.md](references/npm-project.md) | `package.json`, the `verify:*` script set, dependency pinning | running or adding a script, adding a dependency |
39
- | [tooling.md](references/tooling.md) | Prettier, ESLint, tsconfig, staged-files config baselines | touching tool config |
40
- | [git.md](references/git.md) | `git:config`, verify scopes, staleness at commit time | wiring hooks or the commit path |
41
- | [assets.md](references/assets.md) | image formats, the WebP recipe | adding images or other static assets |
42
- | [docs-structure.md](references/docs-structure.md) | what a repo documents, where, how it's written | writing or restructuring repo docs |
27
+ | Reference | Covers | Read when |
28
+ | ------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------- |
29
+ | [library-boundary.md](references/library-boundary.md) | staying behind `@imfusion/web-ui`, wrappers, type derivation, peers | touching anything that renders library components |
30
+ | [react.md](references/react.md) | component roles, state kinds and owners, effects discipline | writing new screens, components, or wrappers |
31
+ | [components.md](references/components.md) | component anatomy on disk: grouping, folders, colocation | adding a component file |
32
+ | [typescript.md](references/typescript.md) | functional style, types, naming | writing any code |
33
+ | [styling.md](references/styling.md) | native CSS Modules, tokens, the override contract | writing CSS or styling beyond the defaults |
34
+ | [class-names.md](references/class-names.md) | CVA variants, `cx`, merging the incoming `className` | writing a component with variants or a `className` prop |
35
+ | [data.md](references/data.md) | `api/`+`http/` shape, Zod boundary, query/mutation patterns | adding an API topic, a fetch, or a mutation |
36
+ | [project-structure.md](references/project-structure.md) | the `src/` tree, file naming, imports | adding files rather than editing existing ones |
37
+ | [testing.md](references/testing.md) | what's worth testing and what isn't | writing or reviewing tests |
38
+ | [stack.md](references/stack.md) | the topic→tool map, when a library owns a layer | choosing or adding any dependency |
39
+ | [npm-project.md](references/npm-project.md) | `package.json`, the `verify:*` script set, dependency pinning | running or adding a script, adding a dependency |
40
+ | [tooling.md](references/tooling.md) | Prettier, ESLint, tsconfig, staged files, CSS class names | touching tool config |
41
+ | [git.md](references/git.md) | `git:config`, verify scopes, staleness at commit time | wiring hooks or the commit path |
42
+ | [assets.md](references/assets.md) | image formats, the WebP recipe | adding images or other static assets |
43
+ | [docs-structure.md](references/docs-structure.md) | what a repo documents, where, how it's written | writing or restructuring repo docs |
43
44
 
44
45
  `imf-web-ui-frontend-setup` bootstraps and audits a repo against this baseline.
@@ -0,0 +1,42 @@
1
+ # Class names in components
2
+
3
+ How a component turns props into a `className` string. What the build does with the result is [tooling.md](tooling.md); what
4
+ goes in the stylesheet is [styling.md](styling.md).
5
+
6
+ **CVA is the only tool.** `class-variance-authority` maps variant props to CSS Module classes. No `clsx`, no
7
+ `tailwind-merge`, no local `cn()` helper. `cx` is CVA's own concatenator and `@imfusion/web-ui` re-exports it, so it comes
8
+ from the library alongside the components.
9
+
10
+ ```tsx
11
+ import { cva } from "class-variance-authority";
12
+ import classes from "./chip.module.css";
13
+
14
+ const chip = cva(classes.root, {
15
+ variants: {
16
+ appearance: { outline: classes.appearanceOutline, solid: classes.appearanceSolid },
17
+ inline: { false: null, true: classes.inline }
18
+ }
19
+ });
20
+
21
+ export function Chip({ appearance = "outline", inline = false, className, ...props }: Props) {
22
+ return <span {...props} className={chip({ appearance, inline, className })} />;
23
+ }
24
+ ```
25
+
26
+ - **Base class is CVA's first argument**, variant values are CSS Module references, never string literals. A boolean axis
27
+ uses `null` for its off-state.
28
+ - **The incoming `className` goes into CVA's `className` slot**, which appends it last so a caller's class always wins. With
29
+ no variants to map, `cx(classes.inline, className)` does the same job.
30
+ - **Every component that accepts `className` merges it.** A component whose surface is deliberately closed omits the prop
31
+ entirely rather than accepting and ignoring it.
32
+ - **Defaults live in the props destructuring, not CVA's `defaultVariants`.** react-docgen-typescript reads the destructuring,
33
+ so defaults declared in CVA don't reach the generated docs.
34
+ - **Resolve a function-form `className` before merging.** Components built on a library that passes render state
35
+ (`className={state => …}`) receive either shape:
36
+
37
+ ```tsx
38
+ className={state => cx(classes.root, typeof className === "function" ? className(state) : className)}
39
+ ```
40
+
41
+ - **Two components sharing one visual share one CVA module**, a `{name}.cva.ts` next to them, so their variant axes can't
42
+ drift apart.
@@ -58,8 +58,19 @@ The `#/` → `src/` alias and its rationale: [project-structure.md](project-stru
58
58
 
59
59
  Runner config (lint-staged or nano-staged) applying eslint `--fix` and prettier `--write` to staged files only.
60
60
 
61
- ## CSS class names across builds
61
+ ## CSS class names
62
62
 
63
- If more than one tool compiles the CSS (app build plus Storybook), define the generated class-name pattern **once** and
64
- import it in both — otherwise the same source file gets different class names per compiler and styles silently don't apply.
65
- `build/css-modules-config.ts` in web-ui is the reference shape.
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.
@@ -4,9 +4,10 @@ description:
4
4
  "Set up or audit an ImFusion frontend's project tooling: the stack, package.json scripts, formatting, linting, typecheck,
5
5
  staged-file and pre-commit hooks, verification scopes, dependency pinning, tsconfig, docs structure, agent wiring. House
6
6
  conventions, not industry standards. Load when starting a new ImFusion frontend, when asked what an existing one's setup is
7
- missing, or when asked to align a repo with the baseline. Not for adding one config file on request — that's just the edit.
8
- Not for wiring the library itself (imf-web-ui-library-setup)."
9
- argument-hint: "[new|audit|align]"
7
+ missing, when asked to align a repo with the baseline, or when asked to check or set up one named topic from it (e.g. CSS
8
+ class names, Prettier). Not for a bare 'add this config file' request — that's just the edit. Not for wiring the library
9
+ itself (imf-web-ui-library-setup)."
10
+ argument-hint: "[new|audit|align|<topic>]"
10
11
  ---
11
12
 
12
13
  # imf-web-ui-frontend-setup
@@ -16,7 +17,7 @@ First-time setup and audit against the ImFusion frontend baseline. The conventio
16
17
  standards: report findings as "missing against the ImFusion baseline", never "against best practice". Built for ImFusion
17
18
  frontends; anyone else who likes the baseline can run it too.
18
19
 
19
- Three modes, same checklist:
20
+ Four modes, same checklist:
20
21
 
21
22
  - **New project** — work down the checklist and set each piece up.
22
23
  - **Audit** — read the repo (don't ask what it has), report present / missing / broken, change nothing until the human picks.
@@ -24,6 +25,10 @@ Three modes, same checklist:
24
25
  report what's _absent_; a working convention you'd have chosen differently is not a finding.
25
26
  - **Align** — when the user asks to _align_ the repo with the baseline ("align"/"alignment" is the flag), the project-wins
26
27
  guard lifts: deviations become migration findings, proposed as a plan, still nothing changed until approved.
28
+ - **One topic** — any other argument names a topic instead of a mode (`class names`, `prettier`, `tooling`). Resolve it to
29
+ the checklist rows it touches and run the audit process against those only, reading their references as usual. Say which
30
+ rows you resolved it to before reporting, and if nothing matches, say so and list the rows rather than guessing or sweeping
31
+ everything. Same output as an audit: findings, nothing changed until the human picks.
27
32
 
28
33
  **Producer scope.** The web-ui repo itself produces this baseline; it is not a consumer frontend. Consumer-only rows — the
29
34
  AGENTS.md fence, vendored-skill staleness, the app stack and app `src/` tree — don't apply there. Audit it against the shared
@@ -31,19 +36,20 @@ rows only: scripts, tooling, git, docs.
31
36
 
32
37
  ## The checklist
33
38
 
34
- Each row is a reference in `../imf-web-ui-frontend-conventions/references/` — read it, then check the repo against it.
35
-
36
- | Reference | Set up / audit |
37
- | ---------------------- | ---------------------------------------------------------------------------- |
38
- | `stack.md` | the dependencies match the topic→tool map; devtools siblings present |
39
- | `npm-project.md` | script names table, `type`/`private`, exact pins, `.npmrc`, Node pinning |
40
- | `tooling.md` | Prettier values, ESLint flat config, tsconfig, staged-file runner |
41
- | `git.md` | `git:config` run and hooks directory present, verify scopes, staleness hooks |
42
- | `project-structure.md` | the `src/` tree, file naming, `#/` alias wiring |
43
- | `components.md` | component folders and colocation |
44
- | `styling.md` | CSS Modules, tokens, no CSS-in-JS or utility framework |
45
- | `docs-structure.md` | docs shape and content rules (see Docs below) |
46
- | — agent tooling | delegated to `imf-web-ui-agent-setup` (see Agent tooling below) |
39
+ Each row is a reference in `../imf-web-ui-frontend-conventions/references/` — read it, then check the repo against it. A
40
+ topic argument narrows this table to the rows it names; every other mode works down all of it.
41
+
42
+ | Reference | Set up / audit |
43
+ | ---------------------- | ------------------------------------------------------------------------------------------- |
44
+ | `stack.md` | the dependencies match the topic→tool map; devtools siblings present |
45
+ | `npm-project.md` | script names table, `type`/`private`, exact pins, `.npmrc`, Node pinning |
46
+ | `tooling.md` | Prettier values, ESLint flat config, tsconfig, staged-file runner, readable CSS class names |
47
+ | `git.md` | `git:config` run and hooks directory present, verify scopes, staleness hooks |
48
+ | `project-structure.md` | the `src/` tree, file naming, `#/` alias wiring |
49
+ | `components.md` | component folders and colocation |
50
+ | `styling.md` | CSS Modules, tokens, no CSS-in-JS or utility framework |
51
+ | `docs-structure.md` | docs shape and content rules (see Docs below) |
52
+ | — agent tooling | delegated to `imf-web-ui-agent-setup` (see Agent tooling below) |
47
53
 
48
54
  ## Docs
49
55