Tag: Hover

Bold on Hover… Without the Layout Shift

When you change the font-weight of a font, the text will typically cause a bit of a layout shift. That’s because bold text is often larger and takes up more space. Sometimes that doesn’t matter, like a vertical stack of links where the wider/bolder text doesn’t push anything anyway. Sometimes it does matter, like a horizontal row where the wider/bolder text pushes other elements away a smidge.

Ryan Mulligan demonstrates:

Ryan’s technique is very clever. Each item in the list has a pseudo-element on it with the exact text in the link. That pseudo-element is visually hidden, but pre-bolded and still occupies width. So when the actual link text is bolded, it won’t take up any additional width.

It also sorta depends on how you’re doing the layout. Here, if I force four columns with CSS grid and text that doesn’t really challenge the width, the bolding doesn’t affect the layout either:

But if I were to, say, let those links flow into automatic columns, we would have the shifting problem.


The post Bold on Hover… Without the Layout Shift appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,

4 Ways to Animate the Color of a Text Link on Hover

Let’s create a pure CSS effect that changes the color of a text link on hover… but slide that new color in instead of simply swapping colors.

There are four different techniques we can use to do this. Let’s look at those while being mindful of important things, like accessibility, performance, and browser support in mind.

Let’s get started!

Technique 1: Using background-clip: text

At the time of writing, the background-clip: text property is an experimental feature and is not supported in Internet Explorer 11 and below.

This technique involves creating knockout text with a hard stop gradient. The markup consists of a single HTML link (<a>) element to create a hyperlink:

<a href="#">Link Hover</a>

We can start adding styles to the hyperlink. Using overflow: hidden will clip any content outside of the hyperlink during the hover transition:

a {   position: relative;   display: inline-block;   font-size: 2em;   font-weight: 800;   color: royalblue;   overflow: hidden; }

We will need to use a linear gradient with a hard stop at 50% to the starting color we want the link to be as well as the color that it will change to:

a {   /* Same as before */   background: linear-gradient(to right, midnightblue, midnightblue 50%, royalblue 50%); }

Let’s use background-clip to clip the gradient and the text value to display the text. We will also use the background-size and background-position properties to have the starting color appear:

a {   /* Same as before */   background-clip: text;   -webkit-background-clip: text;   -webkit-text-fill-color: transparent;   background-size: 200% 100%;   background-position: 100%; }

Finally, let’s add the transition CSS property and :hover CSS pseudo-class to the hyperlink. To have the link fill from left to right on hover, use the background-position property:

a {   /* Same as before */   transition: background-position 275ms ease; } a:hover {   background-position: 0 100%; }

While this technique does achieve the hover effect, Safari and Chrome will clip text decorations and shadows, meaning they won’t be displayed. Applying text styles, such as an underline, with the text-decoration CSS property will not work. Perhaps consider using other approaches when creating underlines.

Technique 2: Using width/height

This works by using a data attribute containing the same text as the one in the <a> tag and setting the width (filling the text from left-to-right or right-to-left) or height (filling the text from top-to-bottom or bottom-to-top), from 0% to 100% on hover.

Here is the markup:

<a href="#" data-content="Link Hover">Link Hover</a>

The CSS is similar to the previous technique minus the background CSS properties. The text-decoration property will work here:

a {   position: relative;   display: inline-block;   font-size: 2em;   color: royalblue;   font-weight: 800;   text-decoration: underline;   overflow: hidden; }

This is when we need to use the content from the data-content attribute. It will be positioned above the content in the <a> tag. We get to use the nice little trick of copying the text in the data attribute and displaying it via the attr() function on the content property of the element’s ::before pseudo-element.

a::before {   position: absolute;   content: attr(data-content); /* Prints the value of the attribute */   top: 0;   left: 0;   color: midnightblue;   text-decoration: underline;   overflow: hidden;   transition: width 275ms ease; }

To keep the text from wrapping to the next line, white-space: nowrap will be applied. To change the link fill color, set the value for the color CSS property using the ::before pseudo-element and having the width start at 0:

a::before {   /* Same as before */   width: 0;   white-space: nowrap; }

