99-reference/v02-component-migration-plan.md
v02 component migration plan
v02 component migration plan
This file records the migration plan for the v02 component API unification work.
Use this file together with:
99-reference/component-vocabulary.md99-reference/component-api-conventions.md
Why this migration exists
The old v02 component layer had several problems that made app creation and maintenance harder, especially for LLMs:
- too many public call signatures
- positional arguments with different meanings in different component families
- binding-first APIs mixed with props-first APIs
- convenience wrappers that expanded the public API surface
- incomplete public exports in
ui.js - component contracts that were implicit instead of explicit
- weak runtime guidance when a component was called incorrectly
The goal of this migration is to make component code:
- easier to read
- easier to document
- easier to validate
- easier to generate correctly with LLMs
- easier to keep consistent across the framework
What we want to improve
1. One clear public component shape
Public components should use only these canonical forms:
Component({ ...props })
Component({ ...props }, ...children)
Rules:
- leaf and widget components use
Component({ ...props }) - container components use
Component({ ...props }, ...children) - no public positional content arguments
- no public binding-first signatures
- no multiple public overloads for the same component
2. Global vocabulary for props
All components should prefer the shared vocabulary from component-vocabulary.md.
Examples:
textlabeliconvaluecheckedonChangeonClicktonevariantsizedisabledloadingclassclassNamestyle
The framework should reduce local synonyms and one-off prop names.
3. Explicit component contracts
Each migrated component should define an explicit allowedProps object.
Example:
export const textAllowedProps = {
text: { type: "content", required: true },
tone: { type: "string", values: ["text", "muted", "primary", "danger"] },
size: { type: "string", values: ["h1", "h2", "h3", "h4", "h5", "h6", "body", "small", "tiny"] }
}
This helps with:
- LLM readability
- validation
- future documentation generation
- consistent prop naming
4. Shared validation helper
All migrated components should use the shared helper:
import { validateProps } from "../system/ui/props.js"
Pattern:
validateProps("ComponentName", props, allowedProps)
The framework should fail early when a component receives:
- the wrong call shape
- unsupported props
- invalid values for enumerated props
- missing required props
5. Smaller public API surface
Where possible, remove specialized wrappers and use variants instead.
Prefer:
Button({ label: "Save", tone: "primary" })
Button({ label: "Delete", tone: "danger" })
Button({ label: "Cancel", variant: "ghost" })
instead of:
PrimaryButton(...)
DangerButton(...)
GhostButton(...)
6. Public UI entrypoint discipline
Apps should import public components from:
/vroqjs/ui.js
ui.js should stay aligned with the migrated public API surface.
7. Demo app as migration safety net
The demo app in:
demo/r01
is the visual and manual test surface for this migration.
After each framework change:
1. patch the component 2. patch internal dependents if needed 3. patch the demo app 4. reload and test manually 5. update docs
Current migration principles
During this migration:
- do not keep old public signatures once a component is migrated
- prefer explicit contracts over convenience magic
- prefer shared helpers over repeated local validation logic
- patch framework dependents when a migrated component is used internally
- keep runtime stable; most changes should stay in the component layer
Step-by-step migration plan
Step 1 — Define the vocabulary and conventions
Status: done
Completed work:
- created
99-reference/component-vocabulary.md - created
99-reference/component-api-conventions.md - defined the canonical public component forms
- defined the shared prop vocabulary
Step 2 — Create the demo app migration surface
Status: done
Completed work:
- created
demo/r01 - used it as showcase and manual test app
- began patching it to canonical APIs as components were migrated
Step 3 — Create shared validation helper
Status: done
Completed work:
- created
vroqjs/v02/system/ui/props.js - moved
validatePropsinto shared helper code - documented that migrated components must use it
Step 4 — Migrate typography primitives
Status: in progress
Completed work:
- migrated
Text - migrated text helpers to object-first props
- removed old positional public
Text(...)usage - patched demo app
Textcalls
Still to watch:
- patch internal users of
Textwhen they still call it with legacy signatures
Step 5 — Migrate action primitives
Status: in progress
Completed work:
- migrated
Badge - migrated
Button - migrated
IconButton - removed public special button wrappers from
ui.js - patched
Dialogto useButtonvariants instead of special wrappers - patched demo app
BadgeandButtoncalls
Still to watch:
- patch internal users of
ButtonandIconButtonwhen old signatures remain
Step 6 — Migrate segmented selection components
Status: in progress
Completed work:
- migrated
Segmented - patched it to use the new
Button({ ...props })form
Still to watch:
- ensure supporting components allow required event props like
onKeyDown
Step 7 — Migrate controlled input components
Status: done
Completed work:
- migrated
TextInput - migrated
Checkbox - migrated
Tabs - added explicit
allowedProps - added shared
validatePropsusage - patched demo app to canonical controlled APIs
Target APIs:
TextInput({ value, onChange, label, placeholder, helper, error, success })
Checkbox({ checked, onChange, label, description })
Tabs({ value, onChange, items, variant, size, stretch, scroll })
Required work for each component:
- define
allowedProps - call
validateProps - remove legacy binding-first and positional public API
- patch internal users
- patch demo app after migration
- update docs/examples after migration
Step 8 — Migrate remaining interactive components
Status: mostly done
Completed work:
- migrated
ComboBox - migrated
Slider - migrated
Modal - migrated
Toast - migrated
TreeView
Still to evaluate:
DrawerToolbarNavbarContextMenu
Priority rule:
- migrate components with the most confusing signatures first
- migrate components with the largest impact on app code before low-impact utility components
Step 9 — Normalize internal framework usage
Status: ongoing
Every time a component is migrated:
- search framework components for old usage
- patch internal calls to canonical props-first form
- remove reliance on deleted wrappers or positional helper signatures
This is required because the framework itself is a consumer of the public component layer.
Step 10 — Update docs continuously
Status: ongoing
As each component family is migrated:
- update examples to canonical API only
- remove docs that teach old signatures
- keep vocabulary and conventions docs aligned with the real code
- use migrated components in the demo app as canonical examples
Step 11 — Keep the public API small and strict
Status: ongoing
After migration of a family:
- do not add compatibility overloads back
- do not add convenience wrappers unless they clearly reduce cognitive load
- prefer variants and vocabulary over alias components
- keep validation strict enough to catch wrong usage quickly
Step 12 — Final cleanup after major families are migrated
Status: later
After the main component families are migrated:
- review
ui.jsexports - review demo app for remaining old patterns
- review internal components for old calls
- review docs for old examples
- remove dead code or helpers that only existed for old signatures
Current migration status summary
Already migrated:
TextBadgeButtonIconButtonSegmentedTextInputCheckboxTabsComboBoxSliderModalToastTreeViewCodeEditorSchemeDialog
Partially migrated / still needs follow-up:
CardDrawerToolbarNavbarContextMenu
Standard checklist for each migrated component
For every component migration, follow this checklist:
1. read the current component source fully 2. define allowedProps 3. import and use validateProps 4. convert public API to object-first props 5. remove old public signatures 6. patch internal framework callers 7. patch the demo app 8. reload and manually test 9. update docs/examples 10. move to the next component only when the framework is working again
Final target state
This migration is successful when:
- public components use only props-first APIs
- component contracts are explicit in code
- validation is shared and consistent
- docs teach one canonical API style
- the demo app uses only canonical APIs
- LLMs can read a component file and quickly understand how to call it correctly