diff --git a/examples/carousel/carousel-1/carousel-1.html b/examples/carousel/carousel-1.html similarity index 65% rename from examples/carousel/carousel-1/carousel-1.html rename to examples/carousel/carousel-1.html index 3c93d9c3cf..7e187f87e6 100644 --- a/examples/carousel/carousel-1/carousel-1.html +++ b/examples/carousel/carousel-1.html @@ -6,13 +6,21 @@ - - - - + + + + - + + @@ -22,19 +30,14 @@

Auto-Rotating Image Carousel Example

-

- NOTE: This is a new example as of the January 2019 release of WAI-ARIA Authoring Practices 1.1. - Please provide feedback in - issue 971. -

The following example implementation of the carousel design pattern @@ -43,36 +46,62 @@

Auto-Rotating Image Carousel Example

The accessibility features section that follows the example describes these features in detail.

+
+

Example View Options

+ +
+

Example

-
@@ -132,7 +161,7 @@

- Survey confirms that TV is America’s most trusted institution + Great Children's Programming on Public TV

@@ -159,7 +188,7 @@

-

8 pm Sunday, March 8, on TV: Sneak peak at the final season.

+

8 pm Sunday, March 8, on TV: Sneak peak at the final season.

@@ -200,7 +229,7 @@

@@ -221,38 +250,54 @@

Accessibility Features

+

Controlling Automatic Slide Rotation

+

+ Users can stop and start slide rotation, which is an essential aspect of accessibility of the carousel for a variety of people with disabilities. + People with low vision or a cognitive disability that effects visual processing or reading benefit from being able to control slide rotation so they have sufficient time to explore slide content. + Similarly, since screen reader users cannot perceive automatic rotation, it can make reading the page confusing and disorienting. + For example, when slides are automatically rotating, a screen reader user may read an element on slide one, execute a screen reader command to read the next element, and, instead of hearing the next element on slide one, hear an element from slide 2 without any knowledge that the element just announced is from an entirely new context. +

+

This example includes the following features for giving users control over slide rotation:

    -
  • Automatic rotation of slides in the carousel can be easily controlled by any user. +
  • + Hovering the mouse over any carousel content pauses automatic rotation. + Automatic rotation resumes when the mouse moves away from the carousel unless another condition, such as keyboard focus, that prevents rotation has been triggered. +
  • +
  • + Moving keyboard focus to any of the carousel content, including the next and previous slide elements, pauses automatic rotation. + Automatic rotation resumes when keyboard focus moves out of the carousel content unless another condition, such as mouse hover, that prevents rotation has been triggered. +
  • +
  • The carousel also contains a rotation control button that can stop and start automatic rotation.
      -
    • - Hovering the mouse over any carousel content pauses automatic rotation. - Automatic rotation resumes when the mouse moves away from the carousel unless another condition, such as keyboard focus, that prevents rotation has been triggered. -
    • -
    • - Moving keyboard focus to any of the carousel content, including the next and previous slide elements, pauses automatic rotation. - Automatic rotation resumes when keyboard focus moves out of the carousel content unless another condition, such as mouse hover, that prevents rotation has been triggered. -
    • -
    • The carousel also contains a rotation control button that can stop and start automatic rotation. -
        -
      • The rotation control button is the first element in the screen reader reading order.
      • -
      • The button is hidden off-screen when it does not have keyboard focus, and becomes visible when users move focus to it with Tab.
      • -
      • - When not visible, the button is positioned off-screen, instead of being hidden with display:none, so it is always available to screen reader users. - This ensures the rotation control is accessible to people using a screen reader that does not move the mouse or keyboard focus when the user is moving its reading cursor. -
      • -
      • If the carousel is rotating, the button is labeled "Stop Automatic Slide Show," informing screen reader users that the slides are changing in addition to providing a way to stop the changes.
      • -
      • If the carousel is not rotating, the button is labeled "Start Automatic Slide Show."
      • -
      • If a user has activated the button to stop the show, the rotation will only restart if the button is activated. Moving keyboard focus or the mouse out of the carousel will not restart rotation.
      • -
      • If keyboard focus is inside the carousel, or if the mouse is hovering over the carousel, the button is disabled; it cannot be used to start rotation.
      • -
      -
    • +
    • The rotation control button is the first element in the screen reader reading order.
    • +
    • The rotation control button is always visible so it is available to all users whether they are interacting via a mouse, keyboard, assistive technology, or touch.
    • +
    • If the carousel is rotating, its accessible name is Stop Automatic Slide Show, informing screen reader users that the slides are changing in addition to providing a way to stop the changes.
    • +
    • If the carousel is not rotating, the accessible name of the button is Start Automatic Slide Show.
    • +
    • If a user has activated the button to stop the show, the rotation will only restart if the button is activated. Moving keyboard focus or the mouse out of the carousel will not restart rotation.
    • +
    • If keyboard focus is inside the carousel, or if the mouse is hovering over the carousel, the button is disabled; it cannot be used to start rotation.
  • +
+

Color Contrast of Text and Rotation Controls

+

+ In the view of this carousel where the controls and captions are displayed on top of the image, the background images can cause color contrast for the controls and text to become insufficient. + This view includes the following features to meet WCAG 2.1 color contrast requirements: +

+
  • - To improve readability of the caption text and content, the transparency of the caption area is decreased when the mouse hovers over the carousel content. - This improves the color contrast ratio of the white text, especially when light colored images are present in the background. + When the rotation control, next slide, and previous slide buttons are rendered on top of the carousel images, the buttons have forground and background colors that meet WCAG 2.1 color contrast requirements. + In addition, the focus styling uses SVG images that make the focus indicator highly visible when a control receives keyboard focus.
  • +
  • The transparency of the caption area is decreased so the caption text meets the WCAG 2.1 color contrast requirements.
+

Screen Reader Announcement of Slide Changes

+

+ When automatic rotation is turned off, the carousel slide content is included in a live region. + This makes it easier for screen reader users to scan through the carousel slides. + When screen reader users activate the next or previous slide button , the new slide content is announced, giving users immediate feedback that helps them determine whether or not to interact with the content. + Very importantly, if automatic rotation is turned on, the live region is disabled. + If it were not, the page would be come unusable as announcements of the continuously changing content constantly interrupt anything else the user is reading. +

@@ -273,8 +318,6 @@

Rotation Control Button

  • Moves focus through interactive elements in the carousel.
  • Rotation control, previous slide, and next slide buttons precede the slide content in the Tab sequence.
  • -
  • The rotation control button moves on screen when it receives keyboard focus and off screen when it is not focused.
  • -
  • Off-screen positioning allows it to be available to screen reader users when not focused.
@@ -359,7 +402,7 @@

Role, Property, State, and Tabindex Attributes

