Skip to content

Helpers

Helpers are the few classes that are not part of a utility family: sr-only, which hides content visually but keeps it available to screen readers, and the breakpoint debugger, an optional overlay that shows the current screen size while you build a layout.

Neither helper is a utility class, so $generate-utility-classes does not control them. Each has its own switch:

Helper Source Emitted when Default
.sr-only Base layer (base/_base.scss, inside @layer base) The base feature flag is on On
.NuvoUI-Debugger-Wrapper and its parts utilities/_helpers.scss $enable-debugger is true Off
Setting .sr-only Debugger
$generate-utility-classes: false Emitted Emitted if $enable-debugger is true
$feature-flags: ("base": false) Not emitted Not affected
$enable-debugger: false Not affected Not emitted
$parent-selector: ".app" .app .sr-only Not prefixed: .NuvoUI-Debugger-Wrapper

Neither helper has a mixin, a responsive variant or a token.

sr-only removes an element from the visual layout but leaves it in the accessibility tree, so screen readers still announce it. Use it for text that the visual design expresses another way: the label of an icon button, a heading that sighted users infer from the layout, or extra context on a repeated link such as “Read more”.

<div class="flex x-center gap-3">
<button type="button">
<span aria-hidden="true">X</span>
<span class="sr-only">Close dialog</span>
</button>
<a href="#">Read more<span class="sr-only"> about backdrop filters</span></a>
</div>

The class is emitted in the base layer together with the element styles:

@layer base {
.sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip-path: inset(50%) !important;
white-space: nowrap !important;
border: 0 !important;
}
}

Every declaration is !important. An important declaration inside a cascade layer outranks unlayered styles, including unlayered !important ones, so no utility class on the same element can reveal the content by accident. The same rule also means you cannot undo sr-only with another class: remove the class instead.

sr-only is described alongside the other ways of hiding content on the Display page, which compares it with hide, hidden and invisible.

You want Use
Hidden visually, announced by screen readers sr-only
Hidden for everyone, no space taken hide or hidden
Hidden for everyone, space kept invisible
Visible, not announced (decorative) aria-hidden="true" on the element, no class

Do not put sr-only on a link or control that should appear when it receives focus, such as a “Skip to content” link: the !important declarations keep it one pixel wide even while focused. Write a dedicated class that hides the element only while it is not focused:

.skip-link {
@include apply(absolute, p(2), bg(surface), rounded);
inset-inline-start: token(space-2);
inset-block-start: token(space-2);
&:not(:focus-visible) {
clip-path: inset(50%);
width: 1px;
height: 1px;
overflow: hidden;
white-space: nowrap;
}
}

With $feature-flags: ("base": false) core emits no base element styles and no sr-only class. There is no sr-only mixin, so apply(sr-only) raises Mixin not found: sr-only. Copy the declarations above into your own class when the base layer is off.

// error
.label {
@include apply(sr-only);
}

The debugger is a small fixed overlay that names the current screen range, for use during development. Core ships only its CSS; it has no script and creates no markup. Turn it on when loading core:

@use "@nuvoui/core" with (
$enable-debugger: true
);

Then add the overlay to your page yourself:

<div class="NuvoUI-Debugger-Wrapper top-right">
<div class="NuvoUI-Debugger-Main" data-size="1180 x 720"></div>
<div class="NuvoUI-Debugger" data-element=".sidebar" data-size="280 x 720"></div>
<span class="NuvoUI-Debugger-Close">x</span>
</div>
Part What it shows or does
.NuvoUI-Debugger-Wrapper Fixed container, z-index: 999999, ignores pointer events. Top left by default.
top-left, top-right, bottom-left, bottom-right Position modifiers on the wrapper, 10px from the corner
.NuvoUI-Debugger-Main The screen label (Screen: md) from media queries, then the data-size attribute
.NuvoUI-Debugger One extra panel per element you want to watch: data-element in bold, then data-size
.NuvoUI-Debugger-Close A round button in the top right corner of the wrapper; it is the only part that accepts clicks

data-size and data-element are plain attributes shown through attr(). Keep them current from your own script, for example with a resize listener or a ResizeObserver, and hide the wrapper when the close button is clicked.

The label is generated from $breakpoints. Each label names the breakpoint that the viewport has not reached yet, so it is one step ahead of the responsive suffixes that are active:

Viewport width (default breakpoints) Label Active responsive suffixes
Up to 480px Screen: xs none (@xs from exactly 480px)
480px to 639.98px Screen: sm @xs
640px to 767.98px Screen: md @xs, @sm
768px to 1023.98px Screen: lg up to @md
1024px to 1279.98px Screen: xl up to @lg
1280px to 1535.98px Screen: 2xl up to @xl
1536px and wider No label up to @2xl

At exactly 480px both the xs and the sm query match and the later one, sm, is shown.

The mixins that react to user preferences, reduced-motion, high-contrast, prefers-dark and dark-mode, live in the accessibility tools module. They are documented on the Media queries page and, for the dark theme, on the Theming page.

  • Keep the text meaningful. sr-only text is read in place, so write it to make sense in the surrounding sentence (“Read more about backdrop filters”), and keep a leading space where it continues visible text.
  • Do not hide focusable controls with sr-only. A keyboard user can tab to a control they cannot see. Use the skip link pattern above for controls that should appear on focus.
  • Prefer real labels. For form controls, a visible label is better for everyone; use sr-only on a label only when the design has no room for it.
  • RTL. sr-only does not depend on text direction. The debugger position modifiers are physical (top-left stays on the left in right-to-left pages).