diff --git a/website/sidebars-tutorials.js b/website/sidebars-tutorials.js index 771615297e..3c75264aae 100644 --- a/website/sidebars-tutorials.js +++ b/website/sidebars-tutorials.js @@ -12,6 +12,12 @@ const sidebars = { dirName: 'customization', }, ] }, + { Accessibility: [ + { + type: 'autogenerated', + dirName: 'a11y', + }, + ] }, ], }, { diff --git a/website/src/css/custom.css b/website/src/css/custom.css index a163c39d6b..faf194b8bf 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -275,3 +275,7 @@ button.navbar__toggle { width: 100%; border-radius: var(--ifm-code-border-radius); } + +.standalone-iframe.h400 { + height: 400px; +} diff --git a/website/src/img/a11y.svg b/website/src/img/a11y.svg new file mode 100644 index 0000000000..a2103fbbe5 --- /dev/null +++ b/website/src/img/a11y.svg @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/website/tutorials/a11y/_category_.yml b/website/tutorials/a11y/_category_.yml new file mode 100644 index 0000000000..3bb94873e1 --- /dev/null +++ b/website/tutorials/a11y/_category_.yml @@ -0,0 +1 @@ +label: "Accessibility" diff --git a/website/tutorials/a11y/assets/a11y-chart.html b/website/tutorials/a11y/assets/a11y-chart.html new file mode 100644 index 0000000000..6090860e24 --- /dev/null +++ b/website/tutorials/a11y/assets/a11y-chart.html @@ -0,0 +1,520 @@ + + + + + + + Lightweight Charts™ A11y Tutorial + + + + + +
+
+ +
+ + +
+
+ + +
+
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/website/tutorials/a11y/conclusion.mdx b/website/tutorials/a11y/conclusion.mdx new file mode 100644 index 0000000000..d910e884f2 --- /dev/null +++ b/website/tutorials/a11y/conclusion.mdx @@ -0,0 +1,76 @@ +--- +sidebar_position: 4 +title: Conclusion +pagination_title: Conclusion +sidebar_label: Conclusion +description: Conclusion to the accessibility tutorial +keywords: + - a11y + - accessibility + - screenreaders + - keyboard + - assistive +pagination_prev: a11y/readability +pagination_next: null +--- + +# Conclusion + +This tutorial demonstrated a wide array of accessibility improvements that can +be introduced to line charts, making them usable for a broader spectrum of +users. The considerations and methods presented illustrate the importance of +creating inclusive web applications. + +Among the discussed topics were: + +1. **Keyboard navigation:** The implementation of +keyboard-only navigation on our chart, allowing for interaction exclusively +through keystrokes. +2. **ARIA roles and descriptive content:** The additions of ARIA +labels, live regions, and the hiding of non-consequential elements to enhance +the usefulness of assistive technologies and to communicate context better to +all users. +3. **High contrast and scalable font size:** The detection of the user's +preference for higher contrast and the inclusion of customizable text sizes for +better readability. + +The example provided has even more improvements not directly discussed during the +the tutorial. One such enhancement is an informative tooltip that lists available +commands, adding another helpful layer of user instruction. + +:::tip + +The complete example code provides further insights, and reviewing it is +recommended for a comprehensive understanding of the accessibility improvements +discussed in this tutorial. + +::: + +This tutorial aimed to foster a better understanding of these practices, +promoting an inclusive web where applications are made with everyone in mind. +Thank you for following along and continue putting accessibility at the +forefront of your web development endeavors. + +## Complete example + +You can view the example in a new window, or download the source file below: + + diff --git a/website/tutorials/a11y/intro.mdx b/website/tutorials/a11y/intro.mdx new file mode 100644 index 0000000000..ef08a655d5 --- /dev/null +++ b/website/tutorials/a11y/intro.mdx @@ -0,0 +1,132 @@ +--- +sidebar_position: 0 +sidebar_label: Introduction +pagination_title: Introduction +title: Improving accessibility +description: + This tutorial provides an introduction to making a Lightweight Charts™' chart + more accessible. +keywords: + - a11y + - accessibility + - screenreaders + - keyboard + - assistive +pagination_prev: null +pagination_next: a11y/keyboard +--- + +# Improving accessibility + +## Introduction + +This tutorial introduces how to make charts with Lightweight Charts™ more +accessible. Lightweight Charts™ does not have built-in accessibility attributes +and behaviors. This gives you the flexibility to customize and implement them on +your own, seamlessly integrating the charts into your site's existing +accessibility policy. + +:::info + +The tutorial serves as a starting point and provides ideas +for creating a fully accessible chart based on your users' needs. **It is not +intended to be a comprehensive tutorial.** + +::: + +Graphical data representation, although visually appealing and informative, can +sometimes pose challenges to individuals with varying abilities and needs. In +line with the principles of inclusivity and universal design, we aim to +demonstrate how to make your charts more accessible to a broader audience. + +## What we will be building + +Before we get started, let us have a look at what we will be building in this +tutorial. + + + + + + +## Topics to be covered + +The following topics will be covered within the tutorial: + +- **Enabling Keyboard Navigation:** Keyboard users predominantly interact with + web content using only their keyboard. We will guide you on setting up + keyboard navigation for our charts, thereby providing a seamless user + experience. +- **Implementing ARIA (Accessible Rich Internet Applications) Suite:** We'll + delve into how to integrate ARIA attributes which aid in improving the access + and understanding of our charts for users with disabilities. +- **Generating Descriptive Content for Charts:** Often, providing a textual + description for charts becomes invaluable for certain users, especially for + those using screen reading technology. We demonstrate how to automate this + process and facilitate a more comprehensive understanding of data being + represented. + +## Prerequisite knowledge + +To fully benefit from this guide, we assume that you are already familiar with: + +- Basic HTML structure and elements. +- JavaScript fundamentals, especially event handling. +- The basics of using the Lightweight Charts™ JavaScript library, including chart + creation and providing data. + +:::tip + +The tutorial will assume that you've already read the [Getting Started](/docs) +section. Additionally it is recommended that you read the +[Customization tutorial](/tutorials/customization/intro) + +::: + +## Terminology + +- **Accessibility:** The practice of making your websites usable by as many + people as possible, including individuals with disabilities or special needs. +- **ARIA (Accessible Rich Internet Applications):** A set of attributes that + define ways to make web content and web applications more accessible to people + with disabilities. +- **Assistive technologies:** Software or devices that people with disabilities + use to improve interaction with the web, such as screen readers, alternative + keyboards, or speech recognition software. +- **Keyboard Navigation:** The ability to navigate through a website using only + the keyboard, which is important for those who cannot use a mouse. +- **High Contrast Mode:** A version of a webpage that has been designed to be + easy on the eyes and readable, typically with black text on a white + background, used by people with visual impairments. +- **Media Queries:** A CSS technique used to apply different style rules to + different devices based on their characteristics, such as color scheme + preference (such as high contrast), display type, height, width, etc. +- **Screen Reader:** A type of software that interprets and reads aloud the + information displayed on a screen, such as text, images, buttons, and menus. + It's primarily used by people with visual impairments or those who have + difficulties reading text on a screen. Examples include JAWS, NVDA, and + VoiceOver. diff --git a/website/tutorials/a11y/keyboard.mdx b/website/tutorials/a11y/keyboard.mdx new file mode 100644 index 0000000000..57b19e77c5 --- /dev/null +++ b/website/tutorials/a11y/keyboard.mdx @@ -0,0 +1,160 @@ +--- +sidebar_position: 1 +sidebar_label: Keyboard navigation +pagination_title: Keyboard navigation +title: Keyboard navigation +description: In this section we will add keyboard navigation to the chart. +keywords: + - a11y + - accessibility + - screenreaders + - keyboard + - assistive +pagination_prev: a11y/intro +pagination_next: a11y/screenreader +--- + +# Keyboard navigation + +## Purpose of keyboard navigation + +One cornerstone of web accessibility is ensuring that sites and applications are +fully operable via keyboard alone. This navigation method benefits a wide range +of users, especially those who are unable to use a mouse or have visual +impairments. + +Screen readers and other assistive technologies rely heavily on keyboard +interaction, and some users choose this method due to an acquired skill set or +personal preference. By including keyboard navigation in our charts, we make the +tool more accessible and user-friendly, living up to the principles of inclusive +design. + +## Implementing keyboard actions with Lightweight Charts™ + +The Lightweight Charts™ API provides numerous methods that enable handling chart +actions programmatically, making it possible to tie these actions to keyboard +events. + +Here's a walk-through of how to add keyboard navigation to the chart. + +### Setting focus on the chart + +Firstly, we must ensure the chart is programmatically focusable for keyboard +interaction. We can achieve this by placing a `tabindex` attribute to the +chart's container div: + +```html +
+``` + +This can also be achieved via JavaScript: + +```js +const containerEl = chart.chartElement().parentElement; +containerEl.tabIndex = 0; +``` + +### Adding event listener for keyboard actions + +Following that, we will tie specific chart interactions to keypress events using +JavaScript's `addEventListener` method. This will allow us to control the chart +using specific keystrokes: + +```js +const chartContainer = document.getElementById('chart'); +chartContainer.addEventListener('keydown', event => { + switch (event.key) { + case 'ArrowLeft': + // Action for ArrowLeft key + break; + case 'ArrowRight': + // Action for ArrowRight key + break; + // ... more cases + } +}); +``` + +The `addEventListener` function lets us listen to `keydown` events that occur +when the user presses a key. + +### Utilizing Lightweight Chart's API for actions + +Next, for each case, we use Lightweight Chart's API for desired actions. + +Let's assume we want the left arrow key to scroll the chart to the left, and the +right arrow key to scroll it to the right: + +```js +function shiftChart(diff) { + const currentPos = chart.timeScale().scrollPosition(); + chart.timeScale().scrollToPosition(currentPos + diff, false); +} + +chartContainer.addEventListener('keydown', event => { + switch (event.key) { + case 'ArrowLeft': + shiftChart(-10); + break; + case 'ArrowRight': + shiftChart(10); + break; + } +}); +``` + +In the above JavaScript code, the `timeScale().scrollToPosition()` method from +Lightweight Charts™ API is used inside the event listener to scroll the chart +whenever the left or right arrow key is pressed. + +Additionally, we can assign the up and down arrow keys to adjust the zoom level +of the chart: + +```js +function scaleChart(pct, zoomIn) { + const currentRange = chart.timeScale().getVisibleLogicalRange(); + if (currentRange) { + const bars = currentRange.to - currentRange.from; + const direction = zoomIn ? -1 : 1; + const newRangeBars = bars * pct * direction + bars; + chart.timeScale().setVisibleLogicalRange({ + to: currentRange.to, + from: currentRange.to - newRangeBars, + }); + } +} + +chartContainer.addEventListener('keydown', event => { + switch (event.key) { + // ... + case 'ArrowUp': + scaleChart(1 / 8, true); + break; + case 'ArrowDown': + scaleChart(1 / 8, false); + break; + } +}); +``` + +We are scaling the chart by adjusting the `visibleLogicalRange` instead of +changing the `barSpacing` option on the time scale. In this example, we are +keeping the right data point fixed when zooming in or out by retaining the `to` +value and only modifying the `from` value. + +--- + +This keyboard navigation inclusion makes the chart's underlying data more +accessible to a wider audience, ensuring a diverse user base can fully interact +with the chart's functions. + +:::tip + +Note that more keyboard actions can be added according to +project-specific requirements, thereby further enhancing the navigation controls +and accessibility. + +::: + +In the next section, we'll continue enhancing our accessible charts by +integrating ARIA (Accessible Rich Internet Applications) roles and properties and generating descriptive content. diff --git a/website/tutorials/a11y/readability.mdx b/website/tutorials/a11y/readability.mdx new file mode 100644 index 0000000000..1302c4bbe5 --- /dev/null +++ b/website/tutorials/a11y/readability.mdx @@ -0,0 +1,141 @@ +--- +sidebar_position: 3 +sidebar_label: Readability +pagination_title: Readability +title: Readability +description: Improving the readability of the chart for visually impaired users. +keywords: + - a11y + - accessibility + - screenreaders + - keyboard + - assistive +pagination_prev: a11y/screenreader +pagination_next: a11y/conclusion +--- + +## High contrast and scalable font size + +Ensuring accessibility in web development means accommodating various user +preferences and physical abilities. High contrast mode and scalable font size +are two such distinct features that have been included in our chart, catering to +diverse user needs. + +### High contrast mode + +Certain users with specific visual impairments may struggle to distinguish +between colors or interpret text and image details in low contrast. For these +users, a high-contrast mode can be immensely helpful. + +In our chart application, we leverage the built-in browser feature +`window.matchMedia()` to ascertain if the user has indicated a preference for +higher contrast in their system settings. + +Here's how we determine if the user prefers a high-contrast mode: + +```js +// Check if user prefers high contrast mode +function checkHighContrast() { + // Use window.matchMedia to check 'prefers-contrast' media feature + const highContrast = window.matchMedia('(prefers-contrast: high)').matches; + return highContrast; // Returns true if high contrast is enabled, false otherwise +} + +// Subscribe to changes +const highContrastMediaQuery = window.matchMedia('(prefers-contrast: high)'); +highContrastMediaQuery.addListener(() => { + setHighContrast(highContrastMediaQuery.matches); +}); +``` + +A `setHighContrast` function could be implemented as follows: + +```js +const seriesBaseContrastSettings = { + color: 'rgb(41, 98, 255)', + lineWidth: 2, +}; +const chartBaseContrastSettings = { + layout: { + textColor: '#191919', + }, + grid: { + vertLines: { + color: '#D6DCDE', + }, + horzLines: { + color: '#D6DCDE', + }, + }, +}; +const seriesHighContrastSettings = { + color: 'rgb(0, 0, 0)', + lineWidth: 4, +}; +const chartHighContrastSettings = { + layout: { + textColor: '#000000', + }, + grid: { + vertLines: { + color: '#777777', + }, + horzLines: { + color: '#777777', + }, + }, +}; + +function setHighContrast(enabled) { + mainSeries.applyOptions( + enabled ? seriesHighContrastSettings : seriesBaseContrastSettings + ); + chart.applyOptions( + enabled ? chartHighContrastSettings : chartBaseContrastSettings + ); +} +``` + +Our `setHighContrast(highContrast)` function updates the chart's colors based on +the user's preference. If the user prefers high contrast, a higher contrast +color scheme is applied. If not, the colors switch back to the default, low-contrast scheme. + +### Scalable font size + +Another key aspect of web accessibility is the option to scale font sizes. +Users with visual impairments may benefit from larger text sizes, improving +readability. + +We provide an option for these users to adjust the text size through a checkbox, +which changes text size in the chart: + +```html + + +``` + +Then, we create an event listener in JavaScript for this checkbox input, which increases +the text size: + +```js +function setFontSize(large) { + chart.applyOptions({ + layout: { + fontSize: large ? 16 : 12, + }, + }); +} + +document + .querySelector('#large-font-checkbox') + .addEventListener('change', event => { + setFontSize(event.target.checked); + }); +``` + +In this example, when the 'Increase Font Size' input is checked, the font size +in the chart increases to 16px. + +Including these additional accessibility features adheres to the principles of +creating an inclusive web, where design embraces all user types, abilities, and +preferences. Note that you should conduct rigorous accessibility testing before deployment. diff --git a/website/tutorials/a11y/screenreader.mdx b/website/tutorials/a11y/screenreader.mdx new file mode 100644 index 0000000000..696fb3acb7 --- /dev/null +++ b/website/tutorials/a11y/screenreader.mdx @@ -0,0 +1,259 @@ +--- +sidebar_position: 2 +sidebar_label: Screen Readers +pagination_title: Screen Readers +title: Screen Readers +description: In this section we will add screen reader support to the chart. +keywords: + - a11y + - accessibility + - screenreaders + - keyboard + - assistive +pagination_prev: a11y/keyboard +pagination_next: a11y/readability +--- + +# Integrating ARIA roles and descriptive content for enhanced accessibility + +Accessibility in web development extends beyond just accommodating keyboard-only +users. Many users with varying abilities make use of assistive technologies like +screen readers to effectively interact with web applications. +[ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) +(Accessible Rich Internet Applications) roles and states offer a powerful +toolkit to improve this interaction by conveying information about the behavior +and purpose of interface components. + +In the context of our line chart, we'll be looking at how to implement ARIA +attributes to further enhance its accessibility in addition to generating +descriptive content for our graph. + +:::info + +In this tutorial we will only make use of +the `aria-live`, `aria-label`, and `aria-hidden` attributes. You can explore +a [more comprehensive list of attributes on this MDN page] +(https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes). + +::: + +## Use of ARIA attributes in the chart + +### aria-live + +To provide real-time updates about the chart to assistive technologies, we use +the `aria-live` attribute. It accepts a few potential values, although +`"polite"` and `"assertive"` are the most common. + +With `aria-live="polite"`, updates are presented at the user's next convenient +opportunity, such as when they stop typing or when a task is completed. With +`aria-live="assertive"`, updates are presented immediately. + +Our chart uses the `"assertive"` value because the description should be read +out based on a user action (keyboard action): + +```html +
+``` + +### aria-label + +The `aria-label` attribute is used to specify a string that labels the current +element. It's useful when there isn't any text content that describes this +element. + +On our chart, it could read something like this: + +```html +
+``` + +### aria-hidden + +The `aria-hidden` attribute is used to hide irrelevant or redundant +information from assistive technologies. Its value is either "true" or "false". + +For example, if our chart contained a decorative element with no semantic +meaning, we could use `aria-hidden="true"` to hide it from screen readers: + +```html + +``` + +### Adding the ARIA attributes to an existing chart via JavaScript + +```html + + + + +``` + +## Generating a description of the chart + +In addition to ARIA roles, providing a textual description for our charts helps +all users, especially those relying on screen readers or other assistive +technologies. + +You may generate a description of a chart by applying domain-specific knowledge. +It's beneficial to outline the general trend or patterns of the data, highlight +any notable points or anomalies, and summarize the implications of the data. + +You can add a descriptive section to the chart: + +```html +
+ +
+``` + +You can use JavaScript to change the content inside this div whenever the chart +data is updated: + +```js +const descriptionElement = document.getElementById('chart-description'); +descriptionElement.textContent = generateDescription(mainSeries.data()); +``` + +Here, `generateDescription(data)` would be a function that you write to +translate the chart's data into human-readable insights. The function would vary +greatly based on what the charts represent and how much detail you wish to +provide. + +The example describes the chart based on its first and last visible data +points in addition to the highest and lowest points displayed. This is used to +generate a description like this: + +> The first price is $679.10 at Wed Sep 19 2018. The last price is $555.37 at +> Wed May 15 2019. The actual change in price was -$123.73, corresponding to a +> percentage change of -18.22%. The lowest price was $32.76 at Fri Dec 21 2018. +> The highest price was $951.33 at Sun Mar 24 2019. + +The following code could be used as a starting point for generating chart +descriptions: + +```js +function formatDate(time) { + return new Date(time * 1000).toDateString(); +} + +function formatValue(value) { + return `${value < 0 ? '-' : ''}$${Math.abs(value).toFixed(2)}`; +} + +function getStats(data) { + const stats = { + start: data[0], + close: data[data.length - 1], + low: data[0], + high: data[0], + }; + + for (const point of data) { + if (point.value > stats.high.value) { + stats.high = point; + } + if (point.value < stats.low.value) { + stats.low = point; + } + } + + return stats; +} + +function getVisibleSeriesData(chart, series) { + const timeScale = chart.timeScale(); + const visibleRange = timeScale.getVisibleLogicalRange(); + const data = []; + for (let i = Math.round(visibleRange.from); i <= visibleRange.to; i++) { + const d = series.dataByIndex(i, 0); + if (d !== null) { + data.push(d); + } + } + return data; +} + +function describeFinanceChart(data) { + if (!data || data.length === 0) { + return 'The data set is empty.'; + } + + const stats = getStats(data); + + const firstPrice = `The first price is ${formatValue( + stats.start.value + )} at ${formatDate(stats.start.time)}.`; + const lastPrice = `The last price is ${formatValue( + stats.close.value + )} at ${formatDate(stats.close.time)}.`; + + const actualChange = stats.close.value - stats.start.value; + const percentChange = (actualChange / stats.start.value) * 100; + + const changeDescription = `The actual change in price was ${formatValue( + actualChange + )}, corresponding to a percentage change of ${percentChange.toFixed(2)}%.`; + + let lowHigh = ''; + if ( + stats.low.time !== stats.start.time && + stats.low.time !== stats.close.time + ) { + lowHigh += `The lowest price was ${formatValue( + stats.low.value + )} at ${formatDate(stats.low.time)}.`; + } + if ( + stats.high.time !== stats.start.time && + stats.high.time !== stats.close.time + ) { + lowHigh += ` The highest price was ${formatValue( + stats.high.value + )} at ${formatDate(stats.high.time)}.`; + } + + return `${firstPrice} ${lastPrice} ${changeDescription} ${lowHigh}`.trim(); +} +``` + +## Semantic HTML + +Using +[Semantic HTML](https://developer.mozilla.org/en-US/docs/Glossary/Semantics#semantics_in_html) +elements offers several benefits. Firstly, they enhance the accessibility of web +content as they provide specific meaning to the browser and assistive technology +like screen readers, helping them understand the content's structure and +purpose. This is crucial for users with disabilities. + +It is suggested that the container provided to the `createChart` method should +use a semantic element such as `
` instead of a generic element like +`div`. + +```html +
+``` + +In the next part, we'll discuss contrast control and font scaling. diff --git a/website/tutorials/index.mdx b/website/tutorials/index.mdx index 7705d3f25f..270b7ced96 100644 --- a/website/tutorials/index.mdx +++ b/website/tutorials/index.mdx @@ -8,6 +8,7 @@ import Shapes from "@site/src/img/shapes.svg"; import ReactLogo from "@site/src/img/react.svg"; import VuejsLogo from "@site/src/img/vuejs.svg"; import WebComponentsLogo from "@site/src/img/webcomponents.svg"; +import A11y from "@site/src/img/a11y.svg"; # Tutorials @@ -32,6 +33,12 @@ import VersionWarningAdmonition from "@site/src/components/VersionWarningAdmonit image: , description: "Customizing appearance & features", }, + { + href: "/tutorials/a11y/intro", + title: "Accessibility", + image: , + description: "How to improve A11y support", + }, ]} />