> 웹 프론트엔드 > CSS 튜토리얼 > 위젯에 접근성 의미 체계 구축 - 웹 접근성 이니셔티브

위젯에 접근성 의미 체계 구축 - 웹 접근성 이니셔티브

Susan Sarandon
풀어 주다: 2024-12-30 14:01:12
원래의
473명이 탐색했습니다.

Build Accessibility Semantics into Widgets - Web Accessibility Initiative

W3C 접근성 실무 그룹은 개발자가 웹 패턴과 위젯에 접근성 의미 체계를 구축하는 것을 지원하기 위해 웹 접근성 이니셔티브를 시작했습니다. 접근 가능한 경험을 만들 때 위젯이 강조됩니다. 머티리얼 디자인, Fluent UI 및 휴먼 인터페이스 UI와 같은 디자인 시스템은 브라우저 플랫폼에서 웹 구성 요소로 구축된 위젯 기반 디자인 시스템이며 네이티브 플랫폼으로 변환될 때 웹 접근성 지침을 밀접하게 따릅니다. 이러한 의미 체계를 사용하면 웹사이트의 접근성뿐 아니라 개발 속도도 향상됩니다.

NPM에서 패키지를 다운로드할 정도로 Image Carousel 또는 Alert Dialogue를 구축하고 문제를 겪은 적이 있습니까? 더 이상은 아닙니다. 웹 접근성 이니셔티브가 여러분을 도와드립니다. CodePen으로 볼 수 있는 샘플 위젯과 패턴이 있습니다. 샘플을 프로젝트에 다운로드하고 수정하면 완료됩니다. Image Carousel을 구축하는 방법을 알게 되며 NPM에서 패키지를 다운로드하지 않고도 액세스할 수 있습니다

예제 이미지 캐러셀:

  • HTML
<section>



<ul>
<li>CSS
</li>
</ul>

<pre class="brush:php;toolbar:false">/* .carousel */

img.reload {
  padding: 0.25em;
  display: block-inline;
  position: relative;
  top: 6px;
  height: 0.9em;
}

.carousel {
  background-color: #eee;
  max-width: 900px;
}

.carousel .carousel-inner {
  position: relative;
}

.carousel .carousel-items {
  padding: 5px;
}

.carousel .carousel-items.focus {
  padding: 2px;
  border: solid 3px #005a9c;
}

.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-item .carousel-image a img {
  height: 100%;
  width: 100%;
}

.carousel .carousel-item .carousel-caption a {
  cursor: pointer;
  text-decoration: underline;
  color: #fff;
  font-weight: 600;
}

.carousel .carousel-item .carousel-caption a,
.carousel .carousel-item .carousel-caption span.contrast {
  display: inline-block;
  margin: 0;
  padding: 6px;
  background-color: rgb(0 0 0 / 65%);
  border-radius: 5px;
  border: 0 solid transparent;
}

.carousel-moreaccessible .carousel-items .carousel-image a {
  display: block;
  margin: 0;
  padding: 5px;
  text-decoration: none;
  border: none;
}

.carousel-moreaccessible .carousel-item .carousel-caption a {
  display: inline-block;
  margin: 0;
  padding: 6px;
  color: black;
  background-color: transparent;
  border: none;
  border-radius: 5px;
}

.carousel-moreaccessible .carousel-item .carousel-caption span.contrast,
.carousel-moreaccessible .carousel-item .carousel-caption span.contrast:hover {
  background-color: transparent;
}

.carousel .carousel-item .carousel-caption a:hover,
.carousel .carousel-item .carousel-caption span.contrast:hover {
  background-color: rgb(0 0 0 / 100%);
}

.carousel .carousel-item .carousel-caption a:focus {
  padding: 4px;
  border: 2px solid #fff;
  background-color: rgb(0 0 0 / 100%);
  outline: none;
  border-width: 2px solid #fff;
  color: #fff;
}

.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, Previous and Next Buttons */

.carousel .controls {
  box-sizing: border-box;
  position: absolute;
  top: 1em;
  z-index: 10;
  display: flex;
  width: 100%;
  padding: 0.25em 1.25em 0;
}

.carousel .controls button {
  position: absolute;
  z-index: 10;
  flex: 0 0 auto;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  outline: none;
}

.carousel .controls button.previous {
  right: 70px;
}

.carousel .controls button.next {
  right: 18px;
}

/* SVG Controls */

