Tag: List

Everything You Need to Know About the Gap After the List Marker

I was reading “Creative List Styling” on Google’s web.dev blog and noticed something odd in one of the code examples in the ::marker section of the article. The built-in list markers are bullets, ordinal numbers, and letters. The ::marker pseudo-element allows us to style these markers or replace them with a custom character or image.

::marker {   content: url('/marker.svg') ' '; }

The example that caught my attention uses an SVG icon as a custom marker for the list items. But there’s also a single space character (" ") in the CSS value next to the url() function. The purpose of this space seems to be to insert a gap after the custom marker.

When I saw this code, I immediately wondered if there was a better way to create the gap. Appending a space to content feels more like a workaround than the optimal solution. CSS provides margin and padding and other standard ways to space out elements on the page. Could none of these properties be used in this situation?

First, I tried to substitute the space character with a proper margin:

::marker {   content: url('/marker.svg');   margin-right: 1ch; }

This didn’t work. As it turns out, ::marker only supports a small set of mostly text-related CSS properties. For example, you can change the font-size and color of the marker, and define a custom marker by setting content to a string or URL, as shown above. But the margin and padding properties are not supported, so setting them has no effect. What a disappointment.

Could it really be that a space character is the only way to insert a gap after a custom marker? I needed to find out. As I researched this topic, I made a few interesting discoveries that I’d like to share in this article.

Adding padding and margins

First, let’s confirm what margin and padding do on the <ul> and <li> elements. I’ve created a test page for this purpose. Drag the relevant sliders and observe the effect on the spacing on each side of the list marker. Tip: Use the Reset button liberally to reset all controls to their initial values.

Note: Browsers apply a default padding-inline-left of 40px to <ol> and <ul> elements. The logical padding-inline-left property is equivalent to the physical padding-left property in writing systems with a left-to-right inline direction. In this article, I’m going to use physical properties for the sake of simplicity.

As you can see, padding-left on <li> increases the gap after the list marker. The other three properties control the spacing to the left of the marker, in other words, the indentation of the list item.

Notice that even when the list item’s padding-left is 0px, there is still a minimum gap after the marker. This gap cannot be decreased with margin or padding. The exact length of the minimum gap depends on the browser.

First three properties: UL margin-left, UL padding-left, LI margin-left. Fourth property: LI padding-left.
The first three properties push the entire list item (including the marker) to the right. The fourth property pushes only the list item’s content to the right.

To sum up, the list item’s content is positioned at a browser-specific minimum distance from the marker, and this gap can be further increased by adding a padding-left to <li>.

Next, let’s see what happens when we position the marker inside the list item.

Moving the marker inside the list item

The list-style-position property accepts two keywords: outside, which is the default, and inside, which moves the marker inside the list item. The latter is useful for creating designs with full-width list items.

A grocery list. Each item has a thin bottom border that extends from the left to the right edge of the list.
The list marker is positioned inside the list item, so that the list item’s bottom border can extend to the left edge of the list box

If the marker is now inside the list item, does this mean that padding-left on <li> no longer increases the gap after the marker? Let’s find out. On my test page, turn on list-style-position: inside via the checkbox. How are the four padding and margin properties affected by this change?

As you can see, padding-left on <li> now increases the spacing to the left of the marker. This means that we’ve lost the ability to increase the gap after the marker. In this situation, it would be useful to be able to add margin-right to the ::marker itself, but that doesn’t work, as we’ve established above.

The four properties: UL margin-left, UL padding-left, LI margin-left, LI padding-left.
All four properties push the entire list item to the right. The minimum gap cannot be increased by standard means.

Additionally, there’s a bug in Chromium that causes the gap after the marker to triple after switching to inside positioning. By default, the length of the gap is about one-third of the text size. So at a default font-size of 16px, the gap is about 5.5px. After switching to inside, the gap grows to the full 16px in Chrome. This bug affects the disc, circle, and square markers, but not ordinal number markers.

The following image shows the default rendering of outside and inside-positioned list markers across three major browsers on macOS. For your convenience, I’ve horizontally aligned all list items on their markers to make it easier to compare the differences in gap sizes.

Six list items with varying gaps between the marker and text.
Only Firefox maintains the same gap size between the two marker positioning modes. This can be considered a browser interoperability (interop) issue.

