Tag: Styling

Styling Buttons in WordPress Block Themes

A little while back, Ganesh Dahal penned a post here on CSS-Tricks responding to a tweet that asked about adding CSS box shadows on WordPress blocks and elements. There’s a lot of great stuff in there that leverages new features that shipped in WordPress 6.1 that provide controls for applying shadows to things directly in the Block Editor and Site Editor UI.

Ganesh touched briefly on button elements in that post. I want to pick that up and go deeper into approaches for styling buttons in WordPress block themes. Specifically, we’re going to crack open a fresh theme.json file and break down various approaches to styling buttons in the schema.

Why buttons, you ask? That’s a good question, so let’s start with that.

The different types of buttons

When we’re talking about buttons in the context of the WordPress Block Editor, we have to distinguish between two different types:

  1. Child blocks inside of the Buttons block
  2. Buttons that are nested inside other block (e.g. the Post Comments Form block)

If we add both of these blocks to a template, they have the same look by default.

A black button above a comment form that also contains a black button.

But the markup is very different:

<div class="wp-block-button">   <a class="wp-block-button__link wp-element-button">Button 1</a> </div> <p class="form-submit wp-block-button">   <input name="submit" type="submit" id="submit" class="wp-block-button__link wp-element-button" value="Post Comment">  </p>

As we can see, the HTML tag names are different. It’s the common classes — .wp-block-button and .wp-element-button — that ensure consistent styling between the two buttons.

If we were writing CSS, we would target these two classes. But as we know, WordPress block themes have a different way of managing styles, and that’s through the theme.json file. Ganesh also covered this in great detail, and you’d do well giving his article a read.

So, how do we define button styles in theme.json without writing actual CSS? Let’s do it together.

Creating the base styles

theme.json is a structured set of schema written in property:value pairs. The top level properties are called “sections”, and we’re going to work with the styles section. This is where all the styling instructions go.

We’ll focus specifically on the elements in the styles. This selector targets HTML elements that are shared between blocks. This is the basic shell we’re working with:

// theme.json {   "version": 2,   "styles": {     "elements": {       // etc.     }   } }

So what we need to do is define a button element.

