When working with components that need to work “across borders”, supporting multiple languages and writing-modes, you'll need a large “todo-checklist”, or you'll get lost sooner or later.
In this tutorial — which is one big todo-list — we'll be creating a Timeline-component, that can be configured in multiple ways, supports dir="ltr"
and dir="rtl"
— and much, much more.
Let's dive in.
Table Of Contents
- Markup should be minimal and semantically correct
- Does it work with both
dir="ltr"
anddir="rtl"
? - Should the markup be enriched with microdata?
- Is it navigable by keyboard?
- Does it have focus-styles?
- What about
:hover
? - Does it scale with longer content?
- Scrolling and snapping
- Testing with Dev Tools
- Checking Validity and Document Outline
- Bonus: A News Timeline
- Conclusion
- Code Examples
Markup should be minimal and semantically correct
Timelines are typically lists, build with <ul>
and <li>
-tags. But do you always need a list? If the first thing you do, is adding list-style: none;
to your CSS, are you using the correct tag?
I've worked on quite a few projects, where someone decided to “CSS reset” all list-styles:
ul,
ol {
list-style: none;
}
This is really annoying, because I typically want to show a list when I'm using <ul>
or <ol>
-tags, otherwise I'd chosen different tags!
Andy Bell's “CSS reset” is much nicer:
/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
ul[role="list"],
ol[role="list"] {
list-style: none;
}
In our first Timeline-example, we'll be using plain <a>
nchor-tags, since the timeline only contains <a>
nchors pointing to locations within the same document.
In CSS naming, we'll consider the <a>
nchors as a type of list-items anyway, and use the class tmln__item
— and tmln__list
for the “list wrapper”:
<nav class="tmln">
<h2 class="tmln__header">Timeline</h2>
<div class="tmln__list">
<a class="tmln__item" href="#2021"><span data-title>2021</span></a>
<a class="tmln__item" href="#2020"><span data-title>2020</span></a>
<a class="tmln__item" href="#2019"><span data-title>2019</span></a>
<a class="tmln__item" href="#2018"><span data-title>2018</span></a>
/* etc. */
</div>
</nav>
Here's the default, dir="ltr"
version:
Does it work with both dir="ltr"
and dir="rtl"
?
Instead of writing unique CSS for both text-directions, use CSS Logical Properties. I've written about them here. Here's the dir="rtl"
version:
Before we continue, let's add a horizontal version. We'll add a modifier to the main element, tmln--hr
:
Remeber to check the dir="rtl"
version:
Should the markup be enriched with microdata?
While we're still working with the markup, let's consider whether we can enrich the markup by adding microdata, aka schema.org, telling search-engines in more detail about the content.
Is your content a list of Events or News Articles?
Google calls schemas for “Rich Results”, and have created a testing-tool, where you can either paste a url or markup.
Is it navigable by keyboard?
In this case, because we used <a>
-tags, it's navigable by keyboard by default. If you'd used a <div>
-tag and added a click-handler with JavaScript, you'd have to add tabindex="0"
for it to recieve keyboard-focus (but please: don't go there!)
Does it have focus-styles?
To make it usable for keyboard-users, we'll add some styles using focus-visible
, thus not triggering the style, when using a pointer-device (mouse or touch):
I'm gonna jump ahead to show an example from the next type of Timeline, we'll be creating — just to show you how focus-within
can be used to target parent-elements of focusable elements.
In this case, a box-shadow
is added to the bullet, a subtle box-shadow
is added to the main box, and a dotted outline
to the link itself:
What about :hover
?
Should :hover
work intentionally on mobile devices (it acts like a “pseudo-click”), or should it be disabled?
If you only want to show :hover
-styles on devices that actually support them (recommended), use:
@media (hover: hover) { ... }
Does it scale with longer content?
Some languages take up much more space than others:
Language | Translation | Ratio |
---|---|---|
Korean | 조회 | 0.8 |
English | views | 1 |
Chinese | 次檢視 | 1.2 |
Portuguese | visualizações | 2.6 |
French | consultations | 2.6 |
German | mal angesehen | 2.8 |
Italian | visualizzazioni | 3 |
Check with various text-length (or use Google Translate live on your content) — depending on your layout, look into min-width
(or min-inline-size
), fit-content
or similar.
Scrolling and snapping
If your content overflows (like our horizontal timeline), do not hide the default scrollbar (it will be hidden on mobile devices, though — but that's expected). The browsers default scrollbar can be navigated by keyboard, using the arrow-keys. You're welcome to style it, though:
For a nicer design, you can set the scrollbar-buttons, ::-webkit-scrollbar-button
in WebKit, to the same color as the background:
As always, remember to check rtl
:
On mobile devices, add “scroll-snap” to the parent:
.tmln__list {
overflow-x: auto;
scroll-snap-type: x mandatory;
}
On the items, add this:
.tmln__item {
scroll-snap-align: start;
scroll-margin-inline-start: value;
}
Testing with Dev Tools
Before we continue, let's check our component in Lighthouse:
Wow - looks good so far!
Now is also a good time to check CSS Coverage.
Open the drawer in Chrome Dev Tools by pressing Escape (if it's not already open). Add/Check the “Coverage”-tab:
Hmm ... there's 9.9% unused CSS ... Let's check:
Ah! It's because the CSS is unminified and contains comments.
When building your own components, look through the entire file, to check, whether you have unused CSS.
Checking Validity and Document Outline
Although Lighthouse finds most issues, I always validate the markup — check the console for errors, and fix them.
To see a visualization of the Document Outline, I use HTML5 Outliner, a plugin for Chrome:
Tip! Add a headline (
<h1>
to<h6>
) to your<nav>
-tags to prevent a bunch ofuntitled NAV
-entries in the outliner.
Bonus: A News Timeline
Now, let's look into a News Timeline. In this case, we do need a list, so we'll replace the <div>
and <a>
nchors with <ul>
and <li>
-items:
<nav class="tmln tmln--box tmln--hr">
<h2 class="tmln__header">Latest News</h2>
<ul class="tmln__list">
<li class="tmln__item tmln__item--active">
<span data-title>32 mins ago</span>
<h3 class="tmln__item-headline">Someone, somewhere, did something stupid</h3>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias, ab? Sequi dolorem aspernatur ad earum! Eius nulla tempora pariatur temporibus.</p>
</li>
</ul>
</nav>
And again, let's make sure the dir="rtl"
version works as well:
Now, let's mix the horizontal and the box-versions:
<nav class="tmln tmln--box tmln--hr">
And … let's check the dir="rtl"
version:
Re-check the list
At this point, we need to review all the steps from the regular Timeline Component again, add or edit focus-states etc.:
Conclusion
Wow — you made it to the end! If you're primarily a JavaScript-developer, you might wonder why I:
- Tagged this article with
JavaScript
†) - Chose the tags I did (instead of just using
<div>
s for everything). Here's a screenshot demonstrating why chosing the correct HTML-tags matters, if CSS for some reason fails:
†) Because of 2 😁
Code Examples
Here's a Codepen with examples:
Cover photo by Andrey Grushnikov from Pexels
Top comments (0)