נגישות

למה נגישות?

נגישות ברשת (ידועה גם כ a11y) היא יצירה ועיצוב אתרים שמתאימים לשימוש ע״י כולם. תמיכה בנגישות נדרשת ע״י טכנולוגית מסייעת כדי לפרש דפי אינטרנט.

React מספקת תמיכה מלאה בבניית אתרים נגישים, בדרך כלל ע״י שימוש בטכניקות HTML סטנדרטיות.

סטנדרטים וקווים מנחים

WCAG

ניתן למצוא קוים מנחים ליצירת אתרים נגישים בWeb Content Accessibility Guidelines.

הרשימה הבאה מ WCAG מספקת סקירה כללית:

WAI-ARIA

המסמך מ Web Accessibility Initiative - Accessible Rich Internet Applications מכיל טכניקות לבניית ווידג׳טים נגישים ב JavaScript.

שימו לב ש JSX תומך לחלוטין בכל תכונות ה-HTML aria-*. בשונה מרוב תכונות ה-DOM ב-React שנקראות ב-camelCase, תכונות אלה נקראות ב-hyphen-cased (שמוכר גם בשמות אחרים כמו kebab-case, lisp-case וכו׳), בדיוק כמו ב-HTML רגיל:

<input
  type="text"
  aria-label={labelText}
  aria-required="true"
  onChange={onchangeHandler}
  value={inputValue}
  name="name"
/>

סמנטי HTML

HTML סמנטי (או דקדוקי) הוא הבסיס לנגישות באפליקציות ואתרי אינטרנט. השימוש באלמנטים שונים על מנת לחזק את המשמעות של המידע שמוצג באתרים, בדרך כלל יוביל לנגישות בפני עצמו.

לפעמים הסמנטיקה נשברת כשאנחנו מוסיפים אלמנטים כמו <div> ל-JSX כדי לגרום לReact לעבוד כמו שצריך, בעיקר בזמן שימוש ברשימות וטבלאות (<ol>, <ul>, <dl>, <table> וכו׳) במקרים האלה ניתן להשתמש בפרגמנטים בReact במקום div, כדי לאחד מספר אלמנטים.

לדוגמא,

import React, { Fragment } from 'react';

function ListItem({ item }) {
  return (
    <Fragment>
      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </Fragment>
  );
}

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        <ListItem item={item} key={item.id} />
      ))}
    </dl>
  );
}

ניתן כמובן למפות רשימת פריטים למערך של פרגמנטים באותה צורה שממפים כל אלמנט אחר:

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // גם לפרגמנטים prop `key` בזמן מיפוי רשימות צריך להוסיף את ה
        <Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </Fragment>
      ))}
    </dl>
  );
}

כשאין צורך בהוספת props ניתן להשתמש בסינטקס מקוצר, בהנחה ושאר הכלים תומכים בו:

function ListItem({ item }) {
  return (
    <>
      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </>
  );
}

לעוד מידע, ראו את עמוד תיעוד הפרגמנטים.

טפסים נגישים

תיוג

כל אלמנט או form ב-HTML, (כמו <input> ו <textarea>), צריכים לקבל תיוג נגיש. עלינו לספק תגיות שמתארות אותם בשביל קוראי המסך.

המשאבים הנ״ל מראים לנו איך לספק תגיות מתאימות:

למרות שבדרך כלל אנחנו יכולים להשתמש בסטנדרטים הנהוגים ב-HTML ישירות ב-React, שימו לב שהתכונה for למשל נכתבת ב-JSX כhtmlFor:

<label htmlFor="namedInput">Name:</label>
<input id="namedInput" type="text" name="name"/>

יידוע המשתמש במקרה של שגיאות

שגיאות צריכות להיות מובנות לכל משתמש. הלינק הבא מראה לנו איך להעביר את תוכן השגיאה גם לקורא המסך:

בקרת פוקוס

חשוב לבנות אפליקציות שניתנות לשימוש בעזרת המקלדת בלבד (ללא עזרת העכבר):