To sum up, switching to list-style-position: inside introduces two problems. We can no longer increase the gap via padding-left on <li>, and the gap size is inconsistent between browsers.

Finally, let’s see what happens when we replace the default list marker with a custom marker.

Switching to a custom marker

There are two ways to define a custom marker:

  • list-style-type and list-style-image properties
  • content property on the ::marker pseudo-element

The content property is more powerful. For example, it allows us to use the counter() function to access the list item’s ordinal number (the implicit list-item counter) and decorate it with custom strings.

Unfortunately, Safari doesn’t support the content property on ::marker yet (WebKit bug). For this reason, I’m going to use the list-style-type property to define the custom marker. You can still use the ::marker selector to style the custom marker declared via list-style-type. That aspect of ::marker is supported in Safari.

Any Unicode character can potentially serve as a custom list marker, but only a small set of characters actually have “Bullet” in their official name, so I thought I’d compile them here for reference.

Character Name Code point CSS keyword
Bullet U+2022 disc
Triangular Bullet U+2023
Hyphen Bullet U+2043
Black Leftwards Bullet U+204C
Black Rightwards Bullet U+204D
Inverse Bullet U+25D8
White Bullet U+25E6 circle
Reversed Rotated Floral Heart Bullet U+2619
Rotated Heavy Black Heart Bullet U+2765
Rotated Floral Heart Bullet U+2767
Circled White Bullet U+29BE
⦿ Circled Bullet U+29BF

Note: The CSS square keyword does not have a corresponding “Bullet” character in Unicode. The character that comes closest is the Black Small Square (▪️) emoji (U+25AA).

Now let’s see what happens when we replace the default list marker with list-style-type: "•" (U+2022 Bullet). This is the same character as the default bullet, so there shouldn’t be any major rendering differences. On my test page, turn on the list-style-type option and observe any changes to the marker.

As you can see, there are two significant changes:

  1. There is no longer a minimum gap after the marker.
  2. The bullet has become smaller, as if it were rendered at a smaller font-size.

According to CSS Counter Styles Level 3, the default list marker (disc) should be “similar to • U+2022 BULLET”. It seems that browsers increase the size of the default bullet to make it more legible. Firefox even uses a special font, -moz-bullet-font, for the marker.

:marker selected in the inspector. Fonts used: -moz-bullet-font.
The “Fonts” pane in Firefox’s DOM inspector reveals the special font.

Can the small size problem be fixed with CSS? On my test page, turn on marker styling and observe what happens when you change the font-size, line-height, and font-family of the marker.

As you can see, increasing the font-size causes the custom marker to become vertically misaligned, and this cannot be corrected by decreasing the line-height. The vertical-align property, which could easily fix this problem, is not supported on ::marker.

But did you notice that changing the font-family can cause the marker to become bigger? Try setting it to Tahoma. This could potentially be a good-enough workaround for the small-size problem, although I haven’t tested which font works best across the major browsers and operating systems.

You may also have noticed that the Chromium bug doesn’t occur anymore when you position the marker inside the list item. This means that a custom marker can serve as a workaround for this bug. And this leads me to the main problem, and the reason why I started researching this topic. If you define a custom marker and position it inside the list item, there is no gap after the marker and no way to insert a gap by standard means.

  1. There is no minimum gap after custom markers.
  2. ::marker doesn’t support padding or margin.
  3. padding-left on <li> doesn’t increase the gap, since the marker is positioned inside.

Summary

Here’s a summary of all the key facts that I’ve mentioned in the article:

  1. Browsers apply a default padding-inline-start of 40px to <ul> and <ol> elements.
  2. There is a minimum gap after built-in list markers (disc, decimal, etc.). There is no minimum gap after custom markers (string or URL).
  3. The length of the gap can be increased by adding a padding-left to <ul>, but only if the marker is positioned outside the list item (the default mode).
  4. Custom string markers have a smaller default size than built-in markers. Changing the font-family on ::marker can increase their size.

Conclusion

Looking back at the code example from the beginning of the article, I think I understand now why there’s a space character in the content value. There is just no better way to insert a gap after the SVG marker. It’s a workaround that is needed because no amount of margin and padding can create a gap after a custom marker that is positioned inside the list item. A margin-right on ::marker could easily do it, but that is not supported.

Until ::marker adds support for more properties, web developers will often have no choice but to hide the marker and emulate it with a ::before pseudo-element. I had to do that myself recently because I couldn’t change the marker’s background-color. Hopefully, we won’t have to wait too long for a more powerful ::marker pseudo-element.


Everything You Need to Know About the Gap After the List Marker originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS-Tricks

, , , , , ,

List Markers and String Styles

Lists—we’ve all worked with them in one form or another. I’m talking about HTML’s <ol> and <ul>. Much of the time, because we desire styling control, we turn off the list’s markers completely with list-style-type: none, and start styling from there. Other times, we choose from a very limited set of unordered list markers, such as disc, circle, or square; or a (much) wider range of ordered list markers. We might even, from time to time, supply the URL of an image to be used.

But what if we want to style the markers differently than the contents of the list items? That’s always been difficult at best. Now, thanks to the ::marker pseudo-element, it’s a whole lot easier. You don’t get the full range of CSS to apply to the markers, but there’s still a great deal that can be done.

::marker is available in Firefox and, thanks to work by Igalia, Chrome as well.

Consider this list:

By default, that will yield an ordered list numbered from 1 to 5, using Arabic numerals (1, 2, 3, etc.), each followed by a dot (period), all of which will match the text contents in font face, size, style, color, and so on.

If you had a design direction that required making the numbers smaller or a different color, you’d have to manually create that effect by suppressing the markers and using the ::before pseudo-element and CSS counters and negative text indenting and… well, it would take a scientist to explain it all.

Enter ::marker. Add these styles to the above list, and you’ll get the result shown after.

That’s all you need!

Before you go tearing off to rewrite all your CSS, though, beware: the properties you can apply via ::marker are fairly limited at the moment. As of February 2021, the properties that markers should recognize are:

  • All font properties (font-face, font-size, etc.)
  • The white-space property
  • The color property
  • The internationalization properties text-combine-upright, unicode-bidi, and direction
  • The content property
  • All animation and transition properties

There are some additions in some browsers, but almost all of the additions relate to text styling, not the box model. So if you were thinking you could put all your list numbers into circles with shaded backgrounds, ::marker won’t get you there—you’ll have to return to the hackfest of ::before generated content. For now, anyway: the specification explicitly says more properties may be permitted for ::marker in the future.

There’s also a limitation around white-space, which has rendering bugs in varying browsers. Chrome, for example, treats all whitespace in markers as white-space: pre as the specification says, but won’t let you change it. This should be fixed when Chrome’s LayoutNG (Next Generation) ships, but not until then. Firefox, on the other hand, ignores any white-space values, and treats whitespace like normal-flow text by default.

With those limits in mind, you can still jazz up your markers with the content property. Instead of numbers followed by a period, you can put each number in brackets with a combination of counters and strings.

Note the space after the closing bracket in the content value. That’s included to provide a little bit of space between the marker and the list content. Ordinarily you might think to use a marking or padding, but as we saw earlier, those properties can’t be applied with ::marker. Which is frustrating! Also note the CSS counter list-item. That wasn’t defined anywhere else in the CSS—it’s a built-in counter that all browsers (that understand CSS counters) use to count list items, like those in ordered lists. You can use it in your CSS as well!

If all you want to do is change the text content of a list marker and don’t care about changing any of its styles, you can do that with ::marker, or you can do it with the new cross-browser support for string values on the list-style-type property.

li.warning {   list-style-type:"⚠"; }

So that’s what’s new in the world of list markers. It might not be something you need to do often, but if you ever do, it’s good to know that the capabilities in this area have increased, and stand to be even better in the future. Let us know if you come up with some clever markers!


The post List Markers and String Styles appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

How to Add Commas Between a List of Items Dynamically with CSS

Imagine you have a list of items. Say, fruit: Banana, Apple, Orange, Pear, Nectarine

We could put those commas (,) in the HTML, but let’s look at how we could do that in CSS instead, giving us an extra level of control. We’ll make sure that last item doesn’t have a comma while we’re at it.

I needed this for a real project recently, and part of the requirements were that any of the items in the list could be hidden/revealed via JavaScript. The commas needed to work correctly no matter which items were currently shown.

