TS-18: Web GUIs
This technical standard covers the design and implementation of web-based graphical user interfaces (GUIs) for applications.
For the design of URLs, which form part of the user interface of any web application, see TS-63: URL Design.
For usage of web platform APIs, see TS-37: Web Platform APIs.
Performance optimization
Web GUIs SHOULD be designed to be as fast and responsive as possible.
For the fastest possible web GUIs, the following optimizations are RECOMMENDED:
- Server-render as much HTML as possible, preferably all of it. The browser’s native HTML engine will always be able to render HTML faster than any custom client-side JavaScript can.
- Pre-fetch as much HTML as possible. For example, when the user hovers over a link, the browser can pre-fetch the HTML for the linked page. Further performance optimization can be achieved by client-side JavaScript pre-fetching partial HTML for the linked page, and then dynamically inserting it into application’s shell. Global areas such as the navigation and footer do not usually require re-rendering when the user navigates to a new page.
- Also use
<link rel="preload">in the HTML<head>section to suggest the browser preload assets such as CSS, JavaScript, and web fonts. This will reduce the number of blocking requests the browser has to make before it can do an initial render of server-side HTML. - Use
<link rel="dns-prefetch">to suggest the browser pre-fetch DNS records for third-party domains that are used by the application. This can reduce the time it takes to resolve domain names to servers. Use this for your CDN, any third-party services called from the client-side code, and for any other assets or resources that are not served from the same domain as the web page document. - Use a CDN to store and serve static assets.
- Use a proxy tool such as Squid to cache dynamic content that is pre-rendered by the server.
- Use client-side HTTP caching aggressively. For example, use the
Cache-Controlheader to specify how long a resource should be cached by the browser. - On the client-side, use a service worker to cache pre-rendered HTML and other dynamically-fetched assets. The service worker intercepts requests and serves up a cached version of the resource, if it has one. This is also helpful to provide offline support.
- Inline CSS in a
<style>tag in the HTML<head>section, uglified. This SHOULD be restricted to just your critical CSS, which is the minimum CSS required to do an initial render of a page. Additional CSS, required only for an optional enhanced user experience, should be deferred to after the page is rendered. If the overall size of the CSS is small, it can all be inlined. This means the browser can start to render the page as soon as HTML is received, without waiting for separate CSS resources to be downloaded. This technique will give you the fastest possible time to first paint – a metric known as the (Largest Contentful Paint (LCP), which is the time is takes the browser to fully render the largest element on the page (ie. after all styles, fonts, images, and other dependencies are fetched). - Similarly, for JavaScript, try to serve only the subset of code that is required to enable dynamic functionality on the current page. This is known as code splitting and tools are available to do this automatically (at compile time or dynamically on the server-side). Avoid loading all your JavaScript on all your pages.
- Consider lazy loading additional JavaScript that enhances the user experience but is not required to enable the core functionality.
- Put fixed
widthandheightattributes on images, or use an inlinestyleattribute to set thewidthandheightCSS properties of the images' containers. This allows the browser to allocate space for an image before it is downloaded, which prevents the page from jumping around as images are loaded – which counts as another re-render. - Don’t be afraid to use age-old techniques such as image sprites to reduce the number of requests required to fetch things like product thumbnails, icons, and other small images.
Web accessibility
Web GUIs MUST be designed to be accessible to all users, including those with visual, auditory, motor, and cognitive disabilities. All web GUIs SHOULD aim to conform with the Web Content Accessibility Guidelines (WCAG), the international standard for web content accessibility. The most recent version is WCAG 2.2. Conformance is measured at three levels: Level A (minimum), Level AA (standard), and Level AAA (enhanced). Level AA is the target RECOMMENDED by this standard, and is required by law in many jurisdictions.
WCAG 2.2 defines success criteria organized under four principles.
1. Perceivable
All content MUST be presentable to users in ways they can perceive.
Text alternatives:
- All images and other non-text content — including icons, charts, audio, and controls — MUST have a descriptive text alternative that conveys their meaning.
- Purely decorative images SHOULD use an empty
alt=""attribute, and optionallyrole="presentation", so assistive technologies can skip them.
Time-based media:
- Pre-recorded videos with audio MUST have synchronized captions that cover all speech and relevant sound effects.
- Pre-recorded audio-only content MUST have a text transcript.
- Pre-recorded video-only content MUST have an audio description or text alternative.
- Live video with audio MUST include real-time captions.
Adaptable:
- Visual information and relationships — such as headings, labels, and groupings
— MUST be communicated in the code using semantic HTML elements (eg.
<label>,<ul>,<h1>) or ARIA attributes, so that assistive technologies can understand the page structure. - Content MUST appear in a logical reading order in the source, regardless of how it is visually presented.
- Instructions MUST NOT rely solely on sensory properties such as color, shape, size, or position to convey meaning.
- Content MUST remain readable and usable in both portrait and landscape orientations.
- Common form fields (such as name, email, and address) SHOULD use the
autocompleteattribute to enable browser autofill.
Distinguishable:
- Color MUST NOT be the only means of conveying information. Always pair color with a supplementary cue such as a text label, icon, underline, or pattern.
- Audio that plays automatically for more than 3 seconds MUST be pausable or stoppable without relying on system-wide volume controls.
- Normal-sized text MUST have a contrast ratio of at least 4.5:1 against its background. Large text (over 24px regular, or over 19px bold) requires a ratio of at least 3:1.
- Text MUST remain readable when zoomed to 200%.
- Text SHOULD be real text, not images of text (except for logotypes and other essential visual treatments).
- Content MUST reflow to a single column at a viewport width of 320px without requiring horizontal scrolling.
- Interactive controls and meaningful graphics MUST have a contrast ratio of at least 3:1 against adjacent colors.
- Layout MUST NOT break when custom text spacing is applied (increased line height, letter spacing, and word spacing).
- Tooltip-style content that appears on hover or keyboard focus MUST be dismissible (eg. via the Escape key), hoverable, and persistent until the user dismisses it.
2. Operable
All UI components and navigation MUST be operable by all users.
Keyboard accessible:
- All functionality MUST be operable using a keyboard alone, unless the task inherently requires freehand input (eg. drawing).
- Focus MUST never become trapped in a UI component. It MUST always be possible to move focus in and out using standard keyboard controls.
- Single-character keyboard shortcuts, if used, MUST be remappable to include a modifier key or disableable entirely.
Enough time:
- Time limits SHOULD be avoided unless essential to the task.
- Where a time limit is used, users MUST be able to turn it off, adjust it to at least 10x the default, or extend it on request.
- Moving, scrolling, blinking, or auto-updating content that persists for more than 5 seconds MUST be pausable, stoppable, or hideable.
Seizures and physical reactions:
- Content MUST NOT flash or flicker more than three times per second, unless the flash falls within safe size and luminance thresholds.
- Animations triggered by user interaction SHOULD be suppressible via the
prefers-reduced-motionCSS media query or a site-level toggle.
Navigable: A skip-navigation mechanism MUST be provided so keyboard users can bypass repeated header and navigation blocks and jump directly to the main content.
- Every page MUST have a unique and descriptive
<title>. - Keyboard focus order MUST follow a logical and meaningful sequence that matches the reading order of the page.
- The purpose of each link MUST be clear from the link text alone, or in combination with its surrounding context.
- At least two methods MUST be available to locate pages or content within the site (eg. a navigation menu and a site search).
- Headings and form labels MUST be descriptive.
- A visible focus indicator MUST always be shown when navigating via keyboard.
- Focused elements MUST NOT be fully obscured by sticky headers, banners, or other overlapping content.
Input modalities:
- Functionality that relies on multi-point or path-based gestures (such as swiping or pinching) MUST also have an alternative that works with a single pointer (such as a tap or click).
- Actions MUST trigger on pointer release (mouse-up or finger lift), not on press, so that accidental activations can be cancelled by moving the pointer away before releasing.
- The visible label text of a button, link, or form field MUST also be present in its accessible (programmatic) name in the code, so voice control users can activate it by speaking the visible label.
- Functionality triggered by device motion (such as shaking or tilting) MUST also be achievable without motion, and motion-based input MUST be disableable. Touch and click targets MUST be at least 24x24px.
3. Understandable
Content and UI behavior MUST be understandable by all users.
Readable:
- Every page MUST identify its primary language using the
langattribute on the<html>element. - Passages of content in a different language MUST be marked with the correct
langattribute on the containing element.
Predictable:
- No unexpected context change MUST occur when an element receives focus (eg. auto-opening a popup or navigating away).
- Changing the value of a form field MUST NOT trigger unexpected context changes such as auto-submitting the form or reloading the page.
- Navigation MUST appear in a consistent location and order across pages.
- Elements that perform the same function MUST be labeled and behave consistently across the site.
- Help options — such as a contact link or support widget — MUST appear in the same location across pages.
Input assistance:
- All form fields MUST have clear, descriptive labels or instructions.
- Errors and validation failures MUST be identified and described in text, not just by color or visual styling alone.
- Error messages MUST include a suggestion for how to fix the problem where possible.
- Before submitting forms that trigger consequential actions (such as payments or legal submissions), users MUST be able to review, correct, or confirm their input.
- Users MUST NOT be required to re-enter information they have already provided earlier in the same process.
- Authentication MUST NOT rely solely on memorized information. Copy-paste, password managers, and alternative authentication methods (such as email magic links) MUST be supported.
4. Robust
Content MUST be robust enough to be reliably interpreted by current and future assistive technologies.
Compatible:
- All interactive elements MUST expose an accessible name (what the element is), the correct semantic role (what it does), and any current value or state, so that assistive technologies such as screen readers can correctly identify and interact with them.
- Use semantic HTML elements wherever possible — supplemented by ARIA roles and properties only where native semantics are insufficient.
- Status messages — such as form confirmation notices, error summaries, or live
content updates — MUST be coded using appropriate ARIA live-region roles (such
as
role="status"orrole="alert"), so that assistive technologies announce them without requiring keyboard focus to move to the message element.
Fonts
Web fonts are part of the critical rendering path and directly affect performance metrics such as largest contentful paint (LCP) and cumulative layout shift (CLS). They MUST be treated with the same care as any other performance-critical asset.
Format
- WOFF2 is the only web font format that SHOULD be served in modern web applications. It has universal browser support and is the most compressed and efficient web font format. Legacy formats — including WOFF, TTF, OTF, EOT, and SVG fonts — SHOULD NOT be served, as they impose a performance cost on every visitor with no benefit for modern browsers.
Hosting
- Fonts SHOULD be self-hosted rather than loaded from third-party CDNs such as Google Fonts. Third-party font services add DNS lookups and network latency, leak visitor data to the third party (a GDPR concern in many jurisdictions), and provide no practical caching benefit (modern browsers partition caches per origin, so a font fetched on one site is never reused on another).
- Font files SHOULD be given long
Cache-Controllifetimes (months up to a year), with versioned file names used for cache-invalidation when fonts change.
Subsetting
- Fonts SHOULD be subsetted so that only the glyphs actually needed by the
application are served. A complete font family can be several hundred
kilobytes or more; most of those glyphs are typically never rendered. Tools
such as
fonttools(pyftsubset), Glyphhanger, and Subfont can automate subsetting. - Use the
unicode-rangedescriptor in@font-facedeclarations to declare separate@font-faceblocks per script (eg. Latin, Latin Extended, Cyrillic). The browser will only download the subsets it needs for the characters on the current page. - Be conservative when subsetting non-Latin scripts such as Arabic, Devanagari, and CJK. These scripts rely on shaping tables (GSUB/GPOS) and contextual forms, so aggressive subsetting can break word rendering entirely. Test non-Latin subsets thoroughly.
Loading strategy
- Inline
@font-facedeclarations in a<style>block in the HTML<head>, rather than placing them in an external stylesheet. Fonts declared in external CSS are not discovered until that stylesheet is downloaded and parsed, delaying font requests unnecessarily. - Never use
@importto load fonts or font stylesheets. Each@importadds a sequential round trip before the font can be discovered, pushing font requests very late in the render waterfall. - Preload critical fonts using
<link rel="preload" as="font" type="font/woff2" crossorigin>in the<head>. This instructs the browser to begin fetching the font immediately, rather than waiting to encounter the@font-facerule. - Preload only the subset(s) needed for above-the-fold content; preloading every subset defeats the purpose by forcing all of them to download regardless of whether they are needed.
- Use the
font-displaydescriptor in every@font-facerule to control rendering during font load.font-display: swapis the RECOMMENDED default. It renders fallback text immediately and swaps to the custom font when it arrives, preventing invisible text. Considerfont-display: optionalfor decorative or non-critical fonts. It permits the browser to skip the custom font entirely on slow connections. - Consider using HTTP 103 Early Hints to push critical font preload hints before the main HTML response is delivered, reducing time-to-text on the first round trip.
Fallbacks
- Design a robust system font stack as the fallback for every custom font. Fonts SHOULD be treated as progressive enhancement: the page MUST be fully legible and usable even if a custom font never loads.
- Tune fallback metrics to minimize CLS when a custom font swaps in. Use the
size-adjust,ascent-override,descent-override, andline-gap-overridedescriptors inside@font-faceto align the dimensions of the custom font with those of the fallback, so that text does not reflow visibly when the swap occurs.
Variable fonts
- Variable fonts — which encode multiple weights, widths, and styles in a single file — SHOULD be used when they genuinely reduce payload compared to loading multiple static font files. They are not a universal win. If only one or two weights are needed, separate static WOFF2 files subsetted to the required glyphs may be smaller. Audit and measure the payload before committing to a variable font.
- Variable fonts SHOULD be subsetted and scoped using
unicode-rangein the same way as static fonts.
Icon fonts
- Icon fonts MUST NOT be used. They are inaccessible (screen readers announce their private-use Unicode characters as gibberish), fragile if the font file fails to load, and wasteful in that the entire font file must be downloaded even when only a handful of icons are used. Use inline SVGs or SVG sprites instead. They are semantic, accessible, styleable with CSS, and can be loaded on demand.
References
- How is this website so fast? (video) — Wes Bos reverse engineers the website for McMaster-Carr, a large industrial supply company, to show how they achieve fast page loads.
- WCAG in Plain English — AAArdvark’s beginner-friendly plain-English guide to the Web Content Accessibility Guidelines (WCAG), one success criterion at a time.
- You’re loading fonts wrong (and it’s crippling your performance) — Jono Alderson’s deep dive into web font performance, covering formats, loading strategies, subsetting, fallbacks, and modern CSS descriptors.