Skip to content

Divergence Register

Structured tracking of all known behavioral differences across Formosaic adapters. Each entry is classified by severity and recommended action for Tier 2 readiness.

Classification Key

CategoryMeaning
Permanent acceptableBy-design difference that will not change
Temporary acceptableAcceptable now, should normalize eventually
Test-environment limitationOnly manifests in jsdom, not in real browsers
UX-visible inconsistencyUsers may notice different behavior across adapters
Should normalize before Tier 2Fix before expanding field coverage
Must monitor during Tier 2Track actively during Tier 2 expansion

Register

DIV-001: Number/Slider readOnly null coercion

PropertyValue
Affected adaptersAll 11
Affected fieldsNumber, Slider
Observed behaviorreadOnly with null/undefined shows "0" (Number) or "0" (Slider) instead of "-" sentinel
Canonical expectationEmpty sentinel "-" per readOnly contract
Root cause?? 0 coercion in Number field; Slider always coerces to 0 for range input
SeverityLow
User-visibleYes — readOnly Number shows "0" instead of "-" when no value is set
jsdom-onlyNo — same behavior in real browsers
CategoryPermanent acceptable
Recommended actionDocument as accepted. The coercion is intentional: Number/Slider need numeric values for their inputs. ReadOnly inherits this. Attempting to show "-" would require special-casing the readOnly path to distinguish "no value" from "value is 0", which is fragile.

DIV-002: Mantine NumberInput empty → null

PropertyValue
Affected adaptersmantine
Affected fieldsNumber
Observed behaviorClearing Mantine NumberInput calls setFieldValue(fieldName, null). Other adapters leave it as browser-level empty state (no setFieldValue call on clear).
Canonical expectationBoth null and undefined are valid empty values per canonical contract
Root causeMantine NumberInput onChange fires with empty string or null on clear
SeverityLow
User-visibleNo (form state ends up equivalent)
jsdom-onlyNo
CategoryMust monitor during Tier 2
Recommended actionMonitor. If Mantine Tier 2 fields also normalize empty→null, this becomes a pattern, not a bug.

DIV-003: Fluent/MUI Textarea required indicator (PopOutEditor)

PropertyValue
Affected adaptersfluent, mui
Affected fieldsTextarea
Observed behaviorPopOutEditor shows required * only inside the expanded modal dialog title, not in the inline textarea rendering
Canonical expectationRequired indicator should be visible in all rendering modes
Root causePopOutEditor inline rendering does not propagate required attribute
SeverityMedium
User-visibleYes — required indicator not visible until modal is opened
jsdom-onlyNo
CategoryUX-visible inconsistency
Recommended actionShould normalize. The inline rendering of PopOutEditor should add aria-required or a visual *. This is a pre-existing design choice, not a regression. Fix is low-risk: add aria-required={required} to the inline <TextField>.

DIV-004: MUI CheckboxGroup required detection

PropertyValue
Affected adaptersmui
Affected fieldsCheckboxGroup
Observed behaviorMUI FormControl with required={true} does not render a detectable required attribute in jsdom standalone rendering
Canonical expectationRequired should be detectable via aria-required or native required attribute
Root causeMUI FormControl applies required semantics through FormLabel, not directly on the fieldset
SeverityLow
User-visibleNo — FieldWrapper adds required indicator in production; this is standalone-render-only
jsdom-onlyPartially — may also affect standalone usage without FieldWrapper
CategoryTest-environment limitation
Recommended actionAccept for now. In production, FieldWrapper provides the required indicator. The parity test skip is appropriate.

DIV-005: MultiSelect readOnly rendering format

PropertyValue
Affected adaptersheadless (<ul> list) vs antd (comma-join text) vs others
Affected fieldsMultiSelect
Observed behaviorDifferent visual representations of selected values in readOnly mode
Canonical expectation"comma-separated option labels, or '-'" per readOnly contract
Root causeAdapters chose different HTML structures for list rendering
SeverityLow
User-visibleYes — different visual structure, same semantic content
jsdom-onlyNo
CategoryUX-visible inconsistency
Recommended actionAccept. The headless <ul> renders raw values (not labels), while antd comma-joins raw values. Both display the correct selected values. The headless list format provides better accessibility (screen readers enumerate items). Normalizing to comma-join would reduce accessibility.

DIV-006: Dropdown readOnly shows value not label -- RESOLVED

