ui-aesthetic-utils
Version:
A lightweight utility library for modern UI aesthetics including glassmorphism, neumorphism, glow effects, and soft shadows
527 lines (517 loc) • 15.2 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
var vue = require('vue');
/**
* Default glass configuration
*/
const defaultGlassConfig = {
blur: {
sm: '8px',
md: '15px',
lg: '25px',
xl: '40px'
}};
/**
* Resolves blur size to pixel value
*/
function resolveBlurSize(blur, config) {
if (typeof blur === 'string' && blur.endsWith('px')) {
return blur;
}
const blurConfig = defaultGlassConfig.blur;
switch (blur) {
case 'sm':
return blurConfig.sm;
case 'md':
return blurConfig.md;
case 'lg':
return blurConfig.lg;
case 'xl':
return blurConfig.xl;
default:
return typeof blur === 'string' ? blur : blurConfig.md;
}
}
/**
* Generates glassmorphism CSS properties
*/
function generateGlassStyles(options = {}) {
const { blur = 'md', opacity = 0.6, background, border, borderRadius = '12px' } = options;
const blurValue = resolveBlurSize(blur);
const finalBackground = background || `rgba(255, 255, 255, ${opacity})`;
const finalBorder = border || `1px solid rgba(255, 255, 255, ${Math.min(opacity + 0.1, 1)})`;
return {
backdropFilter: `blur(${blurValue})`,
WebkitBackdropFilter: `blur(${blurValue})`, // Safari support
background: finalBackground,
border: finalBorder,
borderRadius,
position: 'relative',
overflow: 'hidden'
};
}
/**
* Default neumorphic configuration
*/
const defaultNeumorphicConfig = {
baseColor: '#e0e5ec',
lightSource: '145deg',
darkShadow: 'rgba(163, 177, 198, 0.6)',
lightShadow: 'rgba(255, 255, 255, 0.8)',
depth: {
sm: '2px 2px 4px',
md: '4px 4px 8px',
lg: '8px 8px 16px',
xl: '12px 12px 24px'
}
};
/**
* Resolves depth size to shadow values
*/
function resolveNeumorphicDepth(depth, config) {
const depthConfig = config?.neumorphic.depth || defaultNeumorphicConfig.depth;
switch (depth) {
case 'sm':
return depthConfig.sm;
case 'md':
return depthConfig.md;
case 'lg':
return depthConfig.lg;
case 'xl':
return depthConfig.xl;
default:
return depthConfig.md;
}
}
/**
* Generates neumorphic shadow based on shape and depth
*/
function generateNeumorphicShadow(shape, depth, config) {
const neuroConfig = config?.neumorphic || defaultNeumorphicConfig;
const depthValue = resolveNeumorphicDepth(depth, config);
const [x, y, blur] = depthValue.split(' ');
const darkShadow = `${x} ${y} ${blur} ${neuroConfig.darkShadow}`;
const lightShadow = `${x.replace('-', '')} ${y.replace('-', '')} ${blur} ${neuroConfig.lightShadow}`;
switch (shape) {
case 'flat':
return `${darkShadow}, -${lightShadow}`;
case 'concave':
return `inset ${darkShadow}, inset -${lightShadow}`;
case 'convex':
return `${darkShadow}, -${lightShadow}, inset 1px 1px 2px ${neuroConfig.lightShadow}`;
default:
return `${darkShadow}, -${lightShadow}`;
}
}
/**
* Generates neumorphism CSS properties
*/
function generateNeumorphicStyles(options = {}) {
const { depth = 'md', shape = 'flat', baseColor, borderRadius = '12px' } = options;
const config = {
neumorphic: {
...defaultNeumorphicConfig,
...(baseColor && { baseColor })
}
};
const boxShadow = generateNeumorphicShadow(shape, depth, config);
const background = baseColor || defaultNeumorphicConfig.baseColor;
return {
background,
borderRadius,
boxShadow,
border: 'none',
position: 'relative',
cursor: shape === 'flat' ? 'pointer' : 'default'
};
}
/**
* Default glow configuration
*/
const defaultGlowConfig = {
primary: '#3b82f6'};
/**
* Resolves glow intensity to blur and opacity values
*/
function resolveGlowIntensity(intensity) {
switch (intensity) {
case 'subtle':
return { blur: 10, opacity: 0.3, spread: 0 };
case 'medium':
return { blur: 20, opacity: 0.5, spread: 0 };
case 'strong':
return { blur: 30, opacity: 0.7, spread: 2 };
case 'neon':
return { blur: 40, opacity: 0.9, spread: 4 };
default:
return { blur: 20, opacity: 0.5, spread: 0 };
}
}
/**
* Converts color to rgba format with opacity
*/
function colorToRgba(color, opacity) {
// Handle hex colors
if (color.startsWith('#')) {
const hex = color.replace('#', '');
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
}
// Handle rgb/rgba colors
if (color.startsWith('rgb')) {
const match = color.match(/\d+/g);
if (match && match.length >= 3) {
return `rgba(${match[0]}, ${match[1]}, ${match[2]}, ${opacity})`;
}
}
// Handle named colors (basic support)
const namedColors = {
red: '255, 0, 0',
green: '0, 255, 0',
blue: '0, 0, 255',
white: '255, 255, 255',
black: '0, 0, 0'
};
if (namedColors[color.toLowerCase()]) {
return `rgba(${namedColors[color.toLowerCase()]}, ${opacity})`;
}
// Fallback - return as is (might not work for all cases)
return color;
}
/**
* Generates glow CSS properties
*/
function generateGlowStyles(options = {}) {
const { color = defaultGlowConfig.primary, intensity = 'medium', spread, blur, inset = false } = options;
const resolvedIntensity = resolveGlowIntensity(intensity);
const finalBlur = blur ?? resolvedIntensity.blur;
const finalSpread = spread ?? resolvedIntensity.spread;
const glowColor = colorToRgba(color, resolvedIntensity.opacity);
const shadowPrefix = inset ? 'inset ' : '';
const boxShadow = `${shadowPrefix}0 0 ${finalBlur}px ${finalSpread}px ${glowColor}`;
return {
boxShadow,
transition: 'box-shadow 0.3s ease-in-out'
};
}
/**
* Default shadow configuration
*/
const defaultShadowConfig = {
elevation: {
sm: '0 1px 3px rgba(0, 0, 0, 0.1)',
md: '0 4px 12px rgba(0, 0, 0, 0.15)',
lg: '0 8px 24px rgba(0, 0, 0, 0.2)',
xl: '0 16px 48px rgba(0, 0, 0, 0.25)',
xxl: '0 32px 64px rgba(0, 0, 0, 0.3)'
}};
/**
* Resolves shadow size to shadow values
*/
function resolveShadowSize(size, config) {
const shadowConfig = config?.shadow.elevation || defaultShadowConfig.elevation;
switch (size) {
case 'sm':
return shadowConfig.sm;
case 'md':
return shadowConfig.md;
case 'lg':
return shadowConfig.lg;
case 'xl':
return shadowConfig.xl;
case 'xxl':
return shadowConfig.xxl;
default:
return shadowConfig.md;
}
}
/**
* Adjusts shadow opacity based on softness
*/
function adjustShadowSoftness(shadowValue, softness) {
const softnessMultiplier = {
soft: 0.7,
medium: 1.0,
hard: 1.3
};
const multiplier = softnessMultiplier[softness] || 1.0;
// Extract rgba opacity and adjust it
return shadowValue.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/g, (match, r, g, b, a) => {
const newOpacity = Math.min(parseFloat(a) * multiplier, 1);
return `rgba(${r}, ${g}, ${b}, ${newOpacity})`;
});
}
/**
* Generates directional shadow based on direction
*/
function generateDirectionalShadow(baseShadow, direction) {
// Parse shadow values (assumes format: "x y blur spread color")
const shadowParts = baseShadow.split(' ');
if (shadowParts.length < 3)
return baseShadow;
const [x, y, blur, ...rest] = shadowParts;
const spread = rest.length > 1 ? rest[0] : '0';
const color = rest.length > 1 ? rest.slice(1).join(' ') : rest[0];
let newX = x;
let newY = y;
switch (direction) {
case 'top':
newY = `-${Math.abs(parseInt(y))}px`;
newX = '0';
break;
case 'bottom':
newY = `${Math.abs(parseInt(y))}px`;
newX = '0';
break;
case 'left':
newX = `-${Math.abs(parseInt(x))}px`;
newY = '0';
break;
case 'right':
newX = `${Math.abs(parseInt(x))}px`;
newY = '0';
break;
case 'center':
newX = '0';
newY = '0';
break;
}
return `${newX} ${newY} ${blur} ${spread !== '0' ? spread + ' ' : ''}${color}`;
}
/**
* Generates shadow CSS properties
*/
function generateShadowStyles(options = {}) {
const { elevation = 'md', softness = 'soft', direction = 'center', color } = options;
let shadowValue = resolveShadowSize(elevation);
// Adjust softness
shadowValue = adjustShadowSoftness(shadowValue, softness);
// Apply custom color if provided
if (color) {
shadowValue = shadowValue.replace(/rgba\(\d+,\s*\d+,\s*\d+,\s*[\d.]+\)/g, color);
}
// Apply direction
shadowValue = generateDirectionalShadow(shadowValue, direction);
return {
boxShadow: shadowValue,
transition: 'box-shadow 0.3s ease'
};
}
// Vue Prop Definitions
const vueGlassPanelProps = {
blur: {
type: String,
default: 'md'
},
opacity: {
type: Number,
default: 0.6
},
border: String,
borderRadius: String,
background: String,
class: String,
style: Object
};
const vueNeumorphicButtonProps = {
depth: {
type: String,
default: 'md'
},
shape: {
type: String,
default: 'flat'
},
size: {
type: String,
default: 'md'
},
disabled: {
type: Boolean,
default: false
},
variant: {
type: String,
default: 'primary'
},
class: String,
style: Object
};
const vueGlowBoxProps = {
color: String,
intensity: {
type: String,
default: 'medium'
},
spread: Number,
blur: Number,
animate: {
type: Boolean,
default: false
},
hoverEffect: {
type: Boolean,
default: true
},
class: String,
style: Object
};
const vueShadowContainerProps = {
elevation: {
type: String,
default: 'md'
},
softness: {
type: String,
default: 'soft'
},
direction: {
type: String,
default: 'center'
},
color: String,
borderRadius: String,
class: String,
style: Object
};
/**
* Vue GlassPanel Component
*/
const GlassPanel = vue.defineComponent({
name: 'GlassPanel',
props: vueGlassPanelProps,
setup(props, { slots }) {
const styles = vue.computed(() => {
return generateGlassStyles({
blur: props.blur,
opacity: props.opacity,
border: props.border,
borderRadius: props.borderRadius,
background: props.background
});
});
return () => vue.h('div', {
class: props.class,
style: { ...styles.value, ...props.style }
}, slots.default?.());
}
});
/**
* Vue NeumorphicButton Component
*/
const NeumorphicButton = vue.defineComponent({
name: 'NeumorphicButton',
props: vueNeumorphicButtonProps,
emits: ['click'],
setup(props, { slots, emit }) {
const isHovered = vue.ref(false);
const isPressed = vue.ref(false);
const styles = vue.computed(() => {
return generateNeumorphicStyles({
depth: props.depth,
shape: isPressed.value ? 'concave' : props.shape
});
});
const handleClick = () => {
if (!props.disabled) {
emit('click');
}
};
return () => vue.h('button', {
class: props.class,
style: { ...styles.value, ...props.style },
disabled: props.disabled,
onClick: handleClick,
onMouseenter: () => (isHovered.value = true),
onMouseleave: () => {
isHovered.value = false;
isPressed.value = false;
},
onMousedown: () => (isPressed.value = true),
onMouseup: () => (isPressed.value = false)
}, slots.default?.());
}
});
/**
* Vue GlowBox Component
*/
const GlowBox = vue.defineComponent({
name: 'GlowBox',
props: vueGlowBoxProps,
setup(props, { slots }) {
const isHovered = vue.ref(false);
const styles = vue.computed(() => {
const baseStyles = generateGlowStyles({
color: props.color,
intensity: props.intensity,
spread: props.spread,
blur: props.blur
});
if (props.hoverEffect && isHovered.value) {
return generateGlowStyles({
color: props.color,
intensity: 'strong',
spread: props.spread,
blur: props.blur
});
}
return baseStyles;
});
return () => vue.h('div', {
class: [props.class, props.animate ? 'glow-animate' : ''],
style: { ...styles.value, ...props.style },
onMouseenter: () => (isHovered.value = true),
onMouseleave: () => (isHovered.value = false)
}, slots.default?.());
}
});
/**
* Vue ShadowContainer Component
*/
const ShadowContainer = vue.defineComponent({
name: 'ShadowContainer',
props: vueShadowContainerProps,
setup(props, { slots }) {
const styles = vue.computed(() => {
return generateShadowStyles({
elevation: props.elevation,
softness: props.softness,
direction: props.direction,
color: props.color
});
});
return () => vue.h('div', {
class: props.class,
style: {
...styles.value,
borderRadius: props.borderRadius,
...props.style
}
}, slots.default?.());
}
});
// Export all components
var index = {
GlassPanel,
NeumorphicButton,
GlowBox,
ShadowContainer
};
// Vue plugin for global registration
const UIAestheticUtilsPlugin = {
install(app) {
app.component('GlassPanel', GlassPanel);
app.component('NeumorphicButton', NeumorphicButton);
app.component('GlowBox', GlowBox);
app.component('ShadowContainer', ShadowContainer);
}
};
exports.GlassPanel = GlassPanel;
exports.GlowBox = GlowBox;
exports.NeumorphicButton = NeumorphicButton;
exports.ShadowContainer = ShadowContainer;
exports.UIAestheticUtilsPlugin = UIAestheticUtilsPlugin;
exports.default = index;
//# sourceMappingURL=index.cjs.js.map
;