={   "version": 2,   "styles": {     "elements": {       "button": {         // etc.       }     }   } }

That button corresponds to HTML elements that are used to mark up button elements on the front end. These buttons contain HTML tags that could be either of our two button types: a standalone component (i.e. the Button block) or a component nested within another block (e.g. the Post Comment block).

Rather than having to style each individual block, we create shared styles. Let’s go ahead and change the default background and text color for both types of buttons in our theme. There’s a color object in there that, in turn, supports background and text properties where we set the values we want:

{   "version": 2,   "styles": {     "elements": {       "button": {         "color": {           "background": "#17a2b8",           "text": "#ffffff"         }       }     }   } }

This changes the color of both button types:

A light blue button above a comment form that also contains a light blue button.

If crack open DevTools and have a look at the CSS that WordPress generates for the buttons, we see that the .wp-element-button class adds the styles we defined in theme.json:

.wp-element-button {   background-color: #17a2b8;   color: #ffffff; }

Those are our default colors! Next, we want to give users visual feedback when they interact with the button.

Implementing interactive button styles

Since this is a site all about CSS, I’d bet many of you are already familiar with the interactive states of links and buttons. We can :hover the mouse cursor over them, tab them into :focus, click on them to make them :active. Heck, there’s even a :visited state to give users a visual indication that they’ve clicked this before.

Those are CSS pseudo-classes and we use them to target a link’s or button’s interactions.

In CSS, we might style a :hover state like this:

a:hover {   /* Styles */ }

In theme.json, we’re going to extend our existing button declaration with these pseudo-classes.

{   "version": 2,   "styles": {     "elements": {       "button": {         "color": {           "background": "#17a2b8",           "text": "#ffffff"         }         ":hover": {           "color": {             "background": "#138496"           }         },         ":focus": {           "color": {             "background": "#138496"           }         },         ":active": {           "color": {             "background": "#138496"           }         }       }     }   } }

Notice the “structured” nature of this. We’re basically following an outline:

  • Elements
    • Element
      • Object
        • Property
          • Value

We now have a complete definition of our button’s default and interactive styles. But what if we want to style certain buttons that are nested in other blocks?

Styling buttons nested in individual blocks

Let’s imagine that we want all buttons to have our base styles, with one exception. We want the submit button of the Post Comment Form block to be blue. How would we achieve that?

This block is more complex than the Button block because it has more moving parts: the form, inputs, instructive text, and the button. In order to target the button in this block, we have to follow the same sort of JSON structure we did for the button element, but applied to the Post Comment Form block, which is mapped to the core/post-comments-form element:

{   "version": 2,   "styles": {     "elements" {       "button": {         // Default button styles       }     }     "blocks": {       "core/post-comments-form": {         // etc.       }     }   } }

Notice that we’re no longer working in elements anymore. Instead, we’re working inside blocks which is reserved for configuring actual blocks. Buttons, by contrast, are considered a global element since they can be nested in blocks, even though they are available as a standalone block too.

The JSON structure supports elements within elements. So, if there’s a button element in the Post Comment Form block, we can target it in the core/post-comments-form block:

{   "version": 2,   "styles": {     "elements" {       "button": {         // Default button styles       }     }     "blocks": {       "core/post-comments-form": {         "elements": {           "button": {             "color": {               "background": "#007bff"             }           }         }       }     }   } }

This selector means that not only are we targeting a specific block — we’re targeting a specific element that is contained in that block. Now we have a default set of button styles that are applied to all buttons in the theme, and a set of styles that apply to specific buttons that are contained in the Post Comment Form block.

A light blue button above a comment form that contans a bright blue button.

The CSS generated by WordPress has a more precise selector as a result:

.wp-block-post-comments-form .wp-element-button, .wp-block-post-comments-form .wp-block-button__link {   background-color: #007bff; }

And what if we want to define different interactive styles for the Post Comment Form button? It’s the same deal as the way we did it for the default styles, only those are defined inside the core/post-comments-form block:

{   "version": 2,   "styles": {     "elements" {       "button": {         // Default button styles       }     }     "blocks": {       "core/post-comments-form": {         "elements": {           "button": {             "color": {               "background": "#007bff"             }             ":hover": {               "color": {                 "background": "#138496"               }             },             // etc.           }         }       }     }   } }

What about buttons that are not in blocks?

WordPress automagically generates and applies the right classes to output these button styles. But what if you use a “hybrid” WordPress theme that supports blocks and full-site editing, but also contains “classic” PHP templates? Or what if you made a custom block, or even have a legacy shortcode, that contains buttons? None of these are handled by the WordPress Style Engine!

No worries. In all of those cases, you would add the .wp-element-button class in the template, block, or shortcode markup. The styles generated by WordPress will then be applied in those instances.

And there may be some situations where you have no control over the markup. For example, some block plugin might be a little too opinionated and liberally apply its own styling. That’s where you can typically go to the “Advanced” option in the block’s settings panel and apply the class there:

A WordPress block settings panel with the Advanced settings expanded highlighting the CSS classes section in red.

Wrapping up

While writing “CSS” in theme.json might feel awkward at first, I’ve found that it becomes second nature. Like CSS, there are a limited number of properties that you can apply either broadly or very narrowly using the right selectors.

And let’s not forget the three main advantages of using theme.json:

  1. The styles are applied to buttons in both the front-end view and the block editor.
  2. Your CSS will be compatible with future WordPress updates.
  3. The generated styles work with block themes and classic themes alike — there’s no need to duplicate anything in a separate stylesheet.

If you have used theme.json styles in your projects, please share your experiences and thoughts. I look forward to reading any comments and feedback!

Styling Buttons in WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , ,

Conditionally Styling Selected Elements in a Grid Container

Calendars, shopping carts, galleries, file explorers, and online libraries are some situations where selectable items are shown in grids (i.e. square lattices). You know, even those security checks that ask you to select all images with crosswalks or whatever.


I found a neat way to display selectable options in a grid. No, not recreating that reCAPTCHA, but simply being able to select multiple items. And when two or more adjoining items are selected, we can use clever :nth-of-type combinators, pseudo elements, and the :checked pseudo-class to style them in a way where they look grouped together.

The whole idea of combinators and pseudos to get the rounded checkboxes came from a previous article I wrote. It was a simple single-column design:

This time, however, the rounding effect is applied to elements along both the vertical and horizontal axes on a grid. You don’t have to have read my last article on checkbox styling for this since I’m going to cover everything you need to know here. But if you’re interested in a slimmed down take on what we’re doing in this article, then that one is worth checking out.

Before we start…

It’ll be useful for you to take note of a few things. For example, I’m using static HTML and CSS in my demo for the sake of simplicity. Depending on your application you might have to generate the grid and the items in it dynamically. I’m leaving out practical checks for accessibility in order to focus on the effect, but you would definitely want to consider that sort of thing in a production environment.

Also, I’m using CSS Grid for the layout. I’d recommend the same but, of course, it’s only a personal preference and your mileage may vary. For me, using grid allows me to easily use sibling-selectors to target an item’s ::before and ::after pseudos.

Hence, whatever layout standard you might want to use in your application, make sure the pseudos can still be targeted in CSS and ensure the layout stays in tact across different browsers and screens.

Let’s get started now

As you may have noticed in the earlier demo, checking and unchecking a checkbox element modifies the design of the boxes, depending on the selection state of the other checkboxes around it. This is possible because I styled each box using the pseudo-elements of its adjacent elements instead of its own element.

The following figure shows how the ::before pseudo-elements of boxes in each column (except the first column) overlap the boxes to their left, and how the ::after pseudo-elements of boxes in each row (except the first row) overlap the boxes above.

Two grids of checkboxes showing the placement of before and after pseudos.

Here’s the base code

The markup is pretty straightforward:

<main>   <input type=checkbox>    <input type=checkbox>    <input type=checkbox>   <!-- more boxes --> </main>

There’s a little more going on in the initial CSS. But, first, the grid itself:

/* The grid */ main {   display: grid;   grid:  repeat(5, 60px) / repeat(4, 85px);   align-items: center;   justify-items: center;   margin: 0; }

That’s a grid of five rows and four columns that contain checkboxes. I decided to wipe out the default appearance of the checkboxes, then give them my own light gray background and super rounded borders:

/* all checkboxes */ input {   -webkit-appearance: none;   appearance: none;   background: #ddd;   border-radius: 20px;   cursor: pointer;   display: grid;   height: 40px;   width: 60px;   margin: 0; }

Notice, too, that the checkboxes themselves are grids. That’s key for placing their ::before and ::after pseudo-elements. Speaking of which, let’s do that now:

/* pseudo-elements except for the first column and first row */ input:not(:nth-of-type(4n+1))::before, input:nth-of-type(n+5)::after {   content: '';           border-radius: 20px;   grid-area: 1 / 1;   pointer-events: none; }

We’re only selecting the pseudo-elements of checkboxes that are not in the first column or the first row of the grid. input:not(:nth-of-type(4n+1)) starts at the first checkbox, then selects the ::before of every fourth item from there. But notice we’re saying :not(), so really what we’re doing is skipping the ::before pseudo-element of every fourth checkbox, starting at the first. Then we’re applying styles to the ::after pseudo of every checkbox from the fifth one.

Now we can style both the ::before and ::after pseudos for each checkbox that is not in the first column or row of the grid, so that they are moved left or up, respectively, hiding them by default.

/* pseudo-elements other than the first column */ input:not(:nth-of-type(4n+1))::before {    transform: translatex(-85px); }  /* pseudo-elements other than the first row */ input:nth-of-type(n+5)::after {  transform: translatey(-60px);  }

Styling the :checked state

Now comes styling the checkboxes when they are in a :checked state. First, let’s give them a color, say a limegreen background:

input:checked { background: limegreen; }

A checked box should be able to re-style all of its adjacent checked boxes. In other words, if we select the eleventh checkbox in the grid, we should also be able to style the boxes surrounding it at the top, bottom, left, and right.

A four-by-five grid of squares numbered one through 20. 11 is selected and 7, 10, 12, and 15 are highlighted.

This is done by targeting the correct pseudo-elements. How do we do that? Well, it depends on the actual number of columns in the grid. Here’s the CSS if two adjacent boxes are checked in a 5⨉4 grid:

/* a checked box's right borders (if the element to its right is checked) */ input:not(:nth-of-type(4n)):checked + input:checked::before {    border-top-right-radius: 0;    border-bottom-right-radius: 0;    background: limegreen; } /* a checked box's bottom borders (if the element below is checked) */ input:nth-last-of-type(n+5):checked + * + * + * + input:checked::after {   border-bottom-right-radius: 0;   border-bottom-left-radius: 0;   background: limegreen; } /* a checked box's adjacent (right side) checked box's left borders */ input:not(:nth-of-type(4n)):checked + input:checked + input::before {            border-top-left-radius: 0;    border-bottom-left-radius: 0;    background: limegreen; } /* a checked box's adjacent (below) checked box's top borders */ input:not(:nth-of-type(4n)):checked + * + * + * +  input:checked + input::before {    border-top-left-radius: 0;    border-top-right-radius: 0;    background: limegreen; }

If you prefer you can generate the above code dynamically. However, a typical grid, say an image gallery, the number of columns will be small and likely a fixed number of items, whereas the rows might keep increasing. Especially if designed for mobile screens. That’s why this approach is still an efficient way to go. If for some reason your application happens to have limited rows and expanding columns, then consider rotating the grid sideways because, with a stream of items, CSS Grid arranges them left-to-right and top-to-bottom (i.e. row by row).

We also need to add styling for the last checkboxes in the grid — they’re not all covered by pseudo-elements as they are the last items in each axis.

/* a checked box's (in last column) left borders */ input:nth-of-type(4n-1):checked + input:checked {   border-top-left-radius: 0;   border-bottom-left-radius: 0; } /* a checked box's (in last column) adjacent (below) checked box's top borders */ input:nth-of-type(4n):checked + * + * + * + input:checked {   border-top-left-radius: 0;   border-top-right-radius: 0; }

Those are some tricky selectors! The first one…

input:nth-of-type(4n-1):checked + input:checked

…is basically saying this:

A checked <input> element next to a checked <input> in the second last column.

And the nth-of-type is calculated like this:

4(0) - 1 = no match 4(1) - 1 = 3rd item 4(2) - 1 = 7th item 4(3) - 1 = 11th item etc.

So, we’re starting at the third checkbox and selecting every fourth one from there. And if a checkbox in that sequence is checked, then we style the checkboxes adjacent, too, if they are also checked.

And this line:

input:nth-of-type(4n):checked + * + * + * + input:checked

Is saying this:

An <input> element provided that is checked, is directly adjacent to an element, which is directly adjacent to another element, which is also directly adjacent to another element, which, in turn, is directly adjacent to an <input> element that is in a checked state.

What that means is we’re selecting every fourth checkbox that is checked. And if a checkbox in that sequence is checked, then we style the next fourth checkbox from that checkbox if it, too, is checked.

Putting it to use

What we just looked at is the general principle and logic behind the design. Again, how useful it is in your application will depend on the grid design.

I used rounded borders, but you can try other shapes or even experiment with background effects (Temani has you covered for ideas). Now that you know how the formula works, the rest is totally up to your imagination.

Here’s an instance of how it might look in a simple calendar:

Again, this is merely a rough prototype using static markup. And, there would be lots and lots of accessibility considerations to consider in a calendar feature.

That’s a wrap! Pretty neat, right? I mean, there’s nothing exactly “new” about what’s happening. But it’s a good example of selecting things in CSS. If we have a handle on more advanced selecting techniques that use combinators and pseudos, then our styling powers can reach far beyond the styling one item — as we saw, we can conditionally style items based on the state of another element.

Conditionally Styling Selected Elements in a Grid Container originally published on CSS-Tricks. You should get the newsletter.


, , , , ,

Fun Times Styling Checkbox States

We might leave a text input unstyled. We might leave a link unstyled. Even a button. But checkboxes… we don’t leave them alone. That’s why styling checkboxes never gets old.

Although designing checkboxes is not that complicated, we also don’t have to settle for simple background color changes, or adding and removing borders, to indicate state changes. We also don’t have to pull out any fancy design skills — that we don’t possess — to make this work. I’ll show you how.


In the following demos, the checkboxes pretty much have the same three-stack layout — at the bottom is a checkbox, and on top of it are two stacked elements, or pseudo-elements. The checkbox is indicated as checked or unchecked depending on which of the two is visible.

If you look at the CSS code in the pens you’ll notice all the layouts — including the one for the checkboxes — are grids. You can use other layouts that feel right for your use case (and learn more in the CSS-Tricks Grid Guide). Additional notes on code and design alternatives are at the end of the source code inside the pens.

In addition, any elements stacked on top of the checkbox have pointer-events: none so they don’t prevent users from clicking or tapping the checkbox.

Let’s now get to the first method.

Idea 1: Blended backgrounds as a state

Blending in CSS is a versatile technique. Manipulating colors relative to two or more elements or backgrounds can be handy in contexts you might not have thought of.

One such instance is the checkbox.

<input id="un" type="checkbox"> <label for="un">un</label> <!-- more checkboxes --> 
input[type=checkbox]::before, input[type=checkbox]::after {   mix-blend-mode: hard-light;   pointer-events: none;   /* more style */ } input[type=checkbox]::before {   background: green;   content: '✓';   color: white;   /* more style */ } input[type=checkbox]::after {   background: blue;   content: '⨯';   /* more style */ } input[type=checkbox]:checked::after {   mix-blend-mode: unset;   color: transparent; }

In this demo, I’ve styled the checkbox’s pseudo-elements green and blue, stacked them up, and gave them each a mix-blend-mode value. This means the background of each element blends with its backdrop.

I used the hard-light value, which emulates the result of either multiply or screen depending on if the top color is darker or lighter. You can learn in depth about different blend modes over at MDN.

When the box is checked, the ::after pseudo-element’s mix blend mode value is unset, resulting in a different visual.

Idea 2: Make a 3D animation

Animating a block of color is fun. Make them seem 3D and it’s even better. CSS has the means to render elements along an emulated 3D space. So using that, we make a 3D box and rotate it to indicate the checkbox state change.

<div class="c-checkbox">   <input type="checkbox" id="un">   <!-- cube design -->   <div><i></i><i></i><i></i><i></i></div> </div> <label for="un">un</label> <!-- more checkboxes -->
.c-checkbox > div {   transition: transform .6s cubic-bezier(.8, .5, .2, 1.4);   transform-style: preserve-3d;   pointer-events: none;   /* more style */ } /* front face */ .c-checkbox > div > i:first-child {   background: #ddd;   transform:  translateZ( -10px ); } /* back face */ .c-checkbox > div > i:last-child {   background: blue;   transform:  translateZ( 10px ); } /* side faces */ .c-checkbox > div > i:nth-of-type(2), .c-checkbox > div > i:nth-of-type(3) {   transform: rotateX(90deg)rotateY(90deg);   position: relative;   height: 20px;   top: 10px; } .c-checkbox > div > i:nth-of-type(2) {   background: navy;   right: 20px; } .c-checkbox > div > i:nth-of-type(3) {   background: darkslategray;   left: 20px; }

The <div> after the checkbox becomes a container of a 3D space — its child elements can be placed along the x, y and z axes — after it’s given transform-style: preserve-3d;.

Using the transform property, we place two <i> elements (grey and blue colored) with some distance between them across the z-axis. Two more are wedged between them, covering their left and right sides. It’s like a cardboard box that’s covered except at the top and bottom.

When the checkbox is checked, this grey and blue box is rotated sideways to face the other side. Since I’ve already added a transition to the <div>, its rotation is animated.

input:checked + div {    transform: rotateY( 180deg );  }

Idea 3: Playing with border radius

Changing a checked box’s border radius? Not that fun. Changing also the border radius of other boxes near it? Now we have something.

<input type="checkbox" id="un"> <label for="un">un</label> <!-- more rows of checkboxes -->
input {   background: #ddd;   border-radius: 20px;   /* more style */ } input:not(:first-of-type)::before {   content: '';       transform: translateY(-60px); /* move up a row */   pointer-events: none; } input:checked + * + input::before, input:last-of-type:checked {   border-radius: 20px;   background: blue; } input:checked + * + input:checked + * + input::before {   border-top-left-radius: unset !important;   border-top-right-radius: unset !important; } input:checked::before {   border-bottom-left-radius: unset !important;   border-bottom-right-radius: unset !important; } /* between the second-last and last boxes */  input:nth-of-type(4):checked + * + input:checked {   border-top-left-radius: unset;   border-top-right-radius: unset; }

If you’d just interacted with the demo before, you’ll notice that when you click or tap a checkbox, it not only can change its own borders but also the borders of the boxes after and before it.

Now, we don’t have selectors that can select elements prior, only the ones after. So what we did to control the appearance of a preceding box is use the pseudo-element of a checkbox to style the box before it. With exception of the first box, every other box gets a pseudo-element that’s moved to the top of the box before it.

Let’s say boxes A, B and C are one after another. If I click B, I can change the appearance of A by styling B’s pseudo-element, B by styling C’s pseudo-element, and C by styling D’s pseudo-element.

From B, the pseudo-elements of B, C and D are accessible — as long as the next element selector can be used between them in the layout.

The four corners of each checkbox are initially rounded when checked and unchecked. But if a box is checked, the following box’s top corners and preceding box’s bottom corners are straightened (by overriding and removing their border radii).

Idea 4: Using a CSS mask

Toggles, switches… they are also checkboxes as far as the code goes. So we can style the boxes as toggles for this one, and it’s done with a CSS mask, which Chris has written about before. But in a nutshell, it’s a technique where we use an image to filter out portions of its backdrop.

<input type="checkbox"> <div class="skin one"></div> <div class="skin two"></div>
.one.skin {   background: no-repeat center -40px url('photo-1584107662774-8d575e8f3550?w=350&q=100'); } .two.skin {   background: no-repeat center -110px url('photo-1531430550463-9658d67c492d?w=350&q=100');   --mask: radial-gradient(circle at 45px 45px , rgba(0,0,0,0) 40px, rgba(0,0,0,1) 40px);   mask-image: var(--mask); -webkit-mask-image: var(--mask); }

Two skins (displaying landscape photos) are on top of a checkbox. The topmost one gets a mask-image that’s in the shape of a typical toggle switch — a transparent circle at the left, and the rest is a fully opaque color. Through the transparent circle we see the photo below while the rest of the mask image shows the photo at the top.

When a checkbox is clicked, the transparent circle is moved to the right, so we see the image at the top through the circle while the rest shows the photo at the bottom.

input:checked ~ .two.skin {   --mask: radial-gradient(circle at 305px 45px, rgba(0,0,0,1) 40px, rgba(0,0,0,0) 40px);   mask-image: var(--mask); -webkit-mask-image: var(--mask); }

Idea 5: Using box shadow

Let’s end with the simplest — but what I consider to be the most effective — method of them all: an animated inset box-shadow.

<input id="un" type="checkbox"> <label for="un">un</label>
input {   transition: box-shadow .3s;   background: lightgrey;   /* more style */ } input:checked {    box-shadow: inset 0 0 0 20px blue; }

There are some CSS properties that can be animated by default and one of them is box-shadow. This type of subtle animation goes well with a minimalist theme.

That’s it! I hope this sparks some inspiration the next time you find yourself working with checkboxes. CSS gives us so many possibilities to indicate state changes, so have a little fun and please share if you have any interesting ideas.

The post Fun Times Styling Checkbox States appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, , ,

Intrinsic Typography is the Future of Styling Text on the Web

The way we style text hasn’t changed much over the years. There have been numerous advancements to help make things more flexible, like layouts, but in terms of styling, most finite aspects of our designs, like text, remain relatively unchanged. This is especially true of text styling. We write code to style text explicitly for every portion of our layouts, and then, to make it responsive, we write more code to make it work at every breakpoint. This means that, as different areas of text compress and expand, the result is tension — palpable, experiential tension — just before the content breaks. At these places, content suffers from not being sized or spaced well, all the while being supported by overly complicated and brittle code.

Intrinsic typography shifts all this, clearing it away by starting at the code itself to affect the styling. Instead of writing explicit text styles, you define how those styles change in proportion to the text’s area. This enables you to use more flexible text components in more layout variations. It simplifies your code, increasing the opportunities for new layout possibilities. Intrinsic typography works so that text self-adjusts to the area in which it’s rendered. Instead of sizing and spacing text for each component at every breakpoint, the text is given instructions to respond to the areas it is placed in. As a result, intrinsic typography enables designs to be far more flexible, adapting to the area in which it is placed, with far less code.

Typographic superpowers beyond clamp()

The result of using intrinsic typography goes well beyond what is possible with tools like clamp(). Intrinsic typographic styling blends the component portability of element queries with the interpolation control of CSS animations, enabling seamless changes of any value across container widths. This technique enables things that aren’t possible with other CSS techniques, such as fluidly adjusting variable font settings, color, and unitless line-height as an element’s area changes. You also avoid the accessibility pitfalls of clamp() and locks where changing the browser’s default font size shifts your typography out of alignment with your breakpoints when using relative units.

How is this different from responsive typography?

Responsive typography references the viewport to transform text. It does this through media queries, clamp(), or CSS Locks. While these techniques enable granular control of typography across screen sizes, they lack the ability to control typography in different components. This means that, for a page with an array of differently sized content areas, a new headline style would need to be created for each of these areas with a responsive typography approach.

Intrinsic typography doesn’t need all that. With intrinsic typography, a single headline style can be used in all different content areas. Discrete headline styles can be consolidated into one intrinsic headline. This is a distinction similar to that of element queries versus media queries: with element queries it’s possible to bind all of the scaling information to a component, where media queries the styles always reference the viewport.

A series of entries scaling proportionally to the container they are rendered in. The font in this demo is Obviously by OHno Type Co.

The anatomy of an intrinsic style

If we were to take the intrinsic headline styles above and extrude out all the variations within them, it would look like the following:

An extrapolation of an intrinsic style along the Z-axis. As the width of an area of text changes, different cross sections of this extrapolation are used as the styles.

Within larger areas of the page, the text is typeset to be bigger, bolder, and wider. In smaller areas of the page the text is smaller, lighter, and narrower. The area in which a headline is rendered is measured, and then the appropriate slice is taken from this intrinsic headline style to be used for that specific headline.

You may notice a few things about the shape of this extruded headline style. The text goes from being smaller to larger, but the shape itself has curves. This control over how text scales from one point to another is particularly useful as screens get smaller to ensure optimal legibility. Below you can see the same set of styles being applied to two columns of text, one with a curved shape and one with a linear shape. In the curved intrinsic example the text is vastly more legible in more places, in comparison to the example using linear interpolation, where the text becomes too small too quickly.

Two panels of text that share the same start and end styles. However on the right, the styles are interpolated using a Bézier curve to optimize legibility at all sizes.

Through combining the ability to interpolate text styling across sizes and areas of a layout as well as shaping how those settings are interpolated, intrinsic typography gives designers an unprecedented amount of control over how text is rendered at any screen or component size.

Typeset intrinsically

Typetura developed a tool to add intrinsic typesetting functionality to CSS. This tool enables the necessary typographic styles to be written, injecting flexibility where previously there was none. Intrinsic styles are stored in CSS keyframes and change based on the width of a parent element. This enables interpolation of any animatable property across element widths. To reference back to our element queries example, think interpolated element queries.

To set up your keyframes, 0% is equal to a container with of 0px, and keyframe 100% is the maximum container width your styles will cover. This value is 1600px by default. Containers can be defined by adding the class typetura to an element, with the root element as the default container. Child elements will be styled based on the parent context’s width, unless a new context is defined.

@keyframes headline {   0% {     font-size: 1rem;   }   100% {     font-size: 4rem;   } }

To attach these styles to your element, use the custom property --tt-key. Now you can see your first intrinsic style.

@keyframes headline {   0% {     font-size: 1rem;     line-height: 1.1;   }   100% {     font-size: 4rem;     line-height: 1;   } }  .headline {   --tt-key: headline; }

To shape how these styles scale, use the custom property --tt-ease. This property accepts CSS easing functions and keywords. This enables you to rapidly bring up your base font size or taper off headline scaling and spacing. Additionally, we can constrain the range these styles cover with --tt-max to better fit the constraints of your layouts and what the text is used for.

@keyframes headline {   0% {     font-size: 1rem;     line-height: 1.1;   }   100% {     font-size: 4rem;     line-height: 1;   } }  .headline {   --tt-key: headline;   --tt-max: 600;   --tt-ease: ease-in-out; }

The following example shows how flexible your page can be when all the text on it is driven by intrinsic typographic styles; from the root of the document and up. The text can seamlessly transition from a monitor serving a conference room all the way down to the size of a watch — all without media queries. Text styles can also be shared in different modules; for example, the headline at the top of the page and headlines in the next-click area are all driven by the same style. While efficiencies appear immediately at any size of website, they quickly compound: the larger site you have, the more these efficiencies build.

Editorial page demo at a desktop screen size
Editorial page demo at a small tablet screen size
Editorial page demo at a small phone or watch screen size, and no text is getting cut off

Check out this Pen. In it, I’ve added an intrinsic style inspector so you can click on each headline and see what the rendered size is. Within the inspector you can also manipulate the shape of the intrinsic style, and the upper boundary. This allows you to begin to see the typographic styling possibilities for enabled by Typetura.

Intrinsic Typography is the future of styling on the web

Baking these design rules into your content is the practice of intrinsic design, and baking these rules into your text is the practice of intrinsic typography. Intrinsic web design, coined by Jen Simmons, is a concept where common design mutations are baked into the very fabric of our components. Instead of explicitly stating the style of each individual piece of content, intrinsic layouts are given design constraints and our content responds to its environment, as opposed to explicitly defining styles. This approach both simplifies your codebase and enhances the flexibility of your designs, as components have instructions that help them respond to more than just the viewport.

Typetura brings this philosophy into text styling. With text components being our most foundational design material, a material that is reused in almost every component, intrinsic typography has significant advantages over other methodologies. Advantages of design resilience, scalability, and simplification of code exist deeper in your project and extend its lifespan. Scale down to the size of a watch or up to the size of a TV, and where text once limited how far your layout could reach, it now supports your ambitions.

The post Intrinsic Typography is the Future of Styling Text on the Web appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , ,

Styling Web Components

Nolan Lawson has a little emoji-picker-element that is awfully handy and incredibly easy to use. But considering you’d probably be using it within your own app, it should be style-able so it can incorporated nicely anywhere. How to allow that styling isn’t exactly obvious:

What wasn’t obvious to me, though, was how to allow users to style it. What if they wanted a different background color? What if they wanted the emoji to be bigger? What if they wanted a different font for the input field?

Nolan list four possibilities (I’ll rename them a bit in a way that helps me understand them).

  1. CSS Custom Properties: Style things like background: var(--background, white);. Custom properties penetrate the Shadow DOM, so you’re essentially adding styling hooks.
  2. Pre-built variations: You can add a class attribute to the custom elements, which are easy to access within CSS inside the Shadow DOM thanks to the pseudo selectors, like :host(.dark) { background: black; }.
  3. Shadow parts: You add attributes to things you want to be style-able, like <span part="foo">, then CSS from the outside can reach in like custom-component::part(foo) { }.
  4. User forced: Despite the nothing in/nothing out vibe of the Shadow DOM, you can always reach the element.shadowRoot and inject a <style>, so there is always a way to get styles in.

It’s probably worth a mention that the DOM you slot into place is style-able from “outside” CSS as it were.

This is such a funky problem. I like the Shadow DOM because it’s the closest thing we have on the web platform to scoped styles which are definitely a good idea. But I don’t love any of those styling solutions. They all seem to force me into thinking about what kind of styling API I want to offer and document it, while not encouraging any particular consistency across components.

To me, the DOM already is a styling API. I like the scoped protection, but there should be an easy way to reach in there and style things if I want to. Seems like there should be a very simple CSS-only way to reach inside and still use the cascade and such. Maybe the dash-separated custom-element name is enough? my-custom-elemement li { }. Or maybe it’s more explicit, like @shadow my-custom-element li { }. I just think it should be easier. Constructable Stylesheets don’t seem like a step toward make it easier, either.

Last time I was thinking about styling web components, I was just trying to figure out how to it works in the first place, not considering how to expose styling options to consumers of the component.

Does this actually come up as a problem in day-to-day work? Sure does.

I don’t see any particularly good options in that thread (yet) for the styling approach. If I was Dave, I’d be tempted to just do nothing. Offer minimal styling, and if people wanna style it, they can do it however they want from their copy of the component. Or they can “force” the styles in, meaning you have complete freedom.

The post Styling Web Components appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



Two Issues Styling the Details Element and How to Solve Them

In the not-too-distant past, even basic accordion-like interactions required JavaScript event listeners or some CSS… trickery. And, depending on the solution used, editing the underlying HTML could get complicated.

Now, the <details> and <summary> elements (which combine to form what’s called a “disclosure widget”) have made creation and maintenance of these components relatively trivial.

At my job, we use them for things like frequently asked questions.

Pretty standard question/answer format

There are a couple of issues to consider

Because expand-and-collapse interactivity is already baked into the <details> and <summary> HTML tags, you can now make disclosure widgets without any JavaScript or CSS. But you still might want some. Left unstyled, <details> disclosure widgets present us with two issues.

Issue 1: The <summary> cursor

Though the <summary> section invites interaction, the element’s default cursor is a text selection icon rather than the pointing finger you may expect:

We get the text cursor but might prefer the pointer to indicate interaction instead.

Issue 2: Nested block elements in <summary>

Nesting a block-level element (e.g. a heading) inside a <summary> element pushes that content down below the arrow marker, rather than keeping it inline:

Block-level elements won’t share space with the summary marker.

The CSS Reset fix

To remedy these issues, we can add the following two styles to the reset section of our stylesheets:

details summary {    cursor: pointer; }  details summary > * {   display: inline; }

Read on for more on each issue and its respective solution.

Changing the <summary> cursor value

When users hover over an element on a page, we always want them to see a cursor “that reflects the expected user interaction on that element.”

We touched briefly on the fact that, although <summary> elements are interactive (like a link or form button), its default cursor is not the pointing finger we typically see for such elements. Instead, we get the text cursor, which we usually expect when entering or selecting text on a page.

To fix this, switch the cursor’s value to pointer:

details summary {    cursor: pointer; }

Some notable sites already include this property when they style <details> elements. The MDN Web Docs page on the element itself does exactly that. GitHub also uses disclosure widgets for certain items, like the actions to watch, star and fork a repo.

GitHub uses cursor: pointer on the <summary> element of its disclosure widget menus. 

I’m guessing the default cursor: text value was chosen to indicate that the summary text can (along with the rest of a disclosure widget’s content) be selected by the user. But, in most cases, I feel it’s more important to indicate that the <summary> element is interactive.

Summary text is still selectable, even after we’ve changed the cursor value from text to pointer. Note that changing the cursor only affects appearance, and not its functionality.

Displaying nested <summary> contents inline

Inside each <summary> section of the FAQ entries I shared earlier, I usually enclose the question in an appropriate heading tag (depending on the page outline):

<details>   <summary>     <h3>Will my child's 504 Plan be implemented?</h3>   </summary>   <p>Yes. Similar to the Spring, case managers will reach out to students.</p> </details>

Nesting a heading inside <summary> can be helpful for a few reasons:

  • Consistent visual styling. I like my FAQ questions to look like other headings on my pages.
  • Using headings keeps the page structure valid for users of Internet Explorer and pre-Chromium versions of Edge, which don’t support <details> elements. (In these browsers, such content is always visible, rather than interactive.)
  • Proper headings can help users of assistive technologies navigate within pages. (That said, headings within <summary> elements pose a unique case, as explained in detail below. Some screen readers interpret these headings as what they are, but others don’t.)

Headings vs. buttons

Keep in mind that the <summary> element is a bit of an odd duck. It operates like a button in many ways. In fact, it even has implicit role=button ARIA mapping. But, very much unlike buttons, headings are allowed to be nested directly inside <summary> elements.

This poses us — and browser and assistive technology developers — with a contradiction:

  • Headings are permitted in <summary> elements to provide in-page navigational assistance.
  • Buttons strip the semantics out of anything (like headings) nested within them.

Unfortunately, assistive technologies are inconsistent in how they’ve handled this situation. Some screen-reading technologies, like NVDA and Apple’s VoiceOver, do acknowledge headings inside <summary> elements. JAWS, on the other hand, does not.

What this means for us is that, when we place a heading inside a <summary>, we can style the heading’s appearance. But we cannot guarantee our heading will actually be interpreted as a heading!

In other words, it probably doesn’t hurt to put a heading there. It just may not always help.

Inline all the things

When using a heading tag (or another block element) directly inside our <summary>, we’ll probably want to change its display style to inline. Otherwise, we’ll get some undesired wrapping, like the expand/collapse arrow icon displayed above the heading, instead of beside it.

We can use the following CSS to apply a display value of inline to every heading — and to any other element nested directly inside the <summary>:

details summary > * {    display: inline; }

A couple notes on this technique. First, I recommend using inline, and not inline-block, as the line wrapping issue still occurs with inline-block when the heading text extends beyond one line.

Second, rather than changing the display value of the nested elements, you might be tempted to replace the <summary> element’s default display: list-item value with display: flex. At least I was! However, if we do this, the arrow marker will disappear. Whoops!

Bonus tip: Excluding Internet Explorer from your styles

I mentioned earlier that Internet Explorer and pre-Chromium (a.k.a. EdgeHTML) versions of Edge don’t support <details> elements. So, unless we’re using polyfills for these browsers, we may want to make sure our custom disclosure widget styles aren’t applied for them. Otherwise, we end up with a situation where all our inline styling garbles the element.

Inline <summary> headings could have odd or undesirable effects in Internet Explorer and EdgeHTML.

Plus, the <summary> element is no longer interactive when this happens, meaning the cursor’s default text style is more appropriate than pointer.

If we decide that we want our reset styles to target only the appropriate browsers, we can add a feature query that prevents IE and EdgeHTML from ever having our styles applied. Here’s how we do that using @supports to detect a feature only those browsers support:

@supports not (-ms-ime-align: auto) {    details summary {      cursor: pointer;   }    details summary > * {      display: inline;   }    /* Plus any other <details>/<summary> styles you want IE to ignore. }

IE actually doesn’t support feature queries at all, so it will ignore everything in the above block, which is fine! EdgeHTML does support feature queries, but it too will not apply anything within the block, as it is the only browser engine that supports -ms-ime-align.

The main caveat here is that there are also a few older versions of Chrome (namely 12-27) and Safari (macOS and iOS versions 6-8) that do support <details> but don’t support feature queries. Using a feature query means that these browsers, which account for about 0.06% of global usage (as of January 2021), will not apply our custom disclosure widget styles, either.

Using a @supports selector(details) block, instead of @supports not (-ms-ime-align: auto), would be an ideal solution. But selector queries have even less browser support than property-based feature queries.

Final thoughts

Once we’ve got our HTML structure set and our two CSS reset styles added, we can spruce up all our disclosure widgets however else we like. Even some simple border and background color styles can go a long way for aesthetics and usability. Just know that customizing the <summary> markers can get a little complicated!

The post Two Issues Styling the Details Element and How to Solve Them appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , , ,

Styling Code In and Out of Blocks

We’ll get to that, but first, a long-winded introduction.

I’m still not in a confident place knowing a good time to use native web components. The templating isn’t particularly robust, so that doesn’t draw me in. There is no state management, and I like having standard ways of handling that. If I’m using another library for components anyway, seems like I would just stick with that. So, at the moment, my checklist is something like:

  • Not using any other JavaScript framework that has components
  • Templating needs aren’t particularly complex
  • Don’t need particularly performant re-rendering
  • Don’t need state management

I’m sure there is tooling that helps with these things and more (the devMode episode with some folks from Stencil was good), but if I’m going to get into tooling-land, I’d be extra tempted to go with a framework, and probably not framework plus another thing with a lot of overlap.

The reasons I am tempted to go with native web components are:

  • They are native. No downloads of frameworks.
  • The Shadow DOM is a true encapsulation in a way a framework can’t really do.
  • I get to build my own HTML element that I use in HTML, with my own API design.

It sorta seems like the sweet spot for native web components is design system components. You build out your own little API for the components in your system, and people can use them in a way that is a lot safer than just copy and paste this chunk of HTML. And I suppose if consumers of the system wanted to BYO framework, they could.

So you can use like <our-tabs active-tab="3"> rather than <div class="tabs"> ... <a href="#3" class="tab-is-active">. Refactoring the components certainly gets a lot easier as changes percolate everywhere.

I’ve used them here on CSS-Tricks for our <circle-text> component. It takes the radius as a parameter and the content via, uh, content, and outputs an <svg> that does the trick. It gave us a nice API for authoring that abstracted away the complexity.


It occurred to me a “code block” might be a nice use-case for a web component.

  • The API would be nice for it, as you could have attributes control useful things, and the code itself as the content (which is a great fallback).
  • It doesn’t really need state.
  • Syntax highlighting is a big gnarly block of CSS, so it would be kinda cool to isolate that away in the Shadow DOM.
  • It could have useful functionality like a “click to copy” button that people might enjoy having.

Altogether, it might feel like a yeah, I could use this kinda component.

This probably isn’t really production ready (for one thing, it’s not on npm or anything yet), but here’s where I am so far:

Here’s a thought dump!

  • What do you do when a component depends on a third-party lib? The syntax highlighting here is done with Prism.js. To make it more isolated, I suppose you could copy and paste the whole lib in there somewhere, but that seems silly. Maybe you just document it?
  • Styling web components doesn’t feel like it has a great story yet, despite the fact that Shadow DOM is cool and useful.
  • Yanking in pre-formatted text to use in a template is super weird. I’m sure it’s possible to do without needing a <pre> tag inside the custom element, but it’s clearly much easier if you grab the content from the <pre>. Makes the API here just a smidge less friendly (because I’d prefer to use the <code-block> alone).
  • I wonder what a good practice is for passing along attributes that another library needs. Like is data-lang="CSS" OK to use (feels nicer), and then convert it to class="language-css" in the template because that’s what Prism wants? Or is it better practice to just pass along attributes as they are? (I went with the latter.)
  • People complain that there aren’t really “lifecycle methods” in native web components, but at least you have one: when the thing renders: connectedCallback. So, I suppose you should do all the manipulation of HTML and such before you do that final shadowRoot.appendChild(node);. I’m not doing that here, and instead am running Prism over the whole shadowRoot after it’s been appended. Just seemed to work that way. I imagine it’s probably better, and possible, to do it ahead of time rather than allow all the repainting caused by injecting spans and such.
  • The whole point of this is a nice API. Seems to me thing would be nicer if it was possible to drop un-escaped HTML in there to highlight and it could escape it for you. But that makes the fallback actually render that HTML which could be bad (or even theoretically insecure). What’s a good story for that? Maybe put the HTML in HTML comments and test if <!-- is the start of the content and handle that as a special situation?

Anyway, if you wanna fork it or do anything fancier with it, lemme know. Maybe we can eventually put it on npm or whatever. We’ll have to see how useful people think it could be.

The post Styling Code In and Out of Blocks appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, ,

Styling Comment Threads

Comment threads are one of those things that look really simple when executed right. When designing them yourself, you may find that they are rather deceptively simple. There is a lot that goes into designing nice and usable comment threads, and in this article, I will try my best to walk you through the steps to building a comment thread, that is great to look at, and a joy to use.

What makes a good comment thread?

Before diving into designing and writing code, let’s break down what actually makes a good comment thread. A good comment thread will have the following characteristics:

  1. Comments are readable, and all the important data-points are visible to the user at a glance. This includes the author, number of votes, timestamp, and the content.
  2. Different parts of comments are visually distinct, so that the user is immediately able to understand what is what. This is especially important for the action buttons (e.g. reply, report), and links.
  3. There must be visual cues for hierarchies between the comments. This is a requirement for having nested comments, i.e. one comment being a reply to another one.
  4. There should be an easy way to quickly scroll to a comment, especially to the parent comment from its children (i.e. replies).
  5. Comments should also have a toggle feature which allows the users to hide and show the comment and its replies.

As you can see, that’s quite a lot to consider! There are also some nice-to-haves that we won’t cover in this article, but are certainly good enhancements:

  1. Users should be able to flag a comment so a moderator is aware of inappropriate content.
  2. A comment can be up- or down-voted as a way of signaling helpful and unhelpful comments.
  3. Only the first few comments are displayed and the user is able to load more comments.

The above features would require at least a bit of JavaScript to pull off. Moreover, depending on the tech stack, these features could just as likely be implemented server side, especially keeping track of the number of votes and flag status of comments. That is why we will focus only on styling the comment threads in this article. With that out of the way, let’s knock out the first set of points and design our comment thread.

A basic comment thread

A comment by itself has a pretty simple structure. Here’s a skeleton of a single comment:

A single comment structure

So far, so good. Notice how the replies have an extra margin to the left. This is meant to satisfy the visual hierarchy (point #3 above). The markup for the above structure could look something like this:

<div class="comment">    <!-- Comment heading start -->   <div class="comment-heading">      <!-- Comment voting start -->     <div class="comment-voting">       <button type="button">Vote up</button>       <button type="button">Vote down</button>     </div>     <!-- Comment voting end -->      <!-- Comment info (author, # of votes, time added) start -->     <div class="comment-info">       <a href="#" class="comment-author">{{ author }}</a>       <p>         {{ # of votes }} • {{ time_added }}       </p>     </div>     <!-- Comment info (author, # of votes, time added) end -->   </div>   <!-- Comment heading end -->    <!-- Comment body start -->   <div class="comment-body">     <p>       {{ comment_content }}     </p>     <button type="button">Reply</button>     <button type="button">Flag</button>   </div>   <!-- Comment body end -->    <!-- Replies start -->   <div class="replies">     <!-- ... -->   </div>   <!-- Replies end -->  </div>

There is nothing to explain here — just a bunch of containers with some basic elements. So instead, let’s look at a real-life example, with a comment that also has a reply.

Looks pretty nice, right? The above example satisfies the first three points, so we are already on our way. A few things to understand about the code above:

  • The replies are given an extra margin to the left side to provide a visual cue of the hierarchy.
  • The vote up and vote down buttons are using HTML character codes for the icons.
  • The class .sr-only hides elements on all devices except on screen readers. This makes the voting buttons accessible. If you are using a framework, like Bootstrap or Halfmoon, this class comes automatically packed in.

Adding links that jump to comments

Now that we have a basic comment thread going, let’s add a feature to help users quickly scroll to a comment. As mentioned above, this is especially useful when someone wants to jump to the parent comment of a reply.

In order to build it, we need to decide what these links look like. While this is entirely subjective, one particular design I really like is a clickable “border” on the left hand side of the comment, something like this:

Link (marked in red) that allows users to jump to a comment

In order to accommodate the link, we are pushing the comment body to the right and aligning it with the replies. This design also has the added benefit of reinforcing the hierarchy between the comments, because you can simply look at the number of border links to the left and determine the level of nesting of the comment you are currently reading. And of course, you can also immediately jump to any upper level by clicking on the border links.

To actually create these border links, we need to add anchor elements (<a href="...">) inside each of our comments, and style these anchor elements to look like the borders that can be clicked. Let’s add them to our first code example above.

Here are a few things to understand about the changes made:

  • Anchor links are added inside each of the comments, and these are styled to look like borders on the left-hand side.
  • The body of the comment is now aligned with the replies.
  • The .comment-heading (which contains the votes, author, and time added) has a fixed height of 50px. Therefore, by giving the border links the properties of position:absolute, top: 50px, and height: calc(100% - 50px), we are ensuring that they will start right below the heading, and go all the way down to the end of the comment. If you are not familiar with the calc() function, you can read this cool guide by Chris.
  • The border links have a clipped background, and are also given a width of 12px along with a border-width of 4px on the left and right. This means that while the visible area is only 4px wide, the actual clickable area is 12px wide. A wider surface is to help the users have an easier time actually positioning their pointers on the link and clicking it, because 4px is a little too narrow, but anything wider would look off visually.

With all that, we have knocked out the first four of the points mentioned above. Let’s add more comments to the code example to see how it would look.

Allowing users to hide/show comments with a click

At this point, we have a pretty darn satisfactory comment thread. This design by itself can work for quite a lot of real life use cases. However, let’s go one step farther and add our toggle feature, i.e. hiding and showing comments with a click.

The quickest and easiest way to allow users to hide and show comments with a click is to make use of the <details> and <summary> elements. To put it simply, the visibility of the entire <details> can be toggled by clicking on the <summary>. There is no JavaScript involved, and these tags are supported by ~96% of browsers at this moment. Once again, if you are unfamiliar with these concepts, you can learn more in yet another article from Chris.

Anyway, to actually implement this, we need to make the following changes to our code:

  • Change comments from <div> to <details>, i.e. all the elements with the class .comment will now be a <details> element.
  • Wrap the comment heading (.comment-heading) inside of a <summary> tag.
  • Provide a visual cue to the user to tell them whether a comment is hidden or not.

Seems easy enough. Anyway, here’s our new implementation:

Here are the final things to understand about the changes made:

  • The comments are now <details>, and they are all given the open attribute, so they are visible (i.e. open) by default.
  • The comment headings are now wrapped inside of <summary> tags. The default arrow is also removed.
  • The cue for the visibility status of the comments is mainly created using small text at the right of the comment. The content of this text changes depending on whether the parent <details> element has the open attribute or not. The text itself is a simple pseudo-element created using the ::after selector. Moreover, closed comments also have a border on the bottom to show the users that there is more to see.
  • At the very end of the CSS code, you will find some styles inside the @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {...} selector. This is a hack that only targets Internet Explorer. You see, IE does not support <details>, so they are always open by default there. By removing the text and resetting the cursor, IE users will see a regular comment thread, only without the ability to actually toggle the comment visibility. So, no harm, no foul.

Honorable mentions

Before we end this article, let’s talk about a few more things that is worth considering when designing comment threads in real-life applications.

What if the comment thread has no comments to show?

This may be a very simple problem to solve, but it is often easy to overlook these simple things. If a comment thread does not have any comments (empty state), we need to communicate that clearly to the user. A simple paragraph containing the line “There are no comments yet.” along with a form containing a text box and submit button can go a very long way, and should be the bare minimum when dealing with empty states. If you want to go the extra mile, you can also have a nice image (that communicates the message) accompanying the form.

How to handle the form for replying to a comment?

When it comes to the form for replying to a comment, different websites have different implementations. Some use the old fashioned way of redirecting users to a new page which contains the form — a simple text box with a submit button. Others open up a form right within the comment thread itself, usually with a simple toggle. The latter paradigm obviously requires JavaScript, but it is more more popular these days. For instance, in our example above, we could have a simple form which can be toggled by clicking on the Reply button, like so:

In the above example, we added simple forms inside the comment bodies, and gave them the class .d-none by default, which sets display: none; and hides them from view. Thanks to the simple event listener, any button with the attributes data-toggle="reply-form" and data-target="{{ comment_reply_form_id }} can be clicked to toggle the visibility of the forms. This is a very simple example of handling the reply forms with ease.

Where to place a new reply after a user is done posting it?

Let’s say a user replies to a comment using a form similar to the one shown above. Do you show it above the existing replies or below it? The answer is that it should always be shown above the other replies right after the user posts it for the first time. When a person fills out a form and submits it, they want immediate feedback to tell them that it worked. Therefore, by placing the new reply above the others, we are providing this feedback to the user without them needing to scroll down. On subsequent loads, you can of course arrange your comment replies according to whatever algorithm you see fit for your website.

Handling Markdown and code blocks

Many websites, particularly developer blogs, need to support markdown and code blocks in their comments. This is a much bigger discussion, perhaps warranting a dedicated article on this topic. However, for the sake of this article, let’s just say that there are plenty of Markdown editors out there that you can attach to a text box quite easily. Most of them work with JavaScript, so they should be fairly easy to integrate in our examples. One such plugin is markdown-it, which has a permissive MIT license. You can also look into WYSIWYG editors, which also serve a very similar purpose when it comes to comments on the web.

Spam prevention and user authentication

If you give users a form to provide their inputs, you can guarantee that you will find spam coming your way, so this is obviously an issue to address when building comment threads. A great way to reduce spam is to use services like reCAPTCHA from Google. For instance, in our example above, a reCAPTCHA box could be placed right below the Submit button in the reply forms. This would protect our website from abuse.

Another way to prevent spam is to only allow authenticated users to post comments, i.e. a user must have an account and be logged in to post a comment. Every comment would obviously be linked to an account, so this has the benefit of allowing moderators to handle users who continuously post spam or low effort content. In terms of handling it in the UI, a great way of doing it is by redirecting users to a login page when they click on the Reply or Post comment button if they are not logged in. Once they complete the authentication process, we can simply redirect them back to the comment thread and open up the form.

And we are done! We have fulfilled all five of our points, and have designed a nice-looking comment thread that is highly usable and accessible, with some cool features like jumping to comments, and togging the visibility of each comment. We also talked about forms inside comment threads, and discussed other things to consider in real-life applications. The bulk of our comment thread works using only CSS (no JavaScript), which goes to show you how far CSS has actually come.

The post Styling Comment Threads appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, ,

Comparing Styling Methods in 2020

Over on Smashing, Adebiyi Adedotun Lukman covers all these styling methods. It’s in the context of Next.js, which is somewhat important as Next.js has some specific ways you work with these tools, is React and, thus, is a components-based architecture. But the styling methods talked about transcend Next.js, and can apply broadly to lots of websites.

Here are my hot takes on the whole table-of-contents of styling possibilities these days.

  • Regular CSS. If you can, do. No build tooling is refreshing. It will age well. The only thing I really miss without any other tooling is nesting media queries.
  • Sass. Sass has been around the block and is still a hugely popular CSS preprocessor. Sass is built into tons of other tools, so choosing Sass doesn’t always mean the same thing. A simple Sass integration might be as easy as a sass --watch src/style.scss dist/style.css npm script. Then, once you’ve come to terms with the fact that you have a build process, you can start concatenating files, minifying, busting cache, and all this stuff that you’re probably going to end up doing somehow anyway.
  • Less & Stylus. I’m surprised they aren’t more popular since they’ve always been Node and work great with the proliferation of Node-powered build processes. Not to mention they are nice feature-rich preprocessors. I have nothing against either, but Sass is more ubiquitous, more actively developed, and canonical Sass now works fine in Node-land,
  • PostCSS. I’m not compelled by PostCSS because I don’t love having to cobble together the processing features that I want. That also has the bad side effect of making the process of writing CSS different across projects. Plus, I don’t like the idea of preprocessing away modern features, some of which can’t really be preprocessed (e.g. custom properties cannot be preprocessed). But I did love Autoprefixer when we really needed that, which is based on PostCSS.
  • CSS Modules. If you’re working with components in any technology, CSS modules give you the ability to scope CSS to that component, which is an incredibly great idea. I like this approach wherever I can get it. Your module CSS can be Sass too, so we can get the best of both worlds there.
  • CSS-in-JS. Let’s be real, this means “CSS-in-React.” If you’re writing Vue, you’re writing styles the way Vue helps you do it. Same with Svelte. Same with Angular. React is the un-opinionated one, leaving you to choose between things like styled-components, styled-jsx, Emotion… there are a lot of them. I have projects in React and I just use Sass+CSS Modules and think that’s fine, but a lot of people like CSS-inJS approaches too. I get it. You get the scoping automatically and can do fancy stuff like incorporate props into styling decisions. Could be awesome for a design system.

If you want to hear some other hot takes on this spectrum, the Syntax FM fellas sounded off on this recently.

The post Comparing Styling Methods in 2020 appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,

Styling Complex Labels

Danielle Romo covers the HTML pattern you need when you have a wordy <label> with fancy styling for an <input type="radio">.

The trick? The ol’ <span class="hidden-visually"> that contains the label that you want to be read, and a <span aria-hidden="true"> with the visual-only content.

I think it’s interesting how often people are landing on this pattern. Have you seen Ethan’s The World-Wide Work? The drop-cap pattern he talks about here lands on essentially the same pattern.

<span aria-hidden="true">   Markup for the visual experience only,   where you can (somewhat safely) use markup    that would be crap for screen readers.   </span>  <span class="visually-hidden">   Markup for the read experience only, that   you keep very clean on purpose. </span>

That class is like this.

Direct Link to ArticlePermalink

The post Styling Complex Labels appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, ,