ЁЯПл The SchoolтА║ЁЯУМ UIтА║ЁЯз▒ рдзрдбрд╛ 02 тАФ HTML рд░рдЪрдирд╛: рдлрд▓рдХрд╛рдЪрд╛ рд╕рд╛рдВрдЧрд╛рдбрд╛
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯз▒ рдзрдбрд╛ 02 тАФ HTML рд░рдЪрдирд╛: рдлрд▓рдХрд╛рдЪрд╛ рд╕рд╛рдВрдЧрд╛рдбрд╛

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 02 ┬╖ рдорд╛рдЧреЗ: lesson-01-why-ui ┬╖ рдкреБрдвреЗ: lesson-03-css-layout


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбрд╛ 01, рдЖрдгрд┐ рд╕рд╛рдВрдЧрд╛рдбрд╛: semantic elements (landmarks), рдЕрд░реНрдердкреВрд░реНрдг forms (labels, required, types), рдЖрдгрд┐ mouse рд╡рд╛рдкрд░рдгрд╛рд▒реНрдпрд╛рдВрд╢рд┐рд╡рд╛рдп рдЗрддрд░ рдХреЛрдгреА рдлрд▓рдХ рд╡рд╛рдкрд░реВ рд╢рдХреЗрд▓ рдХреА рдирд╛рд╣реА рд╣реЗ рдард░рд╡рдгрд╛рд░реЗ рдЫреЛрдЯреЗ attributes.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рджреЛрди рдлрд▓рдХ рдЕрдЧрджреА рд╕рд╛рд░рдЦреЗ рджрд┐рд╕реВ рд╢рдХрддрд╛рдд рдЖрдгрд┐ рдЖрддреВрди рдкреВрд░реНрдгрдкрдгреЗ рд╡реЗрдЧрд│реЗ рдЕрд╕реВ рд╢рдХрддрд╛рдд.

рдПрдХ рдлрд▓рдХ рдмрд┐рдирд╛рдирд╛рд╡рд╛рдЪреНрдпрд╛ рдЖрдпрддрд╛рдВрдиреА рдмрдирд▓реЗрд▓рд╛ рдЖрд╣реЗ: рдЗрдереЗ <div>, рддрд┐рдереЗ <div>, button рд╕рд╛рд░рдЦрд╛ рджрд┐рд╕рд╛рд╡рд╛ рдореНрд╣рдгреВрди рд░рдВрдЧрд╡рд▓реЗрд▓рд╛ рдПрдХ <div onclick>. рджреБрд╕рд░рд╛ рдлрд▓рдХ рдЖрдкрд▓реНрдпрд╛ рднрд╛рдЧрд╛рдВрдирд╛ рдирд╛рд╡реЗ рджреЗрддреЛ: рд╣рд╛ header рдЖрд╣реЗ, рд╣реЗ navigation рдЖрд╣реЗ, рд╣рд╛ main рдордЬрдХреВрд░ рдЖрд╣реЗ, рд╣реА рдкрд╛рдЪ items рдЪреА list рдЖрд╣реЗ, рд╣реЗ button рдЖрд╣реЗ, рдЖрдгрд┐ рдпрд╛ input рдЪреЗ label "Name" рдЖрд╣реЗ.

рдлрдХреНрдд рджреБрд╕рд░рд╛ рдлрд▓рдХ screen reader рд╡рд╛рдЪреВ рд╢рдХрддреЛ ("list, five itemsтАж"), keyboard рдиреЗ рдЪрд╛рд▓рд╡рддрд╛ рдпреЗрддреЛ (Tab buttons рдЖрдгрд┐ links рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрддреЛ, Enter рддреНрдпрд╛рдВрдирд╛ рджрд╛рдмрддреЛ), search engine рд▓рд╛ рд╕рдордЬрддреЛ, рдХрд┐рдВрд╡рд╛ "Enrol рдирд╛рд╡рд╛рдЪреЗ button" рдорд╛рдЧрдгрд╛рд░рд╛ robot рддреНрдпрд╛рдЪреА test рдХрд░реВ рд╢рдХрддреЛ. рддреБрдореНрд╣рд╛рд▓рд╛ рдЬреЛ рдЕрд░реНрде рд╕рд╛рдВрдЧрд╛рдпрдЪрд╛ рдЖрд╣реЗ рддреЛрдЪ рдЕрд░реНрде рдЕрд╕рд▓реЗрд▓рд╛ element рдирд┐рд╡рдбреВрди рд╣реЗ рд╕рдЧрд│реЗ рддреБрдореНрд╣рд╛рд▓рд╛ рдлреБрдХрдЯ рдорд┐рд│рддреЗ.

