Visibility

Hiding is two questions, not one. From whom β€” everybody, or only the eye, or only a screen reader? And for how long β€” until you take it away, until the reader asks for it, or until the box it sits in is a different size? Answer both and there is usually exactly one right tool, and most of the time it is the browser's, not Yeti's.

Hide it from…

Tool Out of sight Out of the accessibility tree Keeps its space What brings it back
hidden attribute yes yes no removing the attribute
display: none, your own CSS yes yes no your rule not matching
visibility: hidden, your own CSS yes yes yes your rule not matching
aria-hidden="true" no yes yes removing the attribute
inert no yes yes removing the attribute
visually-hidden yes no no nothing; it is always out of sight
the skip link yes no no focus
details, popover, dialog while shut while shut no the reader asking
data-show / data-hide yes yes no the container's width
print / print with data-print="none" in the other medium in the other medium no the medium
a component's own data-threshold it changes shape rather than going β€” β€” the container's width

What the platform does

The hidden attribute

The blunt one, and usually the right one. The element is gone: off the screen, out of the accessibility tree, out of the flow. Yeti's reset makes it win over any display a component sets, so it works on a card or a nav item the same as on a div.

<p hidden>Not in the page at all until something removes this.</p>

State, not styling. If a script is going to show and hide something, this is the attribute to toggle β€” not a class.

display: none and visibility: hidden

The same thing as hidden and a different thing, respectively, and the difference is space. display: none takes the box out of the layout; visibility: hidden leaves the box exactly where it is and paints nothing in it. When a row must not move as its contents come and go β€” a validation message, a counter, a spinner beside a button β€” visibility: hidden is what keeps it still. Foundation 6 called that .invisible; it is two words of your own CSS and Yeti does not wrap it.

.form-status[data-state="quiet"] { visibility: hidden; }

Both take the element out of the accessibility tree.

aria-hidden="true"

The opposite direction: still on the screen, gone from the accessibility tree. It is for something a sighted reader needs and a screen reader would only be delayed by β€” a decorative icon beside a word that already says what the icon says, or text duplicated for layout.

<a href="/downloads"><svg aria-hidden="true" viewBox="0 0 16 16"><path d="M8 2v9m0 0 4-4m-4 4-4-4M2 14h12" fill="none" stroke="currentColor" stroke-width="2"/></svg> Download</a>

Never put it on anything focusable. A button a screen reader cannot see but can still land on is the worst of both.

inert

Seen, but unreachable and unannounced: no focus, no clicks, no find-in-page, and out of the accessibility tree. It is for the page behind something β€” a custom overlay you built yourself, a form region disabled while a request is in flight.

View Code
<div inert>
    <p>Still visible. Nothing in here can be tabbed to or clicked.</p>
    <a href="/settings">Settings</a>
</div>

A native dialog opened with showModal() does this to the rest of the page for you, so a dialog needs none of it.

details, popover, dialog

Hidden until the reader asks, with the browser keeping the state and announcing it. A disclosure is details and summary; a panel that closes on Escape and on a click outside is popover; a modal is dialog. None of the three needs a line of script, and each tells a screen reader what happened, which a toggled class never did.

View Code
<details>
    <summary>Shipping and returns</summary>
    <p>Ships within two days.</p>
</details>
View Code
<button popovertarget="notes">Notes</button>
<div id="notes" popover>
    <p>Only in the page while it is open.</p>
</div>

If you are about to write script to show something, this is the section to re-read first.

What Yeti adds

Four things, for the four questions the platform has no single word for.

visually-hidden

Out of sight, still announced. It is for the name a control needs when its face is a picture, or the words that make the third "Read more" on a page mean something different from the first two.

<a href="/trail-map.pdf">Read more<span class="visually-hidden"> about the trail map</span></a>

It is the one two-word class in Yeti, and the second word is there because hidden is already taken by an attribute that does the opposite. Full page: visually-hidden.

A special case of the same recipe, and you get it for nothing. The first link in the body, if it points at a fragment, is hidden the way visually-hidden hides β€” out of sight, in the tree, still in Tab order β€” until it takes focus, at which point it appears at the corner of the window. There is no class; being the first link in the body is the whole contract, which is why the base rule is written that way and why a body whose first link is something else will find it hidden. It is a base rule like any other, and the base guide lists it among the rest of what a page gets for free.

<body>
    <a href="#main">Skip to content</a>
    <!-- the furniture -->
    <main id="main">…</main>
</body>

Foundation 6 called this .show-on-focus.

data-show and data-hide

By the space available, measured on the nearest size container rather than on the window. Both take a width from the same scale as data-threshold and both mean at or above: data-show="md" is shown from md up, data-hide="md" is gone from md up.

View Code
<div class="container">
    <p data-show="md">Shown once this column is 32rem or wider.</p>
    <p data-hide="md">Gone once this column is 32rem or wider.</p>
</div>

They need a size container above them and do nothing without one. container is what makes a box into one, and lists which layouts already are.

print

By medium. print on its own is for paper and nowhere else; print with data-print="none" is for everywhere but paper.

<p class="print">Printed from https://foundationcss.com/guides/visibility</p>
<button class="button print" data-print="none" type="button">Share this page</button>

Full page: print.

Not hiding, but the same family

A component with a data-threshold does not hide anything β€” it changes shape. A nav collapses its links behind a toggle, a pagination drops to the compact form, a card puts its figure above the text instead of beside it. That is always the better answer than shipping two versions of a thing and hiding one, because two versions are two things to keep in step forever. The responsive guide is the whole story.

prefers-reduced-motion is the same shape of question β€” who gets what β€” applied to movement rather than to presence. Yeti honours it through the motion tokens, so nothing you write has to ask; the animations guide says how.

What Yeti does not ship

There is no hide-for-small, no show-for-large, no viewport visibility class of any kind, and there will not be one.

A viewport rule breaks the moment the element moves. A columns that was tuned against a wide window is suddenly inside a sidebar, or inside a card, or inside a dialog; the window has not changed and the box has, and every medium- and large- prefix in the markup is now lying. That was true in Foundation 6 too β€” it was just impossible to fix, because the box could not be measured. It can be now, so data-show and data-hide measure it, and nothing in Yeti asks the window how wide a component should be.

There is also no -only band β€” no "shown at md and not above". A band is two elements with a marker each, or a nested container, and the -only classes were the rarest of the set. If you find yourself wanting one, it is usually a sign the component should be changing shape instead.