Form without submit button
forms/no-submit
- WCAG success criterion
- 3.2.2 (level A)
- Outcome class
- Hint only. An automated check that by design reports review hints only: its findings are marked for manual review, never as confirmed violations.
- Area
- Accessibility, Forms & Interaction
- Default severity
- medium
- axe-core rule
form-no-submit
Why we flag this
Keyboard and screen reader users have a harder time completing the form.
Missing submit control in the form.
What the rule checks
Scope. Rendered form elements owning at least one enabled control (HTML form owner, including the form attribute).
Expectation. Needs review when a form has controls but no enabled submit control (button without type or type=submit, input submit/image, role=button marked as submit): without one, a change may submit the form. 3.2.2 fails only on an actual change of context, so never a confirmed violation; without action and without text field (toggles changing the page) the review has low severity. A submit button outside the form with form=... counts; a form with only hidden inputs is not judged.
Source. WCAG 2.2 SC 3.2.2 On Input; technique H32 (submit buttons)
Detection: shared; needs dom.
Failing example
The check flags this for review. Corpus case form_toggles_without_submit, element form#toggles. The page may contain further findings for other rules.
<!DOCTYPE html>
<!-- form-no-submit (#728): a form of toggles without action, text field or
submit control only changes content on the page (here via CSS :has()),
so it is a review note, not a 3.2.2 violation. A form with an action and
no submit control stays a violation. -->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Form of toggles without submit</title>
<style>
/* #783: page shell, so the case only reports what it is about */
.skip, nav a { display: inline-block; padding: 12px; min-height: 44px; min-width: 44px; box-sizing: border-box; }
button, input, select, textarea { min-height: 44px; min-width: 44px; box-sizing: border-box; }
input[type="checkbox"], input[type="radio"] { width: 44px; height: 44px; }
</style>
<style>
body:has(#t-contrast:checked) .demo { color: #777; }
</style>
</head>
<body>
<a class="skip" href="#main">Skip to main content</a>
<header><p>Example Shop</p><nav aria-label="Main"><a href="/" aria-current="page">Home</a> <a href="/help">Help</a></nav></header>
<main id="main">
<h1>Demo</h1>
<form id="toggles">
<fieldset>
<legend>Remove accessibility</legend>
<input type="checkbox" id="t-labels"><label for="t-labels">Remove labels</label>
<input type="checkbox" id="t-contrast"><label for="t-contrast">Remove contrast</label>
<input type="radio" name="size" id="t-small"><label for="t-small">Small</label>
<input type="radio" name="size" id="t-large"><label for="t-large">Large</label>
</fieldset>
<button type="reset">Reset</button>
</form>
<p class="demo">Sample text.</p>
<form id="filter" action="/results">
<fieldset>
<legend>Filter</legend>
<input type="checkbox" id="f-open" name="open"><label for="f-open">Open only</label>
</fieldset>
</form>
</main>
<footer><p>Example Shop, 2026</p></footer>
</body>
</html>Passing example
The check reports nothing here. Corpus case forms_submit_redundant, element form#with-btn. The page may contain further findings for other rules.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Order forms</title>
<style>
body { font-family: sans-serif; color: #222; background: #fff; line-height: 1.5; margin: 0 16px; }
a { color: #0050a0; }
.skip, nav a, .solo { display: inline-block; padding: 12px; min-height: 44px; min-width: 44px; box-sizing: border-box; }
button, input, select, textarea, [role="button"], [role="textbox"], [role="radio"], [role="link"], [role="searchbox"] {
display: inline-block; min-height: 44px; min-width: 44px; box-sizing: border-box; font-size: 1rem; }
input[type="checkbox"], input[type="radio"] { width: 44px; height: 44px; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
</style>
</head>
<body>
<a class="skip" href="#main">Skip to main content</a>
<header><p>Example Shop</p>
<nav aria-label="Main"><a href="/" aria-current="page">Home</a> <a href="/help">Help</a></nav>
</header>
<main id="main">
<h1>Order forms</h1>
<!-- G2 #783: forms with and without a submit control (H32, 3.2.2) and forms asking for the same information twice (3.3.7) with the exceptions: confirmation, autofill, a same-as option, a single request. -->
<form id="no-btn" action="/subscribe" method="post">
<p><label for="ns-mail">Email for the price alert</label> <input id="ns-mail" name="alert" type="email" autocomplete="email"></p>
</form>
<form id="with-btn" action="/subscribe" method="post">
<p><label for="wb-mail">Email for the newsletter</label> <input id="wb-mail" name="news" type="email" autocomplete="email"> <button type="submit">Subscribe</button></p>
</form>
<form id="ext-btn" action="/notify" method="post">
<p><label for="eb-mail">Email for restock notices</label> <input id="eb-mail" name="restock" type="email" autocomplete="email"></p>
</form>
<p><button type="submit" form="ext-btn">Notify me</button></p>
<form id="aria-submit" action="/callback" method="post">
<p><label for="as-time">Preferred call time</label> <input id="as-time" name="slot" type="text" aria-describedby="as-hint"> <span id="as-hint">For example: mornings.</span></p>
<p><span role="button" tabindex="0" aria-label="Send request" data-submit onclick="this.closest('form').requestSubmit()" onkeydown="if (event.key === 'Enter' || event.key === ' ') this.closest('form').requestSubmit()">Send request</span></p>
</form>
<form id="empty-form" action="/ping" method="post"><input type="hidden" name="ping" value="1"><p>Nothing to fill in here.</p></form>
<form id="reg-twice" action="/order" method="post">
<p><label for="rt-mail1">Email</label> <input id="rt-mail1" name="buyer" type="text"></p>
<p><label for="rt-mail2">Email for the invoice</label> <input id="rt-mail2" name="invoice" type="text"></p>
<p><button type="submit">Order</button></p>
</form>
<form id="confirm-twice" action="/account" method="post">
<p><label for="ct-mail1">Email</label> <input id="ct-mail1" name="account" type="text"></p>
<p><label for="ct-mail2">Confirm email</label> <input id="ct-mail2" name="account-confirm" type="text"></p>
<p><button type="submit">Create account</button></p>
</form>
<form id="same-as" action="/billing" method="post">
<p><label for="sa-street1">Street address for delivery</label> <input id="sa-street1" name="delivery-street" type="text"></p>
<p><label><input type="checkbox" name="same" id="sa-same"> Billing address same as delivery address</label></p>
<p><label for="sa-street2">Street address for billing</label> <input id="sa-street2" name="billing-street" type="text"></p>
<p><button type="submit">Next step</button></p>
</form>
<form id="ac-twice" action="/gift" method="post">
<p><label for="ac-mail1">Email</label> <input id="ac-mail1" name="sender" type="email" autocomplete="email"></p>
<p><label for="ac-mail2">Email for the receipt</label> <input id="ac-mail2" name="receipt" type="email" autocomplete="email"></p>
<p><button type="submit">Send gift</button></p>
</form>
<form id="single-mail" action="/quote" method="post">
<p><label for="sm-mail">Email</label> <input id="sm-mail" name="quote" type="text"></p>
<p><button type="submit">Request quote</button></p>
</form>
</main>
<footer><p>Example Shop, 2026</p></footer>
</body>
</html>This page is generated from the rule catalogue and the rule contracts; see therule overview for how the outcome classes are derived.