Keeping Sites Accessible With Types

A presentation at ClojuTre 2019 in September 2019 in Helsinki, Finland by Fotis Papadogeorgopoulos

Slide 1

Slide 1

Keeping Sites Accessible With Types Fotis Papadogeorgopoulos (@isfotis)

Slide 2

Slide 2

Accessibility On The Web

Slide 3

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

Slide 4

Source: Semantics to Screen Readers, by Melanie Richards alistapart.com/article/semantics-to-screen-readers

Slide 5

Slide 5

The Accessibility Cake Markup + Styling + Interactivity Source: http://simplyaccessible.com/article/the-access ibility-stack/

Slide 6

Slide 6

Common Issues

Slide 7

Slide 7

Heading Order Headings provide the outline for the document. They must be consistently nested in the source.

Slide 8

Slide 8

Slide 9

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 10

Slide 11

Slide 11

50% Of home pages on the web, don’t have labels Source: https://webaim.org/projects/million/#wcag

Slide 12

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

Slide 13

68% Of home pages on the web, don’t have alt text Source: https://webaim.org/projects/million/#wcag

Slide 14

Slide 14

On a bad day, or tight deadline, even the most diligent person will get things wrong

Slide 15

Slide 15

Pause and ponder, with types

Slide 16

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

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

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

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

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

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

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

Slide 23

“Hey, I have an error about labels What do I pick?”

Slide 24

Slide 24

“Embedded labels might be simpler”

Slide 25

Slide 25

“Ah, we also have more high-level ones”

Slide 26

Slide 26

“Let’s look at it together”

Slide 27

Slide 27

“In this codebase, we care about form labels”

Slide 28

Slide 28

Slide 29

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

Slide 30

“What is my purpose?”

Slide 31

Slide 31

Slide 32

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

Slide 33

-- In use
view model =
    nav [] [
      a [href "/"] [homeIcon (Content "Home") []],
      a [href "/entries"] [
        homeIcon Decorative [], 
        text "Home"
     ]
    ]

Slide 34

Slide 34

type Purpose =
  | {type: 'decorative'}
  | {type: 'standalone', label: string}

const Decorative = (): Purpose => ({type: 'decorative'});
const Standalone = (label: string): Purpose => ({type: 'standalone', label});

interface IconProps extends SVGAttributes<SVGElement> {
  purpose: Purpose;
  color?: string;
  size?: string | number;
}

Slide 35

Slide 35

Types will not solve everything…

Slide 36

Slide 36

There are layers of expertise, visible and invisible work

Slide 37

Slide 37

Types can help you ask the right questions

Slide 38

Slide 38

Types can help make the work visible

Slide 39

Slide 39

References

  • Tessa Kelly’s accessible-html package for Elm
  • Sara Soueidan on Accessible SVG icons
  • Scott O’ Hara on Contextually Marking up images

Slide 40

Slide 40

Thanks! Want to work with me? Futurice is hiring (futurice.com/careers/) Get in touch @isfotis fotis@fpapado.com Let’s chat in the breaks!