Keeping Sites Accessible With Types Fotis Papadogeorgopoulos (@isfotis)
Slide 2
Accessibility On The Web
Slide 3
Accessibility on The Web Communicate information to users Name + Role + Value (+ Interaction) “Book tickets, button, press enter to activate” Covered in Web Content Accessibility Guidelines (WCAG)
Slide 4
Source: Semantics to Screen Readers, by Melanie Richards alistapart.com/article/semantics-to-screen-readers
Heading Order Headings provide the outline for the document. They must be consistently nested in the source.
Slide 8
Slide 9
Form Labels Labels provide visual guidance and hierarchy Labels expose the accessible name of the input Placeholders don’t count!
Slide 10
Slide 11
50% Of home pages on the web, don’t have labels Source: https://webaim.org/projects/million/#wcag
Slide 12
Missing Image Alt Text Images, both as <img> and <svg>: ● If they are content, need alternative text ● If they are decorative, should be marked as such
Slide 13
68% Of home pages on the web, don’t have alt text Source: https://webaim.org/projects/million/#wcag
Slide 14
On a bad day, or tight deadline, even the most diligent person will get things wrong
Slide 15
Pause and ponder, with types
Slide 16
hX : HeadingLevel -> List (Attribute msg) -> List (Html msg) -> Html msg
hX level =
case level of
H1 ->
h1
H2 ->
h2
H3 ->
h3
H4 ->
h4
H5 ->
h5
H6 ->
h6
Slide 17
heading : HeadingLevel -> List (Attribute msg) -> List (Html msg) -> Html msg
heading level attrs children =
hX level (class "myHeading" :: attrs) children
subHeading : HeadingLevel -> List (Attribute msg) -> List (Html msg) -> Html msg
subHeading level attrs children =
hX level (class "mySubHeading" :: attrs) children
Slide 18
view : Model -> Html ()
view model =
div []
[ heading H1 [] [ text "Heading Demo" ]
, nestedSection H2 "Hello"
]
nestedSection : HeadingLevel -> String -> Html msg
nestedSection level demoData =
div []
[ subHeading level [] [ text "Data" ]
, p [] [ text "This is a section about data." ]
-- Propagate the heading level + 1
, subHeading (incrementLevel level) [] [ text "Count" ]
, p [] [ text demoData ]
]
Slide 19
Elm type error:
The 1st argument to nestedSection is not what I expect.
The argument is a list of type: “List a”
But nestedSection needs the 1st argument to be: HeadingLevel
Slide 20
type
InputLabelMethod
-- Label contains the input + text
= EmbeddedLabel String
--Label is assocciated with a DOM id
| LabelledById DomId
type DomId
= DomId String
domIdToString (DomId idRef) =
idRef
Slide 21
-- Creating an input
input : InputLabelMethod -> List (Attribute msg) -> List (Html msg) -> Html msg
input labelMethod attrs children =
case labelMethod of
EmbeddedLabel labelText ->
label []
[ text labelText
, Html.input attrs children
]
LabelledById (DomId idRef) ->
Html.input
(id idRef :: attrs)
children
inputLabel : DomId -> List (Attribute msg) -> List (Html msg) -> Html msg
inputLabel idRef attrs children =
label (for (domIdToString idRef) :: attrs) children
Slide 22
-- In use
nameLabelId =
DomId "nameLabel"
view : Model -> Html ()
view model =
div []
[ input (EmbeddedLabel "Email") [ attribute "type" "email ] []
, inputLabel nameLabelId [] [text "Name"]
, input (LabelledById nameLabelId) [ attribute "type" "text" ] []
]
Slide 23
“Hey, I have an error about labels What do I pick?”
Slide 24
“Embedded labels might be simpler”
Slide 25
“Ah, we also have more high-level ones”
Slide 26
“Let’s look at it together”
Slide 27
“In this codebase, we care about form labels”
Slide 28
Slide 29
view =
div []
[ -- Ok
img Decorative [ src "/kitten.jpg" ]
, img (Content "The top of some colored buildings lit by sunset.") [ src "/sunset.jpg" ]
, -- Error! Expected "Purpose"
img [ src "/error.jpg" ]
]
Slide 30
“What is my purpose?”
Slide 31
Slide 32
-- Creating a reusable image/icon
homeIcon : Purpose -> List (Attribute msg) -> Html msg
homeIcon purpose attrs =
svgImg purpose attrs
[ polyline [ points "21 8 21 21 3 21 3 8" ] []
, rect [ x "1", y "3", width "22", height "5" ] []
, line [ x1 "10", y1 "12", x2 "14", y2 "12" ] []
]
Slide 33
-- In use
view model =
nav [] [
a [href "/"] [homeIcon (Content "Home") []],
a [href "/entries"] [
homeIcon Decorative [],
text "Home"
]
]