Basically I want to create a shape in CSS only (so no images) that is the opposite of a heart shape. I don't know how to explain it properly so here is an image:
The blue is the background, as you can see, but the shape that I want to create is not a heart, it is the shape of the black rectangle.
If I would have the following shape (THE GRAY NOT THE BLACK)
I could duplicate it and then rotate it, that would give me the shape I am looking for.
Heart shape cut out using box-shadow
Let's create this — the blue is the background color of <body>
The pieces
Feel free to skip directly to the complete demo at the bottom of this answer :)
1 - The rounded corners
The rounded top left and top right corners are created with box-shadow on the two pseudo elements with border-radius: 50% — .heart:before and .heart:after — They form two crescent shapes that look like this:
2 - The angle
The angled shape is created by the box-shadow on .heart. Combined with the two circles, it looks like this:
3 - The filler
We now need to fill in the gaps. This is done by the pseudo elements of the .box-shape container — .shape-box:before and .shape-box:after. The excess is cut-off neatly with overflow: hidden on the .shape-box. Combined with our pieces above, they look like this:
The Complete Example
Combine it all together and we get this nicely cut out heart shape. It is all contained in .shape-box.
body {
background: #00A2F6;
}
.shape-box {
height: 504px;
width: 504px;
position: relative;
margin: 100px;
overflow: hidden;
}
.shape-box:before,
.shape-box:after {
content: '';
display: block;
height: 100px;
width: 120px;
background: #2B2B2B;
transform: rotate(45deg);
left: 190px;
position: absolute;
top: 40px;
}
.shape-box:after {
width: 760px;
height: 750px;
box-shadow: inset 0 0 0 220px #2B2B2B;
top: -150px;
left: -130px;
background: none;
}
.heart {
transform: rotate(45deg);
height: 357px;
width: 356px;
box-shadow: inset 0 0 0 50px #2B2B2B;
position: absolute;
left: 74px;
top: 34px;
}
.heart:before,
.heart:after {
content: '';
display: block;
width: 151px;
height: 151px;
border-radius: 50%;
box-shadow: -40px -15px 0 20px #2B2B2B;
position: absolute;
left: 50px;
top: 157px;
}
.heart:after {
box-shadow: -15px -40px 0 21px #2B2B2B;
left: 156px;
top: 51px;
}
<div class="shape-box">
<div class="heart"></div>
</div>
This can be done with a combination of svg gradients, multiple backgrounds, and a little creative tiling/placement. Sample CSS from my working jsfiddle (without vendor prefixes, i.e. -webkit and -moz):
height: 400px;
width: 400px;
background-image:
radial-gradient(75% 85.5%, circle, transparent 25%, black 26%),
radial-gradient(25% 85.5%, circle, transparent 25%, black 26%),
linear-gradient(225deg, transparent 25%, black 25%),
linear-gradient(135deg, transparent 25%, black 25%);
background-size: 200px 200px;
background-position: top left, top right, bottom left, bottom right;
background-repeat: no-repeat;
This makes a heart-shaped cutout in the middle of a 400px square element. It can be modified to fit whatever size element you want.
Update: here’s a more complex fiddle that uses six gradients instead of four, but looks a bit nicer.
Based on the work that Mark Hubbart did I was able to push this to a slightly more advanced form in this fiddle
This is not 100% complete yet as it will need some media queries to work across more browsers but it does show the start of a much more flexible working for the same goal.
#backgrounder {
z-index: 2;
background-image:
radial-gradient(68% 100%, circle, transparent 48%, white 30%),
radial-gradient(32% 100%, circle, transparent 48%, white 30%),
radial-gradient(110% 1%, circle, transparent 65%, white 30%),
radial-gradient(-8.5% 1%, circle, transparent 65%, white 30%),
linear-gradient(220deg, transparent 41%, white 30%),
linear-gradient(139deg, transparent 41%, white 30%);
background-image:
-webkit-radial-gradient(68% 100%, circle, transparent 48%, white 30%),
-webkit-radial-gradient(32% 100%, circle, transparent 48%, white 30%),
-webkit-radial-gradient(110% 1%, circle, transparent 65%, white 30%),
-webkit-radial-gradient(-8.5% 1%, circle, transparent 65%, white 30%),
linear-gradient(220deg, transparent 41%, white 30%),
linear-gradient(139deg, transparent 41%, white 30%);
background-size: 51% 31%, 50% 31%, 51% 50%, 50% 50%, 51% 51%, 50% 51%;
background-position: top left, top right, 0% 30%, 100% 30%, bottom left, bottom right;
background-repeat: no-repeat;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
}
I'm wondering if I can get a sort of glass-effect border around boxes in CSS. For example, a navigation div that contains a ul etc. Here's an example of what I mean
A glass effect in 2020
When I answered this in 2012, I used only features that were well-supported by browsers at that time. You can find it below for posterity, but first I'll share a slightly more interesting glass effect which looks a bit different from the one in the original question.
I also no longer care so much about an extra element or two, but if you hate presentational elements, check out the old answer to see how you can use a pseudoelement to avoid them.
Full demo
.glass {
backdrop-filter: contrast(130%) brightness(120%) blur(2px);
background:
radial-gradient(
ellipse at 16.7% -10%,
hsla(0, 0%, 100%, 0.44) 24%,
hsla(0, 0%, 100%, 0.4) 25%,
hsla(0, 0%, 100%, 0.2) 45%,
hsla(0, 0%, 100%, 0.1)
);
background-size: 300% 100%;
border-radius: 10px;
box-shadow:
0 2px 1px hsla(0, 0%, 100%, 0.5) inset,
0 -2px 1px hsla(250, 70%, 5%, 0.3) inset,
0 -2px 6px hsla(0, 0%, 100%, 0.25);
}
/* -------------------------------------------
Decorative (not relevant to technique)
------------------------------------------- */
html {
background:
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.15) 30%, rgba(255,255,255,.3) 32.9%, rgba(255,255,255,0) 33%) 0 0,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.1) 11%, rgba(255,255,255,.3) 13.9%, rgba(255,255,255,0) 14%) 0 0,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.2) 17%, rgba(255,255,255,.43) 19.9%, rgba(255,255,255,0) 20%) 0 110px,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.2) 11%, rgba(255,255,255,.4) 13.9%, rgba(255,255,255,0) 14%) -130px -170px,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.2) 11%, rgba(255,255,255,.4) 13.9%, rgba(255,255,255,0) 14%) 130px 370px,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.1) 11%, rgba(255,255,255,.2) 13.9%, rgba(255,255,255,0) 14%) 0 0,
linear-gradient(45deg, #343702 0%, #184500 20%, #187546 30%, #006782 40%, #0b1284 50%, #760ea1 60%, #83096e 70%, #840b2a 80%, #b13e12 90%, #e27412 100%);
background-size: 470px 470px, 970px 970px, 410px 410px, 610px 610px, 530px 530px, 730px 730px, 100% 100%;
background-color: #840b2a;
font: 17px/21px Segoe UI, Tahoma, Helvetica, sans-serif;
height: 100%;
idth: 100%;
}
.frame {
padding: 20px;
}
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.content {
background: lemonchiffon;
border: 1px solid sienna;
padding: 20px 25px;
width: 300px;
}
.content > :first-child { margin-top: 0; }
.content > :last-child { margin-bottom: 0; }
c {
background: rgba(255,255,255,.5);
box-shadow: 0 0 3px rgba(0,0,0,.4);
color: #840b2a;
font-family: Consolas, Courier New, Courier, monospace;
font-weight: bold;
padding: 0 3px;
}
<div class="glass frame centered">
<section class="content">
<p>A glass-effect frame using pure CSS.</p>
</section>
</div>
Breaking it down
Backdrop filter
Blurring what lies behind the pane gives the impression that light is being defocused as it passes through the glass. Also, the contrast and brightness are increased to make the background stand out through the surface reflection effect.
backdrop-filter: contrast(130%) brightness(120%) blur(2px);
CSS gradients
This conveys the impression of light reflecting non-uniformly from the surface of the glass. This example uses a radial gradient to imply a light source a finite distance from the pane, but you could also use a linear or conical gradient (less support) to convey a different lighting effect.
The background size is also increased so that the edges of the ellipse do not show.
background:
radial-gradient(
ellipse at 16.7% -10%,
hsla(0,0%,100%,.44) 24%,
hsla(0,0%,100%,.4) 25%,
hsla(0,0%,100%,.2) 45%,
hsla(0,0%,100%,.1)
);
background-size: 300% 100%;
Multiple box shadows
A couple of inset shadows are used to highlight the top edge and shade the other, while an outer shadow implies that light is being cast onto the background around the edges of the box. Changing the position and intensity of these shadows will imply different things about the position of the implied light source.
box-shadow:
0 2px 1px hsla(0,0%,100%,.5) inset, /* Highlight upper edge */
0 -2px 1px hsla(250,70%,5%,.3) inset, /* Shade lower edge */
0 -2px 6px hsla(0,0%,100%,.5); /* Imply light cast around the edges */
A glass effect in 2012 (original answer)
You can achieve an effect very close to this—almost identical to the example in the question—using simpler CSS. This example uses a single element with an RGBA border colour and multiple box shadows to add the highlights and shadows.
.box {
background: #f0edcc;
background-clip: padding-box; /* Background stops at border */
border: 4px solid rgba(255,255,255,.2);
border-radius: 3px;
box-shadow:
0 0 1px rgba(255,255,255,.8), /* Bright outer highlight */
0 0 3px rgba(0,0,0,.8), /* Outer shadow */
1px 1px 0 rgba(0,0,0,.8) inset, /* Inner shadow (top + left) */
-1px -1px 0 rgba(0,0,0,.8) inset; /* Inner shadow (bottom + right) */
padding: 10px;
}
/* -------------------------------------------
Decorative (not relevant to technique)
------------------------------------------- */
html {
background:
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.15) 30%, rgba(255,255,255,.3) 32.9%, rgba(255,255,255,0) 33%) 0 0,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.1) 11%, rgba(255,255,255,.3) 13.9%, rgba(255,255,255,0) 14%) 0 0,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.2) 17%, rgba(255,255,255,.43) 19.9%, rgba(255,255,255,0) 20%) 0 110px,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.2) 11%, rgba(255,255,255,.4) 13.9%, rgba(255,255,255,0) 14%) -130px -170px,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.2) 11%, rgba(255,255,255,.4) 13.9%, rgba(255,255,255,0) 14%) 130px 370px,
radial-gradient(rgba(255,255,255,0) 0, rgba(255,255,255,.1) 11%, rgba(255,255,255,.2) 13.9%, rgba(255,255,255,0) 14%) 0 0,
linear-gradient(45deg, #343702 0%, #184500 20%, #187546 30%, #006782 40%, #0b1284 50%, #760ea1 60%, #83096e 70%, #840b2a 80%, #b13e12 90%, #e27412 100%);
background-size: 470px 470px, 970px 970px, 410px 410px, 610px 610px, 530px 530px, 730px 730px, 100% 100%;
background-color: #840b2a;
font: 13px/17px Segoe UI, Tahoma, Helvetica, sans-serif;
height: 100%;
width: 100%;
}
c {
background: rgba(255,255,255,.5);
box-shadow: 0 0 3px rgba(0,0,0,.4);
color: #840b2a;
font-family: Consolas, Courier New, Courier, monospace;
font-weight: bold;
padding: 0 3px;
}
.box {
bottom:0;
height: 150px;
left:0;
margin:auto;
position:absolute;
top:0;
right:0;
width: 250px;
}
.box > :first-child { margin-top: 0; }
.box > :last-child { margin-bottom: 0; }
<div class="box">Your message.</div>
Note that box-shadow and RGBA border colours are only supported in IE9+ and more recent versions of Firefox, Chrome, Opera and Safari. (Although older versions of the latter browsers may support prefixed versions of the property.) In browsers that don't support either, this degrades to just the inner yellow box.
You can't create a Glass/Blur effect width CSS for now. But width transparent border and box shadow you can attenuate the background.
You can see the result in my jsfiddle : http://jsfiddle.net/DoubleYo/hyETB/1/
Since there is a pattern in the example border, you will probably need one or more PNG background images with alpha channel (so that the parent's background can shine through where and to the extent desired); borders with uniform RGBA colors alone will not suffice here.
Then nest another block element in the element that has that background. With one background image, example HTML:
<div id="glass-box">
<div id="inner">
<p>Text</p>
</div>
</div>
Example CSS:
#glass-box
{
background: transparent url(glass.png) 0 0 no-repeat;
}
#glass-box #inner
{
margin: 10px;
background-color: white;
}
Instead of transparent you might want to try an RGBA color with opacity < 1.0; perhaps you would use a semi-transparent greyscale glass background image which you could project on top of any hue.
Until multiple borders are better supported natively (see my comment), you can achieve multiple borders by nesting block elements and giving each one different borders. Margins on some of those elements will help to reduce the number of elements that need to be nested for the desired effect.
And until CSS Backgrounds and Borders Level 3's multiple background images are better supported (but it is a CR already, so you may be lucky), you can achieve the illusion of multiple background images by using differently positioned (different) background images for nested (positioned) block elements. That way you do not need a box and background image of fixed size.