Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anchor a repeating linear gradient to the top of an element

Problem description

I'm developing a piece of UI that needs to have a striped background, and which also can be resized by the user dragging its bottom edge.

I have implemented the striped background using a repeating-linear-gradient, which on its own works just fine. But when the element is resized from its bottom edge, the whole background shifts along with it instead of remaining anchored at the top edge.

I'd like the gradient to be anchored to the top edge so that it appears to expand with the element, instead of moving, when the element is resized in this way.

Minimal reproducible example

Just a warning, this example contains movement that might be distracting. But it was necessary to demonstrate the problem.

html, body {
  margin: 0;
  height: 100%;
}

@keyframes changeHeight {
  100% { height: 100%; }
}

.example {
  width: 100%;
  height: 50%;
  
  background-image: repeating-linear-gradient(-45deg, #ccc 0, #ccc 1rem, #999 1rem, #999 2rem);
  
  animation: changeHeight 5s infinite alternate ease-in-out;
}
<div class="example"></div>

Solutions I tried that haven't worked

background-position

I've tried fixing this using background-position but as far as I can tell it's only possible to set a static offset when using it in conjunction with repeating-linear-gradient, and any percentage values just get ignored. I don't think I missed anything obvious here, but I'd be happy to be corrected if there is a way to use background-position to solve this.

Use negative offsets for the gradient to shift

I also tried implementing the gradient using negative offsets, and with calc(100% - ${offset} offsets, hoping that might shift its anchor point to the opposite side. But as far as I can tell this simply doesn't work for repeating gradients.

Potential alternatives (and why I don't want to use them)

I'm not entirely certain that there is a solution that is as simple as I'd like it to be. So if there isn't something better, I may end up either having to go with one of these options or not solving this problem.

But so long as I'm still looking for a better solution, here are the other options I've thought of and the reasons why I would rather not use them.

Use an intermediate layer

I could put the gradient on an extra element or pseudoelement that sits beneath the content of this element, and that element could be attached to the top of its parent and be very tall - tall enough that its parent will never be resized to be larger - and have any overflow hidden by an overflow: hidden; on the parent.

I'd rather avoid this if possible though. My gut tells me it might cause worse problems further down the line, as well as potentially causing performance issues on low power devices when there are many of these elements on the page at once.

Matching the gradient to a fixed resize step

In my particular case, the element can only be resized by fixed jumps. I could make the gradient's size line up with the size of those jumps, so even though it moves it ends up moving just enough to look like it's standing still.

I'd rather not use this approach because it would add an additional difficult constraint to the pattern of the gradient, which means I couldn't correctly implement the design I'm following.

Use linear-gradient and background-repeat

It's possible to construct a fixed-size tile using linear-gradient and set it to repeat, and then control that with background-position. However, creating repeating angled gradients with this approach is finnicky, and any time the angle or the width of the bands is changed the tile needs to be adjusted in a few different ways.

This article examines the downsides of this approach.

Use a repeating background image

I could use a repeating background image of a tile of this repeating background, and anchor that to the top of the element.

I don't want to use this approach because I'd like the shape and colours of the gradient to be easy to read by other developers (or myself in the future) just by looking at the code, since in my use case they will have some transparency so they can modify the appearance of colours underneath so it's important to me that the colour and transparency values are easy to read precisely without needing any extra tools.

Using an image would also mean the angle and band width couldn't be easily adjusted in the CSS, and we'd need to create a new image each time anything needs to be tweaked.

Use background-attachment

Yes, technically background-attachment: fix; would stop the gradient from moving when the element is resized. But it would add a parallax effect when scrolling the page that I don't want.

Use an extra layer with a transform

Another way I could flip the bottom to the top in this way would be to either rotate the element or flip it over with a negative scale.

I'd rather avoid this since it would require an extra set of markup so prevent the text that will also appear in this element from being affected.

As a more minor point, having to keep the transform in mind will add a little extra mental overhead to reading or modifying the gradient definition that I'd rather avoid.

like image 987
Mark Hanna Avatar asked Nov 30 '25 17:11

Mark Hanna


1 Answers

Changing -45deg to 135deg should do it, although in practice it wobbles in current Firefox for me. But if you consider that acceptable…

html, body {
  margin: 0;
  height: 100%;
}

@keyframes changeHeight {
  100% { height: 100%; }
}

.example {
  width: 100%;
  height: 50%;
  
  background-image: repeating-linear-gradient(135deg, #ccc 0, #ccc 1rem, #999 1rem, #999 2rem);
  
  animation: changeHeight 5s infinite alternate ease-in-out;
}
<div class="example"></div>
like image 92
2 revsRy- Avatar answered Dec 02 '25 05:12

2 revsRy-



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!