diff --git a/vipra-ui/app/html/articles/index.html b/vipra-ui/app/html/articles/index.html
index 5f2bea4bbf5967fda77744308451f1b7ed6e9e0c..5560bfaff51ace0ddeb7c818e2d572d2fa7270fd 100644
--- a/vipra-ui/app/html/articles/index.html
+++ b/vipra-ui/app/html/articles/index.html
@@ -4,7 +4,7 @@
 
 <ul class="list-unstyled">
   <li ng-repeat="article in articles">
-    <a ui-sref="articles.detail.show({id: article.id})" ng-bind="::article.title"></a>
+    <a ui-sref="articles.show({id: article.id})" ng-bind="::article.title"></a>
   </li>
 </ul>
 
diff --git a/vipra-ui/app/html/articles/detail/show.html b/vipra-ui/app/html/articles/show.html
similarity index 92%
rename from vipra-ui/app/html/articles/detail/show.html
rename to vipra-ui/app/html/articles/show.html
index 98016a43904610c24ec3cf3a32012795d834053f..2eb45fbbe1b93b12374da912f15a6e11968e3b17 100644
--- a/vipra-ui/app/html/articles/detail/show.html
+++ b/vipra-ui/app/html/articles/show.html
@@ -37,7 +37,7 @@
     <tr>
       <th>Links</th>
       <td>
-        <a ui-sref="articles.detail.network({id:article.id})">Network graph</a>
+        <a ui-sref="network({type:'articles', id:article.id})">Network graph</a>
       </td>
     </tr>
   </tbody>
diff --git a/vipra-ui/app/html/index.html b/vipra-ui/app/html/index.html
index a1b589f3c65b813c7c7dc0ea28614f0356c3afbf..336e7d2f348a602b8c83c30c000aec72297eac34 100644
--- a/vipra-ui/app/html/index.html
+++ b/vipra-ui/app/html/index.html
@@ -11,7 +11,7 @@
       <h4>Latest articles</h4>
       <ul class="list-unstyled">
         <li class="ellipsize" ng-repeat="article in latestArticles">
-          <a ui-sref="articles.detail.show({id:article.id})" ng-bind="article.title"></a>
+          <a ui-sref="articles.show({id:article.id})" ng-bind="article.title"></a>
         </li>
       </ul>
     </div>
@@ -50,7 +50,7 @@
       <h4>Results <query-time/></h4>
       <ul class="list-unstyled search-results">
         <li class="search-result" ng-repeat="article in searchResults">
-          <a ui-sref="articles.detail.show({id:article.id})" ng-bind="article.title"></a>
+          <a ui-sref="articles.show({id:article.id})" ng-bind="article.title"></a>
           <p>
             <span class="text" ng-bind="article.text"></span>
             <br>
diff --git a/vipra-ui/app/html/articles/detail/network.html b/vipra-ui/app/html/network.html
similarity index 65%
rename from vipra-ui/app/html/articles/detail/network.html
rename to vipra-ui/app/html/network.html
index d1e0899b9cfa204a2380a963424719d8224104dd..316ed30991ebb8d53ba2637dd744dc7f2e141ea3 100644
--- a/vipra-ui/app/html/articles/detail/network.html
+++ b/vipra-ui/app/html/network.html
@@ -1,8 +1,4 @@
-<div class="graph"
-    vis-graph
-    vis-type="article"
-    ng-model="article">
-
+<div class="fullsize navpadding" vis-graph vis-type="type" ng-model="model">
     <div class="graph-legend">
       <span style="color:#BBC9D2">Articles</span><br>
       <span style="color:#DBB234">Topics</span><br>
diff --git a/vipra-ui/app/html/topics/show.html b/vipra-ui/app/html/topics/show.html
index 38d1648ede547bad61d2c42d76e7ef2003194cba..0d21fb0817a71a93e3b89ab82a78d9f968cddd71 100644
--- a/vipra-ui/app/html/topics/show.html
+++ b/vipra-ui/app/html/topics/show.html
@@ -18,6 +18,12 @@
       <th>Last modified</th>
       <td ng-bind="::topic.modified"></td>
     </tr>
+    <tr>
+      <th>Links</th>
+      <td>
+        <a ui-sref="network({type:'topics', id:topic.id})">Network graph</a>
+      </td>
+    </tr>
   </tbody>
 </table>
 
diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js
index 29554595c0f76352ac2df53efea8b433c2e5eaee..1de6e116ec9bd86377d19698abf4f84a7865615b 100644
--- a/vipra-ui/app/js/app.js
+++ b/vipra-ui/app/js/app.js
@@ -29,6 +29,12 @@
       controller: 'IndexController'
     });
 
+    $stateProvider.state('network', {
+      url: '/network/:type/:id',
+      templateUrl: tplBase + '/network.html',
+      controller: 'NetworkController'
+    });
+
     // states: articles
 
     $stateProvider.state('articles', {
@@ -43,24 +49,12 @@
       controller: 'ArticlesIndexController'
     });
 
-    $stateProvider.state('articles.detail', {
+    $stateProvider.state('articles.show', {
       url: '/:id',
-      abstract: true,
-      template: '<ui-view/>'
-    });
-
-    $stateProvider.state('articles.detail.show', {
-      url: '',
-      templateUrl: tplBase + '/articles/detail/show.html',
+      templateUrl: tplBase + '/articles/show.html',
       controller: 'ArticlesShowController'
     });
 
-    $stateProvider.state('articles.detail.network', {
-      url: '/network',
-      templateUrl: tplBase + '/articles/detail/network.html',
-      controller: 'ArticlesNetworkController'
-    });
-
     // states: topics
 
     $stateProvider.state('topics', {
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index 7928303720add6d0f173554c9cfc08eb10059450..9f7c3db157569e7a6846bc5c0fc7851b9c46a514 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -48,6 +48,27 @@
 
   }]);
 
+  app.controller('NetworkController', ['$scope', '$state', '$stateParams', 'ArticleFactory', 'TopicFactory',
+    function($scope, $state, $stateParams, ArticleFactory, TopicFactory) {
+
+    $scope.type = $stateParams.type;
+
+    var factory;
+    if($stateParams.type === 'articles')
+      factory = ArticleFactory;
+    else if($stateParams.type === 'topics')
+      factory = TopicFactory;
+    else {
+      console.log('unknown network type');
+      return;
+    }
+
+    factory.get({id: $stateParams.id}, function(response) {
+      $scope.model = response.data;
+    });
+
+  }]);
+
   /*
    * Article Controllers
    */
@@ -83,15 +104,6 @@
 
   }]);
 
-  app.controller('ArticlesNetworkController', ['$scope', '$state', '$stateParams', 'ArticleFactory', 'TopicFactory',
-    function($scope, $state, $stateParams, ArticleFactory, TopicFactory) {
-
-    ArticleFactory.get({id: $stateParams.id}, function(response) {
-      $scope.article = response.data;
-    });
-
-  }]);
-
   /*
    * Topic Controllers
    */
diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js
index f86e7fcd735e3638b49447090b9d9036ca706575..867fd6e4d5edd2211b3897e217065b71c5a0125f 100644
--- a/vipra-ui/app/js/directives.js
+++ b/vipra-ui/app/js/directives.js
@@ -30,7 +30,7 @@
       restrict: 'E',
       replace: true,
       transclude: true,
-      template: '<a class="article-link" ui-sref="articles.detail.show({id:article.id})"><span ng-bind="article.title"></span><ng-transclude/></a>'
+      template: '<a class="article-link" ui-sref="articles.show({id:article.id})"><span ng-bind="article.title"></span><ng-transclude/></a>'
     }
   });
 
@@ -56,18 +56,18 @@
     };
   });
 
