Effects
Overview
Effect utilities help you apply visual effects to elements including box shadows, text shadows, opacity, and blend modes. These utilities create depth, hierarchy, and visual interest in your designs.
Why Use Effect Utilities?
Effect utilities help you:
- Create visual hierarchy: Use shadows to indicate elevation and depth
- Control transparency: Manage element opacity for overlays and fades
- Apply blend effects: Mix colors and images with blend modes
- Integrate with design tokens: Define consistent shadow values across your application
useBoxShadowUtility
The useBoxShadowUtility() function creates utility classes for applying box shadows.
import { styleframe } from "styleframe";
import { useBoxShadowUtility } from "@styleframe/theme";
const s = styleframe();
useBoxShadowUtility(s, {
sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
default: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
'2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
none: 'none',
});
export default s;
._box-shadow\:sm {
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow\:md {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow\:lg {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow\:xl {
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow\:2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow\:inner {
--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow\:none {
--tw-shadow: none;
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
<div class="_box-shadow:sm">Subtle shadow</div>
<div class="_box-shadow:md">Medium shadow</div>
<div class="_box-shadow:lg">Large shadow</div>
<div class="_box-shadow:inner">Inset shadow</div>
--tw-shadow) so it can be combined with ring utilities without conflicts.useBoxShadowColorUtility
The useBoxShadowColorUtility() function creates utility classes for setting shadow colors.
import { styleframe } from "styleframe";
import { useBoxShadowColorUtility } from "@styleframe/theme";
import { useColor } from "@styleframe/theme";
const s = styleframe();
const { ref } = s;
const { colorPrimary } = useColor(s, { primary: '#006cff' } as const);
useBoxShadowColorUtility(s, {
primary: ref(colorPrimary),
black: 'rgb(0 0 0 / 0.25)',
});
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
}
._box-shadow-color\:primary { --tw-shadow-color: var(--color--primary); }
._box-shadow-color\:black { --tw-shadow-color: rgb(0 0 0 / 0.25); }
<div class="_box-shadow:lg _box-shadow-color:primary">Colored shadow</div>
useOpacityUtility
The useOpacityUtility() function creates utility classes for controlling element opacity.
import { styleframe } from "styleframe";
import { useOpacityUtility } from "@styleframe/theme";
const s = styleframe();
useOpacityUtility(s, {
'0': '0',
'5': '0.05',
'10': '0.1',
'20': '0.2',
'25': '0.25',
'30': '0.3',
'40': '0.4',
'50': '0.5',
'60': '0.6',
'70': '0.7',
'75': '0.75',
'80': '0.8',
'90': '0.9',
'95': '0.95',
'100': '1',
});
export default s;
._opacity\:0 { opacity: 0; }
._opacity\:5 { opacity: 0.05; }
._opacity\:10 { opacity: 0.1; }
._opacity\:20 { opacity: 0.2; }
._opacity\:25 { opacity: 0.25; }
._opacity\:30 { opacity: 0.3; }
._opacity\:40 { opacity: 0.4; }
._opacity\:50 { opacity: 0.5; }
._opacity\:60 { opacity: 0.6; }
._opacity\:70 { opacity: 0.7; }
._opacity\:75 { opacity: 0.75; }
._opacity\:80 { opacity: 0.8; }
._opacity\:90 { opacity: 0.9; }
._opacity\:95 { opacity: 0.95; }
._opacity\:100 { opacity: 1; }
<div class="_opacity:50">50% opacity</div>
<div class="_opacity:75">75% opacity</div>
<button class="_opacity:50 _hover:opacity:100">Fade on hover</button>
useTextShadowUtility
The useTextShadowUtility() function creates utility classes for applying text shadows.
import { styleframe } from "styleframe";
import { useTextShadowUtility } from "@styleframe/theme";
const s = styleframe();
useTextShadowUtility(s, {
sm: '0 1px 2px rgb(0 0 0 / 0.05)',
default: '0 1px 3px rgb(0 0 0 / 0.1)',
md: '0 2px 4px rgb(0 0 0 / 0.1)',
lg: '0 4px 8px rgb(0 0 0 / 0.15)',
none: 'none',
});
export default s;
._text-shadow\:sm { text-shadow: 0 1px 2px rgb(0 0 0 / 0.05); }
._text-shadow { text-shadow: 0 1px 3px rgb(0 0 0 / 0.1); }
._text-shadow\:md { text-shadow: 0 2px 4px rgb(0 0 0 / 0.1); }
._text-shadow\:lg { text-shadow: 0 4px 8px rgb(0 0 0 / 0.15); }
._text-shadow\:none { text-shadow: none; }
<h1 class="_text-shadow">Subtle text shadow</h1>
<h1 class="_text-shadow:lg">Large text shadow</h1>
useMixBlendModeUtility
The useMixBlendModeUtility() function creates utility classes for controlling how an element blends with its background.
import { styleframe } from "styleframe";
import { useMixBlendModeUtility } from "@styleframe/theme";
const s = styleframe();
useMixBlendModeUtility(s, {
normal: 'normal',
multiply: 'multiply',
screen: 'screen',
overlay: 'overlay',
darken: 'darken',
lighten: 'lighten',
'color-dodge': 'color-dodge',
'color-burn': 'color-burn',
'hard-light': 'hard-light',
'soft-light': 'soft-light',
difference: 'difference',
exclusion: 'exclusion',
hue: 'hue',
saturation: 'saturation',
color: 'color',
luminosity: 'luminosity',
});
export default s;
._mix-blend-mode\:normal { mix-blend-mode: normal; }
._mix-blend-mode\:multiply { mix-blend-mode: multiply; }
._mix-blend-mode\:screen { mix-blend-mode: screen; }
._mix-blend-mode\:overlay { mix-blend-mode: overlay; }
._mix-blend-mode\:darken { mix-blend-mode: darken; }
._mix-blend-mode\:lighten { mix-blend-mode: lighten; }
._mix-blend-mode\:color-dodge { mix-blend-mode: color-dodge; }
._mix-blend-mode\:color-burn { mix-blend-mode: color-burn; }
._mix-blend-mode\:hard-light { mix-blend-mode: hard-light; }
._mix-blend-mode\:soft-light { mix-blend-mode: soft-light; }
._mix-blend-mode\:difference { mix-blend-mode: difference; }
._mix-blend-mode\:exclusion { mix-blend-mode: exclusion; }
._mix-blend-mode\:hue { mix-blend-mode: hue; }
._mix-blend-mode\:saturation { mix-blend-mode: saturation; }
._mix-blend-mode\:color { mix-blend-mode: color; }
._mix-blend-mode\:luminosity { mix-blend-mode: luminosity; }
<div class="_mix-blend-mode:multiply">Multiply blend</div>
<div class="_mix-blend-mode:screen">Screen blend</div>
<img class="_mix-blend-mode:overlay" src="image.jpg" alt="Blended image">
useBackgroundBlendModeUtility
The useBackgroundBlendModeUtility() function creates utility classes for controlling how background layers blend together.
import { styleframe } from "styleframe";
import { useBackgroundBlendModeUtility } from "@styleframe/theme";
const s = styleframe();
useBackgroundBlendModeUtility(s, {
normal: 'normal',
multiply: 'multiply',
screen: 'screen',
overlay: 'overlay',
darken: 'darken',
lighten: 'lighten',
});
export default s;
._background-blend-mode\:normal { background-blend-mode: normal; }
._background-blend-mode\:multiply { background-blend-mode: multiply; }
._background-blend-mode\:screen { background-blend-mode: screen; }
._background-blend-mode\:overlay { background-blend-mode: overlay; }
._background-blend-mode\:darken { background-blend-mode: darken; }
._background-blend-mode\:lighten { background-blend-mode: lighten; }
<div class="_background-blend-mode:multiply" style="background: url(image.jpg), blue;">
Background layers multiply
</div>
Examples
Card with Elevation
import { styleframe } from "styleframe";
import { useBoxShadowUtility } from "@styleframe/theme";
const s = styleframe();
const { selector, modifier } = s;
useBoxShadowUtility(s, {
sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
default: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
});
// Create hover modifier
const hover = modifier('hover', ({ declarations }) => ({
'&:hover': declarations,
}));
// Apply shadow with hover state
useBoxShadowUtility(s, { lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)' }, [hover]);
export default s;
._box-shadow\:sm {
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._box-shadow\:lg {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
._hover\:box-shadow\:lg:hover {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
Usage in HTML:
<div class="_box-shadow _hover:box-shadow:lg" style="transition: box-shadow 0.2s">
Hover me for elevation
</div>
Faded Overlay
import { styleframe } from "styleframe";
import { useOpacityUtility } from "@styleframe/theme";
import { useBackgroundColorUtility } from "@styleframe/theme";
const s = styleframe();
useBackgroundColorUtility(s, {
black: '#000',
});
useOpacityUtility(s, {
'50': '0.5',
'75': '0.75',
});
export default s;
._bg\:black { background-color: #000; }
._opacity\:50 { opacity: 0.5; }
._opacity\:75 { opacity: 0.75; }
Usage in HTML:
<div class="image-container" style="position: relative;">
<img src="hero.jpg" alt="Hero">
<div class="_bg:black _opacity:50" style="position: absolute; inset: 0;">
<!-- Semi-transparent overlay -->
</div>
</div>
Best Practices
- Use consistent shadow scales: Define a shadow scale (sm, default, md, lg, xl) for consistent elevation
- Consider dark mode: Shadows may need adjustment for dark themes where they can look harsh
- Animate with care: Box shadows can be expensive to animate; consider using transform for hover effects when possible
- Use opacity for overlays: Opacity utilities work well for modal overlays and image tints
- Test blend modes: Blend mode effects vary significantly based on the colors involved
- Combine with transitions: Add CSS transitions for smooth shadow and opacity changes
FAQ
--tw-shadow custom property allows shadows to be combined with ring utilities. Both use box-shadow, so the custom property approach lets them coexist without overwriting each other.mix-blend-mode controls how an element blends with elements behind it. background-blend-mode controls how an element's background layers (image, color, gradient) blend with each other.useBoxShadowColorUtility to set shadow colors. This works best when combined with shadow utilities that use the --tw-shadow-color custom property.