Menu

Tailwind CSS Ring Offset Width

When designing user interfaces, managing focus indicators and interactive borders is a critical part of creating an accessible and polished design. Ring Offset Width in Tailwind CSS defines the space between a visual ring (used as a focus indicator) and the element it surrounds. This feature can be particularly important to maintain visual balance or offset the ring from surrounding elements.

In this guide, we will learn how to use ring offset width utilities in Tailwind CSS:

ClassPropertiesExample
ring-offset-0--tw-ring-offset-width: 0px; box-shadow: 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color), var(--tw-ring-shadow);<div className="ring-offset-0"></div>
ring-offset-1--tw-ring-offset-width: 1px; box-shadow: 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color), var(--tw-ring-shadow);<div className="ring-offset-1"></div>
ring-offset-2--tw-ring-offset-width: 2px; box-shadow: 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color), var(--tw-ring-shadow);<div className="ring-offset-2"></div>
ring-offset-4--tw-ring-offset-width: 4px; box-shadow: 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color), var(--tw-ring-shadow);<div className="ring-offset-4"></div>
ring-offset-8--tw-ring-offset-width: 8px; box-shadow: 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color), var(--tw-ring-shadow);<div className="ring-offset-8"></div>

Overview of Ring Offset Width

Adding the Ring Offset Width

The ring-offset-* utilities, e.g., ring-offset-2, ring-offset-4, etc. applies an offset to focus rings, ensuring adequate spacing between the focus element and its surrounding area.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="h-screen w-screen flex items-center justify-center">
      <button className="ring-2 ring-offset-4 ring-blue-500 px-4 text-black rounded">
        Click Me
      </button>
    </div>
  );
}

Modifying the Offset Color

Because CSS doesn’t natively support shifting a box shadow, Tailwind simulates the effect by applying a solid‐color shadow that matches with the background. By default, it uses white, but if you’re working with a different background, use ring offset color utilities, such as ring-offset-orange-50, ring-offset-yellow-50, etc. to match that color.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="h-screen w-screen flex items-center justify-center bg-gray-200">
      <button className="ring-2 ring-offset-4 ring-offset-gray-200 ring-blue-500 px-4 text-black rounded">
        Click Me
      </button>
    </div>
  );
}

States and Responsiveness

Leveraging ring offset utilities with state-based and responsive modifiers enhances flexibility in complex layouts. Tailwind simplifies conditional styling, whether for hover states, focus conditions, or breakpoints.

Hover and Focus States

You can dynamically adjust ring-offset-width during hover or focus states. Below is an application where the offset width increases when interacted with:

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="h-screen w-screen flex items-center justify-center">
      <button className="ring-2 ring-offset-2 ring-indigo-500 hover:ring-offset-8 p-4 bg-yellow-200 rounded-lg transition-all">
        Hover or Focus Me
      </button>
    </div>
  );
}

Breakpoint Modifiers

Applying ring-offset-width conditionally at breakpoints allows for device-optimized styling. Below, we modify the ring offset based on screen size:

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="h-screen w-screen bg-gray-100 flex items-center justify-center">
      <button className="ring-2 ring-offset-2 md:ring-offset-4 lg:ring-offset-8 ring-purple-400 text-white bg-gray-800 p-5 rounded">
        Resize Your Screen
      </button>
    </div>
  );
}

Custom Ring Offset Width

In scenarios requiring precision, Tailwind allows customization of the ring-offset-width property through theme extensions and arbitrary values. These features give you full control over your design system.

Extending the Theme

Add custom values for ring-offset-width by extending the theme in your Tailwind tailwind.config.js file. Once configured, use the new values directly in your code.

This is a live editor. Play around with it!
import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function App() {
  return (
    <div className="h-screen w-screen flex items-center justify-center bg-gray-200">
      <button className="ring-4 ring-offset-6 ring-orange-500 bg-white text-black p-4 rounded-lg">
        Custom Offset Width
      </button>
    </div>
  );
}

