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.
The CSS border properties allow you to specify the style, width, and color of an element's border. I have borders on all sides. I have a red bottom border. I have rounded borders. I have a blue left border.
You can specifically declare the border on just three sides: div { border-top: 1px solid red; border-right: 1px solid red; border-bottom: 1px solid red; }. Verbose, but easy to understand. Or, you could declare just a border which will cover all four sides and remove the one you don't want.
The element needing multiple borders should have its own border and relative positioning. The secondary border is added with a pseudo element. It is set with absolute positioning and inset with top/left/bottom/right values.
The secondary border is added with a pseudo element. It is set with absolute positioning and inset with top/left/bottom/right values. This will also have a border and is kept beneath the content (preserving, for example, selectability of text and clickability of links) by giving it a negative z-index value.
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>
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With