рдкрд╣рд┐рд▓реНрдпрд╛ рджрд┐рд╡рд╢реА рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдирд┐рдпрдо:

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart TB
    subgraph page["ЁЯУД ui/index.html"]
        sk["ЁЯФЧ skip link (first tab stop)"]
        hd["ЁЯз▒ header тАФ h1 + nav + theme button"]
        mn["ЁЯз▒ main"]
        s1["section#students тАФ h2, filter, ul.cards"]
        s2["section#enrol тАФ h2, form: label+input, label+select, button"]
        s3["section#about тАФ h2, prose"]
        ft["ЁЯз▒ footer"]
        sk --> hd --> mn --> s1 --> s2 --> s3 --> ft
    end
    free["free with the right element:<br/>keyboard ┬╖ focus ┬╖ screen-reader names ┬╖ tests that ask for roles"]
    page -.-> free

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг semantics рдореНрд╣рдгрдЬреЗ рддреБрдореНрд╣реА рдХрдзреАрд╣реА рд╡рд┐рдХрдд рдШреНрдпрд╛рд▓ рдЕрд╢реА рд╕рд░реНрд╡рд╛рдд рд╕реНрд╡рд╕реНрдд accessibility, SEO рдЖрдгрд┐ testability: рддреБрдореНрд╣реА <div> рдРрд╡рдЬреА <button> рд▓рд┐рд╣рд┐рддрд╛ рдЖрдгрд┐ keyboard support, focus, roles рдЖрдгрд┐ рдирд╛рд╡ рдорд┐рд│рддреЗ тАФ рдХрд╛рд╣реАрд╣реА рди рдореЛрдЬрддрд╛. рдирдВрддрд░ рддреЗ рдЬреЛрдбрд╛рдпрдЪреЗ рдореНрд╣рдгрдЬреЗ ARIA рд╡рд╛рдкрд░реВрди browser рд╣рд╛рддрд╛рдиреЗ рдкреБрдиреНрд╣рд╛ рдмрдирд╡рдгреЗ, рддреЗрд╣реА рд╡рд╛рдИрдЯ рдкрджреНрдзрддреАрдиреЗ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

ui/index.html рд╡рд░рдкрд╛рд╕реВрди рдЦрд╛рд▓рдкрд░реНрдпрдВрдд рд╡рд╛рдЪрд╛ тАФ рддреЗ рдЫреЛрдЯреЗ рдЖрд╣реЗ тАФ 80 рдУрд│реАрдВрдкреЗрдХреНрд╖рд╛ рдХрдореА тАФ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ element рддреНрдпрд╛рдЪреНрдпрд╛ рдЕрд░реНрдерд╛рд╕рд╛рдареА рдирд┐рд╡рдбрд▓рд╛ рдЖрд╣реЗ: skip link, рдПрдХ h1 рдЕрд╕рд▓реЗрд▓рд╛ header, рд╕реНрд╡рддрдГрдЪреЗ h2 рдЕрд╕рд▓реЗрд▓реЗ рддреАрди section, рдЬрд┐рдереЗ рдкреНрд░рддреНрдпреЗрдХ control рд▓рд╛ label рдЖрд╣реЗ рдЕрд╕рд╛ form, card рд╕рд╛рдареА рдПрдХ <template>, рдЖрдгрд┐ рд╕рдЬрд╛рд╡рдЯреАрдЪреНрдпрд╛ avatars рд╡рд░ alt="".

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 ui/mock_api.py        # then open http://127.0.0.1:8000/
# 1) keyboard tour: press Tab from the top тАФ skip link, nav links, theme button, filter, form fields, Enrol
# 2) break it on purpose: change <button type="submit"> to <div>Enrol</div>, reload, and try Tab + Enter
# 3) put it back, then remove the <label for="name"> and run:
python3 ui/smoke_test.py      # тЭМ every input has a label
# 4) add a real section of your own: <section id="homework" aria-labelledby="hw-h"><h2 id="hw-h">Homework</h2>тАж</section>

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

