Sari la conținut
megapromotingHai să discutăm

Expertise · UI/UX design

A design system where the contrast is calculated, the motion has a stop button, and whatever fails the check is written down in the open.

We work with colour tokens whose contrast ratio is computed, keyboard patterns checked element by element, and animation that stops when the system asks it to. We verify with tooling, on real pages, and we publish what failed as well.

Construit dejaSistemul rulează pe site-ul acesta și se poate inspecta. Tokenul `--muted: #666b65` obține între 4,72:1 și 5,45:1 pe cele șapte fundaluri ale sitului — recalculat de noi cu formula de luminanță din WCAG, nu preluat dintr-un instrument. Opt culori scrise direct în foaia de stil produceau 2,88–4,49:1; le-am găsit cu axe-core 4.10.2 rulat pe zece pagini live și le-am înlocuit cu tokenul, iar corectura e deja în foaia servită de producție. A doua implementare verificabilă: mecanismul de oprire a animațiilor — 38 de elemente animate la infinit pe pagina de start, toate acoperite de un buton de pauză, plus regula globală care anulează animațiile la `prefers-reduced-motion: reduce`.

Cele mai multe „sisteme de design” sunt o paletă și o listă de componente. Al nostru începe cu un lucru mai plictisitor și mai util: fiecare culoare de text are un raport de contrast calculat față de fiecare fundal pe care apare efectiv. Nu „arată bine”, ci un număr. Tokenul de text secundar de pe acest site, `--muted: #666b65`, obține între 4,72:1 și 5,45:1 pe cele șapte fundaluri folosite. Pragul cerut pentru text mic e 4,5:1.

Numărul acela nu a apărut din bun-simț. Am rulat axe-core 4.10.2 pe zece pagini ale site-ului propriu și singura regulă WCAG încălcată sistematic era contrastul: 31 până la 43 de noduri per pagină, dintre care 31 veneau din antet și subsol, deci se înmulțeau peste toate cele 147 de adrese. Opt culori scrise direct în foaia de stil produceau 2,88:1 până la 4,49:1. Cea mai gravă, la 10 px și 3,72:1, era exact rândul cu linkurile juridice — traseul pe care ajunge cineva când vrea să-și exercite un drept.

A doua parte a sistemului e mișcarea, tratată ca funcție, nu ca decor. Pe pagina de start sunt 38 de elemente animate la infinit. Toate sunt acoperite de un buton de pauză: la apăsare trec pe stare oprită, eticheta se schimbă și starea e anunțată pentru cititoarele de ecran. Separat, o regulă globală anulează orice animație și tranziție când sistemul cere mișcare redusă, oprește derularea lină și ascunde chiar butonul de pauză, care devine inutil. Am verificat și capcana clasică — o animație care pornește de la opacitate aproape zero și lasă conținutul invizibil când e anulată: nu există aici.

Restul e disciplină: indicator de focus vizibil, de 2 px, aplicat doar la navigarea cu tastatura; parcurgere cu săgeți în grupurile de tabsuri, după modelul recomandat; formulare cu erori anunțate ca alertă și confirmări anunțate ca stare. Și o regulă pe care o ținem strâns: ce nu am putut verifica se scrie ca neverificat. Auditul propriu are o secțiune de limite ale instrumentului, iar două defecte găsite acolo sunt încă deschise — sunt mai jos, cu nume.

What it covers

The work, piece by piece

Culorile sunt tokeni, iar contrastul e un număr calculat

Paleta stă într-un bloc de 14 variabile, iar textul secundar folosește un singur token: `--muted: #666b65`. Raporturile, recalculate cu formula de luminanță relativă din WCAG pe cele șapte fundaluri ale sitului: 5,45:1 pe alb, 5,15, 5,11, 5,03, 5,02, 4,79 și 4,72. Textul principal, `--ink: #181a19`, obține 17,49:1 pe alb; accentul verde `#355c46`, 7,58:1. Când o culoare nu ajunge la prag, se schimbă tokenul, nu se argumentează în ședință.

