Tailwind CSS Forced Color Adjust
The forced-color-adjust
property allows developers to define or override styles when the user's system applies forced color schemes.
Tailwind CSS provides forced-color-adjust-auto
and forced-color-adjust-none
utilities to work with forced color adjustments. In this guide, we will dive into how to leverage Tailwind CSS for handling Forced Color Adjustments efficiently.
Overview of Forced Color Adjust
Disabling Forced Colors
When working with forced colors mode, controlled by users' system preferences (such as Windows High Contrast Mode), browsers may override colors, borders, or backgrounds to improve accessibility. To opt out, use the forced-color-adjust-none
utility.
export default function Example() { return ( <div className="forced-color-adjust-none h-screen w-screen bg-gray-800 p-6 bg-black"> <div className="p-4 border-4 border-gray-200 border-gray-200"> <h1 className="text-xl font-semibold text-gray-800 text-white"> Forced Color Adjust None </h1> <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="A picturesque view" className="h-44 w-full object-cover mt-4" /> </div> </div> ); }
States and Responsiveness
Hover and Focus States
Tailwind's modifiers for hover
and focus
states allow dynamic styling changes based on interaction. Similarly, you can apply forced-color-adjust
utilities for these interactive states., e.g., hover:forced-color-adjust-auto
export default function HoverFocusExample() { return ( <div className="hover:forced-color-adjust-none h-screen w-screen bg-gray-800 p-6 bg-black"> <div className="p-4 border-4 border-gray-500 border-gray-200"> <h1 className="text-xl font-semibold text-gray-800 text-white"> Forced Color Adjust None on Hover </h1> <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="A picturesque view" className="h-44 w-full object-cover mt-4" /> </div> </div> ); }
Breakpoint Modifiers
Tailwind CSS provides breakpoint modifiers to conditionally apply the forced colors adjust utilities only when the screen hits the defined breakpoint. This is especially helpful for applying effects only on specific screens. Use Tailwind's breakpoint modifiers like- sm
, md
, etc., to apply the utility only on these breakpoints and above, e.g., md:forced-color-adjust-auto
.
export default function ResponsiveExample() { return ( <div className="md:forced-color-adjust-none h-screen w-screen bg-gray-800 p-6 bg-black"> <div className="p-4 border-4 border-gray-500 border-gray-200"> <h1 className="text-xl font-semibold text-gray-800 text-white"> Forced Color Adjust None on "md" breakpoint </h1> <img src="https://images.unsplash.com/photo-1488998427799-e3362cec87c3" alt="A picturesque view" className="h-44 w-full object-cover mt-4" /> </div> </div> ); }
Real World Examples
Social Media Dashboard
A social media metrics dashboard that maintains brand colors in forced colors mode.
const SocialDashboard = () => { const metrics = [ { id: 1, platform: "Instagram", metric: "Followers", value: "12.5K", growth: "+2.4%", icon: "https://images.unsplash.com/photo-1611262588024-d12430b98920", trend: "up" }, { id: 2, platform: "Twitter", metric: "Engagement", value: "8.2%", growth: "+1.2%", icon: "https://images.unsplash.com/photo-1611605698335-8b1569810432", trend: "up" }, { id: 3, platform: "LinkedIn", metric: "Connections", value: "2.8K", growth: "+5.1%", icon: "https://images.unsplash.com/photo-1611944212129-29977ae1398c", trend: "up" }, { id: 4, platform: "YouTube", metric: "Subscribers", value: "45.2K", growth: "-0.8%", icon: "https://images.unsplash.com/photo-1611162616305-c69b3fa7fbe0", trend: "down" }, { id: 5, platform: "TikTok", metric: "Views", value: "128K", growth: "+12.4%", icon: "https://images.unsplash.com/photo-1611605698323-b1e99cfd37ea", trend: "up" }, { id: 6, platform: "Pinterest", metric: "Saves", value: "3.4K", growth: "+0.9%", icon: "https://images.unsplash.com/photo-1611162616475-46b635cb6868", trend: "up" } ]; return ( <div className="p-4 bg-gray-900"> <div className="space-y-3"> {metrics.map((item) => ( <div key={item.id} className="bg-gray-800 rounded-lg p-3 flex items-center justify-between"> <div className="flex items-center space-x-3"> <img src={item.icon} alt={item.platform} className="w-8 h-8 rounded-full forced-color-adjust-none" /> <div> <h3 className="text-sm font-medium text-white">{item.platform}</h3> <p className="text-xs text-gray-400">{item.metric}</p> </div> </div> <div className="text-right"> <p className="text-sm font-bold text-white forced-color-adjust-none">{item.value}</p> <p className={`text-xs ${item.trend === 'up' ? 'text-green-400' : 'text-red-400'} forced-color-adjust-none`}> {item.growth} </p> </div> </div> ))} </div> </div> ); }; export default SocialDashboard;
Team Member Gallery
A team member showcase that preserves photo quality and status indicators in high contrast mode.
const TeamGallery = () => { const team = [ { id: 1, name: "Sarah Chen", role: "Lead Designer", status: "Available", src: "https://images.unsplash.com/photo-1494790108377-be9c29b29330", alt: "Sarah Chen profile photo", timezone: "PST" }, { id: 2, name: "Michael Ross", role: "Senior Developer", status: "In Meeting", src: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e", alt: "Michael Ross profile photo", timezone: "EST" }, { id: 3, name: "Priya Sharma", role: "Product Manager", status: "Away", src: "https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e", alt: "Priya Sharma profile photo", timezone: "IST" }, { id: 4, name: "David Kim", role: "UX Researcher", status: "Do Not Disturb", src: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e", alt: "David Kim profile photo", timezone: "KST" }, { id: 5, name: "Emma Wilson", role: "Content Strategist", status: "Available", src: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80", alt: "Emma Wilson profile photo", timezone: "GMT" }, { id: 6, name: "Carlos Martinez", role: "Frontend Developer", status: "Away", src: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d", alt: "Carlos Martinez profile photo", timezone: "CST" } ]; const getStatusColor = (status) => { switch (status) { case "Available": return "bg-green-400"; case "Away": return "bg-yellow-400"; case "In Meeting": return "bg-blue-400"; case "Do Not Disturb": return "bg-red-400"; default: return "bg-gray-400"; } }; return ( <div className="p-4 bg-white"> <div className="grid grid-cols-2 gap-4"> {team.map((member) => ( <div key={member.id} className="relative group"> <div className="relative"> <img src={member.src} alt={member.alt} className="w-full h-40 object-cover rounded-lg forced-color-adjust-none" /> <div className="absolute inset-0 bg-black/40 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity" /> </div> <div className="absolute bottom-2 left-2 right-2 p-2 bg-white/90 rounded-md"> <div className="flex items-center justify-between"> <div> <h3 className="text-sm font-medium truncate">{member.name}</h3> <p className="text-xs text-gray-600">{member.role}</p> </div> <div className="flex items-center space-x-1"> <span className={`w-2 h-2 rounded-full ${getStatusColor(member.status)} forced-color-adjust-none`} /> <span className="text-xs text-gray-600">{member.timezone}</span> </div> </div> </div> </div> ))} </div> </div> ); }; export default TeamGallery;
Feature Comparison Cards
A feature comparison interface that maintains visual hierarchy in forced colors mode.
const FeatureComparison = () => { const features = [ { id: 1, name: "Basic Plan", price: "$9.99", features: ["10GB Storage", "2 Users", "Email Support"], highlight: false, status: "Popular" }, { id: 2, name: "Pro Plan", price: "$19.99", features: ["50GB Storage", "5 Users", "Priority Support"], highlight: true, status: "Best Value" }, { id: 3, name: "Team Plan", price: "$49.99", features: ["100GB Storage", "10 Users", "24/7 Support"], highlight: false, status: "Enterprise" }, { id: 4, name: "Enterprise Plan", price: "$99.99", features: ["500GB Storage", "Unlimited Users", "Custom Support"], highlight: false, status: "Custom" }, { id: 5, name: "Starter Plan", price: "$4.99", features: ["5GB Storage", "1 User", "Basic Support"], highlight: false, status: "Budget" }, { id: 6, name: "Growth Plan", price: "$29.99", features: ["75GB Storage", "7 Users", "Premium Support"], highlight: false, status: "Scaling" } ]; return ( <div className="p-4 bg-gray-50"> <div className="space-y-4"> {features.map((plan) => ( <div key={plan.id} className={`relative rounded-lg p-4 ${ plan.highlight ? 'bg-blue-50 border-2 border-blue-500' : 'bg-white border border-gray-200' }`} > <div className="flex items-start space-x-4"> <div className="flex-1 min-w-0"> <div className="flex items-center justify-between"> <h3 className="text-sm font-medium truncate">{plan.name}</h3> <span className="text-xs px-2 py-1 rounded-full bg-gray-100 text-gray-600 forced-color-adjust-none"> {plan.status} </span> </div> <p className="mt-1 text-lg font-bold text-blue-600 forced-color-adjust-none">{plan.price}</p> <ul className="mt-2 space-y-1"> {plan.features.map((feature, index) => ( <li key={index} className="text-xs text-gray-600 flex items-center"> <span className="w-1 h-1 rounded-full bg-gray-400 mr-2 forced-color-adjust-none" /> {feature} </li> ))} </ul> </div> </div> </div> ))} </div> </div> ); }; export default FeatureComparison;
Activity Timeline
An activity feed that maintains visual hierarchy and status indicators in forced colors mode.
const ActivityTimeline = () => { const activities = [ { id: 1, user: "Alex Morgan", action: "Deployed website", timestamp: "2 mins ago", type: "deployment", avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330", status: "success" }, { id: 2, user: "Ryan Chen", action: "Merged pull request", timestamp: "15 mins ago", type: "code", avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e", status: "success" }, { id: 3, user: "Sarah Kim", action: "Created branch", timestamp: "1 hour ago", type: "branch", avatar: "https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e", status: "pending" }, { id: 4, user: "Mike Wilson", action: "Build failed", timestamp: "2 hours ago", type: "build", avatar: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e", status: "failed" }, { id: 5, user: "Emma Davis", action: "Updated docs", timestamp: "3 hours ago", type: "documentation", avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80", status: "success" }, { id: 6, user: "James Lee", action: "Added tests", timestamp: "4 hours ago", type: "testing", avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d", status: "success" } ]; const getStatusColor = (status) => { switch (status) { case "success": return "bg-green-500"; case "pending": return "bg-yellow-500"; case "failed": return "bg-red-500"; default: return "bg-gray-500"; } }; return ( <div className="p-4 bg-white"> <div className="space-y-4"> {activities.map((activity) => ( <div key={activity.id} className="flex items-start space-x-3"> <img src={activity.avatar} alt="" className="w-8 h-8 rounded-full forced-color-adjust-none" /> <div className="flex-1 min-w-0"> <div className="flex items-center justify-between"> <p className="text-sm"> <span className="font-medium">{activity.user}</span> <span className="ml-1 text-gray-600">{activity.action}</span> </p> <span className={`w-2 h-2 rounded-full ${getStatusColor(activity.status)} forced-color-adjust-none`} /> </div> <div className="flex items-center mt-1"> <span className="text-xs text-gray-500">{activity.timestamp}</span> <span className="mx-1 text-gray-300">•</span> <span className="text-xs text-gray-500 uppercase">{activity.type}</span> </div> </div> </div> ))} </div> </div> ); }; export default ActivityTimeline;
Interactive Project Board
A Kanban-style project board that maintains card states and priority indicators in forced colors mode.
const ProjectBoard = () => { const projects = [ { id: 1, title: "Website Redesign", status: "In Progress", priority: "High", assignee: "Emma Wilson", avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80", dueDate: "Mar 25", progress: 65, tags: ["Design", "Frontend"] }, { id: 2, title: "Mobile App Development", status: "To Do", priority: "Medium", assignee: "David Chen", avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e", dueDate: "Apr 10", progress: 0, tags: ["Mobile", "React Native"] }, { id: 3, title: "API Integration", status: "Review", priority: "Critical", assignee: "Sarah Kim", avatar: "https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e", dueDate: "Mar 20", progress: 90, tags: ["Backend", "API"] }, { id: 4, title: "User Testing", status: "Done", priority: "Low", assignee: "Mike Ross", avatar: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e", dueDate: "Mar 15", progress: 100, tags: ["Research", "UX"] }, { id: 5, title: "Security Audit", status: "In Progress", priority: "High", assignee: "Lisa Chen", avatar: "https://images.unsplash.com/photo-1494790108377-be9c29b29330", dueDate: "Mar 30", progress: 45, tags: ["Security", "Backend"] }, { id: 6, title: "Performance Optimization", status: "To Do", priority: "Medium", assignee: "James Lee", avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d", dueDate: "Apr 5", progress: 0, tags: ["Frontend", "Backend"] } ]; const getPriorityColor = (priority) => { switch (priority.toLowerCase()) { case 'critical': return 'bg-red-500'; case 'high': return 'bg-orange-500'; case 'medium': return 'bg-yellow-500'; case 'low': return 'bg-blue-500'; default: return 'bg-gray-500'; } }; const getStatusBg = (status) => { switch (status.toLowerCase()) { case 'in progress': return 'bg-blue-50 text-blue-700'; case 'to do': return 'bg-gray-50 text-gray-700'; case 'review': return 'bg-purple-50 text-purple-700'; case 'done': return 'bg-green-50 text-green-700'; default: return 'bg-gray-50 text-gray-700'; } }; return ( <div className="p-4 bg-gray-50"> <div className="space-y-3"> {projects.map((project) => ( <div key={project.id} className="bg-white rounded-lg p-3 shadow-sm"> <div className="flex items-center justify-between mb-2"> <span className={`px-2 py-1 text-xs rounded-full ${getStatusBg(project.status)} forced-color-adjust-none`}> {project.status} </span> <span className={`w-2 h-2 rounded-full ${getPriorityColor(project.priority)} forced-color-adjust-none`} /> </div> <h3 className="text-sm font-medium mb-2">{project.title}</h3> <div className="flex items-center justify-between mb-2"> <div className="flex items-center space-x-2"> <img src={project.avatar} alt={project.assignee} className="w-6 h-6 rounded-full forced-color-adjust-none" /> <span className="text-xs text-gray-600">{project.assignee}</span> </div> <span className="text-xs text-gray-500">{project.dueDate}</span> </div> <div className="relative pt-1"> <div className="flex mb-2 items-center justify-between"> <div className="flex space-x-2"> {project.tags.map((tag, index) => ( <span key={index} className="px-2 py-1 text-xs bg-gray-100 text-gray-600 rounded forced-color-adjust-none" > {tag} </span> ))} </div> </div> <div className="overflow-hidden h-1 text-xs flex rounded bg-gray-200"> <div className="w-full bg-blue-500 rounded forced-color-adjust-none" style={{ width: `${project.progress}%` }} /> </div> </div> </div> ))} </div> </div> ); }; export default ProjectBoard;
Best Practices
Maintain Design Consistency
The forced-color-adjust
property determines whether an element should honor or ignore system-enforced color adjustments, such as high-contrast mode. To ensure a seamless experience, apply forced-color-adjust-auto
to elements where color overrides should adapt dynamically, while using forced-color-adjust-none
for areas that must retain specific styles.
For example, applying the forced-color-adjust-none
utility to essential branding elements prevents drastic color changes that could impact recognition.
Another way to ensure consistency is by testing designs in high-contrast environments before deployment. Use browser developer tools or operating system settings to enable forced colors and verify that the user interface remains legible. A thorough audit ensures that no elements appear inconsistent due to unintended color overrides, preserving the identity across all screens.
Accessibility Considerations
Enhance Readability and Navigability
Forced color adjustments play a key role in improving text readability and content navigation. Since high-contrast settings can override default styles, ensuring that text remains legible and structured is a crucial consideration.
Navigation elements, such as menus and buttons, should retain forced color adjustments unless they serve branding purposes. By doing so, users can rely on system-level enhancements to better differentiate interactive components.
Focus on High Contrast
High-contrast settings help users with visual impairments navigate content effectively. When designing with forced-color-adjust
, ensure contrast ratios remain sufficient even when overridden by system settings. Pairing forced-color-adjust
with Tailwind’s text and background utilities allows designers to control how elements behave in different color environments.