After an intense period of development, the first phase of our new website has gone live. We are really proud of what we have achieved and would like to share some of the choices and challenges we faced along the road and how we overcame them.
As you may have noticed, our new website is built on modern JavaScript technologies, most notably React and Gatsby. Why did we choose these? What other tools did we find? And what were the challenges in using them?
Keep reading to find out!
Choosing the tools
When talking about accessibility, React is not often the first choice that comes out of conversations. Just search for the combination of WordPress and React, for example. So why would we, as an accessibility company, use it?
First and foremost we have in-house experience in building accessible sites with React and we believe that the modern web should also be built accessibly. We have learnt that with React, as with many modern tools, accessibility issues normally don’t arise because of the tool itself.
A few of the other reasons include:
- It is the most downloaded JavaScript tool for building websites, which means we encounter React a lot in the wild. Working with React gives us more hands-on experience to be able to assist clients better.
- All these downloads mean a lot of React developers. Transferring accessibility knowledge to this community means a large impact on accessibility as a whole.
- React boosts productivity significantly which means that we can build and deliver faster.
- We believe that accessible results can be achieved in a wide range of technologies.
Gatsby
Gatsby emerged as the go-to solution for creating static websites with React at the time we were busy designing our new website. Once again, despite our own initial skepticism about the accessibility potential of such a new tool, we were pleasantly surprised by the ease with which we could achieve accessible results. In fact the features you get out of the box just serves to increase the accessibility of these solutions.
For example, a Gatsby website is more “static” in nature, leading to faster load times and better support for search engines. Also, we were impressed by the offline availability, code splitting, lazy loading and an optimization regime that made views load at breakneck speed.
Gatsby also allowed us to re-use the React components we built, thereby freeing us up to think about what we really wanted to; design, development and accessibility.
DatoCMS
Easy management of site content is crucial in creating a maintainable website. Connecting our Gatsby website to the headless CMS DatoCMSgave us a great way of managing the content of our website while making full use of Gatsby’s brilliant integration with such systems.
Data from the CMS is extracted at build time with GraphQL and either injected straight into the pre-rendered HTML or stored as static JSON payloads for fast transmission to the browser. This meant almost instant contentful page loads, without the normal AJAX delays.
It also meant that we had to worry less about security as the actual data fetch is done only once at build time.
MDX
For a number of pages on our website, such as the documentation, we wanted to continue using Markdown.
We quickly found our way to MDX, a React aware Markdown processor. The ability to use React components directly in Markdown meant that we could easily re-use many of the components we already built while still experiencing the great editing benefits.
Fixing semantics with MDX
In MDX it is very easy to override the actual rendered DOM elements. This made it a breeze to introduce pre-built components and also fix semantics to ensure an accessible result.
By making use of a provider component that ships with the library we could override any of the MDX tags with out own implementations when we needed to:
//Set up the custom components
this.state = {
wrapper: 'section',
strong: 'b',
inlineCode: ({ children }) => (
<span className="text-highlight">{children}</span?
),
code: ({ children, className }) => (
<ExampleBlock
codeString={children}
>
)
}
//Then use this in the render later:
<MDXProvider components={this.state}>{children}</MDXProvider>
In the example above we introduce proper <section>
elements, change bold elements to use the <b>
tag instead and use our own custom built accessible code display block component to show code examples.
Our blog makes use of the same technology which means that you have just experienced our custom built code block right here on this very page!
The challenges from using this stack
Internet Explorer 11
Supporting IE11 is non negotiable for us as it is still being used prolifically and is a browser of choice for JAWS users.
However, as more and more tooling moves onto next generation JavaScript, IE11 needs more constant care to ensure that it can still execute code written according to the newest JavaScript specifications.
Aside from making sure the correct polyfills were loaded, one of our challenges came in the form of the ES6 Object Literal Property Value Shorthand
where an object can be defined without repetition of variable names:
const name = "Joe";
const surname = "Soap";
//ES5
const personObject = {
name: name,
surname: surname
}
//ES6
const personObject = {
name,
surname
}
This commonly used syntax in ES6 breaks when running in IE11. During the transpilation with Babelthis has to be converted back to the ES5 syntax for IE11 to understand it.
We ran into this when we noticed that important parts of our website was no longer working in IE11. After quite an extended debug session we realized that this very compatibility issue was causing our problem. Luckily this is a setting in the Babel and we assisted in finding and fixing this setting inside the Gatsby configuration and thereby enabled Gatsby sites to work in IE11 again.
No screen reader responses when navigating
A common problem found in Single Page Applications, such as those built with React, is that navigation within the application is completely silent to screen reader users. Normally, navigation would mean that the browser would fetch and load a brand new HTML file and this would be announced by the screen reader.
Applications built completely from JavaScript can no longer reload the browser every time a new view is injected as this would also restart the application itself. Instead plugins called routers are used to inject the correct view based on the current browser URL, while preventing the standard browser reload. The result: silent navigation.
This is not just super annoying for screen reader users, but also for keyboard users as focus remains on the navigation element, leaving the user to TAB through the remaining navigation items before reaching the newly loaded content.
However, there is a standard fix for this. Once the new view is loaded, set focus to the container containing this view or, even better, set focus to the main heading element!
While React usually dislikes imperative code, you can use React refs to set focus:
//Declare a ref
this.headingRef = React.createRef();
//Attach to the element in JSX
<h2 ref={this.headingRef}>Focus me on navigation</h2>;
//Set focus when appropriate
this.headingRef.current.focus();
The good news is that this is starting to appear inside router software as default functionality which is great news for the future!
The React focus bubble
JSX mirrors the DOM in terms of which elements are rendered and how events are handled in almost every case. The only exception is that focus and blur events bubble in React while they do not do that in the DOM. This is actually done for very good reason as it makes things like an accessible outside click possible.
But, in an accessible application it means that you can have flashes of the focus outline in some cases. For example:
<section tabIndex="0" role="tabpanel">
<h3>User info</h3>
<label htmlFor="testInput">Name</label>
<input id="testInput />
</section>
This React code will cause a very fast flash of focus on the container element when clicking the label element before the browser sets focus to the input. This happens due to the focus event bubbling to the parent container and being handled there before the browser resolves focus on the actual target.
To fix this we built a reusable component:
const FocusCatcher = ({ children }) => (
<div
tabIndex="-1"
onFocus={e => {
e.stopPropagation();
}}
style={{ outline: 'none' }}
>
{children}
</div>
);
Wrapping event generators in this component prevents the focus from reaching focusable parent elements accidentally:
<section tabIndex="0" role="tabpanel">
<FocusCatcher>
<h3>User info</h3>
<label htmlFor="testInput">Name</label>
<input id="testInput" />
</FocusCatcher>
</section>
The UI components challenge
JavaScript web applications filled the niche for developers wanting to expose richer, more desktop-like functionality to users. This means using interactiveelements, such as disclosures, tabbed interface, spinner elements and more.
If not built to be accessible these interactive building blocks will ensure an accessibility failure. If anything, the lack of readily available accessible UI component libraries out there should be seen as a large contributor to the inaccessible state of the modern web today.
When we embarked on this project we knew that we would more than likely be forced to create our own UI controls. Controls that take into account the WAI-ARIA principles and that would pass our own strict audits.
Out of this Tenon-UIwas born. An accessible UI component library that we believed should exist as a freely available tool to React developers out there dealing with the same challenges we did.
We used this library to build our own website and would very much like you to try it out yourself and let us know what you think!
The future
Our agile mindset means that our journey is far from complete. We have rebuilt only part of our website and are already working on bringing the rest of it into the future.
We will keep expanding Tenon UI and keep innovating in the area of accessibility in this technical stack.
Meanwhile the great tools we chose are not standing still and with every release they get more powerful. And this means we are as excited as you to see where Tenon is heading next!