Componentele au tipar de tastatură, nu doar aspect

Grupul de tabsuri de pe pagina de start folosește indexul de tabulare mobil: doar tabul activ e accesibil cu Tab, celelalte sunt scoase din ordine, iar săgețile stânga/dreapta, Home și End mută focusul, cu comportamentul implicit al tastei anulat. E modelul din practicile recomandate pentru componente accesibile, implementat, nu citat. În componentele de bază ale sitului sunt 21 de etichete accesibile, 7 stări apăsat/neapăsat, 3 legături între titlu și regiune, 2 stări de selecție, 2 regiuni care anunță schimbări și 2 legături între comandă și panoul pe care îl controlează.

Mișcarea are buton de oprire și se stinge la preferința sistemului

Pe pagina de start sunt 38 de elemente animate la infinit, pornite automat, care durează peste cinci secunde — exact situația reglementată de criteriul „Pauză, oprire, ascundere”. Toate 38 sunt acoperite de butoane de pauză: la apăsare, animațiile trec pe stare oprită și starea butonului se schimbă. Separat, la `prefers-reduced-motion: reduce` o regulă globală anulează orice animație și tranziție, oprește derularea lină, elimină deplasarea la trecerea cu mouse-ul și ascunde butonul de pauză. În foaia principală sunt șapte astfel de blocuri, plus unul în foaia de bază.

Indicator de focus vizibil, doar unde trebuie

Contur de 2 px solid, în verdele de accent, cu 5 px distanță, aplicat prin `:focus-visible` — deci apare la navigarea cu tastatura și nu la clic cu mouse-ul. Verificat în browser, element cu element, pe antet și pe primele comenzi din conținut: patru elemente diferite, același contur. Nicăieri în foaia de stil nu există un contur eliminat fără înlocuitor.

Formulare care își anunță stările

În formularul de contact al sitului, etichetele sunt implicite pe câmpuri, eroarea e marcată ca alertă și confirmarea ca stare — deci cititoarele de ecran le anunță fără ca utilizatorul să caute. Capcana pentru boți e ascunsă corect: părinte fără afișare, marcat ca ascuns pentru tehnologii asistive și scos din ordinea de tabulare. Auditul propriu confirmă că regulile de etichetare, de nume pentru selectoare și de completare automată trec toate.

Verificarea se face cu unealtă, pe pagini reale

Rulăm axe-core 4.10.2 în pagina live, nu într-un mediu de test, pe un eșantion de zece pagini care acoperă tipare diferite: pagina de start, catalogul, o pagină de produs, indexul de servicii, o pagină de serviciu, matricea de soluții, o combinație din matrice, contactul, tarifele, un articol și un document juridic. Rezultatul brut se scrie pe pagină, cu numărul de noduri, nu rezumat în „conform”.

Ce a picat se publică, nu se rotunjește

Din același audit: la 320 px lățime, comanda din antet și butonul de meniu depășesc ecranul cu 22 px și pagina derulează orizontal — aritmetica e 362 px de conținut într-un container de 280 px. În documentele juridice, linkurile din text nu se disting de textul din jur decât prin culoare, iar raportul dintre accent și textul de corp e 1,01:1, deci sublinierea e obligatorie. Ambele sunt deschise, cu remediere scrisă. Le publicăm pentru că un cumpărător care cere accesibilitate merită să vadă cum arată o listă de defecte reală.

Fără temă întunecată — spus, nu simulat

Site-ul nu are temă întunecată. Blocul de tokeni e declarat pentru trei selectoare care primesc valori identice, iar în cod nu există nicio regulă pentru preferința de schemă a sistemului. Verificat în browser cu schema întunecată emulată: pagina rămâne albă. Consecința utilă: toate raporturile de contrast măsurate sunt valabile în ambele scheme. Consecința neplăcută: comutatorul de temă e cod mort din punctul de vedere al culorii. O temă întunecată reală înseamnă re-măsurarea integrală a contrastului, nu copierea valorilor.

Tipografie cu reguli, nu cu preferințe

