Skip to content
Recursica

Layers

Most contrast problems in a design system don’t come from bad colors. They come from good colors used in the wrong place: gray text that reads fine on the page and disappears inside a gray card, or a button that looks right on a white background and muddy inside a modal. Layers are how Recursica prevents this. Each element is assigned a depth, and Recursica chooses colors that stay readable at that depth.

What is a layer?

A layer is a level of depth on the screen. Recursica has four, numbered 0 to 3, and each one sits inside the one before it:

  • Layer 0 is the page itself, the background of the whole app.
  • Layer 1 is a surface on the page, such as a card, a sidebar, or a dashboard panel.
  • Layer 2 is content inside a surface, such as an input field, a table row, or a card within a card.
  • Layer 3 floats above everything else, such as a dropdown menu, a select list, or a tooltip.

Each layer has its own surface color, text color, border, elevation, and padding. These are design tokens, set once in Theme Forge, and every layer on every page follows them.

Applying a layer

A layer isn’t a component or a class. It’s a single attribute on a container, and everything inside the container picks up that layer’s values:

<html data-recursica-theme="light">      <!-- layer 0 by default -->
  <section data-recursica-layer="1">     <!-- a card -->
    <input>                              <!-- uses layer 1 values -->
    <section data-recursica-layer="2">   <!-- a field group inside it -->
      <input>                            <!-- uses layer 2 values -->
    </section>
  </section>
</html>

We designed it this way so that components never need to know where they are. The same input component works on the page, in a card, and in a modal, because the container around it decides which values it uses.

Three rules follow from that design:

  1. Put the theme on the root and layers on containers. Setting data-recursica-theme on <html> covers the whole app. Layer 0 is the default, so a page that never sets a layer is already correct.
  2. A layer applies to everything inside it, until a container further in sets a different one.
  3. Components should never name a layer. A component uses general variable names, like --recursica_ui-kit_components_button_variants_styles_solid_properties_colors_background, and the nearest layer decides what they mean. A component whose CSS selects on [data-recursica-layer] stops adapting.

Components on different layers

A layer doesn’t only change its own background. It also redefines every component inside it. That’s why a solid button on the page and the same button in a modal can be slightly different colors: the button is identical, but its surroundings aren’t, so the system adjusts it to keep enough contrast.

There is one consequence worth planning for. The general variable names for layers 1 to 3 only exist inside a container that has that layer’s attribute. If an element uses layer 1 values but isn’t inside a data-recursica-layer="1" container, those values come out empty and the element loses its background. Layer 0’s values are set for the whole page, which is why the default never fails this way.

Layers and accessibility

Layers are the main way Recursica keeps text accessible. Text, icon, and border colors adjust to the layer behind them, so a component meets WCAG AA contrast whether it sits on the page or inside a card, in light mode or dark.

This only works when each element uses the right kind of color. Each layer has two:

  • Surface and tone colors fill an element. Examples are layer_1_properties_surface and a button’s tone.
  • Foreground colors are drawn on top of a fill. Examples are layer_1_elements_text_color and layer_1_elements_interactive_color. These are the colors checked for contrast.

The most common mistake we see is a link colored with an interactive tone instead of an interactive color. The tone is the fill of a solid button, which is often a pale or bright tint that fails contrast as text. We recommend a simple test: if it’s text, an icon, or a link, use a foreground color. If it’s a shape, use a tone.