diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts
index 82878d701b..e3d4013a7c 100644
--- a/angular/src/directives/proxies.ts
+++ b/angular/src/directives/proxies.ts
@@ -978,14 +978,14 @@ where the user's interaction is typing.
@ProxyCmp({
defineCustomElementFn: undefined,
- inputs: ['accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'size', 'spellcheck', 'step', 'type', 'value'],
+ inputs: ['accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'legacy', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'size', 'spellcheck', 'step', 'type', 'value'],
methods: ['setFocus', 'getInputElement']
})
@Component({
selector: 'ion-input',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
- inputs: ['accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'size', 'spellcheck', 'step', 'type', 'value']
+ inputs: ['accept', 'autocapitalize', 'autocomplete', 'autocorrect', 'autofocus', 'clearInput', 'clearOnEdit', 'color', 'counter', 'counterFormatter', 'debounce', 'disabled', 'enterkeyhint', 'errorText', 'fill', 'helperText', 'inputmode', 'label', 'labelPlacement', 'legacy', 'max', 'maxlength', 'min', 'minlength', 'mode', 'multiple', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'shape', 'size', 'spellcheck', 'step', 'type', 'value']
})
export class IonInput {
protected el: HTMLElement;
@@ -2247,13 +2247,13 @@ when programmatically changing the value of the `checked` property.
@ProxyCmp({
defineCustomElementFn: undefined,
- inputs: ['checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'mode', 'name', 'value']
+ inputs: ['checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
})
@Component({
selector: 'ion-toggle',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
- inputs: ['checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'mode', 'name', 'value']
+ inputs: ['checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
})
export class IonToggle {
protected el: HTMLElement;
diff --git a/core/api.txt b/core/api.txt
index f9eacb5fe5..30d77d3d03 100644
--- a/core/api.txt
+++ b/core/api.txt
@@ -550,6 +550,7 @@ ion-input,prop,helperText,string | undefined,undefined,false,false
ion-input,prop,inputmode,"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url" | undefined,undefined,false,false
ion-input,prop,label,string | undefined,undefined,false,false
ion-input,prop,labelPlacement,"end" | "fixed" | "floating" | "stacked" | "start",'start',false,false
+ion-input,prop,legacy,boolean | undefined,undefined,false,false
ion-input,prop,max,number | string | undefined,undefined,false,false
ion-input,prop,maxlength,number | undefined,undefined,false,false
ion-input,prop,min,number | string | undefined,undefined,false,false
@@ -1430,6 +1431,7 @@ ion-toggle,prop,disabled,boolean,false,false,false
ion-toggle,prop,enableOnOffLabels,boolean | undefined,undefined,false,false
ion-toggle,prop,justify,"end" | "space-between" | "start",'space-between',false,false
ion-toggle,prop,labelPlacement,"end" | "fixed" | "start",'start',false,false
+ion-toggle,prop,legacy,boolean | undefined,undefined,false,false
ion-toggle,prop,mode,"ios" | "md",undefined,false,false
ion-toggle,prop,name,string,this.inputId,false,false
ion-toggle,prop,value,null | string | undefined,'on',false,false
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index c5a7c93c10..63607338eb 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -1141,6 +1141,10 @@ export namespace Components {
* Where to place the label relative to the input. `'start'`: The label will appear to the left of the input in LTR and to the right in RTL. `'end'`: The label will appear to the right of the input in LTR and to the left in RTL. `'floating'`: The label will appear smaller and above the input when the input is focused or it has a value. Otherwise it will appear on top of the input. `'stacked'`: The label will appear smaller and above the input regardless even when the input is blurred or has no value. `'fixed'`: The label has the same behavior as `'start'` except it also has a fixed width. Long text will be truncated with ellipses ("...").
*/
"labelPlacement": 'start' | 'end' | 'floating' | 'stacked' | 'fixed';
+ /**
+ * Set the `legacy` property to `true` to forcibly use the legacy form control markup. Ionic will only opt components in to the modern form markup when they are using either the `aria-label` attribute or the `label` property. As a result, the `legacy` property should only be used as an escape hatch when you want to avoid this automatic opt-in behavior. Note that this property will be removed in an upcoming major release of Ionic, and all form components will be opted-in to using the modern form markup.
+ */
+ "legacy"?: boolean;
/**
* The maximum value, which must not be less than its minimum (min attribute) value.
*/
@@ -3002,6 +3006,10 @@ export namespace Components {
* Where to place the label relative to the input. `'start'`: The label will appear to the left of the toggle in LTR and to the right in RTL. `'end'`: The label will appear to the right of the toggle in LTR and to the left in RTL. `'fixed'`: The label has the same behavior as `'start'` except it also has a fixed width. Long text will be truncated with ellipses ("...").
*/
"labelPlacement": 'start' | 'end' | 'fixed';
+ /**
+ * Set the `legacy` property to `true` to forcibly use the legacy form control markup. Ionic will only opt components in to the modern form markup when they are using either the `aria-label` attribute or the default slot that contains the label text. As a result, the `legacy` property should only be used as an escape hatch when you want to avoid this automatic opt-in behavior. Note that this property will be removed in an upcoming major release of Ionic, and all form components will be opted-in to using the modern form markup.
+ */
+ "legacy"?: boolean;
/**
* The mode determines which platform styles to use.
*/
@@ -5057,6 +5065,10 @@ declare namespace LocalJSX {
* Where to place the label relative to the input. `'start'`: The label will appear to the left of the input in LTR and to the right in RTL. `'end'`: The label will appear to the right of the input in LTR and to the left in RTL. `'floating'`: The label will appear smaller and above the input when the input is focused or it has a value. Otherwise it will appear on top of the input. `'stacked'`: The label will appear smaller and above the input regardless even when the input is blurred or has no value. `'fixed'`: The label has the same behavior as `'start'` except it also has a fixed width. Long text will be truncated with ellipses ("...").
*/
"labelPlacement"?: 'start' | 'end' | 'floating' | 'stacked' | 'fixed';
+ /**
+ * Set the `legacy` property to `true` to forcibly use the legacy form control markup. Ionic will only opt components in to the modern form markup when they are using either the `aria-label` attribute or the `label` property. As a result, the `legacy` property should only be used as an escape hatch when you want to avoid this automatic opt-in behavior. Note that this property will be removed in an upcoming major release of Ionic, and all form components will be opted-in to using the modern form markup.
+ */
+ "legacy"?: boolean;
/**
* The maximum value, which must not be less than its minimum (min attribute) value.
*/
@@ -7006,6 +7018,10 @@ declare namespace LocalJSX {
* Where to place the label relative to the input. `'start'`: The label will appear to the left of the toggle in LTR and to the right in RTL. `'end'`: The label will appear to the right of the toggle in LTR and to the left in RTL. `'fixed'`: The label has the same behavior as `'start'` except it also has a fixed width. Long text will be truncated with ellipses ("...").
*/
"labelPlacement"?: 'start' | 'end' | 'fixed';
+ /**
+ * Set the `legacy` property to `true` to forcibly use the legacy form control markup. Ionic will only opt components in to the modern form markup when they are using either the `aria-label` attribute or the default slot that contains the label text. As a result, the `legacy` property should only be used as an escape hatch when you want to avoid this automatic opt-in behavior. Note that this property will be removed in an upcoming major release of Ionic, and all form components will be opted-in to using the modern form markup.
+ */
+ "legacy"?: boolean;
/**
* The mode determines which platform styles to use.
*/
diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx
index b341c25396..2a13a8ae3c 100644
--- a/core/src/components/input/input.tsx
+++ b/core/src/components/input/input.tsx
@@ -184,6 +184,17 @@ export class Input implements ComponentInterface {
*/
@Prop() labelPlacement: 'start' | 'end' | 'floating' | 'stacked' | 'fixed' = 'start';
+ /**
+ * Set the `legacy` property to `true` to forcibly use the legacy form control markup.
+ * Ionic will only opt components in to the modern form markup when they are
+ * using either the `aria-label` attribute or the `label` property. As a result,
+ * the `legacy` property should only be used as an escape hatch when you want to
+ * avoid this automatic opt-in behavior.
+ * Note that this property will be removed in an upcoming major release
+ * of Ionic, and all form components will be opted-in to using the modern form markup.
+ */
+ @Prop() legacy?: boolean;
+
/**
* The maximum value, which must not be less than its minimum (min attribute) value.
*/
@@ -708,9 +719,21 @@ export class Input implements ComponentInterface {
printIonWarning(
`Using ion-input with an ion-label has been deprecated. To migrate, remove the ion-label and use the "label" property on ion-input instead.
+Example:
+
For inputs that do not have a visible label, developers should use "aria-label" so screen readers can announce the purpose of the input.`,
this.el
);
+
+ if (this.legacy) {
+ printIonWarning(
+ `ion-input is being used with the "legacy" property enabled which will forcibly enable the legacy form markup. This property will be removed in an upcoming major release of Ionic where this form control will use the modern form markup.
+
+Developers can dismiss this warning by removing their usage of the "legacy" property and using the new input syntax.`,
+ this.el
+ );
+ }
+
this.hasLoggedDeprecationWarning = true;
}
diff --git a/core/src/components/toggle/toggle.tsx b/core/src/components/toggle/toggle.tsx
index 73b939a640..bc45cc10ec 100644
--- a/core/src/components/toggle/toggle.tsx
+++ b/core/src/components/toggle/toggle.tsx
@@ -89,6 +89,17 @@ export class Toggle implements ComponentInterface {
*/
@Prop() labelPlacement: 'start' | 'end' | 'fixed' = 'start';
+ /**
+ * Set the `legacy` property to `true` to forcibly use the legacy form control markup.
+ * Ionic will only opt components in to the modern form markup when they are
+ * using either the `aria-label` attribute or the default slot that contains
+ * the label text. As a result, the `legacy` property should only be used as
+ * an escape hatch when you want to avoid this automatic opt-in behavior.
+ * Note that this property will be removed in an upcoming major release
+ * of Ionic, and all form components will be opted-in to using the modern form markup.
+ */
+ @Prop() legacy?: boolean;
+
/**
* How to pack the label and toggle within a line.
* `'start'`: The label and toggle will appear on the left in LTR and
@@ -340,6 +351,16 @@ Example: Email:
For toggles that do not have a visible label, developers should use "aria-label" so screen readers can announce the purpose of the toggle.`,
this.el
);
+
+ if (this.legacy) {
+ printIonWarning(
+ `ion-toggle is being used with the "legacy" property enabled which will forcibly enable the legacy form markup. This property will be removed in an upcoming major release of Ionic where this form control will use the modern form markup.
+
+Developers can dismiss this warning by removing their usage of the "legacy" property and using the new toggle syntax.`,
+ this.el
+ );
+ }
+
this.hasLoggedDeprecationWarning = true;
}
diff --git a/core/src/utils/forms/form-controller.ts b/core/src/utils/forms/form-controller.ts
index d59da6fb0b..67b0e33641 100644
--- a/core/src/utils/forms/form-controller.ts
+++ b/core/src/utils/forms/form-controller.ts
@@ -22,7 +22,11 @@ export const createLegacyFormController = (el: AllowedFormElements): LegacyFormC
(controlEl as any).label !== undefined || (controlEl.shadowRoot !== null && controlEl.textContent !== '');
const hasAriaLabelAttribute = controlEl.hasAttribute('aria-label');
- legacyControl = !hasLabelProp && !hasAriaLabelAttribute;
+ /**
+ * Developers can manually opt-out of the modern form markup
+ * by setting `legacy="true"` on components.
+ */
+ legacyControl = controlEl.legacy === true || (!hasLabelProp && !hasAriaLabelAttribute);
const hasLegacyControl = () => {
return legacyControl;
diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts
index 8d13367cf8..3678ba951a 100644
--- a/packages/vue/src/proxies.ts
+++ b/packages/vue/src/proxies.ts
@@ -407,6 +407,7 @@ export const IonInput = /*@__PURE__*/ defineContainer('ion-input',
'helperText',
'label',
'labelPlacement',
+ 'legacy',
'max',
'maxlength',
'min',
@@ -813,6 +814,7 @@ export const IonToggle = /*@__PURE__*/ defineContainer('ion-toggl
'value',
'enableOnOffLabels',
'labelPlacement',
+ 'legacy',
'justify',
'ionChange',
'ionFocus',