Styling and theming
OctaForms ships a deliberately neutral stylesheet so a form looks tidy the moment you add it. This page shows how to make forms and the callback widget match your theme, from a one-line colour change to a full custom look.
There are two ways to style, and you will usually mix them:
- Set a few CSS variables. The fastest path. Change colours, borders, radius and spacing without writing a single override rule.
- Write your own CSS rules. For layout or anything the variables do not cover. The plugin is built to make this easy and hard to break, as long as you follow two small rules below.
How the plugin styles get out of your way
Section titled “How the plugin styles get out of your way”Every default rule is wrapped in :where(), which means it carries zero specificity. The styles still apply, but any selector you write beats them. A plain class is enough:
.my-form-input { border: 2px solid #2563eb; }No need to repeat .octa-forms__field input or stack extra classes to win. There is one deliberate exception: the submit button keeps real specificity so a bare button {} reset in your theme cannot wash it out to a blank control. More on that below.
The markup you are styling
Section titled “The markup you are styling”OctaForms renders the same markup everywhere (the shortcode, the helper, and the widget popup), so one set of styles covers all of them.
<form class="octa-forms" data-octa-forms="{slug}"> <div class="octa-forms__field octa-forms__field--text"> <label for="…">Name</label> <input type="text" …> </div>
<div class="octa-forms__field octa-forms__field--checkbox"> <label> <input type="checkbox" value="on" …> <span class="octa-forms__consent">I agree to the privacy policy</span> </label> </div>
<div class="octa-forms__actions"> <button type="submit" class="octa-forms__submit">Send</button> </div></form>Each field is a .octa-forms__field wrapper with a type modifier: --text, --email, --tel, --date, --select, --textarea, --file, --checkbox.
Two layouts matter when you write CSS:
- Regular fields (text, email, tel, date, select, textarea, file) put the
<label>above the control. The wrapper is a vertical flex column. - The checkbox is different. Its
<label>wraps the box and the consent text side by side. The<input type="checkbox">is a small native box, not a full-width control. This distinction is the single most common source of styling bugs. See Do not style bareinput.
Retheme a form with variables
Section titled “Retheme a form with variables”For the most common changes you do not need an override rule. Set a few values on .octa-forms:
.octa-forms { --octa-forms-submit-bg: #2563eb; --octa-forms-submit-bg-hover: #1d4ed8; --octa-forms-control-radius: 12px; --octa-forms-control-border: 1px solid #cbd5e1; --octa-forms-focus-color: #2563eb;}Because you set them on the container, you can scope different looks to different places:
.site-footer .octa-forms { --octa-forms-submit-bg: #fff; --octa-forms-submit-color: #111; }Every form variable
Section titled “Every form variable”| Variable | Controls | Default |
|---|---|---|
--octa-forms-gap |
Space between fields | 1rem |
--octa-forms-max-width |
Max width of the form | 640px |
--octa-forms-label-size |
Label font size | 0.9375rem |
--octa-forms-label-weight |
Label font weight | 600 |
--octa-forms-control-bg |
Input / select / textarea background | #fff |
--octa-forms-control-color |
Control text colour | inherit |
--octa-forms-placeholder-color |
Placeholder text colour | #b3b3bb |
--octa-forms-control-padding |
Control padding | 0.55rem 0.7rem |
--octa-forms-control-border |
Control border | 1px solid #c7c7cf |
--octa-forms-control-radius |
Control corner radius | 6px |
--octa-forms-focus-color |
Focus border and outline | #111 |
--octa-forms-submit-bg |
Submit button background | #111 |
--octa-forms-submit-bg-hover |
Submit button hover background | #000 |
--octa-forms-submit-color |
Submit button text colour | #fff |
--octa-forms-submit-padding |
Submit button padding | 0.6rem 1.4rem |
--octa-forms-submit-radius |
Submit button corner radius | 6px |
--octa-forms-error-color |
Validation error colour | #b91c1c |
--octa-forms-thank-you-bg |
Thank-you panel background | transparent |
--octa-forms-thank-you-border |
Thank-you panel border | transparent |
Writing your own rules
Section titled “Writing your own rules”When the variables are not enough, write normal CSS. Two rules keep you out of trouble.
Do not style bare input
Section titled “Do not style bare input”A checkbox is an <input> too. If you style every input in a field, you also blow up the checkbox into a full-width, padded, bordered box. It then looks oversized or centred and pushes the layout around. This is the most common styling bug.
/* Wrong — this also hits the checkbox and radio */.octa-forms__field input { width: 100%; padding: 12px; border: 1px solid #ccc; }Always exclude checkbox and radio from generic input styling:
/* Right */.octa-forms__field input:not([type="checkbox"]):not([type="radio"]),.octa-forms__field select,.octa-forms__field textarea { width: 100%; padding: 12px; border: 1px solid #ccc; border-radius: 8px;}Apply the same :not(...) to any :focus, background or border rule that targets input. Style the checkbox only through its own selectors:
.octa-forms__field--checkbox input[type="checkbox"] { width: 1.1em; height: 1.1em; accent-color: #2563eb; }.octa-forms__field--checkbox label { gap: 0.5rem; align-items: flex-start; }.octa-forms__consent { font-size: 0.9rem; line-height: 1.45; }.octa-forms__consent a { text-decoration: underline; }The submit button needs a class, not bare button
Section titled “The submit button needs a class, not bare button”.octa-forms__submit keeps real specificity on purpose, so a theme reset like button { background: none } cannot strip it. To restyle the button with your own rule, use a selector that includes the class, or set the --octa-forms-submit-* variables:
.octa-forms__submit { border-radius: 999px; }Class reference
Section titled “Class reference”| Class | What it is |
|---|---|
.octa-forms |
The <form> element. Set your variables here. |
.octa-forms__field |
A single field wrapper (vertical flex column). |
.octa-forms__field--{type} |
Type modifier: text, email, tel, date, select, textarea, file, checkbox. |
.octa-forms__field label |
A field label. |
.octa-forms__label--sr |
A label hidden visually but kept for screen readers (placeholder-labels mode). Leave it hidden. |
.octa-forms__field--checkbox label |
The checkbox row (box plus consent text). |
.octa-forms__consent |
The consent text next to a checkbox. |
.octa-forms__consent a |
A link inside consent text. |
.octa-forms__actions |
The submit button wrapper. |
.octa-forms__submit |
The submit button (keeps real specificity). |
.octa-forms-error / .octa-forms-error--form |
A field error / a form-level error (added by the helper). |
.octa-forms__thank-you / -title / -text |
The thank-you screen that replaces the form after a successful submit. |
.octa-forms__noscript |
The message shown when JavaScript is off. |
The focus ring and the invalid-field and error colours keep their normal weight on purpose, so a light theme override does not accidentally remove those accessibility signals.
The callback widget
Section titled “The callback widget”The floating handset button and its popup are neutral by default (a black button and handset). They read their colours, radius and spacing from --cb-* variables scoped to .octa-forms-callback. Set them in your theme to retint the whole widget.
One variable does most of the work
Section titled “One variable does most of the work”--cb-primary is the widget’s brand colour. Setting it alone retints, in one line:
- the floating button and its pulsing rings,
- the button and tab focus outlines,
- the active tab colour and its underline,
- the input focus ring inside the popup,
- the popup’s submit button.
.octa-forms-callback { --cb-primary: #e11d2a; /* your brand colour */}That is the important one to know: the popup’s send button takes its colour from --cb-primary, not from --octa-forms-submit-bg. Inside the popup the widget’s own styles win. To change the popup button, set --cb-primary (or override .octa-forms-callback__popup .octa-forms__submit).
Every widget variable
Section titled “Every widget variable”| Variable | Controls | Default |
|---|---|---|
--cb-primary |
Brand colour: button, rings, focus, active tab, popup focus and submit | #111 |
--cb-white |
Button icon, popup surfaces | var(--color-white, #fff) |
--cb-black |
Popup text | var(--color-black, #444) |
--cb-gray-dark |
Muted text and placeholders | var(--color-gray-dark, #777) |
--cb-gray-medium |
Borders and tab separators | var(--color-gray-medium, #d9dee6) |
--cb-gray-light |
Light fills | var(--color-gray-light, #f7f9fc) |
--cb-radius |
Corner radius across the widget | var(--radius, 10px) |
--cb-space-* |
Spacing scale (xs…xxl) |
Falls back to --spacing-* theme tokens |
--cb-fs-* |
Font-size scale (small…huge) |
Falls back to --font-size-* theme tokens |
Because spacing and type fall back to your theme tokens (--spacing-*, --font-size-*), the widget often adopts your theme’s rhythm with no extra work. Override the --cb-* values only where you want to differ.
A worked example, retinting to a brand and rounding the corners:
.octa-forms-callback { --cb-primary: #e11d2a; --cb-radius: 16px; --cb-gray-medium: #dfe3ea;}Widget class reference
Section titled “Widget class reference”If you need finer control than the variables give, these are the pieces you will style most often.
| Class | What it is |
|---|---|
.octa-forms-callback |
The widget host (fixed position). Set --cb-* here. |
.octa-forms-callback--left / --right |
Which side the button sits on. |
.octa-forms-callback__toggle |
The round floating button. |
.octa-forms-callback__icon |
The handset icon (a CSS mask, coloured with currentColor). |
.octa-forms-callback__bubble |
The small teaser bubble by the button. |
.octa-forms-callback__popup |
The open popup wrapper. |
.octa-forms-callback__dialog |
The popup panel. |
.octa-forms-callback__tabs / __tab |
The contact-method tabs (phone, callback, message). |
.octa-forms-callback__tab[aria-selected="true"] |
The active tab. |
.octa-forms-callback__phone / __tel |
The phone number shown in phone mode. |
The form inside the popup is the same .octa-forms markup described above, so everything in the sections before this one applies there too. Its layout rules are zero specificity, so your theme still wins.
Turning the stylesheet off
Section titled “Turning the stylesheet off”If you would rather style everything yourself from scratch, turn the form stylesheet off with the disable_styles setting or the octa_forms_styles_enabled filter. When it is off the --octa-forms-* variables and the base layout go away too, so your theme has to supply the full layout, including the checkbox row. See Frontend assets.
Before you ship — checklist
Section titled “Before you ship — checklist”- No rule targets
.octa-forms__field inputwithout:not([type="checkbox"]):not([type="radio"]). - The checkbox renders as a small native box on the left, with the consent text wrapping beside it and no gap pushing the button around.
- The submit button is styled through
.octa-forms__submitor the--octa-forms-submit-*variables, not barebutton. - The widget is retinted with
--cb-primary(remember the popup send button follows it, not--octa-forms-submit-bg). - Focus, error and thank-you states still stand out against your theme.
.octa-forms__label--srstays visually hidden.- Checked in every place a form appears (page sections, the widget popup), since different contexts can carry different theme styles.