How Do You Put a Border on Three Sides of an Element?

I saw a little conversation about this the other day and figured it would be fun to look at all the different ways to do it. None of them are particularly tricky, but perhaps you’ll favor one over another for clarity of syntax, efficiency, or otherwise.

Let’s assume we want a border on the bottom, left, and right (but not top) of an element.

Explicitly declare each side

.three-sides { border-bottom: 2px solid black; border-right: 2px solid black; border-left: 2px solid black;
}

While that’s pretty clear, it’s still making use of shorthand. Completely expanded it would be like this:

.three-sides { border-bottom-color: black; border-bottom-style: solid; border-bottom-width: 2px; border-left-color: black; border-left-style: solid; border-left-width: 2px; border-right-color: black; border-right-style: solid; border-right-width: 2px;
}

Knock off one of the sides

You can save a little code by declaring the border on all four sides with shorthand and then removing the one you don’t want:

.three-sides { border: 2px solid black; border-top: 0;
}

Shorthand just the width

.three-sides { border-color: black; border-style: solid; /* top, right, bottom, left - just like margin and padding */ border-width: 0 2px 2px 2px;
}

As a fun little aside here, you don’t need to declare the border color to get a border to show up, because the color will inherit the currentColor! So this would work fine:

.three-sides { /* no color declared */ border-style: solid; border-width: 0 2px 2px 2px;
}

And you’d have red borders if you did:

.three-sides { border-color: red; border-style: solid; border-width: 0 2px 2px 2px;
}

Strange, but true.

If you want to add the color explicity, you can kinda mix-and-match shorthand, so this will work fine:

.three-sides { border: solid green; border-width: 2px 0 2px 2px;
}

The post How Do You Put a Border on Three Sides of an Element? appeared first on CSS-Tricks.

Decorating lines of text with box-decoration-break

An institution’s motto, an artist’s intro, a company’s tagline, a community’s principle, a service’s greeting… all of them have one thing in common: they’re one brief paragraph displayed on a website’s home page — or at least the about page!

It’s rare that just one word or one line of text welcomes you to a website. So, let’s look at some interesting ways we could style the lines of a paragraph.

To see how things currently are, let’s try giving borders to all the lines of some text in an inline span and see how it looks:

<p><span>Hummingbirds are birds from...</span></p>
span { border: 2px solid;
}

See the Pen Broken inline box. by Preethi (@rpsthecoder) on CodePen.

The edges appear broken, which they technically are, as the inline box has been fragmented into multiple lines. But we can fix those broken edges with box-decoration-break!

The box-decoration-break property in CSS can decorate the edges of fragments of a broken inline box (as well as of a page, column, and region boxes).

Its value, clone, does that with the same design that appears in the box’s unbroken edges, and its default value, slice, does not copy the decorations at the edges, keeping the break very visible like you saw in the demo above.

Let’s try it:

span { border: 2px solid; box-decoration-break: clone;
}

See the Pen Broken inline box w/ box-decoration-break. by Preethi (@rpsthecoder) on CodePen.

The property affects not only the border but also the shadow, spacing, and background of the broken edges.

Let’s play with the background first. While writing the post on knockout text, I was working with the background-clip property and wanted to see if the design held up for multiple lines of text. It didn’t.

The background gradient I applied was not replicated in every line, and after clipping it, only the first one was left with a background. That is, unless box-decoration-break: clone is added:

<p><span>Singapore:<br>Lion City</span></p>
span { background-image: linear-gradient(135deg, yellow, violet); background-clip: text; color: transparent; padding: .5em; box-decoration-break: clone;
}

See the Pen Gradient multi-line text w/box-decoration-break. by Preethi (@rpsthecoder) on CodePen.

The background-clip property with the text value clips a background to the shape of its foreground text. Since we used box-decoration-break, the gradient background is shown and clipped uniformly across all the lines of the text.

Going back to the border, let’s see how its shape and shadow can be copied across the broken edges, along with padding:

<img src="tree.png">
<p><span>Supertrees are tree-like structures...</span></p>
<img src="tree.png">
<p><span>Supertrees are tree-like structures...</span></p>
span { background: rgb(230,157,231); border-radius: 50% 0%; box-shadow: 0 0 6px rgb(41,185,82), 0 0 3px beige inset; padding: .5em 1.3em; box-decoration-break: clone;
} p:nth-of-type(2) span { background-clip: content-box;
}

