Skip to content

Instantly share code, notes, and snippets.

Created Aug 16, 2016
What would you like to do?
Speech Bubble Caret
<blockquote class="speech-bubble">
<p>Do you think we will find a cure for any major disease in the future?</p>
<cite>Dirk Diggler M.D.</cite>

Speech Bubble Caret

Making a triangle for a speech bubble and using transforms to help create the position. Fork this pen and share your solution in the comments.

Note the angles of the triangle as you create your solution.

A Pen by GRAY GHOST on CodePen.


// This solution uses transforms and a caret mixin.
// What will yours be? Fork this pen and share
// your solution in this comments.
// This pen's solution is on line 91.
/// DEMO
/// ==========================================================
$bg:darken(#8CA8D8, 10%);
/// ==========================================================
$gutter: 20px;
$caret-unit: 25px;
$bubble-bg: #8CA8D8;
$bubble-color: white;
$cite-color: $bubble-color;
/// Opposite Direction
/// ==========================================================
/// Returns the opposite direction of each direction in a list
/// @author Hugo Giraudel
/// @param {List} $directions - List of initial directions
/// @return {List} - List of opposite directions
@function opposite-direction($directions) {
$opposite-directions: ();
$direction-map: (
'top': 'bottom',
'right': 'left',
'bottom': 'top',
'left': 'right',
'center': 'center',
'ltr': 'rtl',
'rtl': 'ltr'
@each $direction in $directions {
$direction: to-lower-case($direction);
@if map-has-key($direction-map, $direction) {
$opposite-directions: append($opposite-directions, unquote(map-get($direction-map, $direction)));
} @else {
@warn "No opposite direction can be found for `#{$direction}`. Direction omitted.";
@return $opposite-directions;
/// Unit Removal
@function strip-unit($num) {
@return $num / ($num * 0 + 1);
/// Triangle Mixin
/// ==========================================================
@mixin caret($point, $border-width, $color) {
$opposite: opposite-direction($point);
border: $border-width solid transparent;
border-#{$opposite}: $border-width solid $color;
border-#{$point}: 0;
height: 0;
width: 0;
/// Component
/// ==========================================================
.speech-bubble {
filter: drop-shadow(-1px -1px 2px rgba(black, .10)) drop-shadow(1px 2px 2px rgba(black, .15));
margin: 1rem;
margin-bottom: ($gutter * 2);
padding: 1.5rem 2rem;
position: relative;
font-family: 'Source Sans Pro', sans-serif;
font-size: 1.2rem;
font-weight: 400;
background: $bubble-bg;
color: $bubble-color;
&::before {
@include caret(bottom, ($caret-unit / 2), $bubble-bg);
border-top-width: $caret-unit;
content: '';
display: block;
position: absolute;
left: 3rem;
bottom: -$caret-unit;
transform-origin: center;
transform: rotate(90deg) skew(-(strip-unit($caret-unit))+deg) translateY($caret-unit / 1.5);
.speech-bubble cite {
position: absolute;
bottom: -2rem;
left: 4.5rem;
font-size: 1rem;
font-style: normal;
font-weight: 300;
letter-spacing: 0.5px;
color: $cite-color;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment