HTML
Overview
Tui components contain HTML that is structured semantically, using the HTML5 standard and provides containing wrappers enough to allow for a wide range of theming needs. Much of our HTML now, or in the future, needs to serve more than one purpose on a given web page once it is rendered. Because of this, we manipulate HTML by embedding logic within the markup, to control application of CSS selectors, ARIA attributes, conditional component trees and more.
Implementation
We follow the principle of "material honesty": we use most appropriate element for the job, and only use a div
or span
if no other element fits. If it's for navigation (it goes somewhere), we use a link. If it's for an action (it does something), we use a button. We use radio
s and checkbox
es for multiple choice options. We use HTML5 input types (email, number, password, search, tel, url). Grids use div
s by default (since they are mostly used as containers for presentation), but can be set to use other more meaningful tags (such as aside
or ul
and li
) if appropriate.
We add ARIA roles where necessary: this is mostly just for custom components that don't have a native HTML element.
Examples
Here's a link being used as navigation.
<a href="/server/totara/competency/rate_competencies.php?user_id=1">Rate competencies</a>
Here's a button being used for action, with type="button"
so that it doesn't submit the form it's inside of.
<button type="button">Show filters</button>
For form controls, our framework renders a label
with a for
attribute, programmatically linking it to the relevant input
. For required fields, we show an asterisk, but hide it for screen reader users, replacing it with meaningful text (classes and other attributes omitted for clarity).
<label for="uid-1">Title <span title="Required"><span aria-hidden="true">*</span> <span class="sr-only">Required</span></span> </label> <input id="uid-1" name="title" type="text">
For groups of controls, we use role="radiogroup"
to group the controls and aria-labelledby
to programmatically link the group to its visible name appearing earlier in the DOM.
<label id="uid-1-label">Favourite colour</label> <div role="radiogroup" aria-labelledby="uid-1-label"> <input id="uid-1" type="radio" name="favouritecolor" value="red"> <label for="uid-1">Red</label> <input id="uid-1" type="radio" name="favouritecolor" value="green"> <label for="uid-1">Green</label> <input id="uid-1" type="radio" name="favouritecolor" value="blue"> <label for="uid-1">Blue</label> </div>
Tips and known limitations
- One limitation of Vue 2 is that components must have a single root element in their template. This element can change (e.g. with a
v-if
) but there must only be one at once - this is enforced both by the linter and runtime warnings. - If you need to have more than one root element, a workaround is sometimes possible - if your component doesn't have any state, you can define it as a functional component, which can have multiple root elements.
- Avoid writing invalid HTML as this will cause issues with server-side rendering - for example, if you put a
<div>
inside of a<p>
, the<div>
will be moved outside the<p>
by the browser, meaning it will no longer match and cause will cause hydration errors on page load.
Recommended reading
- The Mozilla Developer Network has an excellent, well ordered and well explained guide: HTML elements reference.
- For just the newer elements HTML5 Doctor's Element Index is a short and sweet guide.
- MarkSheet has a short, friendly, summary: HTML is about meaning.
- The W3C's Notes on ARIA Use in HTML give some good tips on writing semantic HTML.