20 hand-coded CSS diagonal sections for SaaS landing pages, agency sites and product marketing. Covers clip-path polygon bands, the skewY technique with counter-skewed content, angled heroes, pricing bands, footer transitions, and inline SVG edges that stay crisp at any width. Includes the two fixes people search for when it breaks: angles that go steep on mobile, and content vanishing under a neighbouring band. Scoped under .dgs-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Want to create eye-catching CSS effects like these from scratch?
You just browsed 20 diagonal sections. Learn the advanced CSS behind them: gradients, clipping, masking, blend modes and filters, so the next effect is yours, not a copy-paste.
The reference build every other demo in this collection varies: one clip-path: polygon() on the section, one --cut custom property driving the angle, and flat neighbours above and below so the diagonal is unmistakable. The band is pulled up under the section above it by exactly its own cut depth, which is what makes the two blocks read as a single continuous page instead of a floating panel.
The technique that predates clip-path and still ships on thousands of marketing pages: skewY() the band, then skewY() the inner wrapper by the exact opposite angle so headings, buttons and images stand up straight. Both edges of the band tilt together, and the demo shows the two flat neighbours you need for the tilt to register — plus the height inflation skew causes, which is the part most tutorials leave out.
The simplest job in the collection and the one people search for most: two solid colour bands meeting on a diagonal with no gap, no seam and no hairline of page background showing through the join. The angle is one token; the fix is a 1px overlap plus a matched pull-up, and the demo labels both sides of the join so you can see exactly where the pixels go.
An angled band laid over a photograph: background-size: cover, a two-stop scrim so the headline holds contrast wherever the image is bright, and the clip cutting both the picture and the overlay as one. The important detail is that the image has to extend past the cut — clip the element and the photo is clipped with it, so any under-sized background reveals the page behind the diagonal.
A parallelogram band: both edges cut at the same angle in the same direction, so the section reads as a slice taken clean through the page rather than a join between two colours. Four polygon points, one --cut token, and flat neighbours above and below to prove the slice — plus the padding maths that keeps content off both diagonals at once.
The canonical SaaS landing pattern: a full-viewport hero whose bottom edge is cut on a diagonal, with the headline, sub and two calls to action sitting well clear of the slope. Uses 100svh so mobile browser chrome cannot squash it, and the band below is deliberately flat so the cut is doing the transition rather than decorating it.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
Four feature columns with inline SVG icons inside an angled band. The grid is a plain auto-fit layout that knows nothing about the geometry — the section is clipped, the columns are not — which is the whole point: an angled band is a container decision, and everything inside it stays on the normal grid.
An angled band carrying three pricing cards — and the cards stay perfectly rectangular. That contrast is the design: the container is cut, the commerce is not. A monthly/yearly switch driven by :has() changes every price and note with no script, and the recommended tier is lifted by geometry rather than by another colour.
An angled band holding three testimonial cards that overlap each other by design — negative inline margins and explicit z-index, fanned into a stack on wide screens and unstacked into a readable column on narrow ones. Hovering or focusing a card brings it forward without moving its neighbours, so the overlap never costs you legibility.
The most common real-world use of the whole technique: the footer's top edge is cut on a diagonal so the page body flows into it instead of stopping at a line. Real footer content — four link columns, a newsletter field, legal row — with the cut sized so the first column heading never rides the slope, and a margin-block-start: auto pin so the footer sits at the bottom even on a short page.
The left and right ends of the same edge sit at different depths, so the cut is not a rotation of anything — it cannot be produced by skewY() at any angle. This is pure polygon() territory: two independent tokens, one per side, plus a mid-point that lets the edge change slope halfway across. The demo explains the constraint plainly, because that is the search behind it.
Four stacked bands in different colours whose cuts alternate direction, producing a staircase down the page. One --cut token, one polygon, and :nth-child(even) flipping the slope — which is the whole trick: alternating the sign is what keeps a multi-band stack rhythmic instead of looking like a page that slid sideways.
An angled edge that still has a soft radius where it meets the edge of the viewport. clip-path: polygon() cannot round a corner — that is the constraint behind the query — so the shipped technique is a border-radius wrapper with overflow: clip, and the demo also shows the clip-path: path() alternative with real arc commands, plus exactly why that one does not scale.
An angled gradient band that overlaps the section above it and blends where the two meet — the overlap is the subject, not decoration. The gradient rides a pseudo-element with mix-blend-mode: multiply, so the cards it crosses stay visible through it and the intersection does real work; the band's own text sits above the blend and stays crisp.
Newsletter
Enjoying these demos? Get new ones by email.
A short email when a new collection ships. Unsubscribe any time.
Four KPI figures in an angled band, counting up the first time the section scrolls into view. The final values are in the HTML, so with JavaScript disabled — or before the observer fires — the numbers are already correct; the script only animates from a lower start. Under prefers-reduced-motion nothing counts at all.
The troubleshooting demo: two angled sections deliberately overlapped with negative margins, shown broken and fixed side by side. Broken, the second band swallows the first one's copy because z-index does nothing on a statically positioned element. Fixed, position: relative plus an explicit index decides the join — and the copy explains the stacking-context trap that makes the fix look like it failed.
The other high-intent troubleshooting demo: a fixed deg angle looks composed at 1440px and absurd at 360px, and a vw-based skew pushes the document sideways. Broken and fixed are shown side by side — the fix scales the cut with clamp() so the slope stays proportional, and the copy explains why overflow-x: hidden on body is the wrong answer and overflow: clip on the section is the right one.
The angle itself animates: the band's cut steepens as the section scrolls into view and flattens as it leaves, driven by animation-timeline: view() where that is supported and by a one-shot IntersectionObserver transition where it is not. Under prefers-reduced-motion the band renders at its final angle and nothing moves at all.
An angled photo band whose background moves at a different rate as you scroll. The shipped technique is a transform-driven layer inside an overflow: clip band — background-attachment: fixed is demonstrated too, with an honest note about its iOS Safari failure and its scroll cost. Under prefers-reduced-motion the layer is pinned and the band is a plain photograph.
The same straight diagonal edge, drawn with an inline SVG polygon instead of clip-path. preserveAspectRatio="none" stretches the shape to any width so the slope is whatever you specify, at every viewport, with no sub-pixel seam and no dependency on clip-path support. The howItWorks explains exactly when SVG beats a clip — and when it does not.
FAQ
Frequently asked questions
How do I make a diagonal section in CSS?
Two techniques dominate, and they fail in different ways. clip-path: polygon() cuts the section's own box — you give it four corner points and shift one edge, so polygon(0 0, 100% 0, 100% calc(100% - 5vw), 0 100%) produces a bottom edge that rises 5vw across the width. Nothing inside the section rotates, so text stays upright with no counter-transform needed. The alternative is transform: skewY(-3deg) on the band with skewY(3deg) on an inner wrapper to unskew the content. Skew has broader legacy support and lets the background bleed past the cut, but every child needs the counter-transform and the rotated box grows taller than you authored. For new work use clip-path — support is universal in evergreen browsers and the mental model is far simpler. Drive the angle from one custom property so a consumer changes it in a single place rather than hunting through four polygon coordinates.
Why does my diagonal section cause horizontal scrolling on mobile?
Almost always because the angle is expressed in a fixed unit that does not shrink with the viewport, or because a skew pushes the box wider than 100%. A skewY(-6deg) that looks elegant at 1440px becomes a near-vertical cliff at 360px, and the rotated corners can extend past the viewport edge and take the document sideways. Two fixes work together. First, express the angle in viewport units or clamp it — --cut: clamp(1.5rem, 5vw, 6rem) — so the cut scales with the width instead of dominating a narrow screen. Second, put overflow: clip on the section itself rather than reaching for overflow-x: hidden on body. The body-level fix is the common advice and it is the wrong one: it silently breaks position: sticky anywhere else on the page, because a scroll container is created on the root. clip contains the spill without creating that container.
How do I keep text upright inside a skewed section?
Counter-skew the content by the exact inverse of the parent. If the section is transform: skewY(-4deg), an inner wrapper takes transform: skewY(4deg) and everything inside it renders level again. The wrapper must be a direct child that holds all the content — skew propagates through the subtree, so skewing the section and unskewing individual paragraphs leaves images and buttons still tilted. Two consequences catch people out. The rotated box occupies more vertical space than its authored height, so the section grows and your padding no longer reads as you set it; compensate by reducing block padding. And because both transforms create containing blocks, any position: fixed descendant will anchor to the skewed element rather than the viewport. If you only want the background angled and not the layout, clip-path avoids the whole problem — nothing rotates, so no counter-transform is needed at all.
Why is my content hidden behind the next diagonal section?
Because the sections overlap and the later one wins by document order. Angled bands are usually pulled together with negative margins so the cut edges meet, which means one section's box genuinely sits on top of the other's content. z-index looks like the fix and often does nothing, because it only applies to positioned elements — add position: relative to both sections first, then set explicit z-index values. The subtler trap is stacking contexts. A transform, filter, opacity below 1, or will-change on an ancestor creates a new stacking context, and a child's z-index is then confined inside it no matter how large the number. That is exactly why the skew technique produces this bug more often than clip-path: the skew itself creates the context. Check the ancestor chain in DevTools before escalating z-index values that cannot possibly win.
Should I use clip-path or SVG for the angled edge?
clip-path for most work, inline SVG when you need the edge to be exact. clip-path: polygon() is one declaration, animates cleanly, and needs no extra markup — for a straight diagonal it is almost always the right answer. Inline SVG earns its place in two situations. First, when the edge must stay pixel-crisp across every viewport: an SVG with preserveAspectRatio="none" stretches to any width without the sub-pixel rounding that can leave a faint seam where two clipped bands meet. Second, when the edge needs a fill that differs from the section behind it — a gradient or a second colour on the wedge itself — which a clip cannot express because clipping removes pixels rather than painting them. The SVG approach costs you extra markup and an aria-hidden attribute, since the shape is decorative. Both are fully responsive; neither requires JavaScript.
How do I avoid a thin line between two diagonal sections?
That hairline is sub-pixel rounding, not a mistake in your polygon. When two bands meet on an angle at a fractional device pixel ratio, the browser rounds each edge independently and a sliver of the page background shows through. The reliable fix is to overlap slightly: pull the lower section up by a pixel with margin-block-start: -1px, or extend its clip path a fraction past the join. A second approach is to give the two sections the same background and let the cut be implied by a foreground element, which sidesteps the join entirely. Avoid trying to solve it by rounding the angle to whole degrees — the seam depends on the rendered pixel grid, not on your authored value, so it appears at some zoom levels and not others. Always test at 110% and 125% browser zoom, since those are the ratios where the artefact shows up most reliably.
Are diagonal sections accessible?
They can be, and the risks are mostly about text near the cut. Content must never sit so close to the angled edge that it clips at narrow widths — the safe approach is padding that scales with the angle, so as the cut deepens the text is pushed clear of it. Check contrast against the band's own fill rather than the page background, since an angled section usually introduces a new colour behind the text. Decorative overlays and SVG edges take aria-hidden="true", because a screen reader announcing a shape adds nothing. Use real section elements with a heading each: the angle is presentation, but the band is a genuine landmark in the document and should be reachable as one. Finally, any demo that animates its angle on scroll must be fully static under prefers-reduced-motion; a shape that morphs as the user scrolls is exactly the kind of motion that setting exists to suppress.
How do these port to React, Vue, Svelte and Tailwind?
The CSS ports unchanged — every .dgs-NN selector and custom property is framework-agnostic, and 17 of the 20 designs need no JavaScript at all. The natural component boundary is a single AngledSection wrapper that accepts the angle, the direction of the cut, and which edges are angled, then renders a section with the clip path composed from those props. Because the angle is a custom property rather than a hardcoded polygon, a consumer overrides it inline without touching the stylesheet. For Tailwind, the clip path needs an arbitrary value — [clip-path:polygon(0_0,100%_0,100%_92%,0_100%)] — which is unreadable inline, so it belongs in a component class or a plugin utility rather than scattered through markup. The three JavaScript demos are small init functions: useEffect with a cleanup return in React, onMounted plus onBeforeUnmount in Vue, onMount returning cleanup in Svelte. The scroll-driven one uses animation-timeline: view() where supported, so in modern browsers it needs no script at all.