How to create div with pseudo elements directly on top & under? - html

I'm trying to create a div with a triangle on top & a triangle under it..
the div should be responsive in size, so not a fixed width
not a fixed height either
Already experimented with borders only but they don't seem to give me that flexible width I want..
So where I'm at:
I'm using pseudo elements to place svgs I made of the shape!
Problem is, I'm not sure how to place them properly.. feels so strange to have to set 100% from top to place the bottom one & the other way (but as you can see there is a line in between etc)..
Here's the fiddle:
https://jsfiddle.net/benvanlooy/c4vqb1ay/
.box {
position: relative;
display: inline-block;
width: 40%;
background-color: #D01417;
margin-top: 200px;
margin-bottom: 200px;
padding: 30px;
}
.box::before {
content: url('http://www.benvanlooy.be/fiddle/box-triangle-top-red-new-01.svg');
position: absolute;
width: 100%;
left: 0px;
bottom: 100%;
}
.box::after {
content: url('http://www.benvanlooy.be/fiddle/box-triangle-bottom-red-new-01.svg');
position: absolute;
width: 100%;
left: 0px;
bottom: 0px;
top: 100%;
}
<div class="box">
this is some content
<BR/> this
<br/> box
<br/> has
<br/> a <br/> variable
<br/> height
</div>
Has anyone got any experience with something like this? :-)
The answer is probably easy, so I'm feeling rather stupid :/
Any help would be much appreciated!

