changeset 89:b03f083c3a29

Lots of HTML/JS/Less changes: - more semantic and simple markup, no useless grid. - simpler colour schemes (TODO: colours are ugly, tune them). - better error handling in Backbone views. - fixed some view bugs. - changed new/modified page alert to a ribbon with tooltip.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 06 Apr 2013 18:07:31 -0700
parents a5a3d454eac9
children e4eba42e1678
files static/css/wikked.less static/js/wikked.js static/js/wikked/app.js static/js/wikked/models.js static/js/wikked/views.js static/tpl/404.html static/tpl/diff-page.html static/tpl/edit-page.html static/tpl/error-not-found.html static/tpl/error-unauthorized-edit.html static/tpl/error-unauthorized.html static/tpl/footer.html static/tpl/history-page.html static/tpl/inlinks-page.html static/tpl/login.html static/tpl/nav.html static/tpl/read-page.html static/tpl/revision-page.html static/tpl/search-results.html static/tpl/special-changes.html static/tpl/special-nav.html static/tpl/special-orphans.html static/tpl/special-pages.html static/tpl/state-warning.html
diffstat 24 files changed, 327 insertions(+), 270 deletions(-) [+]
line wrap: on
line diff
--- a/static/css/wikked.less	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/css/wikked.less	Sat Apr 06 18:07:31 2013 -0700
@@ -4,22 +4,15 @@
 @import "/css/wmd.css";
 
 // Constants
-@wikiLinkColor: #12f;
-@wikiLinkColorHover: #36f;
-@wikiLinkColorMissing: #f12;
-
-@colorBlueDark: #48577D;
-@colorBlueMedium: #7690CF;
-@colorBlueLight: #D9E3FF;
-
-@colorGrayBlueLight: #D3DCF2;
-@colorGrayBlueMedium: #9197A6;
-@colorGrayBlueDark: #43464C;
-
+@colorBlue: #0F1FFF;
+@colorBlueDark: #1D3982;
+@colorOrange: #B03500;
+@colorGreen: #B6CC95;
+@colorGreenDark: #496B15;
+@colorPage: #FFFFFF;
+@colorFooter: #E5E7FF;
 @colorCode: #523C37;
 
-@pageBackground: white;
-@pageBackgroundSpecial: #E5D19E;
 
 // Macros
 .box-shadow(@style, @c) {
@@ -35,71 +28,93 @@
 }
 
 // Global classes
