Съдържание на курса

Урок 2 — HTML със смисъл

От Dorian Chávez · основател на Hábil и архитект на интеграции ·

Време: две сесии от около 90 минути. Разпределение, което работи: в първата — „Защо, преди как“, документът и елементите със смисъл (2.1) и таблицата (2.2); във втората — контролите (2.3), цялото табло, написано на ръка, „Грешката, която ще видиш“ и упражненията. Всяка сесия завършва със страница, която можеш да отвориш и да валидираш.

Какво изграждаш: скелета на таблото revisor, написан на ръка

Какво научаваш: да избираш елемента според това, което значи, а не според вида му; заглавия, таблици, бутони, връзки и етикети

Страниците на този урок. Всички фигури са в programas/02-html-con-significado/ на хранилището на курса, всяка с очаквания си изход до нея. Отваряй ги с локалния сървър, който пусна в Урок 1.

След урока ще можеш да

  • Пишеш от нулата цял HTML документ и обясняваш за какво служи всеки ред от главата му: <!DOCTYPE html>, lang, charset, viewport и <title>.
  • Избираш между <header>, <nav>, <main>, <section> и <footer> вместо <div> и казваш какво печели този, който ползва страницата, от това.
  • Построяваш достъпна таблица, със заглавие, заглавия на колоните и заглавия на редовете, и решаваш кога дадени данни не заслужават таблица, а списък.
  • Отличаваш връзка от бутон по онова, което всеки от тях обещава, и го проверяваш с клавиша Tab.
  • Свързваш всяко поле с неговия етикет по двата начина, които съществуват (явен и неявен), групираш опции с <fieldset> и <legend> и обясняваш защо placeholder не е етикет.
  • Прекарваш една страница през официалния валидатор, четеш съобщенията му и оправяш онова, което сочат.

Защо, преди как

Представи си, че предаваш таблото на четирима различни души. Първият го отваря на голям екран и го управлява с мишката: за него почти всяка страница работи, защото вижда резултата. Вторият има наранена китка и управлява всичко с клавиатурата: напредва с клавиша Tab и активира с Enter. Третият е сляп човек, който го слуша с екранен четец, програма, която чете на глас онова, което има на страницата, и позволява да се прескача от едно заглавие на друго. Четвъртият не е човек: това е търсачката, която ще реши какво казва страницата ти в резултатите ѝ.

Последните трима имат нещо общо: не гледат пикселите, четат структурата. Клавиатурата трябва да знае кои неща могат да се активират. Екранният четец трябва да знае кое е заглавие, кое е таблица, кое е бутон. Търсачката трябва да знае кое е основното съдържание и кое е долният колонтитул. И всичко това го дава HTML, не цветът, нито размерът на буквите.

Затова този урок се казва „HTML със смисъл“. HTML не е „езикът, който чертае страницата“: той е езикът, който казва какво е всяко нещо. Да я начертае е работа на CSS, който ще видиш в Урок 3; да я накара да реагира е работа на JavaScript, от Урок 6 нататък. Днес няма да напишеш нито едно правило за стил и това е нарочно: страницата ще излезе грозна, с външния вид по подразбиране на браузъра, и въпреки това ще е използваема от четиримата. Страница, която е използваема само когато е красива, не е завършена, а е гримирана.

Онова, което днес липсва, е конкретно: revisor още не съществува. В края на този урок ще имаш пълния му скелет: заглавие, обобщение, поле за търсене, филтър, бутон и таблица с пет услуги, всичко написано на ръка с примерни данни. Още не филтрира и не търси нищо: това идва по-нататък. Онова, което ще има още от днес, е структура, която не трябва да се прави наново после. Редът на урока е този: първо документът и елементите, които дават структура (раздел 2.1), после данните с форма на таблица (2.2) и накрая контролите, които са бутоните, връзките и етикетите (2.3). Всеки раздел завършва със страница, която можеш да отвориш и да изпробваш.

Понятията

Започни, като запазиш днешната работа в папката на проекта, същата, където остави първия index.html в Урок 1. От тази папка вдигни локалния сървър с python3 -m http.server 8000 --bind 127.0.0.1, както в Урок 1, и отваряй http://localhost:8000/ в браузъра всеки път, когато запазиш промяна. Ако страницата не се е променила, презареди с Ctrl+Shift+R, което игнорира онова, което браузърът е бил запазил.

2.1 Документът и елементите със смисъл

2.1.1 Какво е елемент

Един HTML документ е текст с маркировки. Всяка маркировка се нарича таг и почти винаги идва по двойки: един отварящ, като <h1>, и един затварящ, като </h1>. Онова, което остава между двата, е съдържанието. Цялата двойка, със съдържанието си, се нарича елемент. Като напишеш <h1>Revisor de servicios</h1>, си създал елемент, който казва: „това е заглавие от първо ниво и текстът му е ‚Revisor de servicios‘“.

Някои тагове носят атрибути, които са допълнителни данни във вида име="стойност" вътре в отварящия таг. В <a href="#services"> атрибутът е href и стойността му е #services. Атрибут, който ще видиш в почти всички днешни страници, е id: дава на един елемент име, което не може да се повтаря в документа, като id="services". Връзка, чийто href започва с #, води до частта от същата страница, която има този id: <a href="#services"> скача към елемента с id="services". По-нататък CSS и JavaScript ще ползват същото това име, за да намират елемента. Друг често срещан атрибут е class: също дава име на елемента, но за разлика от id може да се повтаря в много елементи, а един и същ елемент може да носи няколко, разделени с интервали (class="status status-available"). Служи, за да даде CSS един и същ външен вид на всички, които го носят; не променя онова, което елементът значи, и затова <div class="title"> си остава кутия без смисъл, макар името му да казва „заглавие“. Някои елементи нямат съдържание и затова нямат затварящ таг; наричат се празни елементи: <meta>, <input> и <img> са тези, които ще видиш днес.

Когато браузърът получи файла ти, не го „чертае“ директно. Чете го отгоре надолу и изгражда с него структура в паметта във вид на дърво: документът е коренът, вътре са <head> и <body>, вътре в <body> са заглавията, абзаците, таблицата и така нататък. Това дърво се нарича DOM (обектен модел на документа) и ще се появи отново в Урок 7, където JavaScript ще го обхожда и променя. Засега е достатъчно да знаеш, че онова, което браузърът показва, онова, което клавиатурата обхожда, и онова, което екранният четец чете, идват от това дърво, не от текстовия ти файл.

