# Tooltips using SVG Path

--

After the Map Pins, let’s try a more interesting SVG path shape: a Tooltip. Here is an example (click on the Result tab):

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!