Tab рдкреНрд░рддреНрдпреЗрдХ control рдкрд░реНрдпрдВрдд рджрд┐рд╕рдгреНрдпрд╛рдЪреНрдпрд╛ рдХреНрд░рдорд╛рдиреЗрдЪ рдкреЛрд╣реЛрдЪрддреЛ рдЖрдгрд┐ focus ring рд╕реНрдкрд╖реНрдЯ рджрд┐рд╕рддреЗ. <div> рдЕрд╕рддрд╛рдирд╛ Tab рддреНрдпрд╛рд▓рд╛ рдкреВрд░реНрдгрдкрдгреЗ рд╡рдЧрд│рддреЛ рдЖрдгрд┐ Enter рдХрд╛рд╣реАрдЪ рдХрд░рдд рдирд╛рд╣реА тАФ button рддреБрдореНрд╣реА рдХрдзреАрдЪ рди рд▓рд┐рд╣рд┐рд▓реЗрд▓реА рдЪрд╛рд░ рдХрд╛рдореЗ рдХрд░рдд рд╣реЛрддреЗ. Label рдирд╕рд▓реНрдпрд╛рд╡рд░ smoke test fail рд╣реЛрддреЛ рдЖрдгрд┐ рддреЗ рдкрд░рдд рд▓рд╛рд╡рд▓реНрдпрд╛рд╡рд░ рдкреБрдиреНрд╣рд╛ pass рд╣реЛрддреЛ.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рдпреЛрдЧреНрдп element рддреБрдореНрд╣рд╛рд▓рд╛ keyboard, focus, roles рдЖрдгрд┐ рдирд╛рд╡реЗ рдлреБрдХрдЯ рджреЗрддреЛ тАФ рдЖрдгрд┐ рдПрдХ robot рддреЗ рджрд╣рд╛ рдУрд│реАрдВрдд рддрдкрд╛рд╕реВ рд╢рдХрддреЛ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: accessibility рдЪреЗ рдЦрдЯрд▓реЗ, fail рдЭрд╛рд▓реЗрд▓реА audits рдЖрдгрд┐ "tests .btn-3 рдирд┐рд╡рдбрддрд╛рдд рдореНрд╣рдгреВрди flaky рдЖрд╣реЗрдд" тАФ рдпрд╛ рд╕рдЧрд│реНрдпрд╛рдВрдЪреЗ рдореВрд│ рдпрд╛ рдзрдбреНрдпрд╛рдд рдЖрд╣реЗ. Roles рдЖрдгрд┐ labels рдорд╛рдЧрдгрд╛рд▒реНрдпрд╛ tests (lesson 10) redesigns рдирдВрддрд░рд╣реА рдЯрд┐рдХрддрд╛рдд; class names рдорд╛рдЧрдгрд╛рд▒реНрдпрд╛ tests рдЯрд┐рдХрдд рдирд╛рд╣реАрдд.

тПня╕П рдкреБрдвреЗ

рд╕рд╛рдВрдЧрд╛рдбрд╛ рдмрд░реЛрдмрд░ рдЖрд╣реЗ; рдЖрддрд╛ рддреЛ рдкреНрд░рддреНрдпреЗрдХ screen рд╡рд░ рдиреАрдЯ рджрд┐рд╕реЗрд▓ рдЕрд╕реЗ рдХрд░рд╛: CSS layout тАФ box model, flexbox, grid, responsive.

