Conversational forms and the dedicated page
Paid add-on. A form can present itself one question per screen, and take over a WordPress page stripped of its theme.
This document describes what the module guarantees and what it refuses. The user guide gives the short version, under “One question at a time”.
The idea, and what it avoids
The split into screens happens server-side. The module recomposes the form’s pages — one question per page — and the markup produced is that of an ordinary multi-page form.
That is the whole architectural decision, and it reads backwards: as what the module does not contain. No step handling, no focus management, no progress bar, no validation before leaving a screen, no skipping of screens emptied by conditional logic. All of that exists in the core, for multi-page forms, and has worked for years. Rewriting it would have created a second navigation, which would have diverged from the first at the very first fix.
The setting lives in the form’s settings, under conversation:
{
"conversation": {
"enabled": true,
"shell": "neutral",
"title": "Let’s talk about your project",
"description": "Three questions, two minutes.",
"logo_id": 42,
"accent": "#2271b1",
"background": "#ffffff"
}
}
Without JavaScript
The visitor sees a complete multi-page form: every question is on the page, the step buttons stay hidden, and the send happens in one go.
This degraded version took no code at all. It is the direct consequence of the choice above: since the markup is that of an ordinary multi-page form, it behaves like one when the script does not run.
How screens are composed
- One question, one screen.
- A text block or a section heading is not a question. On its own it would make a screen on which nothing can be done but move forward. It is attached to the question it introduces — which is its role. At the end of a page it introduces nothing any more: it is a conclusion, and it gets its own screen.
- A hidden field has no screen. It travels on the first screen of its page, because it has to stay in the form to be submitted.
- Payment comes last. Asking someone to pay before they have finished answering makes them give up, and that is the plan’s explicit recommendation.
- Page breaks remain boundaries. Screens are composed within each existing step: a form already split keeps its split, finely re-split. Crossing it would undo a choice somebody made.
- A page with no question stays as it is. Re-splitting it would produce empty screens.
A step’s title appears only on its first screen. Repeating it would have a screen reader announce “Your details” five times in a row; the following screens get the core’s fallback — “Step 3 of 12” — which is the useful information at that moment.
Accessibility
Everything comes from the core, and is therefore already proven:
- each screen is a
fieldsetwith its legend, onto which focus moves when the step changes — moved, never trapped; - progress is a bar and a live sentence, “Step 3 of 12”;
- the buttons are real
buttonelements, not links in disguise; - errors stay associated with their field.
The module adds only two gestures:
- Enter moves forward, except in a textarea where it inserts a line, on a
button or a link where it activates them, and on the last screen where it
submits.
Shift+Enteris always left to the browser. - The next screen comes back into view by scrolling, immediately if the system asks for as little movement as possible. The appearance animation then disappears entirely.
The dedicated page
A page template, picked under “Page Attributes”: SolisForms — dedicated page. The administrator creates an ordinary page, inserts the form into it, and chooses that template.
No URL of our own. An address served outside WordPress’s cycle loses rewrites, redirects, page caching, sitemaps, the site’s security headers and the current language. A page keeps all of that — with its address, its SEO and its rights — and the template removes only the theme.
No iframe, which the plan rules out. It would isolate the form from the document: focus trapped at the border, height to recompute constantly, third-party cookies, and a screen reader announcing “frame” before every question.
The template calls wp_head() and wp_footer(). Without them, no plugin
stylesheet, no public script, no refreshed submission token — that is the classic
mistake of the genre, and it produces a mute form.
The shell
Two appearances. theme keeps the site’s surroundings: a form inserted in the
middle of an article has no reason to erase the article. neutral only applies
on a page carrying the dedicated template — and it is the template, not this
setting, that removes the surroundings.
The header — logo, title, introduction — appears only if it is filled in. The
title is an h2: a form inserted in an article is not that article’s title, and
two h1 elements on a page are a structural error a screen reader reports. The
logo carries empty alternative text: it is decorative, the name appearing right
beside it in full.
Colours are validated, not sanitised. They end up in a style attribute, as
custom properties. A value you would clean up would let through whatever the
cleaning did not anticipate; a value that must match #rrggbb or not exist at all
lets nothing through. A refused colour falls back to the theme’s, which is always
displayable.
The two core extension points
This module asked two filters of the core, and no specific code inside it:
solis_forms_form_pagesrecomposes the pages before rendering;solis_forms_form_markupwraps the markup produced.
They are public and documented in hooks-reference.md.
Both revalidate what is handed back to them: an add-on that gets it wrong must
not make the form disappear from a public page.
What the module does not do
No free-form visual editor, no custom URL, no A/B testing, no automated conversation, no video, no arbitrary branching — the core’s conditional logic remains the only way to make a question depend on an answer.
It touches neither the validations, nor the calculations, nor the payments, nor the save-and-resume, nor the submission: they are the same as on an ordinary form, since it is the same form.