PropertyValue
Affected adaptersAll 11 None (fixed in v1.5.2)
Affected fieldsDropdown
Observed behaviorReadOnly rendering showed the option VALUE string, not the option LABEL
Canonical expectation"selected option label (not value), or '-'" per readOnly contract for single-select fields
Root causeReadOnlyText received value directly without options lookup
SeverityMedium Resolved
User-visibleYes No (fixed)
jsdom-onlyNo
CategoryShould normalize before Tier 2 Resolved
ResolutionAll 11 adapters now perform options?.find(o => String(o.value) === String(value))?.label before passing to ReadOnlyText. Falls back to raw value if label lookup fails.

DIV-007: Semantic HTML adapter classification honesty

PropertyValue
Affected adaptersatlaskit, heroui
Affected fieldsAll
Observed behaviorThese adapters are labeled as "Atlassian Design System" and "HeroUI" but use pure semantic HTML, not their namesake UI library components
Canonical expectationPackage names imply native component usage
Root causeAdapters created for ecosystem compatibility; native components had jsdom/SSR issues
SeverityLow
User-visibleNo — behavior is correct, just classification
jsdom-onlyNo
CategoryPermanent acceptable
Recommended actionPackage READMEs clarify this. Note: base-web was previously listed here but actually uses native baseui components for 9/12 Tier 1 fields (Textbox, Number, Toggle, Dropdown, MultiSelect, Slider, RadioGroup, CheckboxGroup, Textarea). Only DateControl, DynamicFragment, and ReadOnly use HTML fallbacks.

DIV-008: Chakra compound component DTS fallbacks

PropertyValue
Affected adapterschakra
Affected fieldsNumber, Toggle, MultiSelect, Slider, RadioGroup, CheckboxGroup
Observed behavior6 of 13 Tier 1 fields use semantic HTML instead of native Chakra components
Root causeArk UI v3 DTS issue (Assign type breaks .d.ts generation)
SeverityMedium
User-visibleYes — different visual appearance from native Chakra components
jsdom-onlyNo
CategoryTemporary acceptable
Recommended actionMonitor upstream Ark UI/Chakra v3 releases. When DTS issue is fixed, migrate to native components. Until then, HTML fallbacks are functional and styled with Chakra CSS variables.

DIV-009: Date picker UX variance

PropertyValue
Affected adaptersantd (dayjs DatePicker), headless/heroui (native <input type="date">), others vary
Affected fieldsDateControl
Observed behaviorDifferent date picker UIs across adapters
Canonical expectationISO string serialization is consistent; UX is adapter-specific
Root causeEach adapter uses its ecosystem's date component by design
SeverityNone
User-visibleYes — expected and intentional
jsdom-onlyNo
CategoryPermanent acceptable
Recommended actionNone. Each adapter uses its ecosystem's date component. The canonical contract only requires ISO string serialization.

DIV-010: Radix Select empty value handling

PropertyValue
Affected adaptersradix
Affected fieldsDropdown
Observed behaviorRadix Select.Root does not support "" as a value; uses undefined for no selection. Empty trigger text displayed instead of empty string.
Canonical expectationEmpty string "" for no selection
Root causeRadix Select uses undefined internally for uncontrolled/empty state; "" is not a valid value
SeverityLow
User-visibleNo — form state ends up equivalent (both represent "no selection")
jsdom-onlyNo
CategoryPermanent acceptable
Recommended actionAccept. The boundary conversion (value as string) || undefined handles this cleanly. Form state remains conformant.

DIV-011: Radix Slider array boundary conversion

PropertyValue
Affected adaptersradix
Affected fieldsSlider
Observed behaviorRadix Slider uses number[] for value/onValueChange; Formosaic uses single number
Canonical expectationSingle number value
Root causeRadix Slider supports multi-thumb sliders; single-thumb is [number]
SeverityNone
User-visibleNo — conversion is transparent
jsdom-onlyNo
CategoryPermanent acceptable
Recommended actionNone. The boundary conversion value={[num]} / onValueChange={([num]) => ...} is clean and lossless.

DIV-012: React Aria Select Key type cast

PropertyValue
Affected adaptersreact-aria
Affected fieldsDropdown
Observed behaviorReact Aria Select uses Key type (string | number) for selectedKey/onSelectionChange; Formosaic uses string
Canonical expectationString value
Root causeReact Aria's Key type is a union of string and number to support both use cases
SeverityNone
User-visibleNo — String(key) cast is transparent
jsdom-onlyNo
CategoryPermanent acceptable
Recommended actionNone. The String(key) cast in onSelectionChange is clean and lossless.

Released under the MIT License.