-a.wiki-link {
+a {
     text-decoration: none;
-    color: @wikiLinkColor;
-    &:link { color: @wikiLinkColor; }
-    &:visited { color: @wikiLinkColor; }
-    &:hover { color: @wikiLinkColorHover; text-decoration: underline; }
-    &:active { color: @wikiLinkColor; }
+    color: @colorBlue;
+    &:link { color: @colorBlue; }
+    &:visited { color: @colorBlue; }
+    &:hover { color: @colorBlue; text-decoration: underline; }
+    &:active { color: @colorBlue; }
 }
 a.wiki-link.missing {
-    color: @wikiLinkColorMissing;
+    color: @colorOrange;
+    &:hover { color: @colorOrange; text-decoration: underline; }
 }
 .decorator {
     text-transform: uppercase;
     font-weight: lighter;
     font-size: 0.7em;
     letter-spacing: 0.1em;
-    color: @colorBlueDark;
+    color: @colorBlue;
     .rev_id {
         font-family: @monoFontFamily;
-        color: @colorCode;
     }
 }
-
-div.alert {
+.tooltip {
+    font-size: @baseFontSize*0.75;
+}
+.alert {
     font-size: @fontSizeSmall;
 }
 
-.container {
-    nav, .meta {
+// Main classes
+.wrapper>nav,
+.wrapper>footer {
+    color: @grayLight;
+    font-size: @fontSizeSmall;
+    line-height: (@baseLineHeight * 2);
+
+    a {
+        padding: 0 1em;
+        display: inline-block;
+        text-decoration: none;
         color: @grayLight;
-        font-size: @fontSizeSmall;
-        line-height: (@baseLineHeight * 2);
-        a {
-            padding: 0 1em;
-            display: inline-block;
-            text-decoration: none;
-            color: @grayLight;
-            &:link { color: @grayLight; }
-            &:visited { color: @grayLight; }
-            &:hover { color: @linkColorHover; }
-            &:active { color: @grayLight; }
-        }
+        &:link { color: @grayLight; }
+        &:visited { color: @grayLight; }
+        &:hover { color: @linkColorHover; }
+        &:active { color: @grayLight; }
     }
-    .page {
-        .box-shadow(0 0 10px, rgb(210, 210, 210));
-        padding: @baseLineHeight;
-        background: @pageBackground;
-        overflow: hidden;
+}
+.wrapper>article {
+    .box-shadow(0 0 10px, rgb(210, 210, 210));
+    background: @colorPage;
+    overflow: hidden;
+
+    header {
+        background: @colorBlueDark;
+        color: @colorPage;
     }
-    .page.special {
-        background: @pageBackgroundSpecial;
+    footer {
+        background: @colorFooter;
+        border-top: 1px dashed darken(@colorFooter, 5%);
     }
-
-    .page>pre {
+    pre {
         margin-top: @baseLineHeight;
     }
-    .meta {
-        font-size: @fontSizeMini;
-    }
+}
+.wrapper>article>header,
+.wrapper>article>section,
+.wrapper>article>footer {
+    padding: @baseLineHeight;
+}
+.wrapper>footer {
+    font-size: @fontSizeMini;
+}
+.special>article {
+    background: @colorGreen;
+}
+.special>article header {
+    background: @colorGreenDark;
+}
+.special>article footer {
+    background: @colorGreen;
 }
 
+// Other classes
 form.form-search {
     display: inline-block;
     margin: 0;
-    
+
     .search-query {
         font-size: @fontSizeSmall;
         height: @fontSizeSmall * 1.5;
@@ -108,8 +123,7 @@
 ul.search-results {
     list-style: none;
 }
-
-form.page-edit {
+#page-edit {
     textarea {
         height: 10em;
         font-size: @baseFontSize;
@@ -118,10 +132,10 @@
         border-radius: 0;
     }
 }
-.wmd-input-wrapper {
+#wmd-input-wrapper {
     padding: 1em 0;
 
-    .wmd-input-grip { 
+    #wmd-input-grip {
         background: @grayLighter;
         border: 1px solid @grayLight;
         border-width: 0 1px 1px 1px;
@@ -129,16 +143,13 @@
         cursor: ns-resize;
     }
 }
-.wmd-preview-wrapper {
-    border: 1px dashed @colorGrayBlueMedium;
-    background: @colorGrayBlueLight;
+#wmd-preview-wrapper {
+    border: 1px dashed @colorBlueDark;
 
     h3 {
-        margin-left: 1em;
-        margin-right: 1em;
-        margin-bottom: 1em;
+        margin: 0.5em 1em 0.5em 1em;
     }
-    .wmd-preview {
+    #wmd-preview {
         margin: 1em;
         background: none;
     }
@@ -148,16 +159,16 @@
 }
 .preview-wiki-meta {
     .meta-name {
-        background: @colorBlueMedium;
-        color: #fff;
+        background: @colorBlueDark;
+        color: @colorPage;
         padding: 0.2em 0.4em 0.2em 0.6em;
         border-radius: 0.5em 0 0 0.5em;
         font-variant: small-caps;
         letter-spacing: 0.18em;
     }
     .meta-value {
-        background: @colorGrayBlueDark;
-        color: #fff;
+        background: @colorFooter;
+        color: black;
         padding: 0.2em 0.6em 0.2em 0.4em;
         border-radius:  0 0.5em 0.5em 0;
     }
@@ -166,17 +177,15 @@
     margin-top: 1em;
     margin-bottom: 1em;
 }
-
 b.match {
     padding: 0 0.2em;
     background: #ffeb84;
 }
 
 // Page alerts
-.page .alert {
+article .alert {
     float: right;
     margin-left: -100%;
-    top: -@baseLineHeight;
     position: relative;
     width: 50%;
     -webkit-border-radius: 0;
@@ -188,27 +197,29 @@
 .makeRibbon(@bgColor) {
     background-color: @bgColor;
     overflow: hidden;
-    transform: translate(50%, -@baseLineHeight) translate(-3em,3em) rotate(45deg);
-    -moz-transform: translate(50%, -@baseLineHeight) translate(-3em,3em) rotate(45deg);
-    -webkit-transform: translate(50%, -@baseLineHeight) translate(-3em,3em) rotate(45deg);
-    box-shadow: 0 0 1em #888;
-    -moz-box-shadow: 0 0 1em #888;
-    -webkit-box-shadow: 0 0 1em #888;
+    transform: translate(50%, 0) translate(-3em, 2.05em) rotate(45deg);
+    -moz-transform: translate(50%, 0) translate(-3em, 2.05em) rotate(45deg);
+    -webkit-transform: translate(50%, 0) translate(-3em, 2.05em) rotate(45deg);
+    box-shadow: 0 0 1em rgba(0, 0, 0, 0.4);
+    -moz-box-shadow: 0 0 1em rgba(0, 0, 0, 0.4);
+    -webkit-box-shadow: 0 0 1em rgba(0, 0, 0, 0.4);
+    float: right;
 }
 .makeRibbonContent(@borderColor, @textColor) {
     border: 1px solid @borderColor;
     color: @textColor;
     display: block;
     margin: 0.05em 0 0.05em 0;
-    padding: 0.5em 3.5em;
+    padding: 0.5em 5em;
     text-align: center;
-    text-shadow: 0 0 0.5em #444;
+    text-shadow: 0 0 0.5em rgba(1, 1, 1, 0.2);
 }
 .ribbon {
-    .makeRibbon(#a00);
-    margin-bottom: @baseFontSize * -2.6 - 4px;
+    .makeRibbon(lighten(@colorOrange, 10%));
+    float: right;
 
     .ribbon-inner {
-        .makeRibbonContent(#faa, #fff);
+        .makeRibbonContent(@colorOrange, @colorPage);
+        a { color: #fff; text-decoration: none; }
     }
 }
--- a/static/js/wikked.js	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/js/wikked.js	Sat Apr 06 18:07:31 2013 -0700
@@ -12,7 +12,8 @@
         handlebars: 'js/handlebars-1.0.rc.1',
         moment: 'js/moment.min',
         text: 'js/text',
-        bootstrap_modal: '/bootstrap/js/bootstrap-modal'
+        bootstrap_modal: '/bootstrap/js/bootstrap-modal',
+        bootstrap_tooltip: '/bootstrap/js/bootstrap-tooltip'
     },
     shim: {
         'jquery': {
@@ -30,6 +31,9 @@
         },
         'bootstrap_modal': {
             deps: ['jquery']
+        },
+        'bootstrap_tooltip': {
+            deps: ['jquery']
         }
     }
 });
--- a/static/js/wikked/app.js	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/js/wikked/app.js	Sat Apr 06 18:07:31 2013 -0700
@@ -151,7 +151,7 @@
             var view = new Views.LoginView({
                 model: new Models.LoginModel()
             });
-            this.viewManager.switchView(view);
+            this.viewManager.switchView(view, false);
             this.navigate('/login');
         },
         doLogout: function() {
--- a/static/js/wikked/models.js	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/js/wikked/models.js	Sat Apr 06 18:07:31 2013 -0700
@@ -227,7 +227,7 @@
             this.footer.addExtraUrl('JSON', function() { return '/api/read/' + model.id; });
         },
         _onChange: function() {
-            if (this.getMeta('redirect') && 
+            if (this.getMeta('redirect') &&
                 !this.get('no_redirect') &&
                 !this.get('redirected_from')) {
                 // Handle redirects.
@@ -261,6 +261,10 @@
                 .error(function() {
                     alert('Error saving page...');
                 });
+        },
+        _onChangePath: function(path) {
+            PageEditModel.__super__._onChangePath.apply(this, arguments);
+            this.set('url_read', '/#/read/' + path);
         }
     });
 
@@ -406,6 +410,12 @@
         initialize: function() {
             GenericSpecialPageModel.__super__.initialize.apply(this, arguments);
             this.footer.clearExtraUrls();
+            var key = this.get('page');
+            if (key in this.assignMap) {
+                this.assignMap[key].apply(this);
+            }
+        },
+        assignMap: {
         },
         titleMap: {
             orphans: 'Orphaned Pages',
--- a/static/js/wikked/views.js	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/js/wikked/views.js	Sat Apr 06 18:07:31 2013 -0700
@@ -6,6 +6,7 @@
         'underscore',
         'backbone',
         'handlebars',
+        'bootstrap_tooltip',
         'js/wikked/client',
         'js/wikked/models',
         'js/wikked/util',
@@ -28,7 +29,7 @@
         'text!tpl/special-changes.html',
         'text!tpl/special-orphans.html'
         ],
-    function($, _, Backbone, Handlebars, Client, Models, Util,
+    function($, _, Backbone, Handlebars, BootstrapTooltip, Client, Models, Util,
         tplReadPage, tplEditPage, tplHistoryPage, tplRevisionPage, tplDiffPage, tplInLinksPage,
         tplNav, tplFooter, tplSearchResults, tplLogin,
         tplErrorNotAuthorized, tplErrorNotFound, tplErrorUnauthorizedEdit, tplStateWarning,
@@ -39,15 +40,13 @@
     var PageView = exports.PageView = Backbone.View.extend({
         tagName: 'div',
         className: 'wrapper',
+        isMainPage: true,
         initialize: function() {
             PageView.__super__.initialize.apply(this, arguments);
             if (this.model !== undefined) {
                 var $view = this;
                 this.model.on("change", function() { $view._onModelChange(); });
             }
-            if (this.templateSource !== undefined) {
-                this.template = Handlebars.compile(_.result(this, 'templateSource'));
-            }
             return this;
         },
         dispose: function() {
@@ -61,13 +60,18 @@
             }
         },
         render: function(view) {
+            if (this.template === undefined && this.templateSource !== undefined) {
+                this.template = Handlebars.compile(_.result(this, 'templateSource'));
+            }
             if (this.template !== undefined) {
                 this.renderTemplate(this.template);
                 if (this.renderCallback !== undefined) {
                     this.renderCallback();
                 }
             }
-            this.renderTitle(this.titleFormat);
+            if (this.isMainPage) {
+                this.renderTitle(this.titleFormat);
+            }
             return this;
         },
         renderTemplate: function(tpl) {
@@ -88,13 +92,14 @@
 
     var NavigationView = exports.NavigationView = PageView.extend({
         templateSource: tplNav,
+        isMainPage: false,
         initialize: function() {
             NavigationView.__super__.initialize.apply(this, arguments);
             return this;
         },
         render: function() {
-            this.renderTemplate(this.template);
-            this.origPageEl = $('#app .page');
+            NavigationView.__super__.render.apply(this, arguments);
+            this.origPageEl = $('.wrapper>article');
             return this;
         },
         events: {
@@ -110,18 +115,17 @@
         _previewSearch: function(e) {
             // Restore the original content if the query is now
             // empty. Otherwise, run a search and render only the
-            // `.page` portion of the results page.
+            // `article` portion of the results page.
             var origPageEl = this.origPageEl;
-            var curPreviewEl = $('#app .page[class~="preview-search-results"]');
+            var curPreviewEl = $('.wrapper>article[class~="preview-search-results"]');
             var query = $(e.currentTarget).val();
             if (query && query.length > 0) {
                 var template = Handlebars.compile(tplSearchResults);
                 this.model.doPreviewSearch(query, function(data) {
                     data.is_instant = true;
                     var resultList = $(template(data));
-                    var inner = $('.page', resultList)
-                        .addClass('preview-search-results')
-                        .detach();
+                    var inner = $(resultList)
+                        .addClass('preview-search-results');
                     if (origPageEl.is(':visible')) {
                         inner.insertAfter(origPageEl);
                         origPageEl.hide();
@@ -144,27 +148,19 @@
 
     var FooterView = exports.FooterView = PageView.extend({
         templateSource: tplFooter,
+        isMainPage: false,
         initialize: function() {
             FooterView.__super__.initialize.apply(this, arguments);
             return this;
         },
         render: function() {
-            this.renderTemplate(this.template);
+            FooterView.__super__.render.apply(this, arguments);
             return this;
         }
     });
 
     var LoginView = exports.LoginView = PageView.extend({
         templateSource: tplLogin,
-        initialize: function() {
-            LoginView.__super__.initialize.apply(this, arguments);
-            return this;
-        },
-        render: function() {
-            this.renderTemplate(this.template);
-            document.title = 'Login';
-            return this;
-        },
         events: {
             "submit #login": "_submitLogin"
         },
@@ -183,10 +179,11 @@
             return this;
         },
         renderCallback: function() {
-            this.$el.prepend('<div id="nav"></div>');
-            this.$el.append('<div id="footer"></div>');
-            this.nav.setElement(this.$('#nav')).render();
-            this.footer.setElement(this.$('#footer')).render();
+            this.$el.prepend('<nav></nav>');
+            this.$el.append('<footer></footer>');
+            this.nav.setElement(this.$('>nav')).render();
+            this.footer.setElement(this.$('>footer')).render();
+            this.isError = (this.model.get('error_code') !== undefined);
         },
         templateSource: function() {
             switch (this.model.get('error_code')) {
@@ -215,6 +212,10 @@
         },
         renderCallback: function() {
             PageReadView.__super__.renderCallback.apply(this, arguments);
+            if (this.isError) {
+                return;
+            }
+
             // Replace all wiki links with proper hyperlinks using the JS app's
             // URL scheme.
             this.$('a.wiki-link[data-wiki-url]').each(function(i) {
@@ -237,11 +238,13 @@
             var state = this._pageState.get('state');
             if (state == 'new' || state == 'modified') {
                 var warning = $(this.warningTemplate(this._pageState.toJSON()));
-                warning.css('display', 'none');
-                warning.prependTo($('#app .page'));
-                warning.slideDown();
+                $('[rel="tooltip"]', warning).tooltip({container:'body'});
+                //warning.css('display', 'none');
+                warning.prependTo($('.wrapper>article'));
+                //warning.slideDown();
                 $('.dismiss', warning).click(function() {
-                    warning.slideUp();
+                    //warning.slideUp();
+                    warning.remove();
                     return false;
                 });
             }
@@ -273,14 +276,12 @@
     });
 
     var PageEditView = exports.PageEditView = MasterPageView.extend({
-        templateSource: function() {
-            if (this.model.get('error_code') == 401) {
-                return tplErrorUnauthorizedEdit;
-            }
-            return tplEditPage;
-        },
+        defaultTemplateSource: tplEditPage,
         renderCallback: function() {
             PageEditView.__super__.renderCallback.apply(this, arguments);
+            if (this.isError) {
+                return;
+            }
 
             // Create the Markdown editor.
             var formatter = new Client.PageFormatter();
@@ -292,11 +293,11 @@
             var editor = new Markdown.Editor(converter); //TODO: pass options
             editor.run();
             var editor_control = this.$('textarea#wmd-input');
-            editor_control.outerWidth(this.$('.wmd-input-wrapper').innerWidth());
+            editor_control.outerWidth(this.$('#wmd-input-wrapper').innerWidth());
         },
         events: {
-            "mousedown .wmd-input-grip": "_inputGripMouseDown",
-            "click .wmd-preview-wrapper>h3>a": "_togglePreview",
+            "mousedown #wmd-input-grip": "_inputGripMouseDown",
+            "click #wmd-preview-wrapper>h3>a": "_togglePreview",
             "submit #page-edit": "_submitEditedPage"
         },
         _inputGripMouseDown: function(e) {
@@ -315,8 +316,8 @@
         },
         _togglePreview: function(e) {
             // Show/hide live preview.
-            this.$('#wmd-preview').fadeToggle(function() {
-                var icon = this.$('.wmd-preview-wrapper>h3>a i');
+            $('#wmd-preview').fadeToggle(function() {
+                var icon = $('#wmd-preview-wrapper>h3>a i');
                 if (icon.hasClass('icon-minus')) {
                     icon.removeClass('icon-minus');
                     icon.addClass('icon-plus');
@@ -390,6 +391,7 @@
     });
 
     var SpecialMasterPageView = exports.SpecialMasterPageView = MasterPageView.extend({
+        className: 'wrapper special',
         _createNavigation: function(model) {
             model.set('show_root_link', true);
             return new SpecialNavigationView({ model: model });
--- a/static/tpl/404.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/404.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,14 +1,14 @@
-<div class="wrapper">
-    <nav class="row">
-        <div class="span12">
-            <a href="{{url_home}}">Home</a>
-            <span>Search</span>
-        </div>
-    </nav>
-    <article class="row">
-        <div class="page span12">
-            <h1>Page Not Found</h1>
-            <p>The page you requested was not found.</p>
-        </div>
-    </article>
-</div>
+<nav>
+    <section>
+        <a href="{{url_home}}">Home</a>
+        <span>Search</span>
+    </section>
+</nav>
+<article class="row">
+    <header>
+        <h1>Page Not Found</h1>
+    </header>
+    <section>
+        <p>The page you requested was not found.</p>
+    </section>
+</article>
--- a/static/tpl/diff-page.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/diff-page.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,5 +1,5 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>
             {{meta.title}}
             <span class="decorator">Diff:
@@ -10,6 +10,8 @@
             {{/if}}
             </span>
         </h1>
+    </header>
+    <section>
         <pre><code>{{{diff}}}</code></pre>
-    </div>
+    </section>
 </article>
--- a/static/tpl/edit-page.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/edit-page.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,40 +1,37 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>{{meta.title}} <span class="decorator">Editing</span></h1>
-        <form id="page-edit" class="page-edit row">
-            <div class="span12">
-                <div id="wmd-button-bar"></div>
-            </div>
-            <div class="span12">
-                <div class="wmd-input-wrapper">
-                    <textarea id="wmd-input" name="text" placeholder="Your page's contents go here...">{{content}}</textarea>
-                    <div class="wmd-input-grip"></div>
+    </header>
+    <section>
+        <form id="page-edit">
+            <section id="wmd-button-bar">
+            </section>
+            <section id="wmd-input-wrapper">
+                <textarea id="wmd-input" name="text" placeholder="Your page's contents go here...">{{content}}</textarea>
+                <div id="wmd-input-grip"></div>
+            </section>
+            <section id="wmd-preview-wrapper">
+                <h3><a class="btn"><i class="icon-minus"></i></a> Preview</h3>
+                <div id="wmd-preview"></div>
+            </section>
+            <section>
+                <div class="controls commit-meta">
+                    <div class="control-group input-prepend">
+                        <label for="author" class="control-label add-on">Author: </label>
+                        <input type="text" name="author" class="" placeholder="{{commit_meta.author}}"></input>
+                    </div>
+                    <div class="control-group input-prepend">
+                        <label for="message" class="control-label add-on">Change Description: </label>
+                        <input type="text" name="message" class="input-xxlarge" placeholder="{{commit_meta.desc}}"></input>
+                    </div>
                 </div>
-            </div>
-            <div class="span12">
-                <div class="wmd-preview-wrapper">
-                    <h3><a class="btn"><i class="icon-minus"></i></a> Preview</h3>
-                    <div id="wmd-preview" class="wmd-preview"></div>
-                </div>
-            </div>
-            <div class="span12">
-                    <div class="controls commit-meta">
-                        <div class="control-group input-prepend">
-                            <label for="author" class="control-label add-on">Author: </label>
-                            <input type="text" name="author" class="" placeholder="{{commit_meta.author}}"></input>
-                        </div>
-                        <div class="control-group input-prepend">
-                            <label for="message" class="control-label add-on">Change Description: </label>
-                            <input type="text" name="message" class="input-xxlarge" placeholder="{{commit_meta.desc}}"></input>
-                        </div>
-                    </div>
-            </div>
-            <div class="span12">
+            </section>
+            <section>
                 <button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i> Save</button>
                 <a href="{{url_read}}" class="btn">Cancel</a>
-            </div>
+            </section>
         </form>
-    </div>
+    <section>
 </article>
 <script type="text/javascript" src="/js/pagedown/Markdown.Converter.js"></script>
 <script type="text/javascript" src="/js/pagedown/Markdown.Sanitizer.js"></script>
--- a/static/tpl/error-not-found.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/error-not-found.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,6 +1,8 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>Not Found</h1>
+    </header>
+    <section>
         <p>The page you're trying to access does not exist. You can <a href="/#/edit/{{path}}">create it</a>.</p>
-    </div>
+    </section>
 </article>
--- a/static/tpl/error-unauthorized-edit.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/error-unauthorized-edit.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,6 +1,9 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>You're not authorized to edit this page</h1>
-        <p>The page you're trying to edit is protected. Please <a href="/#/login">log into an account</a> that has write access to it.</p>
-    </div>
+    </header>
+    <section>
+        <p>The page you're trying to edit is protected.
+                Please <a href="/#/login">log into an account</a> that has write access to it.</p>
+    </section>
 </article>
--- a/static/tpl/error-unauthorized.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/error-unauthorized.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,6 +1,9 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>You're not authorized for this</h1>
-        <p>The page you're trying to access is protected. Please <a href="/#/login">log into an account</a> that has access to it.</p>
-    </div>
+    </header>
+    <section>
+        <p>The page you're trying to access is protected.
+                Please <a href="/#/login">log into an account</a> that has access to it.</p>
+    </section>
 </article>
--- a/static/tpl/footer.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/footer.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,8 +1,6 @@
-<div class="row meta">
-    <div class="span12">
-        {{#each url_extras}}
-            <a href="{{url}}">{{name}}</a>
-        {{/each}}
-        <!-- TODO: last modified, etc. -->
-    </div>
-</div>
+<section>
+    {{#each url_extras}}
+        <a href="{{url}}">{{name}}</a>
+    {{/each}}
+    <!-- TODO: last modified, etc. -->
+</section>
--- a/static/tpl/history-page.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/history-page.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,6 +1,8 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>{{meta.title}} <span class="decorator">History</span></h1>
+    </header>
+    <section>
         {{#if history}}
         <p>Here's the revision log for <a href="{{url_read}}">{{meta.title}}</a>.</p>
         <form id="diff-page">
@@ -32,7 +34,9 @@
             </table>
         </form>
         {{else}}
-        <p><i class="icon-warning-sign large big"></i> This page has not been committed to the repository yet. <a href="{{url_edit}}">Edit it</a> to do that now.</p>
+        <p><i class="icon-warning-sign large big"></i>
+            This page has not been committed to the repository yet.
+            <a href="{{url_edit}}">Edit it</a> to do that now.</p>
         {{/if}}
-    </div>
+    </section>
 </article>
--- a/static/tpl/inlinks-page.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/inlinks-page.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,6 +1,8 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>{{meta.title}} <span class="decorator">Incoming Links</span></h1>
+    </header>
+    <section>
         <p>The following pages link to <a href="{{url_read}}">{{meta.title}}</a>:</p>
         <ul>
         {{#each in_links}}
@@ -13,5 +15,5 @@
             </li>
         {{/each}}
         </ul>
-    </div>
+    </section>
 </article>
--- a/static/tpl/login.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/login.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,11 +1,13 @@
-<nav class="row">
-    <div class="span12">
+<nav>
+    <section>
         <a href="/">Home</a>
-    </div>
+    </section>
 </nav>
-<div class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>Login</h1>
+    </header>
+    <section>
         {{#if has_error}}
         <div class="alert alert-error">
             <strong>Begone!</strong> Those credentials don't seem to work here.
@@ -27,7 +29,7 @@
             </div>
             <button type="submit" class="btn btn-primary">Login</button>
         </form>
-    </div>
-</div>
-<div class="row meta">
-</div>
\ No newline at end of file
+    </section>
+</article>
+<footer>
+</footer>
\ No newline at end of file
--- a/static/tpl/nav.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/nav.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,17 +1,15 @@
-<nav class="row">
-    <div class="span12">
-        {{#ifneq action to='read'}}<a href="{{url_read}}">Read</a>{{/ifneq}}
-        {{#ifneq action to='edit'}}<a href="{{url_edit}}">Edit</a>{{/ifneq}}
-        {{#ifneq action to='history'}}<a href="{{url_hist}}">History</a>{{/ifneq}}
-        <form id="search" class="form-search">
-            <input type="text" name="q" class="input-medium search-query" placeholder="Search...">
-            <button type="submit" class="btn btn-small">Search</button>
-        </form>
-        {{#if username}}
-        <a href="{{url_profile}}">{{username}}</a>
-        <a href="{{url_logout}}">Logout</a>
-        {{else}}
-        <a href="{{url_login}}">Login</a>
-        {{/if}}
-    </div>
-</nav>
+<section>
+    {{#ifneq action to='read'}}<a href="{{url_read}}">Read</a>{{/ifneq}}
+    {{#ifneq action to='edit'}}<a href="{{url_edit}}">Edit</a>{{/ifneq}}
+    {{#ifneq action to='history'}}<a href="{{url_hist}}">History</a>{{/ifneq}}
+    <form id="search" class="form-search">
+        <input type="text" name="q" class="input-medium search-query" placeholder="Search...">
+        <button type="submit" class="btn btn-small">Search</button>
+    </form>
+    {{#if username}}
+    <a href="{{url_profile}}">{{username}}</a>
+    <a href="{{url_logout}}">Logout</a>
+    {{else}}
+    <a href="{{url_login}}">Login</a>
+    {{/if}}
+</section>
--- a/static/tpl/read-page.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/read-page.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,14 +1,23 @@
-<article class="row">
-    <div class="page span12">
-        {{#ifnot meta.notitle}}
+<article>
+    {{#ifnot meta.notitle}}
+    <header>
         <h1>{{meta.title}}</h1>
-        {{/ifnot}}
         {{#if redirected_from}}
         <small>Redirected from <a href="/#/read/{{redirected_from}}?no_redirect">{{redirected_from}}</a></small>
         {{/if}}
         {{#if meta.redirect}}
         <small>Redirects to <a href="/#/read/{{meta.redirect}}">{{meta.redirect}}</a></small>
         {{/if}}
+    </header>
+    {{/ifnot}}
+    <section>
         {{content}}
-    </div>
+    </section>
+    {{#if meta.category}}
+    <footer>
+        {{#each meta.category}}
+        <span><a href="/#/special/category/">{{.}}</a></span>
+        {{/each}}
+    </footer>
+    {{/if}}
 </article>
--- a/static/tpl/revision-page.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/revision-page.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,11 +1,14 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>{{meta.title}} <span class="decorator">Revision: <span class="rev_id">{{disp_rev}}</span></span></h1>
+    </header>
+    <section>
         <pre><code>{{text}}</code></pre>
         <form id="page-revert" class="page-revert">
             <input type="hidden" name="rev" value="{{rev}}"/>
             <button type="submit" class="btn">Revert</button>
             <small>Revert the page to this revision</small>
         </form>
-    </div>
+    </section>
+</div>
 </article>
--- a/static/tpl/search-results.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/search-results.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,6 +1,8 @@
-<article class="row">
-    <div class="page span12">
+<article>
+    <header>
         <h1>Search Results</h1>
+    </header>
+    <section>
         {{#if is_instant}}
         <p><small>Press <code>Escape</code> to cancel.</small></p>
         {{/if}}
@@ -16,5 +18,5 @@
         {{else}}
         <p>No matches found.</p>
         {{/if}}
-    </div>
+    </section>
 </article>
--- a/static/tpl/special-changes.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/special-changes.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,6 +1,8 @@
-<article class="row">
-    <div class="page special span12">
+<article>
+    <header>
         <h1>Wiki History</h1>
+    </header>
+    <section>
         <p>Here are the recent changes on this wiki.</p>
         <form>
             <table class="table table-hover">
@@ -25,7 +27,7 @@
                                     <ul class="unstyled">
                                         {{#each changes}}
                                         <li>
-                                            <a href="/#/read/{{url}}">{{url}}</a> 
+                                            <a href="/#/read/{{url}}">{{url}}</a>
                                             {{#if is_edit}}(edit) {{/if}}
                                             {{#if is_add}}(added) {{/if}}
                                             {{#if is_delete}}(deleted) {{/if}}
@@ -42,5 +44,5 @@
                 </tbody>
             </table>
         </form>
-    </div>
+    </section>
 </article>
--- a/static/tpl/special-nav.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/special-nav.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,18 +1,16 @@
-<nav class="row">
-    <div class="span12">
-        <a href="/">Home</a>
-        {{#if show_root_link}}
-        <a href="/#/special">Special Pages</a>
-        {{/if}}
-        <form id="search" class="form-search">
-            <input type="text" name="q" class="input-medium search-query" placeholder="Search...">
-            <button type="submit" class="btn btn-small">Search</button>
-        </form>
-        {{#if username}}
-        <a href="{{url_profile}}">{{username}}</a>
-        <a href="{{url_logout}}">Logout</a>
-        {{else}}
-        <a href="{{url_login}}">Login</a>
-        {{/if}}
-    </div>
-</nav>
+<section>
+    <a href="/">Home</a>
+    {{#if show_root_link}}
+    <a href="/#/special">Special Pages</a>
+    {{/if}}
+    <form id="search" class="form-search">
+        <input type="text" name="q" class="input-medium search-query" placeholder="Search...">
+        <button type="submit" class="btn btn-small">Search</button>
+    </form>
+    {{#if username}}
+    <a href="{{url_profile}}">{{username}}</a>
+    <a href="{{url_logout}}">Logout</a>
+    {{else}}
+    <a href="{{url_login}}">Login</a>
+    {{/if}}
+</section>
--- a/static/tpl/special-orphans.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/special-orphans.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,16 +1,18 @@
-<article class="row">
-    <div class="page special span12">
+<article>
+    <header>
         <h1>Orphaned Pages</h1>
+    </header>
+    <section>
         <p>The following pages are not linked to by any other page in the wiki.</p>
         <p><span class="label label-info">Note</span> The main page can occasionally show up here but that's OK since it's the page visitors will see first anyway.</p>
         {{#if orphans}}
         <ul>
         {{#each orphans}}
-        <li><a href="/#/read/{{meta.url}}">{{meta.title}}</a></li>
+            <li><a href="/#/read/{{meta.url}}">{{meta.title}}</a></li>
         {{/each}}
-    </ul>
+        </ul>
         {{else}}
         <p>No orphaned pages!</p>
         {{/if}}
-    </div>
+    </section>
 </article>
--- a/static/tpl/special-pages.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/special-pages.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,9 +1,11 @@
-<article class="row">
-    <div class="page special span12">
+<article>
+    <header>
         <h1>Special Pages</h1>
+    </header>
+    <section>
         <h3><a href="/#/special/changes">Recent Changes</a></h3>
         <p>See all changes in the wiki.</p>
         <h3><a href="/#/special/orphans">Orphaned Pages</a></h3>
         <p>Lists pages in the wiki that have no links to them.</p>
-    </div>
+    </section>
 </article>
--- a/static/tpl/state-warning.html	Fri Apr 05 08:08:12 2013 -0700
+++ b/static/tpl/state-warning.html	Sat Apr 06 18:07:31 2013 -0700
@@ -1,4 +1,5 @@
-<div class="alert">
-    <button type="button" class="close" data-dismiss="alert">&times;</button>
-    <span>This page is {{state}} in the repository. You can <a href="{{url_edit}}">edit it</a> now to commit.</span>
+<div class="ribbon">
+    <div class="ribbon-inner">
+        <span><a href="{{url_edit}}" rel="tooltip" title="This page is {{state}} in the repository. You can edit it now to commit." data-toggle="tooltip" data-placement="bottom">modified <i class="icon-info-sign icon-white"></i></a></span>
+    </div>
 </div>