Increase the width to 100% to the ::before pseudo element to complete the text effect on hover:

a:hover::before {   width: 100%; }

While this technique does the trick, using the width or height properties will not produce a performant CSS transition. It is best to use either the transform or opacity properties to achieve a smooth, 60fps transition.

Using the text-decoration CSS property can allow for different underline styles to appear in the CSS transition. I created a demo showcasing this using the next technique: the clip-path CSS property.

Technique 3: Using clip-path

For this technique, we will be using the clip-path CSS property with a polygon shape. The polygon will have four vertices, with two of them expanding to the right on hover:

The markup is the same as the previous technique. We will use a ::before pseudo-element again, but the CSS is different:

a::before {   position: absolute;   content: attr(data-content);   color: midnightblue;   text-decoration: underline;   clip-path: polygon(0 0, 0 0, 0% 100%, 0 100%);   transition: clip-path 275ms ease; }

Unlike the previous techniques, text-decoration: underline must be declared to the ::before pseudo-element for the color to fill the underline on hover.

Now let’s look into the CSS for the clip-path technique:

clip-path: polygon(0 0, 0 0, 0% 100%, 0 100%);

The polygon’s vertices of the clip-path property are set in percentages to define coordinates by the order written:

  • 0 0 = top left
  • 0 0 = top right
  • 100% 0 = bottom right
  • 0 100% = bottom left

The direction of the fill effect can be changed by modifying the coordinates. Now that we have an idea for the coordinates, we can make the polygon expand to the right on hover:

a:hover::before {   clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }

This technique works pretty well, but note that support for the clip-path property varies between browsers. Creating a CSS transition with clip-path is a better alternative than using the width/height technique; however, it does affect the browser paint.

Technique 4: Using transform

The markup for this technique uses a masking method with a <span> element. Since we will be using duplicated content in a separate element, we will use aria-hidden="true" to improve accessibility — that will hide it from screen readers so the content isn’t read twice:

<a href="#"><span data-content="Link Hover" aria-hidden="true"></span>Link Hover</a>

The CSS for the <span> element contains a transition that will be starting from the left:

span {   position: absolute;   top: 0;   left: 0;   overflow: hidden;   transform: translateX(-100%);   transition: transform 275ms ease; }

Next, we need to get the <span> to slide the right like this:

To do this, we will use the translateX() CSS function and set it to 0:

a:hover span {   transform: translateX(0); }

Then, we will use the ::before pseudo-element for the <span>, again using the data-content attribute we did before. We’ll set the position by translating it 100% along the x-axis.

span::before {    display: inline-block;   content: attr(data-content);   color: midnightblue;   transform: translateX(100%);   transition: transform 275ms ease;   text-decoration: underline; }

Much like the <span> element, the position of the ::before pseudo-element will also be set to  translateX(0):

a:hover span::before {   transform: translateX(0); }

While this technique is the the most cross-browser compatible of the bunch, it requires more markup and CSS to get there. That said, using the transform CSS property is great for performance as it does not trigger repaints and thus produces smooth, 60fps CSS transitions.

There we have it!

We just looked at four different techniques to achieve the same effect. Although each has its pros and cons, you can see that it’s totally possible to slide in a color change on text. It’s a neat little effect that makes links feel a little more interactive.

The post 4 Ways to Animate the Color of a Text Link on Hover appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Solving Sticky Hover States with @media (hover: hover)

Mezo Istvan does a good job of covering the problem and a solution to it in a blog post on Medium¹.

If you tap on something that has a :hover state but you don’t leave the page then, on a mobile device, there is a chance that :hover state “sticks.” You’ll see this with stuff like jump-links used as tabs or buttons that trigger on-page functionality.

button:hover {   border: 3px solid green; /* might stick! */ }

The solution, or trick, is a new(ish) “CSS4” media query that allows you only to apply styles on devices with hover capability.

@media (hover: hover) {   button:hover {     border: 3px solid green; /* solves sticky problem */   } }

Your typical touch screen mobile device will fail that media query, the style won’t apply, and you’ll avoid the sticky problem.

