On a whimsical Sunday afternoon, I decided to brush up with React. I made the excellent choice of reviewing this talk from Kent C. Dodds, and planned out a relatively straightforward project to explore the different abstractions provided by the framework: build a reusable component to share a quote, or a tweet.

I sketched out the component and quickly realized I was going to practice with a lot that wasn’t React.

this project could sure use a few SVG icons…

uh, I seem to remember there’s a <time> element that would be perfect to display the timestamp...

you know what, this layout is made to use grid properties...

I thought about finding a new idea to just practice with the framework, but luckily enough, I was reminded of this insightful article from Chris Coyier. I realized that my goal was to practice with React, sure, but React as an abstraction to what is ultimately shipped to the browser: HTML, CSS and just enough JavaScript to make the component reusable.

In light of this I was convinced to shift the focus of the project, from the JavaScript library to the building blocks behind the component.

Here’s what I came up with:

And here a few notes on the front-end concepts packed in the very same design.

SVG Syntax

Vector graphics are included repeatedly in the project:

  • the cheerful avatar at the beginning of the tweet;

  • the decorative icons below the tweet’s actual message;

  • the repeating background overlaid on top of a stylish purple-ish hue.

I’ll spare you a rambling on the d attribute of the <path> elements and instead focus on how the graphics are included on the page. If you’re interested, though, here’s a great tutorial from MDN.

Icon set

The icons are described at the very top of the markup, through a series of <symbol> elements. These elements allow to create a template which can be then repeated through <use> elements.

  1. describe the assets in between the opening and closing tags.

    <svg viewBox="...">
        <symbol id="reference">
            <!-- actual icon using path, circle and other drawing elements -->
        </symbol>
    </svg>
    

    Be sure to add a reference through the id attribute.

  2. Include the graphic in the markup which follows.

    <div>
        <svg viewBox="...">
            <use href="#reference" />
        </svg>
    </div>
    

    Notice how the reference is repeated in the href attribute.

There are certainly benefits in adding SVG icons directly inline, but I find this approach to be rather convenient. There's a single <svg> responsible for every possible graphic, and the <use> elements pick up the syntax as much as needed.

Be aware of the following though: while the <symbol> elements are not rendered on the screen, the <svg> container occupies its default size of 300x100. To remove the element from the flow of the document you can set the display property to none and still benefit from the icons described in the element.

  <svg viewBox="..." style="display: none;">
    <symbol id="reference"></symbol>
</svg>

Repeating background

SVG syntax is present again in the stylesheet, and specifically the background property of the body. The idea is to have a basic drawing repeated on top of the solid background.

Starting from the vector graphic describing two lines intersecting each other in a zigzagging motif:

  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 50">
    <g stroke="currentColor" stroke-linecap="square" stroke-linejoin="square" stroke-width="5" fill="none">
        <path d="M2.5 47.5l47.5-45 47.5 45" />
        <path d="M2.5 2.5l47.5 45 47.5-45" />
    </g>
</svg>

The syntax is included in the url() value in a fixed and unforgiving format, before the very color describing the unassuming background.

  body {
    background: url("data:image/svg+xml;utf8,ADD_SVG_SYNTAX_HERE"), BACKGROUND_UNDERNEATH;
}

The element needs to be included in a single line, and I highly recommend SVGOMG from Jake Archibald to automate this process. The tool optimizes vector graphics in more than just removing unnecessary whitespace, but I'll let your explore the GUI on your own.

Once included, you can adjust the size, repetition, position of the background through the matching properties. By default, the pattern is repeated both horizontally and vertically, which means that by describing a relatively small size, the zigzagging pattern can rapidly fill the entirety of the page with its pleasing rhythm.

  body {
    background: url("data:image/svg+xml;utf8,SVG_ZIGZAGGING_PATTERN"), hsl(0, 0%, 100%);
    background-size: 100px;
}

Just be careful about the following:

  • always include the xlmns attribute. I presume this is included in most graphic editors, but if you're writing SVG syntax by hand, like a lunatic, you might forget about adding the value to the <svg> element.

    <svg xmlns="http://www.w3.org/2000/svg"></svg>
    
  • escape any hash character, #, with the %23 string. This trips me up rather frequently, for instance when I use a hexadecimal color, like #C70586, and is better explained in this thread.

Semantic Markup

This section started rather innocently, reading about the <time> element on CSS Tricks, but grew to consider the structure of the component as a whole.

Hierarchy

The information baked in the tweet is provided through a series of elements describing their relative importance.

  <h1>Pas</h1>
<h2>@paslepoulet</h2>
<h3>6th November</h3>
<p>Something witty I bet</p>

As if having a conversation with a screen reader, you can imagine how the component relates the name followed by the handle, timestamp and actual message.

aria-hidden

Below the paragraph element, a series of icons are included through the mentioned <use> syntax. These are meant to be purely decorative, which explains the value of the aria-hidden attribute.

  <svg aria-hidden="true" ...>
    <use href="#like"></use>
</svg>

Assistive technologies like screen readers are therefore instructed to ignore the elements.

You might have noticed the vector graphic describing the profile picture does not share the same attribute.

  <svg ...>
    <use href="#avatar"></use>
</svg>

This is a bit of a judgment call based on the fact that the avatar might actually be something of importance. The SVG is included as a placeholder, an extremely stylish one, which can be then substituted by a <figure> element.

