agentsclimarketplace

Accessible forms

Skill almasumdev/awesome-mobile-accessibility-agent-skills/.github/skills/forms/accessible-forms

Labels, errors, grouping, and autofill for mobile forms. Use this when building any screen that collects user input — login, signup, checkout, profile editing.From its SKILL.md

Install
npx -y skills add almasumdev/awesome-mobile-accessibility-agent-skills --skill accessible-forms

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

7.3 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it

Accessible Forms

Instructions

Forms are where accessibility bugs cost users real money (unfinished checkouts, locked-out accounts). Every input must be labeled, every error actionable, every grouping meaningful.

1. Every Input Has a Visible Label

A placeholder is not a label:

  • Placeholders disappear on focus, leaving nothing for users to reference.
  • Placeholders usually fail contrast (deliberately gray).
  • Screen readers may or may not announce them.

SwiftUI:

VStack(alignment: .leading, spacing: 6) {
    Text("Email").font(.subheadline)
    TextField("[email protected]", text: $email)
        .textFieldStyle(.roundedBorder)
        .textContentType(.emailAddress)
        .keyboardType(.emailAddress)
        .autocorrectionDisabled()
        .textInputAutocapitalization(.never)
        .accessibilityLabel("Email")
}

UIKit — associate via accessibilityLabel or use UITextField.label pattern:

emailField.accessibilityLabel = "Email"
emailField.textContentType = .emailAddress
emailField.keyboardType = .emailAddress

Compose:

Column(Modifier.fillMaxWidth()) {
    Text("Email", style = MaterialTheme.typography.labelLarge)
    OutlinedTextField(
        value = email,
        onValueChange = onChange,
        placeholder = { Text("[email protected]") },
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Email,
            imeAction = ImeAction.Next,
            autoCorrectEnabled = false,
        ),
        modifier = Modifier.semantics { contentDescription = "Email" }
    )
}

Flutter:

TextFormField(
  decoration: const InputDecoration(labelText: 'Email', hintText: '[email protected]'),
  keyboardType: TextInputType.emailAddress,
  autofillHints: const [AutofillHints.email],
)

React Native:

<View>
  <Text nativeID="emailLabel">Email</Text>
  <TextInput
    accessibilityLabel="Email"
    accessibilityLabelledBy="emailLabel"
    keyboardType="email-address"
    textContentType="emailAddress"
    autoCapitalize="none"
    autoComplete="email" />
</View>

2. Autofill Hints

Every input whose value is known to the OS should declare its content type so the OS offers autofill.

FieldiOS textContentTypeAndroid autofillHints / Compose AutofillTypeFlutter AutofillHintsRN textContentType + autoComplete
Email.emailAddressAutofillType.EmailAddressemailemailAddress / email
Password (login).passwordAutofillType.Passwordpasswordpassword / password
New password.newPasswordAutofillType.NewPasswordnewPasswordnewPassword / password-new
OTP.oneTimeCodeAutofillType.SmsOtpCodeoneTimeCodeoneTimeCode / sms-otp
Name.nameAutofillType.PersonFullNamenamename / name
Street.fullStreetAddressAutofillType.PostalAddressstreetAddressLine1fullStreetAddress / street-address

3. Grouping and Field Sets

Related fields (address lines, card details) should announce as a group.

SwiftUI:

VStack {
    Text("Billing address").accessibilityAddTraits(.isHeader)
    TextField("Street", text: $street)
    TextField("City", text: $city)
}
.accessibilityElement(children: .contain)
.accessibilityLabel("Billing address")

Compose:

Column(Modifier.semantics { isTraversalGroup = true }) {
    Text("Billing address", Modifier.semantics { heading() })
    // fields
}

4. Error States

  • Announce errors to the screen reader when they appear (live region).
  • Place the error text next to the field and reference it from the field.
  • Keep focus inside the erroring field after submit fails; don't reset to top.

UIKit:

emailField.accessibilityLabel = "Email. Error: must include @."
errorLabel.accessibilityLiveRegion = .polite // via category
UIAccessibility.post(notification: .announcement,
                     argument: "Email is invalid. Please include @.")

Compose:

OutlinedTextField(
    value = email,
    onValueChange = onChange,
    isError = !isValid,
    supportingText = {
        if (!isValid) {
            Text(
                "Enter a valid email",
                modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite }
            )
        }
    }
)

Flutter:

TextFormField(
  decoration: InputDecoration(
    labelText: 'Email',
    errorText: error, // announced on change via SemanticsService
  ),
  validator: (v) => isValid(v) ? null : 'Enter a valid email',
)

// For explicit announcements
SemanticsService.announce('Email is invalid', TextDirection.ltr);

RN:

<Text
  accessibilityLiveRegion="polite"
  accessibilityRole="alert"
  style={styles.error}>
  {error}
</Text>

5. Required vs Optional

  • Mark required fields explicitly: visible asterisk and text "required" in the a11y label.
  • Prefer marking the few optional fields if most are required ("Phone (optional)").
.accessibilityLabel("Email, required")

6. Submit Button State

  • Disable the submit button only while the request is in flight, not while the form is incomplete (gives better feedback — user presses and hears what's missing).
  • If disabled, expose state and reason:
Button(
    onClick = submit,
    enabled = !loading,
    modifier = Modifier.semantics {
        if (loading) stateDescription = "Submitting"
    }
) { Text("Sign in") }
  • After submit, announce success / error and move focus to a meaningful destination (success screen heading, error summary, or first invalid field).

7. Keyboard Navigation

Chain fields via imeAction / returnKeyType / textInputAction so Tab or Return advances to the next field.

TextFormField(textInputAction: TextInputAction.next, onFieldSubmitted: (_) => nextFocus());

8. Don't Break Paste

Some apps block paste into password or OTP fields "for security". That violates WCAG 3.3.8 (Accessible Authentication) and locks out password managers.

  • Always allow paste.
  • For OTPs, use textContentType = .oneTimeCode / autofillHints = oneTimeCode so the OS can offer autofill from SMS.

9. Sensitive Inputs

  • Password fields: provide a show/hide eye button with accessibilityLabel = "Show password" / "Hide password".
  • Don't clear the password field after a failed login — screen-reader users lose their place.

10. Common Pitfalls

  • Placeholder-as-label.
  • No textContentType / autofillHints.
  • Error shown only in red color.
  • Focus jumps to top on submit error.
  • Submit button disabled with no explanation.
  • OTP field that blocks paste.
  • "Confirm password" field with no newPassword hint.

Checklist

  • Every input has a visible label and an accessible label.
  • Autofill hints set for email, password, name, address, OTP, etc.
  • Errors announced via live region and not color-only.
  • Focus remains on the erroring field after failed submit.
  • Required fields labeled explicitly.
  • Password fields allow paste and have show/hide toggle.
  • Related fields grouped with a heading.
  • Keyboard chain advances through fields with Next/Done.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.