O singură familie pentru titluri și text, cu spațiere între litere ajustată pe nivel — mai strânsă la titlurile mari, `-0,055em` la nivelurile unu și doi, `-0,035em` la nivelul trei — și greutate 500 pe titluri. Paragrafele folosesc echilibrarea rândurilor, ca să nu rămână un cuvânt singur pe ultimul rând. Containerul are lățime maximă de 1240 px cu margini de 48 px, iar butoanele au aceeași înălțime de rând peste tot. Sunt reguli scrise în foaia de stil, deci se aplică și după ce plecăm.

What it looks like

The route, step by step.

01

Inventarul sarcinilor și al punctelor de blocaj

Prima livrare e lista lucrurilor pe care oamenii vin să le facă, în ordinea frecvenței, cu locul unde se opresc azi. Fără ea, orice ecran nou e o preferință estetică. Cu ea, fiecare decizie ulterioară are un criteriu.

02

Tokenii și verificarea contrastului, înainte de ecrane

Se stabilesc culorile, fundalurile pe care apar efectiv și raportul fiecărei perechi, calculat. Livrăm tabelul de perechi cu raporturile lor. E mult mai ieftin decât să descoperi la final că paleta aleasă nu poate fi folosită pentru text mic.

03

Componentele, cu tiparul de tastatură scris odată cu aspectul

Fiecare componentă interactivă primește, în același comit, aspectul și comportamentul: ce se întâmplă la Tab, la săgeți, la Escape, ce se anunță și cum. Tiparul de tastatură adăugat ulterior e o rescriere, nu o completare.

04

Auditul cu unealtă, pe pagini reale, cu rezultatul brut livrat

Rulăm instrumentul pe un eșantion care acoperă tipare diferite și livrăm ieșirea brută, nu un rezumat. Fiecare constatare primește locul din cod, criteriul încălcat, remedierea propusă și criteriul de verificare de după remediere — de exemplu: numărul de noduri de contrast trebuie să scadă la zero pe pagina care conține doar antetul și subsolul.

05

Predarea, cu lista celor neverificate

Predăm tokenii, componentele, documentul de audit și lista a ce nu s-a putut verifica, cu motivul. La site-ul acesta lista include activarea cu tastatura și emularea preferinței de mișcare redusă în browser — ambele limite de unealtă, ambele scrise ca atare.

Token de culoare curaport calculat1componentă cu tiparde tastatură scrisodată cu aspectul2verificare cu unealtăpe pagini reale, curezultatul brutpublicat3Ce pică la pasul trei se întoarce la pasul unu.
Un ciclu în 3 pași

Datele

What we touch, where it lives, and how long it stays

The questions anyone with a data protection officer asks — asked here before they do.

Ce colectăm în etapa de cercetare
Sarcinile pe care oamenii chiar le au de făcut, contextul în care le fac și punctul unde se blochează. Când asta presupune înregistrări, interviuri sau observarea unor sesiuni reale, se stabilesc înainte cine participă, ce se înregistrează, cine are acces și cât se păstrează — în scris, nu prin înțelegere tacită.
Rezultatele auditului de accesibilitate
Ieșirea brută a instrumentului, gruparea nodurilor pe perechea culoare–fundal, raporturile recalculate independent și locul din cod al fiecărei culori. Se livrează ca document versionat lângă cod, nu ca prezentare — ca oricine să poată reproduce măsurătoarea cu comenzile din anexă.
Ce ajunge în cod și ce rămâne în discuție
Tokenii, componentele, tiparele de tastatură și regulile de mișcare ajung în foaia de stil și în componente. Preferințele care nu se pot exprima ca regulă — „să arate mai cald” — rămân decizii de discutat, nu se strecoară ca valori scrise direct în componente. De acolo vin, de obicei, culorile care pică verificarea.
Preferințele vizitatorului
Preferința de mișcare redusă se citește din sistem, prin interogarea de media, și nu se stochează la noi. Nu există temă întunecată de reținut. Măsurarea traficului trece prin instrumentul configurat pe proiect și se activează după consimțământ; nu ține de sistemul de design, dar ține de aceeași pagină.
Ce nu presupunem
Nu presupunem că un rezultat automat înseamnă conformitate. Instrumentul prinde o parte din criterii; restul cere verificare manuală, iar unele lucruri nu s-au putut verifica deloc cu uneltele disponibile — activarea cu tastatura, de exemplu, pentru că panoul de browser folosit nu produce activarea implicită. Scrie așa în audit: neverificat, nu trecut.

