'Next.js and React Bootstrap accessibility component to change font and font size

I am trying to create a component for accessibility and have the option to change settings such as dark mode, higher contrast, font size, etc.

I've made a component as a bootstrap modal where settings for accessibility can be changed. Dark mode and higher contrast are implemented and working.

Bootstrap accessibility modal

The problem I am running into is changing font sizes. I want to be able to dynamically set the font size to a smaller or larger size. My idea was to add a class to the <body> tag since there are some articles out there showing how to set classes in the body tag dynamically in _document.js.

I cannot figure out a good way to implement this and might be looking at it the complete wrong way.

For a better view of what's done so far I have setup a CodeSandbox with the complete code: https://codesandbox.io/s/nextjs-react-bootstrap-accessibility-exndhh

As of now the accessibility component looks like this:

import { useState, useEffect } from "react";

import { useTheme } from "next-themes";

import { Button, Modal, Form } from "react-bootstrap";

import { UniversalAccess } from "./Icons";

const Accessibility = () => {
  // A11Y - Accessibility modal
  const [show, setShow] = useState(false);
  const handleClose = () => setShow(false);
  const handleShow = () => setShow(true);

  // A11Y - Accessibility themes
  const [mounted, setMounted] = useState(false);
  const { theme, setTheme, resolvedTheme } = useTheme();
  // When mounted on client, now we can show the UI
  useEffect(() => setMounted(true), []);
  if (!mounted) return null;
  return (
    <>
      <Button
        variant="warning"
        className="px-4 py-2 ms-lg-3 d-flex align-items-center"
        onClick={handleShow}
      >
        <UniversalAccess className="me-2" />
        Accessibility
      </Button>

      <Modal show={show} onHide={handleClose} centered>
        <Modal.Header closeButton>
          <Modal.Title>Accessibility settings</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <p className="h5">Colors</p>
          <Form.Check
            type="switch"
            id="dark-mode"
            label="Dark mode"
            onChange={() => setTheme(theme === "dark" ? "light" : "dark")}
            checked={resolvedTheme === "dark"}
          />
          <Form.Check
            type="switch"
            id="contrast-mode"
            label="Higher contrast"
            onChange={() =>
              setTheme(theme === "contrast" ? "light" : "contrast")
            }
            checked={resolvedTheme === "contrast"}
          />

          <div className="my-4">
            <p className="h5">Font size/type</p>
            <div className="d-flex mb-1 align-items-end">
              <div className="d-flex flex-column align-items-center">
                <Button
                  className="rounded-pill d-flex align-items-center justify-content-center"
                  variant="outline-primary"
                  style={{ width: "30px", height: "30px" }}
                >
                  <span style={{ fontSize: "0.7rem" }}>A</span>
                </Button>
                <p>Small</p>
              </div>
              <div className="d-flex flex-column align-items-center">
                <Button
                  className="mx-3 rounded-pill d-flex align-items-center justify-content-center"
                  variant="outline-primary"
                  style={{ width: "40px", height: "40px" }}
                  // Should be active because default font size
                  active
                >
                  <span style={{ fontSize: "1rem" }}>A</span>
                </Button>
                <p>Default</p>
              </div>
              <div className="d-flex flex-column align-items-center">
                <Button
                  className="rounded-pill d-flex align-items-center justify-content-center"
                  variant="outline-primary"
                  style={{ width: "50px", height: "50px" }}
                >
                  <span style={{ fontSize: "1.5rem" }}>A</span>
                </Button>
                <p>Large</p>
              </div>
            </div>
            <Form.Check
              type="switch"
              id="dyslexia-mode"
              label="Dyslexia font"
            />
          </div>
        </Modal.Body>
        <Modal.Footer>
          <Button variant="secondary" onClick={handleClose}>
            Close
          </Button>
        </Modal.Footer>
      </Modal>
    </>
  );
};

export default Accessibility;

Eventually I'd like to have the option to changes fonts dynamically as well any suggestions here are more tan welcome!

The goal here is not to become fully WCAG compliant but at least have some of it.

Perhaps there's one of those handy dandy react libraries out there and have not found it yet.



Solution 1:[1]

Typically, we (my company) leave that up to the user agent to determine the font and font size. For example, in Firefox, under "Fonts" you can tell the browser to override a webpage's font and font size; also, under "Zoom" you change the page zoom. In Chrome, under "Appearance," you can do the same.

This appears to be the recommended approach by WCAG2.1 and by me (and you'll see why below).

The scaling of content is primarily a user agent responsibility. User agents that satisfy UAAG 1.0 Checkpoint 4.1 allow users to configure text scale. The author's responsibility is to create Web content that does not prevent the user agent from scaling the content effectively.

When designing for accessibility, the best advice I can give would be to determine what WCAG2.1 considers sufficient for resizing text.

That said, the problems you're going to run into because of nextjs and react-bootstrap:

  • Persistence -- using context inside a modal gets super hacky (you'd need to have the context always in the DOM, which means the modal would need to reside under div#__next, which isn't??? currently possible with react-bootstrap because it appends modals to the body and outside of div#__next container) and local storage is client-side only, so you'll get a massive CLS when the page is SSR'd.
  • Support -- according to WCAG, you need to support up to a 200% font size. Therefore, you'll probably want to have some sort of stepping: 100%, 125%, 150%, 175%, 200%.
  • Layout -- by supporting variable font sizing, you'll also need to support variable layouts. Otherwise, the text can take up more space than visually expected: breaking containers, breaking form inputs, elongating buttons vertically, and wrapping some sort of text that makes it harder to read. This is expected when changing the font in a user-agent, but since you're supporting font resizing, you'll definitely want to also support different layouts (as mentioned in the sufficient link above).
  • Screen readers -- currently your font size buttons aren't communicating their intent; they're being read as "Push button" without any context. You'll need to use semantic markup or aria attributes for it to be picked up a screen reader. If you want to dive into it, I know for sure that Mac OS and most popular Linux distros have built in screen readers in their accessibility system settings -- no idea about Windows.

Is it possible to accomplish the above? Yes, but maybe not (as easy/at all) with the 3rd party libraries you're using.

Before solving the above, I'd ask myself: At what cost (to you, a company or a client) is this going to take to solve these problems, and more importantly, what value does this feature bring that can't be accomplished natively?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1