diff --git a/src/uix/morfo/compile.ts b/src/uix/morfo/compile.ts index 5fe3cc83a..04d341571 100644 --- a/src/uix/morfo/compile.ts +++ b/src/uix/morfo/compile.ts @@ -522,7 +522,8 @@ function compileDataPlan(data: MorfoData, componentKebab: string): AttrPlan { function compileAriaPlan(aria: MorfoAriaEntry, componentKebab: string): AttrPlan { const condition: ConditionPlan = aria.condition ?? 'always' const source = normalizeSource(aria.value, componentKebab) - const mode: AttrMode = aria.value.kind === 'stateRef' ? 'aria-boolean' : 'string' + const mode: AttrMode = + aria.value.kind === 'stateRef' || aria.ariaBoolean ? 'aria-boolean' : 'string' return Object.freeze({ attr: aria.attr, source, diff --git a/src/uix/morfo/components/number-field.ts b/src/uix/morfo/components/number-field.ts index cd9dcb826..5691063b6 100644 --- a/src/uix/morfo/components/number-field.ts +++ b/src/uix/morfo/components/number-field.ts @@ -92,10 +92,30 @@ export const numberFieldMorfo = { condition: { when: 'prop-defined', prop: 'max' } }, { attr: 'aria-valuetext', value: v.propRef('value'), severity: 'recommended' }, - { attr: 'aria-required', value: v.propRef('required'), severity: 'optional' }, - { attr: 'aria-disabled', value: v.propRef('disabled'), severity: 'optional' }, - { attr: 'aria-readonly', value: v.propRef('readonly'), severity: 'optional' }, - { attr: 'aria-invalid', value: v.propRef('invalid'), severity: 'optional' } + { + attr: 'aria-required', + value: v.propRef('required'), + severity: 'optional', + ariaBoolean: true + }, + { + attr: 'aria-disabled', + value: v.propRef('disabled'), + severity: 'optional', + ariaBoolean: true + }, + { + attr: 'aria-readonly', + value: v.propRef('readonly'), + severity: 'optional', + ariaBoolean: true + }, + { + attr: 'aria-invalid', + value: v.propRef('invalid'), + severity: 'optional', + ariaBoolean: true + } ], keyboard: [ { key: 'ArrowUp', action: 'increment' }, diff --git a/src/uix/morfo/schema.ts b/src/uix/morfo/schema.ts index 946e2da0e..d8f9fb573 100644 --- a/src/uix/morfo/schema.ts +++ b/src/uix/morfo/schema.ts @@ -165,7 +165,8 @@ const ariaEntrySchema = object({ attr: string(), value: valueSourceSchema, condition: optional(conditionSchema), - severity: optional(severitySchema) + severity: optional(severitySchema), + ariaBoolean: optional(boolean()) }); const keyboardSchema = object({ diff --git a/src/uix/morfo/types.ts b/src/uix/morfo/types.ts index 838a2d230..4c9976ab2 100644 --- a/src/uix/morfo/types.ts +++ b/src/uix/morfo/types.ts @@ -264,6 +264,13 @@ export interface MorfoAriaEntry { condition?: MorfoCondition; /** Validator severity. Defaults to `'required'`. */ severity?: MorfoSeverity; + /** + * Render the value as an ARIA boolean (`"true"`/`"false"` string) instead of + * passing it through raw. A `stateRef` value already compiles to aria-boolean; + * set this for a `propRef` boolean (e.g. `aria-required` ← `required` prop) so + * `renderProps()` stringifies it and the provider need not re-declare it. + */ + ariaBoolean?: boolean; } // ── Keyboard ────────────────────────────────────────────────────────────── diff --git a/src/uix/soma/components/number-field/number-field-provider.svelte.ts b/src/uix/soma/components/number-field/number-field-provider.svelte.ts index 4764c1cfe..cde5fa552 100644 --- a/src/uix/soma/components/number-field/number-field-provider.svelte.ts +++ b/src/uix/soma/components/number-field/number-field-provider.svelte.ts @@ -617,15 +617,6 @@ export class NumberFieldInputProvider { this.provider.opts.value.current !== undefined ? this.provider.formatValue(this.provider.opts.value.current) : undefined, - // ARIA booleans: the morfo declares them (propRef), but a propRef-valued - // aria attr compiles to raw mode → renderProps emits the boolean. ARIA - // wants the "true"/"false" string, so soma stringifies (same role as the - // formatted aria-valuetext above). A future `v.ariaBool()` morfo helper - // would let these resolve from the morfo too. - 'aria-required': boolToStr(this.provider.isRequired), - 'aria-disabled': boolToStr(this.provider.isDisabled), - 'aria-readonly': boolToStr(this.provider.isReadonly), - 'aria-invalid': boolToStr(this.provider.isInvalid), 'aria-describedby': this.describedBy, disabled: this.provider.isDisabled || undefined, readonly: this.provider.isReadonly || undefined,