<time>

The date described in the <h3> element is actually nested in a <time> element.

  <h3>
    <time datetime="2019-12-31T23:59:59-02:00">
        6th November
    </time>
</h3>

Once again, I'll refer you to the article on CSS Tricks, but the gist of the element is as follows:

  • include a machine-readable format datetime attribute;

  • display something more human-friendly to the screen.

For the component, you can think of the datetime as describing an instance of the date object, for the moment in which the tweet is composed.

CSS Layout

Before diving into the CSS properties describing the appearance of the component, a minor note on the vector graphics included in the markup. You might have noticed each <svg> element has a width and height attribute with a specific size. This size can be actually overwritten in the stylesheet, meaning the following:

  <svg viewBox="0 0 100 100" width="30" height="30">
    <use href="#like"></use>
</svg>

  svg {
    width: 100px;
    height: 100px;
}

Would result in an icon 100 pixels wide and tall.

I still prefer to include the attributes as a fallback, a default which is applied when CSS doesn't intervene with a different value.

On to layout properties.

Block Layout

This is where things start to be more visual, so I'll add a few pens describing the ongoing design.

Without any CSS, the elements are displayed as if in a column, thanks to the block value of the headings, paragraph and div containers.

This is already a satisfying layout, especially for screens with a smaller viewport width: following the order described in the markup, the quote displays the information in an easily digestible flow.

With a bit of style, it almost looks done.

Two important additions though:

  • the elements are spaced vertically by adding a margin-top to every element but the first. You can read more about this technique here on A List Apart, on how this is achieved with the owl selector;

    .tweet > * + * {
        margin-top: 1rem;
    }
    
  • the container nesting the decorative icon is made into a flex container, to have the icons spaced from side to side

    .icons {
        display: flex;
        justify-content: space-between;
    }
    

Looking prettier by the minute.

Grid Layout

The single column layout is already a solid choice, but the project was planned out to display the information in a grid.

  |           | handle   | name   | time  |
| avatar    | message                   |
|           | icons                     |

There are different ways to go about creating and positioning the elements, but for the project at hand, I chose to go with grid-template-areas. In this declarative approach the grid is built out of four columns and three rows.

  .tweet {
    grid-template-areas:
        "avatar handle  name    time"
        "avatar message message message"
        "avatar icons   icons   icons";
}

And each element is assigned a grid-area value describing its position in the layout. For instance and for the main heading:

  .tweet h1 {
    grid-area: name;
}

This covers much of the layout. The avatar and headings are then aligned in the most pleasing manner I could think, and you can rest assured CSS tricks has a a handy reference on the properties being used.

Wrapping things up, we now have two different layouts for the tweet component. As mentioned, the first block-based layout might be preferable for smaller viewports, which means we can specify the grid layout in between a media query.

  /* block layout */

@media (min-width: 550px) {
    /* grid layout */
}

Of course grid properties might not be supported by the viewing device, but that won't stop us from shipping grid properties. In trying to be progressive and all, we can further nest the layout in a support query to make sure that the properties are applied as long as display: grid; is supported.

  /* block layout */

@media (min-width: 550px) {
    @supports (display: grid) {
        /* grid layout */
    }
}

Add to that a couple of niceties, in the form of an accent color when highlighting the text or one of the icons, and the component is complete. A long, arduous journey, leading to a good-looking, hopefully robust design.

React

React ?!

You really didn't think I'd forget about the original goal of this project, right? It might no longer be the single subject of the project, but React still has its role in making the component actually reusable. What's great about adding React at this stage is that we now have a good starting point from which to expand.

Consider this pen, in which I used the library to display the values described by an object in the most befitting elements.

  const data = {
    name: "Pas",
    handle: "paslepoulet",
    time: "2019 10 4 15 30",
    message: "Exploring React's own abstractions, starting with React.createElement()"
};

The design can be now populated with a varying set of values, just by changing the properties of an object. React not only makes the component data-driven though, but introduces a new set of challenges and opportunities.

Too long a paragraph

The length of the message forces the .tweet container to be wider than one would hope. The previous markup used a <br/> element to break the text in two lines, and perhaps we could use JavaScript to add the same element an arbitrary number of words. That's a possible route. Another option, which I personally chose, is actually take advantage of the max-width property, and cap the width of the paragraph directly in CSS.

  .tweet p {
    max-width: 320px;
}

<time> and <time> again

The <time> element has now the possibility to rely on an actual date, which makes the datetime attribute incredibly more useful. How to create a date though? An instance of the date object can be created in a multitude of ways, and this article from Zell Liew has plenty to say about that.

  const date = new Date(2019 10 4 15 30);

Once that's settled, which format to actually display on the screen? The Internationalization API sure looks promising.

  const options = {
    year: "numeric",
    month: "short",
    day: "numeric"
};
const format = new Intl.DateTimeFormat("en-US-u-ca-gregory", options);

Wrap Up

This post began as few notes on a very basic idea, but ended up being more of a novel. I hope you take something out of it, even if that something is adding an xlmns attribute to that SVG background that just won't render.

I certainly discovered plenty, including a new perspective on the tools I use on a project. Without a doubt, it has been a laborious journey, overwhelming at times, but given enough time, rest and reflection, it might just become a rewarding one as well. To be sure, one in which I finally managed to practice with React as an abstraction.

Twice.