ADR
Allow null in StringProp.default
Context
StringProp (the unified string property type, consolidated from the former TextProp, GlyphProp, and IconProp) declares a nullable?: boolean field that signals the prop can hold a null value in the design system output. However, the default field is typed as string | undefined (TypeScript) and { "type": "string" } (JSON Schema). This means a nullable prop cannot express that its default value is null.
When a prop is nullable and has no meaningful default, the correct representation of its default state is null — not an empty string and not the absence of the field. The current contract forces producers to either omit default or use an empty string as a stand-in, both of which lose semantic information.
Decision Drivers
- Type–schema symmetry: Every type change must have a corresponding schema change — no drift (Constitution I)
- Additive-only when possible: Widening an optional field’s type union is additive and avoids a MAJOR bump (Constitution III, Versioning)
- Semantic precision: The contract should express what it means —
nulldefault for a nullable prop is a distinct concept from “no default” or “empty string default” - No runtime logic: The change must remain purely declarative (Constitution II)
Options Considered
Option A: Widen default to string | null (Selected)
Change the default field type from string to string | null on StringProp. In the schema, change "type": "string" to "type": ["string", "null"] for the default property.
Pros:
- Directly expresses the semantic intent — nullable props can declare
nullas their default - Additive change — existing
stringvalues remain valid; onlynullis newly permitted - Symmetric across types and schema
- No new fields or structural changes required
Cons / Trade-offs:
- Downstream consumers reading
defaultmust now handle a possiblenullvalue (minimal impact — the field was already optional)
Option B: Add a separate nullDefault boolean flag (Rejected)
Add a nullDefault?: boolean field to indicate the default is null rather than widening the default type.
Rejected because: Introduces redundancy — the same concept (default value) would be split across two fields. Violates semantic precision: the default field should carry the actual default value, not a companion flag. Adds unnecessary API surface.
Decision
Type changes (types/)
| File | Change | Bump |
|---|---|---|
Props.ts | Widen StringProp.default from string to string | null | MINOR |
Example — new shape (types/Props.ts):
# BeforeStringProp: type: 'string' default?: string nullable?: boolean examples?: string[]
# AfterStringProp: type: 'string' default?: string | null nullable?: boolean examples?: string[]Schema changes (schema/)
| File | Change | Bump |
|---|---|---|
component.schema.json | Widen StringProp.default type from "string" to ["string", "null"] | MINOR |
Example — new shape (schema/component.schema.json):
# Before — StringProp/properties/defaultdefault: type: string
# After — StringProp/properties/defaultdefault: type: ["string", "null"]Notes
- The
defaultfield remains optional (?in TypeScript, not inrequired[]in schema). The change only widens the set of valid values when the field is present. BooleanProp,EnumProp, andSlotPropare unaffected — theirdefaultfields have different semantics and are not nullable.
Type ↔ Schema Impact
- Symmetric: Yes —
StringPropreceives identical changes intypes/Props.tsandschema/component.schema.json - Parity check:
StringProp.default: string | null↔StringProp/properties/default/type: ["string", "null"]
Downstream Impact
| Consumer | Impact | Action required |
|---|---|---|
anova-kit | Recompile — default may now be null | Handle null when reading StringProp.default (field was already optional, so null-check paths likely exist) |
Semver Decision
Version bump: MINOR
Justification: All changes are additive — widening an optional field’s type union to include null does not remove or rename any existing field or type. Existing valid values remain valid. Per Constitution III and Versioning: “MINOR for additive types or new optional fields.”
Consequences
- Nullable
StringPropinstances can now expressdefault: nullto indicate the prop’s default state is explicitly null - Consumers reading
defaultmust account for anullvalue in addition tostringandundefined - Schema validation will accept
nullas a validdefaultvalue forStringProp - No changes to
BooleanProp,EnumProp, orSlotProp