Support is solid, so not much worry there.

  1. It almost feels like we have to apologize to linking to things on Medium lately. I have no idea what you’re going to experience when you get there. Will you just be able to read it? Will it be a teaser where you have to log in to read more? Will it be behind a paywall? I have no idea. In this case, hopefully, this link post has enough info in it that isn’t not blocking you from learning anything.

Direct Link to ArticlePermalink

The post Solving Sticky Hover States with @media (hover: hover) appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Change Color of SVG on Hover

There are a lot of different ways to use SVG. Depending on which way, the tactic for recoloring that SVG in different states or conditions — :hover, :active, :focus, class name change, etc. — is different.

Let’s look at the ways.

Inline SVG

Inline SVG is my favorite way to use SVG anyway, in part because of how easy it is to access and style the SVG.

See the Pen
bJXNyy
by Chris Coyier (@chriscoyier)
on CodePen.

If you’re used to working with icon fonts, one thing you might enjoy about them is how easy it is to change the color. You’re largely limited to a single color with icon fonts in a way that SVG isn’t, but still, it is appealingly easy to change that single color with color. Using inline SVG allows you to set the fill, which cascades to all the elements within the SVG, or you can fill each element separately if needed.

SVG Symbol / Use

There is such thing as an SVG sprite, which is a group of SVGs turned into <symbol> elements such that any given icon can be referenced easily with a <use> element.

See the Pen
Use SVG Hovers
by Chris Coyier (@chriscoyier)
on CodePen.

You can still set the fill color from outside CSS rather easily this way, but there are caveats.

  • The internal SVG elements (like the <path>) can have no fill themselves. This allows the fill set from the parent SVG to cascade into the Shadow DOM created by <use>. As soon as you have something like <path fill="blue" ... /> in the <symbol>, you’ve lost outside CSS control.
  • Likewise, the fill of individual elements cannot be controlled within the SVG like you could with inline SVG. This means you’re pretty firmly in single-color territory. That covers most use cases anyway, but still, a limitation nonetheless.

SVG background images

SVG can be set as a background image just like PNG, JPG, or whatever other graphics format. At this point, you’ve sort of given up on being able to change the fill. One possibility, which I’d argue isn’t a particularly good one, is to have two versions of every icon, in the respective colors, and swap between them:

See the Pen
Background SVG Hovers
by Chris Coyier (@chriscoyier)
on CodePen.

I don’t blame you if you’d rather not swap sources, so another possibility is to get gnarly with filters.

See the Pen
Background SVG Hovers with Filters
by Chris Coyier (@chriscoyier)
on CodePen.

Trying to finagle the right filters to get the color right is tricky stuff. Fortunately, Barrett Sonntag made a tool to calculate the filters for you! Turning black to red ends up a whacky combination like this: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);.

SVG also has object, which is kinda neat in that it had a built-in fallback back in the day — although browser support is so good these days, I honestly have never used it. But if you’re using it, you would probably have to use this filter technique to swap color on hover.

See the Pen
Background SVG Object Hovers
by Chris Coyier (@chriscoyier)
on CodePen.

Use a mask instead of a background image

This way, the SVG is still in charge of essentially drawing the shape, but the color comes from the background-color (or image! or gradient!) behind it rather than the SVG itself.

See the Pen
Background SVG Hovers with Mask
by Chris Coyier (@chriscoyier)
on CodePen.

SVG background images as data URLs

This doesn’t change that much from above, but it does open up one interesting possibility: Using a variable for the internal fills. Here that is with Sass keeping the URLs as variables:

See the Pen
Background SVG Hovers with Data URL variables
by Chris Coyier (@chriscoyier)
on CodePen.

You can also do this with native CSS custom properties!

The post Change Color of SVG on Hover appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

The Many Ways to Change an SVG Fill on Hover (and When to Use Them)

SVG is a great format for icons. Vector formats look crisp and razor sharp, no matter the size or device — and we get tons of design control when using them inline.

SVG also gives us another powerful feature: the ability to manipulate their properties with CSS. As a result, we can make quick and simple interactions where it used to take crafty CSS tricks or swapping out entire image files.

Those interactions include changing color on hover states. It sounds like such a straightforward thing here in 2019, but there are actually a few totally valid ways to go about it — which only demonstrates the awesome powers of SVG more.

