Masks
Fade an edge, spotlight an image, or stack both without writing mask-image gradients by hand.
Fading the bottom of a hero used to mean writing the same gradient twice: once for maskImage, once for
-webkit-mask-image.
Now you set a stop.
css({ maskBottomFrom: '40%' })It's solid through 40%, then it fades to transparent at the bottom. One prop. The -webkit- prefix is already there.
Fading one edge
From is where the fade starts. To is where it finishes. Skip To and it defaults to 100%.
css({ overflow: 'auto', maskBottomFrom: '80%' })
css({ maskBottomFrom: '20%', maskBottomTo: '80%' })| Prop | Fades toward |
|---|---|
maskTopFrom | top |
maskRightFrom | right |
maskBottomFrom | bottom |
maskLeftFrom | left |
Each one has a matching To. Values are spacing tokens or raw lengths (8, 20%, 2rem).
Fading both sides
maskX and maskY are two edges, not one gradient. Left and right. Top and bottom.
css({ overflowX: 'auto', maskXFrom: '8', maskXTo: '90%' })
css({ maskYFrom: '15%', maskYTo: '85%' })Pick maskXFrom or maskLeftFrom, not both. They write the same CSS variables. Stylesheet sort order decides the
winner, not the order you wrote the props.
Stacking a fade and a spotlight
Edge fades live on the linear layer. Radial and conic are separate layers. The browser intersects them, so a bottom fade plus a radial spotlight keeps both.
css({
maskBottomFrom: '50%',
maskRadialFrom: '35%',
maskRadialAt: 'center'
})Fade the footer. Keep a soft circle of the photo.
maskBottomFrom and maskLinear both write the linear layer. Use one or the other. Stacking those two does not add a
second fade. The later class overwrites the first.
Using a linear mask
maskLinear is an angle, or the same to-t / to-br map as bgGradient.
css({ maskLinear: '45', maskLinearFrom: '30%' })
css({ maskLinear: 'to-b', maskLinearFrom: '40%', maskLinearTo: '80%' })A bare number on maskLinear is degrees: '45' means 45deg. A bare number on maskLinearFrom is spacing. That split
is easy to mix up.
Using a radial mask
maskRadialAt is the center of the gradient. It is not maskPosition. maskPosition moves a maskImage.
css({
maskRadialFrom: '20%',
maskRadialAt: 'center',
maskRadialShape: 'circle',
maskRadialSize: 'farthest-side'
})maskRadialAt accepts center, top, top left, 30% 30%, and the rest of the background-position syntax. Write
the CSS — there is no topLeft alias.
Using a conic mask
css({
maskConic: '45',
maskConicFrom: '20%',
maskConicTo: '80%'
})Same degree rule as maskLinear: '45' means 45deg.
Changing stop colors
Stops default to black, then transparent. That is what you want for a normal fade. Override the color with a separate
prop, not by stuffing a color into From.
css({
maskBottomFrom: '25%',
maskBottomFromColor: 'transparent',
maskBottomToColor: 'black'
})Every From / To has a matching FromColor / ToColor: maskBottomFromColor, maskLinearToColor,
maskRadialFromColor, maskConicToColor. Color tokens and opacity modifiers work (red.500, black/50).
Using a raw mask image
A PNG, an SVG, or a gradient you already have goes on maskImage. That is the escape hatch. It replaces the fade
layers. Do not mix it with maskBottomFrom.
css({
maskImage: 'url(/scribble.png)',
maskSize: 'cover',
maskPosition: 'center',
maskRepeat: 'no-repeat'
})Using CSS mask properties
These map 1:1 to CSS. Each one also emits the -webkit- prefix.
| Prop | CSS property | Values |
|---|---|---|
mask | mask | shorthand |
maskImage | mask-image | none, url(...), any image |
maskSize | mask-size | auto, cover, contain, any length |
maskPosition | mask-position | center, top left, 50% 50%, … |
maskRepeat | mask-repeat | repeat, no-repeat, repeat-x, repeat-y, … |
maskClip | mask-clip | border-box, padding-box, content-box, … |
maskOrigin | mask-origin | border-box, padding-box, content-box, … |
maskComposite | mask-composite | add, subtract, intersect, exclude |
maskMode | mask-mode | alpha, luminance, match-source |
maskType | mask-type | alpha, luminance (SVG <mask> only) |
Fade helpers composite as intersect. Set maskComposite to change that — it wins wherever you put it, because the
helpers read the same variable.
css({
maskBottomFrom: '50%',
maskRadialFrom: '70%',
maskComposite: 'add'
})Under strict tokens
Stops resolve against spacing, so a raw length needs the escape hatch.
css({ maskBottomFrom: '4' }) // spacing token
css({ maskBottomFrom: '[20%]' }) // raw percentageColors work the same way: maskBottomFromColor: 'red.500', or '[rebeccapurple]'.
What does not compose
maskImageand the fade helpers. Both writemask-image, so stylesheet sort order picks the winner, not the order you wrote the props. Pick one.maskBottomFromandmaskLinear. Same layer. One overwrites the other.maskXFromandmaskLeftFrom. Same variables. Pick one.- Logical edges. There is no
maskStartFromormaskEndFrom. Fade direction is visual: top, right, bottom, left.
Start with maskBottomFrom. Add a second helper only when one edge is not enough.
How it works
The helpers write CSS variables — --mask-linear, --mask-bottom-from-position, and friends — which Panda registers
with @property. Two things follow from that.
Registrations are inherits: false, so a fade on a parent cannot leak into a child. And Panda only emits the
registrations your stylesheet actually references, so a project that never masks pays nothing for them.
You can set the variables yourself when a helper doesn't cover the case.
css({ maskBottomFrom: '50%', '--mask-bottom-from-color': 'rebeccapurple' })Full reference
Generated from @pandacss/preset-base, so this table always matches the utilities Panda ships.
| Property | Shorthand | Class | Values |
|---|---|---|---|
| mask | — | msk | — |
| maskBottomFrom | — | msk-b-from | spacing |
| maskBottomFromColor | — | msk-b-from-c | colors |
| maskBottomTo | — | msk-b-to | spacing |
| maskBottomToColor | — | msk-b-to-c | colors |
| maskClip | — | msk-cp | — |
| maskComposite | — | msk-cmp | — |
| maskConic | — | msk-conic | — |
| maskConicFrom | — | msk-conic-from | spacing |
| maskConicFromColor | — | msk-conic-from-c | colors |
| maskConicTo | — | msk-conic-to | spacing |
| maskConicToColor | — | msk-conic-to-c | colors |
| maskImage | — | msk-i | — |
| maskLeftFrom | — | msk-l-from | spacing |
| maskLeftFromColor | — | msk-l-from-c | colors |
| maskLeftTo | — | msk-l-to | spacing |
| maskLeftToColor | — | msk-l-to-c | colors |
| maskLinear | — | msk-linear | to-t | to-tr | to-r | to-br | to-b | to-bl + 2 more |
| maskLinearFrom | — | msk-linear-from | spacing |
| maskLinearFromColor | — | msk-linear-from-c | colors |
| maskLinearTo | — | msk-linear-to | spacing |
| maskLinearToColor | — | msk-linear-to-c | colors |
| maskMode | — | msk-md | — |
| maskOrigin | — | msk-o | — |
| maskPosition | — | msk-p | — |
| maskRadial | — | msk-radial | — |
| maskRadialAt | — | msk-radial-at | center | top | bottom | left | right | top left + 3 more |
| maskRadialFrom | — | msk-radial-from | spacing |
| maskRadialFromColor | — | msk-radial-from-c | colors |
| maskRadialShape | — | msk-radial-shape | circle | ellipse |
| maskRadialSize | — | msk-radial-sz | closest-side | closest-corner | farthest-side | farthest-corner |
| maskRadialTo | — | msk-radial-to | spacing |
| maskRadialToColor | — | msk-radial-to-c | colors |
| maskRepeat | — | msk-r | — |
| maskRightFrom | — | msk-r-from | spacing |
| maskRightFromColor | — | msk-r-from-c | colors |
| maskRightTo | — | msk-r-to | spacing |
| maskRightToColor | — | msk-r-to-c | colors |
| maskSize | — | msk-s | — |
| maskTopFrom | — | msk-t-from | spacing |
| maskTopFromColor | — | msk-t-from-c | colors |
| maskTopTo | — | msk-t-to | spacing |
| maskTopToColor | — | msk-t-to-c | colors |
| maskType | — | msk-t | — |
| maskXFrom | — | msk-x-from | spacing |
| maskXFromColor | — | msk-x-from-c | colors |
| maskXTo | — | msk-x-to | spacing |
| maskXToColor | — | msk-x-to-c | colors |
| maskYFrom | — | msk-y-from | spacing |
| maskYFromColor | — | msk-y-from-c | colors |
| maskYTo | — | msk-y-to | spacing |
| maskYToColor | — | msk-y-to-c | colors |