Едно неудобно следствие: браузърът прощава почти всичко. Ако забравиш да затвориш един абзац или поставиш елемент, където не му е мястото, не те предупреждава; налучква какво си искал да кажеш по много точни правила, написани в спецификацията, и сглобява дървото, както може. Това е добре за този, който посещава зле написана страница, но лошо за този, който я пише: грешката не се вижда, личи едва после, в друг браузър или при друг вид потребител. Затова в този урок ще ползваш валидатор, програма, която проверява HTML-а ти спрямо правилата на стандарта и ти казва онова, което браузърът е премълчал.

2.1.2 Минимумът на един цял документ

Това е най-малката страница, която е пълна. Запази я като pagina.html в папката си и я отвори:

<!-- fig02_01.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Revisor de servicios</title>
</head>
<body>
  <main>
    <h1>Revisor de servicios</h1>
    <p>Esta es la página más pequeña que está completa.</p>
  </main>
</body>
</html>
Revisor de servicios
Esta es la página más pequeña que está completa.

Блокът отдолу е онова, което страницата показва при зареждане, сведено до текста си. Важното е в редовете отгоре, които не се виждат. Всеки съществува по конкретна причина.

<!DOCTYPE html> е винаги първи. Прилича на таг, но е декларация: казва на браузъра „тази страница следва сегашния стандарт“. Без нея браузърите влизат в онова, което наричат режим на съвместимост (quirks mode): имитират грешките на браузъри отпреди двайсет и пет години, за да се виждат старите страници, както са се виждали, и няколко правила за изчисляване на размери се променят безшумно. Който забрави DOCTYPE, не получава грешка: получава страница, която изглежда леко различно, и не знае защо. Пиши го винаги, такъв, какъвто е.

<html lang="es"> е коренът на документа и обявява езика. Изглежда подробност от учтивост и не е. Екранният четец избира с него гласа и произношението: без lang може да прочете текста ти на испански по правилата на английския и резултатът е неразбираем. Браузърът го ползва, за да предложи превод на страницата и за да избере кавичките на елемента <q> според езика, а CSS го ползва, за да знае с кой речник да разделя думите с тире в края на реда, когато му го поискаш с hyphens: auto (по подразбиране не разделя никоя; проверих го в Chrome 154). Освен това е критерий за достъпност с име и номер в указанията на W3C (WCAG 3.1.1, „Език на страницата“). Стойността es означава испански; ако някой ден ти потрябва испанският от Мексико по-специално, пише се es-MX.

<meta charset="utf-8"> казва с какъв код на знаците е запазен файлът. UTF-8 е този, който представя ñ, ударенията и отварящите знаци (¿ ¡) без проблеми. Ако го пропуснеш и редакторът ти е запазил в UTF-8, възможно е да видиш „Catálogo“ вместо „Catálogo“: браузърът е налучкал друго кодиране. Спецификацията изисква още този ред да се появи цял в първите 1,024 байта на файла, така че отива в началото на <head>, преди заглавието.

<meta name="viewport" content="width=device-width, initial-scale=1"> е редът, който най-много се забравя и който най-много боли на телефон. Мобилните браузъри са се родили в свят от страници, направени за настолен компютър, и за да не ги счупят, по подразбиране се преструват, че екранът е много по-широк, отколкото е (от порядъка на 980 px), а после го свиват, за да се побере. С този ред им казваш „не се преструвай: ползвай реалната ширина на устройството и мащаб 1“. Без него Урок 5 не може да работи, защото никое адаптивно оформление не се нагажда към ширина, за която браузърът лъже.

<title> е заглавието на документа, което не е същото като заглавието <h1>. Появява се в раздела, в историята, в отметките и, най-вече, е първото, което екранният четец обявява при отваряне на страницата и онова, което почти всички търсачки показват като заглавие на резултата. Друго указание за достъпност го изисква по име (WCAG 2.4.2, „Заглавие на страницата“). Заглавие като „Документ без заглавие“ или „index“ е страница без име. Напиши такова, което казва коя страница е.

Забележи също, в страницата по-горе, какво има вътре в <body>: един <main>, който обгръща всичко. Това е първият елемент със смисъл, който познаваш, и следващата точка е за такива.

2.1.3 Смисъл: разликата между div и main

Тук е централната идея на урока и е добре да се види с контраст. Двете страници по-долу казват точно същото и, с външния вид по подразбиране, изглеждат почти еднакво. Първата ползва само <div>:

<!-- fig02_02.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Revisor de servicios (todo con div)</title>
</head>
<body>
  <div class="top">
    <div class="title">Revisor de servicios</div>
    <div class="links">
      <div><a href="#summary">Resumen</a></div>
      <div><a href="#services">Servicios</a></div>
    </div>
  </div>
  <div class="content">
    <div id="summary">
      <div class="subtitle">Resumen</div>
      <div>4 de 5 servicios disponibles.</div>
    </div>
    <div id="services">
      <div class="subtitle">Servicios</div>
      <div>Catálogo, Pagos, Inventario, Notificaciones y Búsqueda.</div>
    </div>
  </div>
  <div class="bottom">Datos de ejemplo escritos a mano.</div>
</body>
</html>
Revisor de servicios
Resumen
Servicios
Resumen
4 de 5 servicios disponibles.
Servicios
Catálogo, Pagos, Inventario, Notificaciones y Búsqueda.
Datos de ejemplo escritos a mano.

Втората ползва елементите, които казват какво е всяко нещо:

<!-- fig02_03.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Revisor de servicios (con significado)</title>
</head>
<body>
  <header>
    <h1>Revisor de servicios</h1>
    <nav>
      <p>Ir a: <a href="#summary">Resumen</a> o <a href="#services">Servicios</a></p>
    </nav>
  </header>
  <main>
    <section id="summary">
      <h2>Resumen</h2>
      <p>4 de 5 servicios disponibles.</p>
    </section>
    <section id="services">
      <h2>Servicios</h2>
      <p>Catálogo, Pagos, Inventario, Notificaciones y Búsqueda.</p>
    </section>
  </main>
  <footer>
    <p>Datos de ejemplo escritos a mano.</p>
  </footer>
</body>
</html>
Revisor de servicios
Ir a: Resumen o Servicios
Resumen
4 de 5 servicios disponibles.
Servicios
Catálogo, Pagos, Inventario, Notificaciones y Búsqueda.
Datos de ejemplo escritos a mano.

За да видиш разликата, не е нужно да гледаш екрана. Отвори инструментите на браузъра (клавиш F12 или десен бутон и „Инспектиране“), иди в раздела с елементите и намери панела за достъпност: Chrome го показва до стиловете, а Firefox го носи като самостоятелен раздел. Там се появява дървото на достъпността, което е онова, което браузърът предава на екранните четци. Измерих го с Chrome 154 върху двете страници. От първата, това са единствените неща, които разпознава: две връзки и много разпръснат текст. От втората, това:

RootWebArea "Revisor de servicios (con significado)"
  banner
    heading "Revisor de servicios" (nivel 1)
    navigation
      link "Resumen"
      link "Servicios"
  main
    heading "Resumen" (nivel 2)
    heading "Servicios" (nivel 2)
  contentinfo

Всяка дума отляво е роля: онова, което нещото е. <header> на най-горно ниво в <body> се превръща в banner, <nav> в navigation, <main> в main, <footer> в contentinfo. Екранните четци позволяват да се скача директно от едно към друго („иди към основното съдържание“, „изброй заглавията“). В първата страница този, който не вижда екрана, трябва да слуша всичко по ред, от началото, всеки път, когато влезе. Във втората скача към main и започва да чете.

Това е смисъл (указанията го наричат информация и връзки): страницата казва, с името на елемента, каква роля играе всяко парче. Три уточнения предотвратяват най-честите грешки.

Първото: <header> и <footer> са banner и contentinfo само когато са на цялата страница, тоест когато не са вътре в <main>, <section>, <article>, <aside> или <nav>. Няма значение, ако са вътре в <div>: измерих го в Chrome 154 и <header> вътре в <div> си остава banner, докато този вътре в <main> престава да бъде. Вътре в <section> или в <article> са заглавието или долният колонтитул на тази част, не на страницата, и браузърът ги третира така. (<article> е блок със собствен смисъл, който би се разбрал отделно на друга страница: новина, коментар, картата на една услуга. Ще го ползваш във второто упражнение.) Второто: <section> без име не е ориентир. Групира съдържание на една и съща тема и обичаят е да започва със заглавие, но става навигируема област само ако му дадеш име, а това изисква ARIA атрибути, които още не разглеждаме. Затова в дървото по-горе не се появява. Ползвай го, за да групираш, не за да украсяваш. Третото: една страница има само един <main>: това е съдържанието, което се променя от страница на страница, без заглавието и долния колонтитул, които се повтарят.

Втората страница не изглежда по-добре и няма нужда: окото никога не е било проблемът. Който слуша първата, знае, че има две връзки и купчина текст, и трябва да налучква останалото. Който слуша втората, знае къде се намира във всеки момент.

Заглавията са указател. Който ползва екранен четец, може да поиска списъка със заглавията на страницата и да го прочете, както се чете съдържанието на една книга; има и разширения, които го показват на всеки. За да служи този указател, нивата означават йерархия, не размер: <h1> е заглавието на страницата (само едно, солиден обичай, макар стандартът да допуска повече), <h2> са нейните раздели, <h3> — частите на един раздел. Нива не се прескачат: от <h2> се слиза към <h3>, не към <h4>, както една книга не минава от глава 1 към точка 1.1.1. Да избереш <h4>, защото „h2 се вижда много голямо“, е най-честата грешка на начинаещия и я поправя CSS от следващия урок, не HTML. Валидаторът на W3C предупреждава за прескачанията. Ще го видиш в раздела за грешките.

За да е ясно как изглежда указателят на таблото, което ще построиш, така би го прочел един екранен четец:

nivel 1: Revisor de servicios
  nivel 2: Resumen
  nivel 2: Servicios

Кратък и достатъчен. Ако в бъдеще добавиш подробностите за една услуга вътре в „Servicios“ (услуги), тази част би била ниво 3 и указателят би останал последователен.

Какво не е ARIA. Вероятно вече си виждал в някой код атрибути като role="button" или aria-label="...". Това е ARIA, набор от атрибути, който позволява да се добави смисъл към елемент, който няма такъв. Съществува за случаите, които HTML не покрива. Първото му правило, в ръководството, което поддържа W3C, гласи, че ако има HTML елемент със смисъла и поведението, което ти трябва, го използваш. Един <button> вече носи ролята на бутон, възможността да получи фокус, активирането с Enter и с интервала. Един <div role="button"> носи само ролята: останалото трябва да го напишеш ти и почти никой не го пише цялото. Ръководството за практики на ARIA го обобщава с едно изречение, което си струва да запомниш: ролята е обещание. Ако кажеш, че нещо е бутон, се ангажираш да се държи като такъв. Затова в този курс в този урок и в следващия няма да напишеш нито ред ARIA: почти всичко, което му трябва на таблото, го решава родният HTML, а там, където HTML не стига, ще ти кажем навреме.

2.1.4 Текст със смисъл: списъци, данни и дати

Вътре в разделите текстът също се избира според това, което е. Има три начина да групираш сходни неща и всеки значи нещо различно:

<!-- fig02_04.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Resumen del revisor</title>
</head>
<body>
  <main>
    <h1>Resumen</h1>
    <p>Última revisión:
      <time datetime="2026-10-07T10:30:00-06:00">7 de octubre de 2026, 10:30</time>
    </p>

    <h2>Como lista de datos con nombre</h2>
    <dl>
      <div>
        <dt>Servicios revisados</dt>
        <dd>5</dd>
      </div>
      <div>
        <dt>Disponibles</dt>
        <dd>4 de 5</dd>
      </div>
      <div>
        <dt>Caídos</dt>
        <dd>1</dd>
      </div>
    </dl>

    <h2>Como lista simple</h2>
    <ul>
      <li>Catálogo</li>
      <li>Pagos</li>
      <li>Inventario</li>
    </ul>

    <h2>Como lista con orden</h2>
    <ol>
      <li>Búsqueda: 950 ms</li>
      <li>Pagos: 480 ms</li>
      <li>Notificaciones: 310 ms</li>
    </ol>
  </main>
</body>
</html>
Resumen

Última revisión: 7 de octubre de 2026, 10:30

Como lista de datos con nombre
Servicios revisados
5
Disponibles
4 de 5
Caídos
1
Como lista simple
Catálogo
Pagos
Inventario
Como lista con orden
Búsqueda: 950 ms
Pagos: 480 ms
Notificaciones: 310 ms