A case

Eight colours, one variable, and the list that stayed open

Situația

Site propriu, refăcut recent, cu un sistem de tokeni deja existent. Nimeni nu reclamase nimic. Întrebarea nu a fost „arată bine?”, ci „ce spune un instrument care nu are păreri?”.

Ce am construit

Am injectat axe-core 4.10.2 în pagina live și l-am rulat pe zece pagini alese pentru tipare diferite. Singura regulă WCAG încălcată sistematic era contrastul. Am grupat nodurile după perechea text–fundal, am recalculat fiecare raport cu formula de luminanță relativă din standard — ca să nu depindem de un singur instrument — și am căutat fiecare culoare în foaia de stil. Opt valori scrise direct în cod produceau toate eșecurile permanente: 3,89:1 pe cele 25 de linkuri din subsol, 2,88:1 pe numerele de pas, 3,72:1 pe rândul cu linkurile juridice, și încă cinci între 3,99 și 4,49. Tokenul potrivit exista deja; problema era că opt reguli îl ocoleau.

Ce a ieșit

Cele opt valori au fost înlocuite cu tokenul, iar tokenul reglat astfel încât să treacă pragul pe toate cele șapte fundaluri ale sitului. Verificat de două ori: în depozit nu mai apare niciuna dintre cele opt culori, iar foaia de stil servită azi de producție conține doar tokenul. Recalculat independent: 4,72:1 în cel mai defavorabil caz și 5,45:1 pe alb. Restul auditului a confirmat ce era deja corect — indicatorul de focus, tiparul de tastatură din grupurile de tabsuri, etichetele și stările din formular, acoperirea completă a animațiilor cu butoane de pauză.

Ce nu spune cazul

Două constatări au rămas deschise și sunt scrise ca atare: la 320 px lățime antetul depășește ecranul cu 22 px, iar în documentele juridice linkurile din text se disting doar prin culoare, la un raport de 1,01:1 față de textul din jur — culoarea singură nu e suficientă, e nevoie de subliniere. Și limita de metodă: un instrument automat acoperă o parte din criterii. Activarea cu tastatura nu a putut fi testată cu uneltele disponibile și e marcată neverificată, nu trecută.

Întrebări

What people ask us before they call

Ce înseamnă „design accesibil” la voi, concret?

Înseamnă numere, nu adjective. Textul secundar de pe acest site folosește un singur token, `#666b65`, care obține între 4,72:1 și 5,45:1 pe cele șapte fundaluri unde apare — pragul cerut pentru text mic e 4,5:1. Focusul e un contur de 2 px care apare doar la navigarea cu tastatura. Mișcarea are buton de oprire și se anulează complet la preferința de mișcare redusă din sistem. Fiecare dintre afirmațiile astea are un loc în cod și un mod de verificare.

Cum verificați? Cu ce unealtă?

axe-core 4.10.2, rulat în pagina live, pe zece pagini care acoperă tipare diferite — pagina de start, catalogul, o pagină de produs, indexul și o pagină de serviciu, matricea de soluții și o combinație din ea, contactul, tarifele, un articol, un document juridic. Rezultatul brut, cu numărul de noduri per pagină, intră în document. Peste asta recalculăm manual raporturile de contrast cu formula din standard, ca să nu depindem de un singur instrument. Și scriem explicit ce nu a putut fi verificat.

Ce a picat la voi?

