Reputation: 2647
I am hoping someone can help me with trying to find a nice elegant way of getting a border on a CSS arrow border.
I am trying to create this:
Here is my code so far:
HTML
<div class="message-container customer">
<p class="message">Great thanks</p>
</div>
CSS
.message {
width: auto;
max-width: 66%;
padding: 12px 30px;
box-sizing: border-box;
background: #ffffff;
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
margin: 4px 0 0 0;
position: relative;
float: right;
border-right: 4px solid #0892cb;
border-radius: 5px 0 0 5px;
}
.message:after {
top: 100%;
right: 0;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-bottom-color: #FFFFFF;
border-width: 10px;
margin-right: -14px;
margin-top: -10px;
transform: rotate(45deg);
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
}
Here is a working JS fiddle https://jsfiddle.net/kdeo3wpg/
As you can see I have a blue border on the right of he main message, but I cannot figure out a way to get the blue border on the arrow as well. If at all possible I would really like to avoid using an image. It would be great to find a CSS only solution.
I have thought about trying to use the :before
sudo element but I can't get the full control I need.
Any help would be greatly appreciated.
UPDATE
I have managed to find a solution, but to be honest it is not very clean.
https://jsfiddle.net/kdeo3wpg/1/
What I have done is add a new element which is the width of the border and has the same background colour. I then set the height to slightly less than the height of the CSS arrow. I then give my new element a CSS arrow the background colour of the border.
Here is the new code:
HTML
<div class="message-container customer">
<p class="message">
Great thanks
<span class="arrow-border"></span>
</p>
</div>
CSS
.message {
width: auto;
max-width: 66%;
padding: 12px 30px;
box-sizing: border-box;
background: #ffffff;
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
margin: 4px 0 0 0;
position: relative;
float: right;
border-right: 4px solid #0892cb;
border-radius: 5px 0 0 5px;
}
.message:after {
top: 100%;
right: 0;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-bottom-color: #FFFFFF;
border-width: 10px;
margin-right: -14px;
margin-top: -10px;
transform: rotate(45deg);
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
}
.arrow-border {
position: absolute;
background: #0892cb;
width: 4px;
height: 9px;
bottom: -9px;
right: -4px;
z-index: 1;
}
.arrow-border:after {
top: 100%;
right: 0;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-bottom-color: #0892cb;
border-width: 3px;
margin-right: -3px;
margin-top: -3px;
transform: rotate(45deg);
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
}
While this solution works, I feel there could be a better and cleaner options so I am still open to suggestions.
Upvotes: 8
Views: 1925
Reputation: 105893
already answered, but wih a single element and a pseudo + gradient, it could be done quiet similar.
body {
background: #F4F4F3;
}
span {
display: inline-block;
position: relative;
padding: 1em;
margin: 0 1em;
border-radius: 5px 0 0 5px;
box-shadow: -2px 2px 3px -2px gray;
background: #FFFFFF;
font-size: 18px;
}
span:after {
content: '';
position: absolute;
top:0;
/* from here it is a matter of tunning */
bottom:-10px;
width:4.5em;/* see this to increase/decrease angle */
background:linear-gradient(230deg, #237ACB 50%,transparent 50% ) no-repeat top right,/* degrees to follow width/angle set earlier */
linear-gradient(to bottom left, #FFFFFF 50%, gray calc(50% + 1px) , transparent calc(50% + 2px) );/* about blending bg and drawing the the slant bottom shadow */
background-size:5px 197%, auto auto;/* about to draw the blue line just as big as it needs */
z-index:-1;
right:-11px;/* tune about width /angle given earlier */
}
<span> Great thanks</span>
http://codepen.io/gc-nomade/pen/qNyAVa
Upvotes: 0
Reputation: 24559
If you were looking to create this design using a single element, you may be able to use pseudo elements in order to make use of the likes of a Skew to create the bottom triangle as well as a border-right on these elements to create the blue section.
A quick demo can be seen below:
.message{
display:inline-block;
min-width:100px;
position:relative;
padding:10px;
padding-right:30px;
background:lightgray;
border-radius:10px 0 0 10px;
box-shadow: 0 2px 2px #222;
}
.message:before,.message:after{
content:"";
position:absolute;
top:0;right:0;
height:100%;
width:20px;
background:inherit;
border-right:5px solid cornflowerblue;
}
.message:before{
height:50%;top:50%;
width:10px;
box-shadow:inherit;
transform:skewY(45deg);
transform-origin:top left;
}
<div class="message">I'm Doing Well Thanks</div>
Upvotes: 1
Reputation: 22992
Using svg
you could create your text bubble and apply linearGradient
to it.
body {
background: #eee;
}
<svg width="100" height="50" preserveAspectRatio="none" viewBox="-1 -1 102 52">
<defs>
<linearGradient id="grad">
<stop offset="97%" stop-color="#fff" />
<stop offset="97%" stop-color="#237ACB" />
</linearGradient>
</defs>
<path d="M0,5 a5,5 0 0,1 5,-5 h95 v45 l-10,-10 h-85 a5,5 0 0,1 -5,-5" fill="url(#grad)" />
<text x="50%" y="40%" text-anchor="middle" font-size="10">Great Thanks</text>
</svg>
For a text bubble to have dynamic text, you'll need to use the triangle as an svg
. The text will be outside the svg
.
body {
background: #eee;
}
#container {
position: relative;
display: table;
}
#text {
position: relative;
max-width: 200px;
padding: 10px;
box-sizing: border-box;
background: linear-gradient(to right, #fff calc(100% - 3px), #237ACB calc(100% - 3px));
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
#tri {
position: absolute;
z-index: 1;
top: calc(100% - 1px);
right: 0;
}
<div id="container">
<svg id="tri" width="15" height="15" preserveAspectRatio="none" viewBox="0 0 15 15">
<defs>
<linearGradient id="grad">
<stop offset="79%" stop-color="#fff" />
<stop offset="79%" stop-color="#237ACB" />
</linearGradient>
</defs>
<path d="M0,0 h15 v15 l-15,-15" fill="url(#grad)" />
</svg>
<div id="text">Text bubble that will change its width to <code>max-width</code>(200px) and height to contain the dynamic text</div>
</div>
box-shadow
:To add a box-shadow
to the svg
you'll have to apply an svg filter
on the path. Doing it through CSS won't work since CSS can't see the actual path.
feFuncA
element's slope
attribute controls the opacity of the shadow, feOffset
is self explanatory.
body {
background: #eee;
}
#container {
position: relative;
display: table;
}
#text {
width: auto;
max-width: 66%;
padding: 12px 30px;
box-sizing: border-box;
background: #ffffff;
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
margin: 4px 0 0 0;
position: relative;
float: right;
border-right: 5px solid #0892cb;
border-radius: 5px 0 0 5px;
}
#tri {
position: absolute;
z-index: 1;
top: calc(100% - 1px);
right: 0;
}
<div id="container">
<svg id="tri" width="15" height="18" preserveAspectRatio="none" viewBox="0 0 15 18">
<defs>
<linearGradient id="grad">
<stop offset="70%" stop-color="#fff" />
<stop offset="70%" stop-color="#0892cb" />
</linearGradient>
<filter id="shadow" height="130%">
<feOffset dx="0" dy="2" in="SourceAlpha" result="offout" />
<feComponentTransfer>
<feFuncA type="linear" slope="0.1" />
</feComponentTransfer>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<path d="M0,0 h15 v15 l-15,-15" filter="url(#shadow)" fill="url(#grad)" />
</svg>
<div id="text">Text bubble that will change its width to <code>max-width</code>(200px) and height to contain the dynamic text</div>
</div>
svg
:To use the same svg
path
multiple times, you could define the path
element within the defs
element and use it multiple times using the use
element as shown in the example below.
body {
background: #eee;
}
.containerIn, .containerOut {
position: relative;
display: table;
margin: 4px 0 15px;
}
.text {
width: auto;
max-width: 66%;
padding: 12px 30px;
box-sizing: border-box;
background: #ffffff;
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
margin: 4px 0 0 0;
position: relative;
float: right;
border-right: 5px solid #0892cb;
border-radius: 5px 0 0 5px;
}
.containerIn .text {
border: 0;
border-left: 5px solid #689F38;
border-radius: 0 5px 5px 0;
float: left;
}
.tri {
position: absolute;
z-index: 1;
top: calc(100% - 1px);
right: 0;
}
.containerIn .tri {
left: 0;
}
<svg width="15" height="18" preserveAspectRatio="none" viewBox="0 0 15 18">
<defs>
<linearGradient id="gradRight">
<stop offset="70%" stop-color="#fff" />
<stop offset="70%" stop-color="#0892cb" />
</linearGradient>
<linearGradient id="gradLeft">
<stop offset="31%" stop-color="#689F38" />
<stop offset="31%" stop-color="#fff" />
</linearGradient>
<filter id="shadow" height="130%">
<feOffset dx="0" dy="2" in="SourceAlpha" result="offout" />
<feComponentTransfer>
<feFuncA type="linear" slope="0.1" />
</feComponentTransfer>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<path id="triRight" d="M0,0 h15 v15z" filter="url(#shadow)" fill="url(#gradRight)" />
<path id="triLeft" d="M0,0 v15 l15,-15z" filter="url(#shadow)" fill="url(#gradLeft)" />
</defs>
</svg>
<div class="containerOut">
<svg class="tri" width="15" height="18" preserveAspectRatio="none" viewBox="0 0 15 18">
<use xlink:href="#triRight" x="0" y="0" />
</svg>
<div class="text">Text bubble that will change its width to <code>max-width</code>(200px) and height to contain the dynamic text</div>
</div>
<div class="containerIn">
<svg class="tri" width="15" height="18" preserveAspectRatio="none" viewBox="0 0 15 18">
<use xlink:href="#triLeft" x="0" y="0" />
</svg>
<div class="text">Text bubble that will change its width to <code>max-width</code>(200px) and height to contain the dynamic text</div>
</div>
<div class="containerIn">
<svg class="tri" width="15" height="18" preserveAspectRatio="none" viewBox="0 0 15 18">
<use xlink:href="#triLeft" x="0" y="0" />
</svg>
<div class="text">Text bubble that will change its width to <code>max-width</code>(200px) and height to contain the dynamic text</div>
</div>
<div class="containerOut">
<svg class="tri" width="15" height="18" preserveAspectRatio="none" viewBox="0 0 15 18">
<use xlink:href="#triRight" x="0" y="0" />
</svg>
<div class="text">Text bubble that will change its width to <code>max-width</code>(200px) and height to contain the dynamic text</div>
</div>
Upvotes: 5
Reputation: 122047
You can use span
for blue border instead of border on p
element if you want border to be bigger then element. Also you need to adjust positions.
This will work only on white background.
* {
box-sizing: border-box;
}
body {
background: white;
}
.message {
width: auto;
max-width: 66%;
padding: 12px 30px;
box-sizing: border-box;
background: #ffffff;
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
margin: 4px 0 0 0;
position: relative;
float: right;
border-radius: 5px 0 0 5px;
margin: 10px;
}
.message:after {
bottom: -9px;
right: -5px;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-bottom-color: #FFFFFF;
border-width: 10px;
transform: rotate(45deg);
box-shadow: 0 2px 0 2px rgba(177, 177, 177, .07), 0 2px 0 0 rgba(0, 0, 0, .1);
}
span {
content: '';
right: 0;
top: 0;
position: absolute;
height: calc(100% + 18px);
border-right: 5px solid #0892cb;
display: inline-block;
}
span:after {
content: '';
width: 11px;
height: 11px;
background: white;
position: absolute;
bottom: -7px;
right: -3px;
transform: rotate(50deg);
}
<div class="message-container customer">
<p class="message">Great thanks <span></span>
</p>
</div>
You can also use SVG
body {
background: #F4F4F3;
}
.st0 {
fill: #B2B3B3;
}
.st1 {
font-size: 30px;
}
.st2 {
fill: #FFFFFF;
}
.st3 {
fill: none;
}
.st4 {
fill: #3079BE;
}
.st5 {
font-size: 20;
}
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="260.5px" height="120.5px" viewBox="0 0 260.5 120.5" style="enable-background:new 0 0 260.5 120.5;" xml:space="preserve">
<path class="st0" d="M256.016,20.217l0.961,97.533l-22.195-22L24.75,96.167c-6.653,0-11.417-5.834-11.917-10.972V20.217
c0-6.429,5.389-11.637,12.042-11.637h219.099c3.51,0,12.041,0,12.041,0S256.016,17.182,256.016,20.217z" />
<g>
<path class="st2" d="M256.018,15.976v99.42l-21.16-20.42H25.688c-6.63,0-12-5.38-12-12v-67c0-6.63,5.37-12,12-12h218.33
c3.499,0,12,0,12,0S256.018,12.845,256.018,15.976z" />
<polygon class="st4" points="256.5,116.524 249.813,110.064 249.813,4.493 256.643,4.493 " />
</g>
<rect x="84.5" y="36" class="st3" width="109.5" height="19.75" />
<text x="50%" y="60px" text-anchor="middle" class="st1 st5">Great thanks</text>
<path class="st0" d="M234.448,97.333" />
</svg>
Upvotes: 2