display: none vs visibility: hidden in CSS
What You Learn From This Page
- How
display: noneremoves an element subtree from the CSS box tree. - How
visibility: hiddenhides painting while preserving layout space. - How both techniques affect descendants, accessibility, keyboard focus, and interaction.
- How they differ from
opacity: 0,aria-hidden,hidden, andinert. - Which hiding technique suits menus, forms, modals, responsive layouts, animation, and placeholders.
- How to test and debug hidden content without creating inaccessible or unstable interfaces.
Introduction
Web interfaces frequently need to hide content. A navigation menu closes, an error message waits for validation, a modal replaces the page's active layer, or a responsive layout removes controls that do not belong at a narrow width. CSS offers several ways to make something invisible, but invisibility is only one part of the behavior. The browser must also decide whether the element occupies space, participates in layout, accepts interaction, appears to assistive technology, and can be animated.
The two declarations most often compared are display: none and visibility: hidden. They can produce a similar screenshot because neither shows the element. Their layout effects are fundamentally different. Display controls whether boxes are generated. Visibility controls whether generated boxes are painted. That distinction affects surrounding content, descendants, measurements, transitions, focus management, and the architecture of a component.
A reliable choice begins with intent. Ask whether the hidden content is absent from the current interface or merely concealed while its position must remain reserved. Then ask whether users of keyboards and assistive technology should reach it. The correct solution follows from those requirements rather than from whichever declaration first makes the pixels disappear.
The Core Difference
display: none causes the selected element and its descendants to generate no CSS boxes or text sequences. Because there is no box, the subtree consumes no layout space. Siblings are laid out as though that subtree were not present in the box tree, even though the element still exists in the document object model.
visibility: hidden keeps the generated box in layout but makes it invisible. Its width, height, margins, grid track contribution, and other geometry continue to influence surrounding content. The element is visually absent, yet the space it would occupy remains. This is useful only when retaining that geometry is intentional.
.removed-from-layout {
display: none;
}
.reserved-but-invisible {
visibility: hidden;
}
A helpful mental model is that display asks, "Does this subtree generate layout boxes?" Visibility asks, "Should these boxes be drawn?" That model is more accurate than memorizing that one leaves a gap and the other does not, because it also explains descendant behavior and layout calculations.
How display: none Works
When the computed display value is none, the element's entire subtree is omitted from the box tree. There is no principal box for the element and no boxes for descendants. Properties such as padding, border, margin, width, height, positioning, and transform have nothing to render because no CSS box exists.
The document node is not deleted. JavaScript can still find it with querySelector(), read its attributes, change its text, attach listeners, or later restore a visible display value. DOM presence and rendered-box presence are separate concepts. This distinction is important when a component keeps state while it is closed.
const panel = document.querySelector("#filters");
panel.style.display = "none"; // Node remains in the DOM.
panel.textContent = "Updated while hidden";
panel.style.display = "block"; // A box is generated again.
Because surrounding boxes reflow into the released space, hiding a large element with display can move much of the page. That is often desirable for a collapsed accordion or closed menu. It can be undesirable when the interface is expected to remain spatially stable.
Descendants Under display: none
A descendant cannot escape a display: none ancestor by declaring its own display value. The ancestor removes the entire subtree from box generation before the child can create a visible box. Setting a nested element to display: block, flex, or grid does not restore it.
.parent {
display: none;
}
.parent .child {
display: block; /* Still produces no box. */
}
To reveal the child, the relevant hidden ancestor must first return to a box-generating display value. In a real component, it is usually clearer to toggle a state class or the HTML hidden attribute on that ancestor rather than writing inline display values throughout the subtree.
How visibility: hidden Works
The visibility property determines whether generated boxes are rendered. With visibility: hidden, the box remains part of layout but nothing is painted for that box. The browser still computes its geometry, and siblings continue to respect the space it occupies.
Consider three cards in a grid. If the middle card receives display: none, the grid places the next card into the available slot. If the middle card receives visibility: hidden, its grid item continues occupying the slot, leaving an intentional visual gap. Neither outcome is universally correct; the design requirement determines which one is useful.
.schedule-slot.is-unavailable {
visibility: hidden;
}
Visibility is inherited, so descendants normally become hidden with the parent. Unlike display, however, the descendant can override the inherited value. This unusual detail makes visibility useful for a few layered layouts but can also create surprising partial content if styles are not scoped carefully.
Visible Descendants Inside a Hidden Parent
A child can set visibility: visible even when an ancestor is hidden. The ancestor's box still occupies space, the ancestor itself remains unpainted, and the child can be rendered in its normal position. This differs completely from a display-none ancestor, whose descendants have no boxes to reveal.
.card-shell {
visibility: hidden;
}
.card-shell .status-badge {
visibility: visible;
}
This behavior should be deliberate. A broad visibility rule combined with a component rule that restores visibility may expose isolated descendants unexpectedly. Inspect computed styles on both the child and each ancestor when only part of a supposedly hidden region appears.
Quick Comparison Table
| Behavior | display: none | visibility: hidden |
|---|---|---|
| Visible on screen | No | No |
| Generates layout boxes | No | Yes |
| Reserves space | No | Yes |
| Surrounding layout reflows | Usually yes | Usually no |
| Descendant can become visible alone | No | Yes, with visibility: visible |
| Normally exposed to accessibility APIs | No | No |
| Normally participates in sequential focus | No | No |
| Direct opacity-style transition | No traditional interpolation | Discrete state can coordinate with opacity |
| Remains in the DOM | Yes | Yes |
| Typical intent | Remove inactive UI from layout | Preserve an intentional layout slot |
Effect on Normal Flow
In ordinary block flow, removing a hidden block with display allows later blocks to move into the released region. Inline text closes around a display-none phrase as if the phrase generated no text sequence. Lists renumber according to the remaining rendered items, and margins can collapse differently because the hidden box no longer participates.
A visibility-hidden block keeps its normal dimensions. The next block begins after the reserved region, and inline content retains a blank interval corresponding to the hidden fragment. That blank space can confuse users unless it represents an understandable placeholder or stable schedule position.
Effect in Flexbox and Grid
A flex or grid child with display: none does not become a flex or grid item because it generates no box. The container recalculates item distribution, gaps, alignment, and track placement using the remaining items. This can change column count, free-space distribution, and the position of later controls.
A child with visibility: hidden remains a flex or grid item. It contributes its size and occupies its assigned position, although it is not painted. Use this for a stable comparison grid or timed board only when an empty position carries meaning. Do not use it to create arbitrary spacing; Grid tracks and gap express spacing more clearly.
visibility: collapse
The visibility property also defines collapse. Its primary purpose is collapsing table rows, row groups, columns, and column groups while allowing the table to calculate dimensions in a structured way. It can remove the occupied table space more efficiently than rebuilding the table's logical structure.
tr.is-filtered-out {
visibility: collapse;
}
Outside applicable table and certain layout contexts, collapse may behave like hidden according to the relevant specifications and implementation details. Do not treat it as a universal replacement for display none. Test the exact element type and supported browsers before relying on collapse in production.
Accessibility Tree Behavior
Content hidden with either display: none or visibility: hidden is generally excluded from the accessibility tree. Screen readers and other assistive technologies normally do not announce that content during navigation. This is appropriate for interface regions that are currently unavailable, such as a closed menu or inactive modal content.
It is inappropriate when content should remain available to nonvisual users. For example, do not hide a form label with either declaration and expect a screen reader to use it as ordinary visible text. Use a proper label, an accessible-name relationship, or an established visually hidden utility based on the actual requirement.
Accessibility behavior should be tested, not inferred only from sight. Inspect the browser's accessibility tree, navigate with a keyboard, and use representative assistive technology for critical workflows. A CSS declaration is one part of a complete state-management pattern.
Keyboard Focus
Elements under display: none or visibility: hidden normally cannot receive focus through sequential keyboard navigation. If the element that currently owns focus becomes hidden, focus behavior can become confusing or fall back to the document. Interactive components should deliberately move focus to a logical visible control before or immediately after hiding the focused region.
closeButton.addEventListener("click", () => {
dialog.hidden = true;
openButton.focus();
});
Do not depend on CSS alone to restore focus. A disclosure should usually return focus to its trigger only when the interaction requires it, while a modal should return focus to the element that opened it when that element still exists and remains usable.
Pointer and Touch Interaction
A display-none element has no hit-testing box. A visibility-hidden box also does not behave as a visible pointer target. In both cases, users should not be able to click the hidden control. This differs from transparent content created with opacity, which can remain present for hit testing unless interaction is managed separately.
Never make an important control invisible while leaving an unexplained active region above another control. Test with pointer, touch, keyboard, and zoom because overlapping positioned layers can make an apparently unrelated element intercept input.
Comparing opacity: 0
opacity: 0 makes an element fully transparent, but the box remains painted as a composited transparent result, stays in layout, and can remain interactive and focusable. It may also remain exposed to assistive technology. Therefore, opacity is an animation and visual-effect tool, not a complete hiding strategy by itself.
.transparent-only {
opacity: 0;
}
A user can potentially tab to an opacity-zero button and activate it without seeing it. If a component fades out, coordinate opacity with visibility, inertness, or another state that removes interaction at the correct time. The exact combination depends on whether the content should remain accessible during the transition.
Comparing pointer-events: none
pointer-events: none prevents a box from becoming the target of pointer hit testing, but it does not hide the box, remove layout space, remove keyboard focus, or remove accessibility exposure. A keyboard user may still tab to an interactive descendant. The property solves a narrower problem than either display or visibility.
Use it when pointer events should pass through a decorative overlay or when an animation briefly disables pointing. Do not assume it disables an interface for every input method. Native disabled, inert, and complete component state management are more appropriate when all interaction must stop.
The HTML hidden Attribute
The boolean hidden attribute communicates that an element is not currently relevant to the page's presented state. Browsers normally implement its hidden state through rendering rules equivalent to no display. It offers clearer HTML state than repeatedly assigning inline CSS declarations.
<section id="advanced-options" hidden>
<h2>Advanced options</h2>
</section>
advancedOptions.hidden = !advancedOptions.hidden;
CSS can override the browser's default styling for hidden, so broad rules such as [hidden] { display: block; } can accidentally reveal content. Preserve the expected behavior unless implementing a carefully tested specialized pattern. Also distinguish ordinary hidden from hidden="until-found", which supports find-in-page and fragment revelation with different rendering behavior.
aria-hidden Is Not a Visual Hiding Tool
aria-hidden="true" removes an element and its subtree from accessibility exposure, but it does not visually hide them or remove them from layout. It is intended for redundant or decorative content that should remain visible while being ignored by assistive technology.
<span class="icon" aria-hidden="true">*</span>
<span>Required field</span>
Do not put aria-hidden="true" on a focusable control or an ancestor containing focusable descendants. That can create controls keyboard users can reach but screen-reader users cannot identify. A descendant cannot restore accessibility exposure with aria-hidden="false" while an ancestor remains aria-hidden.
The inert Attribute
The HTML inert attribute makes an element's flat-tree descendants noninteractive. Inert content generally cannot receive focus, is ignored for pointer hit testing and text selection, and is not exposed to accessibility APIs. Unlike display none, inert does not inherently hide the content visually or remove its layout.
<main id="page-content" inert>
...temporarily inactive content...
</main>
Inert is useful when a visually obscured region remains in the document, such as background content beneath an active modal or a loading overlay. Pair it with an obvious visual state. Native modal dialogs opened with showModal() make the rest of the document inert through platform behavior, reducing the need for manual background management.
Visually Hidden but Screen-Reader Available
Sometimes text should not be visible but must remain available to assistive technology, such as extra context for an icon-only control. Neither display none nor visibility hidden is suitable because both generally remove accessibility exposure. Use a well-tested visually hidden utility that clips the visual box without removing the semantic content.
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
Prefer visible labels whenever possible. A utility should be shared, documented, and tested across browsers rather than recreated differently in every component.
Showing and Hiding With Component State
A maintainable component keeps one source of truth for open or closed state. JavaScript updates semantic attributes and CSS derives presentation from those attributes. For a disclosure button, aria-expanded communicates the trigger state and aria-controls identifies the controlled region.
<button id="filters-button"
aria-expanded="false"
aria-controls="filters-panel">
Filters
</button>
<div id="filters-panel" hidden>...</div>
filtersButton.addEventListener("click", () => {
const open = filtersButton.getAttribute("aria-expanded") === "true";
filtersButton.setAttribute("aria-expanded", String(!open));
filtersPanel.hidden = open;
});
This pattern avoids conflicting classes, inline styles, and ARIA values. The region is absent from layout and accessibility exposure while closed, then restored consistently when opened.
Menus and Navigation
A closed navigation drawer usually should not reserve space or expose inactive links, so display-none behavior or a native hidden state is often appropriate after any closing animation finishes. An off-canvas drawer that remains translated beyond the viewport needs additional state management because transform alone can leave links focusable.
Visibility can coordinate a fade or slide by making the drawer noninteractive when fully closed while its geometry remains available for animation. Whichever technique is chosen, update the trigger's expanded state, prevent focus from entering a closed menu, and keep the DOM order logical.
Tabs
Only the active tab panel should normally participate in interaction and reading. Inactive panels can use the hidden attribute or display-none behavior so their controls do not appear in the tab sequence. The active tab must have the corresponding selected state, and keyboard behavior should follow the expected tab pattern.
Visibility hidden usually leaves unnecessary panel-sized gaps and is rarely the best default for stacked tab panels. It may make sense in a fixed overlay where every panel must retain exactly the same measured dimensions, but the accessibility state still needs explicit verification.
Accordions and Disclosure Panels
A collapsed accordion should generally release its content space, which points to display-none or hidden behavior. If a smooth height animation is required, developers often animate a wrapper's measured size or use a layout technique designed for expansion, then apply the final hidden state after the animation.
Do not leave collapsed links and form controls focusable merely because the visual panel has zero height or hidden overflow. Visual clipping is not the same as hidden interaction. Test keyboard traversal at the beginning, middle, and end of the transition.
Modals and Dialogs
A closed modal should not remain in the focus order or accessibility tree. A native <dialog> provides platform behavior for opening, closing, focus, and top-layer placement. Calling showModal() also makes content outside the modal inert while it is active.
For a custom dialog, hiding the dialog is only part of the work. The implementation must move focus into the dialog, contain interaction appropriately, label the dialog, handle Escape where expected, obscure and deactivate the background, and restore focus on close. Display and visibility cannot provide that complete contract alone.
Form Validation Messages
An error message that does not apply should usually consume no space, so display-none or hidden behavior is common before validation. When the error appears, associate it with the field and ensure its change is communicated at the appropriate time. Merely setting visibility does not announce a message to screen-reader users.
Some designs reserve a fixed error line beneath every field to prevent layout movement. Visibility hidden can preserve that line, but the empty gap should be intentional and consistent. A minimum block size on the message container may express the design more clearly while the actual message state uses semantic hiding.
Stable Layout and Placeholders
Visibility hidden is useful when a layout slot must remain stable while content is temporarily unavailable. Examples include a board cell, a comparison-table value, or a toolbar action whose location should not change while permission data loads. The invisible region prevents neighboring items from shifting.
However, a blank slot can look like an error. A skeleton, loading label, disabled control, or explicit unavailable state may communicate more effectively. Reserve invisible geometry only when stability is more important than showing status and the empty position has understandable context.
Responsive Interfaces
Responsive CSS sometimes removes an element at a breakpoint with display: none. This is appropriate when the same function is provided through another accessible control or when the content truly does not belong in that presentation. Avoid hiding essential information merely because the screen is narrow.
.desktop-shortcuts {
display: none;
}
@media (min-width: 64rem) {
.desktop-shortcuts {
display: flex;
}
}
Visibility hidden is usually a poor responsive substitute because it leaves unused space. Prefer reflow, wrapping, progressive disclosure, or a single adaptive component over maintaining separate desktop and mobile copies that can drift semantically.
Traditional Fade Transitions
Opacity is interpolable, while visibility changes as a discrete state. A common fade pattern transitions opacity and delays the hidden visibility value until the fade-out completes. When opening, visibility becomes visible immediately and opacity transitions upward.
.popover {
opacity: 0;
visibility: hidden;
transition: opacity 180ms ease, visibility 0s linear 180ms;
}
.popover.is-open {
opacity: 1;
visibility: visible;
transition-delay: 0s;
}
This pattern keeps the closed popover out of normal interaction after the fade. If the element should release layout space, JavaScript can apply hidden or display none after the exit transition and remove it before the entrance begins.
Why display: none Traditionally Does Not Fade
An element with display none has no generated box to paint, so applying opacity at the same moment cannot show an intermediate visual frame. Switching directly between none and block traditionally makes the element appear or disappear without an opacity transition.
The established solution is staged state: generate the box, establish a starting visual style, trigger the opacity transition, and remove the box only after exit completes. This sequence should be represented in component logic so rapid repeated clicks do not leave stale classes or mismatched accessibility states.
Modern Discrete Display Transitions
Modern CSS defines mechanisms such as transition-behavior: allow-discrete and @starting-style that can coordinate transitions involving discrete properties, including display in supporting browsers. They allow the box to remain available for an exit effect and provide a starting style when it first becomes rendered.
.notice {
transition:
opacity 180ms ease,
display 180ms allow-discrete;
}
.notice[hidden] {
display: none;
opacity: 0;
}
@starting-style {
.notice:not([hidden]) {
opacity: 0;
}
}
Treat this as progressive enhancement and verify current browser support for the exact pattern. The base state must still be correct without the transition. Never delay removal from interaction simply to preserve a visual effect.
Reduced Motion
Users who request reduced motion may find fades, slides, and scale effects distracting. The component should remain understandable when transitions are shortened or removed. State changes, focus movement, and accessibility attributes must not depend on animation events that may not fire as originally timed.
@media (prefers-reduced-motion: reduce) {
.popover {
transition-duration: 0.01ms;
}
}
Build the open and closed states first, then add motion as optional feedback. This makes display and visibility decisions robust under user preferences and constrained devices.
DOM State and Form State
Both CSS hiding techniques leave the node in the DOM. Input values, selected options, scroll positions, and component data can persist while hidden. That can be useful for a wizard that temporarily conceals a step, but it can also submit stale form values if controls remain successful form controls.
The HTML standard notes that presentation alone does not necessarily deactivate scripts or form submission. If hidden controls should not submit, disable them or adjust the form model explicitly. Do not assume visual absence equals application inactivity.
Measurement and JavaScript
A display-none element has no rendered box, so geometry APIs commonly report zero-sized layout values. Code that needs its natural dimensions must measure it while rendered, use a safe off-screen measurement strategy, or design the animation around intrinsic layout features.
A visibility-hidden element retains geometry, making it possible to measure dimensions without showing it. This can be useful for positioning a menu before reveal, but it should not justify leaving a large hidden interface in layout when a better measurement strategy exists. Reading layout repeatedly can also force synchronous layout work.
Performance Considerations
Display none can reduce layout and paint work for the hidden subtree because it generates no boxes. Visibility hidden preserves layout participation, so geometry still must be considered. Opacity may keep compositing resources. These facts do not mean that toggling display is always the fastest option; a large reflow can be expensive when many surrounding elements move.
Performance depends on document size, containment, animation, update frequency, and browser optimization. Measure real interactions with performance tools. Choose correct semantics and behavior first, then optimize a demonstrated bottleneck rather than relying on a universal ranking of hiding properties.
Common Mistakes
A retained blank region under visibility: hidden is expected behavior, not a browser defect. Use display-none behavior when surrounding content should close the gap. Likewise, no descendant selector or !important declaration can make a child generate a box while an ancestor remains display: none.
Do not treat opacity: 0 as a complete closed state because transparent controls can remain focusable, clickable, and exposed to assistive technology. Do not hide the element that currently owns focus without moving focus to a logical visible control.
A component also becomes fragile when separate scripts independently toggle classes, hidden, ARIA attributes, and inline visibility. Keep one authoritative state and derive presentation and accessibility from it. At responsive breakpoints, preserve essential content and feature parity instead of assuming narrow screens justify removal.
Testing Strategy
Test the component in every open, closing, closed, opening, loading, error, and disabled state. Navigate with Tab and Shift+Tab, activate controls from the keyboard, inspect focus after closure, and confirm hidden descendants are absent from the accessibility tree when appropriate. Use a screen reader for important workflows.
Resize the container continuously, zoom the page, enlarge text, change writing direction, print the document, and test long translated labels. Inspect Grid and Flex overlays to determine whether hidden items still occupy tracks. Repeatedly toggle state to expose timing problems in transitions.
Automated tests can assert hidden properties and ARIA state, but they should also test observable behavior: whether a closed control is focusable, whether layout gaps are expected, whether focus returns correctly, and whether a restored panel preserves or resets data according to requirements.
Debugging Hidden Elements
Start with the browser's computed styles. Search every matched rule for display, visibility, opacity, content visibility, transform, clipping, overflow, positioning, pointer events, and z-index. An element that seems hidden may actually be off-screen, transparent, clipped, covered, or inside a hidden ancestor.
Walk up the ancestor chain. For display none, identify the nearest ancestor that prevents box generation. For visibility, check inherited values and any descendant override. Inspect the accessibility tree and keyboard order rather than trusting only the Elements panel.
Temporarily add an outline and background after restoring rendering. Check geometry APIs and layout overlays. Disable transition rules to separate final-state bugs from timing bugs. Finally, inspect the component's state attributes to ensure CSS and JavaScript agree about whether it is open.
How to Choose
Use display: none or the HTML hidden state when the content should be absent from layout, interaction, and normal accessibility navigation. Typical examples are closed menus, inactive tab panels, collapsed disclosures, optional form regions, and screen-only controls in print.
Use visibility: hidden when the layout slot must remain but the box should not be painted or interacted with. Typical examples are fixed board positions, stable comparison cells, and a fade pattern that coordinates visibility with opacity.
Use opacity: 0 for transparency effects only after managing interaction and accessibility. Use aria-hidden to remove redundant visible content from accessibility exposure, not to hide it visually. Use inert when a visible or visually obscured subtree must remain rendered but inactive. Use a visually hidden utility when text must remain available to assistive technology.
Common Interview Questions
What is the main difference between display none and visibility hidden? Display none prevents the element and descendants from generating boxes, while visibility hidden keeps the boxes in layout but does not render them visibly.
Which one preserves layout space? Visibility hidden preserves the box's layout contribution. Display none releases the space.
Do both remove content from the DOM? No. Both leave the elements in the DOM; they change rendering and interaction behavior.
Can a child override a hidden parent? A child can restore visibility: visible under a visibility-hidden ancestor, but cannot escape a display-none ancestor.
Are hidden elements announced by screen readers? Content hidden by display none or visibility hidden is generally excluded from accessibility APIs and should not be relied on as screen-reader-only content.
How is opacity zero different? The element remains laid out and can remain focusable, clickable, and exposed to assistive technology even though it is transparent.
What is the purpose of aria-hidden? It removes content from accessibility exposure without visually hiding it. It must not conceal focusable controls from assistive technology.
What does inert do? It makes a subtree noninteractive and generally unavailable to focus and accessibility APIs while leaving its visual layout unchanged unless CSS also changes it.
Can display none be animated? Traditional opacity transitions cannot run while no box exists. Modern discrete-transition features can coordinate display in supporting browsers, or scripts can stage rendered and hidden states.
Which is better for a closed menu? Display-none or hidden behavior is usually appropriate because closed links should not occupy space or receive focus, although animation may require an intermediate visible-box state.
Practical Review Checklist
- Decide whether hidden content should reserve layout space.
- Use display-none behavior when the subtree should leave the box tree.
- Use visibility hidden only when preserving geometry is intentional.
- Remember that a child cannot escape a display-none ancestor.
- Check for descendant visibility overrides under hidden ancestors.
- Do not use either property for screen-reader-only text.
- Keep hidden controls out of keyboard focus and accessibility navigation.
- Move focus logically when closing a menu, disclosure, or dialog.
- Do not treat opacity or pointer-events as complete hidden states.
- Use
aria-hidden,hidden, andinertfor their distinct purposes. - Coordinate animation timing with interaction and semantic state.
- Test responsive, print, zoom, translation, and reduced-motion behavior.
- Measure performance instead of assuming one property is always faster.
- Inspect computed styles, ancestor state, focus order, and the accessibility tree.
Key Takeaway
display: none means that an element and its descendants generate no CSS boxes, so the subtree disappears from layout and normal accessibility exposure. visibility: hidden means that boxes still exist and affect layout but are not visibly rendered, with descendants able to restore visibility. The choice is therefore about interface state and geometry, not simply about making pixels disappear.
Use semantic HTML and one coherent component state, then select the rendering behavior that matches the requirement. Manage focus, interaction, accessibility, motion, and responsive behavior as part of the same design. When those responsibilities are handled together, hidden content behaves predictably for every user instead of becoming an invisible source of layout gaps and unreachable controls.