Using Arbitrary Values

Tailwind also supports arbitrary values using square bracket notation for one-off configurations.

This is a live editor. Play around with it!
export default function App() {
  return (
    <div className="h-screen w-screen flex items-center justify-center bg-gray-100">
      <button className="ring-4 ring-[10px] ring-offset-[5px] ring-pink-600 bg-blue-100 text-black p-6 rounded-lg">
        Arbitrary Customization
      </button>
    </div>
  );
}

Real World Examples

Product Card With Focus States

This example shows a product card grid with enhanced focus states using ring offset width. When a card is focused or hovered, it displays a distinctive ring with offset.

This is a live editor. Play around with it!
export default function ProductGrid() {
  const products = [
    {
      id: 1,
      name: "Premium Leather Wallet",
      price: "$79.99",
      src: "https://images.unsplash.com/photo-1627123424574-724758594e93",
      alt: "Brown leather wallet"
    },
    {
      id: 2,
      name: "Minimalist Watch",
      price: "$149.99",
      src: "https://images.unsplash.com/photo-1524592094714-0f0654e20314",
      alt: "Silver analog watch"
    },
    {
      id: 3,
      name: "Wireless Earbuds",
      price: "$199.99",
      src: "https://images.unsplash.com/photo-1590658268037-6bf12165a8df",
      alt: "White wireless earbuds"
    },
    {
      id: 4,
      name: "Laptop Backpack",
      price: "$89.99",
      src: "https://images.unsplash.com/photo-1553062407-98eeb64c6a62",
      alt: "Gray laptop backpack"
    },
    {
      id: 5,
      name: "Smart Water Bottle",
      price: "$45.99",
      src: "https://images.unsplash.com/photo-1602143407151-7111542de6e8",
      alt: "Steel smart water bottle"
    },
  ];

  return (
    <div className="grid grid-cols-1 gap-6 p-8">
      {products.map((product) => (
        <div
          key={product.id}
          className="bg-white rounded-lg shadow-lg p-4 transition-all hover:ring-2 hover:ring-blue-500 hover:ring-offset-4 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-4"
        >
          <img
            src={product.src}
            alt={product.alt}
            className="w-full h-48 object-cover rounded-md"
          />
          <h3 className="text-lg font-semibold mt-4">{product.name}</h3>
          <p className="text-gray-600">{product.price}</p>
        </div>
      ))}
    </div>
  );
}

Interactive Team Member Cards

This example demonstrates team member cards with ring offset width effects on interaction.

This is a live editor. Play around with it!
export default function TeamGrid() {
  const team = [
    {
      id: 1,
      name: "Sarah Johnson",
      role: "CEO",
      src: "https://images.unsplash.com/photo-1494790108377-be9c29b29330",
      alt: "Sarah Johnson profile picture"
    },
    {
      id: 2,
      name: "Michael Chen",
      role: "CTO",
      src: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e",
      alt: "Michael Chen profile picture"
    },
    {
      id: 3,
      name: "Emily Williams",
      role: "Design Lead",
      src: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80",
      alt: "Emily Williams profile picture"
    },
    {
      id: 4,
      name: "David Kim",
      role: "Developer",
      src: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e",
      alt: "David Kim profile picture"
    },
    {
      id: 5,
      name: "Lisa Martinez",
      role: "Marketing Head",
      src: "https://images.unsplash.com/photo-1487412720507-e7ab37603c6f",
      alt: "Lisa Martinez profile picture"
    },
    {
      id: 6,
      name: "James Wilson",
      role: "Product Manager",
      src: "https://images.unsplash.com/photo-1519345182560-3f2917c472ef",
      alt: "James Wilson profile picture"
    }
  ];

  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-8 p-10">
      {team.map((member) => (
        <div
          key={member.id}
          className="text-center group cursor-pointer"
        >
          <div className="relative inline-block">
            <img
              src={member.src}
              alt={member.alt}
              className="w-40 h-40 rounded-full object-cover transition-all group-hover:ring-4 group-hover:ring-purple-500 group-hover:ring-offset-8"
            />
          </div>
          <h3 className="text-xl font-bold mt-4">{member.name}</h3>
          <p className="text-gray-600">{member.role}</p>
        </div>
      ))}
    </div>
  );
}