One solution I found rather elegant solution is using general sibling combinator. We’ll get to that in a minute. Let’s start with some example HTML. Say you start out with a list of fruits:

<ul class="fruits">   <li class="fruit on">Banana</li>   <li class="fruit on">Apple</li>   <li class="fruit on">Orange</li>   <li class="fruit on">Pear</li>   <li class="fruit on">Nectarine</li> </ul>

And some basic CSS to make them appear in a list:

.fruits {   display: flex;   padding-inline-start: 0;   list-style: none; }  .fruit {   display: none; /* hidden by default */ }  .fruit.on { /* JavaScript-added class to reveal list items */   display: inline-block; }

Now say things happen inside this interface, like a user toggles controls that filter out all fruits that grow in cold climates. Now a different set of fruits is shown, so the fruit.on class is manipulated with the classList API.

So far, our HTML and CSS would create a list like this:

BananaOrangeNectarine

Now we can reach for that general sibling combinator to apply a comma-and-space between any two on elements:

.fruit.on ~ .fruit.on::before {   content: ', ';  }

Nice!

You might be thinking: why not just apply commas to all the list items and remove it from the last with something like :last-child or :last-of-type. The trouble with that is the last child might be “off” at any given time. So what we really want is the last item that is “on,” which isn’t easily possible in CSS, since there is nothing like “last of class” available. Hence, the general sibling combinator trick!

In the UI, I used max-width instead of display and toggled that between 0 and a reasonable maximum value so that I could use transitions to push items on and off more naturally, making it easier for the user to see which items are being added or removed from the list. You can add the same effect to the pseudo-element as well to make it super smooth.

Here’s a demo with a couple of examples that are both slight variations. The fruits example uses a hidden class instead of on, and the veggies example has the animations. SCSS is also used here for the nesting:

I hope this helps others looking for something similar!


The post How to Add Commas Between a List of Items Dynamically with CSS appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,
[Top]

How to Create a Timeline Task List Component Using SVG

I’m thoroughly convinced that SVG unlocks a whole entire world of building interfaces on the web. It might seem daunting to learn SVG at first, but you have a spec that was designed to create shapes and yet, still has elements, like text, links, and aria labels available to you. You can accomplish some of the same effects in CSS, but it’s a little more particular to get positioning just right, especially across viewports and for responsive development.

What’s special about SVG is that all the positioning is based on a coordinate system, a little like the game Battleship. That means deciding where everything goes and how it’s drawn, as well as how it’s relative to each other, can be really straightforward to reason about. CSS positioning is for layout, which is great because you have things that correspond to one another in terms of the flow of the document. This otherwise positive trait is harder to work with if you’re making a component that’s very particular, with overlapping and precisely placed elements.

Truly, once you learn SVG, you can draw anything, and have it scale on any device. Even this very site uses SVG for custom UI elements, such as my avatar, above (meta!).

That little half circle below the author image is just SVG markup.

We won’t cover everything about SVGs in this post (you can learn some of those fundamentals here, here, here and here), but in order to illustrate the possibilities that SVG opens up for UI component development, let’s talk through one particular use case and break down how we would think about building something custom.

The timeline task list component

Recently, I was working on a project with my team at Netlify. We wanted to show the viewer which video in a series of videos in a course they were currently watching. In other words, we wanted to make some sort of thing that’s like a todo list, but shows overall progress as items are completed. (We made a free space-themed learning platform and it’s hella cool. Yes, I said hella.)

Here’s how that looks:

So how would we go about this? I’ll show an example in both Vue and React so that you can see how it might work in both frameworks.

The Vue version

We decided to make the platform in Next.js for dogfooding purposes (i.e. trying out our own Next on Netlify build plugin), but I’m more fluent in Vue so I wrote the initial prototype in Vue and ported it over to React.

Here is the full CodePen demo:

Let’s walk through this code a bit. First off, this is a single file component (SFC), so the template HTML, reactive script, and scoped styles are all encapsulated in this one file.

We’ll store some dummy tasks in data, including whether each task is completed or not. We’ll also make a method we can call on a click directive so that we can toggle whether the state is done or not.

