Guides/ CSS
change_history

CSS Triangle Tutorial: Border Trick & Modern Alternatives

CSS triangles have been a staple of tooltips and speech bubbles for 15+ years. The classic border hack still works, but modern CSS offers cleaner alternatives with clip-path and conic-gradient. This guide covers all three approaches with copy-paste code.

April 2026 · 6 min read

The Classic Border Trick

The oldest trick: a zero-size element with thick borders. Each border is a trapezoid; making three transparent leaves just one triangle.

/* Triangle pointing up */
.triangle-up {
  width: 0;
  height: 0;
  border-left:  20px solid transparent;
  border-right: 20px solid transparent;
  border-bottom: 30px solid #7c5cfc;
}

/* Triangle pointing down */
.triangle-down {
  width: 0;
  height: 0;
  border-left:  20px solid transparent;
  border-right: 20px solid transparent;
  border-top:   30px solid #7c5cfc;
}

/* Triangle pointing right */
.triangle-right {
  width: 0;
  height: 0;
  border-top:    20px solid transparent;
  border-bottom: 20px solid transparent;
  border-left:   30px solid #7c5cfc;
}

/* Triangle pointing left */
.triangle-left {
  width: 0;
  height: 0;
  border-top:    20px solid transparent;
  border-bottom: 20px solid transparent;
  border-right:  30px solid #7c5cfc;
}

Why it works: when an element has width and height of 0, its borders meet at diagonal seams. The non-transparent border you keep becomes a triangle pointing away from itself. Adjust border widths to change triangle proportions — equal left/right = isosceles; unequal = scalene.

Modern Alternative: clip-path

The clip-path approach treats the element as a real box and clips it to a polygon. Code is more intuitive and supports rotation, scaling, and transitions.

.triangle-up {
  width: 40px;
  height: 30px;
  background: #7c5cfc;
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

.triangle-down {
  width: 40px;
  height: 30px;
  background: #7c5cfc;
  clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
}

.triangle-right {
  width: 30px;
  height: 40px;
  background: #7c5cfc;
  clip-path: polygon(0% 0%, 100% 50%, 0% 100%);
}

.triangle-left {
  width: 30px;
  height: 40px;
  background: #7c5cfc;
  clip-path: polygon(100% 0%, 0% 50%, 100% 100%);
}

Advantages of clip-path: (1) the element has real dimensions, so you can apply padding, add content, use flexbox children; (2) easy to animate with transform; (3) readable code — the polygon points are obvious.

Speech Bubble with Triangle Tail

The most common use: adding a tail to a chat bubble or tooltip.

.bubble {
  position: relative;
  background: #7c5cfc;
  color: #fff;
  padding: 12px 16px;
  border-radius: 12px;
  max-width: 300px;
}

.bubble::after {
  content: '';
  position: absolute;
  bottom: -10px;
  left: 20px;
  width: 0;
  height: 0;
  border-left:  10px solid transparent;
  border-right: 10px solid transparent;
  border-top:   10px solid #7c5cfc;
}

Key pattern: use ::before or ::after pseudo-elements so you don't need extra HTML. Match the triangle color to the bubble background color, and position with position: absolute.

Tooltip with Triangle Pointer

.tooltip {
  position: relative;
  display: inline-block;
}

.tooltip:hover::after,
.tooltip:hover::before {
  opacity: 1;
}

.tooltip::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 10px;
  background: #1a1a1a;
  color: #fff;
  border-radius: 6px;
  font-size: 12px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
}

.tooltip::before {
  content: '';
  position: absolute;
  bottom: calc(100% + 2px);
  left: 50%;
  transform: translateX(-50%);
  border-left:  6px solid transparent;
  border-right: 6px solid transparent;
  border-top:   6px solid #1a1a1a;
  opacity: 0;
  transition: opacity 0.2s;
}

Usage: <button class="tooltip" data-tooltip="Hello world">Hover me</button>

Triangle with Outline / Border

Creating a triangle with a visible outline is where border triangles break down (borders are already used up). Use clip-path or stack two triangles:

.triangle-outlined {
  width: 40px;
  height: 35px;
  background: #1a1a1a; /* outline color */
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  position: relative;
}

.triangle-outlined::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  right: 2px;
  bottom: 0;
  background: #7c5cfc; /* fill color */
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

Comparison: Which Approach When

  • Border trick — best for simple tooltip/bubble tails via pseudo-elements. Maximum browser support. Limitation: can't contain content, can't transition shape smoothly.
  • clip-path — best for modern UIs. Readable code, real dimensions, animatable. Supported in all evergreen browsers since 2018. Use for carousel arrows, ribbon badges, cut-corner cards.
  • SVG triangle — best when you need precise control, stroke, gradients, or interactive behavior. Slightly heavier.
  • Unicode characters (▲ ▼ ◀ ▶) — acceptable for text-like arrows but limited styling.

Frequently Asked Questions

Why use CSS triangles instead of an SVG or image?
Zero HTTP requests, infinitely scalable (with clip-path), inherit color via border-color: currentColor, and trivially themeable via CSS variables. An SVG triangle is fine too, but CSS triangles integrate more naturally with pseudo-elements for speech bubbles.
Can I animate a CSS triangle's size or rotation?
Rotation: yes, via transform: rotate() — works on both border and clip-path triangles. Size animation: smooth only with clip-path (animating transform: scale()) — border triangles can't transition their border-width cleanly.
How do I make the triangle responsive to its parent?
Use clip-path with percentage units — the triangle auto-scales to any width/height. Border triangles use fixed pixel border widths, so they don't scale without JavaScript or CSS variables.
What browsers support clip-path for triangles?
All evergreen browsers since 2018 (Chrome 55+, Firefox 54+, Safari 9.1+). Polygon syntax is universally supported. Only IE11 needs a fallback — and IE is no longer a concern for most sites in 2026.
How do I center a triangle under a button?
For pseudo-element tails: position: absolute; left: 50%; transform: translateX(-50%); on the triangle. For standalone triangles in a flex container: margin: 0 auto or parent justify-content: center.