Docs

This page isn't translated yet

next

Cascade layers

Where Systhema's CSS sits in the cascade, why Tailwind utilities beat component classes, and how @layer app overrides both.

On this page

Systhema's stylesheet is ordered with CSS cascade layers, so you can predict which rule wins without raising specificity: a Tailwind utility beats a Systhema component class, and a rule in your app layer beats both.

Layer orderLink to this section

The Tailwind entry declares these top-level layers, in this order (a later layer wins):

theme, base, components, utilities, app
  • theme, base, components and utilities are Tailwind's layers.
  • app is empty. It is reserved for your project's overrides.

Inside utilities, Systhema's component classes (.button-primary, .card-default, .accordion-title, the text styles, gap-* and the other token utilities) live in a named sub-layer, utilities.systhema. Tailwind's own utilities are emitted directly in utilities, outside any sub-layer:

utilities
├─ utilities.systhema   Systhema component classes and token utilities
└─ (no sub-layer)       Tailwind utilities: text-xl, p-4, rounded-none
app                     your overrides

A rule that sits directly in a layer beats every rule in that layer's sub-layers, whatever their specificity. That gives the familiar order: Tailwind utilities beat Systhema's classes, and app beats everything.

Overriding a component with a utilityLink to this section

Because of that order, a Tailwind utility on a component overrides one property and keeps the rest of the component's styles:

Utilities over a component class
import { Button } from '@systhemaui/next'

export default function Demo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button.button variant="primary">Default</Button.button>
      <Button.button variant="primary" className="rounded-none">
        rounded-none
      </Button.button>
      <Button.button variant="primary" className="rounded-full px-8">
        rounded-full px-8
      </Button.button>
    </div>
  )
}

The same goes for the token utilities: text-h2 font-normal is an h2 at regular weight, and gap-md gap-4 resolves to gap-4.

Writing project overridesLink to this section

For overrides that are more than a utility or two, write CSS in the app layer. It wins over Tailwind's utilities and every Systhema class, and keeps your rules in one predictable place:

app/globals.css
@import '@systhemaui/core/tailwind';

@layer app {
  /* Put the accordion icon before the title from md up, on this project only. */
  .accordion-title {
    @variant md {
      @apply flex-row-reverse justify-end;
    }
  }
}

Rules outside any layer would win too, because unlayered CSS beats every layer, but they also beat Tailwind utilities you add in markup later. Keeping overrides in app leaves the utilities in charge.

Overriding !important rulesLink to this section

!important reverses the layer order: an !important declaration in an earlier layer beats one in a later layer, and an unlayered !important loses to every layered one. A few Systhema rules carry !important (the scroll-reveal states, for example), and they sit in base.systhema, so an !important in @layer app cannot beat them. To out-rank one, declare an override layer before the Systhema import, which makes it the earliest layer:

app/globals.css
@layer overrides;
@import '@systhemaui/core/tailwind';

@layer overrides {
  .aos:not(.animated) {
    opacity: 1 !important;
  }
}

Components that borrow another block's classesLink to this section

Some component rules take their type scale, or whole component styles, by applying another Systhema class instead of repeating its declarations: .form-label applies text-form-label, and formatted rich text (.richtext.is-formatted) applies text-h1 to text-h6, text-body, link, quote, separator, list-bullet and list-number.

Those bindings follow the flag of the block that owns the class, not the block that borrows it. Turning blocks.link off while keeping blocks.richtext on does not break your build: the link binding is skipped, and links in formatted rich text fall back to your own link styles. If your tokens do not define a text style, its binding is skipped the same way. Formatted rich text looks its best with typography, link, quote, separator and list all on, which is the default. See Component CSS blocks.

Rich-text block margins apply only to Systhema's .columns, .stack, .card-<type> and .accordion-<type> classes. A project class whose name merely contains card or accordion does not get them; use a Systhema block class for the doubled rhythm, or add a margin rule of your own.