background-position – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Thu, 09 Feb 2023 15:03:59 +0000 en-US hourly 1 https://wordpress.org/?v=6.2.2 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 background-position – CSS-Tricks https://css-tricks.com 32 32 45537868 Moving Backgrounds https://css-tricks.com/moving-backgrounds/ https://css-tricks.com/moving-backgrounds/#comments Thu, 09 Feb 2023 15:03:55 +0000 https://css-tricks.com/?p=376723 We often think of background images as texture or something that provides contrast for legible content — in other words, not really content. If it was content, you’d probably reach for an <img> anyway, accessibility and whatnot.

But there are …


Moving Backgrounds originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We often think of background images as texture or something that provides contrast for legible content — in other words, not really content. If it was content, you’d probably reach for an <img> anyway, accessibility and whatnot.

But there are times when the position or scale of a background image might sit somewhere between the poles of content and decoration. Context is king, right? If we change the background image’s position, it may convey a bit more context or experience.

How so? Let’s look at a few examples I’ve seen floating around.

As we get started, I’ll caution that there’s a fine line in these demos between images used for decoration and images used as content. The difference has accessibility implications where backgrounds are not announced to screen readers. If your image is really an image, then maybe consider an <img> tag with proper alt text. And while we’re talking accessibility, it’s a good idea to consider a user’s motion preference’s as well.

Show me more!

Chris Coyier has this neat little demo from several years back.

The demo is super practical in lots of ways because it’s a neat approach for displaying ads in content. You have the sales pitch and an enticing image to supplement it.

The big limitation for most ads, I’d wager, is the limited real estate. I don’t know if you’ve ever had to drop an ad onto a page, but I have and typically ask the advertiser for an image that meets exact pixel dimensions, so the asset fits the space.

But Chris’s demo alleviates the space issue. Hover the image and watch it both move and scale. The user actually gets more context for the product than they would have when the image was in its original position. That’s a win-win, right? The advertiser gets to create an eye-catching image without compromising context. Meanwhile, the user gets a little extra value from the newly revealed portions of the image.

If you peek at the demo’s markup, you’ll notice it’s pretty much what you’d expect. Here’s an abridged version:

<div class="ad-container">
  <a href="#" target="_blank" rel="noopener">
    <!-- Background image container  -->
    <div class="ad-image"></div>
  </a> 
  <div class="ad-content">
    <!-- Content -->
  </div>
</div>

We could probably quibble over the semantics a bit, but that’s not the point. We have a container with a linked-up <div> for the background image and another <div> to hold the content.

As far as styling goes, the important pieces are here:

.container {
  background-image: url("/path/to/some/image.png");
  background-repeat: no-repeat;
  background-position: 0 0;
  height: 400px;
  width: 350px;
}

Not bad, right? We give the container some dimensions and set a background image on it that doesn’t repeat and is positioned by its bottom-left edge.

The real trick is with JavaScript. We will use that to get the mouse position and the container’s offset, then convert that value to an appropriate scale to set the background-position. First, let’s listen for mouse movements on the .container element:

let container = document.querySelector(".container");
container.addEventListener("mousemove", function(e) {
    // Our function
  }
);

From here, we can use the container’s offsetX and offsetY properties. But we won’t use these values directly, as the value for the X coordinate is smaller than what we need, and the Y coordinate is larger. We will have to play around a bit to find a constant that we can use as a multiplier.

It’s a bit touch-and-feel, but I’ve found that 1.32 and 0.455 work perfectly for the X and Y coordinates, respectively. We multiply the offsets by those values, append a px unit on the result, then apply it to the background-position values.

let container = document.querySelector(".container");
container.addEventListener("mousemove", function(e) {
    container.style.backgroundPositionX = -e.offsetX * 1.32 + "px";
    container.style.backgroundPositionY = -e.offsetY * 0.455 + "px";
  }
);

Lastly, we can also reset the background positions back to the original if the user leaves the image container.

container.addEventListener("mouseleave", function() {
    container.style.backgroundPosition = "0px 0px";
  }
);

Since we’re on CSS-Tricks, I’ll offer that we could have done a much cheaper version of this with a little hover transition in vanilla CSS:

Paint a bigger picture

No doubt you’ve been to some online clothing store or whatever and encountered the ol’ zoom-on-hover feature.

This pattern has been around for what feels like forever (Dylan Winn-Brown shared his approach back in 2016), but that’s just a testament (I hope) to its usefulness. The user gets more context as they zoom in and get a better idea of a sweater’s stitching or what have you.

There’s two pieces to this: the container and the magnifier. The container is the only thing we need in the markup, as we’ll inject the magnifier element during the user’s interaction. So, behold our HTML!

<div class="container"></div>

​​In the CSS, we will create width and height variables to store the dimensions of the of the magnifier glass itself.  Then we’ll give that .container​ some shape and a background-image​:

​​:root {
​​  --magnifer-width: 85;
​​  --magnifer-height: 85;
​​}

.container {
  width: 500px;
  height: 400px;
  background-size: cover;
  background-image: url("/path/to/image.png");
  background-repeat: no-repeat;
  position: relative;
}

There are some things we already know about the magnifier before we even see it, and we can define those styles up-front, specifically the previously defined variables for the .maginifier‘s width and height:

.magnifier {
  position: absolute;
  width: calc(var(--magnifer-width) * 1px);
​​  height: calc(var(--magnifer-height) * 1px);
​​  border: 3px solid #000;
​​  cursor: none;
​​  background-image: url("/path/to/image.png");
​​  background-repeat: no-repeat;
}

It’s an absolutely-positioned little square that uses the same background image file as the .container. Do note that the calc function is solely used here to convert the unit-less value in the variable to pixels. Feel free to arrange that however you see fit as far as eliminating repetition in your code.

Now, let’s turn to the JavaScript that pulls this all together. First we need to access the CSS variable defined earlier. We will use this in multiple places later on. Then we need get the mouse position within the container because that’s the value we’ll use for the the magnifier’s background position.

​​// Get the css variables
​​let root = window.getComputedStyle(document.documentElement);
​​let magnifier_width = root.getPropertyValue("--magnifer-width");
​​let magnifier_height = root.getPropertyValue("--magnifer-height");

let container = document.querySelector(".container");
let rect = container.getBoundingClientRect();
let x = (e.pageX - rect.left);
let y = (e.pageY - rect.top);

// Take page scrolling into account
x = x - window.pageXOffset;
y = y - window.pageYOffset;

What we need is basically a mousemove event listener on the .container. Then, we will use the event.pageX or event.pageY property to get the X or Y coordinate of the mouse. But to get the exact relative position of the mouse on an element, we need to subtract the position of the parent element from the mouse position we get from the JavaScript above. A “simple” way to do this is to use getBoundingClientRect(), which returns the size of an element and its position relative to the viewport.

