When to use :before or :after - css

Before moving to my question, I know how the :before and :after selectors work. (not a duplicate of what is ::before or ::after expression). My question is in regards to use.
I've seen some inconsistencies over the years where these selectors have been used to display the same thing. Same results, different approach. In some specific cases, such as adding a font awesome icon within an li before the a the :before selector makes sense. I'm not inquiring about that use, since it's intuitive enough to understand. But take a speech bubble for a tooltip for instance. I have seen the triangle placed with a :before and also with an :after and in some occasions they use both! I'm confused.
What is the determining factor on choosing which selector should be used to attach an element such as the triangle on a speech bubble?
Allow me to demonstrate:
HTML
<div class="container">
<div class="bubble">This is my text in a bubble using :after</div>
<div class="bubble2">This is my text in a bubble using :before</div>
</div>
CSS
.bubble{
position: relative;
padding: 15px;
margin: 1em 0 3em;
color: #000;
background: #f3961c;
border-radius: 10px;
background: linear-gradient(top, #f9d835, #f3961c);
}
.bubble:after {
content: "";
display: block;
position: absolute;
bottom: -15px;
left: 50px;
width: 0;
border-width: 15px 15px 0;
border-style: solid;
border-color: #f3961c transparent;
}
.bubble2 {
position: relative;
padding: 15px;
margin: 1em 0 3em;
color: #000;
background: #f3961c;
border-radius: 10px;
background: linear-gradient(top, #f9d835, #f3961c);
}
.bubble2:before {
content: "";
display: block;
position: absolute;
bottom: -15px;
left: 50px;
width: 0;
border-width: 15px 15px 0;
border-style: solid;
border-color: #f3961c transparent;
}
IMG
Please don't tell me it's just a matter of preference. lol
DEMO

The naming of ::before and ::after is not entirely arbitrary, but it only really makes sense when the content of those pseudo elements is displayed inline, just before or just after the content of the element they are attached to.
As soon as you use
position: absolute;
in a pseudo element (which is totally legitimate), it no longer matters whether that pseudo element is named ::before or ::after.
It might just as easily be named ::presentational-frill-1 or ::presentational-frill-2.

Related

CSS ribbon-like header

I'm trying to get a ribbon-like banner effect for a header:
My markup is this:
<header>
<div id="logo">
<img src="">
</div>
</header>
I was thinking I could use pseudo :before and :after elements on the <img>, creating extra white space above and below the image to fake the extended `div:
#logo-wrap img:after {
content: '';
display: inline-block;
position: absolute;
left: 10px;
top: 10px;
width: 100px;
height: 100px;
background-color: #000;
}
And then another :before and :afterpseudo elements for the "shadow-fold".
My problem is: if I end up doing it like this, I'll have to insert another div between #logoand <img> in order to add another pair of :before and :after pseudo elements for the bottom "shadow-fold" and I think I'm having problems using the pseudo elements on the <img> element (nothing is appearing).
Can you shed some light and guide me on the right direction, pls? Perhaps, there is a simple way to just "shrink" the <header>?
EDIT
So, :before and :after can't be used with <img>. Thank you for the info :)
What I would like to know is if there is another way to achieve what I desire instead of wrap-wrap-wrap? :P
i.e: is there a way to make the #logo be bigger than <header> despite being its child and its height being the same (since the <header> has always the same height as the <img>)?
Thanks
I think you're on the right track. I would use borders, but I would make your pseudo-elements be behind the logo like so:
body,html {margin: 0; padding: 0;}
header {
background: #eee;
text-align: center;
margin: 1em 0;
}
#logo {
display: inline-block;
vertical-align: top;
position: relative;
margin: -0.5em 0;
}
#logo:before, #logo:after {
content: '';
position: absolute;
width: 100%;
left: -0.25em;
border: 0 solid transparent;
border-width: 0.5em 0.25em;
color: #aaa; /* set so we only have to have the border color in one place.
if not specified, border color is the same as the text
color. */
}
#logo:before {
border-top: none;
border-bottom-color: initial;
top: 0;
}
#logo:after {
border-bottom: none;
border-top-color: initial;
bottom: 0;
}
#logo img {
position: relative;
display:block;
z-index: 1;
}
<header>
<div id="logo">
<img src="//placehold.it/300x100?text=LOGO"/>
</div>
</header>
The concept is that the pseudo-elements are 100% width of the logo with a little bit extra (determined by the border attributes). Then you use both left and right borders simultaneously. There's a few other tricks in that code that help simplify it, but the general idea is to let your pseudo-elements peek out from behind the logo itself.

CSS Border with a botton

Can I achieve a custom CSS border with a button at one end which looks like this
Without url(some image link)?
Note: I want so because when I want to change color, I have to manipulate image.
I have achieved using image JS Fiddle
#stretch {
border-image: url(http://akitech.org/img/border.png) 30 30 stretch;
}
The easiest way is to use CSS pseudo-elements to create the decoration (the circle at the left) and to mask the chamfer at the right of the border (the angle at which the border-right would otherwise meet):
div {
border: 10px solid transparent;
width: 250px;
padding: 10px 20px;
position: relative;
/* this property has to be set to change the border-color: */
border-bottom-color: #f90;
}
/* common shared styles: */
div::before,
div::after {
/* to ensure the pseudo-elements are rendered: */
content: '';
/* for positioning: */
position: absolute;
/* positioning the element with its uppermost edge
against the bottom of the element, against the
upper side of the bottom-border: */
top: 100%;
/* again, set to change the color of the ends: */
background-color: #f90;
}
div::before {
/* position against the left edge: */
left: 0;
/* move the pseudo element 10px up, and
10px left: */
margin: -10px 0 0 -10px;
height: 30px;
width: 30px;
/* making the pseudo-element a circle: */
border-radius: 50%;
}
/* masking the chamfer of the border-bottom's
right-most edge: */
div::after {
left: 100%;
/* making the height/width the same width
as the border itself: */
height: 10px;
width: 10px;
}
div {
border: 10px solid transparent;
width: 250px;
padding: 10px 20px;
position: relative;
border-bottom-color: #f90;
}
div::before,
div::after {
content: '';
position: absolute;
top: 100%;
background-color: #f90;
}
div::before {
left: 0;
margin: -10px 0 0 -10px;
height: 30px;
width: 30px;
border-radius: 50%;
}
div::after {
left: 100%;
height: 10px;
width: 10px;
}
<div id="stretch">Here, the image is stretched to fill the area.</div>
In order to have these borders adapt to the length of the text, either the elements you want to have custom-bordered must themselves be able to contract to the width of the text, either using float:
div {
border: 10px solid transparent;
position: relative;
border-bottom-color: #f90;
padding-left: 20px;
/* forces the element to take up only that space required by
its (non-floated) contents: */
float: left;
/* forces the floated elements to the next line: */
clear: left;
}
div {
border: 10px solid transparent;
position: relative;
border-bottom-color: #f90;
padding-left: 20px;
float: left;
clear: left;
}
div::before,
div::after {
content: '';
position: absolute;
top: 100%;
background-color: #f90;
}
div::before {
left: 0;
margin: -10px 0 0 -10px;
height: 30px;
width: 30px;
border-radius: 50%;
}
div::after {
left: 100%;
height: 10px;
width: 10px;
}
<div>text</div>
<div>longer text</div>
<div>much longer text</div>
<div>much much much longer text</div>
Or, possibly more simply, use display: inline-block:
div {
border: 10px solid transparent;
position: relative;
border-bottom-color: #f90;
padding-left: 20px;
display: inline-block;
}
div {
border: 10px solid transparent;
position: relative;
border-bottom-color: #f90;
padding-left: 20px;
display: inline-block;
}
div::before,
div::after {
content: '';
position: absolute;
top: 100%;
background-color: #f90;
}
div::before {
left: 0;
margin: -10px 0 0 -10px;
height: 30px;
width: 30px;
border-radius: 50%;
}
div::after {
left: 100%;
height: 10px;
width: 10px;
}
<div>text</div>
<div>longer text</div>
<div>much longer text</div>
<div>much much much longer text</div>
Or display: inline (these don't automatically force new-lines between elements, obviously):
div {
border: 10px solid transparent;
position: relative;
border-bottom-color: #f90;
padding-left: 20px;
display: inline;
}
div {
border: 10px solid transparent;
position: relative;
border-bottom-color: #f90;
padding-left: 20px;
display: inline;
}
div::before,
div::after {
content: '';
position: absolute;
top: 100%;
background-color: #f90;
}
div::before {
left: 0;
margin: -10px 0 0 -10px;
height: 30px;
width: 30px;
border-radius: 50%;
}
div::after {
left: 100%;
height: 10px;
width: 10px;
}
<div>text</div>
<div>longer text</div>
<div>much longer text</div>
<div>much much much longer text</div>
summary:
for simplist way to this question, should not using svg, pure css can draw the shape author expected very well cause it's a combination of cycle(border radius)+rect(thicker line), let's refer to the David's answer should be the easiest and most clean way to draw that shape under text.
//below is my debugging history and tries (i searched out many ways to approach it);
//though not good answers
I use background css attribute (not OP wanted) Op used border-image also valid.
<div class="custom-border" >SOME TEXT HERE</div>
<style>
.custom-border{
padding-left:20px;
width:200px;
background:url(http://img1.wikia.nocookie.net/__cb20140224040010/shantae/images/b/bc/HGH_border_bottom.png) 0px 5px no-repeat;
background-size:contain;
height:150px;
}
</style>
later I realized OP might dislike using image traditional way, I re understand the
question is asking how to draw that shape in pure css and place it under the text and the responsive should be as flexible as the traditional way the svg shape will auto strech with the text placed on it.
after that, I've find some way to generate svg and place under text
see if it works for no image solution or you can get it improved based on fiddle
http://jsfiddle.net/hahatey/hsfxS/1464/
during the process, i've found this useful tool of generating svg from below reference url: http://svg-edit.googlecode.com/svn/branches/2.6/editor/svg-editor.html
But the flaw is it's still a fixed width solution, the line svg won't auto stretch.
Have found a unclean way to improve auto stretch though not in pure css responsive way.
but auto strech can be done by dynamically change below line
<rect stroke="#ff0000" id="svg_2" height="8" width="100%" y="27" x="40" stroke-width="5" fill="#FF0000"/>
where width="100%" or fixed value => width="function return value"; //
// during this try, i found a little bug, jquery seems unable to select svg or element inside svg? however svg element tag attribute can be written in backend languge so still valid.
//3.44
Another way without touching the inner "rect' element below "svg" tag, is to add a container to the whole thing, and using function to dynamically
assign width for the container;
like my attempt in this
fiddle: http://jsfiddle.net/hahatey/hsfxS/1468/
so at least the width can be dynamically calculated out by a function to calculate the text length of the upper text so the line will be able to strech if the calculation is accurate enough. There could be other ways to do svg auto strech with the text using pure css if other ppl find it.
Thanks.
5.02// since the author didn't say how complex the content is inside the container,
I've created a demo in pure css triggered effct --- auto strech the shape along with the text above it in below fiddle. but i said it sure has many limitations though looks similar.
http://jsfiddle.net/hahatey/a9z1kyx7/
my upper fiddle is only able to align correctly for singleline auto strech
I'm wondering if complex content (more than one line, there maybe a lot of block,inline mixed tag element inside which increases complexity for alignment) can also use css to do such decoration width auto adjustment without touching javascript or backend language.

Why doesn't inset box-shadow work over images?

I have a container that uses inset box shadow. The container contains images and text. The inset shadow apparently does not work on images:
The white section here is the container. It contains a white image, and there is inset box shadow applied to it.
body {
background-color: #000000;
}
main {
position: absolute;
bottom: 0;
right: 0;
width: 90%;
height: 90%;
background-color: #FFFFFF;
box-shadow: inset 3px 3px 10px 0 #000000;
}
<main>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Solid_white.png">
</main>
Is there a way to make the inset box shadow overlap images?
Just to chime in on this, because I was just creating something similar...
I hate polluting my markup with extra elements for the sake of styling, so the CSS solution is to use the :after pseudo element:
main::after {
box-shadow: inset 3px 3px 10px 0 #000000;
content: '';
display: block;
height: 100%;
position: absolute;
top: 0;
width: 100%;
}
<main>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Solid_white.png">
</main>
It's probably too late for what you were trying to do, but is the better solution in my estimation.
Because the shadow is part of the parent container it renders below the image. One alternative is to have a div which places a shadow overtop the image like so:
body {
background-color: #BBB;
}
main {
position: absolute;
bottom: 0;
right: 0;
width: 90%;
height: 90%;
background-color: #FFFFFF;
border-radius: 20px;
}
main img {
border-radius: 20px;
}
.shadow {
position: absolute;
width: 100%;
height: 100%;
box-shadow: inset 3px 3px 10px 0 #000000;
border-radius: 20px;
top: 0;
left: 0;
}
<main>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Solid_white.png" />
<div class="shadow"></div>
</main>
Edit: I've updated the fiddle to include border radius on the shadow and on the img which solves the issue identified in the comments.
The reason it's not overlapping is because the image is inside the div, so the image is on top of it. The image is higher (closer to the user) than the div.
You can change the image to use position: relative; z-index: -1, and have the containing div use a border instead of setting background color on the body. You'll need to use box-sizing: border-box to include the border in the width of the div.
DEMO
body {
background-color: #FFF;
}
main {
position: absolute;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
border: 60px solid black;
box-shadow: inset 3px 3px 10px 0 #000000;
box-sizing: border-box;
}
img {
z-index:-1;
position: relative;
}
For those, who're using absolute-positioned, full-size :before/:after pseudo elements, consider using pointer-events: none on the pseudo-element so the original elements remain clickable.
The best way to achieve this in 2020 would be to use mix blend mode on the image. use the box-shadow on the parent element of the img and use mix-blend-mode: multiply.
You could set the image as the div's background instead:
background-image:url(http://www.placehold.it/500x500)
jsFiddle example
https://stackoverflow.com/a/21415060/6235358
that's a great way to do it but we can do it in a better way using the ::after pseudo-class so you'll not have to add an empty <div> to your HTML
As Rilus mentioned we could use a pseudo class. Unfortunately this does not seem to work on an img tag for some reason however we can use a combination of inner and outer containers to achieve the affect we need.
.outer:hover .inner:after {
position: absolute;
content: '';
color: white;
display:block;
bottom: -0px;
right: -0px;
width: 100%;
height: 100%;
z-index: 11;
border: solid 10px red;
}
http://jsbin.com/kabiwidego/1/
not sure about ie 10 though as it seems to handle pseudo classes that are absolutely positioned slightly differently to most browsers.
One simple fix if you are clever with your decimals is to store your content in a separate div which you then select and implement a certain number of pixels from the top.
For example, let's say your header has a height of 50px. You could begin your #content div id 53.45px from the top (or whatever height your drop shadow is) and then your shadow would appear above the images.
One issue with this is that if you are using a rather transparent shadow, the more transarent it is the more tacky it may look by implementing this css.
In practice the code would be as follows:
HTML:
<header>
Whatever's in your header
</header>
<div id="content>
Page content
</div>
CSS:
header {
height: 50px;
box-shadow: 0 5px 5px rgba(0,0,0,1);
}
#content {
top: 55px;
}
Even if i'm late for the party, I had the same issue these days and worked on a solution. For me, the best solution (mobile friendly) is this one:
JSFiddle:
.image-inset-container {
margin-bottom: 30px;
}
.image-inset-shadow {
position: relative;
}
.image-inset-shadow img {
border-radius: 20px;
}
.image-shadow {
position: absolute;
width: 100%;
height: 100%;
box-shadow: inset 3px 3px 10px 0 #000;
border-radius: 20px;
top: 0;
left: 0;
}
<body>
<h4>Reimagined Web Design</h4>
<p>With your input and business goals in mind, we bring your brand to life through custom human-facing graphics and
visual elements targeted toward your audience for good user experience and created in future-forward technology,
guaranteeing a successful new web design.</p>
<div class="image-inset-container">
<div class="image-inset-shadow"><img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Solid_white.png" alt="img1" />
<div class="image-shadow"></div>
</div>
</div>
<p>We initiate a collaborative process where your team is involved in every step to create a frictionless and
delightful
experience for your customers. Our designers immerse themselves in your industry and your brand aesthetic to
deliver
a website that represents your business while achieving your goals for a connected future.</p>
</body>

PNG shadow with fluid height

Due to browser performance implications I can't use box-shadow CSS property because I have many similarly looking elements on my page that should have same looking style including shadow. That's the reason I would like to implement shadows using traditional PNG imagery.
Facts
My elements have predefined and more importantly fixed pixel width
They have fluid height (auto) depending on their content
They have content directly in the element and some child elements will be positioned outside their border
CSS3 can be used but performance-critical parts (gradients, shadows...) should be avoided
CSS pseudo elements can be used without limitation
Requirements
There should be no additional wrapper element added in order to have fluid shadow
Application should run smoothly on mobile browsers - shadows seem to slow down performance significantly on mobile devices since their processing power is much lower than desktop computers.
Possible direction
I thought of using :before and :after pseudos to display top-to-bottom and bottom shadows on the containing element, but these pseudos display within their parent element and positioning parent z-index higher than these children has no effect.
Visual demo of end result
This JSFiddle Demo in pure CSS3 that I would like to achieve but using PNG shadows. In reality there are numerous of these boxes so you can imagine mobile browsers are struggling with all these shadows.
Item is one such box (see blow) that needs PNG shadow. Left menu is child element positioned outside of the box.
Display in Chrome
HTML
<div class="item">
<menu>
<li>Yes</li>
<li>No</li>
<li>Maybe</li>
</menu>
<div class="content">
Some content
</div>
</div>
CSS3 LESS
.item {
position: relative;
width: 300px;
background-color: #fff;
box-shadow: 0 0 10px #ccc;
margin: 20px 20px 20px calc(20px + 3.5em);
min-height: 5em;
&:first-child {
margin-top: 0;
}
&:after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 10px;
height: 5em;
background-color: #fff;
}
menu {
position: absolute;
top: 0;
left: -3.5em;
width: 3.5em;
margin: 0;
border: 0;
padding: 0;
list-style: none;
background-color: #fff;
box-shadow: 0 0 10px #ccc;
li a {
display: block;
text-align: center;
padding: 2px 0;
}
}
.content {
padding: .75em 1em;
}
}
Probably I am missing something, but looks like you want something in this way:
demo
The CSS is
.base {
width: 300px;
height: 150px;
font-size: 100px;
font-weight: bolder;
background-color: lightgreen;
position: relative;
z-index: auto;
}
.base:after {
content: '';
position: absolute;
top: 30px;
left: 30px;
background-color: green;
z-index: -1;
width: 100%;
height: 100%;
}
.child {
position: absolute;
left: 150px;
top: 50px;
border: solid 1px black;
color: red;
}
And just change the background of the :after to your image.
I have applied this solution to your fiddle.
The relevant CSS is for the before pseudo element:
.item:before {
content: "";
position: absolute;
top: -10px;
left: -10px;
right: -10px;
bottom: -10px;
z-index: -1;
background-image: url(http://placekitten.com/100/100);
background-repeat: no-repeat;
background-size: 100% 100%;
}
I have used a kitten picture, that is being scaled to cover all the needed size. Just change that to whatever you want.
I needed to do it that way because I had onky a pseudo element available.
The key for that to work (and where you probably had the difficulty) is to add z-index: auto to .item
Updated demo
Well, I had said that it wasn't posible, but I have find a way.
The standard technique would be to use 2 elements, just to avoid stretching the image (as you said). The problem is that we only have 1 pseudo element available.
The solution then would be to use 1 pseudo element, but with 2 backgrounds, to solve the issue.
CSS (only relevant part)
.item:before {
background-image: url(http://placekitten.com/320/10), url(http://placekitten.com/320/500);
background-repeat: no-repeat;
background-size: 100% 9px, 100% calc(100% - 9px);
background-position: left bottom, left top;
}
We will need an image (the first one) only 10 px in height, to cover the bottom shadow. And another one, with enough height to cover the maximumitem posible, and that will be used for the remaining part of the shadow. The dark part is that we need now a calc() height, with limited support. (anyway, better than border image)
demo 3

How to create a "dot-dash" border with css or javascript?

In css3, they plan to add a border-style called "dot-dash" that will look like this:
Unfortunately, that is not yet implemented in any browser, and I need that kind of border now for a web-app.
The Application works with the Javascript-Framework ExtJS, so the solution of my problem can be a javascript one, too.
I already tried with a background-image (very bad solution, I know) - but that didn't work because there will be many divs with this border, which will all have different sizes (which I don't know before).
Thank you!
Well, if you don't have it, make it by yourself !
The recipe for a dash-dot: 1 part of dash and 1 part of dot:
#dash {
width: 200px;
height: 200px;
left: 35px;
top: 35px;
position: absolute;
background-color: lightblue;
border: dashed 6px red;
}
#dash:after {
content: '';
width: 100%;
height: 100%;
left: -6px;
top: -6px;
position: absolute;
border: dotted 6px red;
}
demo
It'll probably be supported by all browsers in the future, but as of now, I don't think it's a supported border type. Here's a test page someone made with the different border types: http://www.aaronsw.com/2002/testcss
You'll probably have to use a border image like Kyle suggested. http://www.w3schools.com/cssref/css3_pr_border-image.asp
Although it looks like Internet Explorer doesn't even support that yet. Surprise!
Here's a workaround for IE: border-image: workaround for IE
I needed something very close to this and came up with a variation of vals' solution. This uses a continuous solid line instead of dashes, but I am showing it here because it removes the need for position: absolute on the main div.
.dash {
background: none;
position: relative;
box-shadow: inset 0 0 0 1px #fff,
inset 0 0 1px 1px #000;
}
.dash:after {
box-sizing: border-box;
content: '';
width: 100%;
height: 100%;
left: 0px;
top: 0px;
position: absolute;
border: dotted 3px #bbb;
border-left: 3px solid white;
border-right: 3px solid white;
}
If you need only the bottom border, you could try the following css
.class:after {
content: '–⋅–⋅–⋅–⋅–⋅–';
display: block;
position: absolute;
top: 30px;
left: -4px;
font-size: xx-large;
font-weight: bolder;
color: #59defc;
height: 12px;
overflow:hidden;
width: 130px;
}
Please note that the dashes in the above code are special unicode characters and are not the ones on the english keyboard (those dots and dashes wont align on the same line).
Please change the pixel properties values to match your need. I found all these properties are needed to get the same behavior across multiple browsers.
#vals solution will work but with inconsistent results across browsers especially IE stretches the dashes.

Resources