Functions & Mixins

This is the SCSS-authoring reference for Sulphuris. Everything here is callable from your own stylesheets once you import the relevant partial.

Importing

@use "sulphuris/core/utils/helpers" as helpers;
@use "sulphuris/core/utils/generators" as gen;

The utils/index.scss re-forwards both fixes and generators (which itself imports helpers), so @use "sulphuris/core/utils" gives you both.


Unit conversion

toRem($value)

Converts a px value to rem relative to config.$base-font-size. If the value has no px unit it is returned unchanged.

// _config.scss: $base-font-size: 16px
font-size: helpers.toRem(24px); // โ†’ 1.5rem
margin:    helpers.toRem(0);    // โ†’ 0

toEm($value, $unit: 'em', $base: config.$base-font-size)

Same conversion but returns em by default. toRem delegates to this internally with $unit: 'rem'. $base is what one unit is worth โ€” override it when the reference is not the document baseline, as the two functions below do.

padding: helpers.toEm(8px);                // โ†’ 0.5em
padding: helpers.toEm(8px, 'em', 32px);    // โ†’ 0.25em

emit-length($value)

A px length as it should be written into the document: converted under $rem-units, passed straight through when it is off. This is what the generators call, and what your own overrides should call โ€” keep sources in px and convert once at the point of emission, because gutter halves and offset arithmetic have to happen in one unit and mixing px with a relative unit is a hard Sass error.

padding: helpers.emit-length(32px); // โ†’ 2rem, or 32px with $rem-units off

query-width($value)

Converts a px width for use inside a media query, dividing by a literal 16px rather than $base-font-size. Returns the value unchanged when $rem-units is off.

A media query resolves rem against the browser's font-size setting and never sees the percentage $base-font-size writes onto html, so the two references have to be kept apart โ€” otherwise a non-default baseline fires the breakpoint at a different width than the layout it switches. Use this for anything that lands in a @media condition, and emit-length() for anything that lands in the document.

// _config.scss: $base-font-size: 20px
@media (min-width: helpers.query-width(1024px)) { โ€ฆ }  // โ†’ 64rem, still 1024px
max-width: helpers.emit-length(1024px);                // โ†’ 51.2rem, still 1024px

tracking($value, $font-size)

Converts a px letter-spacing to em against the font-size it sits on, not the root โ€” tracking corrects a size, so it has to move when the size does. Both arguments must be px; anything else passes through, since a relative unit is already a ratio the author chose.

letter-spacing: helpers.tracking(2px, 14px); // โ†’ 0.1428571429em

Fluid sizing

fluid($min, $max, $min-vw: 420px, $max-vw: 1680px)

Returns a clamp() expression that scales a px value linearly with the viewport between $min-vw and $max-vw, clamped at both ends. px in, rem out.

font-size: helpers.fluid(16px, 24px);
// โ†’ clamp(1rem, 0.8333333333rem + 0.6349206349vw, 1.5rem)

Color

color($name)

Returns a CSS custom property reference for a named color. If the name is in config.$colors it emits var(--color-<name>); otherwise it falls back to var(--color-<name>, <name>) so raw values still work.