Notice how I’m taking scrolling into account. If there is overflow, subtracting the window pageX and pageY offsets will ensure the effect runs as expected.

We will first create the magnifier div. Next, we will create a mousemove function and add it to the image container. In this function, we will give the magnifier a class attribute. We will also calculate the mouse position and give the magnifier the left and top values we calculated earlier.

Let’s go ahead and build the magnifier when we hear a mousemove event on the .container:

// create the magnifier
let magnifier = document.createElement("div");
container.append(magnifier);

Now we need to make sure it has a class name we can scope to the CSS:

// run the function on `mousemove`
container.addEventListener("mousemove", (e) => {
  magnifier.setAttribute("class", "magnifier");
}

The example video I showed earlier positions the magnifier outside of the container. We’re gonna keep this simple and overlay it on top of the container instead as the mouse moves. We will use if statements to set the magnifier’s position only if the X and Y values are greater or equal to zero, and less than the container’s width or height. That should keep it in bounds. Just be sure to subtract the width and height of the magnifier from the X and Y values.

// Run the function on mouse move.
container.addEventListener("mousemove", (e) => {
  magnifier.setAttribute("class", "magnifier");

  // Get mouse position
  let rect = container.getBoundingClientRect();
  let x = (e.pageX - rect.left);
  let y = (e.pageY - rect.top);
  
  // Take page scrolling into account
  x = x - window.pageXOffset;
  y = y - window.pageYOffset;

  // Prevent magnifier from exiting the container
  // Then set top and left values of magnifier
  if (x >= 0 && x <= container.clientWidth - magnifier_width) {
    magnifier.style.left = x + "px";
  }
  if (y >= 0 && y <= container.clientHeight - magnifier_height) {
    magnifier.style.top = y + "px";
  }
});

Last, but certainly not least… we need to play with the magnifier’s background image a bit. The whole point is that the user gets a BIGGER view of the background image based on where the hover is taking place. So, let’s define a magnifier we can use to scale things up. Then we’ll define variables for the background image’s width and height so we have something to base that scale on, and set all of those values on the .magnifier styles:

// Magnifier image configurations
let magnify = 2;
let imgWidth = 500;
let imgHeight = 400;

magnifier.style.backgroundSize = imgWidth * magnify + "px " + imgHeight * magnify + "px";

​​Let’s take the X and Y coordinates of the magnifier’s image and apply them to the .magnifier​ element’s background-position​. As before with the magnifier position, we need to subtract the width and height of the magnifier from the X and Y values using the CSS variables.

// the x and y positions of the magnifier image
let magnify_x = x * magnify + 15;
let magnify_y = y * magnify + 15;

// set backgroundPosition for magnifier if it is within image
if (
  x <= container.clientWidth - magnifier_width &&
  y <= container.clientHeight - magnifier_height
) {
  magnifier.style.backgroundPosition = -magnify_x + "px " + -magnify_y + "px";
}

Tada!

Make it cinematic

Have you seen the Ken Burns effect? It’s classic and timeless thing where an image is bigger than the container it’s in, then sorta slides and scales slow as a slug. Just about every documentary film in the world seems to use it for image stills. If you have an Apple TV, then you’ve certainly seen it on the screen saver.

There are plenty of examples over at CodePen if you wanna get a better idea.

You’ll see that there are a number of ways to approach this. Some use JavaScript. Others are 100% CSS. I’m sure the JavaScript approaches are good for some uses cases, but if the goal is simply to subtly scale the image, CSS is perfectly suitable.

We could spice things up a bit using multiple backgrounds rather than one. Or, better yet, if we expand the rules to use elements instead of background images, we can apply the same animation to all of the backgrounds and use a dash of animation-delay to stagger the effect.

Lots of ways to do this, of course! It can certainly be optimized with Sass and/or CSS variables. Heck, maybe you can pull it off with a single <div> If so, share it in the comments!

Bonus: Make it immersive

I don’t know if anything is cooler than Sarah Drasner’s “Happy Halloween” pen… and that’s from 2016! It is a great example that layers backgrounds and moves them at varying speeds to create an almost cinematic experience. Good gosh is that cool!

GSAP is the main driver there, but I imagine we could make a boiled-down version that simply translates each background layer from left to right at different speeds. Not as cool, of course, but certainly the baseline experience. Gotta make sure the start and end of each background image is consistent so it repeats seamlessly when the animation repeats.


That’s it for now! Pretty neat that we can use backgrounds for much more than texture and contrast. I’m absolutely positive there are tons of other clever interactions we can apply to backgrounds. Temani Afif did exactly that with a bunch of neat hover effects for links. What do you have in mind? Share it with me in the comments!


Moving Backgrounds originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/moving-backgrounds/feed/ 1 376723
Single Element Loaders: The Dots https://css-tricks.com/single-element-loaders-the-dots/ https://css-tricks.com/single-element-loaders-the-dots/#comments Fri, 17 Jun 2022 14:47:55 +0000 https://css-tricks.com/?p=366342 We’re looking at loaders in this series. More than that, we’re breaking down some common loader patterns and how to re-create them with nothing more than a single div. So far, we’ve picked apart the classic spinning loader. Now, …


Single Element Loaders: The Dots originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We’re looking at loaders in this series. More than that, we’re breaking down some common loader patterns and how to re-create them with nothing more than a single div. So far, we’ve picked apart the classic spinning loader. Now, let’s look at another one you’re likely well aware of: the dots.

Dot loaders are all over the place. They’re neat because they usually consist of three dots that sort of look like a text ellipsis (…) that dances around.

Our goal here is to make this same thing out of a single div element. In other words, there is no one div per dot or individual animations for each dot.

That example of a loader up above is made with a single div element, a few CSS declarations, and no pseudo-elements. I am combining two techniques using CSS background and mask. And when we’re done, we’ll see how animating a background gradient helps create the illusion of each dot changing colors as they move up and down in succession.

The background animation

Let’s start with the background animation:

.loader {
  width: 180px; /* this controls the size */
  aspect-ratio: 8/5; /* maintain the scale */
  background: 
    conic-gradient(red   50%, blue   0) no-repeat, /* top colors */
    conic-gradient(green 50%, purple 0) no-repeat; /* bottom colors */
  background-size: 200% 50%; 
  animation: back 4s infinite linear; /* applies the animation */
}

/* define the animation */
@keyframes back {
  0%,                       /* X   Y , X     Y */
  100% { background-position: 0%   0%, 0%   100%; }
  25%  { background-position: 100% 0%, 0%   100%; }
  50%  { background-position: 100% 0%, 100% 100%; }
  75%  { background-position: 0%   0%, 100% 100%; }
}

I hope this looks pretty straightforward. What we’ve got is a 180px-wide .loader element that shows two conic gradients sporting hard color stops between two colors each — the first gradient is red and blue along the top half of the .loader, and the second gradient is green and purple along the bottom half.

The way the loader’s background is sized (200% wide), we only see one of those colors in each half at a time. Then we have this little animation that pushes the position of those background gradients left, right, and back again forever and ever.

When dealing with background properties — especially background-position — I always refer to my Stack Overflow answer where I am giving a detailed explanation on how all this works. If you are uncomfortable with CSS background trickery, I highly recommend reading that answer to help with what comes next.

In the animation, notice that the first layer is Y=0% (placed at the top) while X is changes from 0% to 100%. For the second layer, we have the same for X but Y=100% (placed at the bottom).

Why using a conic-gradient() instead of linear-gradient()?

Good question! Intuitively, we should use a linear gradient to create a two-color gradients like this:

linear-gradient(90deg, red 50%, blue 0)

But we can also reach for the same using a conic-gradient() — and with less of code. We reduce the code and also learn a new trick in the process!

Sliding the colors left and right is a nice way to make it look like we’re changing colors, but it might be better if we instantly change colors instead — that way, there’s no chance of a loader dot flashing two colors at the same time. To do this, let’s change the animation‘s timing function from linear to steps(1)

The loader dots

If you followed along with the first article in this series, I bet you know what comes next: CSS masks! What makes masks so great is that they let us sort of “cut out” parts of a background in the shape of another element. So, in this case, we want to make a few dots, show the background gradients through the dots, and cut out any parts of the background that are not part of a dot.

We are going to use radial-gradient() for this:

.loader {
  width: 180px;
  aspect-ratio: 8/5;
  mask:
    radial-gradient(#000 68%, #0000 71%) no-repeat,
    radial-gradient(#000 68%, #0000 71%) no-repeat,
    radial-gradient(#000 68%, #0000 71%) no-repeat;
  mask-size: 25% 40%; /* the size of our dots */
}

There’s some duplicated code in there, so let’s make a CSS variable to slim things down:

.loader {
  width: 180px;
  aspect-ratio: 8/5;
  --_g: radial-gradient(#000 68%, #0000 71%) no-repeat;
  mask: var(--_g),var(--_g),var(--_g);
  mask-size: 25% 40%;
}

Cool cool. But now we need a new animation that helps move the dots up and down between the animated gradients.

.loader {
  /* same as before */
  animation: load 2s infinite;
}

@keyframes load {      /* X  Y,     X   Y,    X   Y */
  0%     { mask-position: 0% 0%  , 50% 0%  , 100% 0%; } /* all of them at the top */
  16.67% { mask-position: 0% 100%, 50% 0%  , 100% 0%; }
  33.33% { mask-position: 0% 100%, 50% 100%, 100% 0%; }
  50%    { mask-position: 0% 100%, 50% 100%, 100% 100%; } /* all of them at the bottom */
  66.67% { mask-position: 0% 0%  , 50% 100%, 100% 100%; }
  83.33% { mask-position: 0% 0%  , 50% 0%  , 100% 100%; }
  100%   { mask-position: 0% 0%  , 50% 0%  , 100% 0%; } /* all of them at the top */
}

Yes, that’s a total of three radial gradients in there, all with the same configuration and the same size — the animation will update the position of each one. Note that the X coordinate of each dot is fixed. The mask-position is defined such that the first dot is at the left (0%), the second one at the center (50%), and the third one at the right (100%). We only update the Y coordinate from 0% to 100% to make the dots dance.

Dot loader dots with labels showing their changing positions.

Here’s what we get:

Now, combine this with our gradient animation and magic starts to happen:

Dot loader variations

The CSS variable we made in the last example makes it all that much easier to swap in new colors and create more variations of the same loader. For example, different colors and sizes:

What about another movement for our dots?

Here, all I did was update the animation to consider different positions, and we get another loader with the same code structure!

The animation technique I used for the mask layers can also be used with background layers to create a lot of different loaders with a single color. I wrote a detailed article about this. You will see that from the same code structure we can create different variations by simply changing a few values. I am sharing a few examples at the end of the article.

Why not a loader with one dot?

This one should be fairly easy to grok as I am using the same technique but with a more simple logic:

Here is another example of loader where I am also animating radial-gradient combined with CSS filters and mix-blend-mode to create a blobby effect:

If you check the code, you will see that all I am really doing there is animating the background-position, exactly like we did with the previous loader, but adding a dash of background-size to make it look like the blob gets bigger as it absorbs dots.

If you want to understand the magic behind that blob effect, you can refer to these interactive slides (Chrome only) by Ana Tudor because she covers the topic so well!

Here is another dot loader idea, this time using a different technique:

This one is only 10 CSS declarations and a keyframe. The main element and its two pseudo-elements have the same background configuration with one radial gradient. Each one creates one dot, for a total of three. The animation moves the gradient from top to bottom by using different delays for each dot..

Oh, and take note how this demo uses CSS Grid. This allows us to leverage the grid’s default stretch alignment so that both pseudo-elements cover the whole area of their parent. No need for sizing! Push the around a little with translate() and we’re all set.

More examples!

Just to drive the point home, I want to leave you with a bunch of additional examples that are really variations of what we’ve looked at. As you view the demos, you’ll see that the approaches we’ve covered here are super flexible and open up tons of design possibilities.

Next up…

OK, so we covered dot loaders in this article and spinners in the last one. In the next article of this four-part series, we’ll turn our attention to another common type of loader: the bars. We’ll take a lot of what we learned so far and see how we can extend them to create yet another single element loader with as little code and as much flexibility as possible.


Single Element Loaders: The Dots originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/single-element-loaders-the-dots/feed/ 5 366342
Cool Hover Effects That Use Background Properties https://css-tricks.com/cool-hover-effects-using-background-properties/ https://css-tricks.com/cool-hover-effects-using-background-properties/#comments Wed, 27 Apr 2022 14:20:07 +0000 https://css-tricks.com/?p=365383 A while ago, Geoff wrote an article about a cool hover effect. The effect relies on a combination of CSS pseudo-elements, transforms, and transitions. A lot of comments have shown that the same effect can be done using background …


Cool Hover Effects That Use Background Properties originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
A while ago, Geoff wrote an article about a cool hover effect. The effect relies on a combination of CSS pseudo-elements, transforms, and transitions. A lot of comments have shown that the same effect can be done using background properties. Geoff mentioned that was his initial thought and that’s what I was thinking as well. I am not saying the pseudo-element he landed on is bad, but knowing different methods to achieve the same effect can only be a good thing.

Cool Hover Effects series:

  1. Cool Hover Effects That Use Background Properties (you are here!)
  2. Cool Hover Effects That Use CSS Text Shadow
  3. Cool Hover Effects That Use Background Clipping, Masks, and 3D

In this post, we will re-work that hover effect, but also expand it into other types of hover effects that only use CSS background properties.

You can see the background properties at work in that demo, as well as how we can use custom properties and the calc() function to do even more. We are going to learn how to combine all of these so we are left with nicely optimized code!

Hover effect #1

Let’s start with the first effect which is the reproduction of the one detailed by Geoff in his article. The code used to achieve that effect is the following:

.hover-1 {
  background: linear-gradient(#1095c1 0 0) var(--p, 0) / var(--p, 0) no-repeat;
  transition: .4s, background-position 0s;
}
.hover-1:hover {
  --p: 100%;
  color: #fff;
}

If we omit the color transition (which is optional), we only need three CSS declarations to achieve the effect. You are probably surprised how small the code is, but you will see how we got there.

First, let’s start with a simple background-size transition:

We are animating the size of a linear gradient from 0 100% to 100% 100%. That means the width is going from 0 to 100% while the background itself remains at full height. Nothing complex so far.

Let’s start our optimizations. We first transform our gradient to use the color only once:

background-image: linear-gradient(#1095c1 0 0);

The syntax might look a bit strange, but we are telling the browser that one color is applied to two color stops, and that’s enough to define a gradient in CSS. Both color stops are 0, so the browser automatically makes the last one 100% and fills our gradient with the same color. Shortcuts, FTW!

With background-size, we can omit the height because gradients are full height by default. We can do a transition from background-size: 0 to background-size: 100%.

.hover-1 {
  background-image: linear-gradient(#1095c1 0 0);
  background-size: 0;
  background-repeat: no-repeat;
  transition: .4s;
}
.hover-1:hover {
  background-size: 100%;
}

Let’s introduce a custom property to avoid the repetition of background-size:

.hover-1 {
  background-image: linear-gradient(#1095c1 0 0);
  background-size: var(--p, 0%);
  background-repeat: no-repeat;
  transition: .4s;
}
.hover-1:hover {
  --p: 100%;
}

We are not defining --p initially, so the fallback value (0% in our case) will be used. On hover, we define a value that replaces the fallback one ( 100%).

Now, let’s combine all the background properties using the shorthand version to get:

.hover-1 {
  background: linear-gradient(#1095c1 0 0) left / var(--p, 0%) no-repeat;
  transition: .4s;
}
.hover-1:hover {
  --p: 100%;
}

We are getting closer! Note that I have introduced a left value (for the background-position) which is mandatory when defining the size in the background shorthand. Plus, we need it anyway to achieve our hover effect.

We need to also update the position on hover. We can do that in two steps:

  1. Increase the size from the right on mouse hover.
  2. Decrease the size from the left on mouse out.

To do this, we need to update the background-position on hover as well:

We added two things to our code:

  • A background-position value of right on hover
  • A transition-duration of 0s on the background-position

This means that, on hover, we instantly change the background-position from left (see, we needed that value!) to right so the background’s size will increase from the right side. Then, when the mouse cursor leaves the link, the transition plays in reverse, from right to left, making it appear that we are decreasing the background’s size from the left side. Our hover effect is done!

But you said we only needed three declarations and there are four.

That’s true, nice catch. The left and right values can be changed to 0 0 and 100% 0, respectively; and since our gradient is already full height by default, we can get by using 0 and 100%.

.hover-1 {
  background: linear-gradient(#1095c1 0 0) 0 / var(--p, 0%) no-repeat;
  transition: .4s, background-position 0s;
}
.hover-1:hover {
  --p: 100%;
  background-position: 100%;
}

See how background-position and --p are using the same values? Now we can reduce the code down to three declarations:

.hover-1 {
  background: linear-gradient(#1095c1 0 0) var(--p, 0%) / var(--p,0%) no-repeat;
  transition: .4s, background-position 0s;
}
.hover-1:hover {
  --p: 100%;
}

The custom property --p is defining both the background position and size. On hover, It will update both of them as well. This is a perfect use case showing how custom properties can help us reduce redundant code and avoid writing properties more than once. We define our setting using custom properties and we only update the latter on hover.

But the effect Geoff described is doing the opposite, starting from left and ending at right. How do we do that when it seems we cannot rely on the same variable?

We can still use one variable and update our code slightly to achieve the opposite effect. What we want is to go from 100% to 0% instead of 0% to 100%. We have a difference of 100% that we can express using calc(), like this:

.hover-1 {
  background: linear-gradient(#1095c1 0 0) calc(100% - var(--p,0%)) / var(--p,0%) no-repeat;
  transition: .4s, background-position 0s;
}
.hover-1:hover {
  --p: 100%;
}

--p will change from 0% to 100%, but the background’s position will change from 100% to 0%, thanks to calc().

We still have three declarations and one custom property, but a different effect.

Before we move to the next hover effect, I want to highlight something important that you have probably noticed. When dealing with custom properties, I am using 0% (with a unit) instead of a unit-less 0. The unit-less zero may work when the custom property is alone, but will fail inside calc() where we need to explicitly define the unit. I may need another article to explain this quirk but always remember to add the unit when dealing with custom properties. I have two answers on StackOverflow (here and here) that go into more detail.

Hover effect #2

We need a more complex transition for this effect. Let’s take a look at a step-by-step illustration to understand what is happening.

Diagram showing the hover effect in three pieces.
Initially, a fixed-height, full-width gradient is outside of view. Then we move the gradient to the right to cover the bottom side. Finally, we increase the size of the gradient from the fixed height to 100% to cover the whole element.

We first have a background-position transition followed by a background-size one. Let’s translate this into code:

.hover-2 {
  background-image: linear-gradient(#1095c1 0 0);
  background-size: 100% .08em; /* .08em is our fixed height; modify as needed. */
  background-position: /* ??? */;
  background-repeat: no-repeat;
  transition: background-size .3s, background-position .3s .3s;
}
.hover-2:hover {
  transition: background-size .3s .3s, background-position .3s;
  background-size: 100% 100%;
  background-position: /* ??? */;
}

Note the use of two transition values. On hover, we need to first change the position and later the size, which is why we are adding a delay to the size. On mouse out, we do the opposite.

The question now is: what values do we use for background-position? We left those blank above. The background-size values are trivial, but the ones for background-position are not. And if we keep the actual configuration we’re unable to move our gradient.

Our gradient has a width equal to 100%, so we cannot use percentage values on background-position to move it.

Percentage values used with background-position are always a pain especially when you use them for the first time. Their behavior is non-intuitive but well defined and easy to understand if we get the logic behind it. I think it would take another article for a full explanation why it works this way, but here’s another “long” explanation I posted over at Stack Overflow. I recommend taking a few minutes to read that answer and you will thank me later!

The trick is to change the width to something different than 100%. Let’s use 200%. We’re not worried about the background exceeding the element because the overflow is hidden anyway.

.hover-2 {
  background-image: linear-gradient(#1095c1 0 0);
  background-size: 200% .08em;
  background-position: 200% 100%;
  background-repeat: no-repeat;
  transition: background-size .3s, background-position .3s .3s;
}
.hover-2:hover {
  transition: background-size .3s .3s, background-position .3s;
  background-size: 200% 100%;
  background-position: 100% 100%;
}

And here’s what we get:

It’s time to optimize our code. If we take the ideas we learned from the first hover effect, we can use shorthand properties and write fewer declarations to make this work:

.hover-2 {
  background: 
    linear-gradient(#1095c1 0 0) no-repeat
    var(--p, 200%) 100% / 200% var(--p, .08em);
  transition: .3s var(--t, 0s), background-position .3s calc(.3s - var(--t, 0s));
}
.hover-2:hover {
  --p: 100%;
  --t: .3s;
}

We add all the background properties together using the shorthand version then we use --p to express our values. The sizes change from .08em to 100% and the position from 200% to 100%

I am also using another variable --t , to optimize the transition property. On mouse hover we have it set to a .3s value, which gives us this:

transition: .3s .3s, background-position .3s 0s;

On mouse out, --t is undefined, so the fallback value will be used:

transition: .3s 0s, background-position .3s .3s;

Shouldn’t we have background-size in the transition?

That is indeed another optimization we can make. If we don’t specify any property it means “all” the properties, so the transition is defined for “all” the properties (including background-size and background-position). Then it’s defined again for background-position which is similar to defining it for background-size, then background-position.

“Similar” is different than saying something is the “same.” You will see a difference if you change more properties on hover, so the last optimization might be unsuitable in some cases.

Can we still optimize the code and use only one custom property?

Yes, we can! Ana Tudor shared a great article explaining how to create DRY switching where one custom property can update multiple properties. I won’t go into the details here, but our code can be revised like this:

.hover-2 {
  background: 
    linear-gradient(#1095c1 0 0) no-repeat
    calc(200% - var(--i, 0) * 100%) 100% / 200% calc(100% * var(--i, 0) + .08em);
  transition: .3s calc(var(--i, 0) * .3s), background-position .3s calc(.3s - calc(var(--i, 0) * .3s));
}
.hover-2:hover {
  --i: 1;
}

The --i custom property is initially undefined, so the fallback value, 0, is used. On hover though, we replace 0 with 1. You can do the math for both cases and get the values for each one. You can see that variable as a “switch” that update all our values at once on hover.

Again, we’re back to only three declarations for a pretty cool hover effect!

Hover effect #3

We are going to use two gradients instead of one for this effect. We will see that combining multiple gradients is another way to create fancy hover effects.

Here’s a diagram of what we’re doing:

We initially have two gradients that overflow the element so that they are out of view. Each one has a fixed height and toes up half of the element’s width. Then we slide them into view to make them visible. The first gradient is placed at the bottom-left and the second one at the top-right. Finally, we increase the height to cover the whole element.

Here’s how that looks in CSS:

.hover-3 {
  background-image:
    linear-gradient(#1095c1 0 0),
    linear-gradient(#1095c1 0 0);
  background-repeat: no-repeat;
  background-size: 50% .08em;
  background-position:
    -100% 100%,
    200% 0;
  transition: background-size .3s, background-position .3s .3s;
}
.hover-3:hover {
  background-size: 50% 100%;
  background-position:
    0 100%,
    100% 0;  
  transition: background-size .3s .3s, background-position .3s;
}

The code is almost the same as the other hover effects we’ve covered. The only difference is that we have two gradients with two different positions. The position values may look strange but, again, that’s related to how percentages work with the background-position property in CSS, so I highly recommend reading my Stack Overflow answer if you want to get into the gritty details.

Now let’s optimize! You get the idea by now — we’re using shorthand properties, custom properties, and calc() to tidy things up.

.hover-3 {
  --c: no-repeat linear-gradient(#1095c1 0 0);
  background: 
    var(--c) calc(-100% + var(--p, 0%)) 100% / 50% var(--p, .08em),
    var(--c) calc( 200% - var(--p, 0%)) 0    / 50% var(--p, .08em);
  transition: .3s var(--t, 0s), background-position .3s calc(.3s - var(--t, 0s));
}
.hover-3:hover {
  --p: 100%;
  --t: 0.3s;
}

I have added an extra custom property, --c, that defines the gradient since the same gradient is used in both places.

I am using 50.1% in that demo instead of 50% for the background size because it prevents a gap from showing between the gradients. I also added 1% to the positions for similar reasons.

Let’s do the second optimization by using the switch variable:

.hover-3 {
  --c: no-repeat linear-gradient(#1095c1 0 0);
  background: 
    var(--c) calc(-100% + var(--i, 0) * 100%) 100% / 50% calc(100% * var(--i, 0) + .08em),
    var(--c) calc( 200% - var(--i, 0) * 100%) 0 / 50% calc(100% * var(--i, 0) + .08em);
  transition: .3s calc(var(--i, 0) * .3s), background-position .3s calc(.3s - var(--i, 0) * .3s);
}
.hover-3:hover {
  --i: 1;
}

Are you started to see the patterns here? It’s not so much that the effects we’re making are difficult. It’s more the “final step” of code optimization. We start by writing verbose code with a lot of properties, then reduce it following simple rules (e.g. using shorthand, removing default values, avoiding redundant values, etc) to simplify things down as much as possible.

Hover effect #4

I will raise the difficulty level for this last effect, but you know enough from the other examples that I doubt you’ll have any issues with this one.

This hover effect relies on two conic gradients and more calculations.

Initially, we have both gradients with zero dimensions in Step 1. We increase the size of each one in Step 2. We keep increasing their widths until they fully cover the element, as shown in Step 3. After that, we slide them to the bottom to update their position. This is the “magic” part of the hover effect. Since both gradients will use the same coloration, changing their position in Step 4 will make no visual difference — but we will see a difference once we reduce the size on mouse out during Step 5.

If you compare Step 2 and Step 5, you can see that we have a different inclination. Let’s translate that into code:

.hover-4 {
  background-image:
    conic-gradient(/* ??? */),
    conic-gradient(/* ??? */);
  background-position:
    0 0,
    100% 0;
  background-size: 0% 200%;
  background-repeat: no-repeat;
  transition: background-size .4s, background-position 0s;
}
.hover-4:hover {
  background-size: /* ??? */ 200%;
  background-position:
    0 100%,
    100% 100%;
}

The positions are pretty clear. One gradient starts at top left (0 0) and ends at bottom left (0 100%) while the other starts at top right (100% 0) and ends at bottom right (100% 100%).

We’re using a transition on the background positions and sizes to reveal them. We only need a transition value for the background-size. And like before, background-position needs to change instantly, so we’re assigning a 0s value for the transition’s duration.

For the sizes, both gradient need to have 0 width and twice the element height (0% 200%). We will see later how their sizes change on hover. Let’s first define the gradient configuration.

The diagram below illustrates the configuration of each gradient:

Note that for the second gradient (indicated in green), we need to know the height to use it inside the conic-gradient we’re creating. For this reason, I am going to add a line-height that sets the element’s height and then try that same value for the conic gradient values we left out.

.hover-4 {
  --c: #1095c1;
  line-height: 1.2em;
  background-image:
    conic-gradient(from -135deg at 100%  50%, var(--c) 90deg, #0000 0),
    conic-gradient(from -135deg at 1.2em 50%, #0000 90deg, var(--c) 0);
  background-position:
    0 0,
    100% 0;
  background-size: 0% 200%;
  background-repeat: no-repeat;
  transition: background-size .4s, background-position 0s;
}
.hover-4:hover {
  background-size: /* ??? */ 200%;
  background-position:
    0 100%,
    100% 100%;
}

The last thing we have left is to figure out the background’s size. Intuitively, we may think that each gradient needs to take up half of the element’s width but that’s actually not enough.

We’re left with a large gap if we use 50% as the background-size value for both gradients.

We get a gap equal to the height, so we actually need to do is increase the size of each gradient by half the height on hover for them to cover the whole element.

.hover-4:hover {
  background-size: calc(50% + .6em) 200%;
  background-position:
    0 100%,
    100% 100%;
}

Here’s what we get after optimizing them like the previous examples:

.hover-4 {
  --c: #1095c1;
  line-height: 1.2em;
  background:
    conic-gradient(from -135deg at 100%  50%, var(--c) 90deg, #0000 0) 
      0  var(--p, 0%) / var(--s, 0%) 200% no-repeat,
    conic-gradient(from -135deg at 1.2em 50%, #0000 90deg, var(--c) 0) 
      100% var(--p, 0%) / var(--s, 0%) 200% no-repeat;
  transition: .4s, background-position 0s;
}
.hover-4:hover {
  --p: 100%;
  --s: calc(50% + .6em);
}

What about the version with only one custom property?

I will leave that for you! After looking at four similar hover effects, you should be able to get the final optimization down to a single custom property. Share your work in the comment section! There’s no prize, but we may end up with different implementations and ideas that benefit everyone!

Before we end, let me share a version of that last hover effect that Ana Tudor cooked up. It’s an improvement! But note that it lacks Firefox supports due to a known bug. Still, it’s a great idea that shows how to combine gradients with blend modes to create even cooler hover effects.

Wrapping up

We made four super cool hover effects! And even though they are different effects, they all take the same approach of using CSS background properties, custom properties, and calc(). Different combinations allowed us to make different versions, all using the same techniques that leave us with clean, maintainable code.

If you want to get some ideas, I made a collection of 500 (yes, 500!) hover effects, 400 of which are done without pseudo-elements. The four we covered in this article are just the tip of the iceberg!


Cool Hover Effects That Use Background Properties originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/cool-hover-effects-using-background-properties/feed/ 6 365383
Moving Backgrounds With Mouse Position https://css-tricks.com/moving-backgrounds-with-mouse-position/ https://css-tricks.com/moving-backgrounds-with-mouse-position/#comments Thu, 04 Oct 2018 14:04:25 +0000 http://css-tricks.com/?p=276364 Let’s say you wanted to move the background-position on an element as you mouse over it to give the design a little pizzazz. You have an element like this:

<div class="module" id="module"></div>

And you toss a background on it:

.module 


Moving Backgrounds With Mouse Position originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Let’s say you wanted to move the background-position on an element as you mouse over it to give the design a little pizzazz. You have an element like this:

<div class="module" id="module"></div>

And you toss a background on it:

.module {
  background-image: url(big-image.jpg);
}

You can adjust the background-position in JavaScript like this:

const el = document.querySelector("#module");

el.addEventListener("mousemove", (e) => {
  el.style.backgroundPositionX = -e.offsetX + "px";
  el.style.backgroundPositionY = -e.offsetY + "px";
});

See the Pen Move a background with mouse by Chris Coyier (@chriscoyier) on CodePen.

Or, you could update CSS custom properties in the JavaScript instead:

const el = document.querySelector("#module");

el.addEventListener("mousemove", (e) => {
  el.style.setProperty('--x', -e.offsetX + "px");
  el.style.setProperty('--y', -e.offsetY + "px");
});
.module {
  --x: 0px;
  --y: 0px;
  background-image: url(large-image.jpg);
  background-position: var(--x) var(--y);
}

See the Pen Move a background with mouse by Chris Coyier (@chriscoyier) on CodePen.

Here’s an example that moves the background directly in JavaScript, but with a transition applied so it slides to the new position rather than jerking around the first time you enter:

See the Pen Movable Background Ad by Chris Coyier (@chriscoyier) on CodePen.

Or, you could move an actual element instead (rather than the background-position). You’d do this if there is some kind of content or interactivity on the sliding element. Here’s an example of that, which sets CSS custom properties again, but then actually moves the element via a CSS translate() and a calc() to temper the speed.

See the Pen Hotjar Moving Heatmap Ad by Chris Coyier (@chriscoyier) on CodePen.

I’m sure there are loads of other ways to do this — a moving SVG viewBox, scripts controlling a canvas, webGL… who knows! If you have some fancier ways to handle this, link ’em up in the comments.


Moving Backgrounds With Mouse Position originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/moving-backgrounds-with-mouse-position/feed/ 9 276364
Focusing a `background-image` on a Precise Location with Percentages https://css-tricks.com/focusing-background-image-precise-location-percentages/ https://css-tricks.com/focusing-background-image-precise-location-percentages/#comments Mon, 17 Apr 2017 12:16:25 +0000 http://css-tricks.com/?p=253810 Let’s say you have an element with a background-image, where only part of the image is visible, because the image is bigger than the element itself. The rest is cropped away, outside the element.

Now you want to move …


Focusing a `background-image` on a Precise Location with Percentages originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Let’s say you have an element with a background-image, where only part of the image is visible, because the image is bigger than the element itself. The rest is cropped away, outside the element.

Now you want to move that background-image such that you’re focusing the center of the element on a specific point in it. You also want to do that with percentage values rather than pixels. We’re going to have to get clever.

This is going to involve some math.

Let’s use this as our image, which has markers for sizing:

And here’s our element, with that background-image applied. Notice we can only see the (top) left of the image:

See the Pen Background Focus: no position by Jay (@jsit) on CodePen.

Now let’s say we want to align a specific point on the image with the center of that element. Say, the point at 300px in from the left.

Since we’re asking for this position in pixels, it’s straightforward. With no position defined, the background-image “starts” with the point at 100 pixels at the center, so you need to move it to the left by 200 pixels:

See the Pen Background Focus: pixel position by Jay (@jsit) on CodePen.

Let’s formalize it.

The x value you’re using for background-position-x is calculated like so:

(0.5 × [bounding box width]) - [x-coordinate]
             0.5 × 200px -              300px
                   100px -              300px = -200px

It takes a second to figure out, but it’s nothing too taxing. You could have probably figured that out intuitively without needing to use a formula.

But what if you wanted to (or had to) express background-position-x as a percentage? Shouldn’t be too hard, right? Let’s try using a percentage to get ourselves centered at 300px again. We had a background-position-x of -200px, so let’s convert that to percent: -200 / 800 = -25%, so:

See the Pen Background Focus: percentage (1st attempt) by Jay (@jsit) on CodePen.

Hm. That didn’t work at all. Maybe we need to use a positive value?

See the Pen Background Focus: percentage (2nd attempt) by Jay (@jsit) on CodePen.

That’s better, but it’s centered at, like… 250px? How about as a percentage of the bounding box width: 300 / 200 = 150%. That can’t be right…

See the Pen Background Focus: percentage (3rd attempt) by Jay (@jsit) on CodePen.

Yeah, that’s not right.

Let’s back up. What happens if we do this?

See the Pen Background Focus: percentage (4th attempt) by Jay (@jsit) on CodePen.

That feels like it kind of makes sense; background-position-x: 100%; makes the background-image flush-right and centered at 700px, or 7/8 the width of the image. But what if we wanted to center it at 100%? I guess we’d have to do… 9/8?

See the Pen Background Focus: percentage (5th attempt) by Jay (@jsit) on CodePen.

At this point, I’m not surprised that didn’t work.

This doesn’t feel like the right path. Let’s back up.

What does the spec say?

For example, with a value pair of ‘0% 0%’, the upper left corner of the image is aligned with the upper left corner of, usually, the box’s padding edge. A value pair of ‘100% 100%’ places the lower right corner of the image in the lower right corner of the area. With a value pair of ‘75% 50%’, the point 75% across and 50% down the image is to be placed at the point 75% across and 50% down the area.

Maybe we can reverse-engineer this.

On that last one, 112.5%, it was aligning the point at 112.5% across the background-image with the point at 112.5% across the bounding box. That kind makes sense. The spec seems written to make it easy for only three values: 0%, 50%, and 100%. Any other value isn’t so intuitive.

With background-position-x: 0;, we were focused on 100px, or 12.5%. With background-position-x: 100%;, we were focused on 700px, or 87.5%. How does background-position-x: 50%; look, exactly?

See the Pen Background Focus: percentage (6th attempt) by Jay (@jsit) on CodePen.

50% is kind of like our “anchor” here; it’s the point at which our desired focal point and the corresponding background-position-x values are equal.

Let’s pretend we want to focus on 700px or 87.5%. We go 100% of the way from the center: 50% + 50%.

See the Pen Background Focus: percentage (4th attempt) by Jay (@jsit) on CodePen.

With background-position-x set to 100%, the center of our bonding box has “panned” from the center of the image, 3/4 of the way to the rightmost edge (from 400px to 700px). If we want to “pan” to the rightmost edge, we need to go that extra 1/4, or 200px. 1/4 is 1/3 of 3/4, so we need to go a third more than we did a moment ago, or a total of 66.667% from the center:

See the Pen Background Focus: percentage (7th attempt) by Jay (@jsit) on CodePen.

Whew! So to focus on the rightmost edge of a background-image that is 4 times the size of our bounding box, we need to set background-position-x: 116.667%;.

How the heck are we supposed to figure that out?

It’s a difference of 16.667% from the 100% we might expect. So if we wanted to focus on our original goal of 300px (or, 37.5%), we’d, uh, add 16.667%? There’s no way this is going to work:

See the Pen Background Focus: percentage (8th attempt) by Jay (@jsit) on CodePen.

Nope.

If we wanted to focus on the leftmost edge, we’d probably subtract 16.667% from 0%, right? That sounds like it could be right.

See the Pen Background Focus: percentage (9th attempt) by Jay (@jsit) on CodePen.

Cool!

To focus at 100% or 0%, you have to “overshoot” those values by a certain amount, when measured from the center.

So if we want to focus on 0%, or “100% of the way to the left of the center,” we have to subtract 66.667% from 50%. If we want to focus on 100%, or “100% of the way to the right of the center,” we have to add 66.667% to 50%.

I might have expected to have to add or subtract 50% to or from 50% to get to those edges: a 1:1 ratio of “how far I want to go from the center” to “what my background-position-x value should be.” But instead, we have to use a 4:3 ratio. In other words, we have to use a value four-thirds more “away from the center.”

Things are getting a little hairy here, so let’s introduce some terms:

  • c: Desired focal point (in percent) from leftmost edge of background image
  • z: Zoom factor (background width ÷ bounding box width)
  • p: background-position-x, to focus on c, given z

So we take a focal point’s distance from the center (c − 50), multiply it by 4/3, then add that result to “the center,” or 50.

If you wanted to focus on the point at 600px (or 75%), my background-position-x value should be:

(75% − 50%) × 4/3 + 50% = 83.333%

Yes, that sounds like it could work! Please please please:

See the Pen Background Focus: percentage (10th attempt) by Jay (@jsit) on CodePen.

Awesome!

And if you wanted to focus on 200px, or 25%, you would do:

(25% − 50%) × 4/3 + 50% = 16.667%

See the Pen Background Focus: percentage (11th attempt) by Jay (@jsit) on CodePen.

Wow.

Let’s generalize this:

(c − 50%) × 4/3 + 50% = p

So why 4/3? 4 is the ratio of our background-image width to our bounding box width; and 3 is… 1 less than 4. Could it be that simple? Let’s try a larger background-image, this time 1000px wide, or 5 times the width of our bounding box. And let’s again try to focus on the point at 200px. Here our equation would be:

(20% − 50%) × 5/4 + 50% = 12.5%

See the Pen Background Focus: percentage (12th attempt) by Jay (@jsit) on CodePen.

Oh my god. It works!

So to revisit our equation, with a variable background-to-bounding-box ratio:

(c − 50%) × z/(z − 1) + 50% = p

Let’s turn this into English:

Given a point on a background-image at location c…

  1. with c expressed as a percentage of the width of the image
  2. where c is intended to lie in the horizontal center of the background image’s bounding box
  3. and where the background-image is z times as wide as its bounding box

the correct background-position-x p (expressed as a percentage) is given by the following formula:

(c − 50%) × z/(z − 1) + 50% = p

Can we generalize this even more?

What if we wanted to align the point at 25% of the background-image with the point at 75% of the bounding box? Yikes!

Let’s revisit our original formula:

(c − 50%) × z/(z − 1) + 50% = p

Now let’s introduce some new terms:

  • b: Desired focal point (in percent) from the leftmost edge of the bounding box. Earlier we had assumed this to always be 50% so that the center of the bounding box would be focused on our target in the background-image.
  • d: background-image focal point (in percent) to align to bounding box’s midpoint in order to get c to align to b in the bounding box; if d of the background-image aligns with 50% of the bounding box, then c of the background-image will align with b of the bounding box.

Let’s think of it this way

To want to align position c of a background-image with position b of a bounding box is to want to align some other position, d, of a background-image with the center of the bounding box – and we already know how to do that. So can we figure out a way to derive d, the spot we need to be at 50%, from c, b, and z? Sure!

With our 800px wide background-image, in a 200px-wide bounding box (z = 4), if we want to focus the rightmost edge of the bounding box (b = 100%) on the position at 600px (c = 75%) in the image, we would want the center of the bounding box to be focused on the point at 500px (d = 62.5%).

How do we get from c (75%) to d (62.5%)? Where does that -12.5% difference come from?

Well, our b was 100%, 50% greater than our old “default” b of 50%. And 12.5% is 1/4 of that; 1/4 is the inverse of our z of 4. Is that where our d comes from? That would be:

d = c + (50% - b)/z

Looks promising. Now we can substitute d in for c in the original formula:

(d − 50%) × z/(z − 1) + 50% = p

Or:

(c + (50% − b)/z - 50%) × z/(z − 1) + 50% = p

Whew! Let’s test this. Let’s try to align the position at 25% in our background-image (200px) with the position at 75% in our bounding box. This would be:

p = (25% + (50% - 75%)/4 - 50%) × 4/(4 - 1) + 50%
p = -31.25% × 1.333 + 50%
p = 8.333%

See the Pen Background Focus: percentage (13th attempt) by Jay (@jsit) on CodePen.

Unbelievable! Let’s double check. How about the point at 87.5% in our background-image (700px) aligned with the position at 33.333% in our bounding box:

p = (87.5% + (50% - 33.333%)/4 - 50%) × 4/(4 - 1) + 50%
p = 41.6667% × 1.333 + 50%
p = 105.555%

See the Pen Background Focus: percentage (14th attempt) by Jay (@jsit) on CodePen.

Looks good enough to me!

I’m sure there is something intuitive about this to certain types of people, but I am not one of those people.

Let’s build a Sass function that will do all this ridiculous math for us.

See the Pen Background Focus: percentage (final Sass function) by Jay (@jsit) on CodePen.

My head is spinning.

When I began tackling this problem I did not expect it to be this difficult, but what a journey. I hope guiding you through my thought process has been enlightening, and that you may at some point find value in our little Sass function.

All the Pens embedded in this article can be found in this collection.


Focusing a `background-image` on a Precise Location with Percentages originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/focusing-background-image-precise-location-percentages/feed/ 6 253810
background-position https://css-tricks.com/almanac/properties/b/background-position/ https://css-tricks.com/almanac/properties/b/background-position/#comments Tue, 17 Feb 2015 21:32:02 +0000 http://css-tricks.com/?page_id=196146 The background-position property in CSS allows you to move a background image (or gradient) around within its container.

html {
  background-position: 100px 5px; 
}

It has three different types of values:

  • Length values (e.g. 100px 5px)
  • Percentages (e.g. 100%


background-position originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The background-position property in CSS allows you to move a background image (or gradient) around within its container.

html {
  background-position: 100px 5px; 
}

It has three different types of values:

  • Length values (e.g. 100px 5px)
  • Percentages (e.g. 100% 5%)
  • Keywords (e.g. top right)

The default values are 0 0. This places your background image at the top-left of the container.

Length values are pretty simple: the first value is the horizontal position, second value is the vertical position. So 100px 5px will move the image 100px to the right and five pixels down. You can set length values in px, em, or any of the other CSS length values.

Percentages work a little differently. Get your math hats out: moving a background image by X% means it will align the X% point in the image to the X% point in the container. For example, 50% means it will align the middle of the image with the middle of the container. 100% means it will align the last pixel of the image with the last pixel of the container, and so on.

Keywords are just shortcuts for percentages. It’s easier to remember and write top right than 0 100%, and that’s why keywords are a thing. Here is a list of all five keywords and their equivalent values:

  • top: 0% vertically
  • right: 100% horizontally
  • bottom: 100% vertically
  • left: 0% horizontally
  • center: 50% horizontally if horizontal isn’t already defined. If it is then this is applied vertically.

It’s interesting to note that it doesn’t matter what order you use for the keywords: top center is the same as center top. You can only do this if you’re exclusively using keywords, though. center 10% is not the same as 10% center.

Demo

This demo shows examples of background-position set with length units, percentages, and keywords.

Declaring values

You can give background-position up to four values in modern browsers:

  • If you declare one value, that value is the horizontal offset. The browser sets the vertical offset to center.
  • When you declare two values, the first value is the horizontal offset and the second value is the vertical offset.

Things get a little trickier when you start using three or four values, but you also get more control over your background placement. A three- or four-value syntax alternates between keywords and length or percentage units. You can use any of the keyword values except center in a three- or four-value background-position declaration.

When you specify three values, the browser interpets the “missing” fourth value as 0. Here’s an example of a three-value background-position:

.three-values {
  background-position: right 45px bottom;
}

This positions the background image 45px from the right and 0px from the bottom of the container.

Here’s an example of a four-value background-position:

.four-values {
  background-position: right 45px bottom 20px;
}

This puts the background image 45px from the right and 20px from the bottom of the container.

Notice the order of the values in the examples above: keywords followed by length units. A three- or four-value background-position must follow that format, with a keyword preceding a length or percentage unit.

Demo

This demo includes examples of one value, two value, three value, and four value background-position.

Browser support

The basic values are supported everywhere. The four-value syntax has this 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

ChromeFirefoxIEEdgeSafari
25139127

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
1151154.47.0-7.1

That’s the same level of support as the background-position-x and background-position-y properties.

More information


background-position originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/almanac/properties/b/background-position/feed/ 13 196146