See the Pen Inline border shape & shadow w/box-decoration-break. by Preethi (@rpsthecoder) on CodePen.

In the second paragraph of the demo, the background is cropped until the content box (background-clip: content-box). As you can see, the crop happens in the broken edges as well, because of box-decoration-break: clone.

Another way we can style borders is with images. You might see a gradient border around the lines of text below, covering the broken edges, if the browser you’re now using supports border-image and the application of box-decoration-break over its result.

<p><span>The Malaysia–Singapore Second Link...</span></p>
span { border: 2px solid; border-image: linear-gradient(45deg, #eeb075, #2d4944) 1; background: #eef6f3; padding: .5em 1.3em; box-decoration-break: clone;
}

See the Pen Inline border image w/ box-decoration-break. by Preethi (@rpsthecoder) on CodePen.

An additional behavior we can tap into for decorating individual lines is of outline’s. In supported browsers, box-decoration-break can add an outline to every line of the text, including the broken edges, which is useful for creating bicolored dashed borders.

<p><span>Cloud Forest replicates...</span></p>
span { outline: 2px dashed rgb(216,255,248); box-shadow: 0 0 0 2px rgb(39,144,198); background: #fffede; padding: .5em 1.3em; animation: 1s animateBorder ease infinite; box-decoration-break: clone;
} @keyframes animateBorder{ to{ outline-color: rgb(39,144,198); box-shadow: 0 0 0 2px rgb(216,255,248); }
}

See the Pen Inline outline w/ box-decoration-break. by Preethi (@rpsthecoder) on CodePen.

As observed in the demo, box-decoration-break withstands animation.

Besides borders and backgrounds, box-decoration-break can also manage shapes applied over elements. There is not much use for it in inline boxes, and is maybe better used in a column or page box, although the application is not yet widely supported in browsers.

But to show an example of what that does, let’s try applying the clip-path property to the span.

The property clip-path itself is only fully supported by Firefox, so only in it you might see an expected outcome. But following are two images: the results of applying a circular clip path over the span, without and with box-decoration-break.

span { clip-path: circle(50% at 202.1165px 69.5px); ...
}
A screenshot of a span of text being highlighted in DevTools showing that text is split up in three lines and with uneven start and end points.
Circular clip-path on a span
span { clip-path: circle(50% at 202.1165px 69.5px); box-decoration-break: clone; ...
}
A screenshot of a span of text being highlighted in DevTools showing that text is split up in three lines and with even start points but uneven end points.
Circular clip-path on a span with box-decoration-break: clone

You’ll notice in the first image that the 50% radius value is derived from the width of the inline box (the longest line) where box-decoration-break is not used.

The second image shows how box-decoration-break: clone redefines the computed value for 50% by basing them on the widths of the individual lines while keeping the center same as before.

And here’s how the inset function of clip-path (an inset rectangle) applied over the span clips it without and with box-decoration-break:

span { clip-path: inset(0); ...
}
A screenshot of a span of text being highlighted in DevTools showing that text is all on one line but the span continues for three lines with even start points but uneven end points.
Inset clip-path on a span
span { clip-path: inset(0); box-decoration-break: clone; ...
}
Inset clip-path on a span with box-decoration-break: clone

Without box-decoration-break, only a portion of the first line that matches the length of the shortest is visible, and with box-decoration-break: clone, the first line is fully visible while the rest of the box is clipped.

So, maybe if you ever want to show only the first line and hide the rest, this can come in handy. But, as I mentioned before, this application is more suitable for other types of boxes than ones that are inline. Either way, I wanted to show you how it works.

Browser Support

As we’ve seen here, box-decoraton-break can be super useful and opens up a lot of possibilities, like creating neat text effects. The property enjoys a lot support with the -webkit prefix, but is still in Working Draft at the time of this writing and lacks any support in Internet Explorer and Edge. Here’s where you can vote for Edge support.

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
69* 11 32 No No TP*

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
11.3* 11 all 62* 66* 57

Wrapping Up

The box-decoration-break: clone copies any border, spatial, and background designs applied on a fragmented inline box’s unbroken edges to its broken ones. This creates an even design across all the lines of the text, decorating them uniformly and can be super useful for all those blurbs of text that we commonly use on websites.

The post Decorating lines of text with box-decoration-break appeared first on CSS-Tricks.

Animating Border

Transitioning border for a hover state. Simple, right? You might be unpleasantly surprised.

The Challenge

The challenge is simple: building a button with an expanding border on hover.

This article will focus on genuine CSS tricks that would be easy to drop into any project without having to touch the DOM or use JavaScript. The methods covered here will follow these rules

  • Single element (no helper divs, but psuedo-elements are allowed)
  • CSS only (no JavaScript)
  • Works for any size (not restricted to a specific width, height, or aspect ratio)
  • Supports transparent backgrounds
  • Smooth and performant transition

I proposed this challenge in the Animation at Work Slack and again on Twitter. Though there was no consensus on the best approach, I did receive some really clever ideas by some phenomenal developers.

Method 1: Animating border

The most straightforward way to animate a border is… well, by animating border.

.border-button { border: solid 5px #FC5185; transition: border-width 0.6s linear;
} .border-button:hover { border-width: 10px; }

See the Pen by Shaw (@shshaw) on CodePen.

Nice and simple, but there are some big performance issues.

Since border takes up space in the document’s layout, changing the border-width will trigger layout. Nearby elements will shift around because of the new border size, making browser reposition those elements every frame of the animation unless you set an explicit size on the button.

As if triggering layout wasn’t bad enough, the transition itself feels “stepped”. I’ll show why in the next example.

Method 2: Better border with outline

How can we change the border without triggering layout? By using outline instead! You’re probably most familiar with outline from removing it on :focus styles (though you shouldn’t), but outline is an outer line that doesn’t change an element’s size or position in the layout.

.border-button { outline: solid 5px #FC5185; transition: outline 0.6s linear; margin: 0.5em; /* Increased margin since the outline expands outside the element */
} .border-button:hover { outline-width: 10px; }

See the Pen by Shaw (@shshaw) on CodePen.

A quick check in Dev Tools’ Performance tab shows the outline transition does not trigger layout. Regardless, the movement still seems stepped because browsers are rounding the border-width and outline-width values so you don’t get sub-pixel rendering between 5 and 6 or smooth transitions from 5.4 to 5.5.

See the Pen by Shaw (@shshaw) on CodePen.

Strangely, Safari often doesn’t render the outline transition and occasionally leaves crazy artifacts.

border artifact in safari

Method 3: Cut it with clip-path

First implemented by Steve Gardner, this method uses clip-path with calc to trim the border down so on hover we can transition to reveal the full border.

.border-button { /* Full width border and a clip-path visually cutting it down to the starting size */ border: solid 10px #FC5185; clip-path: polygon( calc(0% + 5px) calc(0% + 5px), /* top left */ calc(100% - 5px) calc(0% + 5px), /* top right */ calc(100% - 5px) calc(100% - 5px), /* bottom right */ calc(0% + 5px) calc(100% - 5px) /* bottom left */ ); transition: clip-path 0.6s linear;
} .border-button:hover { /* Clip-path spanning the entire box so it's no longer hiding the full-width border. */ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}

See the Pen by Shaw (@shshaw) on CodePen.

clip-path technique is the smoothest and most performant method so far, but does come with a few caveats. Rounding errors may cause a little unevenness, depending on the exact size. The border also has to be full size from the start, which may make exact positioning tricky.

Unfortunately there’s no IE/Edge support yet, though it seems to be in development. You can and should encourage Microsoft’s team to implement those features by voting for masks/clip-path to be added.

Method 4: linear-gradient background

We can simulate a border using a clever combination of multiple linear-gradient backgrounds properly sized. In total we have four separate gradients, one for each side. The background-position and background-size properties get each gradient in the right spot and the right size, which can then be transitioned to make the border expand.

.border-button { background-repeat: no-repeat; /* background-size values will repeat so we only need to declare them once */ background-size: calc(100% - 10px) 5px, /* top & bottom */ 5px calc(100% - 10px); /* right & left */ background-position: 5px 5px, /* top */ calc(100% - 5px) 5px, /* right */ 5px calc(100% - 5px), /* bottom */ 5px 5px; /* left */ /* Since we're sizing and positioning with the above properties, we only need to set up a simple solid-color gradients for each side */ background-image: linear-gradient(0deg, #FC5185, #FC5185), linear-gradient(0deg, #FC5185, #FC5185), linear-gradient(0deg, #FC5185, #FC5185), linear-gradient(0deg, #FC5185, #FC5185); transition: all 0.6s linear; transition-property: background-size, background-position;
} .border-button:hover { background-position: 0 0, 100% 0, 0 100%, 0 0; background-size: 100% 10px, 10px 100%, 100% 10px, 10px 100%;
}

See the Pen by Shaw (@shshaw) on CodePen.

This method is quite difficult to set up and has quite a few cross-browser differences. Firefox and Safari animate the faux-border smoothly, exactly the effect we’re looking for. Chrome’s animation is jerky and even more stepped than the outline and border transitions. IE and Edge refuse to animate the background at all, but they do give the proper border expansion effect.

Method 5: Fake it with box-shadow

Hidden within box-shadow‘s spec is a fourth value for spread-radius. Set all the other length values to 0px and use the spread-radius to build your border alternative that, like outline, won’t affect layout.

.border-button { box-shadow: 0px 0px 0px 5px #FC5185; transition: box-shadow 0.6s linear; margin: 0.5em; /* Increased margin since the box-shado expands outside the element, like outline */
} .border-button:hover { box-shadow: 0px 0px 0px 10px #FC5185; }

See the Pen by Shaw (@shshaw) on CodePen.

The transition with box-shadow is adequately performant and feels much smoother, except in Safari where it’s snapping to whole-values during the transition like border and outline.

Pseudo-Elements

Several of these techniques can be modified to use a pseudo-element instead, but pseudo-elements ended up causing some additional performance issues in my tests.

For the box-shadow method, the transition occasionally triggered paint in a much larger area than necessary. Reinier Kaper pointed out that a pseudo-element can help isolate the paint to a more specific area. As I ran further tests, box-shadow was no longer causing paint in large areas of the document and the complication of the pseudo-element ended up being less performant. The change in paint and performance may have been due to a Chrome update, so feel free to test for yourself.

I also could not find a way to utilize pseudo-elements in a way that would allow for transform based animation.

Why not transform: scale?

You may be firing up Twitter to helpfully suggest using transform: scale for this. Since transform and opacity are the best style properties to animate for performance, why not use a pseudo-element and have the border scale up & down?

.border-button { position: relative; margin: 0.5em; border: solid 5px transparent; background: #3E4377;
} .border-button:after { content: ''; display: block; position: absolute; top: 0; right: 0; bottom: 0; left: 0; border: solid 10px #FC5185; margin: -15px; z-index: -1; transition: transform 0.6s linear; transform: scale(0.97, 0.93);
} .border-button:hover::after { transform: scale(1,1); }

See the Pen by Shaw (@shshaw) on CodePen.

There are a few issues:

  1. The border will show through a transparent button. I forced a background on the button to show how the border is hiding behind the button. If your design calls for buttons with a full background, then this could work.
  2. You can’t scale the border to specific sizes. Since the button’s dimensions vary with the text, there’s no way to animate the border from exactly 5px to 10px using only CSS. In this example I’ve done some magic-numbers on the scale to get it to appear right, but that won’t be universal.
  3. The border animates unevenly because the button’s aspect ratio isn’t 1:1. This usually means the left/right will appear larger than the top/bottom until the animation completes. This may not be an issue depending on how fast your transition is, the button’s aspect ratio, and how big your border is.

If your button has set dimensions, Cher pointed out a clever way to calculate the exact scales needed, though it may be subject to some rounding errors.

Beyond CSS

If we loosen our rules a bit, there are many interesting ways you can animate borders. Codrops consistently does outstanding work in this area, usually utilizing SVGs and JavaScript. The end results are very satisfying, though they can be a bit complex to implement. Here are a few worth checking out:

  • Creative Buttons
  • Button Styles Inspiration
  • Animated Checkboxes
  • Distorted Button Effects
  • Progress Button Styles

Conclusion

There’s more to borders than simply border, but if you want to animate a border you may have some trouble. The methods covered here will help, though none of them are a perfect solution. Which you choose will depend on your project’s requirements, so I’ve laid out a comparison table to help you decide.

See the Pen by Shaw (@shshaw) on CodePen.

My recommendation would be to use box-shadow, which has the best overall balance of ease-of-implementation, animation effect, performance and browser support.

Do you have another way of creating an animated border? Perhaps a clever way to utilize transforms for moving a border? Comment below or reach me on Twitter to share your solution to the challenge.

Special thanks to Martin Pitt, Steve Gardner, Cher, Reinier Kaper, Joseph Rex, David Khourshid, and the Animation at Work community.


Animating Border is a post from CSS-Tricks