<script> export default {   data() {     return {       tasks: [         {           name: 'thing',           done: false         },         // ...       ]     };   },   methods: {     selectThis(index) {       this.tasks[index].done = !this.tasks[index].done     }   } }; </script> 

Now, what we want to do is create an SVG that has a flexible viewBox depending on the amount of elements. We also want to tell screen readers that this a presentational element and that we will provide a title with a unique id of timeline. (Get more information on creating accessible SVGs.)

<template>   <div id="app">     <div>       <svg :viewBox="`0 0 30 $ {tasks.length * 50}`"            xmlns="http://www.w3.org/2000/svg"             width="30"             stroke="currentColor"             fill="white"            aria-labelledby="timeline"            role="presentation">            <title id="timeline">timeline element</title>         <!-- ... -->       </svg>     </div>   </div> </template>

The stroke is set to currentColor to allow for some flexibility — if we want to reuse the component in multiple places, it will inherit whatever color is used on the encapsulating div.

Next, inside the SVG, we want to create a vertical line that’s the length of the task list. Lines are fairly straightforward. We have x1 and x2 values (where the line is plotted on the x-axis), and similarly, y1 and y2.

<line x1="10" x2="10" :y1="num2" :y2="tasks.length * num1 - num2" />

The x-axis stays consistently at 10 because we’re drawing a line downward rather than left-to-right. We’ll store two numbers in data: the amount we want our spacing to be, which will be num1, and the amount we want our margin to be, which will be num2.

data() {   return {     num1: 32,     num2: 15,     // ...   } }

The y-axis starts with num2, which is subtracted from the end, as well as the margin. The tasks.length is multiplied by the spacing, which is num1.

Now, we’ll need the circles that lie on the line. Each circle is an indicator for whether a task has been completed or not. We’ll need one circle for each task, so we’ll use v-for with a unique key, which is the index (and is safe to use here as they will never reorder). We’ll connect the click directive with our method and pass in the index as a param as well.

CIrcles in SVG are made up of three attributes. The middle of the circle is plotted at cx and cy, and then we draw a radius with r. Like the line, cx starts at 10. The radius is 4 because that’s what’s readable at this scale. cy will be spaced like the line: index times the spacing (num1), plus the margin (num2).

Finally, we’ll put use a ternary to set the fill. If the task is done, it will be filled with currentColor. If not, it will be filled with white (or whatever the background is). This could be filled with a prop that gets passed in the background, for instance, where you have light and dark circles.

<circle    @click="selectThis(i)"    v-for="(task, i) in tasks"   :key="task.name"   cx="10"   r="4"   :cy="i * num1 + num2"   :fill="task.done ? 'currentColor' : 'white'"   class="select"/>

Finally, we are using CSS grid to align a div with the names of tasks. This is laid out much in the same way, where we’re looping through the tasks, and are also tied to that same click event to toggle the done state.

<template>   <div>     <div        @click="selectThis(i)"       v-for="(task, i) in tasks"       :key="task.name"       class="select">       {{ task.name }}     </div>   </div> </template>

The React version

Here is where we ended up with the React version. We’re working towards open sourcing this so that you can see the full code and its history. Here are a few modifications:

  • We’re using CSS modules rather than the SCFs in Vue
  • We’re importing the Next.js link, so that rather than toggling a “done” state, we’re taking a user to a dynamic page in Next.js
  • The tasks we’re using are actually stages of the course —or “Mission” as we call them — which are passed in here rather than held by the component.

Most of the other functionality is the same 🙂

import styles from './MissionTracker.module.css'; import React, { useState } from 'react'; import Link from 'next/link';  function MissionTracker({ currentMission, currentStage, stages }) {  const [tasks, setTasks] = useState([...stages]);  const num1 = [32];  const num2 = [15];   const updateDoneTasks = (index) => () => {    let tasksCopy = [...tasks];    tasksCopy[index].done = !tasksCopy[index].done;    setTasks(tasksCopy);  };   const taskTextStyles = (task) => {    const baseStyles = `$ {styles['tracker-select']} $ {styles['task-label']}`;     if (currentStage === task.slug.current) {      return baseStyles + ` $ {styles['is-current-task']}`;    } else {      return baseStyles;    }  };   return (    <div className={styles.container}>      <section>        {tasks.map((task, index) => (          <div            key={`mt-$ {task.slug}-$ {index}`}            className={taskTextStyles(task)}          >            <Link href={`/learn/$ {currentMission}/$ {task.slug.current}`}>              {task.title}            </Link>          </div>        ))}      </section>       <section>        <svg          viewBox={`0 0 30 $ {tasks.length * 50}`}          className={styles['tracker-svg']}          xmlns="http://www.w3.org/2000/svg"          width="30"          stroke="currentColor"          fill="white"          aria-labelledby="timeline"          role="presentation"        >          <title id="timeline">timeline element</title>           <line x1="10" x2="10" y1={num2} y2={tasks.length * num1 - num2} />          {tasks.map((task, index) => (            <circle              key={`mt-circle-$ {task.name}-$ {index}`}              onClick={updateDoneTasks(index)}              cx="10"              r="4"              cy={index * +num1 + +num2}              fill={                task.slug.current === currentStage ? 'currentColor' : 'black'              }              className={styles['tracker-select']}            />          ))}        </svg>      </section>    </div>  ); }  export default MissionTracker;

Final version

You can see the final working version here:

This component is flexible enough to accommodate lists small and large, multiple browsers, and responsive sizing. It also allows the user to have better understanding of where they are in their progress in the course.

But this is just one component. You can make any number of UI elements: knobs, controls, progress indicators, loaders… the sky’s the limit. You can style them with CSS, or inline styles, you can have them update based on props, on context, on reactive data, the sky’s the limit! I hope this opens some doors on how you yourself can develop more engaging UI elements for the web.


The post How to Create a Timeline Task List Component Using SVG appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

How to Make a List Component with Emotion

I’ve been doing a bit of refactoring this week at Sentry and I noticed that we didn’t have a generic List component that we could use across projects and features. So, I started one, but here’s the rub: we style things at Sentry using Emotion, which I have only passing experience with and is described in the docs as…

[…] a library designed for writing css styles with JavaScript. It provides powerful and predictable style composition in addition to a great developer experience with features such as source maps, labels, and testing utilities. Both string and object styles are supported.

If you’ve never heard of Emotion, the general idea is this: when we’re working on big codebases with lots of components, we want to ensure that we can control the cascade of our CSS. So, let’s say you have an .active class in one file and you want to make sure that doesn’t impact the styles of a completely separate component in another file that also has a class of.active.

Emotion tackles this problem by adding custom strings to your classnames so they don’t conflict with other components. Here’s an example of the HTML it might output:

<div class="css-1tfy8g7-List e13k4qzl9"></div>

Pretty neat, huh? There’s lots of other tools and workflows out there though that do something very similar, such as CSS Modules.

To get started making the component, we first need to install Emotion into our project. I’m not going to walkthrough that stuff because it’s going to be different depending on your environment and setup. But once that’s complete we can go ahead and create a new component like this:

import React from 'react'; import styled from '@emotion/styled';  export const List = styled('ul')`   list-style: none;   padding: 0; `;

This looks pretty weird to me because, not only are we writing styles for the <ul> element, but we’re defining that the component should render a <ul>, too. Combining both the markup and the styles in one place feels odd but I do like how simple it is. It just sort of messes with my mental model and the separation of concerns between HTML, CSS, and JavaScript.

In another component, we can import this <List> and use it like this:

import List from 'components/list';  <List>This is a list item.</List>

The styles we added to our list component will then be turned into a classname, like .oefioaueg, and then added to the <ul> element we defined in the component.

But we’re not done yet! With the list design, I needed to be able to render a <ul> and an <ol> with the same component. I also needed a version that allows me to place an icon within each list item. Just like this:

The cool (and also kind of weird) thing about Emotion is that we can use the as attribute to select which HTML element we’d like to render when we import our component. We can use this attribute to create our <ol> variant without having to make a custom type property or something. And that happens to look just like this:

<List>This will render a ul.</List> <List as="ol">This will render an ol.</List>

That’s not just weird to me, right? It’s super neat, however, because it means that we don’t have to do any bizarro logic in the component itself just to change the markup.

It was at this point that I started to jot down what the perfect API for this component might look like though because then we can work our way back from there. This is what I imagined:

<List>   <ListItem>Item 1</ListItem>   <ListItem>Item 2</ListItem>   <ListItem>Item 3</ListItem> </List>  <List>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem> </List>  <List as="ol">   <ListItem>Item 1</ListItem>   <ListItem>Item 2</ListItem>   <ListItem>Item 3</ListItem> </List>

So after making this sketch I knew we’d need two components, along with the ability to nest icon subcomponents within the <ListItem>. We can start like this:

import React from 'react'; import styled from '@emotion/styled';  export const List = styled('ul')`   list-style: none;   padding: 0;   margin-bottom: 20px;    ol& {     counter-reset: numberedList;   } `;

That peculiar ol& syntax is how we tell emotion that these styles only apply to an element when it’s rendered as an <ol>. It’s often a good idea to just add a background: red; to this element to make sure your component is rendering things correctly.

Next up is our subcomponent, the <ListItem>. It’s important to note that at Sentry we also use TypeScript, so before we define our <ListItem> component, we’ll need to set our props up first:

type ListItemProps = {   icon?: React.ReactNode;   children?: string | React.ReactNode;   className?: string; };

Now we can add our <IconWrapper> component that will size an <Icon> component within the ListItem. If you remember from the example above, I wanted it to look something like this:

<List>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem> </List>

That IconBusiness component is a preexisting component and we want to wrap it in a span so that we can style it. Thankfully, we’ll need just a tiny bit of CSS to align the icon properly with the text and the <IconWrapper> can handle all of that for us:

type ListItemProps = {   icon?: React.ReactNode;   children?: string | React.ReactNode;   className?: string; };  const IconWrapper = styled('span')`   display: flex;   margin-right: 15px;   height: 16px;   align-items: center; `;

Once we’ve done this we can finally add our <ListItem> component beneath these two, although it is considerably more complex. We’ll need to add the props, then we can render the <IconWrapper> above when the icon prop exists, and render the icon component that’s passed into it as well. I’ve also added all the styles below so you can see how I’m styling each of these variants:

export const ListItem = styled(({icon, className, children}: ListItemProps) => (   <li className={className}>     {icon && (       <IconWrapper>         {icon}       </IconWrapper>     )}     {children}   </li> ))<ListItemProps>`   display: flex;   align-items: center;   position: relative;   padding-left: 34px;   margin-bottom: 20px; 	   /* Tiny circle and icon positioning */   &:before, 	& > $ {IconWrapper} {     position: absolute;     left: 0;   }    ul & {     color: #aaa;     /* This pseudo is the tiny circle for ul items */      &:before {       content: '';       width: 6px;       height: 6px;       border-radius: 50%;       margin-right: 15px;       border: 1px solid #aaa;       background-color: transparent;       left: 5px;       top: 10px;     } 		     /* Icon styles */     $ {p =>       p.icon &&       `       span {         top: 4px;       }       /* Removes tiny circle pseudo if icon is present */       &:before {         content: none;       }     `}   }   /* When the list is rendered as an <ol> */   ol & {     &:before {       counter-increment: numberedList;       content: counter(numberedList);       top: 3px;       display: flex;       align-items: center;       justify-content: center;       text-align: center;       width: 18px;       height: 18px;       font-size: 10px;       font-weight: 600;       border: 1px solid #aaa;       border-radius: 50%;       background-color: transparent;       margin-right: 20px;     }   } `;

And there you have it! A relatively simple <List> component built with Emotion. Although, after going through this exercise I’m still not sure that I like the syntax. I reckon it sort of makes the simple stuff really simple but the medium-sized components much more complicated than they should be. Plus, it could be pretty darn confusing to a newcomer and that worries me a bit.

But everything is a learning experience, I guess. Either way, I’m glad I had the opportunity to work on this tiny component because it taught me a few good things about TypeScript, React, and trying to make our styles somewhat readable.

The post How to Make a List Component with Emotion appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

List Style Recipes

Lists are a fundamental part of HTML! They are useful in things like blog posts for listing out steps, recipes for listing ingredients, or items in a navigation menu. Not only are they an opportunity for styling, but they have accessibility implications. For example, the number of items in a list is announced in a screen reader to give some context to the list.

Let’s focus on styling lists here, mostly just ordered and unordered lists (with apologies for snubbing our friend the definition list), and somewhat unusual styling situations.

The Basics

Before you do anything too fancy, know that there is quite few settings for list-style-type that might cover your needs out of the gate.

The Break in the Middle

Ordered lists can start at any number you want them to.

The Nested Decimals

The Reversed Top 10 List

A single reversed attribute will do the trick.

Image Bullets

The best bet is using a background-image on a pseudo-element. You’d think list-style-image would be the way to go, but it’s extremely limited. For example, you can’t position it or even resize it.

Emoji Bullets

Hand-Picked “Random” Order

The value attribute will set a list item to use the marker relevant for that position.

Custom Text Counters

Can be done with pseudo-elements for the most control, but there is also list-style-type: '-';

Inside vs. Outside

Things line up nicer with list-style-position: outside; (the default value), but the list markers render outside the box, so you have to be careful not to cut them off. They could hang off the edge of the browser window, or overflow: hidden; will hide them completely. The last two examples here have a trick to mimic the nicer alignment while rendering inside the element.

Colored Bullets

Three ways here:

  1. ::marker (newest and easiest)
  2. Classic pseudo-element style
  3. background-image (this one is an SVG Data URL so you can manipulate the color from the CSS)

Columized List

The number of columns can be automatic.

Colored Circle Numbers

Custom Cycling List Symbols

One-offs can be done with list-style: symbols() and reusable sets can be made with @counter-style then used. Note this is only supported in Firefox at the time of writing.

The post List Style Recipes appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Did You Know the Ordered List Element Has Start and Reversed Attributes?

I sure didn’t! Tomek Sułkowsi shows how we can reverse the numbering of ordered lists with a simple HTML attribute:

<ol reversed>   <li>Apple</li>   <li>Banana</li>   <li>Pear</li> </ol>

And the start attribute can be added to begin the list at a number other than one, like this:

<ol start="2">   <li>Apple</li>   <li>Banana</li>   <li>Pear</li> </ol>

I’m not sure how I never knew about these properties! I guess I can see how they might come in handy in the future. There are plenty of times when we need to break up ordered lists here on CSS-Tricks with things like code blocks and having a way to pick a list back up where it left off is a nice convenience.

The post Did You Know the Ordered List Element Has Start and Reversed Attributes? appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Finally, it Will Be Easy to Change the Color of List Bullets

In my germinating years, the general advice was this:

<ul>   <li><span>List item</span></li>   <!-- ... --> </ul>
li { color: red; } /* bullet */ li span (color: black; } /* text */

Not terrible, but not great. You’re “resetting” everything at the span level, so it gets more complicated the more you do.

Things are getting much easier. Let’s take a walk through this world getting more modern as we go.


An alternative was to rip off the default list styling and replace it with a pseudo-element.

ul {   list-style: none; }  li::before {   content: "• ";   color: red; }

If we need to count, we could do that with CSS counters.

ol {   list-style: none;   counter-reset: my-awesome-counter; }  ol li {   counter-increment: my-awesome-counter; }  ol li::before {   content: counter(my-awesome-counter) ". ";   color: red; }

Quick aside here: this doesn’t help with the color, but you can specify what character to use for the bullet by setting a string, like:

ul {   list-style-type: '✽ '; }

This is as of Firefox 39 (2015) and Chrome 79 (which comes out Dec 9, 2019).

For ordered lists, there is a ton of language-specific options. And those language styles work for CSS counters as well, which you can learn more about in Hui Jing’s deep dive.

See the Pen
Random CSS counters playground
by Chen Hui Jing (@huijing)
on CodePen.


But all the while, we only wanted to select the stupid bullet (or whatever it is) and style it. Now we are starting to be able to do just that.

As of Firefox 68 (July 2019), you can do like:

li::marker {   color: red;   content: "►"; }

…which, as you can see, changes the color and the bullet thing That is definitely the cleanest and easiest way to go, so it’s nice to see progress.

Tejas demonstrates:

See the Pen
::marker example
by Tejas (@tejask)
on CodePen.

Manuel Matuzović notes that if you set an element to a list-item display type, you can use markers on them as well.

h2 {   display: list-item; }  h2::marker {   color: orange;   content: "☞"; }

Even Safari has support at the time of this writing, so we should lean on Chrome here.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Opera Firefox IE Edge Safari
No No 68 No No 11.1

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
11.3-11.4 No No No No No

The post Finally, it Will Be Easy to Change the Color of List Bullets appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]