Tailwind CSS Visibility
Visibility determines whether an element is visible or hidden in the DOM. This property does not remove the element from the document flow. Instead, it merely toggles its visibility while preserving its space.
In this guide, we'll explore how to use the visibility utilities in Tailwind CSS, delve into conditional states like hover and focus, and show how to adapt visibility to different screen sizes using media queries.
Class | Properties | Example |
---|---|---|
visible | visibility: visible; | <div className="visible"></div> |
invisible | visibility: hidden; | <div className="invisible"></div> |
collapse | visibility: collapse; | <div className="collapse"></div> |
Overview of Visibility
Invisible Elements
You can make an element visually hidden but still preserve its space in the DOM with the invisible
utility.
In the below example, applying invisible
keeps the image's dimensions intact but hides it visually.
export default function InvisibleElement() { return ( <div className="w-screen h-screen flex justify-center items-center bg-gray-100"> {/* The following element is hidden */} <img src="https://images.unsplash.com/photo-1677086813101-496781a0f327" alt="Hidden Landscape" className="invisible w-60 h-60 object-cover" /> {/* invisibility doesn't destroy layout */} <p className="text-gray-700">This text stays aligned to the image space.</p> </div> ); }
Collapsible Elements
When you want to hide table, table rows, row groups, columns, and column groups and remove their space, use the collapse
utility. This works similar to display: none
in tables:
In the below example, remove the collapse
utility from the table to make it visible again:
export default function CollapsingTableBorders() { return ( <div className="w-screen h-screen p-10 bg-gray-200"> <table className="collapse"> <thead> <tr> <th className="border border-gray-500 px-4 py-2">Name</th> <th className="border border-gray-500 px-4 py-2">Age</th> <th className="border border-gray-500 px-4 py-2">Location</th> </tr> </thead> <tbody> <tr> <td className="border border-gray-500 px-4 py-2">Alice</td> <td className="border border-gray-500 px-4 py-2">30</td> <td className="border border-gray-500 px-4 py-2">New York</td> </tr> <tr> <td className="border border-gray-500 px-4 py-2">Bob</td> <td className="border border-gray-500 px-4 py-2">25</td> <td className="border border-gray-500 px-4 py-2">San Francisco</td> </tr> </tbody> </table> <p className="text-gray-800 mt-4"> This table uses the <code>collapse</code>. </p> </div> ); }
Visible Elements
Using the visible
utility, you can reset a hidden element to make it visible again in Tailwind CSS. By explicitly using visible
, you're ensuring that the element is displayed.
export default function ForceVisible() { return ( <div className="w-screen h-screen bg-white flex justify-center items-center"> {/* Visible Image */} <img src="https://images.unsplash.com/photo-1677086813101-496781a0f327" alt="Visible Landscape" className="visible w-64 h-64 object-cover" /> </div> ); }
States and Responsiveness
Tailwind's utilities shine when you need to apply visibility dynamically. Tailwind CSS allows combining states and responsive breakpoints for added clarity.
Hover and Focus States
Implement dynamic visibility changes based on states like hover, focus, or active:
export default function StateBasedVisibility() { return ( <div className="w-screen h-screen flex justify-center items-center bg-gray-50"> <button className="visible hover:invisible relative w-80 h-16 bg-blue-500 text-white"> Hover to make the button invisible </button> </div> ); }
Breakpoint Modifiers
Tailwind enables you to control visibility responsively with breakpoints. For instance, hide an element on small screens but show it on larger ones.
Here, the sm:visible
makes the image visible only for screens larger than the sm
breakpoint.
export default function ResponsiveVisibility() { return ( <div className="w-screen h-screen bg-gray-100"> {/* Image Hidden on Small Screens */} <img src="https://images.unsplash.com/photo-1677086813101-496781a0f327" alt="Responsive Landscape" className="invisible lg:visible w-60 h-60 object-cover" /> <p className="text-center text-gray-700 mt-6"> This image becomes visible on larger screens. </p> </div> ); }
Real World Examples
Toggle Product Availability Status
This component displays a list of products with their availability status that can be toggled between visible and invisible states.
export default function ProductAvailability() { const products = [ { id: 1, name: "Wireless Headphones", price: "$199", status: "In Stock", image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e" }, { id: 2, name: "Smart Watch", price: "$299", status: "Out of Stock", image: "https://images.unsplash.com/photo-1523275335684-37898b6baf30" }, { id: 3, name: "Laptop Pro", price: "$1299", status: "In Stock", image: "https://images.unsplash.com/photo-1541807084-5c52b6b3adef" }, { id: 4, name: "Gaming Console", price: "$499", status: "Limited", image: "https://images.unsplash.com/photo-1486401899868-0e435ed85128" }, { id: 5, name: "Tablet XL", price: "$799", status: "In Stock", image: "https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0" }, { id: 6, name: "Camera DSLR", price: "$899", status: "Out of Stock", image: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32" } ]; return ( <div className="grid gap-4 p-6"> {products.map((product) => ( <div key={product.id} className="border p-4 rounded-lg"> <img src={product.image} alt={product.name} className="w-full h-48 object-cover" /> <h3 className="text-lg font-bold mt-2">{product.name}</h3> <p className="text-gray-600">{product.price}</p> <span className={`${product.status === "Out of Stock" ? "invisible" : "visible"} inline-block px-2 py-1 mt-2 rounded bg-green-100 text-green-800`}> {product.status} </span> </div> ))} </div> ); }
Conditional Navigation Menu
This component shows a responsive navigation menu that toggles visibility based on screen size and user interaction.
export default function NavigationMenu() { const menuItems = [ { id: 1, name: "Dashboard", icon: "https://images.unsplash.com/photo-1517694712202-14dd9538aa97" }, { id: 2, name: "Products", icon: "https://images.unsplash.com/photo-1542291026-7eec264c27ff" }, { id: 3, name: "Analytics", icon: "https://images.unsplash.com/photo-1551288049-bebda4e38f71" }, { id: 4, name: "Settings", icon: "https://images.unsplash.com/photo-1507925921958-8a62f3d1a50d" }, { id: 5, name: "Support", icon: "https://images.unsplash.com/photo-1486312338219-ce68d2c6f44d" }, { id: 6, name: "Profile", icon: "https://images.unsplash.com/photo-1519085360753-af0119f7cbe7" } ]; return ( <nav className="bg-gray-800 text-white p-4"> <div className="flex justify-between items-center"> <h1 className="text-xl font-bold">Dashboard</h1> <button className="md:invisible visible">Menu</button> </div> <ul className="invisible md:visible flex flex-col md:flex-row gap-4 mt-4"> {menuItems.map((item) => ( <li key={item.id} className="flex items-center gap-2 cursor-pointer hover:bg-gray-700 p-2 rounded"> <img src={item.icon} alt={item.name} className="w-6 h-6 rounded" /> <span>{item.name}</span> </li> ))} </ul> </nav> ); }
Notification System
This component displays a notification system with visibility controls for different types of alerts.
export default function NotificationSystem() { const notifications = [ { id: 1, type: "success", message: "Order successfully processed", time: "Just now" }, { id: 2, type: "error", message: "Payment failed", time: "5 min ago" }, { id: 3, type: "warning", message: "Low stock alert", time: "10 min ago" }, { id: 4, type: "info", message: "New feature available", time: "1 hour ago" }, { id: 5, type: "success", message: "Backup completed", time: "2 hours ago" }, { id: 6, type: "warning", message: "System maintenance scheduled", time: "3 hours ago" } ]; return ( <div className="fixed top-4 right-4 w-80"> {notifications.map((notification) => ( <div key={notification.id} className={`${notification.type === "error" ? "visible" : "invisible"} mb-2 p-4 rounded-lg shadow-lg ${notification.type === "success" ? "bg-green-100 text-green-800" : notification.type === "error" ? "bg-red-100 text-red-800" : notification.type === "warning" ? "bg-yellow-100 text-yellow-800" : "bg-blue-100 text-blue-800"}`} > <div className="flex justify-between items-center"> <p>{notification.message}</p> <span className="text-sm opacity-75">{notification.time}</span> </div> </div> ))} </div> ); }
Form Validation Feedback
This component shows form validation feedback with visibility controls for error messages.
export default function FormValidation() { const formFields = [ { id: 1, name: "username", label: "Username", type: "text", error: "Username is required" }, { id: 2, name: "email", label: "Email", type: "email", error: "Invalid email format" }, { id: 3, name: "password", label: "Password", type: "password", error: "Password must be 8 characters" }, { id: 4, name: "phone", label: "Phone", type: "tel", error: "Invalid phone number" }, { id: 5, name: "address", label: "Address", type: "text", error: "Address is required" }, { id: 6, name: "city", label: "City", type: "text", error: "City is required" } ]; return ( <form className="max-w-md mx-auto p-6 space-y-4"> {formFields.map((field) => ( <div key={field.id} className="space-y-1"> <label className="block text-sm font-medium text-gray-700"> {field.label} </label> <input type={field.type} name={field.name} className="w-full border rounded-lg p-2" /> <p className={`${field.name === "email" ? "visible" : "invisible"} text-sm text-red-600`}> {field.error} </p> </div> ))} <button className="w-full bg-blue-600 text-white py-2 rounded-lg"> Submit </button> </form> ); }
Loading State Manager
This component manages visibility of content during loading states.
export default function LoadingStateManager() { const contentItems = [ { id: 1, title: "Latest Blog Post", content: "Understanding React Hooks", loading: true }, { id: 2, title: "Featured Product", content: "Wireless Earbuds", loading: false }, { id: 3, title: "User Statistics", content: "Monthly Active Users: 10k", loading: true }, { id: 4, title: "System Status", content: "All Systems Operational", loading: false }, { id: 5, title: "Recent Orders", content: "15 New Orders Today", loading: true }, { id: 6, title: "Support Tickets", content: "5 Open Tickets", loading: false } ]; return ( <div className="grid grid-cols-2 gap-4 p-6"> {contentItems.map((item) => ( <div key={item.id} className="border rounded-lg p-4"> <h3 className="text-lg font-bold">{item.title}</h3> <div className={`${item.loading ? "invisible" : "visible"}`}> <p className="text-gray-600">{item.content}</p> </div> <div className={`${item.loading ? "visible" : "invisible"} animate-pulse bg-gray-200 h-4 w-3/4 rounded mt-2`}> </div> </div> ))} </div> ); }
Best Practices
Maintain Design Consistency
When utilizing Tailwind CSS’ visibility utilities, it is essential to maintain a consistent design across the project. Consistent visual styles ensure a better user experience, irrespective of device or screen size.
Consider a card layout in which certain sections appear interactively. By ensuring consistent transitions and states, such as hiding descriptions on smaller screens and revealing them on hover or breakpoint-specific conditions, you can create dynamic design interfaces.
Optimize for Reusability
A reusable design system is a cornerstone of efficient development, and Tailwind CSS utilities, including visibility classes, play a vital role in this process.
Grouping frequently used combinations of visibility states and other utilities into reusable components minimizes redundancy throughout the project. This ensures that the same reusable component can cater to multiple contexts, significantly reducing development time while improving scalability.
Accessibility Considerations
Enhance Readability and Navigability
Visibility utilities directly impact the readability and navigability of your content. By using visible
and invisible
effectively, you can guide users’ attention to key areas without overwhelming them with unnecessary information. For instance, a step-by-step tutorial might reveal content progressively, using visibility classes to hide future steps until users complete the current one.
When toggling visibility, use animations or transitions to provide visual feedback. Smooth transitions between visible
and invisible
states help users understand the flow of content and maintain focus.
Support Accessible Interactive Elements
Interactive components such as modals, tooltips, dropdowns, and menus often rely on visibility utilities to manage their display states. Tailwind's visible
and invisible
classes, when combined with focus
or hover
states, allow developers to create accessible, dynamic interactions.
For example, a dropdown menu might default to invisible but become visible when the user hovers over or focuses on its parent element. This ensures the dropdown is accessible to both mouse users and keyboard users relying on the tab
key for navigation.
Debugging Common Issues
Resolve Common Problems
Common issues with visibility utilities arise from unintended conflicts with other layout or spacing properties.
For instance, combining invisible
with hidden
in a component may lead to confusion, as both attempt to hide the same element differently. Review your class structure to eliminate redundant or conflicting utilities to address this.
Handle Nested Element Challenges
Handling visibility within nested structures, such as deeply nested navigation components or modals, often leads to cascading unintended behaviors.
For instance, parent containers marked invisible
misapply their state to nested child elements unintentionally. To solve this, set visibility states directly on targeted elements or use utility combinations selectively in nested structures.