Outer Border Radius Tabs

Complete CSS Implementation Guide

What Makes Outer Border Radius Tabs Challenging

Traditional CSS provides border-radius for rounding corners, but this only works on the outer edges of an element. When you want tabs that connect to a content area below with rounded, flared edges--like a physical file folder tab--the limitations become clear. You can't simply apply border-radius to achieve this effect because the bottom of the tab needs to flare outward, not round inward.

The challenge lies in creating two distinct curves at the bottom of each tab: one at each corner that transitions from the tab's width down to the content area. Standard CSS border-radius can't achieve this because it always rounds the outer corner of an element, creating an inward curve rather than an outward flare.

Understanding these CSS fundamentals is essential for professional web development projects that require polished UI components.

The Physical Folder Metaphor

Think about a real manila folder. The tab at the top has rounded top corners, but where it connects to the folder body, there's a subtle outward curve. This visual cue helps the user understand the relationship between the tab and the content it controls. Implementing this in CSS requires creative solutions beyond simple border-radius declarations.

Common use cases for this pattern include:

  • Settings panels and preference dialogs
  • Documentation sidebars with section navigation
  • Multi-section content layouts
  • Dashboard interfaces with collapsible sections

When implemented correctly, this subtle outward curve at the tab-to-content connection creates a more intuitive visual hierarchy that guides users through your interface.

The Classic Pseudo-Element Approach

The original solution, popularized by Steve Smith and Chris Coyier, uses four additional pseudo-elements per tab to create the outward curves. This approach predates modern CSS shape functions and works reliably across all browsers, making it the most compatible choice for projects requiring broad browser support.

The technique involves placing two pseudo-elements at the bottom corners of the tab, each with a matching background color and border-radius that creates the illusion of an outward flare. Two additional pseudo-elements or careful positioning strategies help hide any unwanted portions of these corner curves.

This method has stood the test of time because it relies on fundamental CSS features--pseudo-elements, absolute positioning, and background inheritance--that have been supported in browsers for decades. This technique demonstrates why CSS is a strongly typed language that requires understanding of property interactions.

Implementation Details

The key to making this technique work lies in precise positioning and color matching. The pseudo-elements are positioned absolutely at the bottom corners, extending beyond the tab's boundaries. By giving them the same background color as the tab (using background: inherit), they appear to be natural extensions of the tab shape.

Key implementation considerations:

  • Use position: relative on the parent tab to establish a positioning context
  • Set pseudo-elements to bottom: 0 to align them with the tab's bottom edge
  • Match the background color using background: inherit for seamless integration
  • Adjust width and border-radius values to control the flare intensity
  • Handle active vs. inactive tab states by updating pseudo-element styles accordingly

When the tab's background uses a gradient or complex background, you'll need to adjust the pseudo-element positioning to ensure the visual continuity is maintained across all states.