.carousel .controls svg .background {
  stroke: black;
  fill: black;
  stroke-width: 1px;
  opacity: 0.6;
}

.carousel .controls svg .border {
  fill: transparent;
  stroke: transparent;
  stroke-width: 2px;
}

.carousel .controls svg .pause {
  stroke-width: 4;
  fill: transparent;
  stroke: transparent;
}

.carousel .controls svg .play {
  stroke-width: 1;
  fill: transparent;
  stroke: transparent;
}

.carousel .controls .pause svg .pause {
  fill: white;
  stroke: white;
}

.carousel .controls .play svg .play {
  fill: white;
  stroke: white;
}

.carousel .controls svg polygon {
  fill: white;
  stroke: white;
}

.carousel .controls button:focus svg .background,
.carousel .controls button:hover svg .background,
.carousel .controls button:hover svg .border {
  fill: #005a9c;
  stroke: #005a9c;
  opacity: 1;
}

.carousel .controls button:focus svg .border {
  stroke: white;
}

/* More accessible carousel styles, with caption and controls above/below image */

.carousel-moreaccessible {
  padding: 0;
  margin: 0;
  position: relative;
  border: #eee solid 4px;
  border-radius: 5px;
}

/* Shared CSS for Pause and Tab Controls */

.carousel-moreaccessible .controls {
  position: relative;
  top: 0;
  left: 0;
  padding: 0.25em 0.25em 0;
}

.carousel.carousel-moreaccessible .controls {
  position: static;
  height: 36px;
}

.carousel.carousel-moreaccessible .controls button.previous {
  right: 60px;
}

.carousel.carousel-moreaccessible .controls button.next {
  right: 6px;
}

.carousel-moreaccessible .carousel-items,
.carousel-moreaccessible .carousel-items.focus {
  padding: 0;
  border: none;
}

.carousel-moreaccessible .carousel-items.focus .carousel-image a {
  padding: 2px;
  border: 3px solid #005a9c;
}

/* More accessible caption styling */

.carousel-moreaccessible .carousel-item {
  padding: 0;
  margin: 0;
  max-height: none;
}

.carousel-moreaccessible .carousel-item .carousel-caption {
  position: static;
  padding: 0;
  margin: 0;
  height: 60px;
  color: black;
}

.carousel-moreaccessible .carousel-item .carousel-caption p {
  padding: 0;
  margin: 0;
}

.carousel-moreaccessible .carousel-item .carousel-caption h3 {
  font-size: 1.1em;
  padding: 0;
  margin: 0;
}

.carousel-moreaccessible .carousel-item .carousel-caption a:hover {
  background-color: rgb(0 0 0 / 20%);
}

.carousel-moreaccessible .carousel-item .carousel-caption a:focus {
  padding: 4px;
  border: 2px solid #005a9c;
  background-color: transparent;
  color: black;
  outline: none;
}
로그인 후 복사
  • JS
/*
 *   File:   carousel-prev-next.js
 *
 *   Desc:   Carousel widget with Previous and Next Buttons that implements ARIA Authoring Practices
 *
 */

'use strict';

