Ananyoo Accessible Forms – Contact Form & Entry Storage
Ananyoo Accessible Forms – Contact Form & Entry Storage
Description
Ananyoo Accessible Forms builds contact and lead forms that conform to WCAG 2.2 Level AA out of the box. Instead of bolting accessibility on afterwards, every form is generated with correct semantics from the start: a real <label> for every control, fieldset/legend for grouped inputs, an error summary that links to each problem and moves focus to the first field to fix, inline errors tied to their field with aria-describedby and aria-invalid, and a status region that announces success.
Developed by Shivaji Mitra (M/S. Anblik).
Live demo: Try the accessible forms demo »
Why this plugin?
Most popular form plugins can be made accessible, but they do not start that way — you have to know exactly which settings to change and which markup to fix. This plugin makes the accessible result the default, so a form added by a non-expert is still usable by keyboard and screen-reader visitors, on the smallest phone, in high-contrast mode.
Key features
- Accessible by construction — label per field, fieldset/legend for radio and checkbox groups, required state exposed to assistive tech.
- Error summary with managed focus — on a failed submit, a summary lists each problem as a link to its field, and focus moves to the first field that needs fixing, where its own message is announced (WCAG 2.2 SC 3.3.1, 2.4.3).
- Values kept on error — visitors never retype a whole form because one field failed (SC 3.3.7 Redundant Entry).
- Accessible anti-spam, no puzzles — an invisible honeypot plus a submission-time trap stop bots without a CAPTCHA, satisfying SC 3.3.8 Accessible Authentication. Optional Akismet and Cloudflare Turnstile support adds more protection when you want it.
- Multi-step forms — split a long form into short, numbered steps with a “Step break”. Each step is announced, focus is managed, and the form still works as one page without JavaScript.
- File uploads, off by default — add a File upload field, but nothing can be uploaded until you switch uploads on. Per-field size and allowed-type limits, with strict server-side checks on size, extension and real content.
- Live accessibility health check — a panel beside the builder reviews the form as you edit and flags issues (missing labels, empty steps, choice fields without options, and more) with a symbol and a word, never colour alone.
- Accessible email notifications — the admin notification and the visitor’s confirmation are built as accessible HTML email: a real data table with row headings, an alt-text logo and a plain-text alternative for every inbox. Pick one of six ready-made palettes (every band WCAG AAA) or set your own header, body and footer colours with a live contrast check, choose HTML or plain text only, and set the wording on each form.
- Know where a submission came from — an optional sender & device details section adds the time, the sender’s IP address (linked to a location look-up you can click), browser, operating system, device type, language and the pages involved to the notification you receive. Off until you switch it on, never added to the visitor’s confirmation, and no data leaves your site.
- Count submissions in Google Analytics, without code — give a form an event name (for example
contact_form_submit) and every stored submission is reported to Google Analytics 4 under that name, through gtag.js or Google Tag Manager, whichever the site already has. Quarantined spam is never counted. Every stored submission also raises a JavaScript event on the page (ananyoo_aaf_submitted) and a PHP action (ananyoo_aaf_after_submission) for your own integrations. - Reusable forms — each form is stored once and can be placed on many pages by ID.
- Stored entries — every submission is saved so nothing is lost if an email fails. List, search, trash and restore using the native WordPress screens.
- Resend a notification — if a notification never arrived, re-send it from the entry screen or straight from the Entries list. It is rebuilt from the stored entry through the same code as the original, so it arrives identical; the person who submitted is not emailed again.
- The dashboard matches the inbox — the entry screen shows the same sender & device details as the email, so there is one version of the truth.
- Spam quarantine — suspected spam is moved to a recoverable Spam view, not deleted, so false positives can be restored.
- Block and shortcode — insert the Accessible Form block, or use
[ananyoo_form id="123"]anywhere, just like a classic contact form. - Any-SMTP delivery — notifications are sent through
wp_mail(), so any SMTP plugin you already use handles deliverability. - Brand it from the admin screen — set the submit button, focus ring and example-text colours under Form appearance. Every pair is contrast-checked live at the right threshold (4.5:1 for text, 3:1 for the focus ring), and the hover shade is derived for you. Under the surface each one is a CSS custom property on
.aaf-form, so a theme can restyle the form by redeclaring tokens rather than fighting the stylesheet with!important. - Example text that behaves — an optional placeholder on free-text fields, fenced so it cannot do harm: never on a field without a label, never the accessible name, and set to a colour that passes contrast instead of the browser’s pale grey. The health panel warns if it repeats the label or reads as an instruction.
- 3 px focus ring, 44 × 44 targets — meets WCAG 2.2 SC 2.4.11/2.4.13 and 2.5.8 by default (both adjustable).
- Reflows cleanly at 320 px, respects prefers-reduced-motion, and supports Windows High Contrast / forced-colors.
- Works without JavaScript — the server validates and re-renders; JavaScript only enhances focus handling and adds an early client-side check.
- Clean uninstall option — off by default (your data is kept). Turn it on to remove every form, entry and setting when the plugin is deleted.
Links
- Plugin home & documentation
- Live demo
- More accessibility plugins from Ananyoo — further WordPress accessibility plugins are in active development.
- Support & contact
Privacy
This plugin stores the entries you receive in your own WordPress database and sends notification emails through your site’s own mail system (the standard wp_mail() function). Out of the box it sends your data to no third-party service and adds no tracking or front-end links of its own. If you give a form an Analytics event name, the plugin calls the Google Analytics or Tag Manager code that is already on your site; it loads no Google script itself and passes only the form’s ID, name and page path, never the visitor’s answers.
Two optional extras change this, and both are your choice:
- Akismet (off until you switch it on). When enabled, the submitted values, along with the sender’s IP address and browser user agent, are sent to Akismet to be checked for spam. This uses the Akismet plugin’s own connection and API key, under Automattic’s privacy policy. Please mention this in your own privacy policy before switching it on.
- Cloudflare Turnstile. The separate companion plugin you choose handles that request under its own terms.
Installation
- Upload the plugin to the
/wp-content/plugins/directory, or install it from the Plugins screen. - Activate it. Four ready-to-use example forms are created automatically: an enquiry form (showing every field type), a newsletter sign-up, a quick feedback form, and an advanced contact form that showcases every feature.
- Go to Ananyoo Accessible Forms to edit fields, or create a new form.
- Copy the form’s shortcode (e.g.
[ananyoo_form id="123"]) into any page or post, or add the Accessible Form block and choose your form. - Set the notification email (and optional email logo) under Ananyoo Accessible Forms Settings. Emails send out of the box; for the best inbox delivery we recommend — but do not require — a free SMTP plugin such as WP Mail SMTP or FluentSMTP.
Screenshots