background: helpers.color(primary);   // โ†’ var(--color-primary)
border-color: helpers.color(#ff0000); // โ†’ var(--color-#ff0000, #ff0000)

get-color($name, $mode: '')

Pulls the raw SCSS value from config.$colors (or a named color mode map) at compile time. Use when you need the actual value, not a CSS variable reference.

$brand: helpers.get-color(primary);         // raw value from $colors
$dark:  helpers.get-color(primary, 'dark'); // raw value from $color-modes.dark

z($name)

Returns a named stacking level from the config.$z-layers map. Warns and returns auto for an unknown name.

z-index: helpers.z(modal); // โ†’ 40

The map ships as: behind -1, base 0, dropdown 10, sticky 20, overlay 30, modal 40, popover 50, toast 60, tooltip 70.

behind is for decorative pseudo-elements that sit under their own content:

&::before { z-index: helpers.z(behind); } // โ†’ -1

Breakpoints

breakpoint($size, $explicit: false) mixin

Wraps @content in a @media query built from the configured breakpoint map. Named sizes match your config.$breakpoints keys (e.g. sm, md, lg, xl, xxl).

$size $explicit Result
'' false no media query โ€” content emitted directly
md false min-width only
md true min-width and max-width (explicit range)
min / minimal โ€” forces $size: '' + $explicit: true (smallest range)
@include gen.breakpoint(md) {
  .sidebar { display: block; }
}
// โ†’ @media only screen and (min-width: 768px) { .sidebar { display: block; } }

@include gen.breakpoint(sm, true) {
  .mobile-only { display: flex; }
}
// โ†’ @media only screen and (min-width: 420px) and (max-width: 767px) { ... }

@include gen.breakpoint(min) {
  .tiny { font-size: 12px; }
}
// โ†’ @media only screen and (max-width: 419px) { ... }

Transitions

transition($properties, $durations, $easings) mixin

Generates a transition declaration. All parameters accept a single value or a list (one entry per property). $durations and $easings default to config.$default-transition-duration and config.$default-transition-easing. Easing names from config.$custom-easings are resolved automatically.

@include gen.transition(opacity);
// โ†’ transition: opacity 250ms cubic-bezier(0.86, 0, 0.07, 1);
// (defaults: 250ms + the 'ease-in-out-quint' custom easing)

@include gen.transition((opacity, transform), (200ms, 400ms), (ease-in, ease-out));
// โ†’ transition: opacity 200ms ease-in, transform 400ms ease-out;

Selection & form helpers

selection($color: primary) mixin

Sets ::selection background to the named color (resolved via color()).

@include helpers.selection(accent);
// โ†’ ::selection { background: var(--color-accent); }

placeholder($color) mixin

Sets placeholder text color via the standard ::placeholder selector.

input {
  @include helpers.placeholder(muted);
}

String & map helpers

str-split($string, $separator)

Splits a string into a list on each occurrence of $separator.

helpers.str-split('a.b.c', '.'); // โ†’ 'a' 'b' 'c'  (list of length 3)

map-deep-get($map, $keys...)

Fetches a nested map value by a path of keys.

helpers.map-deep-get((a: (b: 42)), a, b); // โ†’ 42

svg-uri($svg)

Wraps an inline SVG in a url() data URI, percent-encoding it on the way. Write the icon as SVG and read it back as SVG โ€” the escaped form is the same bytes either way, and only one of the two says what the icon is.

%, <, >, # and " are encoded; everything else survives a quoted url() intact, and encoding more only makes the declaration longer. # is not optional โ€” a hash colour left raw ends the URL and starts a fragment identifier.

.icon-check {
  background: currentcolor;
  mask: helpers.svg-uri("<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L1.72 9.28a.75.75 0 1 1 1.06-1.06L6 11.44l6.72-6.72a.75.75 0 0 1 1.06 0Z'/></svg>") center / contain no-repeat;
}
// โ†’ mask: url("data:image/svg+xml,%3Csvg xmlns='http://โ€ฆ'%3E%3Cpath d='M13.78โ€ฆ'/%3E%3C/svg%3E") center / contain no-repeat;

Single quotes inside the SVG, double quotes around it โ€” the other way round works too, since " is encoded, but it costs six characters per attribute. Masked rather than painted is the usual reason to want this at all: a mask takes the element's own color, so one icon follows a colour mode instead of needing a second copy in another fill.

Markup only, never a path: Sass has no way to read a file, so an icon on disk needs a build step and this is not one.


Utility class generator

utility-class-generator($pref, $property, $values, $unit, $suff, $orientations, $responsive, $var) mixin

The engine behind Sulphuris's utility classes. Generates a set of classes for a CSS property across all configured breakpoints. Accepts either positional arguments or a single config map as $pref.

Positional signature:

Param Type Description
$pref string or map Class name prefix (or full config map)
$property string CSS property (e.g. padding)
$values list or map Values to iterate
$unit string Unit appended to each value (e.g. px, rem)
$suff string Optional class name suffix
$orientations bool When true, also generates -top/-right/-bottom/-left variants using config.$orientations
$responsive bool When true (default), emits breakpoint-prefixed variants
$var bool or string Use CSS custom properties instead of raw values

Example โ€” generate padding utilities:

@include gen.utility-class-generator(
  $pref:         'p',
  $property:     'padding',
  $values:       (0, 4, 8, 12, 16, 24, 32),
  $unit:         'px',
  $orientations: true,
  $responsive:   true
);

This emits classes like .p-0, .p-8, .pt-16, .pb-32, and breakpoint variants .p-md-8, .pt-lg-24, etc.

Map form (equivalent, all keys optional except prefix, property, values):

@include gen.utility-class-generator((
  prefix:       'p',
  property:     'padding',
  values:       (0, 4, 8, 12, 16, 24, 32),
  unit:         'px',
  orientations: true,
  responsive:   true
));

grid-track-map($n)

Returns a map keyed 1โ€ฆ$n whose values are repeat(n, minmax(0, 1fr)). Not a generator itself โ€” feed it to utility-class-generator, which is what emits the classes and their breakpoint variants.

@include gen.utility-class-generator('grid-cols', 'grid-template-columns', gen.grid-track-map(4));
// โ†’ .grid-cols-1 โ€ฆ .grid-cols-4, plus .grid-cols-sm-1 โ€ฆ .grid-cols-xxl-4

core/layout/_grid.scss calls it twice, with config.$columns and config.$rows, to produce .grid-cols-* and .grid-rows-*. Overriding those two config values is enough for the common case; call it directly only for a third track family with a different count.

grid-span-map($n)

Returns a map keyed 1โ€ฆ$n plus full, whose values are span n / span n and 1 / -1. Same shape of use โ€” pass it to utility-class-generator with grid-column or grid-row.

@include gen.utility-class-generator('grid-column-span', 'grid-column', gen.grid-span-map(4));
// โ†’ .grid-column-span-1 โ€ฆ -4, .grid-column-span-full, plus breakpoint variants

Both are plain maps, so map.merge adds your own keys before generating โ€” ('screen': '1 / -1') or a named area โ€” without touching the generator.

See Grid for the classes themselves.


Browser fixes (_fixes.scss)

Two mixins for targeted rendering quirks. Include only where needed.

Mixin Effect
animate-scale-fix() Prevents jank on transform: scale() animations by forcing GPU compositing layer (backface-visibility: hidden, transform-style: preserve-3d)
clearfix() Classic ::after float-clearing pattern
.card {
  @include gen.animate-scale-fix();
}

clearfix() no longer backs a .clearfix class โ€” the float, clear and clearfix utilities were dropped in 3.0.0. The mixin stays for the case it is still good for: clearing a float you set yourself, in your own CSS. Sulphuris emits no floats.


Normalize (_normalize.scss)

A verbatim copy of normalize.css v8.0.1 (MIT). Included automatically when you import the Sulphuris core. It corrects browser inconsistencies without stripping all defaults โ€” box model, font inheritance for form controls, display: block for main/details, sub/sup line-height, and more. No configuration; just let it load.