Skip to content

Surfaces

Surfaces are decorative background treatments applied to <section> elements. They are registered as global CSS via Wage\Assets::register_global_css() and output as <style id="wage-global"> in the <head>.

One surface = one class. Never stack two surface classes on the same element. Each surface is a single, self-contained CSS class defined in a paired CSS file. The child theme’s inc/surfaces/surfaces.php reads the CSS file and registers it with Wage\Assets::register_global_css(). There are two types:

  • Simple surfaces — just set background on the element (gradients, solid colours)
  • Texture surfaces — add a ::after pseudo-element for an SVG texture overlay, or use the Wage\SectionBackground component for image textures

Both use the same surface--{name} class in markup. No extra structural class needed.

Surface classes are named by token role + texture: surface--primary-dark-engraving, surface--base-light-herringbone. Use descriptive names based on the token and texture, not the page context (e.g. surface--primary-dark-engraving, not surface--hero).

<section class="surface--cream">
<div class="container">...</div>
</section>
<section class="surface--stone">
<div class="container">...</div>
</section>
<section class="my-section surface--navy">
<div class="container">...</div>
</section>

Surfaces live in inc/surfaces/ in the child theme as a PHP file and a paired CSS file:

inc/surfaces/
├── surfaces.php # Registers CSS with Wage\Page
└── surfaces.css # All surface class definitions
<?php
Wage\Assets::register_global_css( file_get_contents( __DIR__ . '/surfaces.css' ) );
.surface--cream {
background: linear-gradient(180deg, var(--base-ultra-light) 0%, var(--body-background-color) 100%);
}

Dark surfaces include color-scheme: dark alongside their visual treatment. This causes all semantic tokens inside that section to auto-resolve to their dark values:

.surface--velvet {
color-scheme: dark;
background: radial-gradient(ellipse 70% 60% at 50% 45%,
var(--primary) 0%,
var(--primary-dark) 50%,
var(--primary-ultra-dark) 100%);
}

For full details on how color schemes work — including the surface--dark utility class, locking components to a scheme, and writing adaptive CSS — see the Color Scheme docs.

Texture surfaces are self-contained — they set their own position: relative, isolation: isolate, and ::after pseudo-element:

.surface--stone {
position: relative;
isolation: isolate;
}
.surface--stone::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
z-index: -1;
opacity: 0.04;
background-image: url("data:image/svg+xml,...");
background-repeat: repeat;
background-size: 200px 180px;
}

The framework provides three SVG texture patterns commonly used across projects:

PatternVisualTypical use
StoneBrick/ashlar patternHeritage, masonry, luxury
EngravingFine crosshatch linesCertificates, currency, premium
HerringboneTweed weave patternTextile, craft, warmth

SVG textures use ::after pseudo-elements at low opacity (0.03-0.06) with a bottom fade mask for a subtle, non-repeating edge.

For tiling image textures used inside a surface’s ::after overlay, the image URL must be injected via a CSS variable because surface CSS is inlined via file_get_contents() — relative URLs won’t resolve.

In surfaces.php, register the CSS variable before the CSS file:

$texture_url = get_stylesheet_directory_uri() . '/assets/images/surface--green-felt-tile.jpg';
Wage\Assets::register_global_css( ":root { --surface-velvet-texture: url('{$texture_url}'); }" );
Wage\Assets::register_global_css( file_get_contents( __DIR__ . '/surfaces.css' ) );

Then reference the variable in surfaces.css:

.surface--velvet::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
z-index: 0;
opacity: 0.6;
background-image: var(--surface-velvet-texture);
background-repeat: repeat;
background-size: 212px 212px;
mask-image: linear-gradient(to bottom, transparent 0%, black 70%);
-webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 70%);
mix-blend-mode: difference;
}

Store texture images in assets/images/ in the child theme. For seamless tiling, process the image into a tileable square before use.

Image textures (SectionBackground component)

Section titled “Image textures (SectionBackground component)”

For photographic or complex image textures that should be full-bleed (not tiling), use the Wage\SectionBackground component. This renders an <img> element with object-fit: cover:

<section class="my-section">
<?php echo new \Wage\SectionBackground( id: $image_id, opacity: 0.15 ); ?>
<div class="container">...</div>
</section>

Wage\Assets::register_global_css() collects CSS into a static pool. When Wage\Page::render() is called, it enqueues this pool as an inline <style id="wage-global"> block in <head> — before component CSS but after the core stylesheet.

Because surfaces.php calls register_global_css() at the top level of the file (not inside a class constructor), the CSS registers as soon as the file is require_once’d during bootstrap. This means surface CSS loads on every page, which is fine because it is small (a few gradient declarations and texture blocks).

This is different from component CSS, which only loads when a component is instantiated on a specific page.

Core’s flag CSS automatically hides texture overlays in wireframe mode:

body.wireframe [class*="surface--"]::after { display: none; }

This is output by Wage\Flags::output_css() when the wireframe flag is active.

  • Surfaces are project-specific. Never add them to the parent theme. Each project defines its own set in inc/surfaces/.
  • One surface = one class. Never stack two surface classes on the same element. Each surface class includes its background + texture together.
  • Named by token role + texture. Use surface--primary-dark-engraving, surface--base-light-herringbone — not contextual names like surface--hero.
  • Use tokens in surface definitions. Reference var(--base-ultra-light), var(--body-background-color), etc. — never hardcode brand colours.
  • Dark surfaces include color-scheme: dark. See Color Scheme for the full system.
  • Surfaces can be combined with component classes. For example: <section class="testimonials surface--primary-dark-engraving">.
  • SVG textures use ::after pseudo-elements at low opacity with bottom fade mask.
  • Image textures use Wage\SectionBackground component for lazy loading.