DEV Community

Cover image for Creating Global Components: A Checklist With Examples
Mads Stoumann
Mads Stoumann

Posted on

Creating Global Components: A Checklist With Examples

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

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:

ol {
  list-style: none;
Enter fullscreen mode Exit fullscreen mode

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 */
ol[role="list"] {
  list-style: none;
Enter fullscreen mode Exit fullscreen mode

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. */
Enter fullscreen mode Exit fullscreen mode

Here's the default, dir="ltr" version:
Default left-to-right

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:

Default right-to-left

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, 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) { ... }
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

On the items, add this:

.tmln__item {
  scroll-snap-align: start;
  scroll-margin-inline-start: value;
Enter fullscreen mode Exit fullscreen mode

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 of untitled 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>
Enter fullscreen mode Exit fullscreen mode

New Timeline LTR

And again, let's make sure the dir="rtl" version works as well:

News Timeline RTL

Now, let's mix the horizontal and the box-versions:

<nav class="tmln tmln--box tmln--hr">
Enter fullscreen mode Exit fullscreen mode


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.:



Wow — you made it to the end! If you're primarily a JavaScript-developer, you might wonder why I:

  1. Tagged this article with JavaScript †)
  2. 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

Discussion (0)