First off, let’s begin with a little abbreviated SVG markup:

<svg class="icon">   <path .../> </svg>

Target the .icon class in CSS and set the SVG fill property on the hover state to swap colors.

.icon:hover {   fill: #DA4567; }

This is by far the easiest way to apply a colored hover state to an SVG. Three lines of code!

SVGs can also be referenced using an <img> tag or as a background image. This allows the images to be cached and we can avoid bloating your HTML with chunks of SVG code. But the downside is a big one: we no longer have the ability to manipulate those properties using CSS. Whenever I come across non-inline icons, my first port of call is to inline them, but sometimes that’s not an option.

I was recently working on a project where the social icons were a component in a pattern library that everyone was happy with. In this case, the icons were being referenced from an <img> element. I was tasked with applying colored :focus and :hover styles, without adjusting the markup.

So, how do you go about adding a colored hover effect to an icon if it’s not an inline SVG?

CSS Filters

CSS filters allow us to apply a whole bunch of cool, Photoshop-esque effects right in the browser. Filters are applied to the element after the browser renders layout and initial paint, which means they fall back gracefully. They apply to the whole element, including children. Think of a filter as a lens laid over the top of the element it’s applied to.

These are the CSS filters available to us:

  • brightness(<number-percentage>);
  • contrast(<number-percentage>);
  • grayscale(<number-percentage>);
  • invert(<number-percentage>);
  • opacity(<number-percentage>);
  • saturate(<number-percentage>);
  • sepia(<number-percentage>);
  • hue-rotate(<angle>);
  • blur(<length>);
  • drop-shadow(<length><color>);

All filters take a value which can be changed to adjust the effect. In most cases, this value can be expressed in either a decimal or percent units (e.g. brightness(0.5) or brightness(50%)).

Straight out of the box, there’s no CSS filter that allows us to add our own specific color.
We have hue-rotate(), but that only adjusts an existing color; it doesn’t add a color, which is no good since we’re starting with a monochromatic icon.

The game-changing bit about CSS filters is that we don’t have to use them in isolation. Multiple filters can be applied to an element by space-separating the filter functions like this:

.icon:hover {   filter: grayscale(100%) sepia(100%); }

If one of the filter functions doesn’t exist, or has an incorrect value, the whole list is ignored and no filter will be applied to the element.

When applying multiple filter functions to an element, their order is important and will affect the final output. Each filter function will be applied to the result of the previous operation.

So, in order to colorize our icons, we have to find the right combination.

To make use of hue-rotate(), we need to start off with a colored icon. The sepia() filter is the only filter function that allows us to add a color, giving the filtered element a yellow-brown-y tinge, like an old photo.

The output color is dependent on the starting tonal value:

In order to add enough color with sepia(), we first need to use invert() to convert our icon to a medium grey:

.icon:hover {   filter: invert(0.5) }

We can then add the yellow/brown tone with sepia():

.icon:hover {   filter: invert(0.5) sepia(1); }

…then change the hue with hue-rotate():

.icon:hover {   filter: invert(0.5) sepia(1) hue-rotate(200deg);                                   }

Once we have the rough color we want, we can tweak it with saturation() and brightness():

.icon:hover {   filter:      invert(0.5)     sepia(1)     hue-rotate(200deg)     saturate(4)     brightness(1); }

I’ve made a little tool for this to make your life a little easier, as this is a pretty confusing process to guesstimate.

See the Pen CSS filter example by Cassie Evans (@cassie-codes)
on CodePen.

Even with the tool, it’s still a little fiddly, not supported by Internet Explorer, and most importantly, you’re unable to specify a precise color.

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
18* 15* 35 No 18 6*

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
6.0-6.1* 46 No 4.4* 71 64

So, what do we do if we need a specific hex code?

SVG Filters

If we need more precise control (and better browser support) than CSS filters can offer, then it’s time to turn to SVG.

Filters originally came from SVG. In fact, under the hood, CSS filters are just shortcuts to SVG filters with a particular set of values baked in.

Unlike CSS, the filter isn’t predefined for us, so we have to create it. How do we do this?

This is the syntax to define a filter:

<svg xmlns="<http://www.w3.org/2000/svg>" version="1.1">   <defs>     <filter id="id-of-your-filter">       ...                        ...     </filter>     ...   </defs> </svg>

Filters are defined by a <filter> element, which goes inside the <defs> section of an SVG.

SVG filters can be applied to SVG content within the same SVG document. Or, the filter can be referenced and applied to HTML content elsewhere.

To apply an SVG filter to HTML content, we reference it the same way as a CSS filter: by using the url() filter function. The URL points to the ID of the SVG filter.

.icon:hover {   filter: url('#id-of-your-filter'); }

The SVG filter can be placed inline in the document or the filter function can reference an external SVG. I prefer the latter route as it allows me to keep my SVG filters tidied away in an assets folder.

.icon:hover {   filter: url('assets/your-SVG.svg#id-of-your-filter'); }

Back to the <filter> element itself.

<filter id="id-of-your-filter">   ...                ... </filter>

Right now, this filter is empty and won’t do anything as we haven’t defined a filter primitive. Filter primitives are what create the filter effects. There are a number of filter primitives available to us, including:

  • [<feBlend>]
  • [<feColorMatrix>]
  • [<feComponentTransfer>]
  • [<feComposite>]
  • [<feConvolveMatrix>]
  • [<feDiffuseLighting>]
  • [<feDisplacementMap>]
  • [<feDropShadow>]
  • [<feFlood>]
  • [<feGaussianBlur>]
  • [<feImage>]
  • [<feMerge>]
  • [<feMorphology>]
  • [<feOffset>]
  • [<feSpecularLighting>]
  • [<feTile>]
  • [<feTurbulence>]

Just like with CSS filters, we can use them on their own or include multiple filter primitives in the <filter> tag for more interesting effects. If more than one filter primitive is used, then each operation will build on top of the previous one.

For our purposes we’re just going to use feColorMatrix, but if you want to know more about SVG filters, you can check out the specs on MDN or this (in progress, at the time of this writing) article series that Sara Soueidan has kicked off.

feColourMatrix allows us to change color values on a per-channel basis, much like channel mixing in Photoshop.

This is what the syntax looks like:

<svg xmlns="<http://www.w3.org/2000/svg>" version="1.1">   <defs>     <filter id="id-of-your-filter">       <feColorMatrix         color-interpolation-filters="sRGB"         type="matrix"         values="1 0 0 0 0                 0 1 0 0 0                 0 0 1 0 0                 0 0 0 1 0 "/>     </filter>     ...   </defs> </svg>

Let’s have a closer look at the color matrix values.

The first four columns represent the red, green and blue channels of color and the alpha (opacity) value. The rows contain the red, green, blue and alpha values in those channels.

The M column is a multiplier — we don’t need to change any of these values for our purposes here. The values for each color channel are represented as floating point numbers in the range 0 to 1.

We could write these values as a CSS RGBA color declaration like this:

rgba(255, 255, 255, 1)

The values for each color channel (red, green and blue) are stored as integers in the range 0 to 255. In computers, this is the range that one 8-bit byte can offer.

By dividing these color channel values by 255, the values can be represented as a floating point number which we can use in the feColorMatrix.

And, by doing this, we can create a color filter for any color with an RGB value!

Like teal, for example:

rgba(0, 128, 128, 1). 128%255=0.50

See the Pen
SVG filter – teal hover
by Cassie Evans (@cassie-codes)
on CodePen.

This SVG filter will only impart color to icons with a white fill, so If we have an icon with a black fill, we can use invert() to convert it to white before applying the SVG filter.

.icon:hover {   filter: invert(100%) url('assets/your-SVG.svg#id-of-your-filter'); }

If we just have a hex code, the math is a little trickier, although there are plenty of hex-to-RGBA converters out there. To help out, I’ve made a HEX to feColorMatrix converter.

See the Pen
HEX to feColorMatrix converterr
by Cassie Evans (@cassie-codes)
on CodePen.

Have a play around, and happy filtering!

The post The Many Ways to Change an SVG Fill on Hover (and When to Use Them) appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]