You can use gradient and no need for complex code and extra SVG:
.box {
position: relative;
display: inline-block;
width: 40%;
background:
linear-gradient(to top right,#D01417 49.5%,transparent 50%) top right/50.2% 200px,
linear-gradient(to top left,#D01417 49.5%,transparent 50%) top left/50.2% 200px,
linear-gradient(to bottom right,#D01417 49.5%,transparent 50%) bottom right/50.2% 200px,
linear-gradient(to bottom left,#D01417 49.5%,transparent 50%) bottom left/50.2% 200px,
linear-gradient(#D01417,#D01417) center/100% calc(100% - 400px);
background-repeat:no-repeat;
padding: 200px 30px;
}
<div class="box">
this is some content
<br> this
<br> box
<br> has
<br> a <br> variable
<br> height
</div>

This creates visual triangles without any images (using borders only):
.box {
position: relative;
display: inline-block;
width: 40%;
background-color: #D01417;
margin-top: 200px;
margin-bottom: 200px;
padding: 30px;
}
.box::before {
content: "";
display: block;
position: absolute;
left: 50%;
top: -40px;
transform: translateX(-50%);
border-width: 22px;
border-color: transparent transparent #D01417 transparent;
border-style: solid;
}
.box::after {
content: "";
display: block;
position: absolute;
left: 50%;
bottom: -40px;
transform: translateX(-50%);
border-width: 22px;
border-color: #D01417 transparent transparent transparent;
border-style: solid;
}
<div class="box">
this is some content
<BR/> this
<br/> box
<br/> has
<br/> a <br/> variable
<br/> height
</div>

You can remove a 4 px line by using calc like this:
.box::before {
bottom: calc(100% - 4px);
}
https://jsfiddle.net/c4vqb1ay/33/
or
https://jsfiddle.net/c4vqb1ay/36/

Another option would be to clip the actual div's shape, without using pseudo elements or SVGs. For example:
div {
background-color: #D01417;
width: 40%;
clip-path: polygon(0 3em,
50% 0,
100% 3em,
100% calc(100% - 3em),
50% 100%,
0 calc(100% - 3em)
);
padding: 4em 1em;
}
html * {
box-sizing: border-box;
}
<div>
this is some content<BR/>
this<br/>
box<br/>
has<br/>
a <br/>
variable<br/>
height
</div>
Browser support for clip-path: https://caniuse.com/#feat=css-clip-path
(works in most current browsers except Edge, if you add a -webkit- prefix)
The polygon is drawn by simply defining it's points:
translates to CSS shape as:
0 3em,
50% 0,
100% 3em,
100% calc(100% - 3em),
50% 100%,
0 calc(100% - 3em)
(starting at top left, but it doesn't really matter)
If you ever decide to go for more complex shapes, this tool is quite useful: https://bennettfeely.com/clippy/

Related

Using css how so I create a pill shape which is circular on one side and angled on the other?

This is the shape I am trying to create:
This is what I have so far:
This is my code:
.pill-left {
position: relative;
width: 50px;
height: 50px;
background: #FDA725;
display: inline-block;
border-radius: 100% 100% 0 0;
transform: rotate(-90deg);
}
.pill-right {
position: relative;
width: 50px;
height: 50px;
background: #CED2D9;
display: inline-block;
border-radius: 100% 100% 0 0;
transform: rotate(90deg);
}
<div class="pill-left">5
</div>
<div class="pill-right">10
</div>
The two issues I have is firstly how to display the text within the pill and not be rotated, the second is how to achieve the sloping line. I have tried a few things such as border-right: 50px. How can I achieve this? I suspect it is obvious but I can't get it to work. Thanks
you could accomplish this with a linear-gradient background:
A brief explanation
a linear-gradient background colour handles both pill colours - setting the positions the same here (eg. 48% & 48%, then 52% & 52%) means there's no blend between them.
to get fully rounded corners we set border-radius to half the height of the element or greater - there's no harm in going larger, so 9999px just ensures it will always be more than the element height.
you don't necessarily need display:flex & justify-content in this instance, but it will ensure the percentages are aligned at both ends of the pill if you set a specific width for example.
.split-pill {
border-radius: 9999px;
padding: 10px;
display: inline-flex;
justify-content:space-between;
background: linear-gradient(-70deg, #FDA725 48%, white 48%, white 52%, #CED2D9 52%);
}
.left {
padding-right: 10px;
}
.right {
padding-left: 10px;
}
<div class="split-pill">
<span class="left">10%</span>
<span class="right">50%</span>
</div>

Creating connectors between <div> boxes

I've been trying to create a gray connector between div containers (image below) for a while now. The margin between the containers is set to 2.5vw per container on each side.
I've tried to create it using an .svg background on a :before pseudo element and positioning it, but I couldn't make it work. The gray connector should stay fixed in relation to the boxes until the boxes wrap (mobile version), in which case the connectors should be hidden. The mobile version is easy to setup, but when the elements are next to each other I'm finding it almost impossible to apply the gray connector to the elements. The connector should stay fixed in relation to the containers when transitioning between laptop screens and large desktop screens.
Here's the outcome I want:
Gray connector between boxes.
Here's the outcome I've got and the code I've used:
Outcome I've got.
.box-container:after{
content: '';
height: 700px;
width: 600px;
position: absolute;
background-image: url("folder_path/Connector-1.svg");
background-repeat: no-repeat;
background-position: -80px 60px;
background-overflow: visible !important;
z-index: 0 !important;
}
.box-container{
z-index: 1 !important;
}
Maybe I've been going at it the wrong way, all workarounds are welcome.
Here is one way to do it:
main {
width: 100%;
height: 100%;
position: relative;
}
section {
display: inline-block;
width: 100px;
height: 200px;
background: orange;
box-shadow: 0 5px 15px rgba(0, 0, 0, .3);
}
section + section {
margin-left: 46px;
}
.connector {
position: absolute;
top: 0;
z-index: -1;
background: #444;
width: 100px;
height: 200px;
transform-origin: top left;
transform: skewX(36.87deg); /* do some trigonometry here to get the deg */
}
.connector:nth-of-type(2) {
left: 150px;
}
<main>
<section></section>
<section></section>
<section></section>
<div class="connector"></div>
<div class="connector"></div>
</main>
I figured out the solution! It's to create a pseudo-element, with a height of 100% and a width equal to the margin between the elements. To keep it responsive, it's important to use vw for the width unit, both for the margin between elements and also the width of the decoration itself. The margin of the <div>s are 2.5vw each, so setting the width of the pseudo element to 5 makes it fit perfectly. Heres the code:
.container:after{
content: '';
z-index:-1;
position: absolute;
top: 0;
right: -5vw;
width: 5vw;
height: 100%;
background-color: #444;
clip-path: polygon(0% 0%, 100% 40%, 100% 100%, 0% 60%);
}

How to add a background square behind the image?

I am trying to recreate this:
But I have not been able to do so. I tried with adding a :before on the img but that doesnt work. How would you go on about making this. It has to be responsive in the way that the background doesnt get bigger than the image.
SEO is not important so background-image or whatever is fine with me too.
WRITTEN IN SCSS - CHANGE IN HTML IS OK
UPDATED CODE TO ROB's ANSWER
This is the code I have so far
.imgbox {
padding: 5%;
position: relative;
height: auto;
.backdrop {
position: relative;
min-width: 100px;
min-height: 100px;
div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
background: rgb(208, 0, 0);
background: linear-gradient(
90deg,
rgba(208, 0, 0, 1) 0%,
rgba(149, 0, 0, 1) 100%
);
}
transform: translateX(-5px) translateY(5px);
}
.img {
width: 100%;
height: auto;
transform: translateX(5px) translateY(-5px);
}
}
<div className="imgbox">
<div className="backdrop">
<div></div>
</div>
<img
className="img"
src={'https://source.unsplash.com/400x250'}
alt="test"
>
</div>
It's simple with a box shadow.
The paddings in the parent are there to prevent it from cropping the shadow.
.imgbox {
padding: 0 0 30px 30px;
}
.imgbox .img {
display: inline-block;
box-shadow: -30px 30px 0 rgb(208, 0, 0);
}
<div class="imgbox">
<img
class="img"
src='https://source.unsplash.com/400x250'
alt="test"
/>
</div>
Very easy to get the gradient with a pseudo-element:
.image-container::after {
content:'';
position: absolute;
z-index:-1;
bottom:-24px;
left:-24px;
width:100%;
height:100%;
background: linear-gradient(red, firebrick);
}
You can change the gradient and offset using background, left and bottom respectively. I'm not sure if there is a second gradient as well, to the top right? If so, you pair this with a ::before to get a second background, and play around the with z-index to get the ordering correct.
Just remember - for an absolute positioned pseudo element to work, you'll need to set position:relative on the parent container, and content:'';
Codepen here.

Border bottom with fixed size

What is the best way to add a small border at the bottom of a text?
The border has to be centered and maximum 20px, but the text can be very long, even 200px.
So it's like:
<h1>This text can be longer than the border</h1>
<style>
.h1 {border-bottom:8px solid #000;}
</style>
Should I add a div after my h1 and set a maximum size for it?
you can use pseudo element ::after and use left:50% / transform:translateX(-50%) to align in the middle no matter the width
h1 {
position: relative;
display:inline-block;
/* min-width:200px */
}
h1::after {
border-bottom: 1px solid red;
bottom: -10px;
content: "";
height: 1px;
position: absolute;
width: 20px;
left:50%;
transform: translateX(-50%);
}
<h1>This text can be longer than the border</h1>
Using Linear Gradients for Background:
Or you can also do this using linear-gradient background images. Advantage of using a gradient is that it doesn't require any extra pseudo-elements which can be used for other purposes. The border's thickness is based on the background-size in Y-axis and the width of the border is based on the size in X-axis. The background-position property is used to center the border.
The disadvantage is the relatively poor browser support for linear-gradient as compared to pseudo elements. Gradients are supported only in IE10+.
h1 {
display: inline-block;
padding-bottom: 4px; /* to give some gap between text and border */
background: linear-gradient(to right, black, black);
background-repeat: no-repeat;
background-size: 20px 2px;
background-position: 50% 100%;
}
<h1>This text can be longer than the border</h1><br>
<h1>Some text with lesser length</h1><br>
<h1>Some text</h1>
Using a Pseudo-element:
You can do this using a pseudo-element. By adding a pseudo-element with 20px width, positioning it absolutely we will be able to produce the required effect. The left: 50%, translateX(-50%) is used to position the pseudo-element at the center. The height of the pseudo-element determines border's thickness while the background determines the color of the border.
Advantage of this is the browser support as it should work in IE8+.
h1 {
position: relative;
display: inline-block;
padding-bottom: 4px; /* to give some gap between text and border */
}
h1:after {
position: absolute;
content: '';
left: 50%;
bottom: -2px;
width: 20px;
height: 2px;
background: black;
transform: translateX(-50%);
}
<h1>This text can be longer than the border</h1>
<br>
<h1>Some text with lesser length</h1>
<br>
<h1>Some text</h1>
Improving the answer of dippas, you can see when you use bigger widths, the border of the after elements is innacurate. You can prevent this by using calc(50% - 100px); instead of 50% whereas the 100px are half the width of the after element.
.bottom-border {
position: relative;
display:inline-block;
}
.bottom-border::after {
border-bottom: 2px solid red;
bottom: -10px;
content: "";
height: 1px;
position: absolute;
width: 200px;
left: calc(50% - 100px);
transform: translateY(-50%);
}
<p class="bottom-border">
Hi, i am a quite long text, might be 200px, probably more i guess, but nobody knows really.
</p>

Stretch div according to another one

I have the following code:
#left {
width:383px;
margin:0px;
float: left;
position:relative;
}
.bg_top{
background: transparent url(../bg_top.png) no-repeat;
width: 383px;
height: 140px;
}
.bg_middle{
background: transparent url(../bg_middle.png) no-repeat;
width: 383px;
height: 100%;
}
.bg_bottom{
background: transparent url(../bg_bottom.png) no-repeat;
width: 383px;
height: 131px;
}
#left_inner{
width:375px;
border:2px solid #98b73f;
position: absolute;
top: 5px;
}
<div id='wrap'>
<div id='left'>
<div class='bg_top'>
bg_top
</div>
<div class='bg_middle'>
bg_middle
</div>
<div class='bg_bottom'>
bg_bottom
</div>
<div id='left_inner'>
<p>long text</p></div</div</div>
I want the bg_middle class to stretch to the height of the left_inner div. I have tried with height 100% and searched the net for answers. I'm new in the world of css. I think I have to connect the 3 divs that are the background to the inner div somehow...
With the HTML as is it you can't do it without Javascript. Block elements adjust in height to their container. Si if the goal is to use a background-image just apply it to the left-inner. Don't use position absolute 'cause that block would postion itself indepent from the content flow.
Given that you have fixed heights you can use min-height in case there's not enough content.
I'm not sure this is what you're looking for.
I would strongly recommend to do something different here..
However this is the solution I suggest with the original approach:
#left {
width:383px;
margin:0px;
position: relative;
}
.bg_bottom{
background: transparent url(../bg_bottom.png) no-repeat;
height: 131px;
position: absolute;
bottom: 0;
z-index: -1
}
.bg_top{
background: transparent url(../bg_top.png) no-repeat;
height: 140px;
position: absolute;
top: 0;
z-index: -1
}
.bg_middle{
height: 100%; /* it does work*/
background: transparent url(../bg_middle.png) repeat top left;
position: absolute;
z-index: -2;
}
height= 100% works if the parents (or at least one) have a height (100% of 0 is 0).
You can cascade starting with the html
html, body{height: 100%;}
Or you can just give the #left a height or min-height
This should work
Thanks to all,
Last night I found a way around this. This is my final approach, it still needs some small adjustments, but it works (for now).
<div id="wrap">
<div id="header"></div>
<div id="main">
<div class='bg_top'></div>
<div class='bg_middle'>
<div>LONG LONG TEXT</div>
</div>
<div class='bg_bottom'></div>
</div>
#wrap{
width: 800px;
margin: 0px auto;
border: 1px solid black;
}
.bg_top{
background: transparent url(../bg_top.png) no-repeat;
height: 140px;
}
.bg_middle{
background: transparent url(../bg_middle.png) repeat;
}
.bg_middle div{
margin:-130px 10px -100px 10px;
border:1.5px solid #98b73f;
}
.bg_bottom{
background: transparent url(../bg_bottom.png) no-repeat;
height: 131px;
}
#main {
float:left;
width:500px;
margin-left:0px;
padding:10px;
}
Thanks #Maroshii for the reply. I will also try your approach, but for now I'll stick to this. Hope I will not have to change a lot for browser compatibility :)