diff --git a/README.markdown b/README.markdown
--- a/README.markdown
+++ b/README.markdown
@@ -2,7 +2,8 @@
 ======================================================
 
 `yst` is a tool for generating a static website by filling [string
-template][]s with data taken from [YAML][] or [CSV][] text files. This approach
+template][]s with data taken from [YAML][] or [CSV][] text files or 
+[SQLite3][] file based databases. This approach
 combines the speed, security, and ease of deployment of a static
 website with the flexibility and maintainability of a dynamic site that
 separates presentation and data.
@@ -175,13 +176,19 @@
 
 - `indexfile`: the filename of the index file (default: `index.yaml`)
 - `title`:  the title of the whole site
-- `sourcedir`:  the directory containing all the templates and page sources
+- `sourcedir`:  list of directories containing templates and page sources
   (default: `.`)
-- `datadir`:  the directory containing yaml data files (default: `.`)
-- `filesdir`: the directory containing static files (default: `files`)
+- `datadir`:  list of directories containing yaml data files (default: `.`)
+- `filesdir`: list of directories containing static files (default: `files`)
 - `layout`: the default layout template for the site, relative to
   `sourcedir` (default: `layout.html.st`)
 
+The directories specified by `sourcedir` and `datadir` are searched in
+order to find source/template or data files, respectively.  This allows
+for a `../templates` directory to be shared among multiple sites, for
+example.  Static files are merged from the contents of all directories
+in `filesdir`. All of these accept a string as a singleton list.
+
 ### `index.yaml` and submenus
 
 The index file is a YAML list of pages. Each page may have the following
@@ -221,20 +228,26 @@
 
 ### The `data` field
 