La momentul auditului, singura regulă încălcată sistematic era contrastul: 31 până la 43 de noduri per pagină, dintre care 31 din antet și subsol, deci înmulțite peste toate cele 147 de adrese. Opt culori scrise direct în foaia de stil produceau 2,88 până la 4,49:1; cea mai gravă, la 10 px și 3,72:1, era rândul cu linkurile juridice. Toate opt sunt acum înlocuite cu tokenul, iar corectura e în foaia servită de producție. Rămân deschise două lucruri: la 320 px antetul depășește ecranul cu 22 px, iar în documentele juridice linkurile din text nu se disting decât prin culoare, la un raport de 1,01:1 față de textul din jur. Ambele au remedierea scrisă.

De ce publicați defectele propriului site?

Pentru că altfel „accesibil” e un cuvânt fără cost. Oricine poate scrie că respectă standardul; puțini arată lista de neconformități, cu numărul de noduri și linia din foaia de stil. Dacă lucrăm împreună, o să primești același tip de listă despre site-ul tău — inclusiv părțile care nu ne fac bine. E mai ieftin să afli de la noi.

Site-ul are temă întunecată?

Nu, și spun asta pentru că e verificabil: blocul de tokeni e declarat pentru trei selectoare care primesc valori identice, iar în cod nu există nicio regulă pentru preferința de schemă. Emulând schema întunecată în browser, pagina rămâne albă. Partea bună e că toate raporturile de contrast măsurate sunt valabile în ambele scheme. Partea de rezolvat e că un comutator de temă care nu schimbă culorile e cod mort. O temă întunecată reală înseamnă re-măsurarea completă a contrastului — nu se transferă din cea deschisă.

Animațiile nu deranjează utilizatorii?

Pot, și de asta au două ieșiri. Prima: fiecare element animat la infinit e acoperit de un buton de pauză — pe pagina de start am verificat că toate cele 38 sunt acoperite, zero neacoperite. A doua: la preferința de mișcare redusă din sistem, o regulă globală anulează orice animație și tranziție, oprește derularea lină și ascunde chiar butonul de pauză. Am verificat și capcana clasică: dacă o animație pornește de la opacitate aproape zero și e anulată, conținutul poate rămâne invizibil. Aici nu se întâmplă, pentru că elementele nu au opacitate de bază redusă.

Livrați și cod, sau doar machete?

Livrăm tokenii și componentele în cod. Un sistem de design care rămâne în instrumentul de proiectare se erodează la prima urgență: cineva scrie o culoare direct în componentă pentru că „așa arată mai bine aici”, și după șase luni ai opt culori paralele care pică verificarea. Exact asta s-a întâmplat și la noi, e documentat mai sus, și de aceea tokenul e singura sursă de adevăr pentru culoarea textului secundar.

Ce nu puteți verifica?

Câteva lucruri concrete, scrise ca atare în audit. Activarea cu tastatura: panoul de browser folosit nu produce activarea implicită — apăsarea Enter pe un buton nu generează clic — deci nu raportăm concluzii despre Enter și Space. Emularea preferinței de mișcare redusă: nu am putut-o activa în browser, deci regula a fost verificată în sursă, iar un test manual cu preferința pornită rămâne recomandat. Și metricile de încărcare percepută nu au fost disponibile în instrument, deci sunt marcate ca nemăsurate, nu estimate.

Garantați conformitate WCAG?

Nu, și nimeni onest nu ar trebui să o facă pe baza unui instrument automat. Un instrument acoperă o parte din criterii; restul cer verificare manuală, iar unele cer utilizatori reali. Ce garantăm e metoda: eșantion declarat, unealtă și versiune declarate, rezultat brut publicat, raporturi recalculate independent, remediere cu criteriu de verificare, și lista explicită a ce nu s-a putut verifica. Un certificat fără lista asta nu spune nimic.

Pe ce se sprijină fiecare afirmație de mai sus (15 surse)
  1. Blocul de tokeni are 14 variabile și e declarat pentru `:root, .light, .dark`; `--muted: #666b65`, `--ink: #181a19`, `--accent: #355c46`, `--ink-soft: #525651`mega.css:2-22
  2. Raporturile recalculate independent pentru `#666b65`: 5,45 pe #ffffff; 5,15 pe #f8f9f6; 5,11 pe #f7f8f5; 5,03 pe #f6f6f4; 5,02 pe #f5f6f3; 4,79 pe #edf2e9; 4,72 pe #eeefeb. `#181a19` pe alb = 17,49; `#355c46` pe alb = 7,58; `#525651` pe alb = 7,48calcul propriu cu formula de luminanță relativă din WCAG 2.x, rulat de serv-5 pe 06.09.2026:
  3. Cele opt culori scrise direct în foaia de stil (2,88–4,49:1) nu mai există nici în depozit, nici în foaia de stil servită de producție; în locul lor e `var(--muted)`https://www.megapromoting.com/_next/static/chunks/0~95cl1qc44v9.css · 2026-09-06
  4. axe-core 4.10.2 rulat pe zece pagini live; singura regulă WCAG încălcată sistematic e contrastul (31–43 de noduri/pagină, dintre care 31 din antet și subsol); `heading-order` o singură dată, pe o paginăaccesibilitate-performanta.md:84-104
  5. 38 de elemente animate la infinit pe pagina de start, TOATE acoperite de buton de pauză (0 neacoperite); la apăsare trec pe `animation-play-state: paused` și starea butonului se schimbăaccesibilitate-performanta.md:453-480
  6. Regula globală pentru mișcare redusă anulează animațiile și tranzițiile, oprește derularea lină, elimină deplasarea la hover și ascunde butonul de pauză; șapte blocuri în `mega.css`, unul în `globals.css`mega.css:1438-1454 (blocul global); 2042, 2096, 2513, 2794, 2929, 3003
  7. Indicator de focus: contur 2 px solid `#355c46` cu 5 px distanță, aplicat prin `:focus-visible`; verificat în browser pe patru elemente diferite; nicăieri `outline: none` fără înlocuitormega.css:45-56
  8. Grupul de tabsuri folosește index de tabulare mobil și tratează ArrowLeft, ArrowRight, Home, End cu anularea comportamentului implicitHomeExperience.tsx:75
  9. În componentele de bază: 21 `aria-label`, 7 `aria-pressed`, 3 `aria-labelledby`, 2 `aria-selected`, 2 `aria-live`, 2 `aria-controls`, 1 `aria-hidden`:numărare cu grep peste directorul componentelor
  10. Formularul de contact: etichete implicite, eroare marcată ca alertă, confirmare marcată ca stare, capcană pentru boți ascunsă corect (părinte fără afișare + ascuns pentru tehnologii asistive + scos din ordinea de tabulare)CheckInCounter.tsx:72, 195
  11. DESCHIS: la 320 px conținutul antetului însumează 362 px într-un container de 280 px, pagina derulează orizontal 22 px, iar butonul de meniu e tăiataccesibilitate-performanta.md:156-203
  12. DESCHIS: în documentele juridice linkurile din text se disting doar prin culoare; raportul accent (`#355c46`) față de textul de corp (`#525651`) e 1,01:1, deci sublinierea e obligatorieaccesibilitate-performanta.md:204-248
  13. Nu există temă întunecată: `:root`, `.light` și `.dark` primesc valori identice, zero reguli pentru preferința de schemă, iar cu schema întunecată emulată pagina rămâne albăaccesibilitate-performanta.md:521-546
  14. Reguli tipografice: o familie pentru titluri și text; greutate 500 și spațiere `-0,055em` la h1/h2, `-0,035em` la h3; `text-wrap: pretty` pe paragrafe; container `min(1240px, 100% - 96px)`mega.css:57-80
  15. Limite de unealtă declarate: activarea cu tastatura nu a putut fi testată (Enter pe buton nu generează clic în panoul folosit), iar preferința de mișcare redusă nu a putut fi emulată — regula a fost verificată în sursăa11y.md:

Citările interne arată numele fișierului și linia. Calea completă rămâne în depozitul nostru; o putem parcurge împreună, la cerere.

What would you want to work better?

Tell us about your process. Together we decide what is worth building, what we can connect, and how we check the result.

Hai să discutăm