Mask Gradient Y
Fades its child out at the top and at the bottom, so content scrolls into transparency instead of stopping against a hard edge.
API
Props
| prop | type | default | |
|---|---|---|---|
children? | ReactElement | — | the element to mask; cloned, not wrapped |
fade? | string | "2rem" | length of both fades at once |
fadeTop? | string | — | length of the top fade; overrides fade |
fadeBottom? | string | — | length of the bottom fade; overrides fade |
offsetTop? | string | "0px" | where the top fade starts |
offsetBottom? | string | "0px" | where the bottom fade starts, from the end |
Examples
Usage
<MaskGradientY fade="3rem">
<div className="h-80 overflow-y-auto">…</div>
</MaskGradientY>row 1
row 2
row 3
row 4
row 5
row 6
row 7
row 8
row 9
row 10
row 11
row 12
Details
A mask, not an overlay
The usual way to fade a scroll area is a gradient overlay painted in the background colour, which only works while the background stays flat and opaque: put the same trick over an image, a glow or a themed surface and the seam shows.
Here the fade is a mask-image applied to the element itself, so the content becomes genuinely transparent rather than being covered. Whatever is behind shows through, in both themes, with no colour to keep in sync.
mask-image: linear-gradient(
to bottom,
transparent var(--offset-top),
#000 calc(var(--offset-top) + var(--fade-top)),
#000 calc(100% - var(--offset-bottom) - var(--fade-bottom)),
transparent calc(100% - var(--offset-bottom))
);The consequence worth knowing: the mask applies to the whole element, scrollbars included, and it cannot be undone locally. Anything that must stay fully opaque belongs outside the masked element.
Fade and offset
fade is the length over which the content dissolves; offset is where that dissolve begins. Keeping them separate is what allows a fade to start below a fixed header instead of at the top of the element.
<MaskGradientY offsetTop="var(--spacing-navbar)" fadeTop="0px" fadeBottom="var(--spacing-page-offset)">
<nav className="overflow-y-scroll">…</nav>
</MaskGradientY>This is the sidebar of this site: everything above the navbar height is fully transparent — offsetTop with a zero fadeTop gives a hard cut rather than a gradient — while the bottom dissolves over the page offset. Both take any CSS length, custom properties included, so the values can be tokens rather than numbers.
Only the axis it names
The component fades vertically and nothing else: there is no horizontal counterpart, because a horizontal fade almost always means the layout is hiding an overflow it should have solved. If you need one, the same mask written on the other axis is three lines of CSS.