Feature Card Showcase

This example shows feature cards with distinctive ring offset width animations.

This is a live editor. Play around with it!
export default function FeatureCards() {
  const features = [
    {
      id: 1,
      title: "Cloud Storage",
      description: "Secure and scalable storage solutions",
      icon: "https://images.unsplash.com/photo-1590859808308-3d2d9c515b1a",
      alt: "Cloud storage icon"
    },
    {
      id: 2,
      title: "Analytics",
      description: "Advanced data analysis tools",
      icon: "https://images.unsplash.com/photo-1551288049-bebda4e38f71",
      alt: "Analytics icon"
    },
    {
      id: 3,
      title: "Security",
      description: "Enterprise-grade security features",
      icon: "https://images.unsplash.com/photo-1555949963-aa79dcee981c",
      alt: "Security icon"
    },
    {
      id: 4,
      title: "Integration",
      description: "Seamless third-party integrations",
      icon: "https://images.unsplash.com/photo-1551434678-e076c223a692",
      alt: "Integration icon"
    },
    {
      id: 5,
      title: "Automation",
      description: "Workflow automation tools",
      icon: "https://images.unsplash.com/photo-1518432031352-d6fc5c10da5a",
      alt: "Automation icon"
    },
    {
      id: 6,
      title: "Support",
      description: "24/7 customer support",
      icon: "https://images.unsplash.com/photo-1549923746-c502d488b3ea",
      alt: "Support icon"
    }
  ];

  return (
    <div className="grid grid-cols-2 lg:grid-cols-3 gap-6 p-8">
      {features.map((feature) => (
        <div
          key={feature.id}
          className="bg-white p-6 rounded-xl transition-all hover:ring-2 hover:ring-teal-500 hover:ring-offset-6 cursor-pointer"
        >
          <img
            src={feature.icon}
            alt={feature.alt}
            className="w-16 h-16 object-cover rounded-lg mb-4"
          />
          <h3 className="text-xl font-bold mb-2">{feature.title}</h3>
          <p className="text-gray-600">{feature.description}</p>
        </div>
      ))}
    </div>
  );
}

Social Media Profile Cards

This example displays social media profile cards with interactive ring offset width effects.

This is a live editor. Play around with it!
export default function SocialProfiles() {
  const profiles = [
    {
      id: 1,
      username: "@photography_pro",
      followers: "125K",
      category: "Photography",
      src: "https://images.unsplash.com/photo-1542038784456-1ea8e935640e",
      alt: "Photography profile"
    },
    {
      id: 2,
      username: "@foodie_adventures",
      followers: "89K",
      category: "Food",
      src: "https://images.unsplash.com/photo-1567620905732-2d1ec7ab7445",
      alt: "Food profile"
    },
    {
      id: 3,
      username: "@travel_diary",
      followers: "256K",
      category: "Travel",
      src: "https://images.unsplash.com/photo-1488646953014-85cb44e25828",
      alt: "Travel profile"
    },
    {
      id: 4,
      username: "@fitness_guru",
      followers: "198K",
      category: "Fitness",
      src: "https://images.unsplash.com/photo-1517836357463-d25dfeac3438",
      alt: "Fitness profile"
    },
    {
      id: 5,
      username: "@tech_reviews",
      followers: "145K",
      category: "Technology",
      src: "https://images.unsplash.com/photo-1498049794561-7780e7231661",
      alt: "Tech profile"
    },
    {
      id: 6,
      username: "@art_gallery",
      followers: "167K",
      category: "Art",
      src: "https://images.unsplash.com/photo-1513364776144-60967b0f800f",
      alt: "Art profile"
    }
  ];

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-10">
      {profiles.map((profile) => (
        <div
          key={profile.id}
          className="bg-gradient-to-br from-pink-500 to-purple-600 p-1 rounded-xl group cursor-pointer"
        >
          <div className="bg-white p-6 rounded-lg transition-all group-hover:ring-4 group-hover:ring-pink-500 group-hover:ring-offset-2">
            <img
              src={profile.src}
              alt={profile.alt}
              className="w-full h-48 object-cover rounded-lg mb-4"
            />
            <h3 className="text-lg font-bold">{profile.username}</h3>
            <p className="text-gray-600">{profile.category}</p>
            <p className="text-sm text-gray-500">{profile.followers} followers</p>
          </div>
        </div>
      ))}
    </div>
  );
}

