diff --git docs/STYLE_PLUGINS.txt docs/STYLE_PLUGINS.txt
new file mode 100644
index 0000000..f9704f5
--- /dev/null
+++ docs/STYLE_PLUGINS.txt
@@ -0,0 +1,181 @@
+$Id$
+
+Current for 6.x-2.x
+
+Style plugins are code snippets providing style context
+callbacks to OpenLayers styles.
+
+Such callbacks (written in JavaScript) are needed to provide
+styling which is dependent on characteristics which are not
+known at time of PHP rendering.
+
+A common example is changing pointRadius style property based
+on the number of features taking place in a cluster, whereas
+clustering is performed by JavaScript and so cluster composition
+changes w/out PHP being invoked.
+
+# Creating a new OpenLayers Style Plugin from Scratch
+
+First, you'll need to create a module. Of course, skip through this step if
+there's already a module that exists to which this style plugin will be added.
+But if not, create a file called `modulename.info` with the contents
+
+    core = "6.x"
+    dependencies[] = "openlayers"
+    name = "modulename"
+    package = "OpenLayers"
+    project = "modulename"
+
+Then you'll want to register your style plugins.
+
+## Registering plugins directory
+
+This is done implementing `hook_ctools_plugin_directory'
+and `hook_ctools_plugin_api'.
+
+Since your module is called `modulename', their implementation would be:
+
+  function modulename_ctools_plugin_api($module, $api) {
+    // Define plugins for OpenLayers plugins api
+    if ($module == "openlayers") {
+      switch ($api) {
+        case 'openlayers_styles':
+          return array('version' => 1);
+      }
+    }
+  }
+
+  function modulename_ctools_plugin_directory($module, $plugin) {
+    if ($module == 'openlayers' && $plugin == 'style_plugin') {
+      return 'plugins/style_plugin';
+    }
+  }
+
+At that point you can put actual plugins, one file per plugin,
+in the `plugins/style_plugin' directory, or whatever you returned
+from the `hook_ctools_plugin_directory' implementation. 
+
+## Writing the minimal PHP side of plugin
+
+A plugin file starts with a plugin definition, like the
+following:
+
+  // Define plugin for CTools
+  $plugin = array(
+    'title' => t('My style plugin title'),
+    'description' => t('What this plugin does'),
+    'style_plugin' => array(
+      'class' => 'modulename_pluginname', // <-- NOTE!
+      'parent' => 'openlayers_style_plugin',
+    ),
+  );
+
+The above definition says that the plugin is implemented by
+the `modulename_pluginname' class (see NOTE above), which is
+a subclass of `openlayers_style_plugin`. We implement that next:
+
+
+  class modulename_pluginname extends openlayers_style_plugin {
+
+    // Advertise which style properties we expose support for,
+    // and which callback to use for each one
+    function get_context_properties() {
+      return array(
+        'label' => 'getLabel' // <-- NOTE !
+      );
+    }
+
+    // Send the JS file containing the JavaScript side
+    // implementation of the plugin.
+    function render() {
+      drupal_add_js(drupal_get_path('module', 'modulename') .
+        '/plugins/style_plugin/modulename_pluginname.js');
+    }
+
+  }
+
+The above definition provides NO parameters to the actual context
+callback function. We'll see how to add configurability later.
+For now let's move on to the JavaScript side.
+
+## Writing the JavaScript side of plugin
+
+The OpenLayers module renderer will expect to find a
+Drupal.openlayers.style_plugins.<modulename_pluginname>
+Javascript class, as specified by the 'class' attribute of
+the 'style_plugin' array in the PHP plugin definition.
+
+The JavaScript side of style plugin will be put in the file
+referenced by the render() function of the PHP part, and will need
+to provide this class. As with the following example:
+
+ // Define the class constructor
+ Drupal.openlayers.style_plugin.modulename_pluginname = function (params) {
+   this.params = params;
+ };
+
+Note that the constructor can take parameters, which the PHP side
+of the style plugin can export to let user configure the style plugin.
+We'll see this later.
+
+In addition to the constructor, the Drupal OpenLayers module (`DOL',
+from now on) will also expect to find the callbacks advertised
+by the `get_context_properties` function of the PHP side of the style
+plugin. We'll need to provide those as well. They can be defined
+in the class prototype:
+
+  // Style plugin class prototype
+ Drupal.openlayers.style_plugin.modulename_pluginname.prototype = {
+
+   // Style context function
+   'getLabel' = function(feature) {
+      return feature.attributes.length;
+   }
+
+ }
+
+## Providing configurability of style plugin
+
+The PHP side of code can provide a configuration form for the plugin.
+As you saw above, the JavaScript class constructor will be passed
+configuration parameters. These parameters you can define from
+the PHP side, defining an options_form() and optionally an
+options_init() method. Example:
+
+  function options_init() {
+    return array(
+      'feature_weight' => 2,
+      'point_radius_min' => 6,
+      'point_radius_max' => 12,
+    );
+  }
+
+  function options_form($defaults = array()) {
+    $form = array();
+
+    $form['point_radius_min'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Min radius'),
+      '#description' => t('Minimum value for the point radius.'),
+      '#default_value' => isset($defaults['point_radius_min']) ?
+        $defaults['point_radius_min'] : 6,
+    );
+    $form['point_radius_max'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Max radius'),
+      '#description' => t('Maximum value for the point radius.'),
+      '#default_value' => isset($defaults['point_radius_max']) ?
+        $defaults['point_radius_max'] : 12,
+    );
+    $form['feature_weight'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Feature weight'),
+      '#description' => t('Weight of each additional feature for the final point
+ radius.'),
+      '#default_value' => isset($defaults['feature_weight']) ?
+        $defaults['feature_weight'] : 2,
+    );
+
+    return $form;
+  }
+
diff --git docs/openlayers.api.php docs/openlayers.api.php
index 7951b81..dcb75f3 100644
--- docs/openlayers.api.php
+++ docs/openlayers.api.php
@@ -282,3 +282,33 @@ function hook_openlayers_presets() {
   );
   return array('default' => $default);
 }
+
+/**
+ * CTools Registration Hook (Style Plugins)
+ *
+ * IMPORTANT:
+ *
+ * In order to support style plugins, the first step is to
+ * tell CTools where to find the plugin.
+ *
+ * This function is just an example implementation of 
+ * hook_ctools_plugin_directory() and should be alter according to
+ * your module's name.
+ *
+ * For an example, please see the openlayers_test.module
+ *
+ * @param $module
+ *   Name of a module that supports CTools exportables.
+ * @param $plugin
+ *   Name of the kind of plugin supported.
+ * @return
+ *  If $module is 'openlayers', and $api is a type of exportable that
+ *  your module provides, and you are using Openlayers 2.x, then
+ *  return the directory relative to a module to look for this
+ *  particular plugin.
+ */
+function openlayers_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'openlayers' && $plugin == 'style_plugin') {
+    return 'plugins/style_plugin';
+  }
+}
\ No newline at end of file
diff --git includes/openlayers.render.inc includes/openlayers.render.inc
index 5e018cd..418d94f 100644
--- includes/openlayers.render.inc
+++ includes/openlayers.render.inc
@@ -72,10 +72,41 @@ function _openlayers_behaviors_render($behaviors = array(), &$map = array()) {
  */
 function _openlayers_styles_process($styles = array(), 
   $layer_styles = array(), &$map = array()) {
+  ctools_include('plugins');
 
   // Get styles info array
   $styles_info = openlayers_styles();
 
+  // Process with handler if available.
+  $used_plugins = array();
+  foreach ($styles_info as $i => $style) {
+    // Check for property plugins.
+    foreach ($style->data as $prop => $propval) {
+      if ( is_array($propval) ) {
+        $plugname = $propval['plugin'];
+        if ( ! empty($plugname) ) {
+          // the above should never happen, except
+          // for cases in which the old style plugin 
+          // system was used (for prop == 'plugins')
+          $used_plugins[$plugname] = TRUE;
+        }
+      }
+    }
+  }
+
+  if ( ! empty($used_plugins) ) {
+    $style_plugins = openlayers_style_plugins();
+    foreach (array_keys($used_plugins) as $plugname) {
+      $plug = $style_plugins[$plugname];
+      $plugin_class = ctools_plugin_get_class($plug, 'style_plugin');
+      if (isset($plugin_class)) {
+        $plugin = new $plugin_class;
+        $plugin->render();
+      }
+    }
+  }
+
+
   // Go through styles
   $processed = array();
   foreach ($styles as $k => $style) {
diff --git js/openlayers.js js/openlayers.js
index e8d1589..6d13286 100644
--- js/openlayers.js
+++ js/openlayers.js
@@ -283,16 +283,50 @@ Drupal.openlayers = {
   },
   'getStyleMap': function(map, layername) {
     if (map.styles) {
+
       var stylesAdded = {};
+
       // Grab and map base styles.
       for (var style in map.styles) {
         stylesAdded[style] = new OpenLayers.Style(map.styles[style]);
       }
+
       // Implement layer-specific styles.
       if (map.layer_styles !== undefined && map.layer_styles[layername]) {
-        var style = map.layer_styles[layername];
-        stylesAdded['default'] = new OpenLayers.Style(map.styles[style]);
+
+        var style_name = map.layer_styles[layername]; 
+        var style = map.styles[style_name]; // TODO: skip if undefined
+
+        // Build context object and callback values
+        var newContext = {}
+        for (var propname in style) {
+          if (typeof style[propname] == 'object') {
+            var plugin_name = style[propname]['plugin'];
+            var plugin_options = style[propname]['conf'];
+            var plugin_class = Drupal.openlayers.style_plugin[plugin_name];
+            // Check for existance of plugin_context_class here
+            if ( typeof plugin_class === 'function' ) {
+              var plugin_context = new plugin_class(plugin_options);
+
+              // Add plugin context functions to global context
+              for (var key in plugin_context) {
+                var newkey = plugin_name + '_' + propname + '_' + key;
+                var val = plugin_context[key];
+                if ( typeof val === 'function' ) {
+                    newContext[newkey] = OpenLayers.Function.bind(val,
+                      plugin_context); 
+                }
+              }
+            }
+            style[propname] = '${' + newkey + '}';
+          }
+        }
+
+        // Put together style_name
+        stylesAdded['default'] = stylesAdded['select'] = new OpenLayers.Style(style, 
+            { context: newContext } );
       }
+      
       return new OpenLayers.StyleMap(stylesAdded);
     }
     // Default styles
@@ -323,3 +357,4 @@ Drupal.openlayers = {
 };
 
 Drupal.openlayers.layer = {};
+Drupal.openlayers.style_plugin = {};
diff --git modules/openlayers_ui/includes/openlayers_ui.styles.inc modules/openlayers_ui/includes/openlayers_ui.styles.inc
index 54f149b..0a7ad89 100644
--- modules/openlayers_ui/includes/openlayers_ui.styles.inc
+++ modules/openlayers_ui/includes/openlayers_ui.styles.inc
@@ -12,9 +12,7 @@
 /**
  * Styles add/edit form.
  */
-function openlayers_ui_styles_form(&$form_state, $style = NULL, $edit = FALSE) {
-  $form = array();
-  
+function openlayers_ui_styles_get_properties() {
   // Available styling properies.  Defaults and descriptions are taken
   // from OpenLayers.
   // @see http://docs.openlayers.org/library/feature_styling.html
@@ -205,17 +203,34 @@ function openlayers_ui_styles_form(&$form_state, $style = NULL, $edit = FALSE) {
       'desc' => t('Label font weight.'),
     ),
   );
+
+  return $properties;
+}
+
+/**
+ * Styles add/edit form.
+ */
+function openlayers_ui_styles_form(&$form_state, $style = NULL, $edit = FALSE) {
+  $form = array();
+  
+  $properties = openlayers_ui_styles_get_properties();
   
   // Pass style data along
   $form['style_data'] = array(
     '#type' => 'value',
-    '#value' => $properties,
+    '#value' => array(
+      'definitions' => $properties,
+      'defaults' => $style->data
+    )
   );
 
   // Style object basics
   $form['info'] = array(
     '#type' => 'fieldset',
     '#tree' => FALSE,
+    '#title' => t('Basic Information'),
+    '#description' => t('The basic information for the style, used to refer to and describe the style.'),
+    '#collapsible' => TRUE,
   );
   $form['info']['name'] = array(
     '#title' => t('Name'),
@@ -236,21 +251,89 @@ function openlayers_ui_styles_form(&$form_state, $style = NULL, $edit = FALSE) {
   );
 
   // OpenLayers style properties
-  $form['data'] = array('#type' => 'fieldset', '#tree' => TRUE);
-
+  $form['data'] = array(
+    '#type' => 'fieldset', 
+    '#tree' => TRUE,
+    '#title' => t('Style Properties and Plugins'),
+    '#description' => t('Style properties are properties as 
+      defined by the OpenLayers library.  Plugins are dynamically
+      process the layer at render time; plugins may override the
+      values that you have set for style properies.'),
+    '#collapsible' => TRUE,
+  );
   foreach ($properties as $key => $prop) {
+
     $form['data'][$key] = array(
-      '#type' => !isset($prop['options']) ? 'textfield' : 'select',
+      '#type' => 'fieldset',
+      '#tree' => TRUE,
       '#title' => $key,
       '#description' => $prop['desc'],
-      '#default_value' => isset($style->data[$key]) ?
-        $style->data[$key] : $prop['default'],
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
     );
-    
-    // Add options if needed
-    if (isset($prop['options']) && is_array($prop['options'])) {
-      $form['data'][$key]['#options'] = $prop['options'];
+
+    $def_value = $prop['default'];
+    $def_use_plugin = '';
+
+    if ( isset($style->data[$key]) ) {
+      if ( is_array($style->data[$key]) ) {
+        $def_use_plugin = $style->data[$key]['plugin'];
+      } else {
+        $def_value = $style->data[$key];
+      }
+    }
+
+    // Add plugin options, if any
+    $handling_plugins = openlayers_ui_get_style_plugins_for_property($key);
+
+    if ( ! empty($handling_plugins) ) {
+      $plugin_options = array('' => t('none'));
+      foreach ($handling_plugins as $plugname => $plug) {
+        $plugin_options[$plugname] = $plug['title'];
+      }
+
+      $form['data'][$key]['uses_plugin'] = array(
+        '#title' => t('Use plugin'),
+        '#type' => 'select',
+        '#options' => $plugin_options,
+        '#default_value' => $def_use_plugin,
+        '#ahah' => array(
+          'path' => 'openlayers/ahah/style_plugin/' . $key,
+          'wrapper' => $key . '-style-plugin',
+          'method' => 'replace',
+          'effect' => 'fade', // 'fade', 'none', 'slide'
+        )
+      );
+    }
+
+    // Hackish... but is that new for HTML ? ...
+    $form['data'][$key]['plugin_conf_start'] = array(
+      '#value' => '<div id="' . $key . '-style-plugin">'
+    );
+
+    if ( is_array($style->data[$key]) ) {
+      $defaults = $style->data[$key]['conf'];
+      $plugname = $style->data[$key]['plugin'];
+      $form['data'][$key]['plugin'] =
+        openlayers_ui_get_style_plugin_form($def_use_plugin, $defaults);
+    }  else {
+      $form['data'][$key]['value'] = array(
+        '#type' => !isset($prop['options']) ? 'textfield' : 'select',
+        '#default_value' => $def_value
+      );
+      
+      // Add options if needed
+      if (isset($prop['options']) && is_array($prop['options'])) {
+        $form['data'][$key]['value']['#options'] = $prop['options'];
+      }
     }
+
+
+    // Hackish... but is that new for HTML ? ...
+    $form['data'][$key]['plugin_conf_end'] = array(
+      '#value' => '</div>'
+    );
+
   }
 
   $form['submit'] = array(
@@ -264,32 +347,48 @@ function openlayers_ui_styles_form(&$form_state, $style = NULL, $edit = FALSE) {
  * Submit handler for layers.
  */
 function openlayers_ui_styles_form_submit(&$form, &$form_state) {
-  $style_data = $form_state['values']['style_data'];
+
+  $prop_defn = $form_state['values']['style_data']['definitions'];
   
+  //$data = $form_state['values']['data'];
+  $data = $form_state['clicked_button']['#post']['data'];
+
   // Cast and unset values so JS can handle them better,
-  // Unless vluase is in form ${attribute}
-  foreach ($form_state['values']['data'] as $key => $value) {
-    if ($form_state['values']['data'][$key] === '') {
-      unset($form_state['values']['data'][$key]);
-    }
-    elseif (isset($style_data[$key]['type']) &&
-      strpos($form_state['values']['data'][$key], '${') !== 0) {
-      if ($style_data[$key]['type'] == 'integer') {
-        $form_state['values']['data'][$key] = 
-          (int) $form_state['values']['data'][$key];
+  // Unless value is in form ${attribute}
+  foreach ($data as $key => $value) {
+    if ( $data[$key]['uses_plugin'] == '' ) {
+      $data[$key] = $data[$key]['value'];
+      if ($data[$key] === '') {
+        unset($data[$key]);
+      }
+      elseif (isset($prop_defn[$key]['type']) &&
+          strpos($data[$key], '${') !== 0) {
+        if ($prop_defn[$key]['type'] == 'integer') {
+          $data[$key] = (int) $data[$key];
+        }
+        elseif ($prop_defn[$key]['type'] == 'float') {
+          $data[$key] = (float) $data[$key];
+        }
       }
-      elseif ($style_data[$key]['type'] == 'float') {
-        $form_state['values']['data'][$key] = 
-          (float) $form_state['values']['data'][$key];
+    } else {
+      $spec = array(
+        'plugin' => $data[$key]['uses_plugin'],
+      );
+      if ( isset($data[$key]['plugin']['conf']) ) {
+        $spec['conf'] = $data[$key]['plugin']['conf'];
       }
+      $data[$key] = $spec;
     }
   }
-  
+
+  $form_state['values']['data'] = $data;
+  $form_state['clicked_button']['#post']['data'] = $data;
+
   $style = new stdClass();
   $style->name = $form_state['values']['name'];
   $style->title = $form_state['values']['title'];
   $style->description = $form_state['values']['description'];
-  $style->data = $form_state['values']['data'];
+  $style->data = $data;
 
   $success = openlayers_style_save($style);
   
@@ -303,3 +402,140 @@ function openlayers_ui_styles_form_submit(&$form, &$form_state) {
     form_set_error('openlayers', t('Error trying to save style.'));
   }
 }
+
+/**
+ * Get a list of style plugins providing handling of a given property
+ *
+ * @param $propname Name of the property we're interested in
+ *
+ */
+function openlayers_ui_get_style_plugins_for_property($propname) {
+  $handling = array();
+  $available = openlayers_style_plugins();
+  foreach ($available as $plugname => $plugin) {
+    $plugin_class = ctools_plugin_get_class($plugin, 'style_plugin');
+    if (empty($plugin_class)) continue; // should we watchdog here ?
+    $plugin_instance = new $plugin_class;
+    if ( $plugin_instance->can_handle_property($propname) ) {
+      $handling[$plugname] = $plugin;
+    }
+  }
+  return $handling;
+}
+
+/**
+ * Get options of a style plugin by plugin name
+ */
+function openlayers_ui_get_style_plugin_form($plugname, $defaults) {
+
+  $form = array();
+
+  $available = openlayers_style_plugins();
+  if ( ! $available[$plugname] ) {
+    watchdog('openlayers_ui', 'Style plugin !name unknown',
+      array('!name' => $plugname), WATCHDOG_ERROR);
+    return $form;
+  }
+
+  $plugin = $available[$plugname];
+  $plugin_class = ctools_plugin_get_class($plugin, 'style_plugin');
+  if (empty($plugin_class)) {
+    watchdog('openlayers_ui', 'Style plugin !name does not have a class?!',
+      array('!name' => $plugname), WATCHDOG_ERROR);
+    return $form; 
+  }
+
+  // Create object and ask it for options
+  $style_plugin = new $plugin_class;
+
+  $form = array (
+    '#type' => 'fieldset',
+    '#tree' => TRUE,
+    //'#title' => $plugin['title'],
+    '#description' => $plugin['description'],
+    'conf' => $style_plugin->options_form($defaults)
+  );
+
+  return $form;
+}
+
+function openlayers_ui_style_plugin_ahah($propname) {
+
+  $posted = $_POST["data"][$propname];
+  $plugname = $posted['uses_plugin'];
+
+  // Get cached form 
+  $form_state = array('storage' => NULL, 'submitted' => FALSE);
+  $form_build_id = $_POST['form_build_id'];
+  $form_cached = form_get_cache($form_build_id, $form_state);
+
+  // Get style_data from cached form, and defaults
+  $style_data = $form_cached['style_data']['#value'];
+  $defaults = $style_data['defaults'];
+
+
+
+  if ( ! $plugname ) {
+
+    // Find default value
+    $properties = $style_data['definitions'];
+    $prop = $properties[$propname];
+    $def_value = $prop['default'];
+    if ( isset($defaults[$propname]) && ! is_array($defaults[$propname]) ) {
+      $def_value = $defaults[$propname];
+    }
+
+    $form = array(
+      '#type' => !isset($prop['options']) ? 'textfield' : 'select',
+      '#default_value' => $def_value
+    );
+      
+    // Add options if needed
+    if (isset($prop['options']) && is_array($prop['options'])) {
+      $form['value']['#options'] = $prop['options'];
+    }
+
+    $form['#parents'] = array('data', $propname, 'value');
+
+  } else {
+
+    // Find default value
+    $defs = array();
+    if ( isset($defaults[$propname]) && is_array($defaults[$propname])
+      && $defaults[$propname]['plugin'] == $plugname ) {
+      $defs = $defaults[$propname]['conf'];
+    }
+
+/*
+  ob_start();
+  echo "<PRE>";
+  var_dump($defs);
+  $output = ob_get_clean();
+  drupal_json(array('status' => TRUE, 'data' => $output));
+  return;
+*/
+
+    $form = openlayers_ui_get_style_plugin_form($plugname, $defs);
+    $form['#parents'] = array('data', $propname, 'plugin');
+
+  }
+
+
+  $form_state = array(); // TODO: what to do with this ?
+  $plugform_built = form_builder('style_plugin_form', $form, $form_state);
+
+  $output = drupal_render($plugform_built);
+  
+/*
+  ob_start();
+  echo "<PRE>";
+  var_dump($plugform_built);
+  $output = ob_get_clean();
+  drupal_json(array('status' => TRUE, 'data' => $output));
+  return;
+*/
+
+  // Final rendering callback.
+  drupal_json(array('status' => TRUE, 'data' => $output));
+
+}
diff --git modules/openlayers_ui/openlayers_ui.module modules/openlayers_ui/openlayers_ui.module
index fe77d5d..f3c3f20 100644
--- modules/openlayers_ui/openlayers_ui.module
+++ modules/openlayers_ui/openlayers_ui.module
@@ -324,6 +324,14 @@ function openlayers_ui_menu() {
     'file' => 'includes/openlayers_ui.presets.inc',
     'type' => MENU_CALLBACK,
   );
+  $items['openlayers/ahah/style_plugin/%'] = array(
+    'title' => 'OpenLayers Style Plugin AHAH',
+    'page callback' => 'openlayers_ui_style_plugin_ahah',
+    'page arguments' => array(3), // 3rd url item (the '%') is first arg
+    'access callback' => TRUE,
+    'file' => 'includes/openlayers_ui.styles.inc',
+    'type' => MENU_CALLBACK, 
+  );
 
   return $items;
 }
diff --git openlayers.module openlayers.module
index c730349..cc39779 100644
--- openlayers.module
+++ openlayers.module
@@ -447,6 +447,21 @@ function openlayers_behaviors($reset = FALSE) {
 }
 
 /**
+ * Get all style plugins.
+ *
+ * @ingroup openlayers_api
+ *
+ * @param $reset
+ *   Boolean whether to reset cache or not.
+ * @return
+ *   Array of style handler info.
+ */
+function openlayers_style_plugins($reset = FALSE) {
+  ctools_include('plugins');
+  return ctools_get_plugins('openlayers', 'style_plugin');
+}
+
+/**
  * Get all openlayers styles.
  *
  * @ingroup openlayers_api
@@ -904,6 +919,88 @@ class openlayers_layer_type {
 }
 
 /**
+ * Base class for style plugins
+ *
+ * We define base classes in the core module.
+ * All other parent classes can be autoloaded through ctools.
+ */
+class openlayers_style_plugin {
+
+  /**
+   * Return true if this plugin can handle
+   * the given property
+   */
+  function can_handle_property($propname) {
+    return array_key_exists($propname, $this->get_context_properties());
+  }
+
+  /**
+   * Get an array of style property callbacks
+   *
+   * @return
+   *   Array of <property_name> => <callback_name>
+   */
+  function get_context_properties() {
+    return array();
+  }
+
+  /**
+   * Initial default options.
+   *
+   * @return
+   *   Array of default options.
+   */
+  function options_init() {
+    return array();
+  }
+
+  /**
+   * Options form.
+   *
+   * @param $defaults
+   *   Array of default values for the form.
+   * @return
+   *   Array of Drupal form elements.
+   */
+  function options_form($defaults = array()) {
+    return array();
+  }
+
+  /**
+   * Render the style.
+   */
+  function render() {
+    // Render style.
+  }
+}
+
+/**
+ * Implementation of hook_ctools_plugin_directory
+ */
+function openlayers_ctools_plugin_directory($module, $plugin) {
+  // The format of plugin includes should be the
+  // following:
+  //   modulename_plugin_name.inc
+  //
+  // For example:
+  //  openlayers_style_plugin_name.inc
+  
+  // If this module needed to supply style plugins.
+  /*
+  if ($module == 'openlayers' && $plugin == 'style_plugin') {
+    return 'plugins/style_plugin';
+  }
+  */
+  
+  // This should change to the following when converted:
+  /*
+  if ($module == 'openlayers') {
+    return 'plugins/' . $plugin;
+  }
+  */
+}
+
+/**
  * Implementation of hook_ctools_plugin
  */
 function openlayers_ctools_plugin_behaviors() {
diff --git tests/openlayers_test.module tests/openlayers_test.module
index 9e0d826..67362cf 100644
--- tests/openlayers_test.module
+++ tests/openlayers_test.module
@@ -30,6 +30,16 @@ function openlayers_test_menu() {
 }
 
 /**
+ * Implementation of hook_ctools_plugin_directory
+ */
+function openlayers_test_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'openlayers' && $plugin == 'style_plugin') {
+    return 'plugins/style_plugin';
+  }
+}
+  
+
+/**
  * Implementation of hook_ctools_plugin_api().
  */
 function openlayers_test_ctools_plugin_api($module, $api) {
@@ -38,6 +48,9 @@ function openlayers_test_ctools_plugin_api($module, $api) {
     switch ($api) {
       case 'openlayers_presets':
         return array('version' => 1);
+        
+      case 'openlayers_styles':
+        return array('version' => 1);
 
     }
   }
@@ -455,4 +468,4 @@ function openlayers_test_openlayers_map_alter(&$map) {
     drupal_set_message(t('OpenLayers map alter hook fired.'));
     $performed = TRUE;
   }
-}
\ No newline at end of file
+}
diff --git tests/plugins/style_plugin/openlayers_test_rnd_factor.inc tests/plugins/style_plugin/openlayers_test_rnd_factor.inc
new file mode 100644
index 0000000..9112f72
--- /dev/null
+++ tests/plugins/style_plugin/openlayers_test_rnd_factor.inc
@@ -0,0 +1,61 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Test 
+ */
+
+// Define plugin for CTools
+$plugin = array(
+  'title' => t('TEST: random factor'),
+  'description' => t('Example style plugin for context styling. '
+    . 'Provides a random 0..1 factors.'),
+  'style_plugin' => array(
+    'class' => 'openlayers_test_rnd_factor',
+    'parent' => 'openlayers_style_plugin',
+  ),
+);
+
+/**
+ * Style Plugin for testing purposes.
+ */
+class openlayers_test_rnd_factor extends 
+  openlayers_style_plugin {
+  /**
+   * Provide initial values for options.
+   */
+  function options_init() {
+    return array(
+    );
+  }
+  
+  /**
+   * Options form.
+   */
+  function options_form($defaults = array()) {
+    $form = array();
+    return $form;
+  }
+
+  /**
+   * Get an array of style property callbacks
+   */
+  function get_context_properties() {
+    return array(
+      'fillOpacity' => 'getFactor',
+      'strokeOpacity' => 'getFactor',
+      'graphicOpacity' => 'getFactor',
+    );
+  }
+
+
+  /**
+   * Render function
+   */
+  function render() {
+    // Add JS
+    drupal_add_js(drupal_get_path('module', 'openlayers_test') .
+      '/plugins/style_plugin/openlayers_test_rnd_factor.js');
+  }
+}
diff --git tests/plugins/style_plugin/openlayers_test_rnd_factor.js tests/plugins/style_plugin/openlayers_test_rnd_factor.js
new file mode 100644
index 0000000..823de59
--- /dev/null
+++ tests/plugins/style_plugin/openlayers_test_rnd_factor.js
@@ -0,0 +1,26 @@
+// $Id$
+
+/**
+ * @file
+ * File to hold custom context styling
+ */
+
+/**
+ * Style plugin context class
+ */
+Drupal.openlayers.style_plugin.openlayers_test_rnd_factor = function (params) {
+  this.params = params;
+};
+
+/**
+ * Style plugin context class methods
+ */
+Drupal.openlayers.style_plugin.openlayers_test_rnd_factor.prototype = {
+
+  // Fill opacity context.  Sets random fill opacity.
+  'getFactor' : function(feature) {
+    // Random factor
+    return Math.random();
+  }
+
+};
diff --git tests/plugins/style_plugin/openlayers_test_rnd_int.inc tests/plugins/style_plugin/openlayers_test_rnd_int.inc
new file mode 100644
index 0000000..c72fd90
--- /dev/null
+++ tests/plugins/style_plugin/openlayers_test_rnd_int.inc
@@ -0,0 +1,88 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Test 
+ */
+
+// Define plugin for CTools
+$plugin = array(
+  'title' => t('TEST: random integer'),
+  'description' => t('Example style plugin for context styling. '
+    . 'Provides random integers.'),
+  'style_plugin' => array(
+    'class' => 'openlayers_test_rnd_int',
+    'parent' => 'openlayers_style_plugin',
+  ),
+);
+
+/**
+ * Style Plugin for testing purposes.
+ */
+class openlayers_test_rnd_int extends 
+  openlayers_style_plugin {
+  /**
+   * Provide initial values for options.
+   */
+  function options_init() {
+    return array(
+      'low' => 1,
+      'high' => 10,
+    );
+  }
+  
+  /**
+   * Options form.
+   */
+  function options_form($defaults = array()) {
+    $form = array();
+    
+    // Allow use to pick the highest and lowest for random
+    // point radius
+    $form['low'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Lowest value'),
+      '#description' => t('Lowest value for the random integer.'),
+      '#default_value' => isset($defaults['low']) ?
+        $defaults['low'] : 2,
+    );
+    $form['high'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Highest value'),
+      '#description' => t('Highest value for the random integer.'),
+      '#default_value' => isset($defaults['high']) ?
+        $defaults['high'] : 10,
+    );
+    
+    return $form;
+  }
+
+  /**
+   * Get an array of style property callbacks
+   */
+  function get_context_properties() {
+    return array(
+      'pointRadius' => 'getInt',
+      'strokeWidth' => 'getInt',
+      'graphicWidth' => 'getInt',
+      'graphicHeight' => 'getInt',
+      'graphicXOffset' => 'getInt',
+      'graphicYOffset' => 'getInt',
+      'rotation' => 'getInt',
+      'labelXOffset' => 'getInt',
+      'labelYOffset' => 'getInt',
+      'fontSize' => 'getInt',
+    );
+  }
+
+
+  /**
+   * Render function
+   */
+  function render() {
+    // Add JS
+    drupal_add_js(drupal_get_path('module', 'openlayers_test') .
+      '/plugins/style_plugin/openlayers_test_rnd_int.js');
+  }
+}
diff --git tests/plugins/style_plugin/openlayers_test_rnd_int.js tests/plugins/style_plugin/openlayers_test_rnd_int.js
new file mode 100644
index 0000000..b575d70
--- /dev/null
+++ tests/plugins/style_plugin/openlayers_test_rnd_int.js
@@ -0,0 +1,37 @@
+// $Id$
+
+/**
+ * @file
+ * File to hold custom context styling
+ */
+
+/**
+ * Style plugin context class
+ */
+Drupal.openlayers.style_plugin.openlayers_test_rnd_int = function (params) {
+  this.params = params;
+  this.params.high = parseInt(this.params.high);
+  this.params.low = parseInt(this.params.low);
+};
+
+/**
+ * Style plugin context class methods
+ */
+Drupal.openlayers.style_plugin.openlayers_test_rnd_int.prototype = {
+
+  // Private methods (not copied to final style context object)
+  'prv' : {
+    'random' : function(low, high) {
+      return Math.floor(Math.random() * (high-low+1)) + low;
+    }
+  },
+  
+  // Point radius context.  Given paramters, gets a random
+  // pointRadius.
+  'getInt' :  function(feature) {
+    var high = this.params.high;
+    var low = this.params.low;
+    var ret = this.prv.random(low, high);
+    return ret;
+  }
+};
