CSS Pure Border Triangle Builder

How to use 3 steps
  1. Choose where it Points toward.
  2. Set the Base width, Height and Colour.
  3. Press Copy Triangle CSS and paste it into your stylesheet.
Generated CSS Code

How this tool works

The trick is older than most of CSS's shape tools and still the lightest. An element with no width and no height still draws its four borders, and because those borders meet at diagonals, each one is a triangle with its point at the centre. Make three transparent and the fourth is left standing on its own. No image, no SVG, no extra element.

Which border carries the colour decides which way the triangle points, and it is the opposite of what most people first guess: a triangle pointing up is drawn by colouring the bottom border. Base and height are set independently here, so scalene and tall narrow triangles are reachable, where a single size value only ever gives a right-angled or isosceles one.

Working with a shape made of borders

  • The colour goes on the opposite side to the point: an upward triangle colours the bottom border. Once that is clear the rest of the property follows.
  • The base is twice the value you set: each side border contributes half, so a base width of 20 produces a 40 pixel triangle. The readout shows the finished width to save the arithmetic.
  • It cannot be given a border or a gradient: the shape is made of borders, so there is nothing left to draw an outline with. A clip-path or an inline SVG is the route when either is needed.
  • Two overlapping triangles make an outlined one: a slightly larger triangle in the outline colour behind a smaller one in the fill colour is the usual way to fake a border on this shape.

Why an element with no size still draws something

The trick is older than most of CSS's shape tools and still the lightest way to draw an arrow.

An element with no width and no height still draws its four borders. Because those borders meet at diagonals, each one is not a rectangle but a wedge, with its point at the element's centre. Make three of them transparent and the fourth is left standing alone: a triangle.

No image, no SVG, no extra markup. The [msm_tool slug="css-triangle"] writes the border widths and colours for each direction, but the mechanism is worth understanding because it explains every quirk that follows.

The colour goes on the opposite side to the point

This is the part that catches everyone, and it follows directly from the wedges.

A triangle pointing up is drawn by colouring the bottom border. The bottom border's wedge has its point at the top and its base along the bottom edge, so that is the shape left behind when the others go transparent.

Once that clicks, the rest of the property follows without needing to be memorised.

The base is twice the value you set

The left and right borders each contribute half of an upward triangle's base. Set both to 20 pixels and the finished triangle is 40 pixels wide.

That doubling is the second most common source of confusion, and it is why the generator reports the finished width alongside the value rather than leaving you to do the arithmetic.

Setting the base and the height independently is what allows scalene and tall narrow triangles. A single size value only ever gives a right-angled or isosceles one.

It cannot take a border or a gradient

The shape is made of borders, so there is nothing left over to draw an outline with. There is no fill to apply a gradient to either — the triangle is a solid colour by construction.

Two workarounds:

  • For an outline, stack two triangles: a slightly larger one in the outline colour behind a smaller one in the fill colour, offset by a pixel or two. Fiddly but reliable.
  • For a shadow, use filter: drop-shadow(), which follows the visible shape rather than the element's rectangle. box-shadow would draw a shadow around the invisible box.

If you need either of those regularly, a clip-path or an inline SVG is the less painful route.

Where it is still the right choice

Tooltip and popover arrows, above all. Place the triangle on a pseudo-element positioned against the container's edge, with its colour matching the container's background, and it reads as part of the shape.

Also: dropdown carets, breadcrumb separators, the notch on a speech bubble, and the small indicator on an accordion. Anything where you want a simple arrow with no extra element and no request.

Triangle or clip-path

The border trick for simple arrows — it is smaller, supported everywhere without exception, and needs no vendor prefix. A clip-path when the shape is more complex, needs its own outline, or has to animate.

The border version cannot animate its shape smoothly, because transitioning border widths produces a stepped result rather than a morph.

A note on the markup

Triangles built this way are decoration, and they belong on a pseudo-element rather than a real one. An empty <div> whose only purpose is to be a shape is an element a screen reader has to step over for no reason.

::before or ::after on the element that owns the arrow keeps the markup clean and keeps the decoration out of the accessibility tree entirely.

Frequently Asked Questions

Specific to the CSS Pure Border Triangle Builder. Broader questions about this category are answered on the category page.

Because each border is a wedge meeting at the element's centre. The bottom border's wedge has its point at the top, so that is the shape left when the others go transparent.
Twice the base value, since the left and right borders each contribute half. The readout shows the finished width.
Not directly — the shape is made of borders, so there is none left over. A filter with drop-shadow follows the visible shape and is the usual workaround.
This for a simple arrow or tooltip point, where it is smaller and supported everywhere. A clip-path when the shape is more complex or needs its own outline.
Place the triangle on a pseudo-element positioned against the tooltip's edge, with the colour matching the tooltip's background.

Subscribe to receive notifications about new tools, features, and productivity tips.