Ako som upravila formulár aby spĺňal požiadavky prístupnosti
Keď sa zameriame na kritériá ktoré vieme posúdiť až podrobnou kontrolou podľa platných Slovenských predpisov a noriem, zistíme že ako hlavné vodítko treba použiť WCAG 2.1 na úrovni AA. Podľa neho musia formuláre spĺňať napríklad tieto požiadavky:
Identify Input Purpose – vstupné polia musia mať atribút autocomplete, aby čítačky vedeli, aký typ údajov sa očakáva (meno, priezvisko, email…).
3.3.1 Error Identification – chyby musia byť jasne označené a zrozumiteľné. Nie len farebne, ale aj kontrastným textom, ktorý je zároveň správne prečítaný čítačkou.
3.3.2 Labels or Instructions – každé pole musí mať správne prepojený štítok (label alebo aria-describedby).
4.1.3 Status Messages – oznamy o chybách a úspechu musia byť ozvučené bez toho, aby narušili používateľovu orientáciu. Čítačka nemôže zahltiť používateľa napríklad piatimi upozorneniami naraz.
2.4.3 Focus Order – poradie zamerania klávesou TAB musí byť logické a nesmie obsahovať prvky, ktoré nemajú byť dostupné. Môže sa stať že navigáciou vo formulári čítačka preskočí povinná zaškrtávacie pole pre súhlas s podmienkami a potom sa formulár nemôže odoslať. Tiež sa stáva naopak, že bežne neviditeľná reCaptcha je prečítaná čítačkou, lebo pre čítačku nie je dostatočne schovaná.
Pri tvorbe webov je často podceňovaná prístupnosť formulárov. Podľa WCAG 2.1 AA musia formuláre spĺňať napríklad tieto požiadavky:
Identify Input Purpose – vstupné polia musia mať atribút autocomplete, aby čítačky vedeli, aký typ údajov sa očakáva (meno, priezvisko, email…).
3.3.1 Error Identification – chyby musia byť jasne označené a zrozumiteľné.
3.3.2 Labels or Instructions – každé pole musí mať správne prepojený štítok (label alebo aria-describedby).
4.1.3 Status Messages – oznamy o chybách a úspechu musia byť ozvučené bez toho, aby narušili používateľovu orientáciu.
2.4.3 Focus Order – poradie zamerania klávesou TAB musí byť logické a nesmie obsahovať prvky, ktoré nemajú byť dostupné.
Prečo som nepoužila Divi formulár
Formulár vstavaný v Divi nespĺňal tieto podmienky a jeho kód bol ťažko upraviteľný. Nevylučujem, že sa pustím aj do toho, lebo autori DIVI zjavne toto nepovažujú za prioritu a neexistujú žiadne informácie o tom že by to v blízkej dobe plánovali opraviť.
Rozhodla som sa použiť bezplatný plugin Formidable Forms, ktorý sa dá prispôsobiť vlastným PHP, JavaScriptom a CSS. Myslím že sa mi to nakoniec podarilo aj napriek tomu, že tých úprav bolo treba spraviť viac než som pôvodne predpokladala.
Alternatívy nad ktorými som uvažovala:
DIVI zabudovaný formulár – všade počúvam že to ani celkom nejde, tak som to radšej ani neskúšala
Gravity Forms – podľa ich marketingu sa zameriavajú práve na prístupnosť, takže v prípade že ste ochotní platiť nie práve nízky ročný poplatok, určite stojí za úvahu. Pre zložitejší formulár s viacerými typmi polí, kalkuláciami, kalendárom, alebo inými, by som ho určite zvážila..
WS Form – má dobrú podporu pre accessibility, ale vo free verzii má obmedzenia.
CF7 – veľmi otvorené riešenie, určite je možné prispôsobiť, ale treba viac kódu na dosiahnutie plnej prístupnosti.
Formidable Forms – víťaz. Nájdete ho Free v ponuke pluginov vo WordPresse. Veľmi ľahko upraviteľné základné nastavenia vzhľadu aj notifikácií, spôsobu načítania priamo v nastaveniach formulára v plugine
Konkrétne nastavenia kódom
1. PHP úpravy
Tento kód pre vloženie do functions.php v Child téme je vlastne len fallback riešenie pre prípad, že je vypnutý Javascript. Pozor!! V kóde treba doplniť vlastné ID a keys platné pre aktuálny formulára a stránku. Určite na vašej stránke nebude rovnaké ako tu.
Upravuje nasledujúce problémy:
Dopĺňanie atribútov: pridané required a autocomplete k povinným poliam bez zásahu do <form> tagu (prevencia proti nonce_failure).
Oprava oznamov:
odstránenie role=“alert“ z jednotlivých chýb (prevencia „sirény“),
pridané role=“alert“ pre globálny súhrn chýb a role=“status“ pre úspešnú hlášku.
Bezpečnostné úpravy: zamedzenie pádom skriptu pri chýbajúcich parametroch a odladenie regexov.
Fallback: možnosť nechať zapnuté len základné doplnenie autocomplete a required pre bez-JS režim.
// Nastavenie Formidable forms pre accessibility:// --- No-cache pre /kontakt (bezpečné) ---
add_action('send_headers', function () {
if (function_exists('is_page') && is_page('kontakt')) {
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
}
});// --- Nastavenia (uprav podľa seba) ---
if (!defined('WEBYXL_A11Y_FORM_ID')) define('WEBYXL_A11Y_FORM_ID', 1); // ID formulára
if (!defined('WEBYXL_A11Y_FORM_H1')) define('WEBYXL_A11Y_FORM_H1', 'contact-heading'); // len info; form tag neupravujeme// Pomocná: sme na front-ende na stránke /kontakt ?
if (!function_exists('webyxl_is_contact_page')) {
function webyxl_is_contact_page() {
if (is_admin() || wp_doing_ajax()) return false;
if (function_exists('is_page') && is_page('kontakt')) return true;
global $post;
return $post && isset($post->post_name) && $post->post_name === 'kontakt';
}
}if (class_exists('FrmFormsController')) {
// A) REQUIRED + AUTOCOMPLETE – verzia bez is_page guardu, 2. parameter je voliteľný
if (!function_exists('webyxl_a11y_frm_field_input_html')) {
function webyxl_a11y_frm_field_input_html($input, $field = null){
try {
if (!is_string($input) || $input === '' || !is_object($field)) return $input;
if (!isset($field->form_id) || (int)$field->form_id !== (int)WEBYXL_A11Y_FORM_ID) return $input;// len typy, ktoré chceme upravovať
$type = isset($field->type) ? strtolower((string)$field->type) : '';
$allowed = array('text','email','textarea');
if (!in_array($type, $allowed, true)) return $input;// nehrab sa do hidden/submit/honeypotu
if (stripos($input, 'type="hidden"') !== false) return $input;
if (stripos($input, 'class="frm_verify') !== false) return $input;
if (stripos($input, 'type="submit"') !== false) return $input;// required + aria-required ak je pole Required v Builderi
if (!empty($field->required)) {
if (stripos($input, ' required') === false) {
$input = preg_replace('/(<(input|textarea)\b[^>]*)(?=>)/i', '$1 required', $input, 1);
}
if (stripos($input, 'aria-required="true"') === false) {
$input = preg_replace('/(<(input|textarea)\b[^>]*)(?=>)/i', '$1 aria-required="true"', $input, 1);
}
}// autocomplete pre inputy (nie textarea)
static $ac = array(
'qh4icy' => 'given-name', // Meno
'ocfup1' => 'family-name', // Priezvisko
'29yf4d' => 'email', // Email
);
if ($type !== 'textarea' && isset($field->field_key, $ac[$field->field_key]) && stripos($input, 'autocomplete=') === false) {
$input = preg_replace('/(<input\b[^>]*)(?=>)/i', '$1 autocomplete="'.$ac[$field->field_key].'"', $input, 1);
}return $input;
} catch (Throwable $e) {
error_log('webyxl_a11y_frm_field_input_html: '.$e->getMessage());
return $input;
}
}
}
remove_filter('frm_field_input_html', 'a11y_frm_field_input_html', 10); // ak by stará verzia ešte visela
add_filter('frm_field_input_html', 'webyxl_a11y_frm_field_input_html', 10, 2);// B) stíšenie field-error alertov
add_filter('frm_include_alert_role_on_field_errors', function($on){
return webyxl_is_contact_page() ? false : $on;
}, 10, 1);// C) ARIA pre globálne správy (form tag nechávame tak!)
if (!function_exists('webyxl_a11y_frm_filter_final_form_messages')) {
function webyxl_a11y_frm_filter_final_form_messages($html, $form = null, $atts = null){
if (!webyxl_is_contact_page() || !is_string($html) || $html === '') return $html;// overíme, že ide o náš formulár (podľa $form alebo skrytého inputu form_id)
$is_target = false;
if (is_object($form) && isset($form->id)) {
$is_target = ((int)$form->id === (int)WEBYXL_A11Y_FORM_ID);
} elseif (preg_match('/name="form_id"\s+value="(\d+)"/i', $html, $m)) {
$is_target = ((int)$m[1] === (int)WEBYXL_A11Y_FORM_ID);
}
if (!$is_target) return $html;// úspech → status
$html = preg_replace(
'/(<div[^>]*class="[^"]*\bfrm_message\b[^"]*"[^>]*)>/i',
'$1 role="status" aria-live="polite" aria-atomic="true" tabindex="-1">',
$html
);
// globálna chyba → alert
$html = preg_replace(
'/(<div[^>]*class="[^"]*\bfrm_error_style\b[^"]*"[^>]*)>/i',
'$1 role="alert" aria-live="assertive" tabindex="-1">',
$html
);
return $html;
}
}
add_filter('frm_filter_final_form', 'webyxl_a11y_frm_filter_final_form_messages', 10, 3);
}// Zrušenie "sirény" v čítačke
// Popis opravy: z per-field chýb odstrániť role="alert" a ponechať iba globálny súhrn s role="alert". Ak Formidable nevie nastaviť, môžeš to zahasiť výstupným filtrom:
add_filter('frm_filter_final_form', function($html,$form=null,$atts=null){
// len pre ID=1 – uprav si podľa seba
if (!preg_match('/name="form_id"\s+value="1"/',$html)) return $html;
// odstráni role="alert" z .frm_error (ale NIE z .frm_error_style)
$html = preg_replace('/(<div[^>]*class="[^"]*\bfrm_error\b[^"]*"[^>]*)\srole="alert"/i','$1',$html);
return $html;
},10,3);// A aby sme spoľahlivo „umlčali“ role="alert" na per-field chybách (keď FF ignoruje filter B), pridaj fallback post-processing:
// Odstráň role="alert" len z per-field chýb (.frm_error), globálny súhrn (.frm_error_style) ponechaj
add_filter('frm_filter_final_form', function($html, $form = null, $atts = null){// obmedz len na tvoj formulár (ID si uprav podľa seba)
$is_target = is_object($form) && isset($form->id) ? ((int)$form->id === (int)WEBYXL_A11Y_FORM_ID)
: (bool) preg_match('/name="form_id"\s+value="'.(int)WEBYXL_A11Y_FORM_ID.'"/', (string)$html);
if (!$is_target || !is_string($html) || $html === '') return $html;return preg_replace('/(<div[^>]*class="[^"]*\bfrm_error\b[^"]*"[^>]*)\srole="alert"/i', '$1', $html);
}, 10, 3);
2. JavaScript úpravy
Tento kód treba vložiť do bloku „Kód“ v DIVI hneď pod formulár. Vyrieši nasledujúce problémy:
Natívne HTML validácie:
- doplnenie required pre textové polia, textarea a prvý checkbox v skupine,
- doplnenie autocomplete (given-name, family-name, email).
Zrozumiteľné chyby:
- prepojenie popisu a chybovej hlášky pri „Súhlas“ (aria-describedby).
Ozvučenie správ:
- globálny súhrn chýb → role=“alert“,
- úspešná hláška → role=“status“,
- poistka na odstránenie role=“alert“ z per-field chýb po AJAX validácii.
Honeypot ochrana:
- kontrola a opätovné skrytie pomocou MutationObserver aj po re-rendri formulára.
<!-- autocomplete --> <script> document.addEventListener('DOMContentLoaded',function(){ const m=document.getElementById('field_qh4icy'); if(m) m.setAttribute('autocomplete','given-name'); const p=document.getElementById('field_ocfup1'); if(p) p.setAttribute('autocomplete','family-name'); const e=document.getElementById('field_29yf4d'); if(e) e.setAttribute('autocomplete','email'); }); </script>
<!-- “Súhlas” – popis pripojenie k checkboxu -->
<script>
document.addEventListener('DOMContentLoaded',function(){
const c=document.getElementById('field_getid-0');
if(c){ c.setAttribute('aria-describedby','frm_desc_field_getid frm_error_field_getid-0'); }
});
</script>
<!-- Polia majú len aria-required="true". Týmto sa pridá aj required, kvôli natívnej validácii prehliadača -->
<script>
document.addEventListener('DOMContentLoaded',function(){
['field_qh4icy','field_ocfup1','field_29yf4d','field_e6lis6','field_9jv0r1']
.forEach(id => { const el = document.getElementById(id); if(el) el.required = true; });
const consent = document.getElementById('field_getid-0'); if (consent) consent.required = true;
});
</script>
<style>
/* Skry honeypot pre Formidable form id=1 */
#frm_form_1_container #frm_field_8_container,
#frm_form_1_container .frm_verify,
#frm_form_1_container input[name^="item_meta"][name$="[8]"]{
display:none !important;
visibility:hidden !important;
height:0 !important;
overflow:hidden !important;
}
</style>
<!-- JS ho udrží skrytý aj po dynamickom pre-rendere (AJAX/validácia) -->
<script>
document.addEventListener('DOMContentLoaded', function(){
const root = document.getElementById('frm_form_1_container') || document;
const hideHP = () => {
root.querySelectorAll('#frm_field_8_container, .frm_verify, input[name^="item_meta"][name$="[8]"]').forEach(el=>{
el.setAttribute('hidden','');
el.setAttribute('aria-hidden','true');
el.style.display='none';
el.style.visibility='hidden';
el.style.height='0';
el.style.overflow='hidden';
if (el.matches('input,textarea,select,button,a')) el.tabIndex = -1;
});
};
hideHP();
new MutationObserver(hideHP).observe(root, {childList:true, subtree:true});
});
</script>
<!-- označí globálne správy bez PHP -->
<script>
document.addEventListener('DOMContentLoaded',function(){
const root = document.getElementById('frm_form_1_container');
if(!root) return;
const tune = () => {
root.querySelectorAll('.frm_error_style').forEach(n => {
n.setAttribute('role','alert');
n.setAttribute('aria-live','assertive');
n.tabIndex = -1; // aby šiel fokus, ak chceš
});
root.querySelectorAll('.frm_message').forEach(n => {
n.setAttribute('role','status');
n.setAttribute('aria-live','polite');
n.setAttribute('aria-atomic','true');
});
// poistka proti „siréne“ – ak by niekedy plugin pridal alert k per-field chybám
root.querySelectorAll('.frm_error[role="alert"]').forEach(n => n.removeAttribute('role'));
};
tune();
new MutationObserver(tune).observe(root, {childList:true, subtree:true});
});
</script>3. CSS úpravy
Toto vložiť v DIVI do Možnosti témy – všeobecný – Všeobecné nastavenia – Voliteľný kódovací jazyk CSS:
Skrytie honeypotu:
- použitie display:none !important, aria-hidden=“true“, tabindex=“-1″,
- doplnené ID a class pre selektor v CSS, aby bolo skrytie robustné.
Úprava nadpisu formulára:
- pridanie unikátneho ID pre cielené štýlovanie – do nadpisu „Pošlite nám správu“ vložiť ID: contact-heading. To isté sa potom použije pre PHP kód ,
- zachovanie kontrastu podľa WCAG 1.4.3.
Stabilné rozloženie:
- odstránenie vizuálne rušivých prvkov, ktoré by mohli zmeniť poradie fokusu.
/* ukrytie Honeypot fo formulári Formidable forms aj pre čítačky */
.frm_verify, [id^="frm_field_"][id$="_container"] .frm_verify {
display:none !important;
}
#frm_field_8_container, .frm_verify{display:none!important;}
4. Nastavenia vo Formidable forms
- Zapnutie AJAX odosielania:
Formidable → Forms → [vybraný formulár] → Settings → Form Actions → Form Settings – zaškrtnúť Submit with AJAX. - Zapnutie validácie v prehliadači:
Formidable → Forms → [vybraný formulár] → Settings → Form Actions → Form Settings – zaškrtnúť Validate this form with JavaScript. - Úprava textov chybových hlášok a úspechu:
Formidable → Forms → [vybraný formulár] → Settings → Messages – zmeniť text pre Validation Errors (globálny súhrn chýb) a Success Message (oznámenie o úspešnom odoslaní). - Kontrola povinných polí:
Formidable → Forms → [vybraný formulár] → Build → [klik na pole] → Field Options – nastaviť Required na Yes pre všetky povinné polia.
4. Nastavenia v DIVI
- Zakázanie minifikácie a kompresie:
Divi → Theme Options → Builder → Advanced – vypnúť Static CSS File Generation a v časti Performance vypnúť Minify CSS a Minify JavaScript pre stránku /kontakt (prípadne celkovo). - Overenie správneho načítania kódu:
V Divi Code Module vložiť JS a CSS úpravy priamo do tela stránky s formulárom, aby boli načítané aj pri zapnutom AJAXe.
Výsledok po úpravách
Formulár teraz spĺňa WCAG 2.1 AA v kľúčových oblastiach:
- má required a autocomplete na správnych poliach,
- oznamy o chybách a úspechu sú prístupne ozvučené,
- „Súhlas“ má prepojený popis,
- honeypot je skrytý a mimo tabbovania,
- validácia funguje plynulo a nerozbíja oznámenia.
Odporúčania do budúcna
- Ešte by sme mohli pridať skript na presun fokusu na prvú chybu alebo súhrn po odoslaní.
- Po väčšej aktualizácii urobiť 3-bodový test: prázdne odoslanie, honeypot, kontrola required/autocomplete.









