Menu

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.

This is a live editor. Play around with it!
  
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

This is a live editor. Play around with it!
  
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.

This is a live editor. Play around with it!
  
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.

This is a live editor. Play around with it!
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;

A team member showcase that preserves photo quality and status indicators in high contrast mode.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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.

This is a live editor. Play around with it!
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.