פוקוס מקלדת ומסגרת פוקוס

פוקוס המקלדת מתאר את האלמנט ב-DOM שנבחר ומוכן לקבל נתונים מהמקלדת. ניתן לראות את הפוקוס במסגרת או הדגש, כמו בתמונה הבאה:

Blue keyboard focus outline around a selected link.

מומלץ להסיר את ההדגש הנ״ל אך ורק בכדי להחליף אותו בצורת הדגש אחרת (לדוגמא בעזרת הקונפיגורציה outline: 0)

דילוג לתוכן מבוקש

על מנת שהשימוש באתר יהיה מהיר ואופטימלי עם המקלדת, ניתן לספק דרך לדלג בעזרת אזורי ניווט לתוכן מבוקש

לינקים לדילוג ניווט (SkipLinks) הם לינקים נסתרים שמתגלים בזמן אינטרקציה עם האתר באמצעות המקלדת בלבד. הם פשוטים לפיתוח בעצמאות קישורים עוגנים (anchors) וסגנונות עיצוב:

בנוסף, אפשר להשתמש באלמנטים לציון דרך כמו <main> ו <aside>, על מנת לציין אזורים בדף בשביל להרשות למשתמש לנווט אליהם בקלות ובמהירות בעזרת טכנולוגיה מסייעת.

קרא עוד על אלמנטים לציון דרך לשיפור נגישות בלינק הבא:

שליטה מבוקרת בפוקוס

אפליקציות React משנות את ה-DOM בזמן ריצה באופן מתמשך, מה שגורם לפעמים למקלדת לאבד פוקוס או לפוקוס לעבור למקום בלתי צפוי. על מנת למנוע זאת, ביכולתנו להחזיר את פוקוס המקלדת למקום הנכון באופן תוכניתי. לדוגמא, ע״י החזרת הפוקוס לכפתור שפתח טופס מודאלי, אחרי סגירתו.

התיעוד ב-MDN Web Docs מתאר איך לבנות ניווט מקלדת בווידג׳טים ב-JavaScript.

כדי לתת פוקוס ב-React, אנחנו יכולים להשתמש בקישור לאלמנטים ב-DOM.

נתחיל ביצירת קישור לאלמנט ב-JSX של מחלקת קומפוננטה:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // DOM-ב textInput צור קישור לאלמנט
    this.textInput = React.createRef();
  }
  render() {
  // על מנת לאחסן את ההפניה `ref` callback השתמש בפונקצית ה
  // (this.textInput) בשדה מופע textInput לאלמנט
    return (
      <input
        type="text"
        ref={this.textInput}
      />
    );
  }
}

לאחר מכן נוכל לשנות את הפוקוס למקום אחר בקומפוננטה לפי הצורך:

focus() {
  // DOM-של ה API-שנה את הפוקוס של שדה הטקסט באופן מכוון בעזרת ה
  // DOM-כדי לקבל גישה לצומת ה "current" שימו לב שאנחנו משתמשים ב
  this.textInput.current.focus();
}

לפעמים אלמנט אב צריך לשנות פוקוס לקומפוננטת ילד. ניתן לעשות זאת ע״י חשיפות הפנית ה-DOM לאלמנט האב בעזרת prop מיוחד בקומפוננטת הילד שמעבירה לאלמנט האב את צומת ה-DOM של הילד.

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.inputElement = React.createRef();
  }
  render() {
    return (
      <CustomTextInput inputRef={this.inputElement} />
    );
  }
}

// עכשיו תוכלו להעביר פוקוס לפי הצורך
this.inputElement.current.focus();

בזמן השימוש ב HOC להרחבת קומפוננטות, מומלץ להעביר את ההפניה לקומפוננטה הנעטפת בעזרת הפונקציה forwardRef של React. במקרה וקומפוננטת HOC שלא בשליטתכם (צד שלישי) לא מממשת את ההעברת ההפניה, השיטה לעיל יכולה בכל זאת לעזור.