<ul> е списък без ред: елементи, които са заедно, но могат да си разменят местата. <ol> е списък с ред: ако преместиш един елемент, смисълът се променя (тук, от най-бавния към най-бързия). <dl> е списък с описания: двойки име и стойност, като „Disponibles: 4 de 5“ (налични: 4 от 5). Това е правилната форма на обобщението на таблото и почти никой не я ползва; вместо нея се виждат <div> с удебелен текст, за които екранният четец не знае, че са име и неговата стойност. В <dl> всяко име е <dt>, а всяка стойност — <dd>; стандартът позволява да се групират с <div>, когато искаш да им дадеш кука за стила, както тук.

Една подробност, която изглежда дребна: елементът <time>. Вътре носи текста, който вижда човекът („7 de octubre de 2026, 10:30“, 7 октомври 2026 г., 10:30 ч.), но атрибутът му datetime носи същата дата във формата, който разбира машината: година, месец, ден, час и отместване спрямо UTC (-06:00 е часът на централно Мексико, който от 2022 г. вече не се променя с лятно часово време). Текстът може да смени езика или стила си, а данните остават непокътнати. Търсачките, разширенията и, по-нататък, собственият ти JavaScript могат да го четат, без да тълкуват „7 de octubre“.

И един въпрос, който скоро ще си зададеш: защо не <br> между редовете или интервали за отстъп? Защото <br> значи „прекъсване на реда вътре в един и същи абзац“ (стихотворение, пощенски адрес), не „малко повече място“. Мястото е външен вид и е работа на CSS. Всеки път, когато ползваш маркировка за съдържание, за да постигнеш визуален ефект, лъжеш за това какво е нещо.

2.2 Данни с форма на таблица

2.2.1 Кога таблица и кога не

Таблицата е правилният инструмент, когато данните имат две измерения, които се пресичат: редове, които са неща, и колони, които са свойства на тези неща, и всяка клетка казва „това свойство, на това нещо“. Таблото е точно това: всяка услуга (ред) има състояние и време за отговор (колони). Ако данните имат само едно измерение, е списък. Ако са двойки име и стойност, е <dl>.

Имало е време, преди двайсет години, когато таблиците са се ползвали, за да се подреди страницата на колони, защото друг инструмент не е имало. Днес този навик е грешка, по две причини: екранният четец обявява „таблица с три колони“ за нещо, което не е таблица, и оформлението се чупи на телефон. Правилото, което няма изключения, е: таблица за данни, никога за подреждане. Подреждането е темата на Урок 4.

2.2.2 Частите на една достъпна таблица

Виж таблицата на таблото, с три от петте ѝ услуги, за да не повтаряме:

<!-- fig02_05.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tabla de servicios</title>
</head>
<body>
  <main>
    <h1>Servicios</h1>
    <table>
      <caption>Estado de los servicios en la última revisión</caption>
      <thead>
        <tr>
          <th scope="col">Servicio</th>
          <th scope="col">Estado</th>
          <th scope="col">Tiempo de respuesta</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th scope="row">Catálogo</th>
          <td>Disponible</td>
          <td>120 ms</td>
        </tr>
        <tr>
          <th scope="row">Pagos</th>
          <td>Disponible</td>
          <td>480 ms</td>
        </tr>
        <tr>
          <th scope="row">Inventario</th>
          <td>Caído</td>
          <td>sin respuesta</td>
        </tr>
      </tbody>
    </table>
  </main>
</body>
</html>
Servicios
Estado de los servicios en la última revisión
Servicio	Estado	Tiempo de respuesta
Catálogo	Disponible	120 ms
Pagos	Disponible	480 ms
Inventario	Caído	sin respuesta

Една таблица се сглобява от вложени части и всяка има своята работа:

  • <table> обгръща всичко.
  • <caption> е заглавието на таблицата. Стои като първо дете и е онова, което екранният четец обявява при достигане („Estado de los servicios en la última revisión, tabla de 3 columnas y 3 filas“, състояние на услугите при последната проверка, таблица с 3 колони и 3 реда). По-добро е от самостоятелно заглавие преди таблицата, защото остава свързано с нея.
  • <thead> и <tbody> отделят реда със заглавията от редовете с данни. По-нататък CSS може да стилизира заглавния ред, без да пипа тялото, а браузърът може да повтаря заглавния ред на всяка страница при печат.
  • <tr> е ред.
  • <th> е клетка-заглавие, а <td> — клетка с данни. Това е разграничението, което най-много има значение в цялата таблица.

А вътре в <th> атрибутът scope казва към какво сочи заглавието: scope="col", ако е заглавие на колона, scope="row", ако е заглавие на ред. В първия ред <th> дават заглавия на колоните; в останалите първият <th> на всеки ред е името на услугата и дава заглавие на своя ред.

За какво е всичко това? Защото който ползва екранен четец, не вижда цялата таблица: движи се клетка по клетка със стрелките и във всяка клетка трябва да знае на какво съответства. С правилно поставените <th>, когато стигне до „120 ms“, четецът обявява „Catálogo, Tiempo de respuesta, 120 ms“. Без тях чува само „120 ms“ и трябва да помни на кой ред и в коя колона е бил. Ако сложиш <td> вместо <th> на името на услугата, страницата изглежда същата и преживяването се чупи, а никой валидатор не те предупреждава. Това е причината, поради която този урок настоява, че HTML се проверява с инструменти за достъпност и с клавиатурата, а не само като се гледа.

2.2.3 Празната клетка лъже

Виж последния ред: услугата Inventario е паднала и затова няма време за отговор. Какво слагаш в тази клетка? Има три изкушения и две са погрешни. Да я оставиш празна оставя екранния четец да обявява „празно“, без да казва дали е пропуск, или отсъствие. Да сложиш 0 ms е по-лошо: това е фалшива стойност, защото услуга, която не е отговорила, не е отговорила за нула милисекунди, и това по-късно би съсипало средното. Честното е да кажеш какво е станало: „sin respuesta“ (без отговор). Това е текст, не число, и това решение (времето за отговор може да не съществува) се връща, когато изчисляваш средното в Урок 6.

2.3 Контроли: бутон, връзка и етикет

2.3.1 Връзката отива на място; бутонът прави нещо

С клавиша Tab се обхождат елементите, с които потребителят може да взаимодейства, и следващата страница е експеримент, който си струва да направиш с ръцете си:

<!-- fig02_06.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Cuatro cosas que parecen controles</title>
</head>
<body>
  <main>
    <h1>Cuatro cosas que parecen controles</h1>
    <p><a href="#result">Ir al resultado</a></p>
    <p><button type="button">Revisar ahora</button></p>
    <div>Revisar ahora</div>
    <span>Ir al resultado</span>
    <p id="result">Aquí termina la página.</p>
  </main>