git checkout lesson-03-css-layout

ЁЯз▒ Lesson 02 тАФ HTML structure: the board's skeleton

ЁЯУН You are here: Lesson 02 of 12 ┬╖ Previous: lesson-01-why-ui ┬╖ Next: lesson-03-css-layout


ЁЯУж What's in this branch

Lesson 01, plus the skeleton: semantic elements (landmarks), forms that mean it (labels, required, types), and the small attributes that decide whether anyone but a mouse user can use the board.

ЁЯзТ Explain like I'm 5

Two boards can look identical and be completely different underneath.

One is made of anonymous rectangles: <div> here, <div> there, a <div onclick> painted to look like a button. The other names its parts: this is the header, this is the navigation, this is the main content, this is a list of five items, this is a button, and this input's label is "Name".

Only the second board can be read by a screen reader ("list, five itemsтАж"), operated with a keyboard (Tab reaches buttons and links, Enter presses them), understood by a search engine, or tested by a robot that asks for "the button named Enrol". You get all of that for free by choosing the element that means what you mean.

The rules that matter on day one:

ЁЯЧ║я╕П Diagram

flowchart TB
    subgraph page["ЁЯУД ui/index.html"]
        sk["ЁЯФЧ skip link (first tab stop)"]
        hd["ЁЯз▒ header тАФ h1 + nav + theme button"]
        mn["ЁЯз▒ main"]
        s1["section#students тАФ h2, filter, ul.cards"]
        s2["section#enrol тАФ h2, form: label+input, label+select, button"]
        s3["section#about тАФ h2, prose"]
        ft["ЁЯз▒ footer"]
        sk --> hd --> mn --> s1 --> s2 --> s3 --> ft
    end
    free["free with the right element:<br/>keyboard ┬╖ focus ┬╖ screen-reader names ┬╖ tests that ask for roles"]
    page -.-> free

тЭУ What

ЁЯдФ Why

Because semantics are the cheapest accessibility, SEO and testability you will ever buy: you type <button> instead of <div> and get keyboard support, focus, roles and a name тАФ for nothing. Retrofitting them later means re-implementing the browser by hand with ARIA, badly.

ЁЯФз How (in this repo)

Read ui/index.html top to bottom тАФ it is short тАФ under 80 lines тАФ and every element was chosen for what it means: the skip link, the header with one h1, three sections with their own h2, a form where every control has a label, a <template> for the card, and alt="" on decorative avatars.

ЁЯзк Try it

python3 ui/mock_api.py        # then open http://127.0.0.1:8000/
# 1) keyboard tour: press Tab from the top тАФ skip link, nav links, theme button, filter, form fields, Enrol
# 2) break it on purpose: change <button type="submit"> to <div>Enrol</div>, reload, and try Tab + Enter
# 3) put it back, then remove the <label for="name"> and run:
python3 ui/smoke_test.py      # тЭМ every input has a label
# 4) add a real section of your own: <section id="homework" aria-labelledby="hw-h"><h2 id="hw-h">Homework</h2>тАж</section>

тЬЕ Verify тАФ what you should see

Tab reaches every control in visual order and the focus ring is obvious. With the <div>, Tab skips it entirely and Enter does nothing тАФ the button was doing four jobs you never wrote. The smoke test fails on the missing label and passes again when you restore it.

ЁЯПБ What you just proved

The right element gives you keyboard, focus, roles and names for free тАФ and a robot can check that in ten lines.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: accessibility lawsuits, failed audits and "the tests are flaky because they select .btn-3" all trace back to this lesson. Tests that ask for roles and labels (lesson 10) survive redesigns; tests that ask for class names do not.

тПня╕П Next

The skeleton is right; now make it look like something on every screen: CSS layout тАФ box model, flexbox, grid, responsive.

git checkout lesson-03-css-layout
тЖР Previouswhy uiNext тЖТcss layout

This page is the lesson's README from the lesson-02-html-structure branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.