lukehillonline
lukehillonline

Reputation: 2647

Give a CSS border arrow a border on 1 side

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:

enter image description here

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

Answers (4)

G-Cyrillus
G-Cyrillus

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

jbutler483
jbutler483

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

Weafs.py
Weafs.py

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>


Applying 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>


Reusing the 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

Nenad Vracar
Nenad Vracar

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

Related Questions