{"id":47025978,"url":"https://github.com/bghcore/formosaic","last_synced_at":"2026-04-01T19:59:08.288Z","repository":{"id":343791129,"uuid":"1179138637","full_name":"bghcore/formosaic","owner":"bghcore","description":"Configuration-driven React forms with a built-in rules engine. 12 UI adapter packages, visual designer, and 20 condition operators.","archived":false,"fork":false,"pushed_at":"2026-03-18T23:07:23.000Z","size":4161,"stargazers_count":0,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-19T06:27:59.933Z","etag":null,"topics":["ant-design","chakra-ui","component-library","configuration-driven","drag-and-drop","fluent-ui","form-builder","form-validation","forms","json-schema","mantine","material-ui","monorepo","radix-ui","react","react-aria","react-hook-form","rules-engine","typescript"],"latest_commit_sha":null,"homepage":"https://bghcore.github.io/formosaic/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bghcore.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING_ADAPTER.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-11T18:17:35.000Z","updated_at":"2026-03-18T23:07:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bghcore/formosaic","commit_stats":null,"previous_names":["bghcore/formosaic"],"tags_count":147,"template":false,"template_full_name":null,"purl":"pkg:github/bghcore/formosaic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bghcore%2Fformosaic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bghcore%2Fformosaic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bghcore%2Fformosaic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bghcore%2Fformosaic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bghcore","download_url":"https://codeload.github.com/bghcore/formosaic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bghcore%2Fformosaic/sbom","scorecard":{"id":1244765,"data":{"date":"2026-03-11T22:36:40Z","repo":{"name":"github.com/bghcore/formosaic","commit":"73f368f605f6afa32fdb950ddd5044a1a08b998a"},"scorecard":{"version":"v5.3.0","commit":"c22063e786c11f9dd714d777a687ff7c4599b600"},"score":6.2,"checks":[{"name":"CI-Tests","score":-1,"reason":"no pull request found","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#ci-tests"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#code-review"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dependency-update-tool"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dangerous-workflow"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#security-policy"}},{"name":"Maintained","score":0,"reason":"project was created within the last 90 days. Please review its contents carefully","details":["Warn: Repository was created within the last 90 days."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":9,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yml:19","Info: jobLevel 'contents' permission set to 'read': .github/workflows/publish.yml:54","Info: jobLevel 'contents' permission set to 'read': .github/workflows/publish.yml:142","Warn: jobLevel 'contents' permission set to 'write': .github/workflows/publish.yml:172","Info: found token with 'none' permissions: .github/workflows/ci.yml:1","Warn: topLevel 'security-events' permission set to 'write': .github/workflows/codeql.yml:12","Info: topLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:13","Info: topLevel 'contents' permission set to 'read': .github/workflows/pages.yml:9","Info: found token with 'none' permissions: .github/workflows/publish.yml:1","Info: topLevel permissions set to 'read-all': .github/workflows/scorecard.yml:9"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":8,"reason":"dependency not pinned by hash detected -- score normalized to 8","details":["Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:48","Warn: npmCommand not pinned by hash: .github/workflows/publish.yml:197","Info:  21 out of  21 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   1 third-party GitHubAction dependencies pinned","Info:   3 out of   5 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":10,"reason":"SAST tool detected: CodeQL","details":["Info: SAST configuration detected: CodeQL","Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#sast"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#signed-releases"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#branch-protection"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/publish.yml:167"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#packaging"}},{"name":"Contributors","score":3,"reason":"project has 1 contributing companies or organizations -- score normalized to 3","details":["Info: found contributions from: microsoft"],"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#contributors"}},{"name":"Vulnerabilities","score":6,"reason":"4 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-5f7q-jpqc-wp7h","Warn: Project is vulnerable to: GHSA-36jr-mh4h-2g58","Warn: Project is vulnerable to: GHSA-8gw3-rxh4-v6jx","Warn: Project is vulnerable to: GHSA-jc85-fpwf-qm7x"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2026-03-12T03:55:05.748Z","repository_id":343791129,"created_at":"2026-03-12T03:55:05.749Z","updated_at":"2026-03-12T03:55:05.749Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291332,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ant-design","chakra-ui","component-library","configuration-driven","drag-and-drop","fluent-ui","form-builder","form-validation","forms","json-schema","mantine","material-ui","monorepo","radix-ui","react","react-aria","react-hook-form","rules-engine","typescript"],"created_at":"2026-03-11T23:04:56.982Z","updated_at":"2026-04-01T19:59:08.279Z","avatar_url":"https://github.com/bghcore.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://formosaic.com\"\u003e\n    \u003cpicture\u003e\n      \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"./docs/public/formosaic-brand.png\" /\u003e\n      \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"./docs/public/formosaic-brand-black.png\" /\u003e\n      \u003cimg src=\"./docs/public/formosaic-brand-black.png\" alt=\"Formosaic\" width=\"360\" /\u003e\n    \u003c/picture\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eConfiguration-driven forms with a built-in rules engine\u003c/strong\u003e\n\u003c/p\u003e\n\n[![CI](https://github.com/bghcore/formosaic/actions/workflows/ci.yml/badge.svg)](https://github.com/bghcore/formosaic/actions/workflows/ci.yml)\n[![Publish](https://github.com/bghcore/formosaic/actions/workflows/publish.yml/badge.svg)](https://github.com/bghcore/formosaic/actions/workflows/publish.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)\n[![npm downloads](https://img.shields.io/npm/dm/@formosaic/core.svg?label=npm%20downloads)](https://www.npmjs.com/package/@formosaic/core)\n[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@formosaic/core?label=core%20gzip)](https://bundlephobia.com/package/@formosaic/core)\n[![Node](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org/)\n[![React](https://img.shields.io/badge/react-19-61dafb.svg?logo=react)](https://react.dev/)\n[![Tests](https://img.shields.io/badge/tests-6%2C296%20passing-brightgreen.svg)](https://github.com/bghcore/formosaic/actions/workflows/ci.yml)\n[![Storybook](https://img.shields.io/badge/storybook-deployed-ff4785.svg?logo=storybook)](https://formosaic.com/storybook/)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/bghcore/formosaic/badge)](https://scorecard.dev/viewer/?uri=github.com/bghcore/formosaic)\n[![CodeQL](https://github.com/bghcore/formosaic/actions/workflows/codeql.yml/badge.svg)](https://github.com/bghcore/formosaic/actions/workflows/codeql.yml)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/bghcore/formosaic/pulls)\n[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/bghcore/formosaic/tree/main/packages/examples?file=src%2Fcheckout%2FApp.tsx)\n\n[![npm core](https://img.shields.io/npm/v/@formosaic/core?label=core)](https://www.npmjs.com/package/@formosaic/core)\n[![npm mui](https://img.shields.io/npm/v/@formosaic/mui?label=mui)](https://www.npmjs.com/package/@formosaic/mui)\n[![npm antd](https://img.shields.io/npm/v/@formosaic/antd?label=antd)](https://www.npmjs.com/package/@formosaic/antd)\n[![npm fluent](https://img.shields.io/npm/v/@formosaic/fluent?label=fluent)](https://www.npmjs.com/package/@formosaic/fluent)\n[![npm mantine](https://img.shields.io/npm/v/@formosaic/mantine?label=mantine)](https://www.npmjs.com/package/@formosaic/mantine)\n[![npm chakra](https://img.shields.io/npm/v/@formosaic/chakra?label=chakra)](https://www.npmjs.com/package/@formosaic/chakra)\n[![npm headless](https://img.shields.io/npm/v/@formosaic/headless?label=headless)](https://www.npmjs.com/package/@formosaic/headless)\n[![npm radix](https://img.shields.io/npm/v/@formosaic/radix?label=radix)](https://www.npmjs.com/package/@formosaic/radix)\n[![npm react-aria](https://img.shields.io/npm/v/@formosaic/react-aria?label=react-aria)](https://www.npmjs.com/package/@formosaic/react-aria)\n[![npm base-web](https://img.shields.io/npm/v/@formosaic/base-web?label=base-web)](https://www.npmjs.com/package/@formosaic/base-web)\n[![npm atlaskit](https://img.shields.io/npm/v/@formosaic/atlaskit?label=atlaskit)](https://www.npmjs.com/package/@formosaic/atlaskit)\n[![npm heroui](https://img.shields.io/npm/v/@formosaic/heroui?label=heroui)](https://www.npmjs.com/package/@formosaic/heroui)\n\n[Storybook](https://formosaic.com/storybook/) | [npm](https://www.npmjs.com/org/formosaic)\n\nFormosaic is a React form library where you define forms as JSON and a built-in rules engine handles the rest. One config object controls field rendering, conditional logic, validation, multi-step wizards, and auto-save. Swap between 12 UI frameworks — MUI, Ant Design, Fluent UI, Chakra, Mantine, Radix, and more — without rewriting form logic.\n\nDefine your forms as a single `IFormConfig` JSON object — field definitions, declarative rules with 20 condition operators, validation, ordering — and the library handles rendering, validation, auto-save, and field interactions automatically.\n\n## When to Use This Library\n\n**Use this if you need:**\n- Forms defined as JSON/config objects, not JSX -- field types, labels, validations, and rules declared as data\n- A rules engine where field A changing to value X makes field B required, field C hidden, and field D's dropdown options change -- all declared, not coded\n- Multi-step wizards with conditional step visibility and cross-step rules\n- Auto-save with debounce, retry, and abort -- not just \"submit on click\"\n- To swap UI libraries (Fluent UI, MUI, headless HTML, custom) without rewriting form logic\n\n**Don't use this if you need:**\n- Simple forms with 3-5 static fields -- use react-hook-form directly\n- Pure JSON Schema rendering with no rules engine -- use [RJSF](https://github.com/rjsf-team/react-jsonschema-form) (but if you want RJSF's schema format with our rules engine, use `fromRjsfSchema()` to migrate)\n- Headless form state with zero opinions -- use [TanStack Form](https://tanstack.com/form)\n\nSee the [comparison guide](https://formosaic.com/guide/comparison) for a detailed comparison with migration paths.\n\n### Feature Comparison\n\n| Feature | Formosaic | FormEngine.io | RJSF | TanStack Form | uniforms |\n|---------|:-----------:|:-------------:|:----:|:-------------:|:--------:|\n| Config-driven (JSON/data) | Yes | Yes | Yes | No | Yes |\n| Declarative rules engine | Yes (20 ops) | Partial | Partial | No | No |\n| UI adapter system | 11 adapters | 4 adapters | 5 themes | Headless | 6 bridges |\n| Computed values | `$values`, `$fn` | MobX-based | No | No | No |\n| Wizard / multi-step | Built-in | Layout-based | Add-on | Manual | No |\n| Visual form builder | No | Yes (paid) | No | No | No |\n| AI form generation | No | ChatGPT GPT | No | No | No |\n| Schema import (JSON/Zod) | Both | Proprietary | JSON Schema | No | JSON Schema |\n| License | MIT | MIT core / Paid builder | Apache 2.0 | MIT | MIT |\n| Bundle (core) | ~114 KB | ~189 KB | ~85 KB | ~12 KB | ~45 KB |\n\n## Packages\n\n| Package | Description | Size |\n|---------|-------------|------|\n| [`@formosaic/core`](./packages/core) | UI-agnostic rules engine, form orchestration, validation, analytics, devtools. React + react-hook-form only, no UI library dependency. | ~114 KB ESM |\n| [`@formosaic/fluent`](./packages/fluent) | Fluent UI v9 field components (27 field types). | ~39 KB ESM |\n| [`@formosaic/mui`](./packages/mui) | Material UI field components (27 field types). | ~39 KB ESM |\n| [`@formosaic/headless`](./packages/headless) | Unstyled semantic HTML field components (27 field types). | ~36 KB ESM |\n| [`@formosaic/antd`](./packages/antd) | Ant Design v5 field components (27 field types). | ~24 KB ESM |\n| [`@formosaic/chakra`](./packages/chakra) | Chakra UI v3 field components (27 field types). | ~24 KB ESM |\n| [`@formosaic/mantine`](./packages/mantine) | Mantine v7 field components (27 field types). | ~24 KB ESM |\n| [`@formosaic/atlaskit`](./packages/atlaskit) | Atlassian Design System field components (27 field types). | ~22 KB ESM |\n| [`@formosaic/base-web`](./packages/base-web) | Uber Base Web field components (27 field types). | ~22 KB ESM |\n| [`@formosaic/heroui`](./packages/heroui) | HeroUI field components (27 field types). | ~22 KB ESM |\n| [`@formosaic/radix`](./packages/radix) | Radix UI primitives field components (27 field types). Unstyled. | ~32 KB ESM |\n| [`@formosaic/react-aria`](./packages/react-aria) | React Aria Components field components (27 field types). | ~31 KB ESM |\n| [`@formosaic/examples`](./packages/examples) | 3 example apps (login+MFA, checkout wizard, data entry). | -- |\n\n## Quick Start\n\n```bash\n# With Fluent UI\nnpm install @formosaic/core @formosaic/fluent\n\n# Or with MUI\nnpm install @formosaic/core @formosaic/mui @mui/material @emotion/react @emotion/styled\n\n# Or headless (no UI framework)\nnpm install @formosaic/core @formosaic/headless\n\n# Or with Ant Design\nnpm install @formosaic/core @formosaic/antd antd dayjs\n\n# Or with Mantine\nnpm install @formosaic/core @formosaic/mantine @mantine/core @mantine/hooks\n\n# Or with Radix UI (unstyled primitives, great for Tailwind/shadcn)\nnpm install @formosaic/core @formosaic/radix @radix-ui/react-checkbox @radix-ui/react-radio-group @radix-ui/react-select @radix-ui/react-slider @radix-ui/react-switch\n\n# Or with React Aria (accessibility-first)\nnpm install @formosaic/core @formosaic/react-aria react-aria-components\n```\n\n```tsx\nimport {\n  RulesEngineProvider,\n  InjectedFieldProvider,\n  Formosaic,\n} from \"@formosaic/core\";\nimport { createFluentFieldRegistry } from \"@formosaic/fluent\";\n// Or: import { createMuiFieldRegistry } from \"@formosaic/mui\";\n// Or: import { createHeadlessFieldRegistry } from \"@formosaic/headless\";\n\nconst formConfig = {\n  version: 2 as const,\n  fields: {\n    name: { type: \"Textbox\", label: \"Name\", required: true },\n    status: {\n      type: \"Dropdown\",\n      label: \"Status\",\n      options: [\n        { value: \"Active\", label: \"Active\" },\n        { value: \"Inactive\", label: \"Inactive\" },\n      ],\n    },\n    notes: { type: \"Textarea\", label: \"Notes\" },\n  },\n  fieldOrder: [\"name\", \"status\", \"notes\"],\n};\n\nfunction App() {\n  return (\n    \u003cRulesEngineProvider\u003e\n      \u003cInjectedFieldProvider injectedFields={createFluentFieldRegistry()}\u003e\n        \u003cFormosaic\n          configName=\"myForm\"\n          formConfig={formConfig}\n          defaultValues={{ name: \"\", status: \"Active\", notes: \"\" }}\n          saveData={async (data) =\u003e {\n            console.log(\"Saving:\", data);\n            return data;\n          }}\n        /\u003e\n      \u003c/InjectedFieldProvider\u003e\n    \u003c/RulesEngineProvider\u003e\n  );\n}\n```\n\n## Try It Live\n\n- [Open in StackBlitz](https://stackblitz.com/github/bghcore/formosaic/tree/main/packages/examples?file=src%2Fcheckout%2FApp.tsx) -- edit and run the checkout wizard example in your browser\n- [Storybook](https://formosaic.com/storybook/) -- interactive component gallery with all 27 field types across 11 adapters\n\n## How It Works\n\n### Configuration-Driven Forms\n\nEvery form is defined by an `IFormConfig` object containing a dictionary of `IFieldConfig` entries. Each config specifies:\n\n- **`type`** -- Which field type to render (`\"Textbox\"`, `\"Dropdown\"`, `\"Toggle\"`, etc.)\n- **`label`** -- Display label\n- **`required`** / **`hidden`** / **`readOnly`** -- Default field states\n- **`rules`** -- Declarative rules with rich conditions (`when`/`then`/`else`) that change field states based on other field values\n- **`options`** -- Dropdown/select options as `{ value, label }` pairs\n- **`validate`** -- Validation rules referencing the unified validator registry\n- **`computedValue`** -- Expressions like `\"$values.qty * $values.price\"` or `\"$fn.calculateTotal()\"`\n- **`items`** -- Field array item definitions (full `IFieldConfig` per item field)\n- **`config`** -- Arbitrary metadata passed through to the field component\n\n### Business Rules Engine\n\nRules are **declarative** -- defined as `IRule[]` on each field config, not imperative code. Each rule has a `when` condition, a `then` effect, and an optional `else` effect.\n\nWhen a field value changes, the engine:\n\n1. Identifies transitively affected fields via the dependency graph\n2. Re-evaluates rules for affected fields only (incremental evaluation)\n3. Resolves conflicts via priority (higher priority rule wins)\n4. Applies effects (required, hidden, readOnly, component swap, options, validation, computed value, setValue)\n5. Dispatches to the rules engine reducer for React re-render\n\nThe engine includes **circular dependency detection** via Kahn's algorithm and **config validation** for dev-mode diagnostics.\n\n**20 condition operators:** `equals`, `notEquals`, `greaterThan`, `lessThan`, `greaterThanOrEqual`, `lessThanOrEqual`, `contains`, `notContains`, `startsWith`, `endsWith`, `in`, `notIn`, `isEmpty`, `isNotEmpty`, `matches`, `arrayContains`, `arrayNotContains`, `arrayLengthEquals`, `arrayLengthGreaterThan`, `arrayLengthLessThan`\n\n**Logical operators:** `and`, `or`, `not` (composable condition trees)\n\n```tsx\nconst formConfig = {\n  version: 2 as const,\n  fields: {\n    type: {\n      type: \"Dropdown\",\n      label: \"Type\",\n      options: [\n        { value: \"bug\", label: \"Bug\" },\n        { value: \"feature\", label: \"Feature\" },\n      ],\n      rules: [\n        {\n          when: { field: \"type\", operator: \"equals\", value: \"bug\" },\n          then: { severity: { required: true, hidden: false } },\n          else: { severity: { hidden: true } },\n          priority: 1,\n        },\n      ],\n    },\n    severity: {\n      type: \"Dropdown\",\n      label: \"Severity\",\n      hidden: true,\n      options: [\n        { value: \"low\", label: \"Low\" },\n        { value: \"high\", label: \"High\" },\n      ],\n    },\n  },\n  fieldOrder: [\"type\", \"severity\"],\n};\n```\n\n#### Compound Conditions\n\nCombine conditions with `and`, `or`, and `not`:\n\n```tsx\nrules: [\n  {\n    when: {\n      operator: \"and\",\n      conditions: [\n        { field: \"type\", operator: \"equals\", value: \"bug\" },\n        { field: \"priority\", operator: \"greaterThanOrEqual\", value: 3 },\n      ],\n    },\n    then: { assignee: { required: true } },\n  },\n]\n```\n\n#### Computed Values\n\nUse `computedValue` with `$values`, `$fn`, and `$parent` expressions:\n\n```tsx\nfields: {\n  qty: { type: \"Number\", label: \"Quantity\" },\n  price: { type: \"Number\", label: \"Unit Price\" },\n  total: {\n    type: \"ReadOnly\",\n    label: \"Total\",\n    computedValue: \"$values.qty * $values.price\",\n  },\n  createdDate: {\n    type: \"ReadOnly\",\n    label: \"Created\",\n    computedValue: \"$fn.setDate()\",\n  },\n}\n```\n\n### Multi-Step Wizard\n\nSplit forms into wizard steps with conditional visibility and per-step validation:\n\n```tsx\nimport { WizardForm } from \"@formosaic/core\";\n\nconst formConfig = {\n  version: 2 as const,\n  fields: { /* ... */ },\n  wizard: {\n    steps: [\n      { id: \"basics\", title: \"Basic Info\", fields: [\"name\", \"type\"] },\n      {\n        id: \"details\",\n        title: \"Details\",\n        fields: [\"severity\", \"description\"],\n        visibleWhen: { field: \"type\", operator: \"equals\", value: \"bug\" },\n      },\n      { id: \"review\", title: \"Review\", fields: [\"notes\"] },\n    ],\n    validateOnStepChange: true,\n  },\n};\n\n\u003cWizardForm\n  wizardConfig={formConfig.wizard}\n  entityData={formValues}\n  renderStepContent={(fields) =\u003e \u003cFieldRenderer fields={fields} /\u003e}\n  renderStepNavigation={({ goNext, goPrev, canGoNext, canGoPrev }) =\u003e (\n    \u003cnav\u003e\n      \u003cbutton onClick={goPrev} disabled={!canGoPrev}\u003eBack\u003c/button\u003e\n      \u003cbutton onClick={goNext} disabled={!canGoNext}\u003eNext\u003c/button\u003e\n    \u003c/nav\u003e\n  )}\n/\u003e\n```\n\nAll fields stay in a single `react-hook-form` context. Steps control which fields are visible. Cross-step rules work automatically.\n\n### Field Arrays (Repeating Sections)\n\nAdd \"add another\" patterns for addresses, line items, etc.:\n\n```tsx\nimport { FieldArray } from \"@formosaic/core\";\n\n\u003cFieldArray\n  fieldName=\"contacts\"\n  config={{\n    items: {\n      name: { type: \"Textbox\", label: \"Name\", required: true },\n      email: { type: \"Textbox\", label: \"Email\", validate: [{ name: \"email\" }] },\n    },\n    minItems: 1,\n    maxItems: 5,\n    defaultItem: { name: \"\", email: \"\" },\n  }}\n  renderItem={(fieldNames, index, remove) =\u003e (\n    \u003cdiv key={index}\u003e\n      {/* fieldNames = [\"contacts.0.name\", \"contacts.0.email\"] */}\n      \u003cFieldRenderer fields={fieldNames} /\u003e\n      \u003cbutton onClick={remove}\u003eRemove\u003c/button\u003e\n    \u003c/div\u003e\n  )}\n  renderAddButton={(append, canAdd) =\u003e (\n    \u003cbutton onClick={append} disabled={!canAdd}\u003eAdd Contact\u003c/button\u003e\n  )}\n/\u003e\n```\n\n### Component Injection\n\nThe library uses a component injection system for field rendering. Core provides the orchestration, and UI packages provide the field implementations:\n\n```tsx\n// Use built-in Fluent UI fields\nimport { createFluentFieldRegistry } from \"@formosaic/fluent\";\n\n// Or use MUI fields (swap with one line)\nimport { createMuiFieldRegistry } from \"@formosaic/mui\";\n\n// Or use headless semantic HTML fields\nimport { createHeadlessFieldRegistry } from \"@formosaic/headless\";\n\n// Pass via the injectedFields prop\n\u003cInjectedFieldProvider injectedFields={createFluentFieldRegistry()}\u003e\n\n// Or mix in custom fields\n\u003cInjectedFieldProvider injectedFields={{\n  ...createFluentFieldRegistry(),\n  MyCustomField: \u003cMyCustomField /\u003e,\n}}\u003e\n```\n\n### Pluggable Validation\n\n14 built-in validators plus support for custom sync, async, and cross-field validators via the unified `registerValidators()` API:\n\n```tsx\nimport {\n  registerValidators,\n  createMinLengthValidation,\n  createPatternValidation,\n} from \"@formosaic/core\";\n\n// Register built-in factory validators\nregisterValidators({\n  MinLength5: createMinLengthValidation(5),\n  AlphaOnly: createPatternValidation(/^[a-zA-Z]+$/, \"Letters only\"),\n});\n\n// Add async validators (e.g., server-side uniqueness check)\nregisterValidators({\n  CheckUniqueEmail: async (value, entityData, signal) =\u003e {\n    const response = await fetch(`/api/check-email?email=${value}`, { signal });\n    const { exists } = await response.json();\n    return exists ? \"Email already in use\" : undefined;\n  },\n});\n```\n\nReference validators in field configs:\n\n```tsx\nfields: {\n  email: {\n    type: \"Textbox\",\n    label: \"Email\",\n    validate: [\n      { name: \"email\" },\n      { name: \"CheckUniqueEmail\", async: true, debounceMs: 500 },\n    ],\n  },\n  username: {\n    type: \"Textbox\",\n    label: \"Username\",\n    validate: [\n      { name: \"minLength\", params: { min: 3 } },\n      { name: \"AlphaOnly\" },\n    ],\n  },\n}\n```\n\nBuilt-in validators: `EmailValidation`, `PhoneNumberValidation`, `YearValidation`, `Max150KbValidation`, `Max32KbValidation`, `isValidUrl`, `NoSpecialCharactersValidation`, `CurrencyValidation`, `UniqueInArrayValidation` + factory functions: `createMinLengthValidation`, `createMaxLengthValidation`, `createNumericRangeValidation`, `createPatternValidation`, `createRequiredIfValidation`\n\nUse `registerValidatorMetadata()` to attach human-readable metadata (label, description, parameter schema) to validators for display in tooling or documentation:\n\n```tsx\nimport { registerValidatorMetadata } from \"@formosaic/core\";\n\nregisterValidatorMetadata(\"CheckUniqueEmail\", {\n  label: \"Unique Email\",\n  description: \"Checks that the email address is not already in use\",\n});\n```\n\n### i18n / Localization\n\nAll user-facing strings are localizable:\n\n```tsx\nimport { registerLocale } from \"@formosaic/core\";\n\nregisterLocale({\n  required: \"Obligatoire\",\n  save: \"Sauvegarder\",\n  cancel: \"Annuler\",\n  saving: \"Sauvegarde en cours...\",\n  invalidEmail: \"Adresse e-mail invalide\",\n  // Partial registration -- unspecified keys fall back to English\n});\n```\n\n### Analytics and Telemetry\n\nTrack form lifecycle events via `IAnalyticsCallbacks` in form settings:\n\n```tsx\nconst formConfig: IFormConfig = {\n  version: 2,\n  fields: { /* ... */ },\n  settings: {\n    analytics: {\n      onFieldFocus: (fieldName) =\u003e console.log(\"Focus:\", fieldName),\n      onFieldBlur: (fieldName, timeSpentMs) =\u003e console.log(\"Blur:\", fieldName, timeSpentMs),\n      onFieldChange: (fieldName, oldValue, newValue) =\u003e console.log(\"Change:\", fieldName),\n      onValidationError: (fieldName, errors) =\u003e console.log(\"Validation:\", fieldName, errors),\n      onFormSubmit: (values, durationMs) =\u003e console.log(\"Submit:\", durationMs, \"ms\"),\n      onFormAbandonment: (filledFields, emptyRequired) =\u003e console.log(\"Abandoned:\", emptyRequired),\n      onWizardStepChange: (from, to) =\u003e console.log(\"Step:\", from, \"-\u003e\", to),\n      onRuleTriggered: (event) =\u003e console.log(\"Rule:\", event),\n    },\n  },\n};\n```\n\nThe `useFormAnalytics` hook wraps these callbacks into stable, memoized functions with automatic timing (field focus duration, form completion time).\n\n### FormDevTools\n\nA collapsible dev-only panel with 7 tabs for debugging form state at runtime:\n\n| Tab | Description |\n|-----|-------------|\n| **Rules** | Current runtime state of every field (type, required, hidden, readOnly, active rules) |\n| **Values** | Live JSON dump of all form values |\n| **Errors** | Current validation errors |\n| **Graph** | Text representation of the dependency graph |\n| **Perf** | Per-field render counts, hot field detection, total form renders (via `RenderTracker`) |\n| **Deps** | Sortable dependency table with effect types, cycle detection |\n| **Timeline** | Chronological event log with filtering (via `EventTimeline`) |\n\n```tsx\nimport { FormDevTools } from \"@formosaic/core\";\n\n\u003cFormDevTools\n  configName=\"myForm\"\n  formState={runtimeFormState}\n  formValues={formValues}\n  formErrors={formErrors}\n  dirtyFields={dirtyFields}\n  enabled={process.env.NODE_ENV === \"development\"}\n/\u003e\n```\n\n### Config Validation (Dev Mode)\n\nCatch configuration errors early:\n\n```tsx\nimport { validateFieldConfigs } from \"@formosaic/core\";\n\nconst errors = validateFieldConfigs(fieldConfigs, registeredComponentTypes);\n// Returns: missing dependency targets, unregistered components,\n// unregistered validators, circular dependencies, missing dropdown options\n```\n\n### Error Boundary\n\nEach field is individually wrapped in a `FormErrorBoundary` so a single field crash does not take down the entire form:\n\n```tsx\nimport { FormErrorBoundary } from \"@formosaic/core\";\n\n\u003cFormErrorBoundary\n  fallback={(error, resetErrorBoundary) =\u003e (\n    \u003cdiv\u003e\n      \u003cp\u003eField failed to render: {error.message}\u003c/p\u003e\n      \u003cbutton onClick={resetErrorBoundary}\u003eRetry\u003c/button\u003e\n    \u003c/div\u003e\n  )}\n  onError={(error, errorInfo) =\u003e console.error(\"Field error:\", error)}\n\u003e\n  \u003cMyField /\u003e\n\u003c/FormErrorBoundary\u003e\n```\n\nThis is built into the core rendering pipeline -- you do not need to add it yourself unless you want custom error handling.\n\n### Manual Save vs Auto-Save\n\nBy default, forms auto-save on every field change (debounced). Set `isManualSave={true}` for explicit save control:\n\n```tsx\n// Auto-save (default) -- saves on every field change with debounce\n\u003cFormosaic\n  configName=\"myForm\"\n  formConfig={formConfig}\n  defaultValues={defaultValues}\n  saveData={async (data) =\u003e { await api.save(data); return data; }}\n/\u003e\n\n// Manual save -- shows Save/Cancel buttons, no auto-save\n\u003cFormosaic\n  configName=\"myForm\"\n  formConfig={formConfig}\n  defaultValues={defaultValues}\n  isManualSave={true}\n  saveData={async (data) =\u003e { await api.save(data); return data; }}\n/\u003e\n\n// Manual save with custom button\n\u003cFormosaic\n  isManualSave={true}\n  renderSaveButton={({ onSave, isDirty, isSubmitting }) =\u003e (\n    \u003cbutton onClick={onSave} disabled={!isDirty || isSubmitting}\u003e\n      Save Changes\n    \u003c/button\u003e\n  )}\n  // ... other props\n/\u003e\n```\n\n### Save Reliability\n\nFormosaic includes robust save handling:\n\n- **AbortController** cancels previous in-flight saves when a new save is triggered\n- **Configurable timeout** via `saveTimeoutMs` prop (default 30 seconds)\n- **Retry with exponential backoff** via `maxSaveRetries` prop (default 3 retries)\n\n```tsx\n\u003cFormosaic\n  saveTimeoutMs={15000}   // 15 second timeout\n  maxSaveRetries={5}      // Retry up to 5 times with exponential backoff\n  saveData={async (data) =\u003e { /* ... */ }}\n/\u003e\n```\n\n### Accessibility\n\nBuilt-in accessibility features:\n\n- **Focus trap** in `ConfirmInputsModal` -- Tab key wraps within modal, Escape closes, focus restored on close\n- **Focus-to-first-error** on validation failure -- automatically focuses the first field with an error\n- **ARIA live regions** -- `\u003cdiv role=\"status\" aria-live=\"polite\"\u003e` announces saving/saved/error status to screen readers\n- **aria-label** on filter inputs, **aria-busy** on fields during save\n- **Wizard step announcements** -- screen readers announce \"Step 2 of 4: Details\" on navigation\n\n### Draft Persistence\n\nAuto-save form state to localStorage for recovery after accidental page closures:\n\n```tsx\nimport { useDraftPersistence, useBeforeUnload } from \"@formosaic/core\";\n\nfunction MyForm() {\n  const { isDirty, formValues } = useFormState();\n\n  // Auto-save drafts to localStorage every 5 seconds\n  const { hasDraft, clearDraft } = useDraftPersistence({\n    formId: \"my-form-123\",\n    data: formValues,\n    saveIntervalMs: 5000,\n    enabled: isDirty,\n    storageKeyPrefix: \"myApp\",\n  });\n\n  // Warn user before leaving page with unsaved changes\n  useBeforeUnload(isDirty, \"You have unsaved changes.\");\n\n  return \u003cFormosaic /* ... */ /\u003e;\n}\n```\n\nIncludes `serializeFormState` / `deserializeFormState` utilities for Date-safe JSON round-trips.\n\n### Theming and Customization\n\nCustomize field chrome without replacing components:\n\n```tsx\n// Render props on FieldWrapper\n\u003cFieldWrapper\n  renderLabel={(label, required) =\u003e \u003cMyCustomLabel text={label} isRequired={required} /\u003e}\n  renderError={(error) =\u003e \u003cMyCustomError message={error} /\u003e}\n  renderStatus={(status) =\u003e \u003cMyCustomStatus type={status} /\u003e}\n/\u003e\n```\n\nCSS custom properties for global theming (import optional `styles.css`):\n\n```css\n:root {\n  --formosaic-error-color: #d32f2f;\n  --formosaic-warning-color: #ed6c02;\n  --formosaic-saving-color: #0288d1;\n  --formosaic-label-color: #333;\n  --formosaic-required-color: #d32f2f;\n  --formosaic-border-radius: 4px;\n  --formosaic-field-gap: 12px;\n  --formosaic-font-size: 14px;\n}\n```\n\nForm-level error banner via `formErrors` prop on `Formosaic`:\n\n```tsx\n\u003cFormosaic\n  formErrors={[\"End date must be after start date\"]}\n  /* ... */\n/\u003e\n```\n\n### Headless Adapter\n\nThe headless package renders all 27 field types using native HTML elements with `data-field-type` and `data-field-state` attributes for CSS targeting. No UI framework required.\n\n```tsx\nimport { createHeadlessFieldRegistry } from \"@formosaic/headless\";\nimport \"@formosaic/headless/styles.css\"; // optional minimal styles\n\n\u003cInjectedFieldProvider injectedFields={createHeadlessFieldRegistry()}\u003e\n```\n\nStyle with Tailwind CSS, your own stylesheet, or CSS custom properties:\n\n```css\n[data-field-type=\"Textbox\"] input {\n  @apply w-full rounded-md border border-gray-300 px-3 py-2 text-sm\n         focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200;\n}\n\n[data-field-state=\"error\"] input {\n  @apply border-red-500;\n}\n```\n\nSee the [headless package README](./packages/headless/README.md) for full details.\n\n### SSR / Next.js\n\nAll core components are SSR-safe. Browser-only API access (`localStorage`, `document.activeElement`, `window.addEventListener`) is guarded behind `typeof` checks or confined to `useEffect` callbacks.\n\nFor Next.js App Router, add `\"use client\"` to files containing form components. Server-fetched data can be passed as props across the client boundary.\n\nSee the [SSR / Next.js integration guide](https://formosaic.com/guide/ssr) for full setup instructions covering App Router, Pages Router, draft persistence, lazy loading, and common pitfalls.\n\n### RJSF Schema Import\n\nMigrate from [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form) with zero rewrite. Bring your existing `schema` + `uiSchema` + `formData` and get a full `IFormConfig` with our rules engine layered on top. JSON Schema `dependencies` and `if/then/else` are auto-converted to `IRule[]`.\n\n```tsx\nimport { fromRjsfSchema } from \"@formosaic/core\";\n\n// Your existing RJSF schema\nconst schema = {\n  type: \"object\",\n  properties: {\n    name: { type: \"string\", title: \"Name\", minLength: 1 },\n    age: { type: \"integer\", title: \"Age\", minimum: 0, maximum: 150 },\n    role: { type: \"string\", enum: [\"admin\", \"user\", \"guest\"] },\n    email: { type: \"string\", format: \"email\" },\n  },\n  required: [\"name\"],\n  dependencies: {\n    role: {\n      oneOf: [\n        {\n          properties: {\n            role: { const: \"admin\" },\n            adminCode: { type: \"string\", title: \"Admin Code\" },\n          },\n          required: [\"adminCode\"],\n        },\n      ],\n    },\n  },\n};\n\nconst uiSchema = {\n  age: { \"ui:widget\": \"updown\" },\n  email: { \"ui:placeholder\": \"you@example.com\" },\n  \"ui:order\": [\"name\", \"email\", \"role\", \"age\", \"*\"],\n};\n\n// Convert to IFormConfig -- dependencies become IRule[] automatically\nconst formConfig = fromRjsfSchema(schema, uiSchema, existingFormData);\n// formConfig.fields.adminCode has rules for conditional visibility based on role\n\n// Use directly with Formosaic\n\u003cFormosaic formConfig={formConfig} /* ... */ /\u003e\n```\n\nAlso exports `toRjsfSchema(config)` for converting back to JSON Schema + uiSchema (best-effort, structural fidelity only).\n\n### Zod Schema Import\n\nConvert Zod object schemas to field configs without adding zod as a dependency:\n\n```tsx\nimport { zodSchemaToFieldConfig } from \"@formosaic/core\";\nimport { z } from \"zod\";\n\nconst UserSchema = z.object({\n  name: z.string().min(1),\n  age: z.number().min(0),\n  active: z.boolean(),\n  role: z.enum([\"admin\", \"user\", \"guest\"]),\n  email: z.string().email(),\n  startDate: z.date(),\n  tags: z.array(z.string()),\n});\n\nconst fieldConfigs = zodSchemaToFieldConfig(UserSchema);\n// Maps: ZodString-\u003eTextbox, ZodNumber-\u003eNumber, ZodBoolean-\u003eToggle,\n//       ZodEnum-\u003eDropdown, ZodDate-\u003eDateControl, ZodArray-\u003eMultiselect\n// Detects .email() and .url() checks for automatic validation\n```\n\nNo `zod` peer dependency is required. If you do not use Zod, this function is tree-shaken out of your bundle.\n\n### Lazy Field Registry\n\nLoad field components on demand using React.lazy for bundle optimization:\n\n```tsx\nimport { createLazyFieldRegistry } from \"@formosaic/core\";\n\nconst lazyFields = createLazyFieldRegistry({\n  Textbox: () =\u003e import(\"./fields/HookTextbox\"),\n  Dropdown: () =\u003e import(\"./fields/HookDropdown\"),\n  // Components are loaded only when first rendered\n});\n\n\u003cInjectedFieldProvider injectedFields={lazyFields}\u003e\n```\n\n## Available Field Types\n\nAll 27 field types (21 editable + 6 read-only) are available in the Fluent UI, MUI, and headless adapters:\n\n### Editable Fields\n\n| Component Key | Description |\n|---------------|-------------|\n| `Textbox` | Single-line text input |\n| `Number` | Numeric input with validation |\n| `Toggle` | Boolean toggle switch |\n| `Dropdown` | Single-select dropdown |\n| `Multiselect` | Multi-select dropdown |\n| `DateControl` | Date picker with clear button |\n| `Slider` | Numeric slider |\n\n| `MultiSelectSearch` | Searchable multi-select |\n| `Textarea` | Multiline text with expand-to-modal |\n| `DocumentLinks` | URL link CRUD |\n| `StatusDropdown` | Dropdown with color status indicator |\n| `DynamicFragment` | Hidden field (form state only) |\n| `FieldArray` | Repeating section (add/remove items) |\n| `RadioGroup` | Single-select radio button group |\n| `CheckboxGroup` | Multi-select checkbox group (value: `string[]`) |\n| `Rating` | Star rating input (value: `number`; configurable `max`, `allowHalf`) |\n| `ColorPicker` | Native color picker returning hex string |\n| `Autocomplete` | Searchable single-select with type-ahead |\n| `FileUpload` | File picker (single or multiple); validates size via `config.maxSizeMb` |\n| `DateRange` | Two date inputs (From / To); value: `{ start, end }` ISO strings |\n| `DateTime` | Combined date+time input; value: ISO datetime-local string |\n| `PhoneInput` | Phone input with inline masking (`us`, `international`, `raw` formats) |\n\n### Read-Only Fields\n\n| Component Key | Description |\n|---------------|-------------|\n| `ReadOnly` | Plain text display |\n| `ReadOnlyArray` | Array of strings |\n| `ReadOnlyDateTime` | Formatted date/time |\n| `ReadOnlyCumulativeNumber` | Computed sum of other fields |\n| `ReadOnlyRichText` | Rendered HTML |\n| `ReadOnlyWithButton` | Text with action button |\n\n## Architecture\n\nFormosaic separates **what** a form contains (the `IFormConfig` JSON object) from **how** it renders (UI adapter packages). The core package owns form state (via react-hook-form), evaluates declarative rules to compute field visibility/required/readOnly state, and delegates rendering to pluggable field components registered through the component injection system. This means you can swap your entire UI layer -- from Fluent UI to MUI to headless HTML -- by changing one import.\n\n```\n\u003cRulesEngineProvider\u003e           -- Owns rule state via useReducer (memoized)\n  \u003cInjectedFieldProvider\u003e       -- Component injection registry (memoized)\n    \u003cFormosaic\u003e                -- Form state (react-hook-form), auto-save with retry, rules\n      \u003cFormFields\u003e              -- Renders ordered field list\n        \u003cFormErrorBoundary\u003e     -- Per-field error boundary (crash isolation)\n          \u003cRenderField\u003e         -- Per-field: Controller + component lookup (useMemo)\n            \u003cFieldWrapper\u003e      -- Label, error, saving status (React.memo, render props)\n              \u003cInjectedField /\u003e -- Your UI component via cloneElement\n```\n\n## Building a Custom UI Adapter\n\nSee the [creating an adapter guide](https://formosaic.com/adapters/creating) for a complete guide. The short version:\n\n1. Create field components that accept `IFieldProps\u003cT\u003e`\n2. Build a registry mapping `ComponentTypes` to your field elements\n3. Pass the registry via the `injectedFields` prop on `InjectedFieldProvider`\n\n## Development\n\n```bash\n# Install dependencies\nnpm install --legacy-peer-deps\n\n# Build all packages\nnpm run build\n\n# Build individual packages\nnpm run build:core\nnpm run build:fluent\nnpm run build:mui\nnpm run build:headless\nnpm run build:antd     # Build Ant Design package only\nnpm run build:chakra   # Build Chakra UI package only\nnpm run build:mantine  # Build Mantine package only\nnpm run build:atlaskit # Build Atlaskit package only\nnpm run build:base-web # Build Base Web package only\nnpm run build:heroui     # Build HeroUI package only\nnpm run build:radix      # Build Radix package only\nnpm run build:react-aria # Build React Aria package only\n\n# Run tests\nnpm run test\nnpm run test:watch\nnpm run test:coverage\n\n# Run end-to-end tests\nnpm run test:e2e\n\n# Run benchmarks\nnpm run bench\n\n# Storybook\nnpm run storybook\nnpm run build-storybook\n\n# Clean build output\nnpm run clean\n```\n\n## Project Structure\n\n```\npackages/\n  core/       -- @formosaic/core (React + react-hook-form only)\n  fluent/     -- @formosaic/fluent (Fluent UI v9 adapter, 27 field types)\n  mui/        -- @formosaic/mui (Material UI adapter, 27 field types)\n  headless/   -- @formosaic/headless (semantic HTML adapter, 27 field types)\n  antd/       -- @formosaic/antd (Ant Design v5 adapter, 27 field types)\n  chakra/     -- @formosaic/chakra (Chakra UI v3 adapter, 27 field types)\n  mantine/    -- @formosaic/mantine (Mantine v7 adapter, 27 field types)\n  atlaskit/   -- @formosaic/atlaskit (Atlassian Design System adapter, 27 field types)\n  base-web/   -- @formosaic/base-web (Uber Base Web adapter, 27 field types)\n  heroui/     -- @formosaic/heroui (HeroUI adapter, 27 field types)\n  radix/      -- @formosaic/radix (Radix UI primitives adapter, 27 field types)\n  react-aria/ -- @formosaic/react-aria (React Aria Components adapter, 27 field types)\n  examples/   -- 3 example apps (login+MFA, checkout wizard, data entry)\ne2e/          -- Playwright end-to-end tests\nbenchmarks/   -- Vitest benchmarks for rules engine performance\nstories/      -- Storybook stories for field components\ndocs/         -- MDX docs content (hosted separately at https://formosaic.com/)\ndocs/         -- Internal planning docs (tier1-baseline-report, tier1-patterns, tier2-handoff)\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbghcore%2Fformosaic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbghcore%2Fformosaic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbghcore%2Fformosaic/lists"}