Generated CSS

CSS Triangles — Complete Reference

CSS triangles are a foundational UI pattern used everywhere that an arrow, chevron, or directional indicator is needed — tooltip arrows, dropdown indicators, speech bubbles, section dividers, and decorative geometric shapes. They can be created through three distinct techniques, each with different tradeoffs around browser support, background-image compatibility, box-shadow behaviour, and animation capabilities. Understanding all three gives you the right tool for every situation.

The border trick — how and why it works

The border trick is the oldest and most compatible technique. It exploits a rarely-noticed geometric property of CSS borders: when an element has zero width and zero height, its four borders still occupy space and meet at the center of the element. Each border is actually a trapezoid that fills one quadrant of the element's space. With normal dimensions, adjacent borders form a diagonal line at their meeting point — you can see this if you give an element four differently-colored thick borders and look closely at the corners.

Setting an element to zero width and zero height collapses those four trapezoids so they all meet at a single center point, making each one a triangle. Making three of the four borders transparent leaves a single visible triangle pointing away from the colored border. The colored border's side is the base of the triangle — a colored border-bottom produces a triangle whose base is at the bottom and whose apex points up.

The border trick has one significant limitation: because the visible shape is produced by transparent borders, the element's actual rectangular bounding box still exists and is invisible. This means box-shadow casts on the invisible rectangle rather than the triangle shape, and background-color fills the invisible rectangle instead of the triangle. For triangle shapes that need shadows, use filter: drop-shadow() applied to a wrapper element, or switch to the clip-path technique.

The clip-path technique

The clip-path: polygon() approach takes a regular rectangular element and clips it to a triangle shape. Unlike the border trick, the element retains its normal rectangular structure underneath — it still has a background, can receive box-shadow (though the shadow appears on the rectangle before clipping), and can contain content. The key advantage is that filter: drop-shadow() applied to the element or a wrapper follows the clipped shape, and the shape itself can be animated by transitioning the clip-path value between two polygon definitions.

Clip-path triangles also support gradient backgrounds, which the border trick cannot — you can create a triangle with a gradient fill by applying a linear or radial gradient as the element's background and then clipping it to the triangle shape. The triangle can be any size and scaled via normal CSS dimensions rather than calculating border sizes. The clip-path property is supported in all modern browsers with no vendor prefix needed.

Pseudo-element tooltip arrows

Tooltip arrows are the most common real-world application of CSS triangles. The standard approach uses a ::before or ::after pseudo-element on the tooltip container, positioned absolutely to extend just beyond the tooltip's edge. The pseudo-element is given zero dimensions and colored borders to produce a triangle pointing toward the trigger element. Because pseudo-elements inherit the parent's positioning context, they move with the tooltip automatically.

The tooltip arrow's color must match the tooltip background color. If you have a border on the tooltip, a common technique is to use two stacked pseudo-elements — one slightly larger in the border color and one in the background color positioned to overlap it — creating the illusion of a bordered arrow. The CSS Tooltip generator on this site generates the complete code for this pattern in all four directions.

Corner triangles

Corner triangles — triangles that sit in a corner rather than pointing in a cardinal direction — are produced with the border trick using only two borders instead of three. A top-right corner triangle uses border-top in a solid color and border-left as transparent, with no border-bottom or border-right at all. The result is a right triangle whose hypotenuse runs diagonally from the element's top-left to bottom-right (or whichever corner you target). Corner triangles are commonly used as "ribbon" decorations on cards and as sale or "new" badge indicators positioned at a card's corner.

Animated morphing triangles

The clip-path: polygon() approach enables shape morphing animations that the border trick cannot do. Because clip-path values can be transitioned when both the start and end values have the same number of polygon points, you can animate a triangle changing direction, collapsing to a point, or transforming into a rectangle or other polygon. The constraint is that both polygons must have the same number of vertices — a triangle (3 points) can only animate to another 3-point polygon. If you need to morph between a triangle and a shape with more points, you can add duplicate points to the triangle that don't change the shape but give the animation extra vertices to work with.

Equilateral and isosceles triangles

An equilateral triangle has three equal sides and three 60-degree angles. For the border trick, the height of an equilateral triangle is its width multiplied by √3/2 ≈ 0.866. A 100px-wide equilateral triangle is approximately 86.6px tall. With clip-path, you can use aspect-ratio: 1 / 0.866 on a responsive element and apply clip-path: polygon(50% 0%, 0% 100%, 100% 100%) to get a perfectly proportioned equilateral triangle at any size. An isosceles triangle (two equal sides) is the default shape for the basic border trick when the two transparent borders are equal and the colored border is different.

CSS chevrons and arrow icons

The classic dropdown chevron icon — an open triangle rather than a solid one — is not made with the border trick or clip-path but with a rotated square element with two borders. Setting border-right and border-bottom on a square element and then rotating it 45 degrees with transform: rotate(45deg) produces a downward-pointing chevron. Rotating by -45 degrees gives an upward chevron, 135 degrees gives a left chevron, and -135 degrees gives a right chevron. This technique is universally used in dropdowns and accordions.

Why can't I add box-shadow to a border trick triangle?

The border trick produces a triangle visually, but the element's bounding box is still a zero-width, zero-height rectangle. Box-shadow shadows the bounding box, which is invisible and produces no visible shadow. Use filter: drop-shadow() on a wrapper element instead — it analyzes the element's actual rendered pixels and shadows only the visible parts, following the triangle shape correctly.

How do I make a triangle with a gradient fill?

The border trick cannot accommodate gradient fills — the colored border must be a solid color. For a gradient triangle, use clip-path: polygon() with any gradient as the element's background. The clip-path will clip the gradient to the triangle shape, and the gradient will render correctly within the triangle bounds.

Can I animate a CSS triangle changing direction?

With clip-path, yes. Define both orientations as polygon() values with the same number of points, and transition between them. For example, transition from polygon(50% 0%, 0% 100%, 100% 100%) (up) to polygon(0% 0%, 100% 0%, 50% 100%) (down). This is how the animated chevron in accordion headers typically works — the element shape doesn't change, but the rotation of a square element with two visible borders does via transform: rotate().

What is the right technique for a tooltip arrow?

Use the border trick via a pseudo-element (::before or ::after) for tooltip arrows. It produces the smallest amount of code, works in all browsers including very old ones, and gives you precise pixel-level control over the arrow size and position. The clip-path approach is better for decorative standalone triangles that need shadows or gradient fills.

How do I make an outlined or bordered triangle?

Stack two border-trick triangles using a pseudo-element: the base element produces the larger outer triangle in the border color, and its ::after pseudo-element produces a slightly smaller triangle in the background color, positioned to overlap. The visible difference between the two creates the appearance of an outline. The size difference determines the apparent border width.

Tool last updated: