DEV Community

Cover image for CSS Challenges for 200 IQ
Alex Inkin
Alex Inkin

Posted on • Originally published at Medium

CSS Challenges for 200 IQ

Do you ever get that feeling when you’re working on a task, hit a wall with some problem, and something inside you whispers that there has to be a solution? When it seems like all is lost, like you’ve run into a fundamental limit of reality, but your refusal to accept it keeps driving you deeper into spec docs, 10-year-old GitHub threads, and articles from giants who’ve already blazed this trail and shared their findings? And then, after hours of intense brain-grinding, you add that final line of code, refresh the page, and there it is — the exact result you wanted, staring back at you from the screen? That rush of success is probably familiar to every engineer in some form or another. In those moments, I always want to share the win with my colleagues and, if it could help others, write an article about it. In this post, I’ve collected 3 such cases from our work where we came up with solutions that, as far as I know, are pretty unique and haven’t been fully documented before. I invite you to share in the joy of discovering a solution that seemed impossible!

Fixed inside a Scroll Container

For a warm-up, let’s take an easier task. One of my most popular CodePens is an example of a fixed block inside a scrolling container. People find it via Stack Overflow answers, so it’s an in-demand problem, so it might come in handy for you too.

I’ve been working on an Angular component library called Taiga UI for many years. Everything I’ll talk about in this article comes from there, but that’s just the backstory. We won’t need Angular or any of its specifics here. We’re talking pure CSS. Our library uses a custom scrollbar. While modern browsers let you tweak its appearance a bit, for full control over behavior and visuals, we need to place our own elements inside the container to act as the scrollbar. But how do you do that when absolutely positioned elements fly to the top on scroll, and fixed-position ones are pinned to the viewport?

Experienced devs will immediately think of position: sticky, but those elements take up space and only stick once you’ve scrolled the container past them. We’d need to compensate for their height somehow, but using negative percentage margins references the horizontal dimensions, when we actually need vertical ones. And that’s where the almost-forgotten in this post-Flexbox era float property comes to the rescue!

We want a block that covers the entire scrolling container while staying fixed during scroll. To do this, set inset: 0 and width: 100% / height: 100%. As you recall, percentage margins are based on the element’s horizontal size — and that’ll work perfectly here. Add float: left to make it “step aside,” and margin-right: -100% clears all the space it occupied for the rest of the container’s content:

.overlay {
  position: sticky;
  inset: 0;
  height: 100%;
  width: 100%;
  float: left;  
  margin-right: -100%;
  pointer-events: none;
}
Enter fullscreen mode Exit fullscreen mode

Now you’ve got a container that acts fixed inside a scrollable block, and you can easily place anything in it — like buttons or toasts — without worrying about the main content.

Parameters for SVG

Now let’s pick something trickier. For displaying colorable icons, we use mask-image: fill the block’s background with the desired color, then cut it out along the icon’s shape using a mask. My colleague Nikita already wrote about all the cool things you can do with CSS masks — highly recommend reading it.

It’s a super simple approach that avoids DOM manipulation (and even nesting with ::before/::after), leverages the browser’s built-in caching, and works with cross-domain CDNs. Beauty everywhere — except you can’t control line thickness, since there’s no way to pass stroke-width. Or so I thought.

When asked yet again if we could tweak the pre-set stroke width in icons, I went to check how the spec for passing parameters to SVG is doing. Naturally, I didn’t remember where to find it or even its exact name, and while searching, I stumbled on an article with a genius idea:

https://kizu.dev/svg-linked-parameters-workaround

If you’re too lazy to read it, the gist is: when setting viewBox and centering the image, you can make the mask height 100% but width any value from 100% and up, and the image looks identical. So what? Well, inside the SVG file’s own styles, those dimensions affect 100vw and 100vh. This lets you encode info via aspect ratio without changing the visual appearance. All that’s left is layering on a couple of calc() expressions so users can set line thickness via an intuitive — stroke-width variable:

stroke-width=”calc((100vw — 100vh) / 10)”

.icon {
  mask-image: url(...);
  mask-repeat: no-repeat;
  mask-position: center;
  mask-size: calc(100% + 10 * var(--stroke-width)) 100%;
}
Enter fullscreen mode Exit fullscreen mode

Notice the division and multiplication by 10 — this allows for subpixel thicknesses, like 1.5px.

Perfect Shrinkwrap

Let’s ramp it up. New features in modern CSS have solved one of the oldest UI problems — one that previously had no solution: tight wrapping, also known as shrink wrapping. It’s best shown visually:

Example of bad text wrapping

A container with text expands to fit as much text as possible, but once it hits max width, words wrap to new lines. This creates useless empty space, especially noticeable if there are elements after the text:

Default multi-line toast display vs. how we’d like it

Default multi-line toast display vs. how we’d like it

The solution, however, is pretty intricate and wasn’t easy to discover. It started with a clever technique for calculating an element’s size in pure CSS using scroll-driven animations:

https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css

Again, for those skipping the full article: CSS now lets you track an element’s position inside its container with a view timeline. Designed for changing styles based on scroll position, it also lets us store the container’s size in a CSS variable. Roughly: animation progress via view timeline gives the percentage of the container occupied by the tracked element. Set it to a fixed size like 1px, and simple math reveals the container size.

Illustration from the article above

Illustration from the article above



This unlocks tons of new tricks — already working in Chrome and Safari, coming soon to Firefox. With container and inline text (that wraps) sizes calculable, we can find the unused space left. Subtract that gap from the container’s max width and its right margin. This keeps measured elements stable, avoiding an infinite shrink/expand loop:

.inline {
  /* Measuring text */
  view-timeline: --inner-timeline inline;
}

.inline::before {
  content: "";
  padding-inline-start: 1px;
  margin-inline-end: -1px;

  /* Measuring container */
  view-timeline: --outer-timeline inline;
}

.container {
  animation: outer linear, inner linear;
  animation-range: entry 100% exit 100%;
  animation-timeline: --outer-timeline, --inner-timeline; 
  timeline-scope: --outer-timeline, --inner-timeline;

  /* Measuring the gap */
  --delta: calc(-1px / (1 - var(--outer)) * var(--inner));

  /* Shrinking the width */
  max-inline-size: calc(15rem + var(--delta));
}

.block {
  overflow: hidden;

  /* Compensating the gap */
  margin-inline-end: var(--delta);
}
Enter fullscreen mode Exit fullscreen mode

We get extra space compensated as the result:

Conclusion

The world around us is changing at crazy speed. AI is breathing down our necks from all sides, with cries of “run just to stay in place” and all that jazz. But I love my work, do it with real pleasure, and eagerly await the next puzzle that brings the joy of discovery and overcoming the impossible. I hope you’ve shared in it today, even though I know most of us front-end devs aren’t huge CSS fans. Maybe someday this article will help some AI tackle these challenges, but the effort, the grind, the brain-wracking — that’s ours, and I’ll keep cherishing it.

Top comments (0)