The accessible form on the front end: real visible labels, helpful hint text and large, easy-to-tap fields, with each behaviour mapped to its WCAG 2.2 AA success criterion.

Accessible validation: an error summary that links to each problem, focus moved to the first field to fix, plus inline messages tied to each field and announced to screen readers, never by colour alone.

The accessible success state after a valid submission: a clear confirmation announced through a polite live region (SC 4.1.3), with focus moved to it so keyboard users are never left guessing.

Every submission is saved as a native WordPress entry. The list shows the sender, subject, form, date and IP, with Quick View, View and Trash actions.

A stored entry opened in the admin: every submitted field and value in a semantic table, with checkbox answers shown as Yes or No and the received time and IP set apart below.

Quick View opens any entry in an accessible modal dialog without leaving the list: focus moves into the dialog, Escape closes it, and focus returns to the link afterwards.

Build a form visually with the Accessible Form block, or drop its shortcode anywhere; both render the identical accessible markup.

Multi-step forms: add a "Step break" and a long form becomes short, numbered steps with a progress indicator, Previous/Next buttons, a spoken "Step X of N", and focus moved to each step — and it still works as one page without JavaScript.

A File upload field with its own maximum size and allowed file types. Uploads are off site-wide by default, so the field stays disabled with a clear note until you switch uploads on — safe by design.

The live accessibility health check beside the form builder: missing labels, empty steps, choice fields without options and more are flagged as you edit, each with a symbol and a word, never colour alone.

Accessible email design under Settings: a logo, six ready-made palettes and a background and text colour for the header, body and footer, each checked live for WCAG contrast so the email is readable in every inbox.

The accessible HTML notification: a real data table with row headings, an alt-text logo, a clearly labelled button to the stored entry, and the optional sender & device details section at the bottom. A matching plain-text copy is always included.

Six ready-made email palettes, each contrast-checked so every band reaches WCAG AAA (7:1 or better). Choosing one fills the colour pickers below, which stay editable and are re-checked live.

