Category: CSS & Sass

  • The Biggest Limitation of CSS Custom Properties (and Why I Still Use Them)

    The Biggest Limitation of CSS Custom Properties (and Why I Still Use Them)

    Over the past year, I’ve shifted from relying heavily on Sass variables to using CSS custom properties (aka CSS variables) almost exclusively. Why? Because unlike Sass variables, CSS variables can be overridden dynamically at runtime.

    This difference changes how you structure your styles—and in many cases, it makes your CSS more flexible and maintainable.

    The limitations of Sass variables

    Sass variables are powerful, but they come with one key limitation: they’re compiled away into static values.

    That means once your Sass is compiled into CSS, those variables don’t exist anymore—they’ve been replaced with hard-coded values. If you need different values under different conditions (like responsive breakpoints, dark mode, or user preferences), you can’t “update” the variable. Instead, you end up creating multiple variables for each scenario.

    For example, let’s say we want paragraph text to be 14px on mobile and 16px on desktop:

    $p-sm: 14px;
    $p-md: 16px;
    
    p {
      font-size: $p-sm;
    
      @media (min-width: $breakpoint-md) { 
        font-size: $p-md;
      }
    }

    This works fine, but notice we had to define two separate variables. If you scale this pattern across font sizes, colors, spacing, and themes, you quickly end up with a lot of extra Sass variables.

    How CSS property overrides can solve the problem

    CSS custom properties, on the other hand, are inherited and live in the browser at runtime. That means their values can be updated, overridden, and scoped dynamically—without recompilation.

    Here’s the same font-size example written with CSS custom properties:

    :root {
      --font-size-p: 14px;
      @media (min-width: $breakpoint-md) {
        --font-size-p: 16px;
      }
    }
    
    p {
      font-size: var(--font-size-p);
    }

    Now we only need one variable, and we simply override it at the media query. The p element always references --font-size-p, and the browser handles the cascade.

    This pattern scales beautifully. Whether you’re building responsive typography, theming with light/dark mode, or even reacting to user interactions, CSS variables stay flexible.

    There is still one thing CSS variables can’t do

    Sass variables vanish at compile time, while CSS custom properties live on at runtime and adapt as styles cascade. That flexibility is powerful, but it comes with a catch—media queries are evaluated at parse time, before custom properties exist.

    In practice, this means you can’t write:

    @media (min-width: var(--breakpoint-md)) {
      /* this will fail */
    }

    The parser will ignore it entirely. Instead, the right pattern is to use Sass variables.

    @media (min-width: $breakpoint-md) {
      /* this will work */
    }

    * Edited for improved clarity with a little help from AI

  • How to fix an Angular component that won’t stick

    How to fix an Angular component that won’t stick

    If you’re working in a component-driven framework like Angular, you might notice one day that your components start ignoring your router’s scroll events and are no longer sticky. All of a sudden, you find the only way to keep your components from scrolling off the page is by assigning them a fixed position with CSS. The only reason this works is because the closest positioned ancestor your components can find is the last possible resort, Document, which represents the whole DOM.

    What do you mean by ‘sticky’?

    Just to be clear, I’m referring to the CSS property ‘position’ that has the following possible values:

    1. static – no special positioning
    2. relative – normal positioning
    3. fixed – always in the same place, regardless of scroll
    4. absolute – always in the same place relative to its closest ancestor that has positioning
    5. sticky – always in the same place after it reaches a top, right, bottom, or left scroll position

    Using sticky is like fixed + relative.

    Fixed is cool if you only care about positioning in respect to the viewport. But using it means your components no longer take space in the DOM, and therefore no longer have any relation to the positioning of everything else in the DOM. This runs the risks for things like overlapping, cropped and/or empty content.

    What kinds of issues are we talking about?

    1. Applying sticky has no effect on component. It scrolls out of viewport with everything else.
    2. Applying sticky has partial effect on component. It stays in the right spot until you scroll past the entire length of the screen.

    Steps to troubleshoot

    Make sure you’ve specified at least a top, left, right or bottom position on the component you’re trying to make sticky.

    You can used fixed or relative units, but you must specify at least a top, left, right or bottom position or else it won’t know where to stick.

    Make sure your component’s container has a defined height or width.

    Your component needs to know the dimensions of its container in order to calculate where the sticking point is in relation to itself. It will continue checking its ancestors until it finds one with positioning.

    Make sure your component’s container does NOT have overflow set to hidden.

    Once you’ve scrolled your component out of the viewport, it is considered overflow. Therefore, hiding the overflow on a sticky element that needs overflow to stick won’t work.

    But what if the height is dynamic?

    Let’s say you want the footer to always start at the bottom of the page, even if there’s not enough content to push it down. If you set the component’s container height to 100% and it doesn’t stretch to the end of the page, you might try 100vh. While that works and your component might be sticky at first, you may notice it stops sticking after you’ve scrolled past the full length of the page.

    To solve this problem, set the following styles on your component’s container:

    .container {
       ...
       min-height: 100vh;
       height: 100%;
     }