Found on Codepen, with some modifications. You can see the effect on this site’s aside cards and nav.
Implementation Approach
The main idea is to use multiple layers — four in total:
1 2 3 4 5 6 7 8 <div class ="liquidGlass-wrapper" > <div class ="liquidGlass-effect" > </div > <div class ="liquidGlass-tint" > </div > <div class ="liquidGlass-shine" > </div > <div class ="liquidGlass-box" > </div > </div >
liquidGlass-wrapper
Container shell
1 2 3 4 5 6 7 8 .liquidGlass-wrapper { box-shadow : 0 6px 6px rgba (0 , 0 , 0 , 0.2 ), 0 0 20px rgba (0 , 0 , 0 , 0.1 ) !important ; transition : all 0.4s cubic-bezier (0.175 , 0.885 , 0.32 , 2.2 ) !important ; background : none !important ; backdrop-filter : none !important ; -webkit-backdrop-filter : none !important ; border-style : none !important ; }
Purpose : Outermost container, responsible for overall shadow and animation transitions.
Key points :
background: none and backdrop-filter: none prevent it from producing its own blur, delegating blur to the inner .liquidGlass-effect.
Shadows simulate the 3D feel of glass.
transition makes hover radius and padding changes smoother.
liquidGlass-effect
Core “liquid” blur layer
1 2 3 4 5 6 7 8 9 10 .liquidGlass-effect { position : absolute; z-index : 0 ; inset : 0 ; backdrop-filter : blur (3px ); filter : url (#glass-distortion ); overflow : hidden; isolation : isolate; }
backdrop-filter: blur(3px) : Blurs the background content (e.g. other parts of the webpage) → this is the foundation of “frosted glass”.
filter: url(#glass-distortion) ⭐ This is the key to the “liquid” feel! It references an SVG filter (#glass-distortion), typically defined in the page <svg>, used to apply distortion, ripple, liquid flow effects to the image.
isolation: isolate : Ensures this layer creates a new stacking context, preventing the filter from affecting other elements.
glass-distortion
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <svg style ="display: none" > <filter id ="glass-distortion" x ="0%" y ="0%" width ="100%" height ="100%" filterUnits ="objectBoundingBox" > <feTurbulence type ="fractalNoise" baseFrequency ="0.01 0.01" numOctaves ="1" seed ="5" result ="turbulence" /> <feComponentTransfer in ="turbulence" result ="mapped" > <feFuncR type ="gamma" amplitude ="1" exponent ="10" offset ="0.5" /> <feFuncG type ="gamma" amplitude ="0" exponent ="1" offset ="0" /> <feFuncB type ="gamma" amplitude ="0" exponent ="1" offset ="0.5" /> </feComponentTransfer > <feGaussianBlur in ="turbulence" stdDeviation ="3" result ="softMap" /> <feSpecularLighting in ="softMap" surfaceScale ="5" specularConstant ="1" specularExponent ="100" lighting-color ="white" result ="specLight" > <fePointLight x ="-200" y ="-200" z ="300" /> </feSpecularLighting > <feComposite in ="specLight" operator ="arithmetic" k1 ="0" k2 ="1" k3 ="1" k4 ="0" result ="litImage" /> <feDisplacementMap in ="SourceGraphic" in2 ="softMap" scale ="150" xChannelSelector ="R" yChannelSelector ="G" /> </filter > </svg >
liquidGlass-tint
Color tint overlay
1 2 3 4 5 6 .liquidGlass-tint { z-index : 1 ; position : absolute; inset : 0 ; background : rgba (255 , 255 , 255 , 0.25 ); }
Overlays a semi-transparent white layer on top of the blur layer (can also be light blue, light gray, etc.).
Simulates the “reflective base color” of real glass, making text/content more readable.
z-index: 1 ensures it sits above the effect layer and below the shine layer.
liquidGlass-shine
Highlight layer
1 2 3 4 5 6 7 8 9 10 .liquidGlass-shine { position : absolute; inset : 0 ; z-index : 2 ; overflow : hidden; box-shadow : inset 2px 2px 1px 0 rgba (255 , 255 , 255 , 0.5 ), inset -1px -1px 1px 1px rgba (255 , 255 , 255 , 0.5 ); }
Uses inset box-shadow to simulate highlights and subtle reflections at the glass edges.
Top-right bright (+2px white light), bottom-left slightly darker (-1px white light) → creates a sense of directional lighting.
z-index: 2 placed at the top (but below content).
liquidGlass-box
Content layer
1 2 3 4 5 6 7 .liquidGlass-box { z-index : 3 ; position : relative; box-shadow : none !important ; }
Where actual content goes (e.g. avatar, text).
z-index: 3 ensures content is above all glass effect layers, clearly visible.
position: relative takes it out of normal flow while staying within the wrapper.
Interaction Effect (Hover Dynamics)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .liquidGlass-wrapper ,.liquidGlass-wrapper > div :not (.liquidGlass-box ) { padding : 0.4rem !important ; border-radius : var (--glass-border-radius) !important ; } .liquidGlass-wrapper :hover { padding : 0.6rem !important ; border-radius : calc (var (--glass-border-radius) + 1em ) !important ; } .liquidGlass-wrapper > div :not (.liquidGlass-box ):hover { border-radius : calc (var (--glass-border-radius) + 1em ) !important ; }
On hover :
Border radius increases (+1em) → simulates “liquid expansion” or “soft deformation”;
Padding increases → overall “breathing” feel;
All non-content layers sync border radius changes, maintaining visual consistency.
Applying to Butterfly Examples
Below, we use card_author (the author info card) as an example.
card_author.pug
Modify themes\butterfly\layout\includes\widget\card_author.pug:
1 2 3 4 5 6 7 8 9 10 11 12 13 if theme.aside.card_author.enable .card-widget.card-info.text-center.liquidGlass-wrapper .liquidGlass-effect .liquidGlass-tint .liquidGlass-shine .liquidGlass-box div.card-info-avatar .avatar-img img( src=url_for(theme.avatar.img) onerror="this.onerror=null;this.src='" + url_for(theme.error_img.flink) + "'" alt="avatar" )
liquid_glass.css
Create a custom CSS file themes\butterfly\source\css\liquid_glass.css:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 :root { --glass-border-radius : 1.2rem ; } .liquidGlass-wrapper { box-shadow : 0 6px 6px rgba (0 , 0 , 0 , 0.2 ), 0 0 20px rgba (0 , 0 , 0 , 0.1 ) !important ; transition : all 0.4s cubic-bezier (0.175 , 0.885 , 0.32 , 2.2 ) !important ; background : none !important ; backdrop-filter : none !important ; -webkit-backdrop-filter : none !important ; border-style : none !important ; } .liquidGlass-effect { position : absolute; z-index : 0 ; inset : 0 ; backdrop-filter : blur (3px ); filter : url (#glass-distortion ); overflow : hidden; isolation : isolate; } .liquidGlass-tint { z-index : 1 ; position : absolute; inset : 0 ; background : rgba (255 , 255 , 255 , 0.25 ); } .liquidGlass-shine { position : absolute; inset : 0 ; z-index : 2 ; overflow : hidden; box-shadow : inset 2px 2px 1px 0 rgba (255 , 255 , 255 , 0.5 ), inset -1px -1px 1px 1px rgba (255 , 255 , 255 , 0.5 ); } .liquidGlass-box { z-index : 3 ; position : relative; box-shadow : none !important ; } .liquidGlass-wrapper ,.liquidGlass-wrapper > div :not (.liquidGlass-box ) { padding : 0.4rem !important ; border-radius : var (--glass-border-radius) !important ; } .liquidGlass-wrapper :hover { padding : 0.6rem !important ; border-radius : calc (var (--glass-border-radius) + 1em ) !important ; } .liquidGlass-wrapper > div :not (.liquidGlass-box ):hover { border-radius : calc (var (--glass-border-radius) + 1em ) !important ; }
inject
Inject in the theme’s _config.yaml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 inject: head: - <link rel="stylesheet" href="/css/liquid_glass.css"> bottom: - | <svg style="display: none"> <filter id="glass-distortion" x="0%" y="0%" width="100%" height="100%" filterUnits="objectBoundingBox" > <feTurbulence type="fractalNoise" baseFrequency="0.01 0.01" numOctaves="1" seed="5" result="turbulence" /> <!-- Seeds: 14, 17, --> <feComponentTransfer in="turbulence" result="mapped"> <feFuncR type="gamma" amplitude="1" exponent="10" offset="0.5" /> <feFuncG type="gamma" amplitude="0" exponent="1" offset="0" /> <feFuncB type="gamma" amplitude="0" exponent="1" offset="0.5" /> </feComponentTransfer> <feGaussianBlur in="turbulence" stdDeviation="3" result="softMap" /> <feSpecularLighting in="softMap" surfaceScale="5" specularConstant="1" specularExponent="100" lighting-color="white" result="specLight" > <fePointLight x="-200" y="-200" z="300" /> </feSpecularLighting> <feComposite in="specLight" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="litImage" /> <feDisplacementMap in="SourceGraphic" in2="softMap" scale="150" xChannelSelector="R" yChannelSelector="G" /> </filter> </svg>
References