1
0
Fork 0
mirror of https://git.sr.ht/~seirdy/seirdy.one synced 2024-11-10 00:12:09 +00:00
seirdy.one/content/posts/website-best-practices.gmi

604 lines
48 KiB
Text
Raw Normal View History

The following applies to minimal websites that focus primarily on text. It does not apply to websites that have a lot of non-textual content. It also does not apply to websites that focus more on generating revenue or pleasing investors than being good websites.
2020-11-24 22:24:12 +00:00
This is a "living document" that I add to as I receive feedback.
=> https://git.sr.ht/~seirdy/seirdy.one/log/master/item/content/posts/website-best-practices.gmi See the changelog
2020-11-24 22:24:12 +00:00
I realize not everybody's going to ditch the Web and switch to Gemini or Gopher today (that'll take, like, a month at the longest). Until that happens, here's a non-exhaustive, highly-opinionated list of best practices for websites that focus primarily on text:
* Final page weight under 50kb without images, and under 200kb with images. Page weight should usually be much smaller; these are upper-bounds for exceptional cases.
* Works in Lynx, w3m, links (both graphics and text mode), NetSurf, and Dillo
* Works with popular article-extractors (e.g. Readability) and HTML-to-Markdown converters. This is a good way to verify that your site uses simple HTML and works with most non-browser article readers (e.g. ebook converters, PDF exports).
2022-03-16 23:42:31 +00:00
* No scripts or interactivity, preferably enforced at the Content-Security-Policy (CSP) level
* No cookies
* No animations
* No fonts--local or remote--besides "sans-serif" and "monospace".
* No requests after the page finishes loading
* No 3rd-party resources (preferably enforced at the CSP level)
* No lazy loading
* No custom colors OR explicitly set both the foreground and background colors for light and dark color schemes, knowing that these can be overridden.
* A maximum line length for readability
* Server configured to support compression (gzip, optionally Brotli and Zstandard as well). It's a free speed boost.
2020-11-24 22:19:20 +00:00
* Supports dark mode via a CSS media feature and/or works with most "dark mode" browser addons. More on this below.
2020-11-30 20:09:22 +00:00
* A good score on Mozilla's HTTP Observatory. A bare minimum would be 50, but it shouldn't be too hard to hit 100.
* Optimized images.
* All images labeled with alt-text. The page should make sense without images.
2021-08-04 05:52:28 +00:00
* Probably HTTP/2. Maybe even HTTP/3. Run some tests to see if this is worth it if you're so inclined.
* Works well with Tor and the Tor Browser's safety settings.
* Preserve link underlines.
=> https://observatory.mozilla.org/ HTTP Observatory
I'd like to re-iterate yet another time that this only applies to websites that primarily focus on text. If graphics, interactivity, etc. are an important part of your website, less (possibly none) of this article applies. My hope is for most readers to consider *some* points I make on this page the next time they build a website. I don't expect--or want--anybody to follow 100% of my advice.
Earlier revisions of this post generated some responses I thought I should address below. Special thanks to the IRC and Lobsters users who gave good feedback!
=> https://lobste.rs/s/akcw1m Lobsters thread
2022-03-16 23:42:31 +00:00
I'll also cite the document "Techniques for WCAG 2.2" a number of times:
=> https://www.w3.org/WAI/WCAG22/Techniques/ Techniques for WCAG 2.2
Unlike the Web Content Accessibility Guidelines (WCAG), the Techniques document does not list requirements; rather, it serves to educate authors about *how* to use specific technologies to comply with the WCAG. I don't find much utility in the technology-agnostic goals enumerated by the WCAG without the accompanying technology-specific techniques to meet those goals.
## Security
One of the defining differences between textual websites and advanced Web 2.0 sites/apps is safety. Most browser vulnerabilities are related to modern Web features like JavaScript and WebGL. The simplicity of basic textual websites *should* guarantee some extra safety; however, webmasters need to take some additional measures to ensure limited use of "modern" risky features.
### TLS
All of the simplicity in the world won't protect a page from unsafe content injection by an intermediary. Proper use of TLS protects against page alteration in transit and ensures a limited degree of privacy. Test your TLS setup with these tools:
=> https://testssl.sh/ testssl.sh
=> https://webbkoll.dataskydd.net/ Webbkoll
Mozilla's HTTP Observatory offers a subset of Webbkoll's features but it also gives a beginner-friendly score. Most sites should strive for at least a 50, but a score of 100 or even 120 shouldn't be too hard to reach.
=> https://observatory.mozilla.org/ HTTP Observatory
A false sense of security is far worse than transparent insecurity. Don't offer broken TLS ciphers, including TLS 1.0 and 1.1. Vintage computers can run TLS 1.2 implementations such as BearSSL surprisingly efficiently, leverage a TLS terminator, or they can use a plain unencrypted connection. A broken cipher suite is security theater.
### Scripts and the Content Security Policy
Consider taking hardening measures to maximize the security benefits made possible by the simplicity of textual websites, starting with script removal.
JavaScript and WebAssembly are responsible for the bulk of modern web exploits. Ideally, a text-oriented site can enforce a scripting ban at the content security policy (CSP) level.
=> https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP CSP on MDN
For example, here's the CSP for my website:
```
2022-03-16 23:42:31 +00:00
content-security-policy: default-src 'none';
img-src 'self' data:;
2022-03-16 23:42:31 +00:00
style-src 'sha256-g8fT13xy415WmQo4vYgG4v4xJiNmrhPYQ9PGDGfXX5Y=';
style-src-attr 'none';
frame-ancestors 'none'; base-uri 'none'; form-action 'none';
2022-03-16 23:42:31 +00:00
manifest-src https://seirdy.one/manifest.min.ca9097c5e38b68514ddcee23bc6d4d62.webmanifest;
upgrade-insecure-requests; navigate-to 'none';
sandbox allow-same-origin
```
2022-03-18 06:34:59 +00:00
"default-src: 'none'" implies "script-src: 'none'", causing a compliant browser to forbid the loading of scripts. Furthermore, the "sandbox" CSP directive forbids a wide variety) of potentially insecure actions.
=> https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox "sandbox" CSP directive on MDN
While "script-src" restricts script loading, "sandbox" can also restrict script execution with stronger defenses against script injection (e.g. by a browser addon).¹ I added the "allow-same-origin" parameter so that these addons will still be able to function.²
### If you must enable scripts
Please use progressive enhancement³ throughout your site; every feature possible should be optional, and scripting is no exception.
I'm sure you're a great person, but your readers might not know that; don't expect them to trust your website. Your scripts should look as safe as possible to an untrusting eye. Avoid requesting permissions or using sensitive APIs:
=> https://browserleaks.com/javascript JavaScript Browser Information (BrowserLeaks)
Finally, consider using your CSP to restrict script loading. If you must use inline scripts, selectively allow them with a hash or nonce. Some recent directives restrict and enforce proper use of trusted types.
=> https://web.dev/trusted-types/ Trusted types
=> https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types CSP trusted types on MDN
## About fonts
If you really want, you could use serif instead of sans-serif; however, serif fonts tend to look worse on low-res monitors. Not every screen's DPI has three digits.
To ship custom fonts is to assert that branding is more important than user choice. That might very well be a reasonable thing to do; branding isn't evil! That being said, textual websites in particular don't benefit much from branding. Beyond basic layout and optionally supporting dark mode, authors generally shouldn't dictate the presentation of their websites; that should be the job of the user agent. Most websites are not important enough to look completely different from the rest of the user's system.
A personal example: I set my preferred browser font to "sans-serif", and map "sans-serif" to my preferred font in my computer's fontconfig settings. Now every website that uses sans-serif will have my preferred font. Sites with sans-serif blend into the users' systems instead of sticking out.
### But most users don't change their fonts...
The "users don't know better and need us to make decisions for them" mindset isn't without merits; however, in my opinion, it's overused. Using system fonts doesn't make your website harder to use, but it does make it smaller and stick out less to the subset of users who care enough about fonts to change them. This argument isn't about making software easier for non-technical users; it's about branding by asserting a personal preference.
### Can't users globally override stylesheets instead?
It's not a good idea to require users to automatically override website stylesheets. Doing so would break websites that use fonts such as Font Awesome to display vector icons. We shouldn't have these users constantly battle with websites the same way that many adblocking/script-blocking users (myself included) already do when there's a better option.
That being said, many users *do* actually override stylesheets. We shouldn't *require* them to do so, but we should keep our pages from breaking in case they do. Pages following this article's advice will probably work perfectly well in these cases without any extra effort.
### But wouldn't that allow a website to fingerprint with fonts?
I don't know much about fingerprinting, except that you can't do font enumeration without JavaScript. Since text-based websites that follow these best-practices don't send requests after the page loads and have no scripts, they shouldn't be able to fingerprint via font enumeration.
Other websites can still fingerprint via font enumeration using JavaScript. They don't need to stop at seeing what sans-serif maps to: they can see all the available fonts on a user's system, the user's canvas fingerprint, window dimensions, etc. Some of these can be mitigated with Firefox's protections against fingerprinting, but these protections understandably override user font preferences:
=> https://support.mozilla.org/en-US/kb/firefox-protection-against-fingerprinting Firefox's protection against fingerprinting
Ultimately, surveillance self-defense on the web is an arms race full of trade-offs. If you want both privacy and customizability, the web is not the place to look; try Gemini or Gopher instead.
## About lazy loading
2022-03-14 20:50:01 +00:00
Lazy loading may or may not work. Some browsers, including Firefox and the Tor Browser, disable lazy-loading when the user turns off JavaScript. Turning it off makes sense because lazy-loading, like JavaScript, is a fingerprinting vector. Specifically, it identifies idiosyncratic scrolling patterns:
2022-03-14 20:50:01 +00:00
> Loading is only deferred when JavaScript is enabled. This is an anti-tracking measure, because if a user agent supported lazy loading when scripting is disabled, it would still be possible for a site to track a user's approximate scroll position throughout a session, by strategically placing images in a page's markup such that a server can track how many images are requested and when.
=> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading <img>: The Image Embed element on MDN, the loading attribute
If you cant rely on lazy loading, your pages should work well without it. If pages work well without lazy loading, is it worth enabling?
I dont think so: lazy loading often frustrates users on slow connections. I think I can speak for some of these users: mobile data near my home has a number of “dead zones” with abysmal download speeds, and my homes Wi-Fi repeater setup occasionally results in packet loss rates above 60% (!!).
Users on poor connections have better things to do than idly wait for pages to load. They might open multiple links in background tabs to wait for them all to load at once, and/or switch to another task and come back when loading finishes. They might also open links while on a good connection before switching to a poor connection. For example, I often open 10-20 links on Wi-Fi before going out for a walk in a mobile-data dead-zone. A Reddit user reading an earlier version of this article described a similar experience when travelling by train:
=> https://i.reddit.com/r/web_design/comments/k0dmpj/an_opinionated_list_of_best_practices_for_textual/gdmxy4u/ u/Snapstromegon's comment
Unfortunately, pages with lazy loading don't finish loading off-screen images in the background. To load this content ahead of time, users need to switch to the loading page and slowly scroll to the bottom to ensure that all the important content appears on-screen and starts loading. Website owners shouldn't expect users to have to jump through these ridiculous hoops.
A similar attribute that I *do* recommend is the "decoding" attribute. I typically use `decoding="async"` so that image decoding can be deferred.
=> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Img#attr-decoding <img> decoding on MDN
2022-03-14 20:50:01 +00:00
### Would pre-loading/pre-fetching solve the issues with lazy-loading?
Pre-loading essential resources is fine, but speculatively pre-loading content that the user may or may not request isnt.
2022-03-14 20:50:01 +00:00
A large number of users with poor connections also have capped data, and would prefer that pages dont decide to predictively load many pages ahead-of-time for them. Some go so far as to disable this behavior to avoid data overages. Savvy privacy-conscious users also generally disable pre-loading since pre-loading behavior is fingerprintable.
2022-03-14 20:50:01 +00:00
Users who click a link *choose* to load a full page. Loading pages that a user hasnt clicked on is making a choice for that user. I encourage adoption of “link” HTTP headers to pre-load essential and above-the-fold resources when possible, but doing so does not resolve the issues with lazy-loading: the people who are harmed by lazy loading are more likely to have pre-fetching disabled.
### Can't users on poor connections disable images?
I have two responses:
1. If an image isn't essential, you shouldn't include it inline.
2. Yes, users could disable images. That's *their* choice. If your page uses lazy loading, you've effectively (and probably unintentionally) made that choice for a large number of users.
## About custom colors
2020-11-24 06:56:18 +00:00
Some users' browsers set default page colors that aren't black-on-white. For instance, Linux users who enable GTK style overrides might default to having white text on a dark background. Websites that explicitly set foreground colors but leave the default background color (or vice-versa) end up being difficult to read. Here's what this page would look like if it messed with the colors carelessly:
=> gemini://seirdy.one/misc/website_colors.png Screenshot of this page with some unreadable text
It's got a grey background, a header with unreadable black/grey text, and unreadable white-on-white code snippets
Chris Siebenmann describes this in more detail:
=> https://utcc.utoronto.ca/~cks/space/blog/web/AWebColoursProblem AWebColoursProblem
In short: when setting colors, always set both the foreground and the background color. Don't set just one of the two.
2022-02-28 10:30:48 +00:00
Chris also describes the importance of visited link colors:
=> https://utcc.utoronto.ca/~cks/space/blog/web/RealBlogUsability RealBlogUsability
### Color overrides and accessibility
Even if you set custom colors, ensure that the page is compatible with color overrides: elements shouldn't be distinguished solely by foreground and background color. Technique C25 of the *Web Content Accessibility Guidelines (WCAG) 2.2* describes how doing so can meet the WCAG 2.2's Success Criterion 1.4.8. Specifically, it describes using default colors in combination with visible borders. The latter helps distinguish elements from surrounding content without relying on a custom color palette.
=> https://www.w3.org/WAI/WCAG22/Techniques/css/C25 Technique C25: Specifying borders and layout in CSS to delineate areas of a Web page while not specifying text and text-background colors
The Web version of this page is an example application of Technique C25 and the similar G148:
=> https://seirdy.one/2020/11/23/website-best-practices.html The Web mirror of this Gemlog post
=> https://www.w3.org/WAI/WCAG22/Techniques/general/G148 Technique G148: Not specifying background color, not specifying text color, and not using technology features that change those defaults
The Web version of this page only uses non-default colors when a user agent requests a dark color scheme (using the "prefers-color-scheme" CSS media query; see the next subsection) and for lightening borders. Any image with a solid background may match the page background; to ensure that their dimensions are clear, I surrounded them with borders. I also set a custom color for the borders and ensure that the image backgrounds don't match the border colors. I included horizontal rules (<hr>) further down to break up next/prev post navigation as well as separate footers, since these elements lack heading-based delineation. When overriding color schemes or disabling CSS altogether, the page layout remains clear.
The aforementioned techniques ensure a clear page layout independently of color scheme.
### Dark themes
2021-06-14 01:21:19 +00:00
If you do explicitly set colors, please also include a dark theme using a media query:
```
@media (prefers-color-scheme: dark)
```
2020-11-24 22:19:20 +00:00
For more info, read the relevant docs:
=> https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme prefers-color-scheme docs on MDN
2021-06-14 01:21:19 +00:00
When setting colors, especially with a dark background, I recommend checking your page's contrast using Advanced Perceptual Contrast Algorithm (APCA) values. You can do so in an online checker or Chromium's developer tools (you might have to enable them in a menu for experimental preferences.
=> https://www.myndex.com/APCA/simple Online ACPA Contrast Calculator
Blue and purple links on a black background have much worse perceptual contrast than yellow or green links.
2021-06-14 01:21:19 +00:00
Note that the APCA isn't fully mature as of early 2022. Until version 3.0 of the WCAG is ready, pages should also conform to the contrast ratios described in the WCAG 2.2's success criterions 1.4.3 (Contrast: Minimum, level AA) or 1.4.6 (Contrast: Enhanced, level AAA).
CSS filters such as "invert" are expensive to run, so use them sparingly. Simply inverting your page's colors to provide a dark theme could slow it down or cause a user's fans to spin.
2021-06-14 01:21:19 +00:00
Darker backgrounds draw less power on devices with OLED screens; however, backgrounds should never be solid black. White text on a black background causes halation, esp. for readers who have astigmatism. There has been some experimental and plenty of anecdotal evidence to support this.
=> https://www.laurenscharff.com/research/AHNCUR.html Hill, Alyson (supervised by Scharff, L.V.) Readability Of Websites With Various Foreground / Background Color Combinations, Font Types And Word Styles, 1997
=> https://jessicaotis.com/academia/never-use-white-text-on-a-black-background-astygmatism-and-conference-slides/ Never Use White Text on a Black Background: Astygmatism and Conference Slides
I personally like a foreground and background of "#ececec" and "#0c0c0c", respectively. These shades seem to be as far apart as possible without causing accessibility issues: "#0c0c0c" is barely bright enough to create a soft "glow" capable of minimizing halos.
If you can't bear the thought of parting with your solid-black background, worry not: there exists a CSS media feature and client-hint for contrast preferences, called "prefers-contrast". It takes the parameters "no-preference", "less", and "more". You can serve increased-contrast pages to those who request "more", and vice versa. Check MDN for more information:
=> https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast prefers-contrast on MDN
2020-11-24 11:18:03 +00:00
## Image optimization
Some image optimization tools I use:
2021-08-04 05:52:28 +00:00
=> https://pngquant.org pngquant (lossy)
=> https://github.com/shssoichiro/oxipng Oxipng (lossless)
=> https://github.com/tjko/jpegoptim jpegoptim (lossless or lossy)
=> https://developers.google.com/speed/webp/docs/cwebp cwebp (lossless or lossy)
2021-02-28 20:40:26 +00:00
=> https://github.com/AOMediaCodec/libavif avifenc (lossless or lossy), included in libavif
2020-11-24 11:18:03 +00:00
I put together a quick script to losslessly optimize images using these programs in my dotfile repo:
2020-11-24 11:18:03 +00:00
=> https://git.sr.ht/~seirdy/dotfiles/tree/3b722a843f3945a1bdf98672e09786f0213ec6f6/Executables/shell-scripts/bin/optimize-image optimize-image
You also might want to use the HTML "<picture>" element, using JPEG/PNG as a fallback for more efficient formats such as WebP or AVIF.
2020-11-24 11:18:03 +00:00
=> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture <picture> docs on MDN
Most of my images will probably be screenshots that start as PNGs. My typical flow:
1. Re-size and crop the image. Convert to grayscale if colors aren't important.
2. Lossy compression with pngquant
3. Losslessly optimize the result with Oxipng and its Zopfli backend (slow)
4. Also create a lossless WebP from the lossy PNG and a lossy WebP from the source image, using cwebp. Pick the smaller of the two.
5. Include the resulting WebP in the page, with a fallback to the PNG using a `<picture>` element.
6. Create a lossy AVIF image from the original source image, and include it in the `<picture>` element if it's smaller than the WebP.
7. If the image is too light, repeat for a dark version of the image to display with a `prefers-dark-mode` media query.
Here's a sample command to compress a PNG using ImageMagick, pngquant, and oxipng. It shrinks the image, turns it grayscale, reduces the color palette, and then applies lossless Zopfli compression:
``` sh
convert -resize 75% original.png -colorspace GRAY -format png - \
| pngquant -s 1 12 - \
| oxipng -o max -Z --fix - --out compressed.png
```
It might seem odd to create a lossless WebP from a lossy PNG, but I've found that it's often the best way to get the smallest possible image at the minimum acceptable quality for screenshots with solid backgrounds.
In general, avoid using inline images just for decoration. Only use an image if it has a clear purpose that significantly adds to the content in a way that text can't replace, and provide alt-text as a fallback. Any level of detail that isn't necessary for getting the point across should be removed with lossy compression and cropping. Some conventional wisdom for image compression doesn't hold up when compressing this aggressively; for instance, I've found that extremely aggressive dithering and PNG compression of small black-and-white images consistently surpasses JPEG compression.
If you want to include a profile photo (e.g., if your website is part of the IndieWeb), I recommend re-using one of your favicons. Doing so should be harmless since most browsers will fetch and cache favicons anyway.
2021-06-28 00:02:45 +00:00
If you really want to go overboard with PNG optimization, you can try a tool like Efficient Compression Tool:
=> https://github.com/fhanau/Efficient-Compression-Tool Efficient Compression Tool on GitHub
### Dark image variants
A "<picture>" element allows selection of sources based on any CSS media query. When images have light backgrounds, I like to include dark variants too.
```
<source
srcset=/p/dark.png
type=image/png
media="screen and (prefers-color-scheme: dark)">
<source
srcset=/p/light.png
type=image/png>
```
Requiring the "screen" media type prevents selection of dark variants when printing. Printer paper is almost always white, so dark images could waste ink. Ink waste is a sensitive issue among many students: school printers sometimes charge students who exceed a given ink quota. Ask me how I know!
Light and dark variants of legacy formats (PNG, JPG, GIF), WebP, and AVIF can cause some of my "<picture>" imagesets to have up to six image variants. I could fully automate the process using my static site generator (Hugo) if I wanted to. Since I do want to inspect each image and compress to the minimum acceptable quality, I settled for partial automation using shell scripts and a Hugo shortcode:
=> https://git.sr.ht/~seirdy/seirdy.one/tree/master/layouts/shortcodes/picture.html My shortcode for <picture> elements
2022-03-18 06:34:59 +00:00
### SVG images
I only recommend using SVG in images, not embeds/objects or directly in the body. Remember that users may save images and open them in a non-browser image viewer with reduced SVG compatibility. To maintain maximum compatibility, stick to the subset of SVG Statics secure static processing mode that appears in the SVG Tiny Portable/Secure (PS) spec. SVG PS is a subset of SVG Tiny 1.2, which is a supported export format in most vector drawing programs.
=> https://www.w3.org/TR/SVG/conform.html#secure-static-mode SVG conformance, section 2.2.6: Secure static mode
=> https://datatracker.ietf.org/doc/draft-svg-tiny-ps-abrotman/ SVG Tiny PS
=> https://www.w3.org/TR/SVGTiny12/intro.html SVG Tiny 1.2
Ignore the elements specifically required for SVG Tiny PS; your image can be a standard SVG that only utilizes a tiny subset of the full SVG spec.
2022-03-18 06:37:22 +00:00
The above advice might seem daunting, but its usually easy to use existing tools to generate an SVG Tiny file and manually edit it to support the SVG secure static mode. SVGs that conform to this subset should be compatible with Qt5's SVG implementation, librsvg (used by Wikipedia and GNOME), and most operating systems' icon renderers.
2022-03-18 06:34:59 +00:00
Two tools that can optimize the size of an SVG file are SVGO and the now-discontinued svgcleaner:
=> https://github.com/svg/svgo SVGO
=> https://github.com/RazrFalcon/svgcleaner svgcleaner
Don't overdo lossy compression with these tools, since lossy compression can sometimes *reduce* the effectiveness of gzip and Brotli compression.
## Layout
This is possibly the most subjective item I'm including, and the item with the most exceptions. Consider it more of a weak suggestion than hard advice. Use your own judgement.
A simple layout looks good at a variety of window sizes, rendering responsive layout changes unnecessary. Textual websites really don't need more than a single column; readers should be able to scan a page top-to-bottom, left-to-right (or right-to-left, depending on the locale) exactly once to read all its content. Verify this using the horizontal-line test: mentally draw a horizontal line across your page, and make sure it doesn't intersect more than one (1) item. Keeping a single-column layout that doesn't require responsive layout changes ensures smooth window re-sizing.
Exceptions exist: one or two very simple responsive changes won't hurt. The main anti-patterns are adjusting the relative order of elements, and layout shifts dramatic enough to cause confusion.
For example, the only responsive layout change on my website (https://seirdy.one) is a single CSS declaration to switch between inline and multi-line navigation links at the top of the page:
```
@media (min-width: 32rem) {
nav li {
display: inline;
}
}
```
Nontrivial use of width-selectors, in CSS or imagesets, is actually a powerful vector for JS-free fingerprinting:
2022-02-08 05:26:03 +00:00
=> https://matt.traudt.xyz/posts/2016-09-04-how-css-alone-can-help-track-you/ How CSS alone can help track you
Achieving this type of layout entails using the WCAG 2.2 techniques C27 as well as C6:
=> https://www.w3.org/WAI/WCAG21/Techniques/css/C27.html C27: Making the DOM order match the visual order
=> https://www.w3.org/WAI/WCAG22/Techniques/css/C6 C6: Positioning content based on structural markup
### What about sidebars?
Sidebars are probably unnecessary, and can be quite annoying to readers who re-size windows frequently. This is especially true for tiling window manager users like me: we frequently shrink windows to a fraction of their original size. When this happens on a website with a sidebar, one of two things happens:
1. The site's responsive design kicks in: the sidebar vanishes and its elements move elsewhere. This can be quite CPU-heavy, as the browser has to both re-wrap the text *and* handle a complex layout change. Frequent window re-sizers will experience lag and battery loss, and might need a moment to figure out where everything went.
2. The site doesn't use responsive design. The navbar and main content are now squeezed together. Readers will probably close the page.
Neither situation looks great.
### Sidebar alternatives
Common items in sidebars include tag clouds, an author bio, and an index of entries; these aren't useful while reading an article. Consider putting them in the article footer or--even better--dedicated pages. This does mean that readers will have to navigate to a different page to see that content, but they probably prefer things that way; almost nobody who clicked on "An opinionated list of best practices for textual websites" did so because they wanted to read my bio.
Don't boost engagement by providing readers with information they didn't ask for; earn engagement with good content, and let readers navigate to your other pages *after* they've decided they want to read more.
## Narrow viewports
A single element wider than the viewport will trigger horizontal scrolling for the entire page. This is especially problematic for long pages that require excessive scrolling.
Not every phone has a giant screen: millions of people around the world use Web-enabled feature phones. The Jio Phone 2, for instance, sports a 6 cm (2.4 inch) screen that's 240 pixels wide. Furthermore, some programs sport browser windows in sidebars:
=> https://addons.mozilla.org/en-US/firefox/addon/side-view/ Mozilla's side view
=> https://help.vivaldi.com/desktop/panels/web-panels/ Vivaldi Web Panels
Users who leverage floating or tiling windows rather than maximizing everything could use viewports of arbitrary dimensions.
Long words, especially in headings, can trigger horizontal overflow. Test in a viewport that's under 240 pixels wide (DPR=1) and observe any words that trail off of the edge of the screen. Add soft hyphens to these words using the "&shy;" entity. Prefer breaking off suffixes ("-ing", "-ed", etc).
Most modern browsers support the "hyphens" CSS3 property, but full automatic hyphenation is an overkill solution with a naive implementation. Automatic hyphenation will insert hyphens wherever it can, not necessarily between the best syllables. At the time of writing, humans are still better at hyphenating than most software implementations. I'm also not aware of a CSS property that only breaks syllables when necessary to avoid horizontal scrolling.
Users employing machine translation will not benefit from your soft hyphens, so don't expect them to always work as intended. Translation tools might also replace short words with long ones. Soft hyphens and automatic hyphenation are both flawed solutions, but I find soft hyphens to be less problematic.
Where long inline "<code>" elements can trigger horizontal scrolling, consider a scrollable "<pre>" element instead. Making a single element scrollable in two dimensions is far better than doing so for the whole page.
### Indented elements
Most browser default stylesheets were not optimized for narrow viewports, so narrow-viewport optimization is one of few good reasons to override the defaults. The best example of widescreen bias in browser stylesheets is indentation.
The HTML spec's blockquote section recommends placing a <blockquote> element inside a <figure> and citations in a <figcaption> to show a semantic relationship between a quotation and its citation:
=> https://html.spec.whatwg.org/multipage/grouping-content.html#the-blockquote-element HTML living standard: Grouping Content, section 4.4.4
2022-03-18 06:34:59 +00:00
Browser default stylesheets typically give <figure> elements extra margins on the left and right. <blockquote> elements have a large indent. Combining these two properties gives the final quotation an excessive visual indent, wasting precious vertical screen space. When such a blockquote contains <ol> or <ul> elements, the indentation alone may fill most of a narrow viewport!
2022-03-18 06:34:59 +00:00
I chose to remove the margins in <figure> elements. I don't find the margins useful because I only use them to annotate non-centered phrasing content, such as <blockquote> and <pre> elements. If you're reading the Web version of this page with its own stylesheet enabled, in a CSS 2 compliant browser, you might notice that the blockquotes on it are formatted with a minimal indent and a thick gray border on the left rather than a full indent. These two adjustments allow blockquotes containing bulleted lists to fit on most narrow viewports, even when wrapped by a <figure> element.
## Tor
Many people use Tor out of necessity. On Tor, additional constraints apply.
For one, Tor users are encouraged to set the Tor Browser Bundle's (TBB) security settings to "safest".
=> https://tb-manual.torproject.org/en-US/security-settings/ TBB Security Settings
This disables scripts, MathML, some fonts, SVG images, and other features:
=> https://gitweb.torproject.org/torbutton.git/tree/modules/security-prefs.js Torbutton security-prefs source code
If your site has any SVG images, the Tor browser will download these just like Firefox would (to avoid fingerprinting) but will not render them.
Additionally, hopping between nodes in Tor circuits incurs latency, worsening the impacts of requiring multiple requests and round-trips. Try to minimise the number of requests to view a page.
If you use a CDN or some overcomplicated website security stack, make sure it doesn't block Tor users or require them to enable JavaScript to complete a CAPTCHA. Tor Browser users are supposed to avoid fingerprinting vectors like JS and browser extensions, so requiring a JavaScript-based CAPTCHA will effectively block many Tor users.
To go above and beyond, try mirroring your site to an onion service to reduce the need for exit nodes. Mirroring allows you to keep a separate version of your site optimized for the Tor browser. Normally, optimizing specifically for a given user agent's quirks is a bad practice; however, the Tor Browser is a special case because it sometimes pretends to have Firefox's capabilities. Progressive enhancement and graceful degradation won't work.
For example, my website's clearnet version uses some SVG images. Some browsers can't handle a given image format. The typical solution is to use a <picture> element containing <source> children with varying formats and a fallback <img> element using a legacy image format.
The Tor browser will download whichever format Firefox would, rather than whichever formats it actually supports. A <picture> element containing an SVG and a raster fallback won't help: the Tor browser will avoid fingerprinting by selecting the SVG format, not a fallback format. The image will not be rendered, so users will have downloaded the image only to see a white box.
I address the issue by not using any SVG images on my hidden service:
=> http://wgq3bd2kqoybhstp77i3wrzbfnsyd27wt34psaja4grqiezqircorkyd.onion/ My Tor hidden service (HTTP+HTML)
## Optimal loading
Lightweight content isn't the only factor for optimizing load times. There are ways to optimize loading without a CDN.
### Blocking content
HTML is a blocking resource: images and stylesheets will not load until the user agent loads and parses the HTML that calls them. To start loading above-the-fold images before the HTML parsing finishes, send a "link" HTTP header.
My website includes a "link" header to load an SVG that serves as my IndieWeb photo and favicon:
```
link: </favicon.072dbf7bc4323646b9ee96243fbd71b2.svg>; rel=preload; as=image
```
Reducing load time is especially useful for users with unreliable connections. For much of the world, connectivity comes in short bursts during which loading time is precious. Chances of a connection failure or packet loss increase with time.
### Inline content
In addition to HTML, CSS is also a blocking resource. You could pre-load your CSS using a "link" header. Alternatively: if your CSS is under a kilobyte, consider inlining it in the <head> using a <style> element. Simply inlining stylesheets can pose a security threat, but the "style-src" CSP directive can mitigate this if you include a hash of your inline stylesheet.
You can do the same with images by using a "data:" URI; my 32-pixel PNG site icon is under 200 bytes and inlines quite nicely. On this site's hidden service, it's often the only image on a page (recall that the hidden service replaces SVGs with PNGs). Inlining this image and the stylesheet allows my hidden service's homepage to load in a single request, which is a welcome improvement given the round-trip latency that plagues onion routing implementations.
### Layout shifts
Loading content with unknown dimensions, such as images, can create layout shifts; the WICG's Layout Instability API describes the phenomenon in detail.
=> https://wicg.github.io/layout-instability/#sec-intro Layout Instability API
Avoid layout shifts by including dimensions in HTML attributes. The simplest way to do so is by including "width" and "height" values, but the "style" attribute could work too. I recommend staying away from the "style" attribute, or at least selectively allowing its use with the "style-src-attr" CSP directive.
### Other server-side tweaks
In-depth server configuration is a bit out of scope, so I'll keep this section brief.
Compression--especially static compression--dramatically reduces download sizes. My full-text RSS feed is about a quarter of a megabyte, but the Brotli-compressed version is around 70 kilobytes. Caddy supports this with a "precompressed" directive; Nginx requires a separate module:
=> https://github.com/google/ngx_brotli The ngx_brotli module
When serving many resources at once (e.g., if a page has many images), HTTP/2 could offer a speed boost through multiplexing. HTTP/3 is unlikely to help textual websites much, so run a benchmark to see if it's worthwhile.
## Non-Browsers: Reading mode
Fully standards-compliant browsers aren't the only programs people use. They also use "reading mode" tools and services.
Reading modes leverage article extractors such as Readability (integrated into Firefox, Epiphany, Brave, Vivaldi, and others), DOM Distiller (integrated into Chromium), and Trafilatura (powers a variety of tools and services). A host of other proprietary options exist: Diffbot powers services like Instapaper, Mozilla's Pocket has its own secret parsers, and countless "send to e-reader" services have amassed loyal users. Safari's proprietary fork of Readability has grown quite complex compared to upstream.
=> https://github.com/mozilla/readability Readability.js
=> https://chromium.googlesource.com/chromium/dom-distiller/ DOM Distiller
=> https://trafilatura.readthedocs.io/en/latest/ Trafilatura
I don't recommend catering to each tool's non-standard quirks. Studying their design reveals that they observe open standards, to varying degrees. Readability, DOM Distiller, and Trafilatura understand plain-old, semantic HTML (POSH).
POSH should be enough for most use-cases, but some authors want to go further. For example, they may want a byline or published date to show up in these modes.
Most extractors fetch these values using open standards for structured data. The most well-supported option is microformats (Readability is one of the few that supports the newer microformats2). Some support schema.org vocabularies in Microdata or JSON-LD syntaxes, or Dublin Core vocabularies in RDFa. Most parse <meta> tags from the document <head>, but others don't due to misuse and overly aggressive SEO.
=> https://microformats.org/wiki/microformats Microformats
=> https://schema.org/ Schema.org
=> https://www.dublincore.org/ Dublin Core
Sorry, that was a lot of jargon for a single paragraph. Unfortunately, describing those terms is out of scope for this post. If you'd like to dive down this rabbit hole, read about the "Semantic Web".
Again: avoid catering to non-standard implementations' quirks, especially undocumented proprietary ones. Let's not repeat the history of the browser wars:
=> https://en.wikipedia.org/wiki/Browser_wars
More information about standard and non-standard behavior of reading modes is in this article by Daniel Aleksandersen:
=> https://www.ctrl.blog/entry/browser-reading-mode-parsers.html Web Reading Mode: The non-standard rendering mode
Reading modes aren't the only non-browser user agents out there. Plain-text feed readers and link previewers are some other options. I singled out reading modes because of their widespread adoption and value. Decide which other kinds of agents are important to you (if any), and see if they expose a hole in your semantics.
## Testing
If your site is simple enough, it should automatically handle the vast majority of edge-cases. Different devices and browsers all have their quirks, but they generally have one thing in common: they understand POSH.
In addition to standard testing, I recommend testing with unorthodox setups that are unlikely to be found in the wild. If a website doesn't look good in one of these tests, there's a good chance that it uses an advanced Web feature that can serve as a point of failure in other cases. Simple sites should be able to look good in a variety of situations out of the box.
Your page should easily pass the harshest of tests without any extra effort if its HTML meets basic standards for well-written code (overlooking bad formatting and a lack of comments). Even if you use a complex static site generator, the final HTML should be simple, readable, and semantic.
### Sample unorthodox tests
2022-03-14 20:50:01 +00:00
These tests start out pretty reasonable, but gradually get more ridiculous. Once again, use your judgement.
1. Evaluate the heaviness and complexity of your scripts (if any) by testing with your browser's JIT compilation disabled.⁴
2. Test using the Tor browser with the safest security level enabled (disables JS and other features).
3. Load just the HTML. No CSS, no images, etc. Try loading without inline CSS as well for good measure.
4. Print out the site in black-and-white, preferably with a simple laser printer.
5. Test with a screen reader.
6. Test keyboard navigability with the tab key. Even without specifying tab indices, tab selection should follow a logical order if you keep the layout simple.
7. Test in textual browsers: lynx, links, w3m, ELinks, edbrowse, EWW, Netrik, etc.
8. Read the (prettified/indented) HTML source itself and parse it with your brain. See if anything seems illogical or unnecessary. Imagine giving someone a printout of your page's <body> along with a whiteboard. If they have a basic knowledge of HTML tags, would they be able to draw something resembling your website?
9. Test in an online website translator tool.
10. Test on something ridiculous: try your old e-reader's embedded browser, combine an HTML-to-EPUB converter and an EPUB-to-PDF converter, or stack multiple article-extraction utilities on top of each other. Be creative and enjoy breaking your site. When something breaks, examine the breakage and see if you can fix it by simplifying your page.
11. Build a time machine. Travel decades--or perhaps centuries--into the future. Keep going forward until the WWW is breathing its last breath. Test your site on future browsers. Figuring out how to transfer your files onto their computers might take some time, but you have a time machine so that shouldn't be too hard. When you finish, go back in time to meet Benjamin Franklin:
=> https://xkcd.com/567/ xkcd: Urgent Mission
I'm still on step 10, trying to find new ways to break this page. If you come up with a new test, please share it:
=> mailto:~seirdy/seirdy.one-comments@lists.sr.ht Mailing list for this website
## In defense of link underlines
Some typographers insist that underlined on-screen text is obsolete and hyperlinks are no exception.
=> https://practicaltypography.com/underlining.html Underlining | Buttericks Practical Typography
I disagree. One reason is that underlines make it easy to separate multiple consecutive inline links:
=> gemini://seirdy.one/misc/underlines.png A line of three consecutive hyperlinks with and without underlines
Underlines also make it easy for readers with color vision deficiencies to distinguish the beginnings and ends of links from surrounding text. A basic WCAG Level A requirement is for information to not be conveyed solely through color:
2022-02-26 09:54:26 +00:00
> Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.
2022-02-26 09:54:26 +00:00
=> https://www.w3.org/TR/WCAG22/#distinguishable WCAG 2.2, section 1.4.1
Readers already expect underlined text to signify a hyperlink. Don't break fundamental affordances for aesthetics.
## Other places to check out
2022-03-04 21:55:35 +00:00
This page can be thought of as an extension of the principles of Brutalist Web Design:
* Content is readable on all reasonable screens and devices.
* Only hyperlinks and buttons respond to clicks.
* Hyperlinks are underlined and buttons look like buttons.
* The back button works as expected.
* View content by scrolling.
* Decoration when needed and no unrelated content.
* Performance is a feature.
=> https://brutalist-web.design/ Brutalist Web Design
The 250kb club gathers websites at or under 250kb, and also rewards websites that have a high ratio of content size to total size.
=> https://250kb.club/ The 250kb Club
The 10 KB Club does the same with a 10kb homepage budget (excluding favicons and webmanifest icons). It also has guidelines for noteworthiness, to avoid low-hanging fruit like mostly-blank pages.
=> https://10kbclub.com/ The 10 KB Club
My favorite website club has to be the XHTML Club by Bradley Taunt, the creator of the original 1mb.club.
=> https://xhtml.club the eXtreme HyperText Movement for Luddites
=> https://uglyduck.ca Bradley Taunt's homepage
Motherfucking Website generated a lot of buzz when it was created:
=> https://motherfuckingwebsite.com/ Motherfucking Website
Motherfucking Website inspired several unofficial sequels that tried to gently improve upon it. My favorite:
=> https://bestmotherfucking.website/ Best Motherfucking Website
The WebBS calculator compares a page's size with the size of a PNG screenshot of the full page content, encouraging site owners to minimize the ratio of the two:
=> https://www.webbloatscore.com/ Web Bloat Score Calculator
One resource I found useful (that eventually featured this article!) was the “Your page content” section of Bill Dietrichs comprehensive guide to setting up your personal website:
=> https://www.billdietrich.me/YourPersonalWebSite.html#PageContent Your Personal Website
If you've got some time on your hands, I *highly* recommend reading the WCAG:
=> https://www.w3.org/TR/WCAG22/ Web Content Accessibility Guidelines (WCAG) 2.2
The WCAG 2 standard is technology-neutral, so it doesn't contain Web-specific advice. For that, check the the how-to quick reference. It combines the WCAG with its supplementary list of techniques:
=> https://www.w3.org/WAI/WCAG22/quickref How to Meet WCAG (Quick Reference)
=> https://www.w3.org/WAI/WCAG22/Techniques/ Techniques for WCAG 2.2
## Notes
¹ Many addons function by injecting content into pages; this significantly weakens many aspects of the browser security model (e.g. site and origin isolation) and should be avoided if at all possible. On sensitive pages with content such as public key fingerprints, I recommend setting a blank "sandbox" directive even if it means breaking these addons.
² Some addons will have reduced functionality; for instance, Tridactyl can't create an <iframe> for its command window. I consider this to be worthwhile since the most important functionality is still available, and because authors shouldn't feel compelled to support security weakening. I say this as someone who uses Tridactyl often.
=> https://github.com/tridactyl/tridactyl Tridactyl
³ Here's an overview of PE and my favorite write-up on the subject.
=> https://en.wikipedia.org/wiki/Progressive_enhancement Progressive Enhancement (Wikipedia)
=> https://whalecoiner.com/articles/progressive-enhancement Yes, progressive enhancement is a fucking moral argument
⁴ Consider disabling the JIT for your normal browsing too; doing so removes whole classes of vulnerabilities. In Firefox, toggle javascript.options.ion, javascript.options.baselinejit, javascript.options.native_regexp, javascript.options.asmjs, and javascript.options.wasm in about:config; in Chromium, run chromium with `--js-flags='--jitless'`; in the Tor Browser, set the security level to "Safer".