דוגמא טובה לשליטה בפוקוס אפשר למצוא בreact-aria-modal. זאת דוגמא יחסית נדירה של חלון מודאלי נגיש לגמרי. לא רק שהוא שם פוקוס התחלתי בכפתור הביטול (כדי למנוע ממשתמש המקלדת להתחיל את פעולת האישור בטעות) ושומר את פוקוס המקלדת בתוך החלון, הוא גם מחזיר את הפוקוס לאלמנט שפתח את החלון מלכתחילה לאחר סגירתו.

הערה:

למרות שזאת תכונת נגישות חשובה מאוד, חשוב להשתמש בשיפוט בזמן השימוש בטכניקה. המטרה צריכה להיות החזרת הפוקוס למקלדת, ולא לנסות לחזות את דרישות המשתמש ואופן השימוש שלו/ה באתר.

אירועי עכבר וסמן

יש לדאוג שכל פונקציונאליות שזמינה דרך שימוש בעכבר, נגישה באותה מידה בשימוש במקלדת בלבד. תלות בסמן (דרך עכבר או משטח מגע) מובילה להרבה מקרים לא נגישים למשתמשי מקלדת, שכותצאה מכך לא יוכלו להשתמש באפליקציה.

כדוגמא, נציג מקרה שכיח ביותר של נגישות שבורה כתוצאה מאירועי לחיצה - כשהמשתמש יכול לסגור חלון צץ באמצעות לחיצה מחוץ לחלון.

A toggle button opening a popover list implemented with the click outside pattern and operated with a mouse showing that the close action works.

בדרך כלל התנהגות זו מיושמת ע״י קישור אירוע הלחיצה click לעצם החלון window שסוגר את החלון הצץ:

class OuterClickExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = { isOpen: false };
    this.toggleContainer = React.createRef();

    this.onClickHandler = this.onClickHandler.bind(this);
    this.onClickOutsideHandler = this.onClickOutsideHandler.bind(this);
  }

  componentDidMount() {
    window.addEventListener('click', this.onClickOutsideHandler);
  }

  componentWillUnmount() {
    window.removeEventListener('click', this.onClickOutsideHandler);
  }

  onClickHandler() {
    this.setState(currentState => ({
      isOpen: !currentState.isOpen
    }));
  }

  onClickOutsideHandler(event) {
    if (this.state.isOpen && !this.toggleContainer.current.contains(event.target)) {
      this.setState({ isOpen: false });
    }
  }

  render() {
    return (
      <div ref={this.toggleContainer}>
        <button onClick={this.onClickHandler}>Select an option</button>
        {this.state.isOpen && (
          <ul>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
    );
  }
}

למרות שהתנהגות זו לא מהווה בעיה למשתמשים שיכולים להעזר בסמן, היא מובילה להתנהלות שבורה למשתמשים שתלוים במקלדת כדי לנווט בין אלמנטים, כי עצם החלון לא מקבל את אירוע הלחיצה. ובסופו של דבר גם מונעת ממשתמשים גישה לפונקציונאליות באתר.

A toggle button opening a popover list implemented with the click outside pattern and operated with the keyboard showing the popover not being closed on blur and it obscuring other screen elements.

<<<<<<< HEAD אפשר במקום זאת להגיע להתנהגות זהה בעזרת מטפלי אירועים כמו onBluronFocus: ======= The same functionality can be achieved by using appropriate event handlers instead, such as onBlur and onFocus:

c93286c071bca17ecba91cd130908fe2e33a4766

class BlurExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = { isOpen: false };
    this.timeOutId = null;

    this.onClickHandler = this.onClickHandler.bind(this);
    this.onBlurHandler = this.onBlurHandler.bind(this);
    this.onFocusHandler = this.onFocusHandler.bind(this);
  }

  onClickHandler() {
    this.setState(currentState => ({
      isOpen: !currentState.isOpen
    }));
  }


  // setTimeout סוגרים את החלון הצץ בטיק הבא בעזרת.
  // זה חשוב כי אנחנו צריכים קודם כל לבדוק אם ילד אחר של האלמנט
  // קורה לפני אירוע הפוקוס blur-קיבל פוקוס, כיוון שהאירוע של ה
  onBlurHandler() {
    this.timeOutId = setTimeout(() => {
      this.setState({
        isOpen: false
      });
    });
  }

  // כדי לא לסגור Timeout מסירים את ה
  // את החלון הצץ כשילד אחר מקבל פוקוס
  onFocusHandler() {
    clearTimeout(this.timeOutId);
  }

  render() {
    // לאב focus-וה blur-עוזר לנו בהעלאת אירועי ה React
    return (
      <div onBlur={this.onBlurHandler}
           onFocus={this.onFocusHandler}>
        <button onClick={this.onClickHandler}
                aria-haspopup="true"
                aria-expanded={this.state.isOpen}>
          Select an option
        </button>
        {this.state.isOpen && (
          <ul>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
    );
  }
}

הקוד הזה חושף פונקציונאליות למשתמשי המקלדת בלי להפקיר את הסמן. בנוסף, שימו לב לתוספת הaria-* props שתומכים במשתמשים שנעזרים בקוראי מסך. על מנת להשאיר את הדוגמא פשוטה, לא כללנו את אירועי המקלדת שמאפשרים אינטרקציית arrow key עם החלון הצץ.

A popover list correctly closing for both mouse and keyboard users.

זאת דוגמא שבה מספר מקרים שתלויים רק בסמן מונעים ממשתמשים שתלויים במקלדת נגישות לפונקציונאליות. תמיד בדקו את האתר עם המקלדת כדי למצוא את האזורים הבעיתיים ולתקן אותם בעזרת מטפלי אירועים שזמינים למקלדת.

ווידג׳טים מסובכים יותר

חוויית משתמש מסובכת יותר לא צריכה להיות פחות נגישה. הדרך הפשוטה ביותר לפתח אתר נגיש היא לפתח קוד קרוב ככל האפשר ל HTML, אבל גם ווידג׳טים מסובכים יותר אפשר לפתח בצורה נגישה.

כאן נדרשת הבנה של ARIA Roles וARIA States and Properties.

אלה מספקות ארגז כלים מלא בתכונות HTML עם תמיכה מלאה ב-JSX’ שעוזרים לנו לבנות קומפוננטות React פונקציונאליות.

כל סוג ווידג׳ט ממומש בצורה ותבנית עיצוב שונה, אבל המשתמש וסוכני המשתמש מצפים ממנו להתנהגות מסוימת:

עוד נקודות למחשבה

קביעת שפה

ציין את השפה שבה כתובים הטקסטים בדף על מנת לאפשר לקוראי המסך לבחור את הגדרות הקול בהתאים:

קביעת כותרת הדף

דאג לציין את כותרת הדף בעזרת האלמנט <title> על מנת לתאר את תוכן ומטרת הדף המוצג באופן מדויק למשתמש:

אנחנו יכולים לציין זאת בReact בעזרת קומפוננטת כותרת הדף

ניגוד צבעים

ודאו שלכל הטקסטים באתר יש ניגוד צבעים מספק, על מנת להקל על משתמשים עם יכולות ראייה פחותות לקרוא אותם:

חישוב הניגוד לכל קומבינצית צבעים באתר יכול לקחת הרבה זמן. דרך יותר פשוטה היא לחשב באופן אוטומטי פלטת צבעים מלאה לאתר בעזרת Colorable.

הכלים המצוין להלן (aXe ו WAVE) מספקים בדיקות אוטומטיות ומדווחים על שגיאות בניגודי צבעים:

כלים לפיתוח ובדק

יש מספר כלים שיכולים לעזור ביצירת אפליקציות נגישות.

המקלדת

הבדיקה הכי פשוטה וגם אחת מהחשובות, היא שהאתר מתפקד בצורה מוחלטת בשימוש במקלדת בלבד. אפשר לעשות זאת בצורה הזאת:

  1. נתק/י את העכבר.
  2. בעזרת שימוש במקש ה Tab ו- Shift+Tab כדי לנווט.
  3. בעזרת שימוש במקש ה Enter כדי להפעיל אלמנטים.
  4. שימוש בחצי המקלדת כדי לתפעל אלמנטים כמו תפריטים ורשימות לפי הצורך.

עזרה בפיתוח

אפשר לבדוק מספק תכונות נגישות באופן ישיר בקוד ה-JSX שלנו. לדוגמא בדיקות intellisense כמו ARIA roles, states and properties, מסופקות ישירות ב-IDE’s שמכירים בJSX. בנוסף ניתן להשתמש בכלי להלן:

eslint-plugin-jsx-a11y

הכלי eslint-plugin-jsx-a11y הוא תוסף ל-ESLint שמספק יכולות AST linting מיוחדות לבדיקת נגישות בקוד. IDE’s רבים גם מספקים אינטגרציה ישירה עם כלים כאלה ישירות לחלון שבו נכתב הקוד לאחר כל שמירה.

צור אפליקצית React כולל את התוסף הזה עם חלק מהחוקים מופעלים בצורה מובנית. כדי להפעיל חוקים אחרים, ניתן לצור קובץ .eslintrc בתקייה הראשית של הפרויקט עם התוכן:

{
  "extends": ["react-app", "plugin:jsx-a11y/recommended"],
  "plugins": ["jsx-a11y"]
}

בדיקת נגישות בדפדפן

קיימים מספר כלים שבודקים נגישות על עמודי אינטרנט בדפדפן. כדאי להשתמש בהם בתוספת לכלים אחרים שצוינו קודם, כיוון שהם בודקים אך ורק את הנגישות הטכנית של קוד ה- HTML.

aXe-core, aXe ו- react-axe

Deque Systems מציעה את aXe-core לבדיקות אוטומטיות של האפליקציה. הכלי עובד גם עם Selenium.

מנוע הנגישות שנקרא גם aXe, הוא תוסף לדפדפן שבודק נגישות ומרחיב את aXe-core.

ניתן להשתמש גם ב react-axe כדי ליצור דוחות על ממצאי נגישות ישירות ל console בזמן פיתוח ודיבאג

WebAIM WAVE

כלי בידוק הנגישות ברשת הוא תוסף דפדפן נוסף לבדיקת נגישות.

בודק הנגישות ועץ הנגישות

עץ הנגישות הוא חלק מהDOM שמכיל עצמים נגישים כל אלמנט שחשוף לקוראי המסך או טכנולוגיות מסייעות אחרות.

ֿֿבדפדפני מסוימים ניתן לראות מידע נגישות לכל אלמנט בעץ הנגישות:

<<<<<<< HEAD

קוראי מסך

חשוב להשתמש בקורא מסך כחלק מבדיקות הנגישות באתר.

שימו לב שהשילוב בין סוג הדפדפן וקורא המסוך חשוב יחסית, ומומלץ לבדוק את האפליקציה בדפדפן המתאים ביותר לקורא המסך שבחרתם.

קוראי מסך מוכרים ונפוצים

NVDA ב Firefox

NonVisual Desktop Access או בקיצור NVDA הוא קורא מסך פתוח לWindows שרבים משתמשים בו.

הקישורים הבאים מסבירים על שימוש נכון בNVDA:

VoiceOver ב Safari

VoiceOver הוא קורא מסך מובנה במכשירים של Apple.

הקישורים הבאים מסבירים איך להפעיל ולהשתמש בVoiceOver:

JAWS ב Internet Explorer

Job Access With Speech או בקיצור JAWS, הוא קורא מסך נפוץ ל Windows.

הקישורים הבאים מסבירים איך להשתמש ב JAWS:

קוראי מסך אחרים

ChromeVox ב Google Chrome

ChromeVox הוא קורא מסך מובנה ב Chromebooks שאפשר להוריד כ תוסף ל Google Chrome.

הקישורים הבאים מסבירים איך להשתמש ב ChromeVox: