diff --git a/package.json b/package.json
index d45b5dae11fa7faedf7b2c3af0309f19561662bc..d9fc9c4f7b75cbbd2018fadd6e47df08a1e8d914 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
     "angularytics": "0.3.0",
     "crossfilter": "1.3.11",
     "d3": "3.5.3",
+    "d3-tip": "^0.6.7",
     "dc": "2.0.0-beta.1",
     "fixed-data-table": "0.2.0",
     "jquery": "2.1.4",
diff --git a/resources/frontend_client/app/card/card.charting.js b/resources/frontend_client/app/card/card.charting.js
index 3a9833d3ed22f7ebd3c489359f56590994dced2a..dcc3c03244c36455df79b9866467eaa393522f10 100644
--- a/resources/frontend_client/app/card/card.charting.js
+++ b/resources/frontend_client/app/card/card.charting.js
@@ -8,6 +8,9 @@ import d3 from 'd3';
 import dc from 'dc';
 import moment from 'moment';
 
+import tip from 'd3-tip';
+tip(d3);
+
 // ---------------------------------------- TODO - Maybe. Lots of these things never worked in the first place. ----------------------------------------
 // IMPORTANT
 // - 'titles' (tooltips)
@@ -358,12 +361,31 @@ function applyChartColors(dcjsChart, card) {
     return dcjsChart.ordinalColors([chartColor].concat(colorList));
 }
 
-function applyChartTooltips(dcjsChart, card) {
-    // set the title (tooltip) function for points / bars on the chart
-    console.log('tip tip tip');
-    return dcjsChart.title(function(d) {
-        var commasFormatter = d3.format(",.0f");
-        return d.key + ": " + commasFormatter(d.value);
+function applyChartTooltips(dcjsChart, card, cols) {
+    dcjsChart.renderlet(function(chart) {
+        // Remove old tooltips which are sometimes not removed due to chart being rerendered while tip is visible
+        // We should only ever have one tooltip on screen, right?
+        Array.prototype.forEach.call(document.querySelectorAll('.Tooltip'), (t) => t.parentNode.removeChild(t));
+
+        var valueFormatter = d3.format(',.0f');
+
+        var tip = d3.tip()
+            .attr('class', 'Tooltip')
+            .direction('n')
+            .offset([-10, 0])
+            .html(function(d) {
+                return (
+                    '<div><span class="Tooltip-key">' + cols[0].name + '</span> <span class="Tooltip-value">' + d.data.key + '</span></div>' +
+                    '<div><span class="Tooltip-key">' + cols[1].name + '</span> <span class="Tooltip-value">' + valueFormatter(d.data.value) + '</span></div>'
+                );
+            });
+
+        chart.selectAll('rect.bar,circle.dot,g.pie-slice path,circle.bubble,g.row rect')
+            .call(tip)
+            .on('mouseover.tip', tip.show)
+            .on('mouseleave.tip', tip.hide);
+
+        chart.selectAll('title').remove();
     });
 }
 
@@ -824,7 +846,7 @@ export var CardRenderer = {
         // TODO: if we are multi-series this could be split axis
         applyChartYAxis(chart, card, result.cols, data, MIN_PIXELS_PER_TICK.y);
 
-        applyChartTooltips(chart, card);
+        applyChartTooltips(chart, card, result.cols);
         applyChartColors(chart, card);
 
         // if the chart supports 'brushing' (brush-based range filter), disable this since it intercepts mouse hovers which means we can't see tooltips
@@ -901,7 +923,7 @@ export var CardRenderer = {
         // TODO: if we are multi-series this could be split axis
         applyChartYAxis(chart, card, result.cols, data, MIN_PIXELS_PER_TICK.y);
 
-        applyChartTooltips(chart, card);
+        applyChartTooltips(chart, card, result.cols);
         applyChartColors(chart, card);
 
         // if the chart supports 'brushing' (brush-based range filter), disable this since it intercepts mouse hovers which means we can't see tooltips
diff --git a/resources/frontend_client/app/components/tooltip/tooltip.css b/resources/frontend_client/app/components/tooltip/tooltip.css
new file mode 100644
index 0000000000000000000000000000000000000000..9323f19e160c367af8909b36c5c1bd708cada5e8
--- /dev/null
+++ b/resources/frontend_client/app/components/tooltip/tooltip.css
@@ -0,0 +1,67 @@
+/* based on https://rawgit.com/Caged/d3-tip/master/examples/example-styles.css */
+
+.Tooltip {
+  line-height: 1;
+  padding: 12px;
+  background: rgba(0, 0, 0, 0.8);
+  color: #fff;
+  border-radius: 2px;
+  pointer-events: none;
+  border-radius: 4px;
+}
+
+/* Creates a small triangle extender for the tooltip */
+.Tooltip:after {
+  box-sizing: border-box;
+  display: inline;
+  font-size: 10px;
+  width: 100%;
+  line-height: 1;
+  color: rgba(0, 0, 0, 0.8);
+  position: absolute;
+  pointer-events: none;
+}
+
+/* Northward tooltips */
+.Tooltip.n:after {
+  content: "\25BC";
+  margin: -1px 0 0 0;
+  top: 100%;
+  left: 0;
+  text-align: center;
+}
+
+/* Eastward tooltips */
+.Tooltip.e:after {
+  content: "\25C0";
+  margin: -4px 0 0 0;
+  top: 50%;
+  left: -8px;
+}
+
+/* Southward tooltips */
+.Tooltip.s:after {
+  content: "\25B2";
+  margin: 0 0 1px 0;
+  top: -8px;
+  left: 0;
+  text-align: center;
+}
+
+/* Westward tooltips */
+.Tooltip.w:after {
+  content: "\25B6";
+  margin: -4px 0 0 -1px;
+  top: 50%;
+  left: 100%;
+}
+
+.Tooltip-key {
+    font-weight: bold;
+}
+.Tooltip-key:after {
+    content: ":";
+}
+
+.Tooltip-value {
+}
diff --git a/webpack.config.js b/webpack.config.js
index 971839e6fb174e5e93af67ffbf2bcb8e2dc61427..a1eae0e5784cfdf9dfe9edf59869b82572a5b27f 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -98,6 +98,7 @@ module.exports = {
             'd3':                   __dirname + '/node_modules/d3/d3.min.js',
             'crossfilter':          __dirname + '/node_modules/crossfilter/index.js',
             'dc':                   __dirname + '/node_modules/dc/dc.js',
+            'd3-tip':               __dirname + '/node_modules/d3-tip/index.js'
         }
     },