Cascade Layers

How Shift CSS uses @layer for predictable specificity

The Specificity Problem

Traditional CSS frameworks suffer from specificity wars:

/* Framework defines */
.btn { background: blue; }

/* You want to override */
.btn { background: purple; }  /* Works... sometimes */

/* But then the framework has */
.btn.btn-primary { background: blue; }  /* Higher specificity wins */

/* So you resort to */
.btn { background: purple !important; }  /* The nuclear option */

The Cascade Layers Solution

CSS Cascade Layers (@layer) provide explicit control over which styles take precedence, regardless of specificity:

/* Framework in a layer */
@layer framework {
  .btn { background: blue; }
  .btn.btn-primary { background: blue; }
}

/* Your CSS (unlayered) ALWAYS wins */
.btn { background: purple; }  /* Always applies! */

Shift CSS Layer Hierarchy

@layer shift.reset, shift.tokens, shift.core, shift.utils;

Order (lowest to highest priority):

  1. shift.reset - Browser reset, fluid typography
  2. shift.tokens - CSS custom properties
  3. shift.core - Components (buttons, cards, inputs)
  4. shift.utils - Utility classes

Your CSS (unlayered) automatically has the highest priority.

Practical Example

<style>
  /* Shift CSS is in @layer shift.* */

  /* Your styles override without !important */
  .btn {
    background: var(--color-accent-500);
    border-radius: 9999px;
  }
</style>

<button class="btn">Custom Styled</button>

Working With Layers

You can also create your own layers that sit between Shift CSS and your custom styles:

@layer shift.reset, shift.tokens, shift.core, shift.utils, app.components, app.utilities;

@layer app.components {
  .my-button { /* Your component styles */ }
}

Integrating With Existing CSS

Need to use Shift CSS alongside existing styles or another CSS framework? Wrap the existing CSS in a lower-priority layer:

/* 1. Define the layer hierarchy first */
/* The last layer in this list is the strongest */
@layer legacy, shift.reset, shift.tokens, shift.core, shift.utils;

/* 2. Wrap the existing CSS */
/* This "demotes" it so it can be easily overridden */
@layer legacy {
  @import url("existing-styles.css");
  /* Any other existing styles go here */
}

/* 3. Shift CSS components now take priority */
@layer shift.core {
  [s-btn="primary"] {
    /* This will now win over any .btn styles in legacy */
    background: var(--s-primary-500);
  }
}

This approach lets you:

  • Gradually migrate from existing CSS to Shift CSS
  • Use both side-by-side during transition
  • Override legacy styles without !important hacks
  • Maintain predictable specificity across your entire codebase
Search