A CSS triangle painted through a ::before pseudo-element is built from a zero-size element whose borders form the visible shape, with two transparent borders forming the slopes and one colored border pointing in the chosen direction. The pattern writes the border geometry as explicit longhand declarations rather than collapsing it into a single shorthand line, so the geometry stays visible in the stylesheet and the painted result equals the requested width and height within a single pixel. The element itself measures 0 by 0, so it costs almost nothing to render and can be attached to any block-level element that already has a content or position context. The CSS Triangle Generator produces this exact declaration set in synchronized CSS and HTML, then exposes a live preview so direction, size, and color can be confirmed before any code leaves the page. The default output is a single div with a generated class and aria-hidden, but moving the declarations into a ::before (or ::after) pseudo-element is a mechanical change that does not alter the border math. Once the geometry is correct, the only remaining concerns are contrast against the page background, accessibility of any state the triangle conveys, and behavior under zoom and forced colors.

How a CSS Border Triangle Is Built
The trick is to keep width and height at zero and let the four border sides do the painting. Where two borders meet at a corner, the browser draws a diagonal seam between them. Transparent seams on two sides and a colored seam on one side produce a three-sided wedge. The element itself contributes no measurable box, so layout, transforms, and hit-testing follow the painted borders rather than the content area.
Two rules govern the math, both of which the generator applies explicitly:
- For a vertical triangle (up or down), the two transparent side borders must add up to the requested width, and the colored border must equal the requested height.
- For a horizontal triangle (left or right), the transparent top and bottom borders must add up to the requested height, and the colored border must equal the requested width.
Even dimensions split cleanly. Odd totals use a floor-and-ceiling split, because Chromium can quantize two 20.5px borders down to 20px each and silently lose a pixel. Asking for a 41px base produces a 20px transparent side and a 21px transparent side, which always paints as 41px regardless of the renderer. The implementation writes each longhand explicitly so the geometry remains visible and auditable rather than collapsed into a single shorthand line.
| Direction | Colored border | Transparent borders (the slopes) | Base math |
|---|---|---|---|
| Up | bottom | left, right | Vertical: transparent pair equals width, colored border equals height |
| Down | top | left, right | Vertical: transparent pair equals width, colored border equals height |
| Left | right | top, bottom | Horizontal: transparent pair equals height, colored border equals width |
| Right | left | top, bottom | Horizontal: transparent pair equals height, colored border equals width |
Generate the Triangle Step by Step
- Open the CSS Triangle Generator and pick one of the four directions: up, down, left, or right.
- Enter the visible width and height. Whole integers between 10 and 300 pixels are accepted, and the field rejects decimals and values outside that range rather than quietly clamping.
- Enter a six-digit HEX color with the leading hash, such as #1f6feb. The input is normalized to lowercase and the generator rejects malformed colors instead of substituting a default.
- Inspect the live preview at the approximate size and background where the triangle will be used. The preview uses the same border declarations shown in the CSS output, so what you see is what gets copied.
- Click Copy CSS to copy the border declarations, then click Copy HTML to copy the snippet. Each button requests clipboard permission separately and reports success or denial honestly; if the browser refuses clipboard access, both code blocks remain visible for manual selection and the page does not falsely report success.
- Rename the generated class if it would collide with an existing stylesheet, and update both outputs together so the HTML still matches the CSS.
Move the Output Into a ::before Pseudo-Element
The HTML output is a div with a single class. To render the same triangle as a ::before pseudo-element, drop the div entirely and paste the border declarations under a ::before rule scoped to the parent element. The border math is identical; only the host changes.
Asking for a 41px wide, 30px tall upward triangle in #1f6feb produces CSS that looks like this:
.lizely-triangle { border-left: 20px solid transparent; border-right: 21px solid transparent; border-bottom: 30px solid #1f6feb; }
The total painted base is 20px + 21px = 41px, and the colored border equals the requested 30px height. No other border side is needed because the colored bottom border is the only side that paints for an upward triangle, and width and height stay at zero.
Applied as a pseudo-element, the rule becomes:
.callout::before { content: ""; border-left: 20px solid transparent; border-right: 21px solid transparent; border-bottom: 30px solid #1f6feb; }
The content: "" declaration is required for the pseudo-element to render at all. The parent element should be position: relative or otherwise position its own box, otherwise the triangle will sit at the document origin. Pseudo-elements are not part of the accessibility tree by default, so the aria-hidden attribute from the div version becomes unnecessary; assistive technology already ignores generated content. If the triangle carries meaning such as a state, a direction, validation feedback, or a navigation cue, add an accessible label to the parent control rather than relying on the shape alone.
Where a Border Triangle Reaches Its Limits
A border triangle is small in code and exact in size, but it is also structurally limited. The element's measurable content box is zero while its painted borders occupy space, so layout, transforms, hit testing, outlines, overflow, and alignment can behave less intuitively than a vector shape with an explicit viewBox. A border triangle cannot produce a rounded tip, a curved edge, a hollow shape, a gradient fill, a stroke, a complex shadow, or an arbitrary polygon on its own.
| Need | Border triangle (this generator) | Use instead |
|---|---|---|
| Solid three-sided wedge, up/down/left/right | Exact match — generated in one click | — |
| Rounded tip or curved edge | Not supported | SVG polygon with stroke-linejoin |
| Hollow or stroked shape | Not supported | SVG with fill-rule or clip-path |
| Gradient fill or shadow inside the wedge | Not supported (border color is opaque solid) | SVG or CSS conic/linear-gradient on a clipped div |
| Theme-aware color from currentColor, CSS variables, or alpha | Not generated (hex literal only) | Replace the literal with a trusted design token after copying |
The chosen HEX color is painted as an opaque border; alpha colors, CSS variables, gradients, currentColor, and wide-gamut syntax are not generated by this focused tool. After copying, replacing the literal with a trusted design token can improve theming, but verify that the target syntax is supported and that the transparent side borders remain transparent rather than inheriting a token color.
Accessibility and Contrast for Decorative Triangles
A triangle used purely as decoration, such as a tooltip arrow or a section divider, should not appear in the accessibility tree. The default HTML output already carries aria-hidden="true", and pseudo-elements are ignored by assistive technology by default, so either form meets that requirement. When the shape conveys state, direction, validation, or a navigation cue, do not rely on the visual alone; add visible text or an accessible label to the surrounding interactive control, and keep the decorative triangle hidden from assistive technology.
Color is the other axis. The triangle is painted as a solid opaque wedge, so contrast depends on the page background underneath. A triangle next to text should be checked in light and dark themes rather than assumed to inherit sufficient contrast. Verify any text and background pair against WCAG AA and AAA before the page ships, and re-test under forced colors where the opaque painted border will not pick up an automatic system color.
Test the Result Before Shipping
Three quick checks catch most regressions:
- Render the parent element on light and dark backgrounds and confirm the colored border still meets contrast; transparent sides must remain transparent rather than inheriting a parent color.
- Open the page at 200 percent zoom and on a common mobile width, and confirm the triangle still aligns with the target control. Rasterization can still vary with zoom level and device pixel ratio even when the CSS is exact.
- Inspect with forced-colors mode enabled and confirm the shape either inherits a system color or remains a usable affordance; the opaque painted border will not automatically swap to a system palette.
Performance cost for a single triangle is negligible, but hundreds of decorative elements can still increase DOM and paint work. Prefer one semantic control with a decorative shape over duplicated hidden controls. For reference on the underlying properties, the MDN guide to CSS backgrounds and borders documents border-width, border-style, and border-color behavior, and the CSS Backgrounds and Borders Level 3 specification defines the longhand declarations used here. The exact dimension limits and directional serialization in this generator are deliberately local product rules covered by four-direction, odd-dimension, exact-output, and boundary tests rather than a universal triangle standard.
For a deeper look, see CSS After Triangle Generator: A Solid Border Triangle.