# Tooltips using SVG Path

Feb 24, 2018 · 4 min read

After the Map Pins, let’s try a more interesting SVG path shape: a Tooltip. Here is an example:

The Tooltip shape’s path is defined by 5 parameters: width, height, pointer offset, corner radius, and placement (left, top, right, or bottom).

To make things simple, let’s first build a path for a top-positioned Tooltip without rounded corners. That is what we have:

1. Move to A: M aX,aY
2. Line to B: L bX,bY
3. Horizontal line to C: H cX
4. Vertical Line to D: V dY
5. Horizontal line to E: H eX
6. Vertical Line to F: V fY
7. Horizontal line to G: H gX
8. Line to A: L aX,aY
9. Close the path: z

The point A coordinates are 0, 0. The other points are calculated from width, height, and offset:

`bx = -offsetby = -offset          // also cy, fy, gycx = -width / 2       // also dxdy = -offset - height // also eyex = width / 2        // also fxgx = offset`

… and the whole path will look like this:

`function topTooltipPath(width, height, offset) {  const left = -width / 2  const right = width / 2  const top = -offset - height  const bottom = -offset  return `M 0,0     L \${-offset},\${bottom}     H \${left}     V \${top}     H \${right}     V \${bottom}     H \${offset}     L 0,0 z`}`

To get rounded corners, we have two options: Arc and Quadratic Bezier curve. An Arc uses 7 parameters (see the Pin example). A QB curve is much simpler: It takes the current point of path (set by previous Move to / Line to) and two additional points: the Vertex and the Target. In SVG, a QB curve is specified by letter Q: Q vX,vY tX,tY.

For the corner C, the starting point of the curve is on the incoming line (H cX) right to C (sX = cX + r; sY = cY). The target point in on the outgoing line (V dY), above C (tX = cX; tY = cY - r). The QB vertex is the C point itself. We just replace … H cX V dY … with: … H (cX + r) Q cX, cY cX, (cY - r) V dY …, and get the C corner rounded (by radius r). Here is an improved function:

`function topTooltipPath(width, height, offset, radius) {  const left = -width / 2  const right = width / 2  const top = -offset - height  const bottom = -offset  return `M 0,0     L \${-offset},\${bottom}     H \${left + radius}    Q \${left},\${bottom} \${left},\${bottom - radius}      V \${top + radius}       Q \${left},\${top} \${left + radius},\${top}    H \${right - radius}    Q \${right},\${top} \${right},\${top + radius}    V \${bottom - radius}    Q \${right},\${bottom} \${right - radius},\${bottom}    H \${offset}     L 0,0 z`}`

To make a bottom-positioned tooltip, we just invert all the Y coordinates in the path:

`function bottomTooltipPath(width, height, offset, radius) {  const left = -width / 2  const right = width / 2  const bottom = offset + height  const top = offset  return `M 0,0     L \${-offset},\${top}     H \${left + radius}    Q \${left},\${top} \${left},\${top + radius}      V \${bottom - radius}       Q \${left},\${bottom} \${left + radius},\${bottom}    H \${right - radius}    Q \${right},\${bottom} \${right},\${bottom - radius}    V \${top + radius}    Q \${right},\${top} \${right - radius},\${top}    H \${offset}     L 0,0 z`}`

The same is true for the X coordinates of the left and right-positioned tooltips:

`function leftTooltipPath(width, height, offset, radius) {  const left = -offset - width  const right = -offset  const top = -height / 2  const bottom = height / 2  return `M 0,0     L \${right},\${-offset}     V \${top + radius}    Q \${right},\${top} \${right - radius},\${top}      H \${left + radius}       Q \${left},\${top} \${left},\${top + radius}    V \${bottom - radius}    Q \${left},\${bottom} \${left + radius},\${bottom}    H \${right - radius}    Q \${right},\${bottom} \${right},\${bottom - radius}    V \${offset}     L 0,0 z`}function rightTooltipPath(width, height, offset, radius) {  const left = offset  const right = offset + width  const top = -height / 2  const bottom = height / 2  return `M 0,0     L \${left},\${-offset}     V \${top + radius}    Q \${left},\${top} \${left + radius},\${top}      H \${right - radius}       Q \${right},\${top} \${right},\${top + radius}    V \${bottom - radius}    Q \${right},\${bottom} \${right - radius},\${bottom}    H \${left + radius}    Q \${left},\${bottom} \${left},\${bottom - radius}    V \${offset}     L 0,0 z`}`

Here is a function that does it all:

`function tooltipPath(width, height, offset, radius, position) {  let left, top, right, bottom  switch(position) {    case 'top':       return topTooltipPath(width, height, offset, radius)    case 'left':      return leftTooltipPath(width, height, offset, radius)    case 'right':      return rightTooltipPath(width, height, offset, radius)    case 'bottom':      return bottomTooltipPath(width, height, offset, radius)  }}`

For those valuing the JavaScript elegance:

`class Tooltip {  topTooltipPath(width, height, offset, radius) {...}  leftTooltipPath(width, height, offset, radius) {...}  rightTooltipPath(width, height, offset, radius) {...}  bottomTooltipPath(width, height, offset, radius) {...}  tooltipPath(width, height, offset, radius, position) {    const method = `\${position}TooltipPath`    return this[method](width, height, offset, radius)  }}`

Now a final demo with movable objects and their tooltips. A tooltip’s appearance depends on the object’s position:

Have a nice day!

Written by

## Welldone Software

#### The leading full-stack consultancy. Creating amazing frontends and rock-solid backends using top notch technologies and practices. Visit us at https://welldone.software.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just \$5/month. Upgrade