Skip to main content

CSS Triangle Generator

What it does

The CSS Triangle Generator produces a triangle in any of eight directions — four cardinal (up, down, left, right) plus four diagonal — using the venerable border-trick technique that turns a zero-size box into a triangle made of meeting border edges. Width, height, and colour controls feed a live preview; the output is a small block of CSS you can drop straight into a stylesheet. No SVG, no PNG, no image assets at all.

Common situations

You’re building a tooltip component for a design system and the brief specifies a small arrow pointing at the anchor element. Bundling an SVG icon for that arrow is overkill — a CSS triangle is two lines, scales infinitely, and inherits colour from the parent.

You’re producing a chevron-style indicator next to a clickable item that opens a menu. A Lucide icon would work; a CSS triangle is smaller, faster, and (since it is rendered by the browser, not a font) immune to font-loading delays that produce the brief “icon not yet visible” flash.

You’re styling a speech-bubble component for a chat-style interface. The bubble itself is a rounded rectangle; the small tail pointing at the speaker is the bit that turns it from “card” to “speech bubble”. A CSS triangle as an ::after pseudo-element handles this without an extra DOM node or asset.

You’re decoding old CSS during a code audit and find unfamiliar border-bottom: 50px solid red; border-left: 30px solid transparent; patterns. Recognising the border-trick technique on sight saves “what is this doing?” moments — knowing it produces a triangle pointing up, scaled by the borders, is the key.

You’re producing decorative geometric elements for a marketing page — abstract triangles as background flourishes, accent shapes pointing at content blocks, retro-style badges. Multiple CSS triangles compose into surprisingly elaborate compositions without any image weight.

What you need to know

A triangle in CSS is a hack. A box with zero width and zero height has nothing to display, but its borders still render — and where two borders meet at a corner, they do so on a 45° diagonal rather than a perpendicular line. If you make three of the four borders transparent and the fourth a solid colour, the result is a triangle pointing away from the coloured side. Make the transparent borders different sizes from the coloured one, and the triangle gets taller or shorter.

The diagonal directions use a slightly different recipe — only two adjacent borders, one transparent and one coloured. This produces a right triangle with the right angle in one corner and the hypotenuse forming the diagonal.

Knowing the trick is occasionally useful when reading other people’s CSS. border-bottom: 100px solid red followed by transparent left and right borders is the pattern; recognising it on sight saves “what is this doing?” moments in code review.

The modern alternative is clip-path: polygon(...), which produces triangles directly without the border hack. clip-path: polygon(50% 0%, 0% 100%, 100% 100%) produces an upward-pointing triangle on any element. Clip-path is more flexible (supports any polygon, not just triangles), more readable (the points describe the shape directly), and animatable (the polygon points can transition smoothly). The border trick is more compact in CSS and slightly more performant, particularly when used in ::before and ::after pseudo-elements.

For tooltip and speech-bubble work, pseudo-elements are the right home for the triangle. Setting the parent to position: relative and the triangle to position: absolute lets you anchor the triangle exactly where it should appear without modifying the DOM. The bubble is the styled box; the triangle is the ::after that visually completes it.

A subtle limitation: pure CSS triangles cannot have a border or shadow on the triangle edge itself. The triangle is made of borders; you cannot put a border on a border. For tooltip arrows where the tooltip box has a border, the result is an arrow inside a bordered box rather than an arrow that visually breaks through the border. The fix is either SVG (which supports stroked triangles) or a clipped element (which supports box-shadow on the visible shape).

Frequently asked questions

How do I make a triangle in CSS?

Use the border-trick: set width and height to 0, make three borders transparent and the fourth a solid colour. width: 0; height: 0; border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 100px solid red; produces a red upward-pointing triangle.

Why does this work? It looks like a hack.

It is a hack. Where two borders meet at a corner, they meet on a 45° diagonal. With width and height of 0, the borders meet entirely at the centre of the box, producing four triangles of which three are transparent and one is visible. The hack has been documented since CSS 2.0 (around 2000) and works in every browser.

What’s the difference between border-trick and clip-path triangles?

Border-trick is older, smaller in CSS, and slightly faster. Clip-path is newer (universal browser support since 2017), more flexible, and supports any polygon. For simple triangles in tooltip arrows or chevrons, border-trick is fine. For complex shapes or animated triangles, clip-path is better.

Can I have a border or outline on a CSS triangle?

Not directly — the triangle is made of borders. Workarounds include adding a slightly larger triangle behind it in a different colour (creates a “two-triangle” border effect), or using SVG (which supports stroked shapes natively).

How do I make a triangle point in different directions?