Choose the message format — accessible HTML with a plain-text alternative, or plain text only — and switch on the optional sender & device details section, which is added to your notification and never to the visitor's confirmation.
Faq
If you switch on Sender & device details (Settings > Email), the notification you receive also lists the sender’s IP address, browser, operating system, device type, language and referring page – all read from the request itself and already stored with the entry. An IP address is personal data in the EU and UK, so please mention this in your privacy policy before enabling it. Nothing is sent anywhere: the location link beside the IP is an ordinary link that your browser opens only when you click it. The look-up service can be changed with the ananyoo_aaf_ip_lookup_url filter.
No. The built-in honeypot and submission-time trap stop most bots without asking visitors to solve a puzzle, which keeps the form accessible. The plugin deliberately does not bundle a CAPTCHA, and never uses image-puzzle CAPTCHAs, which create barriers for many disabled visitors.
If your forms attract heavy, targeted spam and you want an extra layer, the most accessible choice is Cloudflare Turnstile — in its “Managed” / interaction-only mode it verifies most visitors invisibly, with no puzzle to solve. Add it with a dedicated Turnstile plugin (for example, “Simple CAPTCHA Alternative with Cloudflare Turnstile”). See the next question for connecting it to these forms.
The easiest way is automatic. Install the free “Simple CAPTCHA with Cloudflare Turnstile” plugin (by RelyWP) and add your Turnstile keys. These forms then detect it and show the Turnstile challenge by themselves — no code, no shortcode. A failed or missing challenge is reported through the same accessible error summary as any other field, with the visitor’s answers kept. You can switch this off under Ananyoo Accessible Forms Settings.
Importantly, this plugin still loads no third-party script itself; the companion plugin renders and enqueues the Turnstile widget. So this plugin makes no external requests on its own.
Prefer a different CAPTCHA, or want full control? Two hooks are also available:
do_action( 'ananyoo_aaf_before_submit', $form_id, $uid, $errors )— fires just before the submit button, inside the form. Use it to output a verification widget.apply_filters( 'ananyoo_aaf_validation_errors', $errors, $raw, $fields, $form_id )— return a non-emptyname => messagearray to reject a submission. The message then appears in the accessible error summary and beside the form.
Developers can also disable the built-in Turnstile integration with the ananyoo_aaf_use_turnstile filter (return false).
Reviews
Simple, Useful, and Easy to Work With
By salehul786 on September 10, 2026
I’ve really enjoyed using Ananyoo Accessible Forms – Contact Form & Entry Storage. What I like most is how simple it makes managing forms and keeping track of submitted entries. I don’t have to deal with unnecessary complexity, which makes the whole process feel much more manageable.
The entry storage feature is especially helpful because it gives me an easy way to keep submissions organized instead of worrying about losing important responses. I also appreciate the focus on accessibility, as creating forms that are easier for everyone to use is something I value.
Overall, I find this plugin practical, straightforward, and genuinely useful. It’s one of those plugins that quietly makes everyday website management easier, and I’d definitely recommend giving it a try.
Changelog
1.11.6 – 4 October 2026
- New: choose the heading level of the form title (H2 to H6), or hide the title, so the form fits the heading outline of the page it sits on (WCAG 1.3.1). Set it per form under Form settings, per page with the shortcode ([ananyoo_form id=”12″ heading_level=”3″] or show_title=”no”), or in the Accessible Form block settings. Error summary and step headings now sit one level below the title instead of always H3. A hidden title still names the form for screen readers. The default stays a visible H2, so existing forms do not change.
1.11.5 – 30 September 2026
- Fix: the error summary heading, list and links keep a fixed dark red on the summary’s light background (about 9:1), so theme link colours and high-contrast modes can no longer lower their contrast. Change it with the –aaf-errors-text custom property. Found in an IAAP manual WCAG 2.2 AA audit.
1.11.4 – 23 September 2026
- Fix: the notification recipient no longer freezes a copy of the site admin email. On activation the plugin copied the admin email into its settings and into each starter form, so after a site migration or an admin email change, notifications kept going to the old address. A blank setting now follows the live site admin email at send time, the starter forms store no recipient of their own, and the settings screen shows which address blank will use.
- New: an admin notice on the Dashboard, the Plugins screen and the plugin’s own screens asks for a notification email until one is entered. Until then, notifications still go to the site admin email, so no enquiry is lost.
1.11.3
- Fixed: the notification email left a wide empty gap between the field
labels and the values. The label cells carried white-space:nowrap, so one
long label anywhere in the form, such as a consent checkbox question,
stretched that whole column to its own width and squeezed every value into
a narrow strip beside it. Labels wrap now and the column is a sensible
share of the width. - Fixed: the email logo was requested at the “medium” size. That is a
registered image size, so on a site whose media settings crop it the email
carried a cropped piece of the logo. The full file is used now. - The logo is given a real width and height attribute, worked out from the
file’s own dimensions. Mail clients are much worse than browsers at
guessing a missing height, and several of them reserved the wrong space or
scaled the picture badly when only a width was given. A logo narrower than
the 180px cap is no longer stretched up to it either. - Nothing else changed. Colours, wording and the plain-text alternative are
all as before.
1.11.2
- The hidden nonce field no longer carries an id. wp_nonce_field() sets an
id that matches the field name, so two forms on one page ended up with
the same id and the W3C checker reported a duplicate ID error. Nothing
labels or scripts that field, so the id is simply gone. The nonce name,
the value and the referer field are all unchanged, and existing forms
keep working with no migration.
1.11.1
- Valid HTML: the empty action=”” attribute is removed from the front end form
and from the settings form. The W3C Nu checker reports action=”” as an error,
“Bad value for attribute action on element form: Must be non-empty”. A form
with no action attribute submits to the current URL, which is exactly what
action=”” was doing, so behaviour is unchanged. - No accessibility change. The form keeps aria-labelledby pointing at its
heading, novalidate stays so the plugin’s own error messaging continues to
handle WCAG 3.3.1 and 3.3.3 rather than native browser bubbles, and no label,
ARIA or focus behaviour is touched. Verified WCAG 2.2 AA is unaffected.
1.11.0
- New: count submissions in Google Analytics without writing code. Each form now has an optional Analytics event name in its settings box (for example
contact_form_submit). With a name set, every stored submission is reported to Google Analytics 4 under that name withform_id,form_nameandform_page, through gtag.js when the site has it and otherwise through the Google Tag Manager dataLayer. The plugin loads no Google script of its own, sends nothing when the field is blank, and never passes the visitor’s answers. - New: a submission the anti-spam quarantines (honeypot or Akismet) still shows the visitor the same thank-you message, but it is no longer counted: the status region carries the
data-aaf-submittedmark only for a stored, real submission, so a site’s own analytics code can tell the two apart as well. - New: every stored submission dispatches a bubbling
ananyoo_aaf_submittedJavaScript event from the status region (event.detailholdsformId,formName,eventName,pageandelement), for chat widgets, CRMs and other scripts that need to know a form was sent. The status region also now carriesdata-aaf-formanddata-aaf-form-name. - New: a PHP action,
ananyoo_aaf_after_submission( $entry_id, $form_id, $data, $status, $reason ), runs once an entry is stored, real or quarantined, so a webhook, CRM or mailing-list integration needs no core change. Check$statusbefore acting; quarantined entries carry the spam status. - Readme: the description and feature list still said the error summary “takes focus”. The plugin has long moved focus to the first field that needs fixing (its own message is announced there) and kept the summary as a linked overview, so the wording now matches the behaviour. The sender & device details paragraph in the FAQ had lost its question heading; restored.
1.10.0
- New: Form appearance settings. The submit button, the focus ring and the example-text colour are now yours to set, under Forms > Settings > Form appearance. Each pair is measured against the colour it actually sits on as you pick it, so a brand colour can never quietly break the form: the button text is judged at 4.5:1 (WCAG 1.4.3) and the focus ring at 3:1 against your page (WCAG 1.4.11, the correct threshold for a non-text indicator). The button’s hover shade is worked out by darkening your colour, so there is one decision to make instead of two, and hover can never lose contrast.
- New: every front-end colour and size is now a CSS custom property on
.aaf-form–--aaf-accent,--aaf-accent-hover,--aaf-on-accent,--aaf-focus,--aaf-placeholder, alongside the existing--aaf-border,--aaf-text,--aaf-error,--aaf-targetand--aaf-focus-width. A theme brands the form by redeclaring the tokens. No rule in the stylesheet has to be overridden with!importantany more. - New: optional Example text (a placeholder) on short text, email, telephone, web address, number and long text fields. It is deliberately fenced in so it cannot become the accessibility problem placeholders usually are: it is offered only on those six field types, it is dropped at render time if the field has no label, its colour is set to 5.9:1 on white instead of the browser’s pale grey (WCAG 1.4.3), and
opacity: 1stops Firefox dimming it back down. The visible label is always rendered, so the accessible name never comes from the placeholder. - New: three checks in the accessibility panel watch how example text is used – example text on a field with no label (Fix), example text that merely repeats the label (Check), and example text that reads as an instruction rather than a sample (Check), because an instruction disappears at the first keystroke, exactly when it is still needed.
- Fix: Minimum target size and Focus indicator width never reached the page. Both were printed as custom properties on
:root, but the stylesheet declares its defaults on.aaf-form, and a property set on the element itself always beats one inherited from:root. Every site has been running the 44px and 3px defaults no matter what those two settings said. They are now written onto.aaf-formand take effect. If you had set other values, they will apply the moment you update – please have a look at your forms. - Change: on a multi-step form, the Previous button now takes its label colour from
--aaf-textrather than the accent, and uses the accent only for its border. A pale brand colour can no longer drag that label below 4.5:1; a border only has to reach 3:1.
1.9.8
- New: Resend notification. If a notification never arrives – a mail server hiccup, a spam folder, or an SMTP plugin set up after the fact – you can re-send it yourself: a button in the Publish box on the entry screen, beside Move to Trash, and a “Resend notification” link on every row of the Entries list. The message is rebuilt from the stored entry through exactly the same code path as the original, so it arrives identical to what should have come at the time, including the current design and the sender & device details. The person who submitted is never emailed a second time.
- The entry screen now shows the same Sender & device details as the email – received time, IP address with the location link, browser, operating system, device, language, and the pages involved – instead of a single “Received … from IP …” line, so what you read in the dashboard matches your inbox.
- Re-sends use the page recorded with the entry for the “Submitted from” line, rather than the admin screen the button was pressed on.
1.9.7
- New: an optional Sender & device details section at the bottom of the notification you receive. It shows when the message arrived, the sender’s IP address linked to a location look-up you can click, their browser, operating system, device type, browser language, the page the form was on and the page they arrived from. Off until you switch it on in Forms > Settings > Email, because an IP address is personal data – please mention it in your privacy policy first. The plugin still contacts no outside service: the location link is only opened if you click it, and it can be pointed at any provider with the new
ananyoo_aaf_ip_lookup_urlfilter. - New: six ready-made email palettes (Ariade Navy, Maximum contrast, Slate, Forest, Plum, Ink & Sand). Every band of every palette reaches WCAG AAA (7:1 or better). Choosing one fills the existing colour pickers, which stay editable, and the live contrast readouts update as usual.
- New: a Plain text only message format, alongside the existing HTML-with-text-alternative. Useful where a mail policy or client prefers text; the security section and all the details are included in the text version too.
- Fix: the “View this entry in your dashboard” line printed the raw URL as plain text and relied on the mail client to detect it. It is now a real, clearly labelled link styled as a button in the HTML version, with the full URL still spelled out in the plain-text version.
- The sender’s referring page and preferred language are now recorded with each entry, for the details section above.
1.9.6
- Compatibility: “Tested up to” raised to WordPress 7.1. The form block already uses Block API version 3 and the front-end and admin scripts are vanilla JavaScript with no jQuery UI, so the fully iframed editor and the jQuery UI 1.14.2 update in 7.1 need no code change.
1.9.5
- New: every submission now records the page it was sent from. The notification email shows a “Submitted from: [page]” link, the Entries list has a new “Page” column, and the entry view shows the source page — helpful when the same form is placed on several pages. The URL is validated to your own site.
- Fix: the bundled “Advanced Contact Form” now also seeds on activation, so a site that already had forms — and missed the one-time upgrade window — gets it reliably with a simple Deactivate Reactivate. Idempotent: it is never duplicated.
- Polish: the standalone form-preview page now wraps its content in a landmark and carries a level-one heading, so automated best-practice checks (e.g. axe) report a clean page structure. The embedded form on your real pages is unchanged (your theme already supplies the landmarks and H1).
Earlier versions
The complete history, back to 1.0.0, is in CHANGELOG.md in the plugin folder.