diff --git modules/block.skinr.inc modules/block.skinr.inc
index df49de2..95386b0 100644
--- modules/block.skinr.inc
+++ modules/block.skinr.inc
@@ -14,38 +14,17 @@
  * Implements hook_skinr_config_info().
  */
 function block_skinr_config_info() {
-  $data['block']['form']['skinr_ui_form'] = array(
-    'preprocess_hook_callback' => 'block_skinr_preprocess_hook_callback',
-    'title' => t('block settings'),
-    'collapsed' => FALSE,
-  );
-  $data['block']['preprocess']['block'] = array(
-    'index_handler' => 'block_skinr_preprocess_index_handler',
-  );
-  $data['block']['contextual_links']['block'] = array(
-    'contextual_links_handler' => 'block_skinr_contextual_links',
-  );
-
-  return $data;
+  return array('block');
 }
 
 /**
- * Skinr preprocess hook callback.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_alter().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_alter().
- *
- * @return
- *   An array of preprocess hooks we wish to use.
+ * Implements hook_skinr_theme_hooks().
  */
-function block_skinr_preprocess_hook_callback(&$form, $form_state) {
-  $preprocess_hooks = array();
-
-  if (empty($form['module']['#value']) && !empty($form['skinr']['element']['#value'])) {
-    list($module, $delta) = explode('__', $form['skinr']['element']['#value'], 2);
+function block_skinr_theme_hooks($module, $element) {
+  $theme_hooks = array();
 
+  if ($module == 'block') {
+    list($module, $delta) = explode('__', $element, 2);
     $result = db_select('block', 'b')
       ->fields('b')
       ->distinct()
@@ -54,53 +33,23 @@ function block_skinr_preprocess_hook_callback(&$form, $form_state) {
       ->range(0, 1)
       ->execute();
     foreach ($result as $block) {
-      $preprocess_hooks[] = 'block_'. $block->module;
+      $theme_hooks[] = 'block_'. $block->module;
     }
+    $theme_hooks[] = 'block';
   }
-  else {
-    $preprocess_hooks[] = 'block_'. $form['module']['#value'];
-  }
-  $preprocess_hooks[] = 'block';
-
-  return $preprocess_hooks;
-}
 
-/**
- * Skinr preprocess index handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from module_preprocess().
- *
- * @return
- *   The index where we can find our values in Skinr's data structure. If an
- *   array is returned, it will loop through each index in Skinr's data
- *   structure and merge the returned classes.
- */
-function block_skinr_preprocess_index_handler(&$variables) {
-  return array($variables['block']->module . '__' . $variables['block']->delta);
+  return $theme_hooks;
 }
 
 /**
- * Skinr contextual links handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from skinr_preprocess().
- *
- * @return
- *   An array. Each value is an array that forms the function arguments for
- *   menu_contextual_links(). For example:
- *   @code
- *     array(
- *       'admin/appearance/skinr/edit', array('system', 'navigation')),
- *     )
- *   @endcode
+ * Implements hook_skinr_elements().
  */
-function block_skinr_contextual_links(&$variables) {
-  $links = array();
-  $links['skinr-block'] = array(
-    'admin/appearance/skinr/edit/nojs', array('block', $variables['block']->module . '__' . $variables['block']->delta),
-  );
-  return $links;
+function block_skinr_elements(&$variables, $hook) {
+  $elements = array();
+  if ($hook == 'block') {
+    $elements['block'] = array($variables['block']->module . '__' . $variables['block']->delta);
+  }
+  return $elements;
 }
 
 /**
diff --git modules/comment.skinr.inc modules/comment.skinr.inc
index 0e17809..524abbe 100644
--- modules/comment.skinr.inc
+++ modules/comment.skinr.inc
@@ -14,122 +14,34 @@
  * Implements hook_skinr_config_info().
  */
 function comment_skinr_config_info() {
-  $data['comment']['form']['skinr_ui_form'] = array(
-    'preprocess_hook_callback' => 'comment_skinr_preprocess_hook_callback',
-    'title' => t('comment settings'),
-    'skinr_weight' => 2,
-    'collapsed' => FALSE,
-  );
-  $data['comment']['preprocess']['comment_wrapper'] = array(
-    'index_handler' => 'comment_skinr_preprocess_index_handler',
-  );
-  $data['comment']['contextual_links']['comment_wrapper'] = array(
-    'contextual_links_handler' => 'comment_skinr_contextual_links',
-  );
-
-  return $data;
+  return array('comment');
 }
 
 /**
- * Skinr form index handler.
- *
- * @param $op
- *   What kind of action is being performed. Possible values:
- *   - 'form': the form elements for Skinr are being inserted in a form.
- *   - 'submit': the form has been submitted.
- * @param &$form
- *   - For 'form', passes in the $form parameter from hook_form_alter().
- *   - For 'submit', passes in the $form parameter from hook_form_submit().
- * @param $form_state
- *   - For 'form', passes in the $form_state parameter from hook_form_alter().
- *   - For 'submit', passes in the $form_state parameter from hook_form_submit().
- *
- * @return
- *   The index where we can find our values in Skinr's data structure.
- */
-function comment_skinr_form_index_handler($op, &$form, &$form_state) {
-  switch ($op) {
-    case 'form':
-      return $form['#node_type']->type;
-
-    case 'submit':
-      // Clear old variable before we set a new one if the node type has changed
-      if ($form_state['values']['old_type'] != $form_state['values']['type']) {
-        foreach ($form_state['values']['skinr_settings']['comment_group'] as $theme_name => $theme_data) {
-          $skinr = new stdClass();
-          $skinr->theme = $theme_name;
-          $skinr->module = 'comment';
-          $skinr->element = $form_state['values']['old_type'];
-          $skinr->skins = array();
-
-          skinr_skin_save($skinr);
-        }
-      }
-    return $form_state['values']['type'];
-  }
-}
-
-/**
- * Skinr preprocess hook callback.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_alter().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_alter().
- *
- * @return
- *   An array of preprocess hooks we wish to use.
+ * Implements hook_skinr_theme_hooks().
  */
-function comment_skinr_preprocess_hook_callback(&$form, $form_state) {
-  $preprocess_hooks = array();
+function comment_skinr_theme_hooks($module, $element) {
+  $theme_hooks = array();
 
-  if (!isset($form['#node_type']->type) && !empty($form['skinr']['element']['#value'])) {
-    $preprocess_hooks[] = 'comment_wrapper_' . $form['skinr']['element']['#value'];
-  }
-  else {
-    $preprocess_hooks[] = 'comment_wrapper_' . $form['#node_type']->type;
+  if ($module == 'comment') {
+    $theme_hooks = array(
+      'comment_wrapper_' . $element,
+      'comment_wrapper',
+    );
   }
-  $preprocess_hooks[] = 'comment_wrapper';
 
-  return $preprocess_hooks;
+  return $theme_hooks;
 }
 
 /**
- * Skinr preprocess index handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from skinr_preprocess().
- *
- * @return
- *   The index where we can find our values in Skinr's data structure. If an
- *   array is returned, it will loop through each index in Skinr's data
- *   structure and merge the returned classes.
+ * Implements hook_skinr_elements().
  */
-function comment_skinr_preprocess_index_handler(&$variables) {
-  return array($variables['node']->type);
-}
-
-/**
- * Skinr contextual links handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from skinr_preprocess().
- *
- * @return
- *   An array. Each value is an array that forms the function arguments for
- *   menu_contextual_links(). For example:
- *   @code
- *     array(
- *       'admin/appearance/skinr/edit', array('system', 'navigation')),
- *     )
- *   @endcode
- */
-function comment_skinr_contextual_links(&$variables) {
-  $links = array();
-  $links['skinr-comment'] = array(
-    'admin/appearance/skinr/edit/nojs', array('comment', $variables['node']->type),
-  );
-  return $links;
+function comment_skinr_elements(&$variables, $hook) {
+  $elements = array();
+  if ($hook == 'comment_wrapper') {
+    $elements['comment'] = array($variables['node']->type);
+  }
+  return $elements;
 }
 
 /**
diff --git modules/node.skinr.inc modules/node.skinr.inc
index d64fee0..bbd3e0f 100644
--- modules/node.skinr.inc
+++ modules/node.skinr.inc
@@ -14,120 +14,34 @@
  * Implements hook_skinr_config_info().
  */
 function node_skinr_config_info() {
-  $data['node']['form']['skinr_ui_form'] = array(
-    'preprocess_hook_callback' => 'node_skinr_preprocess_hook_callback',
-    'title' => t('node settings'),
-    'collapsed' => FALSE,
-  );
-  $data['node']['preprocess']['node'] = array(
-    'index_handler' => 'node_skinr_preprocess_index_handler',
-  );
-  $data['node']['contextual_links']['node'] = array(
-    'contextual_links_handler' => 'node_skinr_contextual_links',
-  );
-
-  return $data;
+  return array('node');
 }
 
 /**
- * Skinr form index handler.
- *
- * @param $op
- *   What kind of action is being performed. Possible values:
- *   - 'form': the form elements for Skinr are being inserted in a form.
- *   - 'submit': the form has been submitted.
- * @param &$form
- *   - For 'form', passes in the $form parameter from hook_form_alter().
- *   - For 'submit', passes in the $form parameter from hook_form_submit().
- * @param $form_state
- *   - For 'form', passes in the $form_state parameter from hook_form_alter().
- *   - For 'submit', passes in the $form_state parameter from hook_form_submit().
- *
- * @return
- *   The index where we can find our values in Skinr's data structure.
+ * Implements hook_skinr_theme_hooks().
  */
-function node_skinr_form_index_handler($op, &$form, $form_state) {
-  switch ($op) {
-    case 'form':
-      return $form['#node_type']->type;
+function node_skinr_theme_hooks($module, $element) {
+  $theme_hooks = array();
 
-    case 'submit':
-      // Clear old variable before we set a new one if the node type has changed.
-      if ($form_state['values']['old_type'] != $form_state['values']['type']) {
-        foreach ($form_state['values']['skinr_settings']['node_group'] as $theme_name => $theme_data) {
-          $skinr = new stdClass();
-          $skinr->theme = $theme_name;
-          $skinr->module = 'node';
-          $skinr->element = $form_state['values']['old_type'];
-          $skinr->skins = array();
-
-          skinr_skin_save($skinr);
-        }
-      }
-    return $form_state['values']['type'];
+  if ($module == 'node') {
+    $theme_hooks = array(
+      'node_' . $element,
+      'node',
+    );
   }
-}
 
-/**
- * Skinr preprocess hook callback.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_alter().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_alter().
- *
- * @return
- *   An array of preprocess hooks we wish to use.
- */
-function node_skinr_preprocess_hook_callback(&$form, $form_state) {
-  $preprocess_hooks = array();
-
-  if (!isset($form['#node_type']->type) && !empty($form['skinr']['element']['#value'])) {
-    $preprocess_hooks[] = 'node_' . $form['skinr']['element']['#value'];
-  }
-  else {
-    $preprocess_hooks[] = 'node_' . $form['#node_type']->type;
-  }
-  $preprocess_hooks[] = 'node';
-
-  return $preprocess_hooks;
-}
-
-/**
- * Skinr preprocess index handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from module_preprocess().
- *
- * @return
- *   The index where we can find our values in Skinr's data structure. If an
- *   array is returned, it will loop through each index in Skinr's data
- *   structure and merge the returned classes.
- */
-function node_skinr_preprocess_index_handler(&$variables) {
-  return array($variables['node']->type);
+  return $theme_hooks;
 }
 
 /**
- * Skinr contextual links handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from skinr_preprocess().
- * @return
- *   An array. Each value is an array that forms the function arguments for
- *   menu_contextual_links(). For example:
- *   @code
- *     array(
- *       'admin/appearance/skinr/edit', array('system', 'navigation')),
- *     )
- *   @endcode
+ * Implements hook_skinr_elements().
  */
-function node_skinr_contextual_links(&$variables) {
-  $links = array();
-  $links['skinr-node'] = array(
-    'admin/appearance/skinr/edit/nojs', array('node', $variables['node']->type),
-  );
-  return $links;
+function node_skinr_elements(&$variables, $hook) {
+  $elements = array();
+  if ($hook == 'node') {
+    $elements['node'] = array($variables['node']->type);
+  }
+  return $elements;
 }
 
 /**
diff --git modules/panels.skinr.inc modules/panels.skinr.inc
index 01f633e..a065f59 100644
--- modules/panels.skinr.inc
+++ modules/panels.skinr.inc
@@ -14,56 +14,39 @@
  * Implements hook_skinr_config_info().
  */
 function panels_skinr_config_info() {
-  $data['panels']['form']['skinr_ui_form'] = array(
-    'preprocess_hook_callback' => 'panels_skinr_preprocess_hook_callback',
-    'title' => t('panel pane settings'),
-    'collapsed' => FALSE,
-  );
-  $data['panels']['preprocess']['panels_pane'] = array(
-    'index_handler' => 'panels_skinr_preprocess_index_handler',
-  );
-
-  return $data;
+  return array('panels');
 }
 
 /**
- * Skinr preprocess hook callback.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_alter().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_alter().
- *
- * @return
- *   An array of preprocess hooks we wish to use.
+ * Implements hook_skinr_theme_hooks().
  */
-function panels_skinr_preprocess_hook_callback(&$form, $form_state) {
-  if (strpos($form['skinr']['element']['#value'], 'region') === 0) {
-    return 'panels_region';
-  }
-  elseif (strpos($form['skinr']['element']['#value'], 'pane') === 0) {
-    return 'panels_pane';
-  }
-  else {
-    return 'panels_display';
+function panels_skinr_theme_hooks($module, $element) {
+  $theme_hooks = array();
+
+  if ($module == 'panels') {
+    if (strpos($element, 'region') === 0) {
+      $theme_hooks[] = 'panels_region';
+    }
+    elseif (strpos($element, 'pane') === 0) {
+      $theme_hooks[] = 'panels_pane';
+    }
+    else {
+      $theme_hooks[] = 'panels_display';
+    }
   }
+
+  return $theme_hooks;
 }
 
 /**
- * Skinr preprocess index handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from module_preprocess().
- *
- * @return
- *   The index where we can find our values in Skinr's data structure. If an
- *   array is returned, it will loop through each index in Skinr's data
- *   structure and merge the returned classes.
+ * Implements hook_skinr_elements().
  */
-function panels_skinr_preprocess_index_handler(&$variables) {
-  $index = array();
-  $index[] = 'pane__' . $variables['pane']->did . '__' . $variables['pane']->pid;
-  return $index;
+function panels_skinr_elements(&$variables, $hook) {
+  $elements = array();
+  if ($hook == 'panels_pane') {
+    //$elements['panels'] = array('pane__' . $variables['pane']->did . '__' . $variables['pane']->pid);
+  }
+  return $elements;
 }
 
 /**
diff --git modules/views.skinr.inc modules/views.skinr.inc
index ea34136..d6521c7 100644
--- modules/views.skinr.inc
+++ modules/views.skinr.inc
@@ -15,77 +15,53 @@
  * Implements hook_skinr_config_info().
  */
 function views_skinr_config_info() {
-  $data['views']['form']['skinr_ui_form'] = array(
-    'preprocess_hook_callback' => 'views_skinr_preprocess_hook_callback',
-    'title' => t('views style settings'),
-    'collapsed' => FALSE,
-  );
-  $data['views']['preprocess']['views_view'] = array(
-    'index_handler' => 'views_skinr_preprocess_index_handler',
-  );
-  $data['views']['contextual_links']['page'] = array(
-    'contextual_links_handler' => 'views_skinr_contextual_links',
-  );
-
-  return $data;
+  return array('views');
 }
 
 /**
- * Skinr form preprocess hook callback.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_alter().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_alter().
- *
- * @return
- *   An array of preprocess hooks we wish to use.
+ * Implements hook_skinr_theme_hooks().
  */
-function views_skinr_preprocess_hook_callback(&$form, $form_state) {
-  $preprocess_hooks = array('views_view');
+function views_skinr_theme_hooks($module, $element) {
+  $theme_hooks = array();
 
-  if (!empty($form_state['view']) && !empty($form_state['view']->name)) {
-    $view = $form_state['view'];
-  }
-  elseif(isset($form['skinr']['element']['#value'])) {
-    list($element_info['view'], $element_info['display']) = explode('__', $form['skinr']['element']['#value'], 2);
-    if ($view = views_get_view($element_info['view'])) {
-      $view->execute_display($element_info['display']);
-    }
-  }
+  if ($module == 'views') {
+    list($name, $display_id) = explode('__', $element, 2);
+    if ($view = views_get_view($name)) {
+      $view->execute_display($display_id);
 
-  if (!empty($view)) {
-    $display = $view->display[$view->current_display];
+      $display = $view->display[$view->current_display];
 
-    // Create list of suggested templates.
-    $preprocess_hooks = views_theme_functions('views_view', $view, $display);
-    // Fetch additional style based suggested templates.
-    $additional_hooks = views_theme_functions($display->display_plugin, $view, $display);
+      // Create list of suggested templates.
+      $theme_hooks = views_theme_functions('views_view', $view, $display);
 
-    $preprocess_hooks = array_merge($additional_hooks, $preprocess_hooks);
+      // @todo Determine whether below code is still relevant.
+      /*
+      // Fetch additional style based suggested templates.
+      $additional_hooks = views_theme_functions($display->display_plugin, $view, $display);
+      $theme_hooks = array_merge($additional_hooks, $theme_hooks);
+      */
+    }
+    else {
+      $theme_hooks[] = 'views_view';
+    }
   }
 
-  return $preprocess_hooks;
-}
-
-/**
- * Skinr preprocess index handler.
- */
-function views_skinr_preprocess_index_handler(&$variables) {
-  return array($variables['view']->name .'__'. $variables['view']->current_display);
+  return $theme_hooks;
 }
 
 /**
- * Skinr contextual links handler.
+ * Implements hook_skinr_elements().
  */
-function views_skinr_contextual_links(&$variables) {
-  $links = array();
-  if (!empty($variables['page']['#views_contextual_links_info'])) {
-    $links['skinr-views'] = array(
-      'admin/appearance/skinr/edit/nojs', array('views', $variables['page']['#views_contextual_links_info']['views_ui']['view_name'] . '__' . $variables['page']['#views_contextual_links_info']['views_ui']['view_display_id']),
-    );
+function views_skinr_elements(&$variables, $hook, $op) {
+  $elements = array();
+  if ($op == 'preprocess' && $hook == 'views_view') {
+    $elements['views'] = array($variables['view']->name .'__'. $variables['view']->current_display);
+  }
+  elseif ($op == 'contextual_links' && $hook == 'page' && isset($variables['page']['#views_contextual_links_info'])) {
+    // Contextual links for views are on 'page' hook, not 'views_view'.
+    $elements['views'] = array($variables['page']['#views_contextual_links_info']['views_ui']['view_name'] . '__' . $variables['page']['#views_contextual_links_info']['views_ui']['view_display_id']);
   }
-  return $links;
+  return $elements;
 }
 
 /**
diff --git skinr.api.php skinr.api.php
index 47ccbf2..9a98a98 100644
--- skinr.api.php
+++ skinr.api.php
@@ -26,69 +26,83 @@
  * This must either be in the same directory as the .module file or in a subdirectory
  * named 'includes'.
  *
- * The configuration info is keyed by the MODULENAME. In the case of $data['block']
- * 'block' is the name of the module.
- *
- * There are two section to the configuration array:
- * - When you specify a "form", Skinr will insert its skins selector into the form
- *   with the specified form_id. Example: $data[MODULENAME]['form'][FORM_ID] = ...
- *   You can specify multiple forms that Skinr should add its skins selector to. A
- *   good example where this would be needed is blocks where you have a different
- *   form_id for adding a new block than when editing an existing block.
- * - When you specify "preprocess", Skinr will create a $vars['skinr'] variable
- *   containing the appropriate skin classes for the specified preprocess hook.
- *   Example: $data[MODULENAME]['preprocess'][PREPROCESS_HOOK] = ...
+ * @return
+ *   An array of element types this module supports.
+ */
+function hook_skinr_config() {
+  return array('block');
+}
+
+/**
+ * Provide a list of all available theme hooks for a given element.
  *
- * Form options:
- * - "index_handler" is required. It specifies a function that returns an index where
- *   Skinr can find the values in its data structure.
- * - "access_handler" specifies a function that returns TRUE if you wish to grant access
- *   to skinr, or FALSE if not.
- * - "data_handler" specifies a function that returns the data used to populate the form.
- *   This is useful in cases where a module caches data (like panels and views) and has
- *   an option to cancel changes.
- * - "submit_handler" specifies a function that process the form data and saves it.
- * - "preprocess_hook" is required. Each skin states which preprocess hooks it will
- *   work for. This parameter will limit the available skins by the specified
- *   preprocess hook.
- * - "title" overrides the default title on the Skinr fieldset.
- * - "description" overrides the default description that provides additional
- *   information to the user about this Skinr selector.
- * - "weight" overrides the order where Skinrs selector appears on the form.
- * - "collapsed" sets whether the fieldset appears collapsed or not. Defaults to TRUE.
- * - "selector_weight" overrides the weight of the selector field inside the fieldset.
- *   This is useful, for instance, if you have multiple modules add selectors to the
- *   same form.
- * - "selector_title" overrides the title of the selector field inside the fieldset.
+ * @param $module
+ *   The module implementing given element.
+ * @param $element
+ *   An element.
  *
- * Preprocess options:
- * - "indexhandler" is required. It specifies a function that returns an index where
- *   Skinr can find the values in its data structure.
+ * @return
+ *   An array of theme hooks.
  */
-function hook_skinr_config() {
-  $data['example']['form']['block_admin_configure'] = array(
-    'index_handler' => 'example_skinr_index_handler',
-    'preprocess_hook' => 'block',
-    'title' => t('Skinr settings'),
-    'description' => t('Here you can manage which Skinr styles, if any, you want to apply.'),
-    'weight' => 1,
-    'collapsed' => TRUE,
-    'selector_weight' => 0,
-    'selector_title' => t('Choose Skinr Style(s)'),
-  );
-  $data['example']['form']['block_add_block_form'] = array(
-    'index_handler' => 'example_skinr_index_handler',
-    'title' => t('Skinr settings'),
-    'description' => t('Here you can manage which Skinr styles, if any, you want to apply to this block.'),
-    'weight' => -10,
-    'collapsed' => FALSE,
-  );
+function hook_skinr_theme_hooks($module, $element) {
+  $theme_hooks = array();
 
-  $data['example']['preprocess']['block'] = array(
-    'index_handler' => 'block_skinr_preprocess_handler_block',
-  );
+  if ($module == 'node') {
+    $theme_hooks =  array(
+      'node_' . $element,
+      'node',
+    );
+  }
+
+  return $theme_hooks;
+}
 
-  return $data;
+/**
+ * Perform alterations to theme hooks before widgets are rendered.
+ *
+ * @param $theme_hooks
+ *   An array of theme hooks.
+ * @param $module
+ *   The module implementing given element.
+ * @param $element
+ *   An element.
+ */
+function hook_skinr_theme_hooks_alter(&$theme_hooks, $module, $element) {
+  if ($module == 'node') {
+    array_unshift($theme_hooks, 'node_' . $element . '_custom');
+  }
+}
+
+/**
+ * Return an array of element ids.
+ *
+ * @todo Needs a better description.
+ *
+ * @param $variables
+ *   The variables array from skinr_preprocess().
+ * @param $hook
+ *   The name of the theme hook.
+ * @param $op
+ *   The operation being performed:
+ *   - 'preprocess'
+ *   - 'contextual_links'
+ *
+ * @return
+ *   An array of element arrays, keyed by element type. Example:
+ * @code
+ *   array(
+ *     'block' => array('system__navigation'),
+ *   );
+ * @endcode
+ *
+ * @see skinr_preprocess()
+ */
+function hook_skinr_elements(&$variables, $hook, $op) {
+  $elements = array();
+  if ($hook == 'block') {
+    $elements['block'] = array($variables['block']->module . '__' . $variables['block']->delta);
+  }
+  return $elements;
 }
 
 /**
diff --git skinr.handlers.inc skinr.handlers.inc
index 2cc6474..41203c0 100644
--- skinr.handlers.inc
+++ skinr.handlers.inc
@@ -4,181 +4,6 @@
  * Defines the various default handler functions to support Skinr.
  */
 
-/*
- * Skinr access handler.
- *
- * @param $op
- *   What kind of action is being performed. Possible values:
- *   - access skinr: Access to edit Skinr's settings.
- *   - access skinr classes: Access to edit Skinr's additional classes.
- * @param &$form
- *   Passes in the $form parameter from hook_form_alter().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_alter().
- *
- * @return
- *   TRUE if we get access, FALSE otherwise.
- */
-function skinr_access_handler($op, &$form, $form_state) {
-  switch ($op) {
-    case 'edit skin settings':
-      return user_access('edit skin settings') || user_access('administer skinr');
-    case 'edit advanced skin settings':
-      return user_access('edit advanced skin settings') || user_access('administer skinr');
-  }
-}
-
-/**
- * Skinr form index handler.
- *
- * @param $op
- *   What kind of action is being performed. Possible values:
- *   - form: The form elements for Skinr are being inserted in a form.
- *   - submit: The form has been submitted.
- * @param &$form
- *   - For 'form', passes in the $form parameter from hook_form_alter().
- *   - For 'submit', passes in the $form parameter from hook_form_submit().
- * @param $form_state
- *   - For 'form', passes in the $form_state parameter from hook_form_alter().
- *   - For 'submit', passes in the $form_state parameter from
- *     hook_form_submit().
- *
- * @return
- *   The index where we can find our values in Skinr's data structure.
- */
-function skinr_index_handler($op, &$form, $form_state) {
-  switch ($op) {
-    case 'form':
-      if (empty($form['skinr']['element']['#value'])) {
-        trigger_error(sprintf("The form with form_id '%' is not a valid skinr form.", $form['form_id']['#value']), E_USER_ERROR);
-        return FALSE;
-      }
-      return $form['skinr']['element']['#value'];
-
-    case 'submit':
-      if (empty($form_state['values']['element'])) {
-        trigger_error(sprintf("The form with form_id '%' is not a valid skinr form.", $form['form_id']['#value']), E_USER_ERROR);
-        return FALSE;
-      }
-      return $form_state['values']['element'];
-  }
-}
-
-/**
- * Skinr data handler.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_submit().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_submit().
- * @param $theme
- *   The theme that is currently being processed.
- * @param $module
- *   The module that is currently being processed.
- * @param $form_settings
- *   The settings from hook_skinr_config() for the form that's currently being
- *   processed.
- *
- * @return
- *   A skinr settings object.
- */
-function skinr_data_handler(&$form, $form_state, $theme, $module, $form_settings) {
-  // Ensure we have the required index_handler
-  if (empty($form_settings['index_handler'])) {
-    trigger_error(sprintf("No index_handler was found for form_id '%s' in module '%s'.", $form_id, $module), E_USER_ERROR);
-  }
-  $element = skinr_handler('form_index_handler', 'form', $form_settings['index_handler'], $form, $form_state);
-
-  $params = array(
-    'theme' => $theme,
-    'module' => $module,
-    'element' => $element,
-  );
-  return skinr_skin_load_multiple(skinr_skin_get_sids($params));
-}
-
-/**
- * Skinr submit handler.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_submit().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_submit().
- * @param $module
- *   The module that is currently being processed.
- * @param $form_settings
- *   The settings from hook_skinr_config() for the form that's currently being
- *   processed.
- */
-function skinr_submit_handler(&$form, $form_state, $module, $form_settings) {
-  if (!$element = skinr_handler('form_index_handler', 'submit', $form_settings['index_handler'], $form, $form_state)) {
-    // We require a valid element to continue.
-    // @todo This should really be in a validation handler.
-    drupal_set_message(t("Skinr settings weren't saved due to an error."), 'error');
-    return;
-  }
-
-  if (isset($form_state['values']['skinr_settings'][$module . '_group'])) {
-    foreach ($form_state['values']['skinr_settings'][$module . '_group'] as $theme_name => $theme) {
-      // Process widgets.
-      if (!empty($theme) && is_array($theme)) {
-        foreach ($theme as $skin_name => $options) {
-          if ($skin_name == '_additional' && !user_access('edit advanced skin settings')) {
-            // This user doesn't have access to alter these options.
-            continue;
-          }
-
-          // Ensure options is an array.
-          if (!is_array($options)) {
-            $options = $skin_name == '_additional' ? explode(' ', $options) : array($options);
-          }
-          // Sanitize options.
-          $options = _skinr_array_strip_empty($options);
-
-          // Find existing skin.
-          $params = array(
-            'theme' => $theme_name,
-            'module' => $module,
-            'element' => $element,
-            'skin' => $skin_name,
-          );
-          $sids = skinr_skin_get_sids($params);
-
-          unset($skin);
-          if (!empty($sids)) {
-            $sid = reset($sids);
-            $skin = skinr_skin_load($sid);
-          }
-
-          if (empty($options)) {
-            if (!empty($skin)) {
-              // Delete this skin configuration.
-              skinr_skin_delete($skin->sid);
-            }
-            continue;
-          }
-
-          if (empty($skin)) {
-            // It doesn't exist, so create a new skin.
-            $skin = new stdClass();
-            $skin->theme = $theme_name;
-            $skin->module = $module;
-            $skin->element = $element;
-            $skin->skin = $skin_name;
-          }
-          $skin->options = $options;
-          $skin->status = 1;
-
-          // Save skin.
-          if (!skinr_skin_save($skin)) {
-            drupal_set_message(t("Skinr settings for %skin weren't saved due to an error.", array('%skin' => $skin_name)), 'error');
-          }
-        }
-      }
-    }
-  }
-}
-
 /**
  * Implements hook_skinr_api_VERSION().
  */
diff --git skinr.module skinr.module
index 8b6a3e3..05c916c 100644
--- skinr.module
+++ skinr.module
@@ -47,6 +47,9 @@ function skinr_help($path, $arg) {
 function skinr_hook_info() {
   $hooks = array(
     'skinr_api_2',
+    'skinr_elements',
+    'skinr_theme_hooks',
+    'skinr_theme_hooks_alter',
   );
   $hooks = array_fill_keys($hooks, array(
     'group' => 'skinr',
@@ -62,28 +65,6 @@ function skinr_cache_reset() {
 }
 
 /**
- * Implements hook_module_implements_alter().
- */
-function skinr_module_implements_alter(&$implementations, $hook) {
-  // Run Skinr first to avoid issues with other modules during hook_init().
-  if ($hook == 'init') {
-    $skinr['skinr'] = $implementations['skinr'];
-    unset($implementations['skinr']);
-    $implementations = array_merge($skinr, $implementations);
-  }
-}
-
-/**
- * Implements hook_init().
- *
- * @todo Kill me. Entirely.
- */
-function skinr_init() {
-  module_load_include('inc', 'skinr', 'skinr.handlers');
-  skinr_load_includes();
-}
-
-/**
  * Implements hook_preprocess().
  *
  * @todo Optimize this function by removing dependencies on
@@ -96,82 +77,80 @@ function skinr_preprocess(&$variables, $hook) {
     return;
   }
 
-  $config = skinr_get_config_info();
   $current_theme = skinr_current_theme();
-  $theme_registry = theme_get_registry();
   $skin_info = skinr_get_skin_info();
 
+  $theme_registry = theme_get_registry();
   $original_hook = (isset($theme_registry[$hook]['original hook']) ? $theme_registry[$hook]['original hook'] : $hook);
 
-  foreach ($config as $module => $module_settings) {
-    if (!empty($module_settings['preprocess'][$original_hook])) {
-      $preprocess_settings = $module_settings['preprocess'][$original_hook];
-      $elements = skinr_handler('preprocess_index_handler', 'preprocess', $preprocess_settings['index_handler'], $variables);
-      if (empty($elements)) {
-        // We can receive empty arrays; if that happens, there's no point in continuing.
-        continue;
-      }
+  // An array of $elements based on $module and $original_hook, derived from $variables.
+  $array_elements = module_invoke_all('skinr_elements', $variables, $original_hook, 'preprocess');
+  foreach ($array_elements as $module => $elements) {
+    if (empty($elements)) {
+      // We can receive empty arrays; if that happens, there's no point
+      // in continuing.
+      continue;
+    }
 
-      // Get a list of skin configuration IDs to pass to
-      // skinr_skin_load_multiple().
-      $params = array(
-        'theme' => $current_theme,
-        'module' => $module,
-        'element' => $elements,
-        'status' => 1,
-      );
-      $sids = skinr_skin_get_sids($params);
-      if (empty($sids)) {
-        // Noting to apply.
-        continue;
-      }
+    // Get a list of skin configuration IDs to pass to
+    // skinr_skin_load_multiple().
+    $params = array(
+      'theme' => $current_theme,
+      'module' => $module,
+      'element' => $elements,
+      'status' => 1,
+    );
+    $sids = skinr_skin_get_sids($params);
+    if (empty($sids)) {
+      // Noting to apply.
+      continue;
+    }
 
-      $applied_skins = array();
-      foreach (skinr_skin_load_multiple($sids) as $skin) {
-        $applied_skins = array($skin->skin => $skin->options) + $applied_skins;
-      }
+    $applied_skins = array();
+    foreach (skinr_skin_load_multiple($sids) as $skin) {
+      $applied_skins = array($skin->skin => $skin->options) + $applied_skins;
+    }
 
-      // Invoke hook_skinr_preprocess_alter() in all modules.
-      // @todo Review whether this alter hook is useful or not, and if it's in
-      //   the right place or not.
-      $context = array(
-        'hook' => $hook,
-        'variables' => &$variables,
-        'theme' => $current_theme,
-        'module' => $module,
-        'elements' => $elements,
-        'options' => $applied_skins,
-      );
-      drupal_alter('skinr_preprocess', $context);
+    // Invoke hook_skinr_preprocess_alter() in all modules.
+    // @todo Review whether this alter hook is useful or not, and if it's in
+    //   the right place or not.
+    $context = array(
+      'hook' => $hook,
+      'variables' => &$variables,
+      'theme' => $current_theme,
+      'module' => $module,
+      'elements' => $elements,
+      'options' => $applied_skins,
+    );
+    drupal_alter('skinr_preprocess', $context);
 
-      // Use drupal_process_attached() to add attachements such as JS and CSS.
-      if (!empty($applied_skins)) {
-        foreach ($applied_skins as $skin_name => $skin_options) {
+    // Use drupal_process_attached() to add attachements such as JS and CSS.
+    if (!empty($applied_skins)) {
+      foreach ($applied_skins as $skin_name => $skin_options) {
 
-          // Special case for _additional.
-          if ($skin_name == '_additional') {
-            continue;
-          }
+        // Special case for _additional.
+        if ($skin_name == '_additional') {
+          continue;
+        }
 
-          // Make sure this skin is enabled for the current theme.
-          if (isset($skin_info[$skin_name]['attached'])) {
-            $elements['#attached'] = $skin_info[$skin_name]['attached'];
-            drupal_process_attached($elements);
-          }
+        // Make sure this skin is enabled for the current theme.
+        if (isset($skin_info[$skin_name]['attached'])) {
+          $elements['#attached'] = $skin_info[$skin_name]['attached'];
+          drupal_process_attached($elements);
+        }
 
-          if (!is_array($skin_options)) {
-            $skin_options = array($skin_options);
-          }
-          foreach ($skin_options as $skin_option) {
-            if (isset($skin_info[$skin_name]['options'][$skin_option]['attached'])) {
-              $elements['#attached'] = $skin_info[$skin_name]['options'][$skin_option]['attached'];
-              drupal_process_attached($elements);
-            }
+        if (!is_array($skin_options)) {
+          $skin_options = array($skin_options);
+        }
+        foreach ($skin_options as $skin_option) {
+          if (isset($skin_info[$skin_name]['options'][$skin_option]['attached'])) {
+            $elements['#attached'] = $skin_info[$skin_name]['options'][$skin_option]['attached'];
+            drupal_process_attached($elements);
           }
         }
-
-        $variables['classes_array'] = array_merge($variables['classes_array'], skinr_flatten_skins_array($applied_skins));
       }
+
+      $variables['classes_array'] = array_merge($variables['classes_array'], skinr_flatten_skins_array($applied_skins));
     }
   }
 }
@@ -444,6 +423,11 @@ function skinr_implements() {
 
   if (!isset($cache)) {
     $cache = array();
+
+    // Load built-in support code.
+    // @todo Eliminate this.
+    module_load_include('inc', 'skinr', 'skinr.handlers');
+
     // Collect hook_skinr_api_VERSION() module implementations. This will also
     // auto-load $module.skinr.inc files, which may contain skin/group hook
     // implementations (when not using the plugin system).
@@ -549,20 +533,6 @@ function skinr_implements() {
 }
 
 /**
- * Includes $extension.skinr.inc files of extensions compatible with this version of Skinr.
- *
- * @todo Shoot me. Twice.
- */
-function skinr_load_includes() {
-  foreach (skinr_implements() as $extension) {
-    $file = DRUPAL_ROOT . '/' . $extension['path'] . '/' . $extension['name'] . '.skinr.inc';
-    if (file_exists($file)) {
-      require_once $file;
-    }
-  }
-}
-
-/**
  * Includes Skinr plugin files for an extension, if any.
  *
  * @param $extension
@@ -1230,107 +1200,26 @@ function skinr_get_config_info() {
 }
 
 /**
- * Prepare default configuration data for modules.
- *
- * @todo Search and destroy.
- */
-function skinr_config_info_default() {
-  return array(
-    'access_handler' => 'skinr_access_handler',
-    'index_handler' => 'skinr_index_handler',
-    'data_handler' => 'skinr_data_handler',
-    'submit_handler' => 'skinr_submit_handler',
-    'submit_handler_attach_to' => array('#submit'),
-    'skinr_title' => t('Skinr'),
-    'skinr_weight' => 1,
-    'title' => '',
-    'description' => t('Manage which skins you want to apply to the hooks'),
-    'collapsed' => TRUE,
-    'weight' => 0,
-  );
-}
-
-/**
- * Execute a module's data handler.
+ * Provide a list of all available theme hooks for a given element.
  *
- * @param $type
- *   The type of handler to execute. Possible values:
- *   - 'access_handler':
- *   - 'contextual_links':
- *   - 'data_handler':
- *   - 'form_index_handler':
- *   - 'preprocess_index_handler':
- *   - 'preprocess_hook_callback':
- *   - 'submit_handler':
- * @param $op
- *   For 'access_handler' the possible values are 'edit skin settings'
- *     and 'edit advanced skin settings'.
- *   For 'contextual_links' an empty string is passed.
- *   For 'data_handler' the possible values are 'form' and 'submit'.
- *   For 'form_index_handler' the possible values are 'form' and 'submit'.
- *   For 'preprocess_index_handler' the possible values are 'preprocess'.
- *   For 'preprocess_hook_callback' an empty string is passed.
- *   For 'submit_handler' an empty string is passed.
- * @param $handler
- *   The function name for this handler as gotten from skinr_fetch_config().
- * @param $a3
- *   For 'access_handler', passes in the $form parameter as provided to a form
- *     function.
- *   For 'contextual_links', passes in the $variables parameter from
- *     skinr_preprocess().
- *   For 'data_handler', passes in the $form parameter from hook_form_submit().
- *   For 'form_index_handler':
- *   - For $op 'form', passes in the $form parameter from hook_form_alter().
- *   - For $op 'submit', passes in the $form parameter from hook_form_submit().
- *   For 'preprocess_index_handler', passes in the $variables parameter from
- *     module_preprocess().
- *   For 'preprocess_hook_callback', passes in the $form parameter from
- *     hook_form_alter().
- *   For 'submit_handler', passes in the $form parameter from hook_form_alter().
+ * @param $module
+ *   The module implementing given element.
+ * @param $element
+ *   An element.
  *
- * @param $a4
- *   For 'access_handler', passes in the $form_state array as provided to a
- *     form function.
- *   For 'data_handler', passes in the $form_state parameter form
- *     hook_form_submit().
- *   For 'form_index_handler':
- *   - For $op 'form', passes in the $form_state parameter from
- *     hook_form_alter().
- *   - For $op 'submit', passes in the $form_state parameter from
- *     hook_form_submit().
- *   For 'preprocess_hook_callback', passes in the $form_state parameter from
- *     hook_form_alter().
- *   For 'submit_handler', passes in the $form_state parameter from
- *     hook_form_alter().
- * @param $a5
- *   For 'data_handler', passes in the module that is currently being processed.
- *   For 'submit_handler', passes in the module that is currently being
- *     processed.
- * @param $a6
- *   For 'data_handler', passes in the settings from hook_skinr_config() for
- *     the form that's currently being processed.
- *   For 'submit_handler', passes in the settings from hook_skinr_config() for
- *     the form that's currently being processed.
- * @param $a7
+ * @return
+ *   An array of theme hooks.
  */
-function skinr_handler($type, $op, $handler, &$a3, $a4 = NULL, $a5 = NULL, $a6 = NULL, $a7 = NULL) {
-  if (is_callable($handler)) {
-    switch ($type) {
-      case 'contextual_links':
-      case 'preprocess_index_handler':
-        return $handler($a3);
-
-      case 'preprocess_hook_callback':
-        return $handler($a3, $a4);
-
-      case 'data_handler':
-      case 'submit_handler':
-        return $handler($a3, $a4, $a5, $a6, $a7);
-
-      default:
-        return $handler($op, $a3, $a4);
-    }
+function skinr_theme_hooks($module, $element) {
+  $theme_hooks = &drupal_static(__FUNCTION__, array());
+
+  if (!isset($theme_hooks[$module][$element])) {
+    // Invoke hook_skinr_theme_hooks() and hook_skinr_theme_hooks_alter().
+    $theme_hooks[$module][$element] = module_invoke_all('skinr_theme_hooks', $module, $element);
+    drupal_alter('skinr_theme_hooks', $theme_hooks[$module][$element], $module, $element);
   }
+
+  return $theme_hooks[$module][$element];
 }
 
 /**
diff --git skinr.skinr.inc skinr.skinr.inc
index 862a332..454524f 100644
--- skinr.skinr.inc
+++ skinr.skinr.inc
@@ -38,160 +38,48 @@ function skinr_skinr_group_info() {
  * Implementation of hook_skinr_config_info().
  */
 function skinr_skinr_config_info() {
-  $data['rules']['form']['skinr_rule_edit'] = array(
-    'index_handler' => 'rules_skinr_form_index_handler',
-    'preprocess_hook_callback' => 'rules_skinr_preprocess_hook_callback',
-    'title' => t('rule settings'),
-    'skinr_weight' => 0,
-    'collapsed' => FALSE,
-  );
-  $data['rules']['form']['skinr_ui_form'] = array(
-    'preprocess_hook_callback' => 'rules_skinr_preprocess_hook_callback',
-    'title' => t('rule settings'),
-    'collapsed' => FALSE,
-  );
-  $data['rules']['preprocess']['html'] = array(
-    'index_handler' => 'rules_skinr_preprocess_index_handler',
-  );
-  $data['rules']['preprocess']['region'] = array(
-    'index_handler' => 'rules_skinr_preprocess_index_handler',
-  );
-  $data['rules']['contextual_links']['html'] = array(
-    'contextual_links_handler' => 'rules_skinr_contextual_links',
-  );
-  $data['rules']['contextual_links']['region'] = array(
-    'contextual_links_handler' => 'rules_skinr_contextual_links',
-  );
-
-  return $data;
-}
-
-/**
- * Skinr form index handler.
- *
- * @param $op
- *   What kind of action is being performed. Possible values:
- *   - 'form': the form elements for Skinr are being inserted in a form.
- *   - 'submit': the form has been submitted.
- * @param &$form
- *   - For 'form', passes in the $form parameter from hook_form_alter().
- *   - For 'submit', passes in the $form parameter from hook_form_submit().
- * @param $form_state
- *   - For 'form', passes in the $form_state parameter from hook_form_alter().
- *   - For 'submit', passes in the $form_state parameter from hook_form_submit().
- * @return
- *   The index where we can find our values in Skinr's data structure.
- */
-function rules_skinr_form_index_handler($op, &$form, $form_state) {
-  switch ($op) {
-    case 'form':
-      if (!empty($form['rule']['rid']['#value'])) {
-        return $form['rule']['rid']['#value'];
-      }
-      else {
-        return 0;
-      }
-
-    case 'submit':
-      return $form_state['values']['rid'];
-  }
+  return array('rules');
 }
 
 /**
- * Skinr preprocess_hook_callback.
- *
- * @param &$form
- *   Passes in the $form parameter from hook_form_alter().
- * @param $form_state
- *   Passes in the $form_state parameter from hook_form_alter().
- * @return
- *   The preprocess_hook we wish to use.
+ * Implements hook_skinr_theme_hooks().
  */
-function rules_skinr_preprocess_hook_callback(&$form, $form_state) {
-  $preprocess_hooks = array();
+function skinr_skinr_theme_hooks($module, $element) {
+  $theme_hooks = array();
 
-  if (!empty($form['rule'])) {
-    $hooks = explode('__', $form['rule']['rule_type']['#value']);
-  }
-  else {
-    $rule = skinr_rule_load($form['skinr']['element']['#value']);
+  if ($module == 'rules') {
+    $rule = skinr_rule_load($element);
     $hooks = explode('__', $rule->rule_type);
-  }
-  while (count($hooks)) {
-    $preprocess_hooks[] = implode('__', $hooks);
-    array_pop($hooks);
+    while (count($hooks)) {
+      $theme_hooks[] = implode('__', $hooks);
+      array_pop($hooks);
+    }
   }
 
-  return $preprocess_hooks;
+  return $theme_hooks;
 }
 
 /**
- * Skinr preprocess index handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from module_preprocess().
- * @return
- *   The index where we can find our values in Skinr's data structure. If an
- *   array is returned, it will loop through each index in Skinr's data
- *   structure and merge the returned classes.
+ * Implements hook_skinr_elements().
  */
-function rules_skinr_preprocess_index_handler(&$variables) {
-  if (!empty($variables['region'])) {
-    $rule_type = 'region__' . $variables['region'];
-  }
-  else {
-    $rule_type = 'page';
-  }
-  $rules = skinr_rule_load_multiple(array(), array('rule_type' => $rule_type));
+function skinr_skinr_elements(&$variables, $hook) {
+  $elements = array();
+  if ($hook == 'html' || $hook == 'region') {
+    $elements['rules'] = array();
 
-  // Find any page level skinr options and return an array of them.
-  $indices = array();
-  foreach ($rules as $rule) {
-    if (skinr_rule_is_visible($rule->rid)) {
-      $indices[] = $rule->rid;
-    }
-  }
-  return $indices;
-}
-
-/**
- * Skinr contextual links handler.
- *
- * @param &$variables
- *   Passes in the $variables parameter from skinr_preprocess().
- * @return
- *   An associative array. Each value is an array that forms the function
- *   arguments for menu_contextual_links(). For example:
- *   @code
- *    $links = array(
- *      'skinr-modulename' => array(
- *        'admin/appearance/skinr/edit', array('system', 'navigation')),
- *      ),
- *      'skinr-modulename-1' => array(
- *        'admin/appearance/skinr/edit', array('system', 'something-else')),
- *      ),
- *    );
- *   @endcode
- */
-function rules_skinr_contextual_links(&$variables) {
-  if (!empty($variables['region'])) {
-    $rule_type = 'region__' . $variables['region'];
-  }
-  else {
     $rule_type = 'page';
-  }
-  $rules = skinr_rule_load_multiple(array(), array('rule_type' => $rule_type));
-  $links = array();
-  $counter = 1;
+    if ($hook == 'region') {
+      $rule_type = 'region__' . $variables['region'];
+    }
 
-  foreach ($rules as $rule) {
-    if (skinr_rule_is_visible($rule->rid)) {
-      $links['skinr-rule-' . $counter++] = array(
-        'admin/config/skinr/edit/nojs', array('rule', $rule->rid),
-      );
+    $rules = skinr_rule_load_multiple(array(), array('rule_type' => $rule_type));
+    foreach ($rules as $rule) {
+      if (skinr_rule_is_visible($rule->rid)) {
+        $elements['rules'][] = $rule->rid;
+      }
     }
   }
-  return $links;
+  return $elements;
 }
 
 /**
diff --git skinr_ui.admin.inc skinr_ui.admin.inc
index 06d02c0..3b9595d 100644
--- skinr_ui.admin.inc
+++ skinr_ui.admin.inc
@@ -84,7 +84,7 @@ function skinr_ui_filters() {
   $config = skinr_get_config_info();
 
   $options = array('[any]' => t('any'));
-  foreach ($config as $type => $data) {
+  foreach ($config as $type) {
     $options[$type] = $type;
   }
 
diff --git skinr_ui.api.php skinr_ui.api.php
new file mode 100644
index 0000000..cbc400a
--- /dev/null
+++ skinr_ui.api.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * This file contains no working PHP code; it exists to provide additional documentation
+ * for doxygen as well as to document hooks in the standard Drupal manner.
+ */
+
+/**
+ * @mainpage Skinr UI API Manual
+ *
+ * Topics:
+ * - @ref skinr_ui_hooks
+ */
+
+/**
+ * @defgroup skinr_ui_hooks Skinr UIs hooks
+ * @{
+ * Hooks that can be implemented by other modules in order to implement the
+ * Skinr UI API.
+ */
+
+/**
+ * @}
+ */
diff --git skinr_ui.module skinr_ui.module
index bbc9e61..396570c 100644
--- skinr_ui.module
+++ skinr_ui.module
@@ -24,6 +24,23 @@ function skinr_ui_permission() {
 }
 
 /**
+ * Determine whether the user has a given privilege.
+ *
+ * @param $string
+ *   The permission, such as "administer nodes", being checked for.
+ * @param $account
+ *   (optional) The account to check, if not given use currently logged in user.
+ *
+ * @return
+ *   Boolean TRUE if the current user has the requested permission.
+ *
+ * @see user_access()
+ */
+function skinr_ui_access($string, $account = NULL) {
+  return user_access($string, $account) || user_access('administer skinr', $account);
+}
+
+/**
  * Implements hook_menu().
  */
 function skinr_ui_menu() {
@@ -89,18 +106,18 @@ function skinr_ui_menu() {
     'access arguments' => array('administer skinr'),
     'file' => 'skinr_ui.rules.inc',
   );
-  $items['admin/appearance/skinr/rules/edit'] = array(
+  $items['admin/appearance/skinr/rules/edit/%skinr_rule'] = array(
     'title' => 'Edit rule',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('skinr_rule_edit'),
+    'page arguments' => array('skinr_rule_edit', 5),
     'type' => MENU_CALLBACK,
     'access arguments' => array('administer skinr'),
     'file' => 'skinr_ui.rules.inc',
   );
-  $items['admin/appearance/skinr/rules/delete'] = array(
+  $items['admin/appearance/skinr/rules/delete/%skinr_rule'] = array(
     'title' => 'Delete rule',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('skinr_rule_delete_confirm'),
+    'page arguments' => array('skinr_rule_delete_confirm', 5),
     'type' => MENU_CALLBACK,
     'access arguments' => array('administer skinr'),
     'file' => 'skinr_ui.rules.inc',
@@ -258,7 +275,14 @@ function skinr_ui_help($path, $arg) {
       return  '<p>' . t('To import skin configurations, paste exported code and click the "Import" button.') . '</p>';
     case 'admin/appearance/skinr/export':
       return  '<p>' . t('To export skin configurations, ensure the correct theme is selected and click the "Export" button.') . '</p>';
-      break;
+    case 'admin/appearance/skinr/edit/%/%/%':
+      // @todo Make this help text more relevant.
+      $theme_hooks = skinr_theme_hooks($arg[5], $arg[6]);
+      return  '<p>' . t('Manage which skins you want to apply to the hooks <strong>!hooks</strong>.', array('!hooks' => implode(', ', $theme_hooks))) . '</p>';
+    case 'admin/appearance/skinr/rules/edit/%':
+      // @todo Make this help text more relevant.
+      $theme_hooks = skinr_theme_hooks('rules', $arg[5]);
+      return  '<p>' . t('Manage which skins you want to apply to the hooks <strong>!hooks</strong>.', array('!hooks' => implode(', ', $theme_hooks))) . '</p>';
   }
 }
 
@@ -289,11 +313,10 @@ function skinr_ui_edit_contextual_title($module, $element) {
     foreach ($links as $link) {
       if ($link[1][0] == $module && $link[1][1] == $element) {
         if (count($links) > 1) {
-          return t('Edit skin !number', array('!number' => $counter));
+          return t('Edit skin !number', array('!number' => $counter++));
         }
         break 2;
       }
-      $counter++;
     }
   }
   return t('Edit skin');
@@ -355,7 +378,8 @@ function skinr_ui_form($form, &$form_state, $arguments) {
     );
   }
 
-  $form['submit'] = array(
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save'),
     '#weight' => 50,
@@ -373,7 +397,20 @@ function skinr_ui_form_alter(&$form, $form_state, $form_id) {
     return;
   }
 
-  $config = skinr_get_config_info();
+  // Ensure module and element values are set.
+  if (empty($form['skinr']['module']['#value']) || empty($form['skinr']['element']['#value'])) {
+    return;
+  }
+
+  // Check for access.
+  if (!skinr_ui_access('edit skin settings')) {
+    // Deny access.
+    return;
+  }
+
+  $module = $form['skinr']['module']['#value'];
+  $element = $form['skinr']['element']['#value'];
+
   $groups = skinr_get_group_info();
   $skin_infos = skinr_get_skin_info();
 
@@ -382,230 +419,193 @@ function skinr_ui_form_alter(&$form, $form_state, $form_id) {
     $skin_infos[$skin_name]['status'] = skinr_skin_info_status_get($skin_infos[$skin_name]);
   }
 
-  foreach ($config as $module => $module_settings) {
-    if (isset($module_settings['form'][$form_id])) {
-      if (!empty($form['skinr']) && $form['skinr']['module']['#value'] !== $module) {
-        continue;
-      }
-
-      $form_settings = array_merge(skinr_config_info_default(), $module_settings['form'][$form_id]);
-
-      // Check for access.
-      if (!skinr_handler('access_handler', 'edit skin settings', $form_settings['access_handler'], $form, $form_state)) {
-        // Deny access.
-        break;
-      }
-
-      // Ensure we have the required preprocess_hook or preprocess_hook_callback.
-      if (empty($form_settings['preprocess_hook']) && empty($form_settings['preprocess_hook_callback'])) {
-        trigger_error(sprintf("No preprocess_hook or preprocess_hook_callback was found for form_id '%s' in module '%s'.", $form_id, $module), E_USER_ERROR);
-      }
+  // Invoke hook_skinr_theme_hooks() and hook_skinr_theme_hooks_alter().
+  $theme_hooks = skinr_theme_hooks($module, $element);
 
-      $themes = list_themes();
-      ksort($themes);
+  $form['skinr_settings'] = array(
+    '#tree' => TRUE,
+    // Set weight to accommodate Rules UI.
+    '#weight' => 0,
+  );
 
-      foreach ($themes as $theme) {
-        if (!$theme->status) {
-          continue;
-        }
 
-        if (!isset($form['skinr_settings'])) {
-          $form['skinr_settings'] = array(
-            '#tree' => TRUE,
-            '#weight' => $form_settings['skinr_weight'],
-          );
-        }
-
-        $preprocess_hooks = isset($form_settings['preprocess_hook']) ? $form_settings['preprocess_hook'] : skinr_handler('preprocess_hook_callback', '', $form_settings['preprocess_hook_callback'], $form, $form_state);
-        if (!is_array($preprocess_hooks)) {
-          $preprocess_hooks = array($preprocess_hooks);
-        }
+  $themes = list_themes();
+  ksort($themes);
+  // Get current theme, but make sure it's not the admin theme when we're editing with AJAX.
+  $current_theme = skinr_current_theme(TRUE);
 
-        // If this hook is a region, and the region does not exist for this
-        // theme, don't bother outputting any of the settings.
-        if (strpos($preprocess_hooks[0], 'region') === 0) {
-          // Strip the region__ part off the region name.
-          $region = substr($preprocess_hooks[0], 8);
+  foreach ($themes as $theme) {
+    if (!$theme->status) {
+      continue;
+    }
 
-          $regions = system_region_list($theme->name, REGIONS_VISIBLE);
-          if (!isset($regions[$region])) {
-            continue;
-          }
-        }
+    // If this hook is a region, and the region does not exist for this
+    // theme, don't bother outputting any of the settings.
+    if (strpos($theme_hooks[0], 'region') === 0) {
+      // Strip the region__ part off the region name.
+      $region = substr($theme_hooks[0], 8);
 
-        if (!$form_state['submitted']) {
-          if ($skins = skinr_handler('data_handler', 'form', $form_settings['data_handler'], $form, $form_state, $theme->name, $module, $form_settings)) {
-            $defaults = array();
-            foreach ($skins as $skin) {
-              $defaults[$skin->skin] = $skin->options;
-            }
-          }
-          else {
-            $defaults = array();
-          }
-        }
-        else {
-          // Handle preview before submit.
-          // @todo Is this still needed? If so, it needs to be fixed.
-          $defaults = $form_state['values'];
-        }
+      $regions = system_region_list($theme->name, REGIONS_VISIBLE);
+      if (!isset($regions[$region])) {
+        continue;
+      }
+    }
 
-        if (!isset($form['skinr_settings'][$module . '_group'])) {
-          $form['skinr_settings'][$module . '_group'] = array(
-            '#type' => 'fieldset',
-            '#title' => t('@skinr_title @title', array('@skinr_title' => $form_settings['skinr_title'], '@title' => $form_settings['title'])),
-            '#description' => t($form_settings['description']) . ' <strong>' . implode(', ', $preprocess_hooks) . '</strong>.',
-            '#weight' => $form_settings['weight'],
-            '#collapsible' => TRUE,
-            '#collapsed' => $form_settings['collapsed'],
-          );
+    if (!$form_state['submitted']) {
+      $params = array(
+        'theme' => $theme->name,
+        'module' => $module,
+        'element' => $element,
+      );
+      if ($skins = skinr_skin_load_multiple(skinr_skin_get_sids($params))) {
+        $defaults = array();
+        foreach ($skins as $skin) {
+          $defaults[$skin->skin] = $skin->options;
         }
+      }
+      else {
+        $defaults = array();
+      }
+    }
+    else {
+      // Handle preview before submit.
+      // @todo Is this still needed? If so, it needs to be fixed.
+      $defaults = $form_state['values'];
+    }
 
-        // Get current theme, but make sure it's not the admin theme when we're editing with AJAX.
-        $current_theme = skinr_current_theme(TRUE);
-
-        $form['skinr_settings'][$module . '_group'][$theme->name] = array(
-          '#type' => 'fieldset',
-          '#title' => $theme->info['name'] . ($theme->name == $current_theme ? ' (' . t('enabled + default') . ')' : ''),
-          '#collapsible' => TRUE,
-          '#collapsed' => $theme->name == $current_theme ? FALSE : TRUE,
+    if (!isset($form['skinr_settings'][$module . '_group'])) {
+      $form['skinr_settings'][$module . '_group'] = array(
+        '#type' => 'container',
+      );
+      if ($module == 'rules') {
+        $form['skinr_settings']['skinr_settings_title'] = array(
+          '#type' => 'item',
+          '#title' => t('Skinr settings'),
+          '#weight' => -1,
         );
-        if ($theme->name == $current_theme) {
-          $form['skinr_settings'][$module . '_group'][$theme->name]['#attributes'] = array('class' => array('skinr-ui-current-theme'));
-          $form['skinr_settings'][$module . '_group'][$theme->name]['#weight'] = -10;
-        }
-
-        // Create individual widgets for each skin.
-        foreach ($skin_infos as $skin_name => $skin_info) {
-          // Check if this skin is disabled.
-          if (empty($skin_info['status'][$theme->name])) {
-            continue;
-          }
+      }
+    }
 
-          // Check if this skin applies to this hook.
-          if (!is_array($skin_info['theme hooks']) || (!in_array('*', $skin_info['theme hooks']) && !_skinr_is_featured($preprocess_hooks, $skin_info['theme hooks']))) {
-            continue;
-          }
+    $form['skinr_settings'][$module . '_group'][$theme->name] = array(
+      '#type' => 'fieldset',
+      '#title' => $theme->info['name'] . ($theme->name == $current_theme ? ' (' . t('enabled + default') . ')' : ''),
+      '#collapsible' => TRUE,
+      '#collapsed' => $theme->name == $current_theme ? FALSE : TRUE,
+    );
+    if ($theme->name == $current_theme) {
+      // Current theme goes at the top.
+      $form['skinr_settings'][$module . '_group'][$theme->name]['#attributes'] = array('class' => array('skinr-ui-current-theme'));
+      $form['skinr_settings'][$module . '_group'][$theme->name]['#weight'] = -10;
+    }
 
-          // Create widget.
-          if (!empty($skin_info['form callback']) && function_exists($skin_info['form callback'])) {
-            // @todo Allow for custom form callbacks.
-            $field = array();
-          }
-          else {
-            switch ($skin_info['type']) {
-              case 'checkboxes':
-                $field = array(
-                  '#type' => 'checkboxes',
-                  '#multiple' => TRUE,
-                  '#title' => t($skin_info['title']),
-                  '#options' => skinr_ui_info_options_to_form_options($skin_info['options']),
-                  '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : array(),
-                  '#description' => t($skin_info['description']),
-                  '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
-                );
-                break;
-              case 'radios':
-                $field = array(
-                  '#type' => 'radios',
-                  '#title' => t($skin_info['title']),
-                  '#options' => array_merge(array('' => '&lt;none&gt;'), skinr_ui_info_options_to_form_options($skin_info['options'])),
-                  '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
-                  '#description' => t($skin_info['description']),
-                  '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
-                );
-                break;
-              case 'select':
-                $field = array(
-                  '#type' => 'select',
-                  '#title' => t($skin_info['title']),
-                  '#options' => array_merge(array('' => '<none>'), skinr_ui_info_options_to_form_options($skin_info['options'])),
-                  '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
-                  '#description' => t($skin_info['description']),
-                  '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
-                );
-                break;
-            }
-          }
-          if (empty($skin_info['group']) || empty($groups[$skin_info['group']])) {
-            $form['skinr_settings'][$module . '_group'][$theme->name][$skin_name] = $field;
-          }
-          else {
-            if (!isset($form['skinr_settings'][$module . '_group'][$theme->name][$skin_info['group']])) {
-              $group = $groups[$skin_info['group']];
-              $form['skinr_settings'][$module . '_group'][$theme->name][$skin_info['group']] = array(
-                '#type' => 'fieldset',
-                '#title' => t($group['title']),
-                '#description' => t($group['description']),
-                '#weight' => isset($group['weight']) ? $group['weight'] : NULL,
-              );
-            }
-            $form['skinr_settings'][$module . '_group'][$theme->name][$skin_info['group']][$skin_name] = $field;
-          }
-        }
+    // Create individual widgets for each skin.
+    foreach ($skin_infos as $skin_name => $skin_info) {
+      // Check if this skin is disabled.
+      if (empty($skin_info['status'][$theme->name])) {
+        continue;
+      }
 
-        // Check for access.
-        if (skinr_handler('access_handler', 'edit advanced skin settings', $form_settings['access_handler'], $form, $form_state)) {
-          $skin_name = '_additional';
-          $form['skinr_settings'][$module . '_group'][$theme->name]['_additional'] = array(
-            '#type' => 'textfield',
-            '#title' => t('CSS classes'),
-            '#size' => 40,
-            '#description' => t('To add CSS classes manually, enter classes separated by a single space i.e. <code>first-class second-class</code>'),
-            '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
-          );
+      // Check if this skin applies to this hook.
+      if (!is_array($skin_info['theme hooks']) || (!in_array('*', $skin_info['theme hooks']) && !_skinr_is_featured($theme_hooks, $skin_info['theme hooks']))) {
+        continue;
+      }
 
-          // Only add validation handler once.
-          if (!isset($form['#validate']) || !in_array('skinr_ui_form_validate', $form['#validate'])) {
-            $form['#validate'][] = 'skinr_ui_form_validate';
-          }
-          // Special for views.
-          if (isset($form['buttons']['submit']['#validate']) && !in_array('skinr_ui_form_validate', $form['buttons']['submit']['#validate'])) {
-            $form['buttons']['submit']['#validate'][] = 'skinr_ui_form_validate';
-          }
+      // Create widget.
+      if (!empty($skin_info['form callback']) && function_exists($skin_info['form callback'])) {
+        // @todo Allow for custom form callbacks.
+        $field = array();
+      }
+      else {
+        switch ($skin_info['type']) {
+          case 'checkboxes':
+            $field = array(
+              '#type' => 'checkboxes',
+              '#multiple' => TRUE,
+              '#title' => t($skin_info['title']),
+              '#options' => skinr_ui_info_options_to_form_options($skin_info['options']),
+              '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : array(),
+              '#description' => t($skin_info['description']),
+              '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
+            );
+            break;
+          case 'radios':
+            $field = array(
+              '#type' => 'radios',
+              '#title' => t($skin_info['title']),
+              '#options' => array_merge(array('' => '&lt;none&gt;'), skinr_ui_info_options_to_form_options($skin_info['options'])),
+              '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
+              '#description' => t($skin_info['description']),
+              '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
+            );
+            break;
+          case 'select':
+            $field = array(
+              '#type' => 'select',
+              '#title' => t($skin_info['title']),
+              '#options' => array_merge(array('' => '<none>'), skinr_ui_info_options_to_form_options($skin_info['options'])),
+              '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
+              '#description' => t($skin_info['description']),
+              '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
+            );
+            break;
         }
       }
-
-      // Add weight to additional settings and submit form elements.
-      $form['additional_settings']['#weight'] = 39;
-      $form['submit']['#weight'] = 40;
-
-      // Only add submit handler once.
-      // Find the property to check.
-      $attr = &$form;
-      foreach($form_settings['submit_handler_attach_to'] as $part) {
-        $attr = &$attr[$part];
+      if (empty($skin_info['group']) || empty($groups[$skin_info['group']])) {
+        $form['skinr_settings'][$module . '_group'][$theme->name][$skin_name] = $field;
       }
-      if (!empty($attr) && !in_array('skinr_ui_form_submit', $attr)) {
-        $string = $attr[] = 'skinr_ui_form_submit';
+      else {
+        if (!isset($form['skinr_settings'][$module . '_group'][$theme->name][$skin_info['group']])) {
+          $group = $groups[$skin_info['group']];
+          $form['skinr_settings'][$module . '_group'][$theme->name][$skin_info['group']] = array(
+            '#type' => 'fieldset',
+            '#title' => t($group['title']),
+            '#description' => t($group['description']),
+            '#weight' => isset($group['weight']) ? $group['weight'] : NULL,
+          );
+        }
+        $form['skinr_settings'][$module . '_group'][$theme->name][$skin_info['group']][$skin_name] = $field;
       }
+    }
 
-      // Keep looping, there might be other modules that implement the same form_id.
+    // Check for access.
+    if (skinr_ui_access('edit advanced skin settings')) {
+      $skin_name = '_additional';
+      $form['skinr_settings'][$module . '_group'][$theme->name]['_additional'] = array(
+        '#type' => 'textfield',
+        '#title' => t('CSS classes'),
+        '#size' => 40,
+        '#description' => t('To add CSS classes manually, enter classes separated by a single space i.e. <code>first-class second-class</code>'),
+        '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '',
+      );
     }
   }
+
+  // Only add validation handler once.
+  if (!isset($form['#validate']) || !in_array('skinr_ui_form_validate', $form['#validate'])) {
+    $form['#validate'][] = 'skinr_ui_form_validate';
+  }
+
+  // Only add submit handler once.
+  if (!isset($form['#submit']) || !in_array('skinr_ui_form_submit', $form['#submit'])) {
+    $form['#submit'][] = 'skinr_ui_form_submit';
+  }
 }
 
 /**
  * Form validation handler for skinr_ui_form_alter().
  */
 function skinr_ui_form_validate($form, &$form_state) {
-  $form_id = $form_state['values']['form_id'];
-  $config = skinr_get_config_info();
+  $module = $form_state['values']['module'];
+  $element = $form_state['values']['element'];
 
   $error = FALSE;
-  foreach ($config as $module => $module_settings) {
-    if (isset($module_settings['form'][$form_id]) && isset($form_state['values']['skinr_settings'][$module . '_group'])) {
-      foreach ($form_state['values']['skinr_settings'][$module . '_group'] as $theme_name => $theme) {
-        if (isset($theme['_additional'])) {
-          $form_settings = array_merge(skinr_config_info_default(), $module_settings['form'][$form_id]);
-
-          // Validate additional classes field.
-          if (preg_match('/[^a-zA-Z0-9\-\_\s]/', $theme['_additional'])) {
-            form_set_error('skinr_settings][' . $module . '_group][' . $theme_name . '][_additional', t('Additional classes for Skinr may only contain alphanumeric characters, spaces, - and _.'));
-            $error = TRUE;
-          }
-
-          // Keep looping, there might be other modules that implement the same form_id.
+  if (isset($form_state['values']['skinr_settings'][$module . '_group'])) {
+    foreach ($form_state['values']['skinr_settings'][$module . '_group'] as $theme_name => $theme) {
+      if (isset($theme['_additional'])) {
+        // Validate additional classes field.
+        if (preg_match('/[^a-zA-Z0-9\-\_\s]/', $theme['_additional'])) {
+          form_set_error('skinr_settings][' . $module . '_group][' . $theme_name . '][_additional', t('Additional classes for Skinr may only contain alphanumeric characters, spaces, - and _.'));
+          $error = TRUE;
         }
       }
     }
@@ -614,15 +614,13 @@ function skinr_ui_form_validate($form, &$form_state) {
   // Undo any grouping to ease processing on submit.
   if (!$error) {
     $groups = skinr_get_group_info();
-    foreach ($config as $module => $module_settings) {
-      if (isset($module_settings['form'][$form_id]) && isset($form_state['values']['skinr_settings'][$module . '_group'])) {
-        foreach ($form_state['values']['skinr_settings'][$module . '_group'] as $theme_name => $theme) {
-          foreach ($groups as $group_name => $group) {
-            if (!empty($theme[$group_name]) && is_array($theme[$group_name])) {
-              $group_values = $theme[$group_name];
-              unset($form_state['values']['skinr_settings'][$module . '_group'][$theme_name][$group_name]);
-              $form_state['values']['skinr_settings'][$module . '_group'][$theme_name] = array_merge($form_state['values']['skinr_settings'][$module . '_group'][$theme_name], $group_values);
-            }
+    if (isset($form_state['values']['skinr_settings'][$module . '_group'])) {
+      foreach ($form_state['values']['skinr_settings'][$module . '_group'] as $theme_name => $theme) {
+        foreach ($groups as $group_name => $group) {
+          if (!empty($theme[$group_name]) && is_array($theme[$group_name])) {
+            $group_values = $theme[$group_name];
+            unset($form_state['values']['skinr_settings'][$module . '_group'][$theme_name][$group_name]);
+            $form_state['values']['skinr_settings'][$module . '_group'][$theme_name] = array_merge($form_state['values']['skinr_settings'][$module . '_group'][$theme_name], $group_values);
           }
         }
       }
@@ -634,16 +632,68 @@ function skinr_ui_form_validate($form, &$form_state) {
  * Form submission handler for skinr_ui_form_alter().
  */
 function skinr_ui_form_submit($form, &$form_state) {
-  $form_id = $form_state['values']['form_id'];
-  $config = skinr_get_config_info();
   $current_theme = skinr_current_theme(TRUE);
 
-  foreach ($config as $module => $module_settings) {
-    if (isset($module_settings['form'][$form_id])) {
-      $form_settings = array_merge(skinr_config_info_default(), $module_settings['form'][$form_id]);
-      skinr_handler('submit_handler', '', $form_settings['submit_handler'], $form, $form_state, $module, $form_settings);
+  $module = $form_state['values']['module'];
+  $element = $form_state['values']['element'];
+
+  if (isset($form_state['values']['skinr_settings'][$module . '_group'])) {
+    foreach ($form_state['values']['skinr_settings'][$module . '_group'] as $theme_name => $theme) {
+      // Process widgets.
+      if (!empty($theme) && is_array($theme)) {
+        foreach ($theme as $skin_name => $options) {
+          if ($skin_name == '_additional' && !user_access('edit advanced skin settings')) {
+            // This user doesn't have access to alter these options.
+            continue;
+          }
+
+          // Ensure options is an array.
+          if (!is_array($options)) {
+            $options = $skin_name == '_additional' ? explode(' ', $options) : array($options);
+          }
+          // Sanitize options.
+          $options = _skinr_array_strip_empty($options);
+
+          // Find existing skin.
+          $params = array(
+            'theme' => $theme_name,
+            'module' => $module,
+            'element' => $element,
+            'skin' => $skin_name,
+          );
+          $sids = skinr_skin_get_sids($params);
+
+          unset($skin);
+          if (!empty($sids)) {
+            $sid = reset($sids);
+            $skin = skinr_skin_load($sid);
+          }
 
-      // Keep looping, there might be other modules that implement the same form_id.
+          if (empty($options)) {
+            if (!empty($skin)) {
+              // Delete this skin configuration.
+              skinr_skin_delete($skin->sid);
+            }
+            continue;
+          }
+
+          if (empty($skin)) {
+            // It doesn't exist, so create a new skin.
+            $skin = new stdClass();
+            $skin->theme = $theme_name;
+            $skin->module = $module;
+            $skin->element = $element;
+            $skin->skin = $skin_name;
+          }
+          $skin->options = $options;
+          $skin->status = 1;
+
+          // Save skin.
+          if (!skinr_skin_save($skin)) {
+            drupal_set_message(t("Skinr settings for %skin weren't saved due to an error.", array('%skin' => $skin_name)), 'error');
+          }
+        }
+      }
     }
   }
 }
@@ -652,22 +702,26 @@ function skinr_ui_form_submit($form, &$form_state) {
  * Implements hook_preprocess().
  */
 function skinr_ui_preprocess(&$variables, $hook) {
-  $config = skinr_get_config_info();
-
   $original_hook = $hook;
   $theme_registry = theme_get_registry();
   if (isset($theme_registry[$hook]['original hook'])) {
     $original_hook = $theme_registry[$hook]['original hook'];
   }
 
-  foreach ($config as $module => $module_settings) {
-    if (!empty($module_settings['contextual_links'][$original_hook])) {
-      // Set contextual links.
-      if ($contextual_links = skinr_handler('contextual_links', '', $module_settings['contextual_links'][$original_hook]['contextual_links_handler'], $variables)) {
-        skinr_ui_contextual_links($variables, $original_hook, $contextual_links);
-      }
+  $array_elements = module_invoke_all('skinr_elements', $variables, $original_hook, 'contextual_links');
+  $contextual_links = array();
+  $counter = 0;
+  foreach ($array_elements as $module => $elements) {
+    foreach ($elements as $element) {
+      $contextual_links['skinr-' .  $module . '-' . $counter++] = array(
+        'admin/appearance/skinr/edit/nojs', array($module, $element),
+      );
     }
   }
+  if (!empty($contextual_links)) {
+    skinr_ui_contextual_links($variables, $original_hook, $contextual_links);
+  }
+  return;
 }
 
 /**
@@ -737,7 +791,7 @@ function _skinr_ui_set_contextual_links($hook = NULL, $links = NULL) {
  * Helper function to determine whether one of a set of hooks exists in a list
  * of required theme hooks.
  *
- * @param $preprocess_hooks
+ * @param $theme_hooks
  *   An array of theme hooks available to this element.
  * @param $theme_hooks
  *   An array of allowed theme hooks.
@@ -747,9 +801,9 @@ function _skinr_ui_set_contextual_links($hook = NULL, $links = NULL) {
  *
  * @todo Rename function to be more descriptive.
  */
-function _skinr_is_featured($preprocess_hooks, $theme_hooks) {
-  foreach ($preprocess_hooks as $preprocess_hook) {
-    if (in_array($preprocess_hook, $theme_hooks)) {
+function _skinr_is_featured($theme_hooks, $theme_hooks) {
+  foreach ($theme_hooks as $theme_hook) {
+    if (in_array($theme_hook, $theme_hooks)) {
       return TRUE;
     }
   }
diff --git skinr_ui.rules.inc skinr_ui.rules.inc
index 2a217e7..911199b 100644
--- skinr_ui.rules.inc
+++ skinr_ui.rules.inc
@@ -127,55 +127,41 @@ function skinr_rule_add_submit($form, &$form_state) {
  * @see skinr_rule_edit_submit()
  * @ingroup forms
  */
-function skinr_rule_edit($form, &$form_state, $rid = NULL) {
+function skinr_rule_edit($form, &$form_state, $rule) {
+  $form['skinr']['module'] = array(
+    '#type' => 'hidden',
+    '#value' => 'rules',
+  );
+  $form['skinr']['element'] = array(
+    '#type' => 'hidden',
+    '#value' => $rule->rid,
+  );
+
   $form['rule'] = array(
     '#weight' => -1,
   );
 
-  if (isset($form_state['values'])) {
-    $rule = array(
-      'title' => $form_state['values']['title'],
-      'rule_type' => $form_state['values']['rule_type'],
-      'node_types' => $form_state['values']['types'],
-      'roles' => $form_state['values']['roles'],
-      'visibility' => $form_state['values']['visibility'],
-      'pages' => $form_state['values']['pages'],
-    );
-  }
-  elseif (isset($rid) && $rule = skinr_rule_load($rid)) {
-    $rule = (array) $rule;
-    $form['rule']['rid'] = array(
-      '#type' => 'value',
-      '#value' => $rid,
-    );
-  }
-  else {
-    $rule = array(
-      'title' => '',
-      'rule_type' => '',
-      'node_types' => array(),
-      'roles' => array(),
-      'visibility' => 0,
-      'pages' => '',
-    );
-  }
+  $form['rule']['rid'] = array(
+    '#type' => 'value',
+    '#value' => $rule->rid,
+  );
 
   $form['rule']['title'] = array(
     '#type' => 'textfield',
     '#title' => t('Rule title'),
-    '#default_value' => $rule['title'],
+    '#default_value' => $rule->title,
     '#description' => t('Descriptive title for this rule; used by administrators.'),
     '#required' => TRUE,
   );
 
   $form['rule']['rule_type'] = array(
     '#type' => 'hidden',
-    '#value' => $rule['rule_type'],
+    '#value' => $rule->rule_type,
   );
   $form['rule']['rule_type_displayed'] = array(
     '#type' => 'item',
     '#title' => t('Rule type'),
-    '#markup' => $rule['rule_type'],
+    '#markup' => $rule->rule_type,
     '#description' => t('Type of element the rule is applied to.'),
   );
 
@@ -202,14 +188,14 @@ function skinr_rule_edit($form, &$form_state, $rid = NULL) {
   );
 
   $access = user_access('use PHP for settings');
-  if (isset($rule['visibility']) && $rule['visibility'] == SKINR_RULE_VISIBILITY_PHP && !$access) {
+  if ($rule->visibility == SKINR_RULE_VISIBILITY_PHP && !$access) {
     $form['visibility']['path']['visibility'] = array(
       '#type' => 'value',
       '#value' => SKINR_RULE_VISIBILITY_PHP,
     );
     $form['visibility']['path']['pages'] = array(
       '#type' => 'value',
-      '#value' => isset($rule['pages']) ? $rule['pages'] : '',
+      '#value' => $rule->pages,
     );
   }
   else {
@@ -231,12 +217,12 @@ function skinr_rule_edit($form, &$form_state, $rid = NULL) {
       '#type' => 'radios',
       '#title' => t('Show block on specific pages'),
       '#options' => $options,
-      '#default_value' => isset($rule['visibility']) ? $rule['visibility'] : SKINR_RULE_VISIBILITY_NOTLISTED,
+      '#default_value' => $rule->visibility,
     );
     $form['visibility']['path']['pages'] = array(
       '#type' => 'textarea',
       '#title' => '<span class="element-invisible">' . $title . '</span>',
-      '#default_value' => isset($rule['pages']) ? $rule['pages'] : '',
+      '#default_value' => $rule->pages,
       '#description' => $description,
     );
   }
@@ -253,7 +239,7 @@ function skinr_rule_edit($form, &$form_state, $rid = NULL) {
   $form['visibility']['node_type']['types'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Show block for specific content types'),
-    '#default_value' => $rule['node_types'],
+    '#default_value' => $rule->node_types,
     '#options' => node_type_get_names(),
     '#description' => t('Show this block only on pages that display content of the given type(s). If you select no types, there will be no type-specific limitation.'),
   );
@@ -271,7 +257,7 @@ function skinr_rule_edit($form, &$form_state, $rid = NULL) {
   $form['visibility']['role']['roles'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Show block for specific roles'),
-    '#default_value' => $rule['roles'],
+    '#default_value' => $rule->roles,
     '#options' => $role_options,
     '#description' => t('Show this rule only for the selected role(s). If you select no roles, the rule will be visible to all users.'),
   );
@@ -281,13 +267,11 @@ function skinr_rule_edit($form, &$form_state, $rid = NULL) {
     '#type' => 'submit',
     '#value' => t('Save rule'),
   );
-  if (isset($rule['rid'])) {
-    $form['actions']['delete'] = array(
-      '#type' => 'submit',
-      '#value' => t('Delete'),
-      '#submit' => array('skinr_rule_delete_submit'),
-    );
-  }
+  $form['actions']['delete'] = array(
+    '#type' => 'submit',
+    '#value' => t('Delete'),
+    '#submit' => array('skinr_rule_delete_submit'),
+  );
 
   return $form;
 }
@@ -336,13 +320,12 @@ function skinr_rule_delete_submit($form, &$form_state) {
  *
  * @ingroup forms
  */
-function skinr_rule_delete_confirm($form, &$form_state, $rid) {
+function skinr_rule_delete_confirm($form, &$form_state, $rule) {
   $form['rid'] = array(
     '#type' => 'value',
-    '#value' => $rid,
+    '#value' => $rule->rid,
   );
 
-  $rule = skinr_rule_load($rid);
   return confirm_form($form,
     t('Are you sure you want to delete %title?', array('%title' => $rule->title)),
     isset($_GET['destination']) ? $_GET['destination'] : 'admin/appearance/skinr/rules',
diff --git tests/skinr.test tests/skinr.test
index 2d6ee92..463a37d 100644
--- tests/skinr.test
+++ tests/skinr.test
@@ -293,13 +293,21 @@ class SkinrApiTestCase extends DrupalWebTestCase {
     $config = skinr_get_config_info();
 
     // Skinr's own implementation in skinr.skinr.inc should always be found.
-    $this->assertTrue(isset($config['rules']), 'hook_skinr_config_info() in $module.skinr.inc found.');
-    unset($config['rules']);
+    $this->assertTrue(in_array('rules', $config), 'hook_skinr_config_info() in $module.skinr.inc found.');
+    foreach ($config as $key => $type) {
+      if ($type == 'rules') {
+        unset($config[$key]);
+      }
+    }
 
     // Skinr's implementation on behalf of Node module in modules/node.skinr.inc
     // should be found.
-    $this->assertTrue(isset($config['node']), 'hook_skinr_config_info() in a custom path found.');
-    unset($config['node']);
+    $this->assertTrue(in_array('node', $config), 'hook_skinr_config_info() in a custom path found.');
+    foreach ($config as $key => $type) {
+      if ($type == 'node') {
+        unset($config[$key]);
+      }
+    }
 
     // Ensure that skinr_test_incompatible is not contained.
     $this->assertTrue(!isset($config['skinr_test_incompatible']), 'Incompatible hook_skinr_config_info() not found.');