Provides a label that describes the content in the carousel region. - + aria-live=off @@ -375,7 +418,7 @@

Role, Property, State, and Tabindex Attributes

- + aria-live=polite @@ -436,42 +479,15 @@

Role, Property, State, and Tabindex Attributes

- - - - aria-disabled="true" - - - button - - -
    -
  • Applied to the automatic rotation control button when rotation is stopped and conditions that prevent automatic rotation are present.
  • -
  • The button is disabled when a pointer is hovering over the carousel or any element in the carousel has keyboard focus.
  • -
  • The aria-disabled state is used instead of the HTML disabled attribute to keep the button in the Tab sequence.
  • -
  • If hover or focus is removed from the carousel, the aria-disabled attribute is removed from the button.
  • -
- - - - - button - - - - a - - Identifies the element as a button. - - + aria-label="LABEL_STRING" - a + button - Defines the accessible name for the next and previous slide buttons. + Defines the accessible name for the pause auto-rotation button and the next and previous slide buttons. @@ -479,7 +495,7 @@

Role, Property, State, and Tabindex Attributes

aria-controls="IDREF" - a + button
    @@ -495,7 +511,7 @@

    Role, Property, State, and Tabindex Attributes

    Javascript and CSS Source Code

      -
    • CSS: carousel.css
    • +
    • CSS: carousel-1.css
    • Javascript: carousel.js
    • Javascript: carouselItem.js
    • Javascript: carouselButtons.js
    • diff --git a/examples/carousel/carousel-1/css/carousel.css b/examples/carousel/carousel-1/css/carousel.css deleted file mode 100644 index 11ce7cd722..0000000000 --- a/examples/carousel/carousel-1/css/carousel.css +++ /dev/null @@ -1,176 +0,0 @@ - -/* .carousel */ -.carousel-item { - display: none; - max-height: 400px; - max-width: 900px; - position: relative; - overflow: hidden; - width: 100%; -} - -.carousel .carousel-item.active { - display: block; -} - -/* More like bootstrap, less accessible */ - -.carousel .carousel-inner { - max-width: 900px; - position: relative; -} - -.carousel button.pause { - display: block; - font-size: 20px; - width: auto; - left: -300em; - margin-bottom: 10px; - height: auto; - position: relative; - top: 5px; - right: -20px; - border: thin solid outset; -} - -.carousel button[aria-disabled=true] { - color: #666; -} - -.carousel button.pause:focus { - display: block; - position: relative; - font-size: 20px; - width: auto; - left: 0; - margin-bottom: 10px; - height: auto; - top: 5px; - right: -20px; -} - -.carousel .carousel-items { - border: solid 2px transparent; -} - -.carousel .carousel-items.focus { - border-color: white; - outline: solid 3px #005a9c; -} - -.carousel .carousel-inner .carousel-image a img { - height: 100%; - width: 100%; -} - -.carousel .carousel-inner .carousel-caption a { - text-decoration: underline; - border: none; -} - -.carousel .carousel-inner .carousel-caption h3 a { - color: #fff; - font-weight: 600; -} - -.carousel .carousel-inner .carousel-caption a:focus, -.carousel .carousel-inner .carousel-caption a:hover { - outline: solid 2px #fff; - outline-offset: 1px; -} - -.carousel .carousel-inner .carousel-caption p { - font-size: 1em; - line-height: 1.5; - margin-bottom: 0; -} - -.carousel .carousel-caption { - position: absolute; - right: 15%; - bottom: 0; - left: 15%; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); -} - -.carousel .carousel-inner .carousel-caption { - bottom: 0; - left: 0; - padding: 3% 3% 50px; - right: 0; - text-shadow: none; -} - -.carousel:hover .carousel-inner .carousel-caption, -.carousel .carousel-item.focus .carousel-caption { - background-color: rgba(0, 0, 0, 0.4); -} - -.carousel .carousel-inner, -.carousel .carousel-item, -.carousel .carousel-slide { - max-height: 400px; -} - -.carousel .carousel-control { - position: absolute; - top: 0; - z-index: 10; - font-size: 200%; - font-weight: bold; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); -} - -.carousel a.carousel-control svg { - position: relative; - display: inline-block; - top: 45%; -} - -.carousel a.carousel-control svg polygon { - opacity: 0.7; -} - -.carousel a.carousel-control:focus { - border: 3px solid #fff; - outline: 1px solid #005a9c; -} - -.carousel a.carousel-control:focus svg polygon, -.carousel a.carousel-control:hover svg polygon { - opacity: 1; -} - -.carousel a.carousel-control.previous { - bottom: 0; - width: 15%; - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); -} - -.carousel a.carousel-control.previous:focus, -.carousel a.carousel-control.previous:hover { - bottom: 0; - width: 15%; - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.7) 0, rgba(0, 0, 0, 0.0001) 100%); -} - -.carousel a.carousel-control.next { - right: 0; - bottom: 0; - width: 15%; - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); -} - -.carousel a.carousel-control.next:focus, -.carousel a.carousel-control.next:hover { - right: 0; - bottom: 0; - width: 15%; - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.7) 100%); -} diff --git a/examples/carousel/carousel-1/js/pauseButton.js b/examples/carousel/carousel-1/js/pauseButton.js deleted file mode 100644 index ebd875ee56..0000000000 --- a/examples/carousel/carousel-1/js/pauseButton.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -* File: pasueButton.js -* -* Desc: Implements the pause button for the carousel widget -* -*/ - -var PauseButton = function (domNode, carouselObj) { - this.domNode = domNode; - - this.carousel = carouselObj; -}; - -var StartButton = function (domNode, carouselObj) { - this.domNode = domNode; - - this.carousel = carouselObj; -}; - -PauseButton.prototype.init = function () { - this.domNode.addEventListener('click', this.handleClick.bind(this)); - this.domNode.addEventListener('focus', this.handleFocus.bind(this)); - this.domNode.addEventListener('blur', this.handleBlur.bind(this)); -}; - -/* EVENT HANDLERS */ - -PauseButton.prototype.handleClick = function (event) { - this.carousel.toggleRotation(); -}; - -PauseButton.prototype.handleFocus = function (event) { - this.domNode.classList.add('focus'); -}; - -PauseButton.prototype.handleBlur = function (event) { - this.domNode.classList.remove('focus'); -}; diff --git a/examples/carousel/css/carousel-1-more-accessible.css b/examples/carousel/css/carousel-1-more-accessible.css new file mode 100644 index 0000000000..4342f03dd6 --- /dev/null +++ b/examples/carousel/css/carousel-1-more-accessible.css @@ -0,0 +1,185 @@ +/* .carousel */ + +.carousel .carousel-inner { + display: static; +} + +.carousel .carousel-item { + display: none; + max-width: 900px; + width: 100%; +} + +.carousel .carousel-item.active { + display: block; +} + +/* More like bootstrap, less accessible */ + +/* Shared CSS for Pause, Next and Previous Slide Controls */ + +.carousel .controls { + margin: 0; + padding: 0; + width: 100%; + position: relative; + height: 36px; + background-color: #eee; + border: 4px solid #eee; + border-radius: 5px 5px 0 0; +} + +.carousel .controls button { + position: absolute; + top: 6px; + display: block; + background-color: transparent; + border: none; + outline: none; +} + +.carousel .controls button.previous { + right: 58px; +} + +.carousel .controls button.next { + right: 4px; +} + +.carousel .controls button.rotation { + left: 4px; +} + +.carousel .controls button svg rect.background { + stroke: black; + fill: black; + stroke-width: 1px; + opacity: 0.8; +} + +.carousel .controls button svg rect.border { + fill: transparent; + stroke: transparent; + stroke-width: 2px; +} + +/* Next and Previous Slide Controls */ + +.carousel .controls button svg polygon { + stroke: white; + fill: white; + stroke-width: 2; + opacity: 1; +} + +.carousel .controls button.rotation svg polygon.pause { + stroke-width: 4; + fill: transparent; + stroke: transparent; +} + +.carousel .controls button.rotation svg polygon.play { + stroke-width: 1; + fill: transparent; + stroke: transparent; +} + +.carousel .controls button.rotation.pause svg polygon.pause, +.carousel .controls button.rotation.play svg polygon.play { + fill: white; + stroke: white; +} + +/* Common focus styling for svg buttons */ + +.carousel .controls button:focus rect.background, +.carousel .controls button:hover rect.background, +.carousel .controls button:focus rect.border, +.carousel .controls button:hover rect.border { + fill: #005a9c; + stroke: #005a9c; + opacity: 1; +} + +.carousel .controls button:focus rect.border { + stroke: white; +} + +/* Caption Positioning */ + +.carousel .carousel-items { + width: 100%; + background-color: #eee; + border: solid 4px #eee; + border-radius: 0 0 5px 5px; +} + +.carousel .carousel-items.focus { + border-color: #005a9c; +} + +.carousel .carousel-item .carousel-image { + margin: 0; + padding: 0; + width: 100%; +} + +.carousel .carousel-item .carousel-image a { + margin: 0; + padding: 0; +} + +.carousel .carousel-item .carousel-image a img { + margin: 0; + padding: 0; + display: block; + overflow: hidden; + max-height: 100%; + max-width: 100%; +} + +.carousel .carousel-item .carousel-caption { + margin: 0; + padding: 0.5em; + width: 100%; + height: 3em; + text-align: center; +} + +.carousel .carousel-item .carousel-caption a { + display: inline-block; + background-color: rgba(0, 0, 0, 0); + padding-left: 0.25em; + padding-right: 0.25em; + padding-top: 0.125em; + padding-bottom: 0.125em; + border-radius: 5px; + border: 2px solid transparent; + margin: 0; + text-decoration: underline; +} + +.carousel .carousel-item .carousel-caption h3 { + margin: 0; + padding: 0; + font-weight: bold; +} + +.carousel .carousel-item .carousel-caption h3 a { + color: black; +} + +.carousel .carousel-item .carousel-caption a:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.carousel .carousel-item .carousel-caption a:focus { + border-color: #005a9c; + background-color: rgba(0, 0, 0, 0.1); + outline: none; +} + +.carousel .carousel-item .carousel-caption p { + margin: 0; + padding: 0; +} diff --git a/examples/carousel/css/carousel-1.css b/examples/carousel/css/carousel-1.css new file mode 100644 index 0000000000..735f61e3db --- /dev/null +++ b/examples/carousel/css/carousel-1.css @@ -0,0 +1,165 @@ + +/* .carousel */ + +.carousel .carousel-inner { + position: relative; +} + +.carousel .carousel-item { + display: none; + max-height: 400px; + max-width: 900px; + position: relative; + overflow: hidden; + width: 100%; +} + +.carousel .carousel-item.active { + display: block; +} + +/* More like bootstrap, less accessible */ + +.carousel .carousel-items { + border: solid 2px transparent; +} + +.carousel .carousel-items.focus { + border-color: white; + outline: solid 3px #005a9c; +} + +.carousel .carousel-item .carousel-image a img { + height: 100%; + width: 100%; +} + +.carousel .carousel-item .carousel-caption a { + text-decoration: underline; +} + +.carousel .carousel-item .carousel-caption a, +.carousel .carousel-item .carousel-caption span.contrast { + display: inline-block; + background-color: rgba(0, 0, 0, 0.65); + padding-left: 0.25em; + padding-right: 0.25em; + border-radius: 5px; + border: 2px solid transparent; + margin: 0; +} + +.carousel .carousel-item .carousel-caption h3 a { + color: #fff; + font-weight: 600; +} + +.carousel .carousel-item .carousel-caption a:hover, +.carousel .carousel-item .carousel-caption span.contrast:hover { + background-color: rgba(0, 0, 0, 1); + margin: 0; +} + +.carousel .carousel-item .carousel-caption a:focus { + background-color: rgba(0, 0, 0, 1); + border-color: #fff; + margin: 0; +} + +.carousel .carousel-item .carousel-caption p { + font-size: 1em; + line-height: 1.5; + margin-bottom: 0; +} + +.carousel .carousel-item .carousel-caption { + position: absolute; + right: 15%; + bottom: 0; + left: 15%; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; +} + +/* Shared CSS for Pause, Next and Previous Slide Controls */ + +.carousel .controls button { + padding: 0; + position: absolute; + top: 5px; + z-index: 10; + background-color: transparent; + border: none; + outline: none; +} + +.carousel .controls button svg rect.background { + stroke: black; + fill: black; + stroke-width: 1px; + opacity: 0.6; +} + +.carousel .controls button svg rect.border { + fill: transparent; + stroke: transparent; + stroke-width: 2px; +} + +/* Next and Previous Slide Controls */ + +.carousel .controls button svg polygon { + stroke: white; + fill: white; + stroke-width: 2; + opacity: 1; +} + +.carousel .controls button.previous { + right: 50px; +} + +.carousel .controls button.next { + right: 6px; +} + +/* Pause Control */ + +.carousel .controls button.rotation { + left: 6px; +} + +.carousel .controls button.rotation svg polygon.pause { + stroke-width: 4; + fill: transparent; + stroke: transparent; +} + +.carousel .controls button.rotation svg polygon.play { + stroke-width: 1; + fill: transparent; + stroke: transparent; +} + +.carousel .controls button.rotation.pause svg polygon.pause, +.carousel .controls button.rotation.play svg polygon.play { + fill: white; + stroke: white; +} + +/* Common focus styling for svg buttons */ + +.carousel .controls button:focus rect.background, +.carousel .controls button:hover rect.background, +.carousel .controls button:focus rect.border, +.carousel .controls button:hover rect.border { + fill: #005a9c; + stroke: #005a9c; + opacity: 1; +} + +.carousel .controls button:focus rect.border { + stroke: white; +} diff --git a/examples/carousel/carousel-1/images/amsterdamslide__800x600.jpg b/examples/carousel/images/amsterdamslide__800x600.jpg similarity index 100% rename from examples/carousel/carousel-1/images/amsterdamslide__800x600.jpg rename to examples/carousel/images/amsterdamslide__800x600.jpg diff --git a/examples/carousel/carousel-1/images/britcomdavidslide__800x600.jpg b/examples/carousel/images/britcomdavidslide__800x600.jpg similarity index 100% rename from examples/carousel/carousel-1/images/britcomdavidslide__800x600.jpg rename to examples/carousel/images/britcomdavidslide__800x600.jpg diff --git a/examples/carousel/carousel-1/images/foyleswarslide__800x600.jpg b/examples/carousel/images/foyleswarslide__800x600.jpg similarity index 100% rename from examples/carousel/carousel-1/images/foyleswarslide__800x600.jpg rename to examples/carousel/images/foyleswarslide__800x600.jpg diff --git a/examples/carousel/carousel-1/images/lands-endslide__800x600.jpg b/examples/carousel/images/lands-endslide__800x600.jpg similarity index 100% rename from examples/carousel/carousel-1/images/lands-endslide__800x600.jpg rename to examples/carousel/images/lands-endslide__800x600.jpg diff --git a/examples/carousel/carousel-1/images/mag800-2__800x600.jpg b/examples/carousel/images/mag800-2__800x600.jpg similarity index 100% rename from examples/carousel/carousel-1/images/mag800-2__800x600.jpg rename to examples/carousel/images/mag800-2__800x600.jpg diff --git a/examples/carousel/carousel-1/images/trustslide-2__800x600.jpg b/examples/carousel/images/trustslide-2__800x600.jpg similarity index 100% rename from examples/carousel/carousel-1/images/trustslide-2__800x600.jpg rename to examples/carousel/images/trustslide-2__800x600.jpg diff --git a/examples/carousel/carousel-1/js/carousel.js b/examples/carousel/js/carousel.js similarity index 68% rename from examples/carousel/carousel-1/js/carousel.js rename to examples/carousel/js/carousel.js index 9e3071e921..96e79aeaef 100644 --- a/examples/carousel/carousel-1/js/carousel.js +++ b/examples/carousel/js/carousel.js @@ -22,8 +22,8 @@ var Carousel = function (domNode) { this.currentItem = null; this.pauseButton = null; - this.startLabel = 'Start automatic slide show'; - this.stopLabel = 'Stop automatic slide show'; + this.playLabel = 'Start automatic slide show'; + this.pauseLabel = 'Stop automatic slide show'; this.rotate = true; this.hasFocus = false; @@ -34,12 +34,14 @@ var Carousel = function (domNode) { Carousel.prototype.init = function () { + var elems, elem, button, items, item, imageLinks, i; + this.liveRegionNode = this.domNode.querySelector('.carousel-items'); - var items = this.domNode.querySelectorAll('.carousel-item'); + items = this.domNode.querySelectorAll('.carousel-item'); - for (var i = 0; i < items.length; i++) { - var item = new CarouselItem(items[i], this); + for (i = 0; i < items.length; i++) { + item = new CarouselItem(items[i], this); item.init(); this.items.push(item); @@ -50,7 +52,7 @@ Carousel.prototype.init = function () { } this.lastItem = item; - var imageLinks = items[i].querySelectorAll('.carousel-image a'); + imageLinks = items[i].querySelectorAll('.carousel-image a'); if (imageLinks && imageLinks[0]) { imageLinks[0].addEventListener('focus', this.handleImageLinkFocus.bind(this)); @@ -59,27 +61,28 @@ Carousel.prototype.init = function () { } - // Next Slide and Previous Slide Buttons + // Pause, Next Slide and Previous Slide Buttons - var elems = document.querySelectorAll('.carousel a.carousel-control'); + elems = document.querySelectorAll('.carousel .controls button'); - for (var i = 0; i < elems.length; i++) { - if (elems[i].tagName.toLowerCase() == 'a') { - var button = new CarouselButton(elems[i], this); + for (i = 0; i < elems.length; i++) { + elem = elems[i]; - button.init(); + if (elem.classList.contains('rotation')) { + button = new PauseButton(elem, this); + this.pauseButton = elem; + this.pauseButton.classList.add('pause'); + this.pauseButton.setAttribute('aria-label', this.pauseLabel); + } + else { + button = new CarouselButton(elem, this); } - } - - this.currentItem = this.firstItem; - this.pauseButton = this.domNode.parentNode.parentNode.querySelector('button.pause'); - if (this.pauseButton) { - var button = new PauseButton(this.pauseButton, this); button.init(); - this.pauseButton.innerHTML = this.stopLabel; } + this.currentItem = this.firstItem; + this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this)); this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this)); @@ -151,42 +154,42 @@ Carousel.prototype.rotateSlides = function () { setTimeout(this.rotateSlides.bind(this), this.timeInterval); }; -Carousel.prototype.startRotation = function () { +Carousel.prototype.updateRotation = function () { + if (!this.hasHover && !this.hasFocus && !this.isStopped) { this.rotate = true; this.liveRegionNode.setAttribute('aria-live', 'off'); - this.pauseButton.innerHTML = this.stopLabel; } - this.disablePauseButton(); -}; - -Carousel.prototype.stopRotation = function () { - this.rotate = false; - this.liveRegionNode.setAttribute('aria-live', 'polite'); - this.pauseButton.innerHTML = this.startLabel; - this.disablePauseButton(); -}; + else { + this.rotate = false; + this.liveRegionNode.setAttribute('aria-live', 'polite'); + } -Carousel.prototype.disablePauseButton = function () { - if (this.hasHover || this.hasFocus) { - this.pauseButton.setAttribute('aria-disabled', 'true'); + if (this.isStopped) { + this.pauseButton.setAttribute('aria-label', this.playLabel); + this.pauseButton.classList.remove('pause'); + this.pauseButton.classList.add('play'); } else { - this.pauseButton.removeAttribute('aria-disabled'); + this.pauseButton.setAttribute('aria-label', this.pauseLabel); + this.pauseButton.classList.remove('play'); + this.pauseButton.classList.add('pause'); } + }; Carousel.prototype.toggleRotation = function () { if (this.isStopped) { - if (this.pauseButton.getAttribute('aria-disabled') !== 'true') { + if (!this.hasHover && !this.hasFocus) { this.isStopped = false; - this.startRotation(); } } else { this.isStopped = true; - this.stopRotation(); } + + this.updateRotation(); + }; Carousel.prototype.handleImageLinkFocus = function () { @@ -197,19 +200,21 @@ Carousel.prototype.handleImageLinkBlur = function () { this.liveRegionNode.classList.remove('focus'); }; -Carousel.prototype.handleMouseOver = function () { - this.hasHover = true; - this.stopRotation(); +Carousel.prototype.handleMouseOver = function (event) { + if (!this.pauseButton.contains(event.target)) { + this.hasHover = true; + } + this.updateRotation(); }; Carousel.prototype.handleMouseOut = function () { this.hasHover = false; - this.startRotation(); + this.updateRotation(); }; /* Initialize Carousel Tablists */ -window.addEventListener('load', function (event) { +window.addEventListener('load', function () { var carousels = document.querySelectorAll('.carousel'); for (var i = 0; i < carousels.length; i++) { diff --git a/examples/carousel/carousel-1/js/carouselButtons.js b/examples/carousel/js/carouselButtons.js similarity index 73% rename from examples/carousel/carousel-1/js/carouselButtons.js rename to examples/carousel/js/carouselButtons.js index 22bde956e9..82c6396775 100644 --- a/examples/carousel/carousel-1/js/carouselButtons.js +++ b/examples/carousel/js/carouselButtons.js @@ -33,7 +33,6 @@ var CarouselButton = function (domNode, carouselObj) { }; CarouselButton.prototype.init = function () { - this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); this.domNode.addEventListener('click', this.handleClick.bind(this)); this.domNode.addEventListener('focus', this.handleFocus.bind(this)); this.domNode.addEventListener('blur', this.handleBlur.bind(this)); @@ -51,26 +50,6 @@ CarouselButton.prototype.changeItem = function () { /* EVENT HANDLERS */ -CarouselButton.prototype.handleKeydown = function (event) { - var flag = false; - - switch (event.keyCode) { - case this.keyCode.SPACE: - case this.keyCode.RETURN: - this.changeItem(); - this.domNode.focus(); - flag = true; - break; - - default: - break; - } - - if (flag) { - event.stopPropagation(); - event.preventDefault(); - } -}; CarouselButton.prototype.handleClick = function (event) { this.changeItem(); @@ -79,11 +58,11 @@ CarouselButton.prototype.handleClick = function (event) { CarouselButton.prototype.handleFocus = function (event) { this.carousel.hasFocus = true; this.domNode.classList.add('focus'); - this.carousel.stopRotation(); + this.carousel.updateRotation(); }; CarouselButton.prototype.handleBlur = function (event) { this.carousel.hasFocus = false; this.domNode.classList.remove('focus'); - this.carousel.startRotation(); + this.carousel.updateRotation(); }; diff --git a/examples/carousel/carousel-1/js/carouselItem.js b/examples/carousel/js/carouselItem.js similarity index 93% rename from examples/carousel/carousel-1/js/carouselItem.js rename to examples/carousel/js/carouselItem.js index 7ed5417f63..dc1f1d1513 100644 --- a/examples/carousel/carousel-1/js/carouselItem.js +++ b/examples/carousel/js/carouselItem.js @@ -32,11 +32,11 @@ CarouselItem.prototype.show = function () { CarouselItem.prototype.handleFocusIn = function (event) { this.domNode.classList.add('focus'); this.carousel.hasFocus = true; - this.carousel.stopRotation(); + this.carousel.updateRotation(); }; CarouselItem.prototype.handleFocusOut = function (event) { this.domNode.classList.remove('focus'); this.carousel.hasFocus = false; - this.carousel.startRotation(); + this.carousel.updateRotation(); }; diff --git a/examples/carousel/js/pauseButton.js b/examples/carousel/js/pauseButton.js new file mode 100644 index 0000000000..833e274307 --- /dev/null +++ b/examples/carousel/js/pauseButton.js @@ -0,0 +1,22 @@ +/* +* File: pasueButton.js +* +* Desc: Implements the pause button for the carousel widget +* +*/ + +var PauseButton = function (domNode, carouselObj) { + this.domNode = domNode; + + this.carousel = carouselObj; +}; + +PauseButton.prototype.init = function () { + this.domNode.addEventListener('click', this.handleClick.bind(this)); +}; + +/* EVENT HANDLERS */ + +PauseButton.prototype.handleClick = function () { + this.carousel.toggleRotation(); +}; diff --git a/index.html b/index.html index 43d1fe56ac..bd370078ef 100644 --- a/index.html +++ b/index.html @@ -786,7 +786,7 @@

      This document is governed by the 1 March 2019 W3C Process Document. -

@@ -4500,8 +4501,8 @@

5.1

5.2 How Are Name and Description Strings Derived?

Because there are several elements and attributes for specifying text to include in an accessible name or description string, and because authors can combine them in a practically endless number of ways, browsers implement fairly complex algorithms for assembling the strings. -The sections on accessible name calculation and accessible description calculation provide detailed explanations of the algorithms. -However, most authors do not need detailed understanding of the algorithms since nearly all circumstances where a name or description is useful are supported by the coding patterns described in the naming techniques and describing techniques sections. +The sections on accessible name calculation and accessible description calculation explain the algorithms and how they implement precedence. +However, most authors do not need such detailed understanding of the algorithms since nearly all circumstances where a name or description is useful are supported by the coding patterns described in the naming techniques and describing techniques sections.

@@ -4538,8 +4539,9 @@
5.3.1.2 Ru
5.3.1.3 Rule 3: Prefer Native Techniques

- In HTML documents, whenever possible, rely on HTML naming techniques, such as the HTML label element, alt attribute for images, caption element for tables, etc. + In HTML documents, whenever possible, rely on HTML naming techniques, such as the HTML label element for form elements and caption element for tables. While less flexible, their simplicity and reliance on visible text help ensure robust accessible experiences. + Several of the naming techniques highlight specific accessibility advantages of using HTML features instead of ARIA attributes.

@@ -4605,8 +4607,7 @@
5.3.2.1 Nam In two special cases, certain descendants are ignored: group descendants of treeitem elements and menu descendants of menuitem elements are omitted from the calculation. For example, in the following tree, the name of the first tree item is Fruits; Apples, Bananas, and Oranges are omitted.

-

-<ul role="tree">
+        
<ul role="tree">
   <li role="treeitem">Fruits
     <ul role="group">
       <li role="treeitem">Apples</li>
@@ -4614,8 +4615,7 @@ 
5.3.2.1 Nam <li role="treeitem">Oranges</li> </ul> </li> -</ul> -
+</ul>
Warning

If an element with one of the above roles that supports naming from child content is named by using aria-label or aria-labelledby, content contained in the element and its descendants is hidden from assistive technology users unless the descendant content is referenced by aria-labelledby. It is strongly recommended to avoid using either of these attributes to override content of one of the above elements except in rare circumstances where hiding content from assistive technology users is beneficial. @@ -4639,11 +4639,9 @@

-

-  <nav aria-label="Product">
-    <!-- list of navigation links to product pages -->
-  </nav>
-          
+
<nav aria-label="Product">
+  <!-- list of navigation links to product pages -->
+</nav>

When encountering this navigation region, a screen reader user will hear the name and role of the element, e.g., "Product navigation region", and then be able to read through the links contained in the region.

@@ -4664,19 +4662,15 @@
aria-labelledby property enables authors to reference other elements on the page to define an accessible name. For example, the following switch is named by the text content of a previous sibling element.

-

-  <span id="night-mode-label">Night mode</span>
-  <span role="switch" aria-checked="false" tabindex="0" aria-labelledby="night-mode-label"></span>
-          
+
<span id="night-mode-label">Night mode</span>
+<span role="switch" aria-checked="false" tabindex="0" aria-labelledby="night-mode-label"></span>

Note that while using aria-labelledby is similar in this situation to using an HTML label element with the for attribute, one significant difference is that browsers do not automatically make clicking on the labeling element activate the labeled element; that is an author responsibility. However, HTML label cannot be used to label a span element. Fortunately, an HTML input with type="checkbox" allows the ARIA switch role, so when feasible, using the following approach creates a more robust solution.

-

-  <label for="night-mode">Night mode</label>
-  <input type="checkbox" role="switch" id="night-mode">
-          
+
<label for="night-mode">Night mode</label>
+<input type="checkbox" role="switch" id="night-mode">

The aria-labelledby property is useful in a wide variety of situations because:

  • It has the highest precedence when browsers calculate accessible names, i.e., it overrides names from child content and all other naming attributes, including aria-label.

  • @@ -4688,18 +4682,16 @@

    An example of referencing a hidden element with aria-labelledby could be a label for a night switch control:

    <span id="night-mode-label" hidden>Night mode</span>
    -  <input type="checkbox" role="switch" aria-labelledby="night-mode-label">
    +<input type="checkbox" role="switch" aria-labelledby="night-mode-label">

    In some cases, the most effective name for an element is its own content combined with the content of another element. Because aria-labelledby has highest precedence in name calculation, in those situations, it is possible to use aria-labelledby to reference both the element itself and the other element. In the following example, the "Read more..." link is named by the element itself and the article’s heading, resulting in a name for the link of "Read more... 7 ways you can help save the bees".

    -
    
    -  <h2 id="bees-heading">7 ways you can help save the bees</h2>
    -  <p>Bees are disappearing rapidly. Here are seven things you can do to help.</p>
    -  <p><a id="bees-read-more" aria-labelledby="bees-read-more bees-heading">Read more...</a></p>
    -          
    +
    <h2 id="bees-heading">7 ways you can help save the bees</h2>
    +<p>Bees are disappearing rapidly. Here are seven things you can do to help.</p>
    +<p><a id="bees-read-more" aria-labelledby="bees-read-more bees-heading">Read more...</a></p>

    When multiple elements are referenced by aria-labelledby, text content from each referenced element is concatenated in the order specified in the aria-labelledby value. If an element is referenced more than one time, only the first reference is processed. @@ -4731,21 +4723,17 @@

    label element as follows gives the checkbox an accessible name.

    -
    
    -  <label>
    -    <input type="checkbox" name="subscribe">
    -    subscribe to our newsletter
    -  </label>
    -          
    +
    <label>
    +  <input type="checkbox" name="subscribe">
    +  subscribe to our newsletter
    +</label>

    A form control can also be associated with a label by using the for attribute on the label element. This allows the label and the form control to be siblings or have different parents in the DOM, but requires adding an id attribute to the form control, which can be error-prone. When possible, use the above encapsulation technique for association instead of the following for attribute technique.

    -
    
    -  <input type="checkbox" name="subscribe" id="subscribe_checkbox">
    -  <label for="subscribe_checkbox">subscribe to our newsletter</label>
    -          
    +
    <input type="checkbox" name="subscribe" id="subscribe_checkbox">
    +<label for="subscribe_checkbox">subscribe to our newsletter</label>

    Using the label element is an effective technique for satisfying Rule 2: Prefer Visible Text. It also satisfies Rule 3: Prefer Native Techniques. @@ -4760,14 +4748,12 @@

    5. The HTML fieldset element can be used to group form controls, and the legend element can be used to give the group a name. For example, a group of radio buttons can be grouped together in a fieldset, where the legend element labels the group for the radio buttons.

    -
    
    -  <fieldset>
    -    <legend>Select your starter class</legend>
    -    <label><input name="starter-class" value="green"> Green</label>
    -    <label><input name="starter-class" value="red"> Red</label>
    -    <label><input name="starter-class" value="blue"> Blue</label>
    -  </fieldset>
    -          
    +
    <fieldset>
    +  <legend>Select your starter class</legend>
    +  <label><input type="radio" name="starter-class" value="green"> Green</label>
    +  <label><input type="radio" name="starter-class" value="red"> Red</label>
    +  <label><input type="radio" name="starter-class" value="blue"> Blue</label>
    +</fieldset>

    This grouping technique is particularly useful for presenting multiple choice questions. It enables authors to associate a question with a group of answers. @@ -4776,27 +4762,32 @@

    5.

    Similar benefits can be gained from grouping and naming other types of related form fields using fieldset and legend.

    -
    
    -  <fieldset>
    -    <legend>Shipping address</legend>
    -    <p><label>Full name <input name="name" required></label></p>
    -    <p><label>Address line 1 <input name="address-1" required></label></p>
    -    <p><label>Address line 2 <input name="address-2"></label></p>
    -    ...
    -  </fieldset>
    -  <fieldset>
    -    <legend>Billing address</legend>
    -    ...
    -  </fieldset>
    -          
    +
    <fieldset>
    +  <legend>Shipping address</legend>
    +  <p><label>Full name <input name="name" required></label></p>
    +  <p><label>Address line 1 <input name="address-1" required></label></p>
    +  <p><label>Address line 2 <input name="address-2"></label></p>
    +  ...
    +</fieldset>
    +<fieldset>
    +  <legend>Billing address</legend>
    +  ...
    +</fieldset>

    Using the legend element to name a fieldset element satisfies Rule 2: Prefer Visible Text and Rule 3: Prefer Native Techniques.

    5.3.2.6 Naming Tables and Figures with Captions
    -

    The accessible name for the HTML table and figure elements can be derived from a child caption or figcaption element, respectively.

    -

    Tables and figures often have a caption to explain what the table of figure is about, how to read them, and sometimes giving them numbers to be able to refer to them in surrounding prose. This can help users navigate and understand the content better. This is generally useful for all users, but especially for users of assistive technologies.

    -

    In HTML, the table element marks up a data table, and can be provided with a caption using the caption element.

    +

    + The accessible name for HTML table and figure elements can be derived from a child caption or figcaption element, respectively. + Tables and figures often have a caption to explain what they are about, how to read them, and sometimes giving them numbers used to refer to them in surrounding prose. + Captions can help all users better understand content, but are especially helpful to users of assistive technologies. +

    +

    + In HTML, the table element marks up a data table, and can be provided with a caption using the caption element. + If the table element does not have aria-label or aria-labelledby, then the caption will be used as the accessible name. + For example, the accessible name of the following table is Special opening hours. +

    <table>
      <caption>Special opening hours</caption>
      <tr><td>30 May <td>Closed
    @@ -4820,12 +4811,23 @@ 
    5.3 </table>

    Note: Above table content is from Caloric restriction, the traditional Okinawan diet, and healthy aging: the diet of the world's longest-lived people and its potential impact on morbidity and life span.

    -

    Similarly, the HTML figure element can be given a caption using the figcaption element. The caption can appear before or after the figure, but it is more common for figures to have the caption after.

    +

    + If a table is named using aria-label or aria-labelledby, then a caption element, if present, will become an accessible description. + For an example, see Describing Tables and Figures with Captions. +

    +

    + Similarly, an HTML figure element can be given a caption using the figcaption element. + The caption can appear before or after the figure, but it is more common for figures to have the caption after. +

    <figure>
      <img alt="Painting of a person walking in a desert." src="Hole_JesusalDesierto.jpg">
      <figcaption>Jesus entering the desert as imagined by William Hole, 1908</figcaption>
     </figure>
    - +

    + Like with table elements, if a figure is not named using aria-label or aria-labelledby, the content of the figcaption element will be used as the accessible name. + However unlike table elements, if the figcaption element is not used for the name, it does not become an accessible description unless it is referenced by aria-describedby. + Nevertheless, assistive technologies will render the content of a figcaption regardless of whether it is used as a name, description, or neither. +

    Using the caption element to name a table element, or a figcaption element to name a figure element, satisfies Rule 2: Prefer Visible Text and Rule 3: Prefer Native Techniques.

    @@ -4833,7 +4835,8 @@
    5.3
    5.3.2.7 Fallback Names Derived from Titles and Placeholders

    When an accessible name is not provided using one of the primary techniques (e.g., the aria-label or aria-labelledby attributes), or native markup techniques (e.g., the HTML label element, or the alt attribute of the HTML img element), browsers calculate an accessible name from other attributes as a fallback mechanism. - It is recommended authors prefer the explicit labeling techniques described above over fallback techniques described in this section. + Because the attributes used in fallback name calculation are not intended for naming, they typically yield low quality accessible names that are not effective. +So, As advised by Rule 4: Avoid Browser Fallback, prefer the explicit labeling techniques described above over fallback techniques described in this section.

    @@ -4845,10 +4848,10 @@

    For example, a fieldset element without a legend element child, but with a title attribute, gets its accessible name from the title attribute.

    <fieldset title="Select your starter class">
    -    <label><input name="starter-class" value="green"> Green</label>
    -    <label><input name="starter-class" value="red"> Red</label>
    -    <label><input name="starter-class" value="blue"> Blue</label>
    -   </fieldset>
    + <label><input type="radio" name="starter-class" value="green"> Green</label> + <label><input type="radio" name="starter-class" value="red"> Red</label> + <label><input type="radio" name="starter-class" value="blue"> Blue</label> +</fieldset>

    For the HTML input and textarea elements, the placeholder attribute is used as a fallback labeling mechanism if nothing else (including the title attribute) results in a label. @@ -5692,8 +5695,7 @@

    5.3.5 Acces When calculating a name from content for the treeitem role, descendant content of child group elements are not included. For example, in the following tree, the name of the first tree item is Fruits; Apples, Bananas, and Oranges are automatically omitted.

    -
    
    -<ul role="tree">
    +        
    <ul role="tree">
       <li role="treeitem">Fruits
         <ul role="group">
           <li role="treeitem">Apples</li>
    @@ -5701,14 +5703,12 @@ 

    5.3.5 Acces <li role="treeitem">Oranges</li> </ul> </li> -</ul> -

    +</ul>

    Similarly, when calculating a name from content for the menuitem role, descendant content of child menu elements are not included. So, the name of the first parent menuitem in the following menu is Fruits.

    -
    
    -<ul role="menu">
    +        
    <ul role="menu">
       <li role="menuitem">Fruits
         <ul role="menu">
           <li role="menuitem">Apples</li>
    @@ -5716,8 +5716,7 @@ 

    5.3.5 Acces <li role="menuitem">Oranges</li> </ul> </li> -</ul> -

    +</ul>
    5.3.5.1 Examples of non-recursive accessible name calculation
    @@ -5777,12 +5776,10 @@
    - <div id="meeting-1"> - <button aria-labelledby="meeting-1" aria-label="Remove meeting:">X</button> - Daily status report - </div> - +
    <div id="meeting-1">
    +  <button aria-labelledby="meeting-1" aria-label="Remove meeting:">X</button>
    +  Daily status report
    +</div>
    @@ -5798,19 +5795,15 @@

    -
    
    -  <button aria-describedby="trash-desc">Move to trash</button>
    -  ...
    -  <p id="trash-desc">Items in the trash will be permanently removed after 30 days.</p>
    -          
    +
    <button aria-describedby="trash-desc">Move to trash</button>
    +...
    +<p id="trash-desc">Items in the trash will be permanently removed after 30 days.</p>

    Descriptions are reduced to text strings. For example, if the description contains an HTML img element, a text equivalent of the image is computed.

    -
    
    -  <button aria-describedby="trash-desc"> Move to <img src="bin.svg" alt="trash"></button>
    -  ...
    -  <p id="trash-desc">Items in <img src="bin.svg" alt="the trash"> will be permanently removed after 30 days.</p>
    -          
    +
    <button aria-describedby="trash-desc"> Move to <img src="bin.svg" alt="trash"></button>
    +...
    +<p id="trash-desc">Items in <img src="bin.svg" alt="the trash"> will be permanently removed after 30 days.</p>

    As with aria-labelledby, it is possible to reference an element using aria-describedby even if that element is hidden. @@ -5819,62 +5812,34 @@

    input element is Your username is the name that you use to log in to this service.

    -
    
    -  <label for="username">Username</label>
    -  <input id="username" name="username" aria-describedby="username-desc">
    -  <button aria-expanded="false" aria-controls="username-desc" aria-label="Help about username">?</button>
    -  <p id="username-desc" hidden>
    -    Your username is the name that you use to log in to this service.
    -  </p>
    -          
    +
    <label for="username">Username</label>
    +<input id="username" name="username" aria-describedby="username-desc">
    +<button aria-expanded="false" aria-controls="username-desc" aria-label="Help about username">?</button>
    +<p id="username-desc" hidden>
    +  Your username is the name that you use to log in to this service.
    +</p>
    -
    -
    5.4.1.2 Describing by referencing content with aria-details
    -

    - In some cases, a plain text description is insufficient. - The aria-details property can be used in such situations. - In the following example, a text field for a passenger’s name (when booking a flight) has a description that is a list of three items, and contains a link to an external document with further details. -

    - -
    
    -  <ul id="full-name-desc">
    -    <li>The passenger's name must match the name in their passport.</li>
    -    <li>The name must consist of only characters in the A-Z range.</li>
    -    <li><a href="faq.html#name">What if the name in the passport contains other characters?</a></li>
    -  </ul>
    -  <fieldset>
    -    <legend>Passenger 1 (adult)</legend>
    -    <p>
    -      <label>Full name <input name="full-name" aria-details="full-name-desc"></label>
    -    </p>
    -    ...
    -  </fieldset>
    -          
    - +
    +
    5.4.1.2 Describing Tables and Figures with Captions

    - If both aria-details and aria-describedby are specified on an element, browsers ignore the aria-describedby value when calculating the accessible description. - Specifying both is potentially useful if there is a need to provide a fallback description for browsers and assistive technologies that do not yet support aria-details. + In HTML, if the table is named using aria-label or aria-labelledby, a child caption element becomes an accessible description. + For example, a preceding heading might serve as an appropriate accessible name, and the caption element might contain a longer description. + In such a situation, aria-labelledby could be used on the table to set the accessible name to the heading content and the caption would become the accessible description.

    -
    - -
    -
    5.4.1.3 Describing Tables and Figures with Captions
    -

    In HTML, a table element can get its accessible description from the child caption element, if that wasn't used as the accessible name.

    -

    For example, a preceding heading might be appropriate as an accessible name for a table, and the caption element can contain a longer description of the table and how the table is laid out. In such a situation, the aria-labelledby attribute can be used on the table to set the accessible name to that of the heading.

    -
    <h2 id="events-heading">Upcoming events</h2>
    -<table aria-labelledby="events-heading">
    - <caption>
    +          
    <h2 id="events-heading">Upcoming events</h2>
    +<table aria-labelledby="events-heading">
    + <caption>
       Calendar of upcoming events, weeks 27 through 31, with each week starting with
       Monday. The first column is the week number.
    - </caption>
    - <tr><th>Week<th>Monday<th>Tuesday<th>Wednesday<th>Thursday<th>Friday<Saturday<Sunday
    - <tr><td>27<td><td><td><td><td><td><td>
    - <tr><td>28<td><td><td><td><td><td><td><a href="/events/9856">Crown Princess's birthday</a>
    - <tr><td>29<td><td><td><td><td><td><td>
    - <tr><td>30<td><td><td><td><td><td><td>
    - <tr><td>31<td><td><td><td><td><td><td>
    -</table>
    + </caption> + <tr><th>Week<th>Monday<th>Tuesday<th>Wednesday<th>Thursday<th>Friday<th>Saturday<th>Sunday + <tr><td>27<td><td><td><td><td><td><td> + <tr><td>28<td><td><td><td><td><td><td><a href="/events/9856">Crown Princess's birthday</a> + <tr><td>29<td><td><td><td><td><td><td> + <tr><td>30<td><td><td><td><td><td><td> + <tr><td>31<td><td><td><td><td><td><td> +</table>

    The HTML figure element can get its accessible name from its figcaption element, but it will not be used as the accessible description, even if it was not used as the accessible name. If the figcaption element is appropriate as an accessible description, and the accessible name is set using aria-labelledby or aria-label, then the figcaption can be explicitly set as the accessible description using the aria-describedby attribute.

    <h2 id="neutron">Neutron</h2>
     <figure aria-labelledby="neutron" aria-describedby="neutron-caption">
    @@ -5889,9 +5854,9 @@ 
    -
    5.4.1.4 Descriptions Derived from Titles
    -

    If the aria-details attribute is not used, and an accessible description was not provided using the aria-describedby attribute or one of the primary host-language-specific attributes or elements (e.g., the caption element for table), then, for HTML, if the element has a title attribute, that is used as the accessible description.

    -

    A visible description together with aria-describedby or aria-details is generally recommended. If a description that is not visible is desired, then the title attribute can be used, for any HTML element that can have an accessible description.

    +
    5.4.1.3 Descriptions Derived from Titles
    +

    If an accessible description was not provided using the aria-describedby attribute or one of the primary host-language-specific attributes or elements (e.g., the caption element for table), then, for HTML, if the element has a title attribute, that is used as the accessible description.

    +

    A visible description together with aria-describedby is generally recommended. If a description that is not visible is desired, then the title attribute can be used, for any HTML element that can have an accessible description.

    Note that the title attribute might not be accessible to some users, in particular sighted users not using a screen reader and not using a pointing device that supports hover (e.g., a mouse).

    For example, an input element with input constrained using the pattern attribute can use the title attribute to describe what the expected input is.

    <label> Part number:
    @@ -5904,7 +5869,7 @@ 
    5.4.1.4 title="Follow W3C on Twitter"> <img src="/2008/site/images/Twitter_bird_logo_2012.svg" alt="Twitter" class="social-icon" height="40" /> - </a>
    +</a>
    @@ -5912,14 +5877,13 @@
    5.4.1.4 5.4.2 Accessible description calculation

    Like the accessible name calculation, the accessible description calculation produces a text string. - However, there is one exception: when an element is described using the aria-details attribute the assistive technology is provided with a reference instead of constructing a flattened string.

    The accessible description calculation algorithm is the same as the accessible name calculation algorithm except for a few branch points that depend on whether a name or description is being calculated. In particular, when accumulating text for an accessible description, the algorithm uses aria-describedby instead of aria-labelledby.

    - When aria-details is not used, user agents construct an accessible description string for an element by walking through a list of potential description methods and using the first that generates a description. + User agents construct an accessible description string for an element by walking through a list of potential description methods and using the first that generates a description. The algorithm they follow is defined in the accessible name specification. It is roughly like the following: