diff --git a/Bookshelf.cabal b/Bookshelf.cabal
--- a/Bookshelf.cabal
+++ b/Bookshelf.cabal
@@ -1,5 +1,5 @@
 name:           Bookshelf
-version:        0.1.2
+version:        0.1.3
 synopsis:       A simple document organizer with some wiki functionality
 description:    A simple document organizer with some wiki functionality
 category:       Text
@@ -11,5 +11,15 @@
 build-depends:  base, filepath, directory, utf8-string, parseargs, pandoc
 build-type:     Simple
 
-executable:     bookshelf
-main-is:        Bookshelf.hs
+data-files:
+  bookshelf.css
+  Documentation/Manual.shelf
+  Documentation/Test/Document1.txt
+  Documentation/Test/Document2.txt
+  Documentation/Test/Document3.txt
+  Documentation/Test/Document3.shelf
+  Documentation/Test/Notes.shelf
+
+extra-source-files: Generate.hs
+executable:         bookshelf
+main-is:            Bookshelf.hs
diff --git a/Documentation/Manual.shelf b/Documentation/Manual.shelf
new file mode 100644
--- /dev/null
+++ b/Documentation/Manual.shelf
@@ -0,0 +1,96 @@
+% Bookshelf manual
+% Emil Axelsson
+% May 13, 2009
+
+Introduction
+============
+
+Bookshelf is a simple wiki-like document organizer for use locally on one machine. Viewing is done through a web browser, and the content is generated statically. There are two types of documents: *shelf documents* which are generated from the convenient [markdown](http://daringfireball.net/projects/markdown/) format and can be viewed directly in the web browser, and *ordinary documents* (for example PDF, HTML, etc.) which the web browser treats in any way it likes.
+
+The most important feature of Bookshelf is its simplicity: No web server is needed. There are no global files for configuration/caching/indexing etc. A complete document collection is simply represented as a directory structure containing the relevant documents. Bookshelves are modular in the sense that sub-directories can be moved arbitrarily within or between bookshelves without worrying about inconsistencies[^PossibleInconsistency]. Documents are added simply by placing them within the directory structure.
+
+[^PossibleInconsistency]: Links may of course be broken when sub-directories are moved, but this can often be prevented by appropriate use of relative and absolute links. Furthermore, broken local links will be detected when the page is generated.
+
+Usage
+=====
+
+The input to Bookshelf is a directory tree with shelf documents (extension `.shelf`) and ordinary documents (any other file). Running Bookshelf on the directory `Shelf` is done as follows:
+
+    bookshelf Shelf
+
+This will (re-)generate the whole directory contents (see section [Operation](#operation)).
+
+We can also supply some optional parameters:
+
+    bookshelf --css bookshelf.css --editor gedit Shelf
+
+The first parameter tells Bookshelf which style sheet to use for the generated pages. The recommended style sheet for Bookshelf is [`bookshelf.css`](http://www.cs.chalmers.se/~emax/bookshelf.css). The second parameter tells what editor to use when editing the source files. This command will appear in an edit/regenerate script at the end of each generated page.
+
+Running Bookshelf without arguments results in the following message which shows the complete usage:
+
+    bookshelf [options] <source>
+      [-c,--css <CSS file>]           Path or URL to CSS style sheet
+      [-e,--editor <editor command>]  Command used for editing source files
+      [-n,--no-script]                Do not display the edit/regenerate script
+      <source>                        Root directory of bookshelf to be generated
+
+The markdown syntax is described [here](http://daringfireball.net/projects/markdown/syntax), and the extensions supported by [Pandoc](http://johnmacfarlane.net/pandoc/) (the converter used internally by Bookshelf) are described [here](http://johnmacfarlane.net/pandoc/README.html#pandocs-markdown-vsstandard-markdown).
+
+Operation
+=========
+
+Bookshelf performs two main operations in a given directory:
+
+  * Converts each file `name.shelf` to the file `name.shelf.html`. In this process, any links to files of the form `name.shelf` get redirected to `name.shelf.html`.
+  * Places a file `index.html` at each directory node. This file contains links to the directory's sub-directories and documents. The [Meta information](#meta-information) section describes how the document listing can be affected.
+
+For convenience, each generated HTML file displays a "script" for editing/regenerating the source file at the end. This can be turned off by providing the `--no-script` flag.
+
+It is possible to exclude files or directories from being converted and indexed by using "ignore" files. To exclude the file `name`, simply add a file `name.ignore` in the same directory.
+
+<u>Warning</u>: Any existing file with the extension `.shelf.html` or the name `index.html` are assumed to have been generated by previous runs of Bookshelf. Such files will first be removed[^RemovedFiles], and then possibly regenerated, so one should be careful not to have ordinary documents with such names.
+
+[^RemovedFiles]: This process does not take any `.ignore` files into account, so, for example, the file `name.shelf.html` will be deleted regardless of whether the file `name.shelf.html.ignore` exists.
+
+Bookshelf checks that all local links (in shelf documents) are valid, and will issue warnings if this is not the case.
+
+Meta information
+================
+
+Pandoc's markdown syntax allows the specification of ["title blocks"](http://johnmacfarlane.net/pandoc/README.html#title-blocks) containing information about document title, authors and date. If such information is provided for shelf documents, it will be included in the document listing for each directory.
+
+Whenever a shelf document and an ordinary document have the same name, apart from extensions, the shelf document is assumed to contain information about the ordinary document. In this situation, we call the ordinary document the *main document* and the corresponding shelf document the *info document*. An info document will only appear as an "info" link next to its main document in directory listings. Further more, the info document may optionally specify meta information about the main document by including a section of the following form at the top of the markdown source:
+
+    Meta
+    ====
+
+      * Title: Title of document
+      * Authors:
+        * Author number 1
+        * Author number 2
+        * etc.
+      * Date:    Some string indicating date
+      * Comment: Some comment
+
+This information will then be used when listing the ordinary document.
+
+Demonstration
+=============
+
+The contents in [Documentation](.) has been generated by Bookshelf. In addition to the current manual, it contains a sub-directory [Test](Test/) that demonstrates some of Bookshelf's features. The source of the documentation directory can be found [here](http://www.cs.chalmers.se/~emax/darcs/Bookshelf/Documentation).
+
+Installation
+============
+
+The easiest way to install Bookshelf is using the [Cabal install](http://hackage.haskell.org/trac/hackage/wiki/CabalInstall) tool. Cabal install is available in the [Haskell Platform](http://hackage.haskell.org/platform/).
+
+Bookshelf can be installed directly from [Hackage](http://hackage.haskell.org/cgi-bin/hackage-scripts/package/Bookshelf):
+
+    cabal install Bookshelf
+
+Alternatively, it can be downloaded from its [repository](http://www.cs.chalmers.se/~emax/darcs/Bookshelf), and installed by running
+
+    cabal install
+
+in the `Bookshelf` directory.
+
diff --git a/Documentation/Test/Document1.txt b/Documentation/Test/Document1.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/Test/Document1.txt
@@ -0,0 +1,1 @@
+This is a text file. It could have been any other document type. The web browser chooses how to display it.
diff --git a/Documentation/Test/Document2.txt b/Documentation/Test/Document2.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/Test/Document2.txt
@@ -0,0 +1,2 @@
+This is another text file.
+
diff --git a/Documentation/Test/Document3.shelf b/Documentation/Test/Document3.shelf
new file mode 100644
--- /dev/null
+++ b/Documentation/Test/Document3.shelf
@@ -0,0 +1,13 @@
+Meta
+====
+
+  * Title: An interesting document
+  * Authors:
+      * Adam Helf
+  * Date:
+  * Comment: This is a cool document.
+
+Notes
+=====
+
+There could be some more notes here.
diff --git a/Documentation/Test/Document3.txt b/Documentation/Test/Document3.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/Test/Document3.txt
@@ -0,0 +1,1 @@
+This file has an associated info document that specifies title, authors, etc.
diff --git a/Documentation/Test/Notes.shelf b/Documentation/Test/Notes.shelf
new file mode 100644
--- /dev/null
+++ b/Documentation/Test/Notes.shelf
@@ -0,0 +1,16 @@
+% Notes
+
+Heading 1
+=========
+
+Some notes...
+
+Heading 2
+=========
+
+  * Bullet 1
+  * Bullet 2
+      * Bullet 2.1
+      * Bullet 2.2
+      * Bullet 2.3
+  * Bullet 3
diff --git a/Generate.hs b/Generate.hs
new file mode 100644
--- /dev/null
+++ b/Generate.hs
@@ -0,0 +1,486 @@
+-- Copyright (C) 2009 Emil Axelsson <emax@chalmers.se>
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, write to the Free Software
+-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+-- |
+-- Copyright  : Copyright (C) 2009 Emil Axelsson <emax@chalmers.se>
+-- License    : GNU GPL, version 2 or above
+--
+-- Maintainer : Emil Axelsson <emax@chalmers.se>
+--
+-- Convertion of shelf documents to HTML.
+
+module Generate where
+
+
+
+import Control.Monad
+import qualified Data.Foldable as Fold
+import Data.Function
+import Data.List
+import System.FilePath
+
+import Text.Pandoc
+
+
+
+data MetaInfo = MetaInfo
+       { title   :: [Inline]
+       , authors :: [[Inline]]
+       , date    :: [Inline]
+       , comment :: [Inline]
+       }
+     deriving (Eq, Show)
+
+data Context = Context
+       { -- | Optional link to CSS file
+         cssLink_    :: Maybe FilePath
+         -- | Optional editor command
+       , editor_     :: Maybe String
+         -- | Show the edit/regenerate script?
+       , showScript_ :: Bool
+         -- | Path to root of bookshelf
+       , rootPath_   :: FilePath
+         -- | Path to current node, relative to the root
+       , relPath_    :: FilePath
+       }
+     deriving (Eq, Show)
+
+data ShelfInfo = ShelfInfo
+       { -- | The document's context
+         shelfContext  :: Context
+         -- | The full name of the associated main document. `Nothing` means
+         -- that there is no main document.
+       , mainDocument  :: Maybe String
+         -- | The full name of the shelf document.
+       , shelfDocument :: String
+       }
+
+class HasContext a
+  where
+    cssLink    :: a -> Maybe FilePath
+    editor     :: a -> Maybe String
+    showScript :: a -> Bool
+    rootPath   :: a -> FilePath
+    relPath    :: a -> FilePath
+
+instance HasContext Context
+  where
+    cssLink    = cssLink_
+    editor     = editor_
+    showScript = showScript_
+    rootPath   = rootPath_
+    relPath    = relPath_
+
+instance HasContext ShelfInfo
+  where
+    cssLink    = cssLink_    . shelfContext
+    editor     = editor_     . shelfContext
+    showScript = showScript_ . shelfContext
+    rootPath   = rootPath_   . shelfContext
+    relPath    = relPath_    . shelfContext
+
+
+
+-- | Move down to the given directory (affects `relPath_` and `cssLink_` in
+-- `Context`).
+moveDown :: Context -> String -> Context
+moveDown context dir = context
+    { relPath_ = relPath context </> dir
+    , cssLink_ = liftM (".." </>) $ cssLink context
+    }
+
+-- | Checks wether a path starts with @http:\/\/@.
+isHttp :: FilePath -> Bool
+isHttp path = ("http://" `isPrefixOf` path) || ("https://" `isPrefixOf` path)
+
+-- | Drops the @file:\/\/@ prefix (if any) of a path.
+dropFile :: FilePath -> FilePath
+dropFile path = case stripPrefix "file://" path of
+    Just path' -> path'
+    Nothing    -> path
+
+-- | Drops the label (if any) from a path. For example
+--
+-- > dropLabel "/dir/file.html#label" == "/dir/file.html"
+-- > dropLabel "/dir/file.html"       == "/dir/file.html"
+dropLabel :: FilePath -> FilePath
+dropLabel = takeWhile (/='#')
+
+-- | Applies the function to the path without its label (if any) (see
+-- `dropLabel`), and adds the label back to the result again.
+underLabel :: (FilePath -> FilePath) -> (FilePath -> FilePath)
+underLabel f pathLab = f path ++ label
+  where
+    (path,label) = break (=='#') pathLab
+
+-- | Redirects a link to a @.shelf@ file, by changing the @.shelf@ extension to
+-- @.shelf.html@. Links to non-shelf files are passed through unchanged.
+redirect :: FilePath -> FilePath
+redirect path = underLabel redir path
+  where
+    redir path
+        | takeExtension path == ".shelf" = path ++ ".html"
+        | otherwise                      = path
+
+-- | Lifts `redirect` to Pandoc inline elements (links).
+redirectLink :: Inline -> Inline
+redirectLink (Link is (path,tit)) = Link is (redirect path, tit)
+redirectLink i                    = i
+
+-- | Collects local links (at most one) from a Pandoc inline element.
+localLink :: Inline -> [FilePath]
+localLink inl = case inl of
+    Link  _ (path,_) -> linked path
+    Image _ (path,_) -> linked path
+    _                -> []
+  where
+    linked path = do
+      guard (not $ isHttp path)
+      return (dropLabel $ dropFile path)
+
+-- | Assumes that the input string is plain text, without any markdown syntax.
+pandocStr :: String -> [Inline]
+pandocStr = intersperse Space . map Str . words
+
+-- | Adds the given blocks at the beginning of the document.
+prependBlocks :: [Block] -> Pandoc -> Pandoc
+prependBlocks bs' (Pandoc meta bs) = Pandoc meta (bs' ++ bs)
+
+
+
+-- | Displays the relative path, with each directory name linked to its index
+-- file. If the `shelfDocument` field is not empty, a link to the document
+-- source will be added at the end of the relative path.
+makeContextPath :: ShelfInfo -> Block
+makeContextPath shelfInfo = Plain
+    [ Emph
+         $ pandocStr "➤ Context:"
+        ++ [Space]
+        ++ concat (reverse [link n dir | (n,dir) <- [0..] `zip` reverse nodes])
+        ++ fileLink
+    , LineBreak
+    ]
+  where
+    nodes = splitDirectories $ relPath shelfInfo
+    name  = shelfDocument shelfInfo
+
+    link n dir =
+        [ Link
+            (pandocStr dir)
+            (index, "Go to directory '" ++ dir ++ "'")
+        , Space
+        , HtmlInline "<b>/</b>"
+        , Space
+        ]
+      where
+        index = joinPath (replicate n "..") </> "index.html"
+
+    fileLink = do
+      guard (not $ null name)
+      return $ Link (pandocStr name) (name, "View document source")
+
+
+
+-- | Makes a link to the main document, if any. The result has 0 or 1 element.
+makeMainLink :: ShelfInfo -> [Block]
+makeMainLink shelfInfo = do
+    Just name <- return $ mainDocument shelfInfo
+    return $ Plain $ return $ Emph
+      [ Str "➤"
+      , Space
+      , Link (pandocStr "Main document") (name, "View main document")
+      ]
+
+
+
+-- | Makes a section with commands for editing the current document and
+-- regenerating the bookshelf.
+editRegenerate :: ShelfInfo -> String
+editRegenerate shelfInfo = guard (showScript shelfInfo) >> script
+  where
+    css     = cssLink       shelfInfo
+    edit    = editor        shelfInfo
+    name    = shelfDocument shelfInfo
+    relPth  = relPath       shelfInfo
+    rootPth = rootPath      shelfInfo
+
+    edit' = case edit of
+      Just e  -> e
+      Nothing -> "editor"
+
+    file = rootPth </> relPth </> name
+    root = rootPth </> head (splitDirectories relPth)
+
+    makeOpt (_,    Nothing)  = ""
+    makeOpt (flag, Just val) = flag ++ " " ++ show val
+
+    opts = filter (not . null) $ map makeOpt
+      [ ("--editor", edit)
+      , ("--css",    css)
+      ]
+
+    editCmd  = edit' ++ " " ++ show file
+    regenCmd = unwords ("bookshelf" : opts ++ [show root])
+
+    script =
+      "<p><br/></p>\n\
+      \<div class=\"bookshelf-meta\">\n\
+      \  <em>&#10148; Edit/regenerate this document:</em>\n\
+      \  <pre><code>" ++ editCmd ++ "\n" ++ regenCmd ++ "</code></pre>\n\
+      \</div>\n"
+
+
+
+bookshelfCreds :: String
+bookshelfCreds =
+    "<div id=\"footer\">\n\
+    \  Organized by <a href=\"http://www.cs.chalmers.se/~emax/bookshelf/Manual.shelf.html\">Bookshelf</a>\n\
+    \</div>\n"
+
+
+
+-- | Extracts title block information.
+extractMeta :: Pandoc -> MetaInfo
+extractMeta (Pandoc meta _) = MetaInfo title authors' date' []
+  where
+    Meta title authors date = meta
+
+    authors' = map pandocStr authors
+    date'    = pandocStr date
+
+
+
+-- | Extracts meta information about the \"main\" document from a shelf
+-- document. The document body will be matched against the following form:
+--
+-- > Meta
+-- > ====
+-- >
+-- >   * Title: Title of document
+-- >   * Authors:
+-- >     * Author number 1
+-- >     * Author number 2
+-- >     * etc.
+-- >   * Date:    Some string indicating date
+-- >   * Comment: Some comment
+-- >
+-- > Here comes the rest of the document.
+-- > ...
+-- > ...
+--
+-- If the document doesn't have the above form, some or all of the returned
+-- fields may be empty. However, the function does some attempt to handle
+-- partial specifications. In particular, the fields are matched from the top,
+-- so it's fine to leave out fields at the bottom.
+parseMainMeta :: Pandoc -> MetaInfo
+parseMainMeta (Pandoc _ blocks) = MetaInfo title authors date comment
+  where
+    metaBullets = case blocks of
+      Header 1 [Str "Meta"] : BulletList bulls : _ -> bulls
+      _                                            -> []
+
+    bsTit : bsAuth : bsDate : bsComm : _ = metaBullets ++ repeat []
+
+    plain bs = do
+      [Plain is] <- Just bs
+      return is
+
+    field f bs = Fold.concat $ do
+      Str f' : Space : rest <- plain bs
+      guard (f==f')
+      return rest
+
+    parseAuthors bs = Fold.concat $ do
+      [Plain [Str "Authors:"], BulletList bulls] <- return bs
+      mapM plain bulls
+
+    title   = field "Title:"   bsTit
+    date    = field "Date:"    bsDate
+    comment = field "Comment:" bsComm
+    authors = parseAuthors bsAuth
+
+
+
+-- | Makes an HTML header that links to the supplied CSS (if any).
+makeHeader :: Context -> String
+makeHeader context = case cssLink context of
+  Nothing -> ""
+  Just css
+    -> "<link rel=\"stylesheet\" href=\""
+    ++ css
+    ++ "\" type=\"text/css\" media=\"all\" />\n"
+
+
+
+-- | @markdownToHtml shelfInfo markdown = (html,links,meta)@:
+--
+-- Converts a shelf document to HTML. The result also contains a list of all
+-- local links in the document as well as meta information, in case such a
+-- specification is given. If the document is not linked to a main document, the
+-- returned meta information is taken from the title block (see Pandoc's
+-- markdown syntax). If the document has a main document, the meta information
+-- is obtained as specified in `parseMainMeta`.
+markdownToHtml :: ShelfInfo -> String -> (String, [FilePath], MetaInfo)
+markdownToHtml shelfInfo markdown = (html,links,meta)
+  where
+    pandoc   = readMarkdown rOpts markdown
+    links    = queryWith localLink pandoc
+    thisMeta = extractMeta pandoc
+    mainMeta = parseMainMeta pandoc
+    context  = shelfContext shelfInfo
+
+    contextBlocks
+       = [RawHtml "<div class=\"bookshelf-meta\">", makeContextPath shelfInfo]
+      ++ makeMainLink shelfInfo
+      ++ [RawHtml "</div>", Para [LineBreak]]
+
+    html
+      = writeHtmlString wOpts
+      $ prependBlocks contextBlocks
+      $ processWith redirectLink
+      $ pandoc
+
+    meta = case mainDocument shelfInfo of
+      Nothing -> thisMeta
+      _       -> mainMeta
+
+    rOpts = defaultParserState
+      { stateSmart = True
+      }
+
+    wOpts = defaultWriterOptions
+      { writerStandalone      = True
+      , writerTableOfContents = True
+      , writerHeader          = makeHeader context
+      , writerIncludeAfter    = editRegenerate shelfInfo ++ bookshelfCreds
+      }
+
+
+
+-- | If the title is empty in the `MetaInfo`, it gets set to the supplied
+-- string; otherwise nothing is changed.
+fixTitle :: MetaInfo -> String -> MetaInfo
+fixTitle meta tit = case title meta of
+    [] -> meta {title = pandocStr tit}
+    _  -> meta
+
+
+
+-- | @listDocument file meta view info@:
+--
+-- Makes an item for the given document to be displayed in the directory index.
+-- The link text is taken from the `title` field in @meta@. The other fields of
+-- @meta@ will be put in a bullet list underneath. @file@ is the file name of
+-- document. @view@ is a supplementary name of the document that will appear in
+-- the link title. The @info@ argument will inlined directly after the link.
+listDocument :: String -> MetaInfo -> String -> [Inline] -> [Block]
+listDocument file meta view info =
+    [ Plain $ [Link (title meta) (file, "View '" ++ view ++ "'")] ++ info'
+    , BulletList $ filter (not . null) $
+        [ bullet $ concat $ intersperse [Str ",", Space] $ authors meta
+        , bullet $ date meta
+        , bullet $ comment meta
+        ]
+    ]
+  where
+    info' = do
+      guard (not $ null info)
+      [Space, Emph info]
+
+    bullet is = do
+      guard (not $ null is)
+      [Plain [Emph is]]
+
+
+
+-- | @makeIndex context shelfs infos ords dirs@:
+--
+-- Makes an HTML file with the index of the current directory. @shelfs@ is a
+-- list of shelf documents (file name + meta information) that are not
+-- associated with a main document. @infos@ is a list of ordinary documents that
+-- have an associated info document. @ords@ is a list ordinary documents that
+-- have no associated info document. @dirs@ is a list of all sub-directories.
+makeIndex
+    :: Context
+    -> [(String, MetaInfo)]
+    -> [(String, MetaInfo)]
+    -> [String]
+    -> [String]
+    -> String
+
+makeIndex context shelfs infos ords dirs = writeHtmlString wOpts pandoc
+  where
+    wOpts = defaultWriterOptions
+      { writerStandalone      = True
+      , writerTableOfContents = False
+      , writerHeader          = makeHeader context
+      , writerIncludeAfter    = bookshelfCreds
+      }
+
+    pandoc  = Pandoc meta
+         $ [makeContextPath (ShelfInfo context Nothing "")]
+        ++ docBlocks
+        ++ dirBlocks
+
+      where
+        dir  = last $ splitDirectories $ relPath context
+        meta = Meta (pandocStr dir) [] []
+
+    docBlocks = do
+      guard (0 < length shelfs + length infos + length ords)
+      [ Header 1 [Str "Documents"]
+       , BulletList shelfBlocks
+       , BulletList infoBlocks
+       , BulletList ordBlocks
+       ]
+
+    shelfBlocks = map listShelfDoc $ sortBy (compare `on` fst) shelfs
+    infoBlocks  = map listInfoDoc  $ sortBy (compare `on` fst) infos
+    ordBlocks   = map listOrdDoc   $ sort ords
+
+    dirBlocks = do
+      guard (not $ null dirs)
+      [Header 1 [Str "Directories"]] ++ [BulletList $ map listDir $ sort dirs]
+
+    listShelfDoc (doc,meta) = listDocument docShelfHtml meta' doc' []
+      where
+        doc'         = dropExtension doc
+        docShelfHtml = doc `addExtension` ".html"
+        meta'        = fixTitle meta doc'
+
+    listInfoDoc (doc,meta) = listDocument doc meta' doc info
+      where
+        meta'        = fixTitle meta doc
+        docShelfHtml = replaceExtension doc ".shelf" `addExtension` ".html"
+        info =
+          [ Str "("
+          , Link
+             (pandocStr "info")
+             (docShelfHtml, "View document information")
+          , Str ")"
+          ]
+
+
+    listOrdDoc doc = return $ Plain
+      [Link (pandocStr doc) (doc, "View '" ++ doc ++ "'")]
+
+    listDir dir = return $ Plain
+      [ Link
+          (pandocStr dir)
+          (dir </> "index.html", "Go to directory '" ++ dir ++ "'")
+      , LineBreak
+      ]
+
diff --git a/bookshelf.css b/bookshelf.css
new file mode 100644
--- /dev/null
+++ b/bookshelf.css
@@ -0,0 +1,113 @@
+/* Based on pandoc.css (http://johnmacfarlane.net/pandoc/) */
+
+body {
+  margin: auto;
+  padding-right: 1em;
+  padding-left: 1em;
+  max-width: 50em;
+  border-left: 1px solid black;
+  border-right: 1px solid black;
+  font-family: verdana, sans-serif;
+  font-size: 85%;
+  line-height: 140%;
+  color: #333;
+}
+
+pre {
+  border: 1px dotted gray;
+  background-color: #ececec;
+  color: #1111111;
+  padding: 0.5em;
+}
+
+code {
+  font-family: monospace;
+  font-size: 115%;
+}
+
+h1 a, h2 a, h3 a, h4 a, h5 a {
+  text-decoration: none;
+  color: #7a5ada;
+}
+
+h1, h2, h3, h4, h5 {
+  font-family: verdana, sans-serif;
+  font-weight: bold;
+  color: #7a5ada;
+}
+
+h2, h3, h4, h5 {
+  border-bottom: 1px dotted #BBB;
+}
+
+h1 {
+  font-size: 130%;
+  border: 1px solid #ddd;
+  border-bottom: 2px solid #AAA;
+  border-right: 2px solid #AAA;
+  background-color: #f4e0ca;
+  margin-top: 1.5em;
+  padding: 0.2em;
+}
+
+h2 {
+  font-size: 110%;
+}
+
+h3 {
+  font-size: 95%;
+  margin-left: 2em;
+}
+
+h4 {
+  font-size: 90%;
+  font-style: italic;
+  margin-left: 3em;
+}
+
+h5 {
+  font-size: 85%;
+  font-style: italic;
+  margin-left: 4em;
+}
+
+h1.title {
+  font-size: 170%;
+  font-weight: bold;
+  padding-top: 0.2em;
+  padding-bottom: 0.7em;
+  text-align: center;
+  border: none;
+  background: none;
+}
+
+dt code {
+  font-weight: bold;
+}
+
+dd p {
+  margin-top: 0;
+}
+
+.bookshelf-meta {
+  border: 1px solid #ddd;
+  background-color: #dfc;
+  color: #1111111;
+  padding: 0.2em;
+}
+
+#footer {
+  padding-top: 1em;
+  font-size: 70%;
+  color: gray;
+  text-align: center;
+}
+
+a {
+  text-decoration: none;
+  color: #33f;
+}
+
+a:hover {
+  background-color: #ccc;
+}
