I have a project where I need to insert speech bubbles / message boxes. The general shape I am trying to achieve is this one :
.bubble { height: 100px; width: 200px; border: 3px solid gray; background: lightgray; position: relative; cursor:pointer; } .triangle { width: 0; border-top: 20px solid black; border-left: 20px solid transparent; border-right: 20px solid transparent; cursor:pointer; }
<div class="bubble">Speech bubble </div> <div class="triangle"> </div>
This currently does not pass a hit-test as the transparent border is also clickable.
Objectives
The hit box (clickable / hoverable areas) needs to stick to the shape's boundaries (the transparent borders here are also hoverable, invalidating this).
I need to display the shape over various content (images, gradents, text...),
Issues
The main issues I am having when manipulating this shape are:
Is there anyway of addressing these issues?
Speech bubbles An in-panel character (one who is fully or mostly visible in the panel of the strip of comic that the reader is viewing) uses a bubble with a pointer, termed a tail, directed towards the speaker.
Speech input is depicted on the left of the model and output on the right; arrows indicate the flow of information through a series of hypothesized stages, and stored representations appear at the top in the bolded ...
You can drag the text bubble bounding box (the blue dots that you would use to resize the bubble) over to the other side. This effectively over-"resizes" the bubble so it is mirrored. Show activity on this post. Just drag the cursor starting from the direction where you want the point to be.
This style of speech bubble which is spiky with jagged edges and has a long tail is used to indicate someone screaming or shouting. The tail may not necessarily reach the source's mouth but may simply point at the character from the top or from the side.
In order to achieve this, you should consider altering your markup in order to make your html more efficient. This can be achieved using a pseudo element. I'll address each point individually, and put it all together at the end of my answer.
First of all,
You could use a pseudo element to remove the extra .triangle
div. This not only reduces your div numbers, but also helps with positioning as you can use the top:
left:
right:
and bottom:
css properties in order to position according to your main element. This can be seen below:
.oneAndOnlyDiv { height: 100px; width: 200px; border: 3px solid gray; background: lightgray; position: relative; } .oneAndOnlyDiv:before { content: ""; position: absolute; top: 100%; left: 20px; width: 0; border-top: 20px solid black; border-left: 20px solid transparent; border-right: 20px solid transparent; }
<div class="oneAndOnlyDiv">Main div</div>
In order to create your "hit test", you may wish to use a rotated element instead of a border hack.
Something like:
div { height: 100px; width: 200px; background: gray; position: relative; cursor:pointer; } div:before { content: ""; position: absolute; top: 100%; left: 20px; height: 20px; width: 20px; background: black; transform: rotate(45deg); transform-origin:top right; }
<div>Only element</div>
or use a skewed pseudo element:
div { height: 100px; width: 200px; background: gray; position: relative; cursor:pointer; } div:before { content: ""; position: absolute; top: 90%; left: 20px; height: 30%; width: 20px; background: black; transform: skewY(-45deg); transform-origin:bottom left; z-index:-1; }
<div>Only element</div>
which will show the pointer only when the square or main element is hovered. But hang on, that messes up the positioning? how can you deal with that?
There are a few solutions to that. One of which is to use the calc
CSS property.
div { height: 100px; width: 200px; background: gray; position: relative; cursor:pointer; } div:before { content: ""; position: absolute; top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/ top: calc(100% - 10px); /*i.e. half the height*/ left: 20px; height: 20px; width: 20px; background: gray; transform: rotate(45deg); }
<div>Only element</div>
You can add a border quite easily now, simply by adding a border declaration to the main element, and setting the border-bottom
and border-right
of the pseudo element to inherit
Border
div { height: 100px; width: 200px; background: gray; position: relative; cursor:pointer; border:3px double black; } div:before { content: ""; position: absolute; top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/ top: calc(100% - 10px); /*i.e. half the height*/ left: 20px; height: 20px; width: 20px; background: gray; transform: rotate(45deg); border-bottom:inherit; border-right:inherit; box-shadow:inherit; }
<div>Only element</div>
Box Shadow:
In order to have a box shadow, I've used the :after
pseudo element in order to hide the box shadow over the other pseudo, making the element seem as one single element.
div { height: 100px; width: 200px; background: gray; position: relative; cursor:pointer; box-shadow: 5px 5px 10px 2px black; } div:before,div:after { content: ""; position: absolute; top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/ top: calc(100% - 10px); /*i.e. half the height*/ left: 20px; height: 20px; width: 20px; background: gray; transform: rotate(45deg); z-index:-1; box-shadow:inherit; } div:after{ box-shadow:none; z-index:8; }
<div>Only element</div>
You can also add a border radius to your message box or speech bubble by again, using the border-radius property:
div { height: 100px; width: 200px; background: gray; position: relative; cursor:pointer; border:3px double black; border-radius:10px; } div:before { content: ""; position: absolute; top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/ top: calc(100% - 10px); /*i.e. half the height*/ left: 20px; height: 20px; width: 20px; background: gray; transform: rotate(45deg); border-bottom:inherit; border-right:inherit; box-shadow:inherit; }
<div>Only element</div>
This even allows you to create not only a triangle, but how about a circle instead?
div { height: 100px; width: 200px; background: gray; position: relative; cursor:pointer; border:3px double black; border-radius:10px; } div:before { content: ""; position: absolute; top: -webkit-calc(100% - 13px); /*may require prefix for old browser support*/ top: calc(100% - 13px); /*i.e. half the height + border*/ left: 20px; height: 20px; width: 20px; background: gray; transform: rotate(45deg); border:3px double transparent; border-bottom:inherit; border-right:inherit; box-shadow:inherit; border-radius:50%; }
<div>Only element</div>
If you are having issues with content overflowing and being 'hidden' behind this pseudo element, and you aren't fussed about having a border, you could use a negative z-index which will solve this issue.
If you don't like the idea of using a calc value, in which the positioning in my answer is currently using (whilst working), you may wish to use transform:translate(50%)
This would be a much better approach, since:
div { height: 100px; width: 200px; background: gray; position: relative; cursor: pointer; border: 3px double black; border-radius: 10px; } div:before { content: ""; position: absolute; top: 100%; left: 30px; height: 20px; width: 20px; background: gray; box-sizing:border-box; transform: rotate(45deg) translate(-50%); border-bottom: inherit; border-right: inherit; box-shadow: inherit; }
<div>Only element</div>
Want to move it? You can!
div { height: 100px; width: 200px; background: gray; position: relative; cursor: pointer; border: 3px double black; border-radius: 10px; } div:before { content: ""; position: absolute; top: 100%; left: 10%; height: 20px; width: 20px; background: gray; box-sizing: border-box; transform: rotate(45deg) translate(-50%); border-bottom: inherit; border-right: inherit; box-shadow: inherit; transition: all 0.8s; } div:hover:before { left: 90%; }
<div>Only element</div>
Want it one the right?
div { height: 100px; width: 200px; background: gray; position: relative; cursor: pointer; border: 3px double black; border-radius: 10px; } div:before { content: ""; position: absolute; top: 15%; left: 100%; height: 20px; width: 20px; background: gray; box-sizing:border-box; transform: rotate(45deg) translate(-50%); border-top: inherit; border-right: inherit; box-shadow: inherit; transition:all 0.8s; } div:hover:before{ top:80%; }
<div>Only Element</div>
Want it to be a different shape of triangle?
div { height: 100px; width: 200px; background: gray; position: relative; cursor: pointer; border-radius: 10px; } div:before { content: ""; position: absolute; top: 70%; left: 100%; height: 20px; width: 20px; background: gray; box-sizing:border-box; transform: translate(-50%) skewX(45deg); box-shadow: inherit; transition:all 0.8s; z-index:-1; } div:hover:before{ transform: translate(-50%); border-radius:50%; top:20%; }
<div>Only Element</div>
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