1.tab {2 position: relative;3 padding: 12px 24px;4 background: linear-gradient(180deg, #5a9fd4, #2980b9);5 border-radius: 12px 12px 0 0;6 color: white;7 font-weight: 500;8}9 10/* Create the left flare */11.tab::before {12 content: '';13 position: absolute;14 bottom: 0;15 left: -20px;16 width: 20px;17 height: 20px;18 background: inherit;19 border-radius: 0 0 0 20px;20}21 22/* Create the right flare */23.tab::after {24 content: '';25 position: absolute;26 bottom: 0;27 right: -20px;28 width: 20px;29 height: 20px;30 background: inherit;31 border-radius: 0 0 20px 0;32}

Modern CSS shape() Approach

The shape() function, used with clip-path, represents a revolutionary approach to creating outer border radius tabs. Instead of adding extra pseudo-elements to your markup, you define the exact shape of the tab using drawing commands similar to SVG path syntax. This results in cleaner HTML and more precise control over the final appearance.

The shape() function accepts several drawing commands:

  • from [point] - Start drawing from a specific coordinate
  • curve to [x] [y] with [cx] [cy] - Draw a Bezier curve using a control point
  • vline to [value] - Draw a vertical line to a specific position
  • hline to [value] - Draw a horizontal line to a specific position
  • arc to [x] [y] of [radius] - Draw a circular arc

This approach eliminates the need for extra DOM elements entirely, resulting in cleaner, more maintainable code that integrates seamlessly with modern CSS grid layouts.

Shape Commands Explained

The shape() drawing commands work together to trace the precise perimeter of your tab shape. Each command builds upon the previous one, creating a complete path that defines exactly where the tab should be visible and where it should be clipped.

The curve to command is particularly powerful for creating the outward flares at the bottom of tabs. By specifying a target point and a control point, you can create smooth Bezier curves that would be impossible to achieve with traditional border-radius alone.

The vline to and hline to commands draw straight lines between curves, creating the flat edges of the tab. By combining these with variable values and CSS custom properties, you can create fully responsive tab shapes that adapt to different sizes automatically.

1.tab {2 --tabGirth: 12px;3 --tabCornerRadius: calc(var(--tabGirth) * 2);4 5 display: inline-block;6 padding: 0.5rem 2rem;7 white-space: nowrap;8 9 clip-path: shape(10 from bottom left,11 curve to var(--tabGirth) calc(100% - var(--tabGirth))12 with var(--tabGirth) 100%,13 vline to var(--tabGirth),14 curve to var(--tabCornerRadius) 0 with var(--tabGirth) 0,15 hline to calc(100% - var(--tabCornerRadius)),16 curve to calc(100% - var(--tabGirth)) var(--tabGirth)17 with calc(100% - var(--tabGirth)) 0,18 vline to calc(100% - var(--tabGirth)),19 curve to 100% 100% with calc(100% - var(--tabGirth)) 100%20 );21}
Step-by-Step Shape Construction

1. Starting Point

`from bottom left` - Begin drawing at the bottom-left corner of the tab. This is where the shape tracing begins and where the first outward curve will originate.

2. First Curve

`curve to var(--tabGirth) calc(100% - var(--tabGirth)) with var(--tabGirth) 100%` - Create the outward curve from the bottom-left corner. The control point at (0, 100%) pulls the curve outward.

3. Vertical Line Up

`vline to var(--tabGirth)` - Draw up the left side of the tab to the position where the top-left corner curve should begin.

4. Top Left Corner

`curve to var(--tabCornerRadius) 0 with var(--tabGirth) 0` - Create the rounded top-left corner. This curve connects the left edge to the top edge.

5. Top Edge

`hline to calc(100% - var(--tabCornerRadius))` - Draw across the top of the tab to the position where the top-right corner curve should begin.

6. Top Right Corner

`curve to calc(100% - var(--tabGirth)) var(--tabGirth) with calc(100% - var(--tabGirth)) 0` - Create the rounded top-right corner connecting the top edge to the right edge.

7. Vertical Line Down

`vline to calc(100% - var(--tabGirth))` - Draw down the right side to the position where the final outward curve should begin.

8. Final Curve

`curve to 100% 100% with calc(100% - var(--tabGirth)) 100%` - Create the outward curve at the bottom-right corner, completing the shape back to the starting point.

Using CSS Custom Properties for Flexibility

One of the most powerful aspects of the shape() approach is the ability to use CSS custom properties (variables) for all the dimension values. This makes the tab design easily adjustable without rewriting the entire clip-path definition.

By defining --tabGirth (the amount the tab flares outward) and --tabCornerRadius (the corner rounding amount) as variables, you can create different tab sizes by simply changing these values.

This approach allows you to create a consistent design system where all tabs share the same shape logic but can vary in size as needed. The calculations ensure that corner radii always scale proportionally with the tab girth, maintaining visual harmony across different tab sizes. Using CSS variables effectively is a key skill for maintainable web development.

Browser Support and Fallbacks

Browser support for shape() is growing but not yet universal. As of 2025, the function has full support in Chromium-based browsers (Chrome, Edge) and partial support in Firefox behind flags. Safari support remains limited, requiring careful consideration of fallback strategies.

The @supports rule allows you to provide alternative styling for browsers that don't support shape(). This enables a progressive enhancement strategy where modern browsers get the enhanced experience while older browsers receive a simpler but still functional design. This approach to graceful degradation is essential for cross-browser compatible web development.

Progressive Enhancement Strategy

Implementing outer border radius tabs with progressive enhancement follows a layered approach:

  1. Base layer: Start with basic border-radius styling that works everywhere
  2. Classic enhancement: Add pseudo-elements for the traditional flare effect
  3. Modern enhancement: Apply shape() for cleaner code in supporting browsers
  4. Feature queries: Use @supports to layer enhancements gracefully

This strategy ensures that every visitor receives a functional tab interface, with visual improvements applied based on their browser's capabilities.

1.tab {2 padding-inline: 1rem;3 border-start-start-radius: var(--tabGirth);4 border-start-end-radius: var(--tabGirth);5}6 7@supports not (clip-path: shape(from top left, hline to 0)) {8 .tab {9 border-radius: var(--tabGirth) var(--tabGirth) 0 0;10 }11}12 13@supports (clip-path: shape(from top left, hline to 0)) {14 .tab {15 clip-path: shape(16 from bottom left,17 curve to var(--tabGirth) calc(100% - var(--tabGirth))18 with var(--tabGirth) 100%,19 vline to var(--tabGirth),20 curve to var(--tabCornerRadius) 0 with var(--tabGirth) 0,21 hline to calc(100% - var(--tabCornerRadius)),22 curve to calc(100% - var(--tabGirth)) var(--tabGirth)23 with calc(100% - var(--tabGirth)) 0,24 vline to calc(100% - var(--tabGirth)),25 curve to 100% 100% with calc(100% - var(--tabGirth)) 100%26 );27 }28}

Accessibility Considerations

Tabs are a well-established ARIA pattern, and implementing outer border radius tabs doesn't exempt you from proper accessibility requirements. The visual flair of flared edges should never come at the cost of usability for keyboard and screen reader users.

Proper Tab Implementation

The WAI-ARIA tabs pattern requires specific roles and attributes:

  • role="tablist" for the container element
  • role="tab" for each individual tab button
  • role="tabpanel" for each content panel
  • aria-selected to indicate the active tab
  • aria-controls to link tabs to their panels
  • aria-labelledby to label panels with their controlling tab

These attributes enable screen readers to navigate your tab interface effectively, announcing the tab list structure and allowing users to jump between tabs and panels. Accessible interfaces also contribute positively to SEO performance, as search engines prioritize well-structured, accessible content.

1<div class="tablist" role="tablist" aria-label="Content Tabs">2 <button role="tab" aria-selected="true" aria-controls="panel1" id="tab1">Overview</button>3 <button role="tab" aria-selected="false" aria-controls="panel2" id="tab2">Details</button>4</div>5<div id="panel1" role="tabpanel" aria-labelledby="tab1"></div>

Keyboard Navigation Requirements

Tabs must be fully navigable using only the keyboard:

  • Tab: Move focus into and out of the tab list
  • Arrow keys: Move focus between tabs when focus is in the list
  • Enter/Space: Activate the focused tab
  • Home/End: Navigate to first and last tab (recommended)

Visual Focus Indicators

The clip-path used for the shape() technique can interfere with visible focus indicators, as the focused state may be clipped. Use outline-offset or wrap tabs in a focusable container to ensure focus states remain visible.

1.tab:focus-visible {2 outline: 2px solid #0056b3;3 outline-offset: 2px;4}

Performance Best Practices

When animating tabs (hover states, active states, or transitions), performance should be a key consideration. The clip-path property, while powerful, can be computationally expensive to animate because it affects how the browser renders the element's shape.

Efficient Animation Techniques

Avoid animating clip-path directly when possible. Instead, animate properties that can be handled by the GPU:

/* Instead of animating clip-path */
.tab:hover {
 clip-path: shape(/* different shape */); /* Expensive */
}

/* Animate transform instead */
.tab {
 clip-path: shape(/* final shape */);
 transform: translateY(0);
 transition: transform 0.2s ease;
}
.tab:hover {
 transform: translateY(-2px); /* GPU-accelerated */
}

GPU-accelerated properties to prefer:

  • transform (translate, scale, rotate)
  • opacity

Properties to avoid animating directly:

  • width, height
  • margin, padding
  • clip-path

Reducing Paint Operations

The pseudo-element approach for classic outer border radius tabs can cause additional paint operations, especially when animating hover states. To minimize performance impact:

/* Use will-change sparingly */
.tab {
 will-change: transform;
}

/* Prefer transform for hover effects */
.tab:hover {
 transform: translateY(-2px);
}

Additional optimization tips:

  • Use will-change sparingly and only on elements that will actually animate
  • Prefer transform over margin or padding changes for hover effects
  • Consider using CSS containment (contain: paint) on complex tab interfaces
  • Test animations on lower-end devices to ensure smooth performance

By following these performance guidelines, you can create tab interfaces that not only look polished but also perform smoothly across all devices.

Complete Production-Ready Implementation
1:root {2 --tab-girth: 12px;3 --tab-radius: calc(var(--tab-girth) * 2);4 --tab-bg: #f8fafc;5 --tab-active-bg: #ffffff;6 --tab-text: #475569;7 --tab-active-text: #0f172a;8 --tab-border: #e2e8f0;9 --tab-accent: #3b82f6;10}11 12.tabs {13 display: flex;14 gap: 4px;15 padding: 8px 8px 0;16 border-bottom: 1px solid var(--tab-border);17}18 19.tab {20 --local-girth: var(--tab-girth);21 --local-radius: var(--tab-radius);22 position: relative;23 display: inline-flex;24 align-items: center;25 justify-content: center;26 padding: 12px 24px;27 white-space: nowrap;28 font-family: system-ui, sans-serif;29 font-size: 14px;30 font-weight: 500;31 color: var(--tab-text);32 background: var(--tab-bg);33 border: none;34 cursor: pointer;35 transition: all 0.2s ease;36 border-radius: var(--local-radius) var(--local-radius) 0 0;37}38 39@supports (clip-path: shape(from top left, hline to 0)) {40 .tab {41 clip-path: shape(42 from bottom left,43 curve to var(--local-girth) calc(100% - var(--local-girth)) with var(--local-girth) 100%,44 vline to var(--local-girth),45 curve to var(--local-radius) 0 with var(--local-girth) 0,46 hline to calc(100% - var(--local-radius)),47 curve to calc(100% - var(--local-girth)) var(--local-girth) with calc(100% - var(--local-girth)) 0,48 vline to calc(100% - var(--local-girth)),49 curve to 100% 100% with calc(100% - var(--local-girth)) 100%50 );51 border-radius: 0;52 }53}54 55.tab:hover {56 color: var(--tab-active-text);57 background: linear-gradient(180deg, #fff, var(--tab-bg));58}59 60.tab[aria-selected="true"] {61 color: var(--tab-active-text);62 background: var(--tab-active-bg);63 box-shadow: 0 -1px 0 var(--tab-accent) inset;64}65 66.tab:focus-visible {67 outline: 2px solid var(--tab-accent);68 outline-offset: 2px;69}

Conclusion

Outer border radius tabs represent a sophisticated UI pattern that elevates the perceived quality of your interface. While the classic pseudo-element approach remains the most compatible choice for broad browser support, the modern shape() function with clip-path offers a cleaner, more maintainable solution for projects targeting modern browsers.

Key takeaways from this guide:

  1. Choose your approach based on browser support needs: Use pseudo-elements when maximum compatibility is required, or shape() for cleaner code in modern browser environments
  2. Use CSS variables: Make your tab shapes configurable and maintainable by centralizing size values in custom properties
  3. Provide graceful fallbacks: Ensure the design degrades elegantly in older browsers using @supports queries
  4. Prioritize accessibility: Implement proper ARIA roles, keyboard navigation, and visible focus indicators
  5. Optimize for performance: Animate GPU-accelerated properties like transform and opacity, avoiding expensive repaints from clip-path changes

By combining these techniques with progressive enhancement, you can create polished tab interfaces that work beautifully across all browsers while taking full advantage of modern CSS capabilities where available.


Sources

  1. CSS-Tricks: Outer Border Radius Tabs - The original technique by Chris Coyier
  2. Frontend Masters Blog: Modern CSS Round-Out Tabs - Cutting-edge approach using shape()
  3. MDN Web Docs: border-radius - Official CSS property documentation
  4. CSS-Tricks: shape() Commands - Complete reference for shape() function commands
  5. WAI-ARIA: Tabs Pattern - Official accessibility guidelines