Explore My Notes

Animating the height of content using CSS grid | Kevin Powell

A useful trick for animating the height of a specific piece of content. You cannot transform a height in CSS to the auto keyword; you have to provide a fixed value, which is obviously suboptimal in most situations. Kevin's trick is to use grid row heights and the fr unit (which is animatable) to create the illusion of height manipulation.

The general code looks like this (though you'd probably use a JavaScript event to trigger the expansion, rather than hover):

<style>
    section {
        display: grid;
        grid-template-rows: 0fr;
        transition: grid-template-rows 500ms;
    }

    section > div {
        overflow: hidden;
    }

    section:hover {
        grid-template-rows: 1fr;
    }
</style>

<section>
    <div>
        <h2>Title</h2>
        <p>Content goes here</p>
    </div>
</section>

The trick here is really the overflow property (and I did find it was possible to tweak this back to auto or scroll, but it's less smooth).

In testing, yes you can animate multiple rows in different ways, use other keywords like max-content, and generally play around with this technique quite a lot. Though there may be dragons; Chrome in particular does weird things with Grid animations if there are multiple rows, and you can't tweak timing that much (though you can use keyframe animation to do a similar thing and tweak timing there, again with caveats and Chrome weirdness).

Nuclear anchored sidenotes | Eric Meyer

I have long sought a web-native way to achieve sidenotes with CSS, and it turns out Eric is in the same boat. The new Anchor Position API in CSS is seeking to solve that problem, and whilst it remains very experimental right now (and support is only in early stages) it looks extremely promising. This is a brilliant write-up of how to achieve the precise thing I keep on attempting, as well as a general overview of where this functionality is (currently) headed.

The gist is that an anchor comes in two parts:

  1. A named in-flow element (or elements, which is cool);
  2. And an out-of-flow element that will be anchored to it.

You define the anchor point for the in-flow element using typical CSS naming patterns:

main { 
    anchor-name: --main-content; 
}

sup {
    anchor-name: --reference;
}

And then reference this from your out-of-flow element in the same way that you would use absolute positioning, though you can also specify an "edge" to use as the connection point:

.sidenote {
    position: absolute;
    left: anchor(--main-content right);        <-- this will attach to the previously named element, and align with the right-hand edge.
}

(I'm not sure whether logical properties work here – i.e. inline-end rather than right – but I hope so.)

Eric points out that one of the cool superpowers of the current spec is that you can anchor an element to multiple named anchors:

Yes, I’m anchoring the sidenotes with respect to two completely different anchors, one of which is a descendant of the other. That’s okay! You can do that! Literally, you could position each edge of an anchored element to a separate anchor, regardless of how they relate to each other structurally.

So this kind of pattern, where you anchor a sidenote both to the main content block and to a footnote reference in the text is extremely easy (you can also use calc to add spacing between anchored elements):

.sidenote {
    position: absolute;
    top: anchor(--reference);
    left: calc(anchor(--main-content right) + 0.5rem);
}

But because these elements are all out-of-flow, the browser has no issue with them overlapping. So how do you prevent that? How do you keep your sidenotes nicely stacked? Well, the spec states that if multiple elements have the same anchor name then the anchor point should be assumed as the closest element to the positioned item. That gives us some pretty cool behaviour, because it means we can turn all of the sidenotes into anchor points themselves, give them all the same anchor name, and then each one will be able to position itself relative to the last sidenote in the DOM 🤯 (Eric points out this technique was first discovered by Roman Komarov who has a very clever CodePen detailing some of these tricks.)

The specification says the named anchor that occurs most recently before the thing you’re positioning is what wins.

So you can do something like this and prevent overlapping from ever occurring:

.sidenote {
    position: absolute;
    anchor-name: --sidenote;
    top: max(anchor(--reference), calc(anchor(--sidenote bottom) + 0.5rem);
    left: calc(anchor(--main-content right) + 0.66em);
    bottom: auto;
    max-width: 23em;
}

The max function here is doing a lot of heavy lifting, ensuring that a sidenote is either placed alongside the superscript citation link in the text or just below the previous sidenote (with some padding), whichever is greater:

It can all be verbalized as, “the position of the top edge of the list item is either the same as the top edge of its anchor, or two-thirds of an em below the bottom edge of the previous sidenote, whichever is further down”.
The browser knows how to do this because the list items have all been given an anchor-name of --sidenote (again, that could be anything, I just picked what made sense to me).
Given my setup, this means an anchored sidenote will use the previous sidenote as the anchor for its top edge.

Frankly, this is incredible. I love it 😍 Though browser support is likely to be slow and the spec is still changing, so this may not ultimately be what gets shipped. Personally, my one concern here (which Eric briefly mentions) is accessibility. Much like Grid positioning, this feels like we need to be able to control the reading order of content before this can be considered a truly useable approach. And because anchoring is being intrinsically linked to absolute positioning, it makes me worried that even if we do get reading order control in the future, it won't apply here. So in that sense, I'd almost prefer to see this shifted to a new position value (e.g. position: anchored;) which can have some better, more accessible behaviour baked in.

Remember that all this is experimental, and the specification (and thus how anchor positioning works) could change.

📆 22 Sep 2023  | 🔗

  • HTML & CSS
  • anchor positioning
  • CSS
  • sidenote
  • Edward Tufte
  • marginalia 

A static-site CMS that runs in VS Code | Front Matter

A clever, open-source CMS designed for static site generators like Eleventy, Astro, and Hugo which use Markdown files and frontmatter metadata. The CMS itself runs directly in your IDE, giving you a visual interface to your content. I'm not sure you can host it or access it otherwise, so definitely one for techie projects rather than content teams.

📆 21 Sep 2023  | 🔗

  • Nuts & Bolts
  • CMS
  • Eleventy
  • SSG
  • AstroJS
  • VSCode
  • frontmatter
  • Front Matter 

React modal using <dialog> element | Log Rocket

An extremely detailed overview of how to use the native <dialog> element in React to build a state-driven modal component. As ever, the answer is significantly more complicated than it would be outside of React land, but just about every step is thought through here.

The one issue I had when following along was the onKeyDown event listener. Assigning this to the <dialog> directly only works if the user uses the Escape key whilst focused on the modal. If they click the background, or anywhere else on the browser, then use Escape the modal will still close, but the state will not be updated correctly. I had to modify the code to use a more traditional event listener instead. Here's my final snippet:

interface ModalProps {
	title: string
	isOpen?: boolean
	onClose?: () => void
}

export const Modal = ({
	title,
	isOpen = false,
	onClose,
	children,
	...props
}: PropsWithChildren<ModalProps>) => {
	const modalRef = React.useRef<HTMLDialogElement | null>(null)
	const [open, setOpen] = React.useState(isOpen)

	// Function: Closes the modal and syncs state with parent
	const closeModal = () => {
		if (onClose) {
			onClose()
		}
		setOpen(false)
	}

	// Function: Control modal via props/parent
	React.useEffect(() => {
		setOpen(isOpen)
	}, [isOpen])

	// Function: Control the modal with native browser APIs
	React.useEffect(() => {
		const modal = modalRef.current

		if (modal) {
			if (open) {
				modal.showModal()
			} else {
				modal.close()
			}
		}
	}, [open])

	// Function: Listen for escape key and close modal / sync state
	React.useEffect(() => {
		const escapeModal = (event: KeyboardEvent) => {
			if (event.key === "Escape") {
				if (onClose) {
					onClose()
				}
				setOpen(false)
			}
		}
		document.addEventListener("keydown", (e) => escapeModal(e))

		return () =>
			document.removeEventListener("keydown", (e) => escapeModal(e))
	}, [onClose])

	return (
		<dialog
			ref={modalRef}
			className="modal"
			aria-labelledby="modalTitle"
			{...props}
		>
			<header>
				<h2 id="modalTitle">{title}</h2>
				<button
					aria-label="Close"e"
					onClick={() => closeModal()}
				>
                    <CloseIcon />
                </button>
			</header>
			{children}
		</dialog>
	)
}

On how to handle native escape key functionality:

However, since we are managing the states of our Modal component using the useState Hook, we need to update it accordingly when the escape key is pressed to ensure the proper functioning of the Modal dialog.

Designing accessible data visualisations | Sarah L. Fossheim

A broad overview of the best practices involved in designing data visualisations, with heaps of additional context and excellent imagery to really drive home the issues and better solutions. The key list are as follows:

  • Don't rely on colour to explain the data
  • Don't use very bright or low-contrast colors
  • Don't hide important data behind interactions
  • Don't overwhelm the user with information
  • Do use accessibility tools when designing
  • Do use patterns and shapes in addition to colour
  • Do use labels and legends
  • Do translate the data into clear language
  • Do provide context and explain the visualisation
  • Do focus on accessibility during user interviews

On the issue with tooltips:

Don't rely on hover effects for explanations. This won't work well on mobile (especially if clicking on a data point already has another action tied to it), and also makes it less accessible for people who rely on screen readers or have mobility issues.

Accessible data visualisations with D3.js | Sarah L. Fossheim

A comprehensive guide to ensuring simple charts and graphs are accessible. Focuses on D3, but most of the tips and suggestions can easily be ported to any visual UI. These include:

  • Ensuring graph regions (e.g. pie slices, bars, lines) use enough colour contrast and colours that are distinct enough regardless of colour vision needs (i.e. colour blindness);
  • Using textures and patterns within graph regions where colours are not distinct enough;
  • Inclusion of a clear legend;
  • Use of a title and summary description;
  • Visible data values and axes titles;
  • Use of "ticks" for the y-axis so that it's easier to compare bars.

The final outcome looks like this:

A bar chart showing daily visitor numbers to Sarah's website. There is a headline and description at the top, detailing the period the data covers (the first half of 2020) and stating that days which had less than 100 visitors are highlighted. Below this is a legend with two entries: blue with a checked pattern for over 100 daily visitors; red with a lined pattern for under 100 visitors. The chart itself has labelled x and y axes; the y shows amount of visitors, with marked lines at 100, 200, and 300. The x-axis shows the days of the week, from Monday to Sunday in order. Each bar on the chart is colour-coded as per the legend and shows a data value at the top. These are: Monday, 100; Tuesday, 174; Wednesday, 92; Thursday, 193; Friday, 103; Saturday, 104; Sunday, 294.
It's frankly impressive how many people read Sarah's blog on Sundays! Must get that Sunday morning, breakfast-in-bed, RSS crowd 😂

On using texture or patterns as well as colours to distinguish graph regions:

If you want to be extra safe, or can't avoid using colors that meet the guidelines when it comes to colorblindness, you can also add patterns to your graphs. Make sure to not overdo it and go for calm patterns, otherwise the graph might become too busy on the eyes as well.

On where to best position legend elements:

Screen readers read the DOM elements in the order that they appear in your code. So in my example, I added the code for the legend on top, before the code for the x-axis

Accessibility, data visualisations, & the Norwegian 2023 election | Sarah L. Fossheim

An excellent write-up of where election maps, result graphs, and other common data visualisations used in election reporting most commonly fall down. Missing descriptive text, unassociated labels, inaccessible toggles and interactive elements. Unfortunately doesn't go into much detail on solutions, but interesting screen reader recordings highlight some of the main roadblocks.

On the inherent difficulties with working with data on the web:

Data visualizations typically are inaccessible by default. If we don't actively curate accessibility of our charts, we will end up with visualizations that are varying levels of inaccessible.

Accessible D3 donut charts | A11y With Lindsey

A solid tutorial on making an accessible donut chart with D3. A couple of options are discussed, along with the pros/cons of each. I personally prefer the first option, where the labels and data are intrinsically tied together.

On creating accessible tooltips that can be accessed by mouse and keyboard events:

To make tooltips accessible, we have to do a few things:

  1. Add a tabindex="0" to all the arc paths so that we can tab to it.
  2. Add an aria-describedby to each selected path with a unique value
  3. Add an id to the tooltip that matched the aria-describedby value. Because each id must be unique, we have to create multiple tooltips.
  4. Make sure that we add not only mouse events, but focus and blur events.

On ways to remove the redundant ARIA roles in SVGs, which are acting more like Canvas elements:

I learned through manual testing is that every single path element (the arcs) had a role of Image. And it announces on a screenreader. As a result, I’m going to put a role="presentation" on each path.

Accessible chart & data library | High Charts

Easily the most comprehensive and impressive charting and data visualisation library I've come across. Graphs and charts are keyboard accessible, well-labelled, meaningfully marked up, with colour options that help ensure contrast levels and much more. Heck, there are options for a user to generate a data table (where appropriate) and it just appears below the graph, with full HTML semantics. The only shame here is the cost, but you absolutely get what you're paying for 👏

As an aside, High Charts were recommended after some quick accessibility testing of other popular React options (the following conclusions are based on examples from each respective tool's demo page, assumed to be "as good as it gets":

  • Apex Charts: Nice visuals, good colour and texture options, and legend text is at least targetable, but relationships between values and labels are missing, popovers/tooltips are completely inaccessible, no keyboard support, and data values which are not visible cannot be uncovered by assistive tech. Verdict: meh.
  • Chart.js: Charts are surfaced as "clickable images" with more "clickable images" nested within. Keyboard support is patchy and buggy (I could jump around some graphs, but would get stuck at times). Data and labels are not paired. In fairness, the official recommendation here is that you add your own accessibility onto the Canvas element, but that's a suboptimal hack at best. Verdict: poor.
  • Google Charts (specifically the react-google-charts package): Legend is navigable with AT, but for some reason identifies as a task list. No real keyboard support. Text labels are accessible, but not paired to data meaningfully; any data points without visible labels cannot have their values surfaced, as tooltips are inaccessible. Charts have titles and at least some capability to provide accessible colour choices. Relatively well marked up and easy to skip around using AT, though (again) some values are simply impossible to get to without a mouse and visual browsing. Verdict: average but still inaccessible.
  • Nivo: In my opinion, the best looking. Excellent colour and texture options. Some nice accessibility affordances. Data is still not adequately associated with labels, though these are at least all accessible as text nodes. Unfortunately, the result is gibberish without visual guidance – strings of labels, legends, data values all given in blocks with no additional context. Verdict: better but still inaccessible.

Why not "simply" provide a text alternative? Make the chart invisible to AT using ARIA and stick a label on the wrapping element? That's definitely an improvement over the examples given, and at a pinch (if you're stuck with any of the above libraries) would be my suggestion. I also think – if done well – that this would meet WCAG SC 1.1.1 requirements. But if the graph accepts user control (which it likely should, if you are relying on popovers or similar techniques) then I don't think this goes far enough, and full keyboard control, contextual labelling, etc. would be needed as well.

We're still not innovating with AI-generated UI | Heather Buchel

With the recent announcement of an AI-UI tool from the folks at Vercel, there's been a lot of discussion about claims around "production-ready code" that is actually riddled with bugs, lacks semantics, and fails to meet even basic accessibility and UX criteria. Heather has written some solid thoughts around how not only is this not as innovative or new as is being touted, but that we're doomed to keep making these mistakes until actual UI experts are involved, and UI requirements like accessibility are baked in from the ground up:

No tool that asserts you can build production-grade UI code from their AI is innovative if it's not driven by accessibility. It's also not production-grade if the code it generates is inaccessible. That is the short and sweet of it.

On the reality of (and best application for) these tools, POCs and design/engineering discussions:

In the end, all we've built is a really high fidelity mockup of what the UI might look like.

Track design system use & adoption | Luro

A new tool that allows an organisation to check how, where, and why components are being used across shared codebases, and gain insights into their usage and performance.

Particularly important for determining how design system adoption is going and therefore where potential pain points might be. It also integrates with Storybook and other embeddable component libraries; Figma and other design tools; and runs industry-standard analytics on your sites at a component and page level (Lighthouse etc.). The result is a dashboard of key information, analytics, and documentation on every component and composed architecture, which sounds incredibly powerful and pretty darn cool.

📆 19 Sep 2023  | 🔗

  • Nuts & Bolts
  • Luro
  • design system
  • Figma
  • Storybook
  • documentation
  • analytics
  • Lighthouse 

Echoed whisper | Eric Meyer

Analysis of working with Whisper, an AI-driven tool for audio transcriptions; contains some useful information on caption accessibility and guidelines.

On optimal caption lengths:

The BBC recommends line lengths at or below 37 characters, and Netflix suggests a limit of 42 characters, with actual hard limits for a few languages.

Made By Me, But Made Possible By:

CMS:

Build: Gatsby

Deployment: GitHub

Hosting: Netlify

Connect With Me:

Twitter Twitter

Instagram Instragram

500px 500px

GitHub GitHub

Keep Up To Date:

All Posts RSS feed.

Articles RSS feed.

Journal RSS feed.

Notes RSS feed.