From 332e43403992cc114a21cb03aa8296a9a8da16d8 Mon Sep 17 00:00:00 2001
From: Benaka Moorthi <benaka.moorthi@gmail.com>
Date: Fri, 19 Jul 2013 10:27:58 -0400
Subject: [PATCH] Refs #3942, allow server-side and JS UI code to use colors
 defined in CSS.

Notes:
 - Removed colors.piwik.json loading code. No longer necessary.
 - UserCountryMap is the only remaining portion of Piwik that doesn't use this technique. Not yet, anyway.
---
 core/API/DocumentationGenerator.php           |   2 +-
 core/AssetManager.php                         |   1 +
 core/Controller.php                           |   9 +-
 core/Plugin/MetadataLoader.php                |  49 +-----
 core/Twig.php                                 |  10 +-
 core/Visualization/Chart.php                  |   4 +-
 core/Visualization/Chart/Evolution.php        |  12 +-
 core/Visualization/Chart/Pie.php              |   4 -
 core/Visualization/Chart/VerticalBar.php      |   5 -
 core/Visualization/Sparkline.php              |  32 ++--
 core/testMinimumPhpVersion.php                |   2 +-
 plugins/CoreHome/CoreHome.php                 |   2 +
 .../DataTableRowAction/RowEvolution.php       |   9 +-
 plugins/CoreHome/javascripts/color_manager.js | 132 +++++++++++++++
 .../javascripts/datatable_rowactions.js       |   1 +
 plugins/CoreHome/javascripts/jqplot.js        | 126 ++++++++-------
 plugins/CoreHome/javascripts/sparkline.js     |  39 +++--
 .../CoreHome/stylesheets/color_manager.css    |   3 +
 .../getMultiRowEvolutionPopover.twig          |   7 +-
 .../templates/getRowEvolutionPopover.twig     |  13 +-
 plugins/Referers/Controller.php               |   2 +-
 .../Transitions/javascripts/transitions.js    |  62 +++++---
 plugins/Zeitgeist/colors.piwik.json           |   3 -
 plugins/Zeitgeist/stylesheets/base.less       |   8 +-
 .../Zeitgeist/stylesheets/general/_misc.less  |   7 +
 .../Zeitgeist/stylesheets/general/_utils.less |   8 +
 .../Zeitgeist/stylesheets/ui/_dataTable.less  |   3 +
 plugins/Zeitgeist/stylesheets/ui/_jqplot.less | 150 ++++++++++++++++++
 .../Zeitgeist/stylesheets/ui/_sparkline.less  |  26 +++
 .../stylesheets/ui/_transitions.less          |  98 ++++++++++++
 tests/PHPUnit/Core/AccessTest.php             |   6 +
 31 files changed, 639 insertions(+), 196 deletions(-)
 create mode 100644 plugins/CoreHome/javascripts/color_manager.js
 create mode 100644 plugins/CoreHome/stylesheets/color_manager.css
 delete mode 100644 plugins/Zeitgeist/colors.piwik.json
 create mode 100644 plugins/Zeitgeist/stylesheets/general/_misc.less
 create mode 100644 plugins/Zeitgeist/stylesheets/ui/_dataTable.less
 create mode 100644 plugins/Zeitgeist/stylesheets/ui/_jqplot.less
 create mode 100644 plugins/Zeitgeist/stylesheets/ui/_sparkline.less
 create mode 100644 plugins/Zeitgeist/stylesheets/ui/_transitions.less

diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php
index bc3dcf8b01..ebc2b1ce98 100644
--- a/core/API/DocumentationGenerator.php
+++ b/core/API/DocumentationGenerator.php
@@ -98,7 +98,7 @@ class Piwik_API_DocumentationGenerator
                 $str .= '</small>';
                 $str .= "</div>\n";
             }
-            $str .= '<div style="margin:15px;"><a href="#topApiRef" style="color:#95AECB">↑ Back to top</a></div>';
+            $str .= '<div style="margin:15px;"><a href="#topApiRef">↑ Back to top</a></div>';
         }
 
         $str = "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2>