</body>
</html>
Cuatro cosas que parecen controles
Ir al resultado
Revisar ahora
Revisar ahora
Ir al resultado
Aquí termina la página.

Отвори я и натискай Tab многократно. Проверих този експеримент с Chrome 154: клавишът спира само два пъти, на връзката и на бутона, и после излиза от страницата. <div> с текста „Revisar ahora“ (провери сега) и <span> с „Ir al resultado“ (иди към резултата) не получават фокус. За клавиатурата не съществуват. С CSS от Урок 3 би могъл да ги направиш да изглеждат идентични на бутон и на връзка и пак щяха да са недостижими без мишка, а за екранния четец щяха да си останат текст.

Виж какво получаваш безплатно с всеки истински елемент:

  • <a href="..."> е връзка: обещава да те отведе на друго място (друга страница или друга част на същата, като #result). Безплатно носи фокус с Tab, активиране с Enter, десен бутон за копиране на адреса, щракване с колелцето за отваряне в друг раздел и възможността да запомни кои връзки си посетил. Без href <a> не е връзка, а маркер без дестинация.
  • <button> е бутон: обещава да направи нещо на тази страница. Носи фокус, активиране с Enter и с интервала и състоянието „деактивиран“ с атрибута disabled.

Правилото, което обобщава двете: ако действието води към друг адрес, е връзка; ако променя нещо точно тук, е бутон. „Ir a Servicios“ (иди към услугите) е връзка. „Revisar ahora“ е бутон. Да сбъркаш произвежда странности, които вече си виждал: „бутон“, който е връзка и не се активира с интервала, или „връзка“, която е бутон и не може да се отвори в друг раздел.

Една бележка за type="button". Когато един <button> е вътре във форма (ще го видиш в Урок 10) и не декларира тип, стандартът му приписва submit: изпраща формата и презарежда страницата. Това е една от най-честите изненади. Да декларираш type="button" на всеки бутон, който не изпраща нищо, ти я спестява. Днес бутонът „Revisar ahora“ не прави нищо, защото страницата още няма JavaScript. В Урок 8 той отново ще поиска данните на таблото и за целта HTML, който пишеш днес, ще има нужда само от още един id.

2.3.2 Поле без етикет е поле без име

Последният контрол за днес е полето за търсене. Правилото е просто и винаги се нарушава: всяко поле има нужда от видим и свързан етикет. „Свързан“ значи, че браузърът знае, че този текст принадлежи на това поле. Има два начина да го свържеш и двата са в следващата страница:

<!-- fig02_07.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Etiquetas y grupos</title>
</head>
<body>
  <main>
    <h1>Etiquetas y grupos</h1>

    <p>
      <label for="search">Buscar servicio</label>
      <input type="search" id="search" name="search">
    </p>

    <p>
      <label>
        Nombre del servicio
        <input type="text" name="name">
      </label>
    </p>

    <fieldset>
      <legend>Mostrar</legend>
      <label><input type="radio" name="filter" value="all" checked> Todos</label>
      <label><input type="radio" name="filter" value="available"> Disponibles</label>
      <label><input type="radio" name="filter" value="down"> Caídos</label>
    </fieldset>
  </main>
</body>
</html>
Etiquetas y grupos

Buscar servicio 

Nombre del servicio 

Mostrar
 Todos  Disponibles  Caídos

Първият е явното свързване: <label> носи for="search", а полето носи id="search"; стойността на for е id на полето и това съвпадение е връзката. Вторият е неявното свързване: полето е вътре в <label> и не е нужен никакъв id. И двата работят; явният е по-гъвкав, защото етикетът и полето могат да са на различни места в документа, а неявният е по-кратък. За да провериш, че връзката е истинска, щракни върху текста „Buscar servicio“ (търси услуга): курсорът скача в полето. Проверих го, като автоматизирах това щракване в Chrome, и полето получава фокус. Това е видима печалба: много по-голяма цел за щракване за този, който има лош прицел или докосва с пръст.

Отзад се случва нещо, което е добре да се назове. Всеки интерактивен елемент има достъпно име: текстът, с който екранният четец го обявява. Браузърът го изчислява по фиксирани правила и за поле основният източник е свързаният етикет. В дървото на достъпността на Chrome полето по-горе се появява като searchbox "Buscar servicio": роля и име. Ако нямаше етикет (нито друга следа, от която да се извлече име), щеше да се появи просто searchbox: кутия без име. И инструмент за одит, като тези, които ще видиш по-нататък, го маркира със съобщението „Form elements must have labels“.

placeholder не е етикет. Това е онази сива подсказка, която се появява вътре в полето и изчезва при писане. Изкушението да го ползваш като етикет е силно, защото спестява място, но се проваля от три страни: изчезва точно когато имаш нужда да си спомниш какво е трябвало да напишеш, сивият му цвят обикновено има малък контраст с фона и не е етикет за браузъра: не създава никакво свързване и не може да се щракне върху него, за да се стигне до полето. Браузърът го взема за име само като резервно, когато няма етикет; измерих го в Chrome 154 и поле за търсене без <label> и с placeholder="Buscar servicio" се обявява като searchbox "Buscar servicio". Това е кръпка на браузъра, не етикет, който човекът може да види. Ползвай го, ако изобщо, за пример за формат (ej. catalogo), никога като единствено име.

Третата част е групата. Три радио бутона („Todos“, „Disponibles“, „Caídos“; всички, налични, паднали) образуват един-единствен въпрос: какво искаш да се показва? <fieldset> групира контролите, които вървят заедно, а <legend> е заглавието на групата, което екранният четец обявява преди всяка опция. Радио бутоните, които споделят един и същ name, вече се държат като група за браузъра: само един може да бъде отметнат. И затова, в експеримента с клавиша Tab, цялата група се брои като едно спиране; влиза се с Tab и между опциите се сменя със стрелките. Измерих го в цялото табло: клавишът Tab спира пет пъти (две връзки, полето за търсене, групата радио бутони и бутонът).

Обърни внимание на едно проектантско решение: филтърът е група радио бутони, а не три бутона. Това е избор между взаимно изключващи се опции, което е точно онова, което означава радио бутонът. Който чете кода, разбира намерението без коментар.

2.3.3 Кога да ползваш div и span

След толкова похвали за смисъла, едно пояснение: <div> и <span> не са лоши. Те са елементите, които не значат нищо, и това е полезно, когато имаш нужда да групираш нещо само за да му дадеш стил или за да го намериш после с JavaScript. <div> групира като блок, а <span> групира вътре в ред. Правилото е за ред: първо потърси елемента със смисъл; само ако не съществува, ползвай div или span. В повечето страници, които виждаш, става обратното.

Ще видиш <span class="status status-available"> вътре в таблицата на таблото. Служи за това: състоянието „Disponible“ не е нищо друго освен текст, но ни трябва място, където CSS от следващия урок да сложи цветна значка. Това е атрибутът class, който срещна в 2.1.1: име, към което CSS да сочи, което не променя онова, което елементът значи.

Решен пример: цялото табло

Вече познаваш всички части. Това е страницата, която ги събира, скелетът на revisor, с пет примерни услуги. Запази я като index.html в папката си revisor, на мястото на index.html от Урок 1: от днес този файл е таблото и следващите уроци ще го карат да расте. Чети я отгоре надолу с картата в главата: какво е всяко нещо, защо този елемент, а не друг.

<!-- fig02_08.html -->
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Revisor de servicios</title>
  <link rel="icon" href="data:,">
</head>
<body>
  <header>
    <h1>Revisor de servicios</h1>
    <p>Última revisión:
      <time datetime="2026-10-07T10:30:00-06:00">7 de octubre de 2026, 10:30</time>
    </p>
    <nav>
      <p>Ir a: <a href="#summary">Resumen</a> o <a href="#services">Servicios</a></p>
    </nav>
  </header>

  <main>
    <section id="summary">
      <h2>Resumen</h2>
      <dl>
        <div>
          <dt>Servicios revisados</dt>
          <dd>5</dd>
        </div>
        <div>
          <dt>Disponibles</dt>
          <dd>4 de 5</dd>
        </div>
        <div>
          <dt>Caídos</dt>
          <dd>1</dd>
        </div>
        <div>
          <dt>Respuesta promedio</dt>
          <dd>465 ms</dd>
        </div>
      </dl>
    </section>

    <section id="services">
      <h2>Servicios</h2>

      <p>
        <label for="search">Buscar servicio</label>
        <input type="search" id="search" name="search">
      </p>

      <fieldset>
        <legend>Mostrar</legend>
        <label><input type="radio" name="filter" value="all" checked> Todos</label>
        <label><input type="radio" name="filter" value="available"> Disponibles</label>
        <label><input type="radio" name="filter" value="down"> Caídos</label>
      </fieldset>

      <p><button type="button">Revisar ahora</button></p>

      <table>
        <caption>Estado de los servicios en la última revisión</caption>
        <thead>
          <tr>
            <th scope="col">Servicio</th>
            <th scope="col">Estado</th>
            <th scope="col">Tiempo de respuesta</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th scope="row">Catálogo</th>
            <td><span class="status status-available">Disponible</span></td>
            <td>120 ms</td>
          </tr>
          <tr>
            <th scope="row">Pagos</th>
            <td><span class="status status-available">Disponible</span></td>
            <td>480 ms</td>
          </tr>
          <tr>
            <th scope="row">Inventario</th>
            <td><span class="status status-down">Caído</span></td>
            <td>sin respuesta</td>
          </tr>
          <tr>
            <th scope="row">Notificaciones</th>
            <td><span class="status status-available">Disponible</span></td>
            <td>310 ms</td>
          </tr>
          <tr>
            <th scope="row">Búsqueda</th>
            <td><span class="status status-available">Disponible</span></td>
            <td>950 ms</td>
          </tr>
        </tbody>
      </table>
    </section>
  </main>

  <footer>
    <p>Datos de ejemplo escritos a mano.</p>
  </footer>
</body>
</html>
Revisor de servicios

Última revisión: 7 de octubre de 2026, 10:30

Ir a: Resumen o Servicios

Resumen
Servicios revisados
5
Disponibles
4 de 5
Caídos
1
Respuesta promedio
465 ms
Servicios

Buscar servicio 

Mostrar
 Todos  Disponibles  Caídos

Revisar ahora

Estado de los servicios en la última revisión
Servicio	Estado	Tiempo de respuesta
Catálogo	Disponible	120 ms
Pagos	Disponible	480 ms
Inventario	Caído	sin respuesta
Notificaciones	Disponible	310 ms
Búsqueda	Disponible	950 ms

Datos de ejemplo escritos a mano.

Мини през решенията, защото там е онова, което се научава:

  • Празната иконка (<link rel="icon" href="data:,">) е редът от Упражнение 2 на Урок 1: казва на браузъра, че страницата няма иконка, за да не иска /favicon.ico и да не замърсява конзолата с 404. Това е пряк път и има цена, която ще видиш в Урок 11, когато таблото се публикува с политика за сигурност и тази иконка се смени с истинска.
  • Заглавната част на страницата (<header>) групира заглавието, датата на последната проверка и навигацията. Тъй като виси от <body>, е banner. Заглавието е единственият <h1>.
  • Навигацията (<nav>) има две връзки, защото водят до други части на страницата: това е, което прави една връзка. Написани са вътре в изречение („Ir a: … o …“, иди към: … или …); указанията за достъпност изискват докосваните цели да са поне 24 × 24 пиксела или да имат достатъчно място около тях (WCAG 2.5.8) и изрично изключват връзките, които са вътре в изречение, защото размерът им се налага от реда текст. Да ги напишеш в изречение не ги прави по-големи: разделя ги с думи и ги оставя в това изключение, без ред CSS. Две отделни връзки, една до друга, нямаше да имат нито едното, нито другото. В Урок 4 ще видиш истинско меню.
  • Обобщението е <dl>: имена и стойности. Обърни внимание на изчислението, защото се използва пак: това са 5 услуги, 4 налични и 1 паднала; средният отговор от 465 ms е средното на четирите, които са отговорили (120 + 480 + 310 + 950 = 1,860; 1,860 / 4 = 465). Падналата услуга не се брои като нула.
  • Контролите са трите, които познаваш: поле с етикет, група радио бутони със заглавие и бутон с изричен тип. Нито един още не е вътре във форма; формата, с изпращане и валидация, е темата на Урок 10.
  • Таблицата носи заглавие, заглавия на колоните и на редовете и клетка, която казва истината за падналата услуга.
  • Долният колонтитул (<footer>) казва откъде идват данните: примерни и написани на ръка.

Сега направи онова, което никой читател на този код не прави: ползвай я така, както биха я ползвали другите трима души от началото. Натисни Tab от началото и преброй спиранията: трябва да са пет, а фокусът да върви по ред отгоре надолу. Отвори панела за достъпност на инструментите на браузъра и провери, че виждаш banner, main и contentinfo. Прекарай страницата през официалния валидатор, както се обяснява в следващия раздел. Ако трите проверки излязат добре, HTML-ът ти прави онова, което казва.

Грешката, която ще видиш

Грешките в HTML почти никога не ги показва браузърът. Показва ги валидатор. Този на W3C се казва Nu Html Checker и живее на https://validator.w3.org/nu/; там можеш да качиш файл или да поставиш кода в текстово поле. Нарочно ще счупим страницата, за да се научим да четем отговора му. Тази има шест дефекта:

<!-- fig02_09.html -->
<html>
<head>
  <title>Revisor roto</title>
</head>
<body>
  <h1>Revisor de servicios</h1>
  <h4>Servicios</h4>
  <input type="search" placeholder="Buscar servicio">
  <a href="#result"><button>Revisar ahora</button></a>
  <img src="logo.png">
  <p id="result">
    <div>4 de 5 disponibles</div>
  </p>
</body>
</html>

Като я поставиш във валидатора, ето какво отговаря (копирано от истинския резултат):

Error: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”.
From line 1, column 23; to line 2, column 6
Error: The element “button” must not appear as a descendant of the “a” element.
From line 10, column 21; to line 10, column 28
Error: An “img” element must have an “alt” attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images.
From line 11, column 3; to line 11, column 22
Error: No “p” element in scope but a “p” end tag seen.
From line 14, column 3; to line 14, column 6
Warning: Consider adding a “lang” attribute to the “html” start tag to declare the language of this document.
From line 1, column 23; to line 2, column 6
Error: The heading “h4” (with computed level 4) follows the heading “h1” (with computed level 1), skipping 2 heading levels.
From line 8, column 3; to line 8, column 6
There were errors.

На английски е и това е английският на документацията, която ще четеш през цялата си кариера, така че си струва да се научиш да го четеш. Всяко съобщение носи реда и колоната, където се случва. Едно по едно:

  1. Липсва DOCTYPE. Вече знаеш защо има значение: без него браузърът влиза в режим на съвместимост. Оправя се, като напишеш <!DOCTYPE html> като първи ред.
  2. <button> вътре в <a>. Честа грешка с причина: интерактивен елемент не може да живее вътре в друг. Какво се активира при щракване, бутонът или връзката? Браузърите не се съгласяват. Избира се един: ако контролът води на друго място, връзка; ако прави нещо тук, бутон.
  3. <img> без alt. Атрибутът alt е текстът, който екранният четец чете вместо изображението. Ако изображението дава информация, alt я описва; ако е само украса, се слага празен (alt=""), за да се игнорира. Никога не се пропуска. Таблото няма изображения и въпреки това това е най-честата грешка в уеба, затова я включвам.
  4. </p> без своя <p>. Това е следствие от едно правило на HTML: <p> може да съдържа само текст и редови елементи, не <div>. Браузърът затваря абзаца преди <div> по своя инициатива, а затварящият таг, който си написал, остава сирак. Затова съобщението казва „няма отворен p“: затворил го е браузърът, без да те предупреди.
  5. Липсва lang. Появява се като предупреждение, не като грешка, но вече знаеш какво струва.
  6. <h4> след <h1>. Валидаторът е направил сметката: прескочени са две нива. Поправяш го с <h2>.

Обърни внимание на онова, което не каза. <input> без етикет, само с placeholder, мина без предупреждение: валидаторът проверява дали синтаксисът е законен, не дали страницата е използваема. Това го откриват други инструменти, тези за одит на достъпността. Браузърът носи един в инструментите си за разработка (в Chrome се казва Lighthouse) и почти всички се опират на двигател, наречен axe, който върху същата страница отговаря, между другото, с тези истински съобщения:

heading-order: Heading levels should only increase by one
html-has-lang: <html> element must have a lang attribute
image-alt: Images must have alternative text

И върху вариант на таблото с махнат <label>: label: Form elements must have labels. Двата вида инструмент се допълват: валидаторът улавя онова, което стандартът забранява, а одитът улавя онова, което оставя някого без възможност да ползва страницата. Има трети вид грешка, която никой от двата не улавя: да смениш <th> с <td> не произвежда никакво съобщение, макар да чупи обявяването на таблицата. За нея помага само изпробването с клавиатурата и с дървото на достъпността.

Какво се прави погрешно

1. Да строиш контроли с div или span. Случаят със страницата с четирите неща: <div>, който казва „Revisar ahora“. Цена: никой с клавиатура не го достига, никой екранен четец не го обявява като бутон, а да възстановяваш на ръка фокуса, интервала и деактивираното състояние е работа, която браузърът вече е свършил. Поправка: <button type="button">.

2. Да избираш заглавието по размера му. <h4>, защото <h2> „изглежда голямо“, или удебелен <p>, защото „вече е заглавие“. Цена: указателят със заглавия остава пълен с дупки или празен и който навигира по заглавия, не намира нищо. Поправка: избери нивото по йерархия; размерът е на CSS.

3. Да ползваш таблици за подреждане на страницата. Цена: екранният четец обявява таблици там, където няма данни, страницата не се нагажда към телефон и кодът е труден за четене. Поправка: таблица само за данни с редове и колони, които се пресичат.

4. Всички клетки като <td>. Най-трудният за забелязване случай, защото страницата изглежда съвършено. Цена: екранният четец обявява „120 ms“, без да каже на кого, нито на какво. Поправка: <th scope="col"> в заглавията на колоните и <th scope="row"> в името на реда.

5. placeholder като единствен етикет. Цена: подсказката изчезва при писане, обикновено има малък контраст и полето зависи от резервно име, което човекът престава да вижда, щом започне да пише. Поправка: видим <label>, свързан с for и id или обгръщащ полето.

6. Връзка, която играе ролята на бутон, или бутон, който играе ролята на връзка. <a href="#">, който задейства действие, или <button>, който навигира. Цена: чупят се щракването с колелцето, интервалът, копирането на адреса, историята. Поправка: „отива на място“ е връзка, „прави нещо“ е бутон.

7. <br> и &nbsp; за даване на място. Цена: екранният четец може да прочете „празен ред“ или прекъсвания, които не значат нищо, и мястото остава вързано за текста. Поправка: мястото е на CSS.

8. Да не слагаш alt на изображение. Цена: изображението е невидимо за този, който не го вижда. Поправка: alt, който казва каква функция изпълнява изображението, или alt="", ако е чиста украса.

Упражнения

Упражнение 1 — Преброй спиранията

С отворено цялото табло на локалния ти сървър пусни мишката и ползвай само клавиатурата. Натисни Tab от началото и запиши, по ред, къде спира фокусът всеки път. После смени <button type="button"> с <div> в твоето копие, презареди и преброй отново. Колко спирания по-малко имаш? Какво престана да е възможно?

Упражнение 2 — От div към смисъл

Този фрагмент показва „карта“ на една услуга, написана само с div и span. Препиши го с елементите, които казват какво е всяко нещо, без да променяш текста, който се чете:

<div class="card">
  <div class="card-title">Pagos</div>
  <div class="card-row"><span>Estado</span> <span>Disponible</span></div>
  <div class="card-row"><span>Respuesta</span> <span>480 ms</span></div>
  <div class="card-action" onclick="check()">Revisar este servicio</div>
</div>

Подсказка: това са две двойки име и стойност, едно действие и един текст, който заслужава да бъде истинско заглавие.

Упражнение 3 — Добави услуга и преправи сметките

Добави към таблото шеста услуга, Correo, налична и с 210 ms отговор. След това обнови обобщението на ръка: колко услуги има, колко са налични, колко са паднали и колко е средният отговор. Помни от кои услуги се изчислява средното.

Упражнение 4 — Счупи три неща и виж кои се забелязват

В копие на таблото направи три промени: махни <label> от полето за търсене, смени втория <h2> с <h4> и смени <th scope="row">Pagos</th> с <td>Pagos</td>. Прекарай копието през официалния валидатор. Кои от трите дефекта сочи? За тези, които не сочи, как би ги открил?

Решения

Решение 1

С истинския бутон фокусът спира пет пъти, в този ред: връзката „Resumen“, връзката „Servicios“, полето за търсене, групата радио бутони (едно-единствено спиране, а вътре в нея се сменя със стрелките) и бутонът „Revisar ahora“. После фокусът излиза от страницата към лентата на браузъра. С <div> остават четири спирания, едно по-малко: бутонът престана да получава фокус, така че вече не може да се активира без мишка. Това е разликата между контрол и нещо, което прилича на контрол.

Решение 2

Заглавието е елемент за заглавие (<h3>, защото виси от раздела „Servicios“, който е ниво 2), двете двойки име и стойност са списък с описания, а действието е бутон. Тъй като onclick е JavaScript вътре в HTML и още не го ползваме, се маха: кой слуша щракването ще се види в Урок 7.

<article>
  <h3>Pagos</h3>
  <dl>
    <div>
      <dt>Estado</dt>
      <dd>Disponible</dd>
    </div>
    <div>
      <dt>Respuesta</dt>
      <dd>480 ms</dd>
    </div>
  </dl>
  <button type="button">Revisar este servicio</button>
</article>

Ползва се <article>, защото картата е единица със собствен смисъл, която би се разбрала отделно на друга страница; <div> също би бил валиден, ако не искаш да декларираш тази идея. Което не е валидно, е да оставиш заглавието като <div> или действието като <div>.

Решение 3

С шестата услуга обобщението става така: 6 проверени услуги, 5 от 6 налични, 1 паднала. Средният отговор се изчислява с петте, които са отговорили: 120 + 480 + 310 + 950 + 210 = 2,070, и 2,070 / 5 = 414 ms. Падналата услуга продължава да не влиза в средното, защото няма време за отговор. Новият ред спазва структурата на останалите:

<tr>
  <th scope="row">Correo</th>
  <td><span class="status status-available">Disponible</span></td>
  <td>210 ms</td>
</tr>

Ако си изчислил 345 ms (2,070 / 6), си броил падналата, сякаш е отговорила за нула, което е точно грешката, която описва раздел 2.2.3.

Решение 4

Валидаторът сочи само един: <h4> след <h2>, със съобщението „The heading “h4” (with computed level 4) follows the heading “h2” (with computed level 2), skipping 1 heading level“ (проверих го с истинско копие на таблото). Липсващият <label> не го сочи, защото поле без етикет е законен HTML; би го открил с инструмент за одит, който отговаря „Form elements must have labels“, или с дървото на достъпността, където полето се появява без име. <td> вместо <th> не го сочи нито един от двата инструмента: открива се с дървото на достъпността (името на реда вече не се свързва с клетките) или, още по-добре, като послушаш таблицата с екранен четец. Поука: да минеш валидатора е необходимо и не е достатъчно.

Как разбирам, че съм успял

  • Таблото се отваря на http://localhost:8000/, сервирано с python3 -m http.server 8000 --bind 127.0.0.1, без грешки в раздела с конзолата на инструментите на браузъра.
  • Клавишът Tab спира точно пет пъти вътре в страницата (две връзки, полето, групата радио бутони и бутонът), по ред отгоре надолу.
  • Панелът за достъпност на инструментите на браузъра показва banner, navigation, main и contentinfo, а заглавията излизат в реда 1, 2, 2.
  • Като поставиш страницата в https://validator.w3.org/nu/, отговорът е „Document checking completed. No errors or warnings to show.“
  • Като щракнеш върху текста „Buscar servicio“, курсорът скача в полето за търсене.
  • Можеш със свои думи да обясниш защо <div>, който казва „Revisar ahora“, не е бутон и защо 120 ms без своя <th> е осиротяла стойност.

За допълнително четене

  • (на английски, както почти цялата официална документация) Стандартът HTML на WHATWG, „Sections“ — източникът, който решава какво значат <header>, <nav>, <main>, <section> и <footer> и кога всеки е ориентир. Посетено на 7 октомври 2026 г.
  • MDN, „Структуриране на съдържанието с HTML“ — учебният модул на Mozilla, който следва същия ред като този урок, с практически задачи за таблици и структура. Посетено на 7 октомври 2026 г.
  • W3C WAI, ръководство за таблици — как се строят таблици, които един екранен четец може да обходи, с примери за прости и сложни заглавия. Посетено на 7 октомври 2026 г.
  • W3C WAI, „Бележки за използването на ARIA в HTML“ — ръководството за правилата на ARIA, започвайки с „ако съществува роден HTML елемент, ползвай го“. Посетено на 7 октомври 2026 г.
Да обсъдим вашия проект

Предпочитате имейл? Пишете ни на hola@habil.mx