Change which border is the coloured one. Solid bottom border = points up. Solid top = points down. Solid right = points left. Solid left = points right. The transparent borders define the triangle’s base width.

Can CSS triangles be animated?

The dimensions can transition (border widths animate cleanly), but morphing between different triangle shapes is awkward because the recipe changes per direction. For animated triangles, clip-path is the better tool — clip-path: polygon(...) can interpolate between two polygons smoothly.

Should I use a Unicode triangle character (▲) instead?

Sometimes. and work as text and inherit font weight, colour, and size. The downsides are font-rendering quirks and limited control over exact shape and size. CSS triangles give precise pixel control; Unicode characters give text-flow integration. Pick based on context.

Are CSS triangles accessible to screen readers?

Pseudo-element triangles are presentational and not announced — which is correct for decorative arrows. If the triangle conveys meaning (e.g. “this section is expanded” via a chevron direction), supplement with aria-expanded or visible text rather than relying on the visual.

Common problems

Problem: The triangle is not the size I specified.

Border widths control the triangle’s size, not the element’s width and height (which must remain 0). If you set width: 100px on the triangle element, it pushes the borders apart and breaks the shape. Keep width: 0 and height: 0; size the triangle via the border widths.

Problem: The triangle is in the wrong position relative to its parent.

CSS triangles via the border trick render at the element’s location plus the size of the coloured border. To position precisely, use position: absolute on the triangle element with explicit top/left/bottom/right values, anchored to a parent with position: relative.

Problem: The triangle has visible jagged edges on small sizes.

Browsers anti-alias the diagonal where borders meet, but at very small sizes (below 8px in the smaller dimension) sub-pixel rendering can produce step artefacts. There is no fix at that size — increase the triangle, or switch to inline SVG which renders crisper at small sizes due to stroke smoothing.

Problem: When I add a border to the triangle’s parent box, the triangle does not visually break through it.

The triangle sits inside the parent’s content box. Add position: absolute to position it outside the parent’s bounds (e.g. top: 100% to anchor below the parent). Use negative offsets if needed to overlap the parent’s border. For “arrow that crosses the border” effects, you need either two triangles (one inside, one outside) or SVG.

Problem: Hover state on the triangle does not work.

The triangle’s hit area is the full bounding box, including the transparent borders. Mouse hover on the transparent regions of a triangle still triggers hover events, which is rarely what you want. Use pointer-events: none on decorative triangles to disable hit detection.

Quick guides

For tooltip arrows: Position the triangle as ::after on the tooltip box. Set the tooltip position: relative, the triangle position: absolute with top: 100%; left: 50%; transform: translateX(-50%); to centre under the tooltip pointing down at the anchor.

For chevron indicators: Use a small right-pointing triangle (8–12px) and rotate via transform: rotate(90deg) or transform: rotate(-90deg) to produce up/down chevrons from the same base CSS. Saves repeating the recipe per direction.

For speech bubbles: Triangle as ::before on the bubble container, positioned along the speaker’s side (left: -10px; top: 50%). Match the triangle’s coloured border to the bubble’s background. For bubbles with borders, layer two triangles (one matching the border colour, one matching the background, slightly offset).

Tips

  • The “size” of the triangle is controlled by the border thicknesses, not by any width or height. The element itself is always 0×0.
  • Pure CSS triangles cannot have a border or shadow on the triangle edge itself — that needs SVG or a clipped element. For tooltip arrows where the tooltip has a border, this matters.
  • The triangle inherits currentColor if you set the visible border to that — useful for triangles that should match surrounding text without explicit colour duplication.
  • For a “callout box” tail, position the triangle as a pseudo-element with negative margin to make it overlap the parent box’s edge.
  • Subpixel rendering at small sizes can produce jagged edges on some browsers. Triangles below about 8px in their smaller dimension are safer as inline SVG.
  • For complex or animated shapes, prefer clip-path: polygon(...) over the border trick — more flexible, more readable, and animatable.
  • Use pointer-events: none on decorative triangles so the transparent border regions do not catch unintended hover events.

Related tools in this suite

The natural pairing is the Border Radius Generator — both manipulate borders to produce shapes; together they cover most of the “build a small UI shape without an asset” workflow. The CSS Clip Path Generator is the modern alternative for more complex shapes — clip-path is more powerful, more flexible, and increasingly the default choice over the border trick for anything beyond simple triangles.

Take it further

The wider question, when you find yourself building shapes in CSS, is whether the design system has documented those shapes as components. A team that ships its own tooltip arrows hand-rolled across twelve different components has a consistency problem hiding in plain sight. The systems we build absorb that — one tooltip component with one triangle, used everywhere — so the triangle decision is made once and reused.