diff --git a/core/AssetManager.php b/core/AssetManager.php
index 307ad4a99a..761ea0d2ab 100644
--- a/core/AssetManager.php
+++ b/core/AssetManager.php
@@ -253,6 +253,7 @@ class Piwik_AssetManager
     {
         $priorityCssOrdered = array(
             'libs/',
+            'plugins/CoreHome/stylesheets/color_manager.css', // must be before other Piwik stylesheets
             'plugins/Zeitgeist/stylesheets/base.less',
             'plugins/Zeitgeist/stylesheets/',
             'plugins/',
diff --git a/core/Controller.php b/core/Controller.php
index eb70f9a714..b37b7c57da 100644
--- a/core/Controller.php
+++ b/core/Controller.php
@@ -809,12 +809,13 @@ abstract class Piwik_Controller
 
         $titleEvolutionPercent = $evolutionPercent;
         if ($evolutionPercent < 0) {
-            $color = "#e02a3b"; //red
+            $class = "negative-evolution";
             $img = "arrow_down.png";
         } else if ($evolutionPercent == 0) {
+            $class = "neutral-evolution";
             $img = "stop.png";
         } else {
-            $color = "green";
+            $class = "positive-evolution";
             $img = "arrow_up.png";
             $titleEvolutionPercent = '+' . $titleEvolutionPercent;
         }
@@ -830,8 +831,8 @@ abstract class Piwik_Controller
         $result = '<span class="metricEvolution" title="' . $title
             . '"><img style="padding-right:4px" src="plugins/MultiSites/images/' . $img . '"/><strong';
 
-        if (isset($color)) {
-            $result .= ' style="color:' . $color . '"';
+        if (isset($class)) {
+            $result .= ' class="' . $class . '"';
         }
         $result .= '>' . $evolutionPercent . '</strong></span>';
 
diff --git a/core/Plugin/MetadataLoader.php b/core/Plugin/MetadataLoader.php
index 1fe999edce..8bdd5b9ff8 100644
--- a/core/Plugin/MetadataLoader.php
+++ b/core/Plugin/MetadataLoader.php
@@ -17,15 +17,10 @@ require_once PIWIK_INCLUDE_PATH . '/core/Version.php';
 /**
  * Loads plugin metadata found in the following files:
  * - plugin.piwik.json
- * - colors.piwik.json
  */
 class Piwik_Plugin_MetadataLoader
 {
     const PLUGIN_JSON_FILENAME = 'plugin.piwik.json';
-    const COLORS_JSON_FILENAME = 'colors.piwik.json';
-    
-    const SHORT_COLOR_LENGTH = 4;
-    const LONG_COLOR_LENGTH = 7;
     
     /**
      * The name of the plugin whose metadata will be loaded.
@@ -53,8 +48,7 @@ class Piwik_Plugin_MetadataLoader
     {
         return array_merge(
             $this->getDefaultPluginInformation(),
-            $this->loadPluginInfoJson(),
-            $this->loadPluginColorsJson()
+            $this->loadPluginInfoJson()
         );
     }
     
@@ -79,47 +73,6 @@ class Piwik_Plugin_MetadataLoader
         return $this->loadJsonMetadata($path);
     }
     
-    private function loadPluginColorsJson()
-    {
-        $path = Piwik_PluginsManager::getPluginsDirectory() . $this->pluginName . '/' . self::COLORS_JSON_FILENAME;
-        $info = $this->loadJsonMetadata($path);
-        $info = $this->cleanAndValidatePluginColorsJson($path, $info);
-        return $info;
-    }
-    
-    private function cleanAndValidatePluginColorsJson($path, $info)
-    {
-        // check that if "colors" exists, it is an array
-        $colors = isset($info["colors"]) ? $info["colors"] : array();
-        if (!is_array($colors)) {
-            throw new Exception("The 'colors' value in '$path' must be an object mapping names with colors.");
-        }
-        
-        // validate each color
-        foreach ($colors as $color) {
-            if (!$this->isStringColorValid($color)) {
-                throw new Exception("Invalid color string '$color' in '$path'.");
-            }
-        }
-        
-        return array("colors" => $colors); // make sure only 'colors' element is loaded
-    }
-    
-    private function isStringColorValid($color)
-    {
-        if (strlen($color) !== self::SHORT_COLOR_LENGTH
-            && strlen($color) !== self::LONG_COLOR_LENGTH
-        ) {
-            return false;
-        }
-        
-        if ($color[0] !== '#') {
-            return false;
-        }
-        
-        return ctype_xdigit(substr($color, 1)); // check if other digits are hex
-    }
-    
     private function loadJsonMetadata($path)
     {
         if (!file_exists($path)) {
diff --git a/core/Twig.php b/core/Twig.php
index a3f69dbc7e..8e4bcb7c01 100644
--- a/core/Twig.php
+++ b/core/Twig.php
@@ -18,6 +18,9 @@
  */
 class Piwik_Twig
 {
+    const SPARKLINE_TEMPLATE = '<img class="sparkline" alt="" data-src="%s" width="%d" height="%d" />
+    <script type="text/javascript">$(document).ready(function () { piwik.initSparklines(); });</script>';
+    
     /**
      * @var Twig_Environment
      */
@@ -90,10 +93,9 @@ class Piwik_Twig
     protected function addFunction_sparkline()
     {
         $sparklineFunction = new Twig_SimpleFunction('sparkline', function ($src) {
-            $graph = new Piwik_Visualization_Sparkline();
-            $width = $graph->getWidth();
-            $height = $graph->getHeight();
-            return sprintf('<img class="sparkline" alt="" src="%s" width="%d" height="%d" />', $src, $width, $height);
+            $width = Piwik_Visualization_Sparkline::DEFAULT_WIDTH;
+            $height = Piwik_Visualization_Sparkline::DEFAULT_HEIGHT;
+            return sprintf(Piwik_Twig::SPARKLINE_TEMPLATE, $src, $width, $height);
         }, array('is_safe' => array('html')));
         $this->twig->addFunction($sparklineFunction);
     }
diff --git a/core/Visualization/Chart.php b/core/Visualization/Chart.php
index 74d4bf1b52..d825aefd98 100644
--- a/core/Visualization/Chart.php
+++ b/core/Visualization/Chart.php
@@ -24,7 +24,6 @@ abstract class Piwik_Visualization_Chart implements Piwik_View_Interface
     protected $data = array();
     protected $axes = array();
     protected $tooltip = array();
-    protected $seriesColors = array('#000000');
     protected $seriesPicker = array();
 
     // other attributes (not directly used for jqplot)
@@ -157,8 +156,7 @@ abstract class Piwik_Visualization_Chart implements Piwik_View_Interface
         $data = array(
             'params'       => array(
                 'axes'         => &$this->axes,
-                'series'       => &$this->series,
-                'seriesColors' => &$this->seriesColors
+                'series'       => &$this->series
             ),
             'data'         => &$this->data,
             'tooltip'      => &$this->tooltip,
diff --git a/core/Visualization/Chart/Evolution.php b/core/Visualization/Chart/Evolution.php
index e8e82527cf..db00d00bce 100644
--- a/core/Visualization/Chart/Evolution.php
+++ b/core/Visualization/Chart/Evolution.php
@@ -17,9 +17,7 @@
  */
 class Piwik_Visualization_Chart_Evolution extends Piwik_Visualization_Chart
 {
-
-    protected $seriesColors = array('#5170AE', '#F29007', '#CC3399', '#9933CC', '#80a033',
-                                    '#246AD2', '#FD16EA', '#49C100');
+    const SERIES_COLOR_COUNT = 8;
 
     public function customizeChartProperties()
     {
@@ -34,14 +32,8 @@ class Piwik_Visualization_Chart_Evolution extends Piwik_Visualization_Chart
         }
     }
 
-    public function getSeriesColors()
-    {
-        return $this->seriesColors;
-    }
-
     public function setSelectableRows($selectableRows)
     {
         $this->seriesPicker['selectableRows'] = $selectableRows;
     }
-
-}
+}
\ No newline at end of file
diff --git a/core/Visualization/Chart/Pie.php b/core/Visualization/Chart/Pie.php
index f37b079ddc..cd0e137ebf 100644
--- a/core/Visualization/Chart/Pie.php
+++ b/core/Visualization/Chart/Pie.php
@@ -17,10 +17,6 @@
  */
 class Piwik_Visualization_Chart_Pie extends Piwik_Visualization_Chart
 {
-
-    protected $seriesColors = array('#59727F', '#7DAAC0', '#7F7259', '#C09E7D', '#9BB39B',
-                                    '#B1D8B3', '#B39BA7', '#D8B1C5', '#A5A5A5');
-
     function customizeChartProperties()
     {
         if (count($this->data) == 0) {
diff --git a/core/Visualization/Chart/VerticalBar.php b/core/Visualization/Chart/VerticalBar.php
index 89fe7fb9a1..dab4a8ef16 100644
--- a/core/Visualization/Chart/VerticalBar.php
+++ b/core/Visualization/Chart/VerticalBar.php
@@ -17,10 +17,6 @@
  */
 class Piwik_Visualization_Chart_VerticalBar extends Piwik_Visualization_Chart
 {
-
-    protected $seriesColors = array('#5170AE', '#F3A010', '#CC3399', '#9933CC', '#80a033',
-                                    '#246AD2', '#FD16EA', '#49C100');
-
     public function customizeChartProperties()
     {
         parent::customizeChartProperties();
@@ -42,5 +38,4 @@ class Piwik_Visualization_Chart_VerticalBar extends Piwik_Visualization_Chart
             }
         }
     }
-
 }
diff --git a/core/Visualization/Sparkline.php b/core/Visualization/Sparkline.php
index 5ea0ca1571..e7165fbb15 100644
--- a/core/Visualization/Sparkline.php
+++ b/core/Visualization/Sparkline.php
@@ -24,23 +24,28 @@ require_once PIWIK_INCLUDE_PATH . '/libs/sparkline/lib/Sparkline_Line.php';
  */
 class Piwik_Visualization_Sparkline implements Piwik_View_Interface
 {
+    const DEFAULT_WIDTH = 100;
+    const DEFAULT_HEIGHT = 25;
+
+    private static $colorNames = array('lineColor', 'red', 'blue', 'green');
+    
     /**
      * Width of the sparkline
      * @var int
      */
-    protected $_width = 100;
+    protected $_width = self::DEFAULT_WIDTH;
 
     /**
      * Height of sparkline
      * @var int
      */
-    protected $_height = 25;
+    protected $_height = self::DEFAULT_HEIGHT;
 
     /**
      * Array with format: array( x, y, z, ... )
      * @param array $data
      */
-    function setValues($data)
+    public function setValues($data)
     {
         $this->values = $data;
     }
@@ -91,16 +96,13 @@ class Piwik_Visualization_Sparkline implements Piwik_View_Interface
         return $this->_height;
     }
 
-    function main()
+    public function main()
     {
         $width = $this->getWidth();
         $height = $this->getHeight();
 
         $sparkline = new Sparkline_Line();
-        $sparkline->SetColor('lineColor', 22, 44, 74); // dark blue
-        $sparkline->SetColorHtml('red', '#FF7F7F');
-        $sparkline->SetColorHtml('blue', '#55AAFF');
-        $sparkline->SetColorHtml('green', '#75BF7C');
+        $this->setSparklineColors($sparkline);
 
         $min = $max = $last = null;
         $i = 0;
@@ -137,8 +139,20 @@ class Piwik_Visualization_Sparkline implements Piwik_View_Interface
         $this->sparkline = $sparkline;
     }
 
-    function render()
+    public function render()
     {
         $this->sparkline->Output();
     }
+
+    private function setSparklineColors($sparkline)
+    {
+        $colors = Piwik_Common::getRequestVar('colors', false, 'json');
+        if (!empty($colors)) {
+            foreach (self::$colorNames as $name) {
+                if (!empty($colors[$name])) {
+                    $sparkline->SetColorHtml($name, $colors[$name]);
+                }
+            }
+        }
+    }
 }
diff --git a/core/testMinimumPhpVersion.php b/core/testMinimumPhpVersion.php
index e44c39ea1e..845ba6002a 100644
--- a/core/testMinimumPhpVersion.php
+++ b/core/testMinimumPhpVersion.php
@@ -79,7 +79,7 @@ if (!function_exists('Piwik_ExitWithMessage')) {
     {
         @header('Content-Type: text/html; charset=utf-8');
         if ($optionalTrace) {
-            $optionalTrace = '<span style="color:#888888">Backtrace:<br /><pre>' . $optionalTrace . '</pre></span>';
+            $optionalTrace = '<span class="exception-backtrace">Backtrace:<br /><pre>' . $optionalTrace . '</pre></span>';
         }
         if ($optionalLinks) {
             $optionalLinks = '<ul>
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index 07820ffe01..cd489cd764 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -47,6 +47,7 @@ class Piwik_CoreHome extends Piwik_Plugin
         $cssFiles[] = "plugins/CoreHome/stylesheets/jquery.ui.autocomplete.css";
         $cssFiles[] = "plugins/CoreHome/stylesheets/jqplot.css";
         $cssFiles[] = "plugins/CoreHome/stylesheets/promo.less";
+        $cssFiles[] = "plugins/CoreHome/stylesheets/color_manager.css";
     }
 
     public function getJsFiles(&$jsFiles)
@@ -75,6 +76,7 @@ class Piwik_CoreHome extends Piwik_Plugin
         $jsFiles[] = "plugins/CoreHome/javascripts/jqplot.js";
         $jsFiles[] = "libs/jqplot/jqplot-custom.min.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/promo.js";
+        $jsFiles[] = "plugins/CoreHome/javascripts/color_manager.js";
     }
 
 }
diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
index c15aaee7ec..6d2abe8a86 100644
--- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php
+++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
@@ -204,8 +204,7 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution
     protected function getMetricsToggles()
     {
         $chart = new Piwik_Visualization_Chart_Evolution;
-        $colors = $chart->getSeriesColors();
-
+        
         $i = 0;
         $metrics = array();
         foreach ($this->availableMetrics as $metric => $metricData) {
@@ -239,10 +238,8 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution
                 $details .= ', ' . Piwik_Translate('RowEvolution_MetricChangeText', $change);
             }
 
-            $color = $colors[$i % count($colors)];
             $newMetric = array(
                 'label'     => $metricData['name'],
-                'color'     => $color,
                 'details'   => $details,
                 'sparkline' => $this->getSparkline($metric),
             );
@@ -261,11 +258,11 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution
     protected function getSparkline($metric)
     {
         // sparkline is always echoed, so we need to buffer the output
-        ob_start();
         $view = $this->getRowEvolutionGraph($graphType = 'sparkline', $metrics = array($metric => $metric));
         $view->main();
-        $view->getView()->render();
 
+        ob_start();
+        $view->getView()->render();
         $spark = ob_get_contents();
         ob_end_clean();
 
diff --git a/plugins/CoreHome/javascripts/color_manager.js b/plugins/CoreHome/javascripts/color_manager.js
new file mode 100644
index 0000000000..cfdc26bbff
--- /dev/null
+++ b/plugins/CoreHome/javascripts/color_manager.js
@@ -0,0 +1,132 @@
+/*!
+ * Piwik - Web Analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+(function ($) {
+
+    /**
+     * The ColorManager class allows JS code to grab colors defined in CSS for
+     * components that don't manage HTML (like jqPlot or sparklines). Such components
+     * can't use CSS colors directly since the colors are used to generate images
+     * or by <canvas> elements.
+     * 
+     * Colors obtained via ColorManager are defined in CSS like this:
+     * 
+     * .my-color-namespace[data-name=color-name] {
+     *     color: #fff
+     * }
+     * 
+     * and can be accessed in JavaScript like this:
+     * 
+     * piwik.ColorManager.getColor("my-color-namespace", "color-name");
+     * 
+     * The singleton instance of this class can be accessed via piwik.ColorManager.
+     */
+    var ColorManager = function () {
+        // empty
+    };
+    
+    ColorManager.prototype = {
+
+        /**
+         * Returns the color for a namespace and name.
+         * 
+         * @param {String} namespace The string identifier that groups related colors
+         *                           together. For example, 'sparkline-colors'.
+         * @param {String} name The name of the color to retrieve. For example, 'lineColor'.
+         * @return {String} A hex color, eg, '#fff'.
+         */
+        getColor: function (namespace, name) {
+            var element = this._getElement();
+
+            element.attr('class', 'color-manager ' + namespace).attr('data-name', name);
+            return this._normalizeColor(element.css('color'));
+        },
+        
+        /**
+         * Returns the colors for a namespace and a list of names.
+         * 
+         * @param {String} namespace The string identifier that groups related colors
+         *                           together. For example, 'sparkline-colors'.
+         * @param {Array} names An array of color names to retrieve.
+         * @param {Boolean} asArray Whether the result should be an array or an object.
+         * @return {Object|Array} An object mapping color names with color values or an
+         *                        array of colors.
+         */
+        getColors: function (namespace, names, asArray) {
+            var colors = asArray ? [] : {};
+            for (var i = 0; i != names.length; ++i) {
+                var name = names[i],
+                    color = this.getColor(namespace, name);
+                if (color) {
+                    if (asArray) {
+                        colors.push(color);
+                    } else {
+                        colors[name] = color;
+                    }
+                }
+            }
+            return colors;
+        },
+
+        /**
+         * Turns a color string that might be an rgb value rgb(12, 34, 56) into
+         * a hex color string.
+         */
+        _normalizeColor: function (color) {
+            if (color == this._getTransparentColor()) {
+                return null;
+            }
+
+            if (color
+                && color[0] != '#'
+            ) {
+                // parse rgb(#, #, #) and get rgb numbers
+                var parts = color.split(/[()rgb,\s]+/);
+                parts = [+parts[1], +parts[2], +parts[3]];
+
+                // convert parts to hex with one leading 0
+                for (var i = 0; i != parts.length; ++i) {
+                    parts[i] = ("00" + parts[i].toString(16)).slice(-2);
+                }
+
+                // create hex string
+                color = '#' + parts.join('');
+            }
+            return color;
+        },
+        
+        /**
+         * Returns the manufactured <div> element used to obtain color data. When
+         * getting color data the class and data-name attribute of this element are
+         * changed.
+         */
+        _getElement: function () {
+            if (!this.$element) {
+                $('body').append('<div id="color-manager"></div>');
+                this.$element = $('#color-manager');
+            }
+
+            return this.$element;
+        },
+
+        /**
+         * Returns this browser's representation of the 'transparent' color. Used to
+         * compare against colors obtained in getColor. If a color is 'transparent'
+         * it means there's no color for that namespace/name combination.
+         */
+        _getTransparentColor: function () {
+            if (!this.transparentColor) {
+                this.transparentColor = $('<div style="color:transparent;"></div>').appendTo($('body')).css('color');
+            }
+
+            return this.transparentColor;
+        }
+    };
+    
+    piwik.ColorManager = new ColorManager();
+
+}(jQuery));
diff --git a/plugins/CoreHome/javascripts/datatable_rowactions.js b/plugins/CoreHome/javascripts/datatable_rowactions.js
index 5aea12e4b1..510861f46b 100644
--- a/plugins/CoreHome/javascripts/datatable_rowactions.js
+++ b/plugins/CoreHome/javascripts/datatable_rowactions.js
@@ -376,6 +376,7 @@ DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function (apiMeth
 
     requestParams.module = 'CoreHome';
     requestParams.action = action;
+    requestParams.colors = JSON.stringify(piwik.getSparklineColors());
 
     var ajaxRequest = new ajaxHelper();
     ajaxRequest.addParams(requestParams, 'get');
diff --git a/plugins/CoreHome/javascripts/jqplot.js b/plugins/CoreHome/javascripts/jqplot.js
index d77d7f582f..15ba8da0ab 100644
--- a/plugins/CoreHome/javascripts/jqplot.js
+++ b/plugins/CoreHome/javascripts/jqplot.js
@@ -8,7 +8,6 @@
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  */
 
-
 /**
  * Constructor function
  *
@@ -27,33 +26,31 @@ JQPlot.prototype = {
         this.originalData = data;
         this.data = data.data;
 
-        defaultParams = {};
-        defaultParams.grid = {
-            drawGridLines: false,
-            background: '#fff',
-            borderColor: '#f00',
-            borderWidth: 0,
-            shadow: false
-        };
-
-        defaultParams.title = {
-            show: false
-        };
-
-        defaultParams.axesDefaults = {
-            pad: 1.0,
-            tickRenderer: $.jqplot.CanvasAxisTickRenderer,
-            tickOptions: {
-                showMark: false,
-                fontSize: '11px',
-                fontFamily: 'Arial'
+        defaultParams = {
+            grid: {
+                drawGridLines: false,
+                borderWidth: 0,
+                shadow: false
             },
-            rendererOptions: {
-                drawBaseline: false
+            title: {
+                show: false
+            },
+            axesDefaults: {
+                pad: 1.0,
+                tickRenderer: $.jqplot.CanvasAxisTickRenderer,
+                tickOptions: {
+                    showMark: false,
+                    fontSize: '11px',
+                    fontFamily: 'Arial'
+                },
+                rendererOptions: {
+                    drawBaseline: false
+                }
             }
         };
 
         this.params = $.extend(true, {}, defaultParams, data.params);
+        this._setColors();
 
         this.tooltip = data.tooltip;
         this.seriesPicker = data.seriesPicker;
@@ -61,6 +58,7 @@ JQPlot.prototype = {
         if (typeof this.params.axes.yaxis == 'undefined') {
             this.params.axes.yaxis = {};
         }
+
         if (typeof this.params.axes.yaxis.tickOptions == 'undefined') {
             this.params.yaxis.tickOptions = {
                 formatString: '%d'
@@ -302,26 +300,26 @@ JQPlot.prototype = {
         });
 
         var popover = $(document.createElement('div'));
-		
-		popover.append('<div style="font-size: 13px; margin-bottom: 10px;">'
+        
+        popover.append('<div style="font-size: 13px; margin-bottom: 10px;">'
             + lang.exportText + '</div>').append($(img))
-			
-		popover.dialog({
-			title: lang.exportTitle,
-			modal: true,
-			width: 'auto',
-			position: ['center', 'center'],
-			resizable: false,
-			autoOpen: true,
-			open: function (event, ui) {
-				$('.ui-widget-overlay').on('click.popover', function () {
-					popover.dialog('close');
-				});
-			},
-			close: function (event, ui) {
-				$(this).dialog("destroy").remove();
-			}
-		});
+            
+        popover.dialog({
+            title: lang.exportTitle,
+            modal: true,
+            width: 'auto',
+            position: ['center', 'center'],
+            resizable: false,
+            autoOpen: true,
+            open: function (event, ui) {
+                $('.ui-widget-overlay').on('click.popover', function () {
+                    popover.dialog('close');
+                });
+            },
+            close: function (event, ui) {
+                $(this).dialog("destroy").remove();
+            }
+        });
     },
 
 
@@ -355,7 +353,8 @@ JQPlot.prototype = {
         defaultParams.piwikTicks = {
             showTicks: true,
             showGrid: true,
-            showHighlight: true
+            showHighlight: true,
+            tickColor: this.tickColor
         };
 
         this.params = $.extend(true, {}, defaultParams, this.params);
@@ -433,18 +432,21 @@ JQPlot.prototype = {
         this.params.piwikTicks = {
             showTicks: false,
             showGrid: false,
-            showHighlight: false
+            showHighlight: false,
+            tickColor: this.tickColor
         };
 
         this.params.legend = {
             show: false
         };
         this.params.pieLegend = {
-            show: true
+            show: true,
+            labelColor: this.singleMetricColor
         };
         this.params.canvasLegend = {
             show: true,
-            singleMetric: true
+            singleMetric: true,
+            singleMetricColor: this.singleMetricColor
         };
 
         // pie charts have a different data format
@@ -477,7 +479,8 @@ JQPlot.prototype = {
         this.params.piwikTicks = {
             showTicks: true,
             showGrid: false,
-            showHighlight: false
+            showHighlight: false,
+            tickColor: this.tickColor
         };
 
         this.params.axes.xaxis.renderer = $.jqplot.CategoryAxisRenderer;
@@ -592,8 +595,25 @@ JQPlot.prototype = {
             this.data = [this.data[0]];
             this.params.series = [this.params.series[0]];
         }
-    }
+    },
 
+    /**
+     * Sets the colors used to render this graph.
+     */
+    _setColors: function () {
+        var colorManager = piwik.ColorManager,
+            seriesColorNames = ['series1', 'series2', 'series3', 'series4', 'series5',
+                                'series6', 'series7', 'series8', 'series9', 'series10'];
+
+        var graphType = $('#' + this.dataTableId).find('.piwik-graph').attr('data-graph-type'),
+            namespace = graphType + '-graph-colors';
+
+        this.params.seriesColors = colorManager.getColors(namespace, seriesColorNames, true);
+        this.params.grid.background = colorManager.getColor(namespace, 'grid-background');
+        this.params.grid.borderColor = colorManager.getColor(namespace, 'grid-border');
+        this.tickColor = colorManager.getColor(namespace, 'ticks');
+        this.singleMetricColor = colorManager.getColor(namespace, 'single-metric-label')
+    }
 };
 
 
@@ -831,7 +851,7 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function () {
             for (var i = 0; i < ticks.length; i++) {
                 var pos = Math.round(i * tickWidth + tickWidth / 2);
                 var full = xaxisLabels[i] && xaxisLabels[i] != ' ';
-                drawLine(ctx, pos, full, c.showGrid);
+                drawLine(ctx, pos, full, c.showGrid, c.tickColor);
             }
         }
     };
@@ -840,9 +860,9 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function () {
     $.jqplot.postDrawHooks.push($.jqplot.PiwikTicks.postDraw);
 
     // draw a 1px line
-    function drawLine(ctx, x, full, showGrid) {
+    function drawLine(ctx, x, full, showGrid, color) {
         ctx.save();
-        ctx.strokeStyle = '#cccccc';
+        ctx.strokeStyle = color;
 
         ctx.beginPath();
         ctx.lineWidth = 2;
@@ -987,7 +1007,7 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function () {
 
             ctx.fillStyle = s.color;
             if (legend.singleMetric) {
-                ctx.fillStyle = '#666666';
+                ctx.fillStyle = legend.singleMetricColor;
             }
 
             ctx.fillRect(x, 10, 10, 2);
@@ -1387,7 +1407,7 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function () {
                 var x = x2 - 9 - ctx.measureText(label).width;
             }
 
-            ctx.fillStyle = '#666666';
+            ctx.fillStyle = legend.labelColor;
             ctx.fillText(label, x, y2 + 3);
         }
 
diff --git a/plugins/CoreHome/javascripts/sparkline.js b/plugins/CoreHome/javascripts/sparkline.js
index 712dd0e7a5..8e74dbf94c 100644
--- a/plugins/CoreHome/javascripts/sparkline.js
+++ b/plugins/CoreHome/javascripts/sparkline.js
@@ -5,7 +5,29 @@
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  */
 
-function initializeSparklines() {
+(function ($) {
+
+var sparklineColorNames = ['lineColor', 'red', 'blue', 'green'];
+
+piwik.getSparklineColors = function () {
+    return piwik.ColorManager.getColors('sparkline-colors', sparklineColorNames);
+};
+
+// initializes each sparkline so they use colors defined in CSS
+piwik.initSparklines = function () {
+    $('.sparkline').each(function () {
+        var $self = $(this);
+        
+        if ($self.attr('src')) {
+            return;
+        }
+        
+        var colors = JSON.stringify(piwik.getSparklineColors());
+        $self.attr('src', $self.attr('data-src') + '&colors=' + encodeURIComponent(colors));
+    });
+};
+
+window.initializeSparklines = function () {
     var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable'];
 
     $("[data-graph-id]").each(function () {
@@ -46,18 +68,9 @@ function initializeSparklines() {
                     // reload the datatable w/ a new column & scroll to the graph
                     dataTable.trigger('reload', params);
                 });
-                $(this).hover(
-                    function () {
-                        $(this).css({
-                            "cursor": "pointer",
-                            "border-bottom": "1px dashed #C3C3C3"
-                        });
-                    },
-                    function () {
-                        $(this).css({"border-bottom": "1px solid white"});
-                    }
-                );
             }
         });
     });
-}
+};
+
+}(jQuery));
diff --git a/plugins/CoreHome/stylesheets/color_manager.css b/plugins/CoreHome/stylesheets/color_manager.css
new file mode 100644
index 0000000000..725d3df59a
--- /dev/null
+++ b/plugins/CoreHome/stylesheets/color_manager.css
@@ -0,0 +1,3 @@
+.color-manager {
+    color: transparent;
+}
\ No newline at end of file
diff --git a/plugins/CoreHome/templates/getMultiRowEvolutionPopover.twig b/plugins/CoreHome/templates/getMultiRowEvolutionPopover.twig
index daf93f91b7..7f027b458e 100644
--- a/plugins/CoreHome/templates/getMultiRowEvolutionPopover.twig
+++ b/plugins/CoreHome/templates/getMultiRowEvolutionPopover.twig
@@ -1,3 +1,4 @@
+{% set seriesColorCount = constant("Piwik_Visualization_Chart_Evolution::SERIES_COLOR_COUNT") %}
 <div class="rowevolution multirowevolution">
     <div class="popover-title">{{ 'RowEvolution_MultiRowEvolutionTitle'|translate }}</div>
     <div class="graph">
@@ -6,7 +7,7 @@
     <div class="metrics-container">
         <h2>{{ availableRecordsText|translate }}</h2>
         <table class="metrics" border="0" cellpadding="0" cellspacing="0">
-            {% for metric in metrics %}
+            {% for i, metric in metrics %}
                 <tr>
                     <td class="sparkline">
                         {{ metric.sparkline|raw }}
@@ -14,7 +15,9 @@
                     <td class="text">
                         {% import 'macros.twig' as piwik %}
                         {{ piwik.logoHtml(metric, "") }}
-                        <span style="color:{{ metric.color }}">{{ metric.label|raw }}</span><br/>
+                        <span class="evolution-graph-colors" data-name="series{{ (i % seriesColorCount) + 1 }}">
+                            {{- metric.label|raw -}}
+                        </span>
                         <span class="details">{{ metric.details }}</span>
                     </td>
                 </tr>
diff --git a/plugins/CoreHome/templates/getRowEvolutionPopover.twig b/plugins/CoreHome/templates/getRowEvolutionPopover.twig
index 6179cfd5cf..4c38c80ac7 100644
--- a/plugins/CoreHome/templates/getRowEvolutionPopover.twig
+++ b/plugins/CoreHome/templates/getRowEvolutionPopover.twig
@@ -1,3 +1,4 @@
+{% set seriesColorCount = constant("Piwik_Visualization_Chart_Evolution::SERIES_COLOR_COUNT") %}
 <div class="rowevolution">
     <div class="popover-title">{{ popoverTitle | raw }}</div>
     <div class="graph">
@@ -9,14 +10,16 @@
         <div class="rowevolution-documentation">
             {{ 'RowEvolution_Documentation'|translate }}
         </div>
-        <table class="metrics" border="0" cellpadding="0" cellspacing="0">
-            {% for metric in metrics %}
-                <tr>
+        <table class="metrics" border="0" cellpadding="0" cellspacing="0" data-thing="{{ seriesColorCount }}">
+            {% for i, metric in metrics %}
+                <tr data-i="{{ i }}">
                     <td class="sparkline">
-                        {{ metric.sparkline | raw }}
+                        {{ metric.sparkline|raw }}
                     </td>
                     <td class="text">
-                        <span style="color:{{ metric.color }}">{{ metric.label|raw }}</span>
+                        <span class="evolution-graph-colors" data-name="series{{ (i % seriesColorCount) + 1 }}">
+                            {{- metric.label|raw -}}
+                        </span>
                         {% if metric.details %}:
                             <span class="details">{{ metric.details }}</span>
                         {% endif %}
diff --git a/plugins/Referers/Controller.php b/plugins/Referers/Controller.php
index 09fdad8cf3..e45dd6287b 100644
--- a/plugins/Referers/Controller.php
+++ b/plugins/Referers/Controller.php
@@ -226,7 +226,7 @@ class Piwik_Referers_Controller extends Piwik_Controller
         $label = strtolower(Piwik_Translate($indexTranslation));
 
         // return html that displays it as grey & italic
-        return '<span style="color:#999"><em>(' . $label . ')</em></span>';
+        return '<span class="datatable-label-category"><em>(' . $label . ')</em></span>';
     }
 
     function getKeywords($fetch = false)
diff --git a/plugins/Transitions/javascripts/transitions.js b/plugins/Transitions/javascripts/transitions.js
index 14ccd28a19..da8522d11b 100644
--- a/plugins/Transitions/javascripts/transitions.js
+++ b/plugins/Transitions/javascripts/transitions.js
@@ -415,10 +415,10 @@ Piwik_Transitions.prototype.renderLoops = function () {
 Piwik_Transitions.prototype.renderEntries = function (onlyBg) {
     if (this.model.directEntries > 0) {
         var self = this;
-        var gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', 'left');
-        if (this.highlightedGroup == 'directEntries') {
-            gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', 'left');
-        }
+
+        var isHighlighted = this.highlightedGroup == 'directEntries';
+        var gradient = this.canvas.createHorizontalGradient('entries', 'left', isHighlighted);
+
         this.canvas.renderBox({
             side: 'left',
             onlyBg: onlyBg,
@@ -442,10 +442,10 @@ Piwik_Transitions.prototype.renderEntries = function (onlyBg) {
 Piwik_Transitions.prototype.renderExits = function (onlyBg) {
     if (this.model.exits > 0) {
         var self = this;
-        var gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', 'right');
-        if (this.highlightedGroup == 'exits') {
-            gradient = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', 'right');
-        }
+
+        var isHighlighted = this.highlightedGroup == 'exits';
+        var gradient = this.canvas.createHorizontalGradient('exits', 'right', isHighlighted);
+
         this.canvas.renderBox({
             side: 'right',
             onlyBg: onlyBg,
@@ -481,12 +481,10 @@ Piwik_Transitions.prototype.renderOpenGroup = function (groupName, side, onlyBg)
     var details = self.model.getDetailsForGroup(groupName);
 
     // prepare gradients
-    var gradientItems = this.canvas.createHorizontalGradient('#E3DFD1', '#E8E4D5', side);
-    var gradientOthers = this.canvas.createHorizontalGradient('#F5F3EB', '#E8E4D5', side);
-    var gradientBackground = this.canvas.createHorizontalGradient('#FFFFFF', '#BACFE8', side);
-    if (groupName == this.highlightedGroup) {
-        gradientBackground = this.canvas.createHorizontalGradient('#FFFFFF', '#FAD293', side);
-    }
+    var gradientItems = this.canvas.createHorizontalGradient('items', side);
+    var gradientOthers = this.canvas.createHorizontalGradient('others', side);
+    var gradientBackground =
+        this.canvas.createHorizontalGradient('background', side, groupName == this.highlightedGroup);
 
     // remember current offsets to reset them later for drawing the background
     var boxPositionBefore, curvePositionBefore;
@@ -600,10 +598,9 @@ Piwik_Transitions.prototype.renderOpenGroup = function (groupName, side, onlyBg)
 /** Render a closed group without detailed data, only one box for the sum */
 Piwik_Transitions.prototype.renderClosedGroup = function (groupName, side, onlyBg) {
     var self = this;
-    var gradient = this.canvas.createHorizontalGradient('#DDE4ED', '#9BBADE', side);
-    if (groupName == this.highlightedGroup) {
-        gradient = this.canvas.createHorizontalGradient('#FAE2C0', '#FAD293', side);
-    }
+
+    var isHighlighted = groupName == this.highlightedGroup;
+    var gradient = this.canvas.createHorizontalGradient('closed-group', side, isHighlighted);
 
     var nbTransitionsVarName = groupName + 'NbTransitions';
 
@@ -782,6 +779,16 @@ function Piwik_Transitions_Canvas(canvasBgLeft, canvasBgRight, canvasLeft, canva
     this.rightBoxEndX = this.width;
     this.rightBoxBeginX = this.rightCurveEndX = this.rightBoxEndX - this.boxWidth;
     this.rightCurveBeginX = this.rightCurveEndX - this.curveWidth;
+
+    // load gradient colors from CSS
+    this.colors = {};
+
+    var transitionsColorNamespaces = ['entries', 'exits', 'background', 'closed-group', 'items', 'others', 'loops'];
+    var gradientColorNames = ['light', 'dark', 'light-highlighted', 'dark-highlighted'];
+    for (var i = 0; i != transitionsColorNamespaces.length; ++i) {
+        var namespace = 'transition-' + transitionsColorNamespaces[i];
+        this.colors[namespace] = piwik.ColorManager.getColors(namespace, gradientColorNames);
+    }
 }
 
 /**
@@ -800,13 +807,22 @@ Piwik_Transitions_Canvas.prototype.isNarrowMode = function () {
 
 /**
  * Helper to create horizontal gradients
- *
+ * TODO
  * @param    lightColor
  * @param    darkColor
  * @param    position    left|right
  */
-Piwik_Transitions_Canvas.prototype.createHorizontalGradient = function (lightColor, darkColor, position) {
-    var fromX, toX, fromColor, toColor;
+Piwik_Transitions_Canvas.prototype.createHorizontalGradient = function (colorGroup, position, isHighlighted) {
+    var fromX, toX, fromColor, toColor, lightColor, darkColor;
+
+    colorGroup = 'transition-' + colorGroup;
+    if (isHighlighted) {
+        lightColor = this.colors[colorGroup]['light-highlighted'];
+        darkColor = this.colors[colorGroup]['dark-highlighted'];
+    } else {
+        lightColor = this.colors[colorGroup]['light'];
+        darkColor = this.colors[colorGroup]['dark'];
+    }
 
     if (position == 'left') {
         // gradient is used to fill a box on the left
@@ -1109,8 +1125,8 @@ Piwik_Transitions_Canvas.prototype.renderLoops = function (share) {
 
     // create gradient
     var gradient = this.contextLoops.createLinearGradient(this.leftCurveEndX - 50, 0, this.rightCurveBeginX + 50, 0);
-    var light = '#F5F3EB';
-    var dark = '#E8E4D5';
+    var light = this.colors['transition-loops']['light'];
+    var dark = this.colors['transition-loops']['dark'];
     gradient.addColorStop(0, dark);
     gradient.addColorStop(.5, light);
     gradient.addColorStop(1, dark);
diff --git a/plugins/Zeitgeist/colors.piwik.json b/plugins/Zeitgeist/colors.piwik.json
deleted file mode 100644
index d2ca30e80e..0000000000
--- a/plugins/Zeitgeist/colors.piwik.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "colors": {}
-}
diff --git a/plugins/Zeitgeist/stylesheets/base.less b/plugins/Zeitgeist/stylesheets/base.less
index d4a014f807..4f593a9c92 100644
--- a/plugins/Zeitgeist/stylesheets/base.less
+++ b/plugins/Zeitgeist/stylesheets/base.less
@@ -2,7 +2,7 @@
 /* base.css is automatically generated. DO NOT MODIFY */
 
 
-/* Genreral styles */
+/* General styles */
 @import "general/_default.less";
 
 @import "general/_utils.less";
@@ -11,6 +11,7 @@
 
 @import "general/_jqueryUI.less";
 
+@import "general/_misc.less";
 
 /* Component styles */
 @import "ui/_header.less";
@@ -25,6 +26,11 @@
 
 @import "ui/_loading.less";
 
+@import "ui/_sparkline.less";
+
+@import "ui/_jqplot.less";
+
+@import "ui/_transitions.less";
 
 /* Remote components */
 @import "../../CoreHome/stylesheets/_donate.less";
diff --git a/plugins/Zeitgeist/stylesheets/general/_misc.less b/plugins/Zeitgeist/stylesheets/general/_misc.less
new file mode 100644
index 0000000000..38ef9f463f
--- /dev/null
+++ b/plugins/Zeitgeist/stylesheets/general/_misc.less
@@ -0,0 +1,7 @@
+#topApiRef {
+    color: #95AECB;
+}
+
+.exception-backtrace {
+    color: #888;
+}
\ No newline at end of file
diff --git a/plugins/Zeitgeist/stylesheets/general/_utils.less b/plugins/Zeitgeist/stylesheets/general/_utils.less
index ae8f0915b5..0077685734 100644
--- a/plugins/Zeitgeist/stylesheets/general/_utils.less
+++ b/plugins/Zeitgeist/stylesheets/general/_utils.less
@@ -45,6 +45,14 @@ html.old-ie .ie-hide {
   opacity: 0.75;
 }
 
+.metricEvolution > .positive-evolution {
+  color: green;
+}
+
+.metricEvolution > .negative-evolution {
+  color: #e02a3b;
+}
+
 .reportsByDimensionView > .entityList {
   float: left;
   width: 220px;
diff --git a/plugins/Zeitgeist/stylesheets/ui/_dataTable.less b/plugins/Zeitgeist/stylesheets/ui/_dataTable.less
new file mode 100644
index 0000000000..f4303658c0
--- /dev/null
+++ b/plugins/Zeitgeist/stylesheets/ui/_dataTable.less
@@ -0,0 +1,3 @@
+.datatable-label-category {
+    color:#999;
+}
\ No newline at end of file
diff --git a/plugins/Zeitgeist/stylesheets/ui/_jqplot.less b/plugins/Zeitgeist/stylesheets/ui/_jqplot.less
new file mode 100644
index 0000000000..b8f47543d2
--- /dev/null
+++ b/plugins/Zeitgeist/stylesheets/ui/_jqplot.less
@@ -0,0 +1,150 @@
+// evolution graph colors
+.evolution-graph-colors[data-name=series1] {
+    color: #5170AE;
+}
+
+.evolution-graph-colors[data-name=series2] {
+    color: #F29007;
+}
+
+.evolution-graph-colors[data-name=series3] {
+    color: #CC3399;
+}
+
+.evolution-graph-colors[data-name=series4] {
+    color: #9933CC;
+}
+
+.evolution-graph-colors[data-name=series5] {
+    color: #80a033;
+}
+
+.evolution-graph-colors[data-name=series6] {
+    color: #246AD2;
+}
+
+.evolution-graph-colors[data-name=series7] {
+    color: #FD16EA;
+}
+
+.evolution-graph-colors[data-name=series8] {
+    color: #49C100;
+}
+
+.evolution-graph-colors[data-name=grid-background] {
+    color: #fff;
+}
+
+.evolution-graph-colors[data-name=grid-border] {
+    color: #f00;
+}
+
+.evolution-graph-colors[data-name=ticks] {
+    color: #ccc;
+}
+
+.evolution-graph-colors[data-name=single-metric-label] {
+    color: #666;
+}
+
+// bar graph colors
+.bar-graph-colors[data-name=series1] {
+    color: #5170AE;
+}
+
+.bar-graph-colors[data-name=series2] {
+    color: #F3A010;
+}
+
+.bar-graph-colors[data-name=series3] {
+    color: #CC3399;
+}
+
+.bar-graph-colors[data-name=series4] {
+    color: #9933CC;
+}
+
+.bar-graph-colors[data-name=series5] {
+    color: #80a033;
+}
+
+.bar-graph-colors[data-name=series6] {
+    color: #246AD2;
+}
+
+.bar-graph-colors[data-name=series7] {
+    color: #FD16EA;
+}
+
+.bar-graph-colors[data-name=series8] {
+    color: #49C100;
+}
+
+.bar-graph-colors[data-name=grid-background] {
+    color: #fff;
+}
+
+.bar-graph-colors[data-name=grid-border] {
+    color: #f00;
+}
+
+.bar-graph-colors[data-name=ticks] {
+    color: #ccc;
+}
+
+.bar-graph-colors[data-name=single-metric-label] {
+    color: #666;
+}
+
+// pie graph colors
+.pie-graph-colors[data-name=series1] {
+    color: #59727F;
+}
+
+.pie-graph-colors[data-name=series2] {
+    color: #7DAAC0;
+}
+
+.pie-graph-colors[data-name=series3] {
+    color: #7F7259;
+}
+
+.pie-graph-colors[data-name=series4] {
+    color: #C09E7D;
+}
+
+.pie-graph-colors[data-name=series5] {
+    color: #9BB39B;
+}
+
+.pie-graph-colors[data-name=series6] {
+    color: #B1D8B3;
+}
+
+.pie-graph-colors[data-name=series7] {
+    color: #B39BA7;
+}
+
+.pie-graph-colors[data-name=series8] {
+    color: #D8B1C5;
+}
+
+.pie-graph-colors[data-name=series9] {
+    color: #A5A5A5;
+}
+
+.pie-graph-colors[data-name=grid-background] {
+    color: #fff;
+}
+
+.pie-graph-colors[data-name=grid-border] {
+    color: #f00;
+}
+
+.pie-graph-colors[data-name=ticks] {
+    color: #ccc;
+}
+
+.pie-graph-colors[data-name=single-metric-label] {
+    color: #666;
+}
diff --git a/plugins/Zeitgeist/stylesheets/ui/_sparkline.less b/plugins/Zeitgeist/stylesheets/ui/_sparkline.less
new file mode 100644
index 0000000000..7d2b9c7c8b
--- /dev/null
+++ b/plugins/Zeitgeist/stylesheets/ui/_sparkline.less
@@ -0,0 +1,26 @@
+// sparkline styles
+div.sparkline {
+    border-bottom: 1px solid white;
+}
+
+div.sparkline:hover {
+    cursor: pointer;
+    border-bottom: 1px dashed #c3c3c3;
+}
+
+// sparkline colors
+.sparkline-colors[data-name=lineColor] {
+    color: rgb(22, 44, 74);
+}
+
+.sparkline-colors[data-name=red] {
+    color: #ff7f7f;
+}
+
+.sparkline-colors[data-name=blue] {
+    color: #55AAFF;
+}
+
+.sparkline-colors[data-name=green] {
+    color: #75BF7C;
+}
diff --git a/plugins/Zeitgeist/stylesheets/ui/_transitions.less b/plugins/Zeitgeist/stylesheets/ui/_transitions.less
new file mode 100644
index 0000000000..e74d16b341
--- /dev/null
+++ b/plugins/Zeitgeist/stylesheets/ui/_transitions.less
@@ -0,0 +1,98 @@
+// 
+// transitions colors
+// 
+
+// colors for entries gradients
+.transition-entries[data-name=light] {
+    color: #fff;
+}
+
+.transition-entries[data-name=dark] {
+    color: #BACFE8;
+}
+
+.transition-entries[data-name=light-highlighted] {
+    color: #fff;
+}
+
+.transition-entries[data-name=dark-highlighted] {
+    color: #FAD293;
+}
+
+// colors for exits gradients
+.transition-exits[data-name=light] {
+    color: #fff;
+}
+
+.transition-exits[data-name=dark] {
+    color: #BACFE8;
+}
+
+.transition-exits[data-name=light-highlighted] {
+    color: #fff;
+}
+
+.transition-exits[data-name=dark-highlighted] {
+    color: #FAD293;
+}
+
+// background gradients colors
+.transition-background[data-name=light] {
+    color: #fff;
+}
+
+.transition-background[data-name=dark] {
+    color: #BACFE8;
+}
+
+.transition-background[data-name=light-highlighted] {
+    color: #fff;
+}
+
+.transition-background[data-name=dark-highlighted] {
+    color: #FAD293;
+}
+
+// closed group gradient colors
+.transition-closed-group[data-name=light] {
+    color: #DDE4ED;
+}
+
+.transition-closed-group[data-name=dark] {
+    color: #9BBADE;
+}
+
+.transition-closed-group[data-name=light-highlighted] {
+    color: #FAE2C0;
+}
+
+.transition-closed-group[data-name=dark-highlighted] {
+    color: #FAD293;
+}
+
+// items gradient colors
+.transition-items[data-name=light] {
+    color: #E3DFD1;
+}
+
+.transition-items[data-name=dark] {
+    color: #E8E4D5;
+}
+
+// 'others' gradients colors
+.transition-others[data-name=light] {
+    color: #F5F3EB;
+}
+
+.transition-others[data-name=dark] {
+    color: #E8E4D5;
+}
+
+// loop gradient colors
+.transition-loops[data-name=light] {
+    color: #F5F3EB;
+}
+
+.transition-loops[data-name=dark] {
+    color: #E8E4D5;
+}
\ No newline at end of file
diff --git a/tests/PHPUnit/Core/AccessTest.php b/tests/PHPUnit/Core/AccessTest.php
index 09637df503..a8c01a2ccd 100644
--- a/tests/PHPUnit/Core/AccessTest.php
+++ b/tests/PHPUnit/Core/AccessTest.php
@@ -7,6 +7,12 @@
  */
 class AccessTest extends DatabaseTestCase
 {
+    public function setUp()
+    {
+        parent::setUp();
+        Piwik_Access::setSingletonInstance(null);
+    }
+
     /**
      * @group Core
      * @group Access
-- 
GitLab