var CarouselPreviousNext = function (node, options) {
  // merge passed options with defaults
  options = Object.assign(
    { moreaccessible: false, paused: false, norotate: false },
    options || {}
  );

  // a prefers-reduced-motion user setting must always override autoplay
  var hasReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
  if (hasReducedMotion.matches) {
    options.paused = true;
  }

  /* DOM properties */
  this.domNode = node;

  this.carouselItemNodes = node.querySelectorAll('.carousel-item');

  this.containerNode = node.querySelector('.carousel-items');
  this.liveRegionNode = node.querySelector('.carousel-items');
  this.pausePlayButtonNode = null;
  this.previousButtonNode = null;
  this.nextButtonNode = null;

  this.playLabel = 'Start automatic slide show';
  this.pauseLabel = 'Stop automatic slide show';

  /* State properties */
  this.hasUserActivatedPlay = false; // set when the user activates the play/pause button
  this.isAutoRotationDisabled = options.norotate; // This property for disabling auto rotation
  this.isPlayingEnabled = !options.paused; // This property is also set in updatePlaying method
  this.timeInterval = 5000; // length of slide rotation in ms
  this.currentIndex = 0; // index of current slide
  this.slideTimeout = null; // save reference to setTimeout

  // Pause Button

  var elem = document.querySelector('.carousel .controls button.rotation');
  if (elem) {
    this.pausePlayButtonNode = elem;
    this.pausePlayButtonNode.addEventListener(
      'click',
      this.handlePausePlayButtonClick.bind(this)
    );
  }

  // Previous Button

  elem = document.querySelector('.carousel .controls button.previous');
  if (elem) {
    this.previousButtonNode = elem;
    this.previousButtonNode.addEventListener(
      'click',
      this.handlePreviousButtonClick.bind(this)
    );
    this.previousButtonNode.addEventListener(
      'focus',
      this.handleFocusIn.bind(this)
    );
    this.previousButtonNode.addEventListener(
      'blur',
      this.handleFocusOut.bind(this)
    );
  }

  // Next Button

  elem = document.querySelector('.carousel .controls button.next');
  if (elem) {
    this.nextButtonNode = elem;
    this.nextButtonNode.addEventListener(
      'click',
      this.handleNextButtonClick.bind(this)
    );
    this.nextButtonNode.addEventListener(
      'focus',
      this.handleFocusIn.bind(this)
    );
    this.nextButtonNode.addEventListener(
      'blur',
      this.handleFocusOut.bind(this)
    );
  }

  // Carousel item events

  for (var i = 0; i < this.carouselItemNodes.length; i++) {
    var carouselItemNode = this.carouselItemNodes[i];

    // support stopping rotation when any element receives focus in the tabpanel
    carouselItemNode.addEventListener('focusin', this.handleFocusIn.bind(this));
    carouselItemNode.addEventListener(
      'focusout',
      this.handleFocusOut.bind(this)
    );

    var imageLinkNode = carouselItemNode.querySelector('.carousel-image a');

    if (imageLinkNode) {
      imageLinkNode.addEventListener(
        'focus',
        this.handleImageLinkFocus.bind(this)
      );
      imageLinkNode.addEventListener(
        'blur',
        this.handleImageLinkBlur.bind(this)
      );
    }
  }

  // Handle hover events
  this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this));
  this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this));

  // initialize behavior based on options

  this.enableOrDisableAutoRotation(options.norotate);
  this.updatePlaying(!options.paused && !options.norotate);
  this.setAccessibleStyling(options.moreaccessible);
  this.rotateSlides();
};

/* Public function to disable/enable rotation and if false, hide pause/play button*/
CarouselPreviousNext.prototype.enableOrDisableAutoRotation = function (
  disable
) {
  this.isAutoRotationDisabled = disable;
  this.pausePlayButtonNode.hidden = disable;
};

/* Public function to update controls/caption styling */
CarouselPreviousNext.prototype.setAccessibleStyling = function (accessible) {
  if (accessible) {
    this.domNode.classList.add('carousel-moreaccessible');
  } else {
    this.domNode.classList.remove('carousel-moreaccessible');
  }
};

CarouselPreviousNext.prototype.showCarouselItem = function (index) {
  this.currentIndex = index;

  for (var i = 0; i < this.carouselItemNodes.length; i++) {
    var carouselItemNode = this.carouselItemNodes[i];
    if (index === i) {
      carouselItemNode.classList.add('active');
    } else {
      carouselItemNode.classList.remove('active');
    }
  }
};

CarouselPreviousNext.prototype.previousCarouselItem = function () {
  var nextIndex = this.currentIndex - 1;
  if (nextIndex < 0) {
    nextIndex = this.carouselItemNodes.length - 1;
  }
  this.showCarouselItem(nextIndex);
};

CarouselPreviousNext.prototype.nextCarouselItem = function () {
  var nextIndex = this.currentIndex + 1;
  if (nextIndex >= this.carouselItemNodes.length) {
    nextIndex = 0;
  }
  this.showCarouselItem(nextIndex);
};

CarouselPreviousNext.prototype.rotateSlides = function () {
  if (!this.isAutoRotationDisabled) {
    if (
      (!this.hasFocus && !this.hasHover && this.isPlayingEnabled) ||
      this.hasUserActivatedPlay
    ) {
      this.nextCarouselItem();
    }
  }

  this.slideTimeout = setTimeout(
    this.rotateSlides.bind(this),
    this.timeInterval
  );
};

CarouselPreviousNext.prototype.updatePlaying = function (play) {
  this.isPlayingEnabled = play;

  if (play) {
    this.pausePlayButtonNode.setAttribute('aria-label', this.pauseLabel);
    this.pausePlayButtonNode.classList.remove('play');
    this.pausePlayButtonNode.classList.add('pause');
    this.liveRegionNode.setAttribute('aria-live', 'off');
  } else {
    this.pausePlayButtonNode.setAttribute('aria-label', this.playLabel);
    this.pausePlayButtonNode.classList.remove('pause');
    this.pausePlayButtonNode.classList.add('play');
    this.liveRegionNode.setAttribute('aria-live', 'polite');
  }
};

/* Event Handlers */

CarouselPreviousNext.prototype.handleImageLinkFocus = function () {
  this.liveRegionNode.classList.add('focus');
};

CarouselPreviousNext.prototype.handleImageLinkBlur = function () {
  this.liveRegionNode.classList.remove('focus');
};

CarouselPreviousNext.prototype.handleMouseOver = function (event) {
  if (!this.pausePlayButtonNode.contains(event.target)) {
    this.hasHover = true;
  }
};

CarouselPreviousNext.prototype.handleMouseOut = function () {
  this.hasHover = false;
};

/* EVENT HANDLERS */

CarouselPreviousNext.prototype.handlePausePlayButtonClick = function () {
  this.hasUserActivatedPlay = !this.isPlayingEnabled;
  this.updatePlaying(!this.isPlayingEnabled);
};

CarouselPreviousNext.prototype.handlePreviousButtonClick = function () {
  this.previousCarouselItem();
};

CarouselPreviousNext.prototype.handleNextButtonClick = function () {
  this.nextCarouselItem();
};

/* Event Handlers for carousel items*/

CarouselPreviousNext.prototype.handleFocusIn = function () {
  this.liveRegionNode.setAttribute('aria-live', 'polite');
  this.hasFocus = true;
};

CarouselPreviousNext.prototype.handleFocusOut = function () {
  if (this.isPlayingEnabled) {
    this.liveRegionNode.setAttribute('aria-live', 'off');
  }
  this.hasFocus = false;
};

/* Initialize Carousel and options */

window.addEventListener(
  'load',
  function () {
    var carouselEls = document.querySelectorAll('.carousel');
    var carousels = [];

    // set example behavior based on
    // default setting of the checkboxes and the parameters in the URL
    // update checkboxes based on any corresponding URL parameters
    var checkboxes = document.querySelectorAll(
      '.carousel-options input[type=checkbox]'
    );
    var urlParams = new URLSearchParams(location.search);
    var carouselOptions = {};

    // initialize example features based on
    // default setting of the checkboxes and the parameters in the URL
    // update checkboxes based on any corresponding URL parameters
    checkboxes.forEach(function (checkbox) {
      var checked = checkbox.checked;

      if (urlParams.has(checkbox.value)) {
        var urlParam = urlParams.get(checkbox.value);
        if (typeof urlParam === 'string') {
          checked = urlParam === 'true';
          checkbox.checked = checked;
        }
      }

      carouselOptions[checkbox.value] = checkbox.checked;
    });

    carouselEls.forEach(function (node) {
      carousels.push(new CarouselPreviousNext(node, carouselOptions));
    });

    // add change event to checkboxes
    checkboxes.forEach(function (checkbox) {
      var updateEvent;
      switch (checkbox.value) {
        case 'moreaccessible':
          updateEvent = 'setAccessibleStyling';
          break;
        case 'norotate':
          updateEvent = 'enableOrDisableAutoRotation';
          break;
      }

      // update the carousel behavior and URL when a checkbox state changes
      checkbox.addEventListener('change', function (event) {
        urlParams.set(event.target.value, event.target.checked + '');
        window.history.replaceState(
          null,
          '',
          window.location.pathname + '?' + urlParams
        );

        if (updateEvent) {
          carousels.forEach(function (carousel) {
            carousel[updateEvent](event.target.checked);
          });
        }
      });
    });
  },
  false
);
로그인 후 복사

문서 링크는 다음과 같습니다.

https://www.w3.org/WAI/ARIA/apg/patterns/

https://www.w3.org/WAI/ARIA/apg/practices/

다음은 Mozilla 개발자 네트워크 문서입니다:

https://developer.mozilla.org/en-US/docs/Web/API

브라우저 플랫폼으로 구축하세요. 웹 표준으로 구축하세요. 접근 가능한 경험을 구축하세요. 웹 구성요소로 구축하세요. 프레임워크 없이 구축하세요.

위 내용은 위젯에 접근성 의미 체계 구축 - 웹 접근성 이니셔티브의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