-  app.directive('visGraph', ['$state', 'ArticleFactory', 'TopicFactory',
-    function($state, ArticleFactory, TopicFactory) {
+  app.directive('visGraph', ['$state', '$timeout', 'ArticleFactory', 'TopicFactory',
+    function($state, $timeout, ArticleFactory, TopicFactory) {
 
     return {
       scope: {
         ngModel: '=',
-        visType: '@',
+        visType: '=',
         visData: '=',
         visOptions: '='
       },
       transclude: true,
-      template: '<ng-transclude/><div class="graph" id="visgraph"></div>',
+      template: '<ng-transclude/><div class="fullsize navpadding" id="visgraph"></div>',
       link: function($scope, $element) {
         var id = 0,
             ids = {},
@@ -130,64 +130,69 @@
         };
         
         // on node select
+        var selectTimeout;
         $scope.select = function(props) {
-          var node = $scope.nodes.get(props.nodes[0]);
-          if(node && !node.loaded) {
-            if(node.type === 'article') {
-              // node is article, load article to get topics
-              ArticleFactory.get({id:node.article}, function(res) {
-                if(res.data && res.data.topics) {
-                  var topics = res.data.topics,
-                      newNodes = [],
-                      newEdges = [];
-                  for(var i = 0; i < topics.length; i++) {
-                    var topic =  topics[i].topic;
-                    if(ids.hasOwnProperty(topic.id)) {
-                      if(!$scope.nodes.get(ids[topic.id]).loaded)
-                        newEdges.push({from:node.id, to:ids[topic.id]});
-                    } else {
-                      newNodes.push(topicNode(topic));
-                      newEdges.push({from:node.id, to:id});
-                      ids[topic.id] = id;
+          $timeout.cancel(selectTimeout);
+          selectTimeout = $timeout(function() {
+            var node = $scope.nodes.get(props.nodes[0]);
+            if(node && !node.loaded) {
+              if(node.type === 'article') {
+                // node is article, load article to get topics
+                ArticleFactory.get({id:node.article}, function(res) {
+                  if(res.data && res.data.topics) {
+                    var topics = res.data.topics,
+                        newNodes = [],
+                        newEdges = [];
+                    for(var i = 0; i < topics.length; i++) {
+                      var topic =  topics[i].topic;
+                      if(ids.hasOwnProperty(topic.id)) {
+                        if(!$scope.nodes.get(ids[topic.id]).loaded)
+                          newEdges.push({from:node.id, to:ids[topic.id]});
+                      } else {
+                        newNodes.push(topicNode(topic));
+                        newEdges.push({from:node.id, to:id});
+                        ids[topic.id] = id;
+                      }
                     }
+                    if(newNodes.length) $scope.nodes.add(newNodes);
+                    if(newEdges.length) $scope.edges.add(newEdges);
                   }
-                  if(newNodes.length) $scope.nodes.add(newNodes);
-                  if(newEdges.length) $scope.edges.add(newEdges);
-                }
-              });
-            } else {
-              // node is topic, load topic to get articles
-              TopicFactory.get({id:node.topic}, function(res) {
-                if(res.data && res.data.articles) {
-                  var articles = res.data.articles,
-                      newNodes = [],
-                      newEdges = [];
-                  for(var i = 0; i < articles.length; i++) {
-                    var article = articles[i];
-                    if(ids.hasOwnProperty(article.id)) {
-                      if(!$scope.nodes.get(ids[article.id]).loaded)
-                        newEdges.push({from:ids[article.id], to:node.id});
-                    } else {
-                      newNodes.push(articleNode(article));
-                      newEdges.push({from:id, to:node.id});
-                      ids[article.id] = id;
+                });
+              } else {
+                // node is topic, load topic to get articles
+                TopicFactory.get({id:node.topic}, function(res) {
+                  if(res.data && res.data.articles) {
+                    var articles = res.data.articles,
+                        newNodes = [],
+                        newEdges = [];
+                    for(var i = 0; i < articles.length; i++) {
+                      var article = articles[i];
+                      if(ids.hasOwnProperty(article.id)) {
+                        if(!$scope.nodes.get(ids[article.id]).loaded)
+                          newEdges.push({from:ids[article.id], to:node.id});
+                      } else {
+                        newNodes.push(articleNode(article));
+                        newEdges.push({from:id, to:node.id});
+                        ids[article.id] = id;
+                      }
                     }
+                    if(newNodes.length) $scope.nodes.add(newNodes);
+                    if(newEdges.length) $scope.edges.add(newEdges);
                   }
-                  if(newNodes.length) $scope.nodes.add(newNodes);
-                  if(newEdges.length) $scope.edges.add(newEdges);
-                }
-              });
+                });
+              }
+              node.loaded = true;
+              $scope.nodes.update(node);
             }
-            node.loaded = true;
-            $scope.nodes.update(node);
-          }
+          }, 500);
         };
 
         // on node open
         $scope.open = function(props) {
+          $timeout.cancel(selectTimeout);
           var node = $scope.nodes.get(props.nodes[0]);
           if(node.type === 'article')
-            $state.transitionTo('articles.detail.show', {id:node.article});
+            $state.transitionTo('articles.show', {id:node.article});
           else
             $state.transitionTo('topics.show', {id:node.topic});
         };
@@ -197,7 +202,7 @@
           if(!newVal) return;
 
           // root node
-          if($scope.visType === 'article')
+          if($scope.visType === 'articles')
             nodes.push(articleNode($scope.ngModel));
           else
             nodes.push(topicNode($scope.ngModel));
diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less
index 9d966708c902291a24bd5a1b8d3305ee4cfed3c4..313bcd09d99686450621c94930766a75f1e2226f 100644
--- a/vipra-ui/app/less/app.less
+++ b/vipra-ui/app/less/app.less
@@ -101,7 +101,7 @@ body {
 
 .graph {
   position: absolute;
-  top: 10px;
+  top: 50px;
   left: 0;
   right: 0;
   bottom: 50px;
@@ -126,6 +126,19 @@ body {
   cursor: default;
 }
 
+.fullsize {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+
+.navpadding {
+  padding-top: 50px;
+  padding-bottom: 50px;
+}
+
 @-moz-keyframes spin { 100% { -moz-transform: rotateY(360deg); } }
 @-webkit-keyframes spin { 100% { -webkit-transform: rotateY(360deg); } }
 @keyframes spin { 100% { -webkit-transform: rotateY(360deg); transform:rotateY(360deg); } }
\ No newline at end of file