-The `data` field in `index.yaml` can populate any number of stringtemplate
-attributes with data from YAML or CSV files.  The syntax is easiest to
-explain by example (note that the keywords do not have to be in ALL CAPS,
-although they may, and the query doesn't have to end with a semicolon,
-though it may):
+The `data` field in `index.yaml` can populate any number of
+stringtemplate attributes with data from YAML or CSV files or SQLite3
+databases.  The syntax is easiest to explain by example (note that the
+keywords do not have to be in ALL CAPS, although they may, and the
+query doesn't have to end with a semicolon, though it may):
 
     data:
         events:  from events.yaml order by date desc group by title then location
         people:  from people.csv order by birthday then lastname where
                   birthstate = 'CA' limit 5
+        beststudents: from students.sqlite 
+                  query "select * from students where grade > 5"
+                  order by name
 
 First we have the name of the stringtemplate attribute to be populated
 (say, `events`).  Then, after the colon, we have the data source
-(`events.yaml`) followed by one or more *transformations*, which will
+(`events.yaml`). If the data source is an SQLite3 database, it should be
+followed by a query that is a quoted string.
+
+The data source is followed by  one or more *transformations*, which will
 be applied in order.  Here are the possible transformations.  In
 what follows, brackets denote an optional component, `|` denotes
 alternatives, and `*` indicates that the component may be repeated
@@ -438,6 +451,20 @@
 (Thanks to
 <http://www.terminally-incoherent.com/blog/2006/07/20/dump-mysql-table-into-csv-file/>.)
 
+### Using a SQLite database as data source
+
+You can also get the data directly from a database. Just give the file
+name from the database followed by a query quoted in "'s
+
+In this way you can do joins and other advanced operations on your
+data before handing them over to yst:
+
+    data:
+      meetings : FROM data.sqlite 
+                 QUERY "select * from meetings 
+                   left outer join persons
+                   on meetings.speaker = persons.name"
+
 ### Using HTML in the templates
 
 Markdown allows raw HTML to be used, so you can embed HTML in templates.
@@ -528,6 +555,7 @@
 [string template]: http://www.stringtemplate.org/
 [YAML]: http://www.yaml.org/
 [CSV]: http://en.wikipedia.org/wiki/Comma-separated_values
+[SQLite3]: http://www.sqlite.org/
 [cabal install]: http://hackage.haskell.org/trac/hackage/wiki/CabalInstall
 [Haskell platform]: http://hackage.haskell.org/platform/
 [markdown]: http://daringfireball.com/markdown
diff --git a/Yst/Build.hs b/Yst/Build.hs
--- a/Yst/Build.hs
+++ b/Yst/Build.hs
@@ -39,25 +39,30 @@
 import System.IO (stderr)
 import Control.Monad
 
-dependencies :: Site -> String -> [FilePath]
-dependencies site url =
+findSource :: Site -> FilePath -> IO FilePath
+findSource = searchPath . sourceDir
+
+dependencies :: Site -> String -> IO [FilePath]
+dependencies site url = do
   let page = case M.lookup url (pageIndex site) of
                   Nothing   -> error $ "Tried to get dependencies for nonexistent page: " ++ url
                   Just pg   -> pg
-      layout = sourceDir site </> stripStExt (fromMaybe (defaultLayout site) $ layoutFile page) <.> "st"
-      requires = map (sourceDir site </>) $ requiresFiles page
-      srcdir = sourceDir site </>
+  layout <- findSource site $ stripStExt (fromMaybe (defaultLayout site) $ layoutFile page) <.> "st"
+  requires <- mapM (findSource site) $ requiresFiles page
+  srcdir <- findSource site $
                  case sourceFile page of
                        TemplateFile f -> stripStExt f <.> "st"
                        SourceFile f   -> f
-      fileFromSpec (DataFromFile f _) = Just f
+  let fileFromSpec (DataFromFile f _) = Just f
+      fileFromSpec (DataFromSqlite3 f _ _) = Just f
       fileFromSpec _ = Nothing
-      dataFiles = map (dataDir site </>) $ mapMaybe (\(_,s) -> fileFromSpec s) $ pageData page
-  in  indexFile site : layout : srcdir : (requires ++ dataFiles)
+  dataFiles <- mapM (searchPath $ dataDir site) $ mapMaybe (\(_,s) -> fileFromSpec s) $ pageData page
+  return $ indexFile site : layout : srcdir : (requires ++ dataFiles)
 
 buildSite :: Site -> IO ()
 buildSite site = do
-  files <- liftM (filter (/=".") . map (makeRelative $ filesDir site)) $ getDirectoryContentsRecursive $ filesDir site
+  let filesIn dir = liftM (filter (/=".") . map (makeRelative dir)) $ getDirectoryContentsRecursive dir
+  files <- liftM concat $ mapM filesIn $ filesDir site
   let pages = M.keys $ pageIndex site
   let overlap = files `intersect` pages
   unless (null overlap) $ forM_ overlap
@@ -72,7 +77,7 @@
 updateFile :: Site -> FilePath -> IO ()
 updateFile site file = do
   let destpath = deployDir site </> file
-  let srcpath = filesDir site </> file
+  srcpath <- searchPath (filesDir site) file
   srcmod <- getModificationTime srcpath
   destmod <- catch (getModificationTime destpath) (\_ -> return $ TOD 0 0)
   if srcmod > destmod
@@ -85,7 +90,7 @@
 updatePage :: Site -> Page -> IO ()
 updatePage site page = do
   let destpath = deployDir site </> pageUrl page
-  let deps = dependencies site $ pageUrl page
+  deps <- dependencies site $ pageUrl page
   forM_ deps $ \dep -> do
     exists <- doesFileExist dep
     unless exists $ do
diff --git a/Yst/Config.hs b/Yst/Config.hs
--- a/Yst/Config.hs
+++ b/Yst/Config.hs
@@ -33,9 +33,9 @@
          let indexfile = getStrAttrWithDefault "indexfile" "index.yaml" xs
          (ind, nav) <- parseIndexFile indexfile
          return Site{ siteTitle     = getStrAttrWithDefault "title" "" xs
-                    , sourceDir     = getStrAttrWithDefault "sourcedir" "." xs
-                    , dataDir       = getStrAttrWithDefault "datadir" "." xs
-                    , filesDir      = getStrAttrWithDefault "filesdir" "files" xs
+                    , sourceDir     = getStrListWithDefault "sourcedir" "." xs
+                    , dataDir       = getStrListWithDefault "datadir" "." xs
+                    , filesDir      = getStrListWithDefault "filesdir" "files" xs
                     , deployDir     = getStrAttrWithDefault "deploydir" "site" xs
                     , defaultLayout = getStrAttrWithDefault "layout" "layout" xs
                     , indexFile     = indexfile
diff --git a/Yst/Data.hs b/Yst/Data.hs
--- a/Yst/Data.hs
+++ b/Yst/Data.hs
@@ -23,18 +23,26 @@
 import Yst.Util
 import Yst.Yaml
 import Yst.CSV
+import Yst.Sqlite3 (readSqlite3)
 import Control.Monad
 import Data.Char
 import Data.Maybe (fromMaybe)
 import Data.List (sortBy, nub, isPrefixOf)
 import Text.ParserCombinators.Parsec
-import System.FilePath (takeExtension, (</>))
+import System.FilePath (takeExtension)
 
+findData :: Site -> FilePath -> IO FilePath
+findData = searchPath . dataDir
+
 getData :: Site -> DataSpec -> IO Node
 getData site (DataFromFile file opts) = do
-  raw <- catch (readDataFile $  dataDir site </> file)
+  raw <- catch (findData site file >>= readDataFile)
           (\e -> errorExit 15 ("Error reading data from " ++ file ++ ": " ++ show e) >> return undefined)
   return $ foldl applyDataOption raw opts
+getData site (DataFromSqlite3 database query opts) = do
+  raw <- catch (findData site database >>= \d -> readSqlite3 d query)
+          (\e -> errorExit 15 ("Error reading Sqlite3 database from " ++ database ++ ": " ++ show e) >> return undefined)
+  return $ foldl applyDataOption raw opts
 getData _ (DataConstant n) = return n
 
 readDataFile :: FilePath -> IO Node
@@ -103,18 +111,20 @@
 
 parseDataField :: Node -> DataSpec
 parseDataField n@(NString s) = case parse pDataField s s of
-  Right (f,opts)  -> DataFromFile f opts
+  Right (f, Nothing, opts)  -> DataFromFile f opts
+  Right (f, Just query, opts) -> DataFromSqlite3 f query opts
   Left err        -> if "from" `isPrefixOf` (dropWhile isSpace $ map toLower s)
                         then error $ "Error parsing data field: " ++ show err
                         else DataConstant n 
 parseDataField n = DataConstant n
 
-pDataField :: GenParser Char st ([Char], [DataOption])
+pDataField :: GenParser Char st (String, Maybe String,[DataOption])
 pDataField = do
   spaces
   pString "from"
   pSpace
-  fname <- pIdentifier <?> "name of YAML or CSV file"
+  fname <- pIdentifier <?> "name of YAML, CSV or SQLite3 file"
+  query <- (optionMaybe $ pQuery) <?> "a SQL query"
   opts <- many $ (pOptWhere <?> "where [CONDITION]")
               <|> (pOptLimit <?> "limit [NUMBER]")
               <|> (pOptOrderBy <?> "order by [CONDITION]")
@@ -123,7 +133,7 @@
   optional $ char ';'
   spaces
   eof
-  return (fname, opts)
+  return (fname, query, opts)
 
 pIdentifier :: GenParser Char st [Char]
 pIdentifier = spaces >> (pQuoted '\'' <|> pQuoted '"' <|> many (noneOf " \t\n<>=;,'\""))
@@ -141,6 +151,15 @@
   char delim
   res <- many (noneOf [delim] <|> (try $ char '\\' >> char delim))
   char delim
+  return res
+
+pQuery :: GenParser Char st String
+pQuery = try $ do
+  optional $ oneOf ",;"
+  spaces
+  pString "query"
+  pSpace
+  res <- pQuoted '"'
   return res
 
 pOptLimit :: GenParser Char st DataOption
diff --git a/Yst/Render.hs b/Yst/Render.hs
--- a/Yst/Render.hs
+++ b/Yst/Render.hs
@@ -100,26 +100,32 @@
                              ".txt"   -> PlainFormat
                              ".markdown" -> PlainFormat
                              _       -> HtmlFormat
+
 renderPage :: Site -> Page -> IO String
 renderPage site page = do
   let menuHtml = renderNav (pageUrl page) (navigation site)
   let layout = fromMaybe (defaultLayout site) $ layoutFile page
-  srcDir <- canonicalizePath $ sourceDir site
-  g <- directoryGroupRecursive srcDir
+  srcDirs <- mapM canonicalizePath $ sourceDir site
+  gs <- mapM directoryGroupRecursive srcDirs
+  let g = foldl1 mergeSTGroups gs
   attrs <- forM (pageData page) $ \(k, v) -> getData site v >>= \n -> return (k,n)
   todaysDate <- liftM utctDay getCurrentTime
+  let root' = case length (filter (=='/') $ pageUrl page) of
+                    0  -> ""
+                    n  -> concat $ replicate n "../"
   rawContents <-
     case sourceFile page of
-          SourceFile sf   -> liftM (filter (/='\r')) $ readFile (srcDir </> sf)
+          SourceFile sf   -> liftM (filter (/='\r')) $ searchPath srcDirs sf >>= readFile
           TemplateFile tf -> do
             templ <- getTemplate tf g
-            return $ render (setManyAttrib attrs templ)
+            return $ render
+                    . setManyAttrib attrs
+                    . setAttribute "root" root'
+                    . setAttribute "gendate" todaysDate
+                    $ templ
   layoutTempl <- getTemplate layout g
   let format = formatFromExtension (stripStExt layout)
   let contents = converterForFormat format rawContents
-  let root' = case length (filter (=='/') $ pageUrl page) of
-                    0  -> ""
-                    n  -> concat $ replicate n "../"
   return $ render
          . setManyAttrib attrs
          . setAttribute "sitetitle" (siteTitle site)
diff --git a/Yst/Sqlite3.hs b/Yst/Sqlite3.hs
new file mode 100644
--- /dev/null
+++ b/Yst/Sqlite3.hs
@@ -0,0 +1,27 @@
+module Yst.Sqlite3 (readSqlite3)
+where
+
+import Database.HDBC
+import Database.HDBC.Sqlite3
+import Data.Maybe
+import Yst.Types
+import Yst.Util (parseAsDate)
+
+readSqlite3 :: FilePath -> String -> IO Node
+readSqlite3 filename query = do
+  conn <- connectSqlite3 filename
+  stmt <- prepare conn query
+  execute stmt []
+  records <- sFetchAllRows' stmt
+  fieldNames <- getColumnNames stmt
+  disconnect conn
+         
+  return $ NList $ map (NMap .
+                       (zip fieldNames) .
+                       (map (fieldToNode . fromMaybe ""))) records
+
+fieldToNode :: String -> Node
+fieldToNode s =
+  case parseAsDate s of
+       Nothing -> NString s
+       Just d  -> NDate d
diff --git a/Yst/Types.hs b/Yst/Types.hs
--- a/Yst/Types.hs
+++ b/Yst/Types.hs
@@ -26,9 +26,9 @@
 
 data Site = Site {
     siteTitle     :: String
-  , sourceDir     :: FilePath
-  , dataDir       :: FilePath
-  , filesDir      :: FilePath
+  , sourceDir     :: [FilePath]
+  , dataDir       :: [FilePath]
+  , filesDir      :: [FilePath]
   , deployDir     :: FilePath
   , defaultLayout :: FilePath
   , indexFile     :: FilePath
@@ -75,6 +75,7 @@
 
 data DataSpec = DataConstant Node
               | DataFromFile FilePath [DataOption]
+              | DataFromSqlite3 FilePath String [DataOption]
               deriving (Show, Read, Eq)
 
 data DataOption = OrderBy [(String, SortDirection)]
diff --git a/Yst/Util.hs b/Yst/Util.hs
--- a/Yst/Util.hs
+++ b/Yst/Util.hs
@@ -16,7 +16,7 @@
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 -}
 
-module Yst.Util (stripBlanks, parseAsDate, stripStExt, getStrAttrWithDefault, fromNString, getDirectoryContentsRecursive, errorExit)
+module Yst.Util (stripBlanks, parseAsDate, stripStExt, getStrAttrWithDefault, getStrListWithDefault, fromNString, getDirectoryContentsRecursive, searchPath, errorExit)
 where
 import Yst.Types
 import System.Exit
@@ -59,6 +59,17 @@
         Just _             -> error $ attr ++ " must have string value."
         Nothing            -> def
 
+getStrListWithDefault :: String -> String -> [(String, Node)] -> [String]
+getStrListWithDefault attr def xs =
+  case lookup attr xs of
+    Just (NString s) -> [s]
+    Just (NList ys) -> map nodeToString ys
+    Just _ -> formatError
+    Nothing -> [def]
+  where nodeToString (NString s) = s
+        nodeToString _ = formatError
+        formatError = error $ attr ++ " must be a string or list of strings."
+
 fromNString :: Node -> String
 fromNString (NString s) = s
 fromNString x = error $ "Expected string value, got " ++ show x
@@ -73,6 +84,13 @@
        children <- mapM getDirectoryContentsRecursive contents'
        return (concat children)
      else return [path]
+
+searchPath :: [FilePath] -> FilePath -> IO FilePath
+searchPath [] file = return file       -- may or may not exist, but we tried.
+searchPath (dir:dirs) file = do
+  exists <- doesFileExist curFile
+  if exists then return curFile else searchPath dirs file
+    where curFile = dir </> file
 
 errorExit :: Int -> String -> IO ()
 errorExit lvl msg = hPutStrLn stderr msg >> exitWith (ExitFailure lvl)
diff --git a/changelog b/changelog
new file mode 100644
--- /dev/null
+++ b/changelog
@@ -0,0 +1,7 @@
+yst 0.3 (released 20 Oct 2012)
+
+  * Added support for loading data from Sqlite3 databases.
+    (Sigurd Meldgaard)
+
+  * Allow `sourcedir`, `datadir`, `filesdir` to specify a list
+    of directories, searched in order. (Christopher League)
diff --git a/demo/config.yaml b/demo/config.yaml
--- a/demo/config.yaml
+++ b/demo/config.yaml
@@ -1,6 +1,6 @@
 indexfile: index.yaml
 title: My Website
-templatedir: .
+sourcedir: [., templates]
 datadir: .
-filesdir: files
+filesdir: [files, yahoo]
 layout: layout.html.st
diff --git a/demo/date.st b/demo/date.st
deleted file mode 100644
--- a/demo/date.st
+++ /dev/null
@@ -1,1 +0,0 @@
-$it; format="%B %d, %Y"$
diff --git a/demo/event.st b/demo/event.st
deleted file mode 100644
--- a/demo/event.st
+++ /dev/null
@@ -1,2 +0,0 @@
-- $if(it.speaker)$$it.speaker; separator=" and "$, $endif$ *$it.title$*.
-
diff --git a/demo/eventgroup.st b/demo/eventgroup.st
deleted file mode 100644
--- a/demo/eventgroup.st
+++ /dev/null
@@ -1,5 +0,0 @@
-**$first(it).date:date(); separator=" - "$**
-
-
-$it:event()$
-
diff --git a/demo/files/css/base-min.css b/demo/files/css/base-min.css
deleted file mode 100644
--- a/demo/files/css/base-min.css
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
-Copyright (c) 2009, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.net/yui/license.txt
-version: 2.7.0
-*/
-body{margin:10px;}h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong,dt{font-weight:bold;}optgroup{font-weight:normal;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}del{text-decoration:line-through;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{border:1px solid #000;padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}sup{vertical-align:super;}sub{vertical-align:sub;}p,fieldset,table,pre{margin-bottom:1em;}button,input[type="checkbox"],input[type="radio"],input[type="reset"],input[type="submit"]{padding:1px;}
diff --git a/demo/files/css/reset-fonts-grids.css b/demo/files/css/reset-fonts-grids.css
deleted file mode 100644
--- a/demo/files/css/reset-fonts-grids.css
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
-Copyright (c) 2009, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.net/yui/license.txt
-version: 2.7.0
-*/
-html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}body{text-align:center;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.25em;}#doc2{width:73.076em;*width:71.25em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.05em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main,.yui-g .yui-u .yui-g{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 #yui-main .yui-b{margin-left:13.30769em;*margin-left:13.05em;}.yui-t2 .yui-b{float:left;width:13.8461em;*width:13.50em;}.yui-t2 #yui-main .yui-b{margin-left:14.8461em;*margin-left:14.55em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 #yui-main .yui-b{margin-left:24.0769em;*margin-left:23.62em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.55em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 #yui-main .yui-b{margin-right:19.4615em;*margin-right:19.125em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 #yui-main .yui-b{margin-right:24.0769em;*margin-right:23.62em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:1.99%;}.yui-gb .yui-u{*margin-left:1.9%;*width:31.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-g .yui-u{width:48.1%;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#hd:after,#bd:after,#ft:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#hd,#bd,#ft,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;}
diff --git a/demo/files/css/reset-min.css b/demo/files/css/reset-min.css
deleted file mode 100644
--- a/demo/files/css/reset-min.css
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
-Copyright (c) 2009, Yahoo! Inc. All rights reserved.
-Code licensed under the BSD License:
-http://developer.yahoo.net/yui/license.txt
-version: 2.7.0
-*/
-html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}
diff --git a/demo/templates/date.st b/demo/templates/date.st
new file mode 100644
--- /dev/null
+++ b/demo/templates/date.st
@@ -0,0 +1,1 @@
+$it; format="%B %d, %Y"$
diff --git a/demo/templates/event.st b/demo/templates/event.st
new file mode 100644
--- /dev/null
+++ b/demo/templates/event.st
@@ -0,0 +1,2 @@
+- $if(it.speaker)$$it.speaker; separator=" and "$, $endif$ *$it.title$*.
+
diff --git a/demo/templates/eventgroup.st b/demo/templates/eventgroup.st
new file mode 100644
--- /dev/null
+++ b/demo/templates/eventgroup.st
@@ -0,0 +1,5 @@
+**$first(it).date:date(); separator=" - "$**
+
+
+$it:event()$
+
diff --git a/demo/yahoo/css/base-min.css b/demo/yahoo/css/base-min.css
new file mode 100644
--- /dev/null
+++ b/demo/yahoo/css/base-min.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+body{margin:10px;}h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong,dt{font-weight:bold;}optgroup{font-weight:normal;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}del{text-decoration:line-through;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{border:1px solid #000;padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}sup{vertical-align:super;}sub{vertical-align:sub;}p,fieldset,table,pre{margin-bottom:1em;}button,input[type="checkbox"],input[type="radio"],input[type="reset"],input[type="submit"]{padding:1px;}
diff --git a/demo/yahoo/css/reset-fonts-grids.css b/demo/yahoo/css/reset-fonts-grids.css
new file mode 100644
--- /dev/null
+++ b/demo/yahoo/css/reset-fonts-grids.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}body{text-align:center;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.25em;}#doc2{width:73.076em;*width:71.25em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.05em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main,.yui-g .yui-u .yui-g{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 #yui-main .yui-b{margin-left:13.30769em;*margin-left:13.05em;}.yui-t2 .yui-b{float:left;width:13.8461em;*width:13.50em;}.yui-t2 #yui-main .yui-b{margin-left:14.8461em;*margin-left:14.55em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 #yui-main .yui-b{margin-left:24.0769em;*margin-left:23.62em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.55em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 #yui-main .yui-b{margin-right:19.4615em;*margin-right:19.125em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 #yui-main .yui-b{margin-right:24.0769em;*margin-right:23.62em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:1.99%;}.yui-gb .yui-u{*margin-left:1.9%;*width:31.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-g .yui-u{width:48.1%;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#hd:after,#bd:after,#ft:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#hd,#bd,#ft,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;}
diff --git a/demo/yahoo/css/reset-min.css b/demo/yahoo/css/reset-min.css
new file mode 100644
--- /dev/null
+++ b/demo/yahoo/css/reset-min.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}
diff --git a/yst.cabal b/yst.cabal
--- a/yst.cabal
+++ b/yst.cabal
@@ -1,13 +1,14 @@
 name:                yst
-version:             0.2.4.1
+version:             0.3
 Tested-With:         GHC == 6.10.4, GHC == 6.12.1
-Cabal-version:       >= 1.2
+Cabal-version:       >= 1.6
 build-type:          Simple
 synopsis:            Builds a static website from templates and data in YAML or
                      CSV files.
 description:         `yst` is a tool for generating a static website
                      by filling string templates with data taken from
-                     YAML text files (or, alternatively, CSV files).
+                     YAML text files (or, alternatively, CSV files or 
+                     from a Sqlite3 database).
                      This approach combines the speed, security, and
                      ease of deployment of a static website with the
                      flexibility and maintainability of a dynamic site
@@ -21,12 +22,13 @@
 homepage:            http://github.com/jgm/yst
 stability:           experimental
 data-files:          README.markdown
+                     changelog
                      demo/config.yaml
-                     demo/date.st
-                     demo/eventgroup.st
+                     demo/templates/date.st
+                     demo/templates/eventgroup.st
                      demo/april_events.st
                      demo/events.st
-                     demo/event.st
+                     demo/templates/event.st
                      demo/events.yaml
                      demo/index.st
                      demo/index.yaml
@@ -34,23 +36,29 @@
                      demo/layout.tex.st
                      demo/room101.txt
                      demo/room203.txt
-                     demo/files/css/base-min.css
+                     demo/yahoo/css/base-min.css
                      demo/files/css/hk-pyg.css
                      demo/files/css/ie.css
                      demo/files/css/print.css
-                     demo/files/css/reset-fonts-grids.css
-                     demo/files/css/reset-min.css
+                     demo/yahoo/css/reset-fonts-grids.css
+                     demo/yahoo/css/reset-min.css
                      demo/files/css/screen.css
                      demo/files/js/nav.js
+
+Source-repository    head
+  type:              git
+  location:          git://github.com/jgm/yst.git
+
 Executable           yst
   hs-source-dirs:    .
   main-is:           yst.hs
   other-modules:     Yst.Types, Yst.Yaml, Yst.Util, Yst.Data, Yst.Config,
-                     Yst.Render, Yst.Build, Yst.CSV
-  build-depends:     base >=3 && < 5, HStringTemplate >= 0.6.1, HsSyck, csv,
+                     Yst.Render, Yst.Build, Yst.CSV, Yst.Sqlite3
+  build-depends:     base >=3 && < 5, HStringTemplate >= 0.6.1 && < 0.6.9,
+                     HsSyck, csv,
                      filepath, containers, directory, utf8-string, time,
                      old-locale, old-time, parsec, xhtml, pandoc, bytestring,
-                     split
+                     split, HDBC, HDBC-sqlite3
   extensions:        CPP
   if impl(ghc >= 6.12)
     ghc-options:     -Wall -threaded -fno-warn-orphans -fno-warn-unused-do-bind