Project Portfolio Cards

This example showcases project portfolio cards with ring offset width highlighting.

This is a live editor. Play around with it!
export default function PortfolioGrid() {
  const projects = [
    {
      id: 1,
      title: "E-commerce Platform",
      category: "Web Development",
      src: "https://images.unsplash.com/photo-1523474253046-8cd2748b5fd2",
      alt: "E-commerce project thumbnail"
    },
    {
      id: 2,
      title: "Mobile Banking App",
      category: "App Development",
      src: "https://images.unsplash.com/photo-1563986768609-322da13575f3",
      alt: "Banking app thumbnail"
    },
    {
      id: 3,
      title: "Restaurant Website",
      category: "Web Design",
      src: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4",
      alt: "Restaurant website thumbnail"
    },
    {
      id: 4,
      title: "Fitness Tracker",
      category: "Mobile App",
      src: "https://images.unsplash.com/photo-1576678927484-cc907957088c",
      alt: "Fitness app thumbnail"
    },
    {
      id: 5,
      title: "Social Network",
      category: "Full Stack",
      src: "https://images.unsplash.com/photo-1522542550221-31fd19575a2d",
      alt: "Social network thumbnail"
    },
    {
      id: 6,
      title: "Learning Platform",
      category: "Web Application",
      src: "https://images.unsplash.com/photo-1524178232363-1fb2b075b655",
      alt: "Learning platform thumbnail"
    }
  ];

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-10 p-8">
      {projects.map((project) => (
        <div
          key={project.id}
          className="group relative"
        >
          <div className="overflow-hidden rounded-xl transition-all hover:ring-4 hover:ring-orange-500 hover:ring-offset-8">
            <img
              src={project.src}
              alt={project.alt}
              className="w-full h-64 object-cover transform group-hover:scale-105 transition-transform duration-300"
            />
            <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-6">
              <h3 className="text-white text-xl font-bold">{project.title}</h3>
              <p className="text-gray-300">{project.category}</p>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

Customization Examples

Custom Ring Offset for Profile Cards

This example demonstrates a profile card component with customized ring offset width for hover effects, creating an elegant depth appearance.

This is a live editor. Play around with it!
// App.js
  import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function ProfileCard() {
  return (
    <div className="flex justify-center items-center min-h-screen bg-gray-100">
      <div 
        className="w-80 bg-white rounded-xl p-6
        ring-2 ring-blue-500 ring-offset-custom
        hover:ring-offset-profile hover:ring-4
        transition-all duration-300 cursor-pointer"
      >
        <img 
          src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e"
          alt="Profile"
          className="w-24 h-24 rounded-full mx-auto mb-4"
        />
        <h2 className="text-xl font-bold text-center text-gray-800">
          John Davidson
        </h2>
        <p className="text-gray-600 text-center mt-2">
          Senior Developer
        </p>
      </div>
    </div>
  )
}

Interactive Button with Dynamic Ring Offset

This example shows how to create an interactive button with different ring offset widths based on user interaction states.

This is a live editor. Play around with it!
// App.js
  import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function ActionButton() {
  return (
    <div className="flex justify-center items-center min-h-screen bg-gray-900">
      <button 
        className="px-8 py-4 bg-gradient-to-r from-purple-500 to-pink-500
        text-white font-bold rounded-lg
        ring-2 ring-white ring-offset-btn ring-offset-gray-900
        hover:ring-offset-btn-active hover:scale-105
        active:ring-offset-btn
        transition-all duration-300"
      >
        <div className="flex items-center space-x-2">
          Take Action
        </div>
      </button>
    </div>
  )
}

Feature Card with Layered Ring Offset

This example implements a feature card with multiple layers of ring offsets for a sophisticated visual hierarchy.

This is a live editor. Play around with it!
// App.js
  import tailwindConfig from "./tailwind.config.js";
tailwind.config = tailwindConfig;

export default function FeatureCard() {
  return (
    <div className="flex justify-center items-center min-h-screen bg-gray-50">
      <div className="relative w-96 bg-white p-6 rounded-2xl">        
        <img src="https://images.unsplash.com/photo-1498050108023-c5249f4df085"
          alt="Feature"
          className="ring-2 ring-emerald-700 ring-offset-layer-one transition-all duration-500 w-full h-40 object-cover rounded-xl mb-6"
        />
        <h3 className="text-2xl font-bold text-gray-800 mb-4">
          Cloud Solutions
        </h3>
        <p className="text-gray-600 leading-relaxed">
          Leverage our advanced cloud infrastructure to scale your applications seamlessly. Built with enterprise-grade security and performance in mind.
        </p>
      </div>
    </div>
  )
}

Best Practices

Maintain Design Consistency

A consistent design language is essential for user-friendly and professional applications. When using Tailwind CSS’s ring-offset-width utilities, maintain uniformity across interactive components like buttons, cards, or form elements. Consider standardizing the offset width values throughout the app's design. This consistency ensures smooth transitions between UI elements, creating a visually cohesive user experience.

You can also define consistent values in your Tailwind configuration file and apply them across multiple components. By defining reusable utilities in config, you avoid inconsistency when applying offsets.

Optimize for Reusability

When designing components with ring-offset-width, aim for scalability and reusability. Create utility classes or components that can adapt to different contexts without requiring significant modifications. For example, define a reusable class like focus-ring that encapsulates all related styles, including ring-offset-width, ring-color, ring-width, etc.

Reusable components are especially valuable in large-scale projects where maintaining consistency can be challenging. Leverage Tailwind’s @apply directive or component frameworks like React to build modular, reusable utilities and components that can be customized as needed. This approach saves time and reduces redundancy, ensuring a more efficient development process.

Accessibility Considerations

Enhance Readability and Navigability

The ring-offset-width utility can significantly impact content readability and navigability. By creating a distinct visual separation around elements, it helps users identify interactive and important components more easily. For instance, applying a noticeable ring-offset-width to form fields or buttons can guide users' attention, especially for those who rely on visual cues to navigate interfaces.

Use colors and widths that align with accessibility guidelines, ensuring the offset does not blend into the background or overwhelm the content. Testing your designs with tools like contrast checkers can help ensure the ring-offset-width enhances usability for a broad audience.

Ensure Keyboard Accessibility

Keyboard accessibility is a critical consideration for users who navigate interfaces without a mouse. The ring-offset-width utility enhances focus visibility, making it easier for keyboard users to identify which element is currently active. This utility, when combined with focus modifier, can significantly improve the usability of your site or application.

When designing keyboard-accessible interfaces, test the tab order and focus states thoroughly. Use ring-offset-width consistently for all focusable elements to avoid confusing or disorienting users. This ensures that your design is not only visually appealing but also functionally inclusive.