diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for tintin
+
+## Unreleased changes
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,195 @@
+Apache License
+==============
+
+_Version 2.0, January 2004_  
+_&lt;<http://www.apache.org/licenses/>&gt;_
+
+### Terms and Conditions for use, reproduction, and distribution
+
+#### 1. Definitions
+
+“License” shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+“Licensor” shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+“Legal Entity” shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, “control” means **(i)** the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
+outstanding shares, or **(iii)** beneficial ownership of such entity.
+
+“You” (or “Your”) shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+“Source” form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+“Object” form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+“Work” shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+“Derivative Works” shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+“Contribution” shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+“submitted” means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as “Not a Contribution.”
+
+“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+#### 2. Grant of Copyright License
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+#### 3. Grant of Patent License
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+#### 4. Redistribution
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+* **(b)** You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+#### 5. Submission of Contributions
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+#### 6. Trademarks
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+#### 7. Disclaimer of Warranty
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+#### 8. Limitation of Liability
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+#### 9. Accepting Warranty or Additional Liability
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+_END OF TERMS AND CONDITIONS_
+
+### APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets `[]` replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same “printed page” as the copyright notice for easier identification within
+third-party archives.
+
+    Copyright 2018 Theam
+    
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+    
+      http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+# Tintin
+
+Tintin allows you to create fancy **plug 'n' play** documentation sites for your Haskell project very easily.
+
+[Proceed to site](https://theam.github.io/tintin) to learn how to use Tintin.
+
+# FAQ
+
+## Why not just use Haddock?
+
+Haddock provides a great interface for writing API documentation, which is a kind of *hard documentation*.
+Newcomers from other languages are used to have a website per library, but sometimes this gets uncomfortable,
+as maintaining a documentation website is more work for the author.
+
+Tintin helps with this problem, as you get styling and organization for free. You just have to write your
+documentation using Markdown.
+
+The website generation is focused towards guides and tutorials, helping your users understand how to use your
+package easily.
+
+## Why not using ReadTheDocs or GitBook?
+
+ReadTheDocs and GitBook are well established solutions for documentation, but they don't provide automatic
+compilation and evaluation of Haskell code. This is error prone and can lead to confuse your users.
+
+Tintin is tightly integrated with Haskell and its tools, helping you to document your library in a very easy
+way.
+
+# Projects using Tintin
+
+Please submit a PR if you are using Tintin!
+
+|λ|λ|
+|-|-|
+|[Tintin](https://theam.github.io/tintin)| A softer alternative to Haddock |
+
+# Copyright
+
+tintin - Copyright © 2018 [Theam](http://theam.io)
+
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,32 @@
+module Main where
+
+import Universum
+
+import Options.Generic
+
+import Tintin
+import Tintin.Core
+import qualified Tintin.Capabilities.Logging as Logging
+import qualified Tintin.Capabilities.Filesystem as Filesystem
+import qualified Tintin.Capabilities.Process as Process
+
+
+data Options = Options
+  { outputDirectory :: Maybe Text
+  , verbose :: Bool
+  }
+
+deriving instance Generic Options
+instance ParseRecord Options
+
+
+main :: IO ()
+main = do
+  opts <- getRecord "Tintin, the tutorial website generator"
+  let outputDir = fromMaybe ".stack-work/tintin/rendered/" (outputDirectory opts)
+  let logger     = if verbose opts
+                   then Logging.stdOut
+                   else Logging.mute
+  let filesystem = Filesystem.local
+  let process    = Process.local
+  runEffects ( runApp $ OutputDirectory outputDir ) (logger, filesystem, process)
diff --git a/src/Tintin.hs b/src/Tintin.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin.hs
@@ -0,0 +1,64 @@
+module Tintin
+  ( runApp
+  )
+where
+
+import Tintin.Core
+
+import qualified Tintin.Capabilities.Logging as Logging
+import qualified Tintin.Capabilities.Filesystem as Filesystem
+import qualified Tintin.Capabilities.Process as Process
+import qualified Tintin.Parse as Parse
+import qualified Tintin.Render as Render
+import qualified Tintin.ConfigurationLoading as ConfigurationLoading
+
+
+runApp :: ( Has Logging.Capability eff
+          , Has Filesystem.Capability eff
+          , Has Process.Capability eff
+          )
+       => OutputDirectory
+       -> Effectful eff ()
+runApp outputDirectory = do
+  cleanUp outputDirectory
+  docDir    <- getDocumentationDirectory
+  filenames <- getDocumentationFilenames docDir
+
+  Parse.docs docDir filenames
+   |>> Render.perform
+   |>> ConfigurationLoading.loadInfo
+   |>> Render.writeOutput outputDirectory
+
+
+cleanUp :: ( Has Logging.Capability eff
+           , Has Filesystem.Capability eff
+           )
+        => OutputDirectory
+        -> Effectful eff ()
+cleanUp (OutputDirectory p) = do
+  Logging.debug "Cleaning output directory"
+  Filesystem.deleteIfExists (Filesystem.Path p)
+
+
+
+getDocumentationFilenames :: ( Has Logging.Capability eff
+                             , Has Filesystem.Capability eff
+                             )
+                          => DocumentationDirectory
+                          -> Effectful eff [Filesystem.Path]
+getDocumentationFilenames (DocumentationDirectory docDir) = do
+  Logging.debug ( "Reading documentation files at " <> docDir )
+  Filesystem.Path docDir
+   |>  Filesystem.list
+   |$> Filesystem.getPathsWith (Filesystem.Extension ".md")
+
+
+
+getDocumentationDirectory :: Has Filesystem.Capability eff
+                          => Effectful eff DocumentationDirectory
+getDocumentationDirectory = do
+  Filesystem.Path currentDir <- Filesystem.currentDirectory
+  return ( DocumentationDirectory $ currentDir <> "/doc/" )
+
+
+
diff --git a/src/Tintin/Capabilities.hs b/src/Tintin/Capabilities.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Capabilities.hs
@@ -0,0 +1,92 @@
+
+module Tintin.Capabilities
+  (
+    {-|
+'liftCapability' allows executing an 'IO' action stored in
+a Capability value in the 'Effectful' monad.
+
+It is intended to be used within the Capability definition
+module, not outside.
+
+@
+data MyCapability = MyCapability
+  { _myAction :: IO ()
+  }
+
+myAction :: Has MyCapability e
+         => Effectful e ()
+myAction = liftCapability _myAction
+@
+    -}
+    liftCapability
+  )
+where
+
+import Tintin.Core
+
+
+{-
+This class should NOT be instantiated outside of this module,
+as it serves to make the code more clean outside.
+
+It allows us to do a
+
+liftCapability f
+
+instead of
+
+liftCapability $ \capability -> f capability foo bar quux
+
+where foo, bar, and quux are the remaining arguments.
+
+These two instances allow the compiler to recurse on the arguments
+and typecheck that the application is correct.
+
+See: https://stackoverflow.com/q/50353294/3847023
+-}
+
+class LiftableWithCapability v where
+
+  -- v must have an execution type like 'IO a'
+  type Execution v :: *
+
+  -- also, v must have a context available, to get the Capability from
+  type Context v :: *
+
+  -- We provide liftCapability, that ensures that it exists in the Context of v
+  -- and, after passing a function that takes the capability and executes it in the
+  -- Execution type of V
+  liftCapability :: Has capability (Context v) => (capability -> Execution v) -> v
+
+
+
+-- We instantiate it for our Effectful type, which is an alias for ReaderT e IO a
+instance LiftableWithCapability (ReaderT e IO a) where
+
+  -- The execution type of that is 'IO a'
+  type Execution (ReaderT e IO a) = IO a
+
+  -- And the context is e
+  type Context (ReaderT e IO a) = e
+
+  -- liftCapability just grabs the capability and calls the function on it
+  liftCapability f = do
+    capability <- asks getter
+    lift ( f capability )
+
+
+-- a function that takes some parameter a and returns v also can be lifted
+instance LiftableWithCapability v => LiftableWithCapability (a -> v) where
+
+  -- The execution type of the function, is just the Execution of the return type
+  type Execution (a -> v) = a -> Execution v
+
+  -- Same with the Context, the Context of the return type
+  type Context (a -> v) = Context v
+
+  -- We flip the arguments of f, so the capability record stays in the last place
+  -- and we can recurse over it, until we reach the definition from before.
+  --
+  -- It is the same as doing 'liftCapability $ \x -> f x b'
+  liftCapability f = liftCapability . flip f
+
diff --git a/src/Tintin/Capabilities/Filesystem.hs b/src/Tintin/Capabilities/Filesystem.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Capabilities/Filesystem.hs
@@ -0,0 +1,112 @@
+module Tintin.Capabilities.Filesystem
+  ( Capability
+  , Path(..)
+  , Extension(..)
+
+  , local
+  , deleteIfExists
+  , list
+  , currentDirectory
+  , readFile
+  , makeDirectory
+  , writeFile
+  , doesExist
+
+  , getPathsWith
+  )
+where
+
+import Tintin.Core hiding (list, local, readFile, writeFile)
+import qualified Tintin.Core as Core
+import Tintin.Capabilities
+
+import qualified Data.Text as Text
+
+
+newtype Path      = Path Text deriving ( Show, Ord, Eq )
+newtype Extension = Extension Text
+
+
+data Capability = Capability
+  { _deleteIfExists   :: Path -> IO ()
+  , _list             :: Path -> IO [Path]
+  , _currentDirectory :: IO Path
+  , _readFile         :: Path -> IO Text
+  , _makeDirectory    :: Path -> IO ()
+  , _writeFile        :: Path -> Text -> IO ()
+  , _doesExist        :: Path -> IO Bool
+  }
+
+
+local :: Capability
+local =
+  Capability {..}
+ where
+  _deleteIfExists (Path p) = do
+    let pathToDelete = toString p
+    pathExists <- doesDirectoryExist pathToDelete
+    when pathExists (removeDirectoryRecursive pathToDelete)
+
+  _list (Path p) = do
+    files <- listDirectory (toString p)
+    return $ sort (Path . toText <$> files)
+
+  _currentDirectory =
+    getCurrentDirectory
+    |$> toText
+    |$> Path
+
+  _readFile (Path p) = Core.readFile (toString p)
+
+  _makeDirectory (Path p) = createDirectoryIfMissing True (toString p)
+
+  _writeFile (Path p) = Core.writeFile (toString p)
+
+  _doesExist (Path p) = doesPathExist (toString p)
+
+
+deleteIfExists :: Has Capability eff
+               => Path
+               -> Effectful eff ()
+deleteIfExists = liftCapability _deleteIfExists
+
+
+list :: Has Capability eff
+     => Path
+     -> Effectful eff [Path]
+list = liftCapability _list
+
+
+currentDirectory :: Has Capability eff
+                 => Effectful eff Path
+currentDirectory = liftCapability _currentDirectory
+
+
+readFile :: Has Capability eff
+         => Path
+         -> Effectful eff Text
+readFile = liftCapability _readFile
+
+
+makeDirectory :: Has Capability eff
+              => Path
+              -> Effectful eff ()
+makeDirectory = liftCapability _makeDirectory
+
+
+writeFile :: Has Capability eff
+          => Path
+          -> Text
+          -> Effectful eff ()
+writeFile = liftCapability _writeFile
+
+
+doesExist :: Has Capability eff
+          => Path
+          -> Effectful eff Bool
+doesExist = liftCapability _doesExist
+
+
+getPathsWith :: Extension -> [Path] -> [Path]
+getPathsWith (Extension e) =
+  filter (\(Path fn) -> e `Text.isSuffixOf` fn)
diff --git a/src/Tintin/Capabilities/Logging.hs b/src/Tintin/Capabilities/Logging.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Capabilities/Logging.hs
@@ -0,0 +1,70 @@
+module Tintin.Capabilities.Logging
+  ( Capability
+  , log
+  , err
+  , debug
+
+  , stdOut
+  , mute
+  )
+where
+
+import Tintin.Core
+import Tintin.Capabilities
+
+
+{- |
+Capability for our context to be able to log from any place where it is available:
+
+@
+foo :: Has Logging.Capability e
+    => Effectful e ()
+foo = do
+  log "Something happened :("
+  ...
+@
+-}
+data Capability = Capability
+  { _log   :: Text -> IO ()
+  , _err   :: Text -> IO ()
+  , _debug :: Text -> IO ()
+  }
+
+
+-- | Logs a text message using the available logger
+log :: Has Capability e
+    => Text
+    -> Effectful e ()
+log = liftCapability _log
+
+
+-- | Logs an error message using the available logger
+err :: Has Capability e
+    => Text
+    -> Effectful e ()
+err = liftCapability _err
+
+
+-- | Logs a debug message using the available logger
+debug :: Has Capability e
+      => Text
+      -> Effectful e ()
+debug = liftCapability _debug
+
+
+-- | Creates a logger that just prints things to STDOUT
+stdOut :: Capability
+stdOut = Capability
+  { _log   = putTextLn
+  , _err   = putTextLn . ("[ERROR] - " <>)
+  , _debug = putTextLn . ("[DEBUG] - " <>)
+  }
+
+
+-- | Creates a logger that does literally nothing
+mute :: Capability
+mute = Capability
+  { _log = return . const ()
+  , _debug = return . const ()
+  , _err = return . const ()
+  }
diff --git a/src/Tintin/Capabilities/Process.hs b/src/Tintin/Capabilities/Process.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Capabilities/Process.hs
@@ -0,0 +1,44 @@
+module Tintin.Capabilities.Process
+  ( Capability
+  , CommandName(..)
+  , Arguments(..)
+  , StdOut(..)
+  , StdErr(..)
+
+  , local
+  , read
+  )
+where
+
+import Tintin.Core hiding (stdout, stderr, local)
+import Tintin.Capabilities
+
+import System.Exit (ExitCode(..))
+
+
+newtype CommandName = CommandName Text
+newtype Arguments   = Arguments [Text]
+newtype StdOut      = StdOut Text
+newtype StdErr      = StdErr Text
+
+newtype Capability = Capability
+  { _read :: CommandName -> Arguments -> IO (Either StdErr StdOut) 
+  }
+
+
+local :: Capability
+local =
+  Capability {..}
+ where
+  _read (CommandName cn) (Arguments args) = do
+    result <- readProcessWithExitCode (toString cn) (toString <$> args) ""
+    case result of
+      (ExitSuccess, stdout, _) -> return (Right . StdOut $ toText stdout)
+      (_, _, stderr) -> return (Left  . StdErr $ toText stderr)
+
+
+read :: Has Capability eff
+           => CommandName
+           -> Arguments
+           -> Effectful eff (Either StdErr StdOut)
+read = liftCapability _read
diff --git a/src/Tintin/ConfigurationLoading.hs b/src/Tintin/ConfigurationLoading.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/ConfigurationLoading.hs
@@ -0,0 +1,106 @@
+module Tintin.ConfigurationLoading
+  ( loadInfo )
+where
+
+import Tintin.Core
+import qualified Tintin.Errors as Errors
+import qualified Tintin.Capabilities.Logging as Logging
+import qualified Tintin.Capabilities.Filesystem as Filesystem
+import qualified Tintin.Domain.HtmlFile as HtmlFile
+import qualified Tintin.Domain.Project as Project
+
+import qualified Data.Text as Text
+import Universum.Unsafe (fromJust)
+import Text.Read (read)
+
+import qualified Universum.Debug as Debug
+
+loadInfo :: ( Has Logging.Capability eff
+            , Has Filesystem.Capability eff
+            )
+         => [HtmlFile.Value]
+         -> Effectful eff Project.Info
+loadInfo htmlFiles = do
+  let pages = htmlFiles
+              |> map (\HtmlFile.Value {..} -> Project.Page title content filename)
+  Filesystem.Path currentDir <- Filesystem.currentDirectory
+  files <- Filesystem.list (Filesystem.Path currentDir)
+  let packageYamlFile = find isPackageYaml files
+  let cabalFile       = find isCabalFile files
+  case packageYamlFile <|> cabalFile of
+    Nothing -> do
+      Errors.showAndDie ["No package.yaml or *.cabal file found."]
+      error ""
+
+    Just p -> do
+      let tintinPath = Filesystem.Path $ currentDir <> "/.tintin.yml"
+      Logging.debug "Reading project info"
+      projectInfoFile <- Filesystem.readFile p
+      tintinExists    <- Filesystem.doesExist tintinPath
+      unless tintinExists $
+        Filesystem.writeFile tintinPath "color: blue\n"
+      tintinFile <- Filesystem.readFile tintinPath
+      let
+        projectName     = projectInfoFile |> getFieldValue "name"
+        projectSynopsis = projectInfoFile |> getFieldValue "synopsis"
+        projectGithub   = (projectInfoFile |> getFieldValue "github")
+                          <|> (projectInfoFile |> getFieldValue "location")
+        projectAuthor   = projectGithub |$> getAuthor
+        tintinColor     = tintinFile |> getFieldValue "color"
+        tintinLogo      = tintinFile |> getFieldValue "logo"
+      when (isNothing projectName) (Errors.showAndDie ["Project must have a name. Please set it in package.yaml or *.cabal."])
+      when (isNothing projectSynopsis) (Errors.showAndDie ["Project must have a synopsis. Please set it in package.yaml or *.cabal."])
+      when (isNothing projectGithub) (Errors.showAndDie ["Project must be hosted in a Github repository. Please set it in package.yaml or *.cabal."])
+      when (isNothing tintinColor)
+        (Errors.showAndDie ["Tintin usually generates a .tintin.yml file with a color configuration. Maybe you don't have enough permissions?\
+                           \\n\nTry creating .tintin.yml and adding color:blue to it."])
+      return Project.Info
+        { name = fromJust projectName
+        , synopsis = fromJust projectSynopsis
+        , githubLink = parseGithubUrl $ fromJust projectGithub
+        , githubAuthor = fromJust projectAuthor
+        , color = makeColor $ fromJust tintinColor
+        , logoUrl = tintinLogo
+        , pages = pages
+        }
+
+ where
+  isPackageYaml (Filesystem.Path p) =
+    p == "package.yaml"
+
+  isCabalFile   (Filesystem.Path p) =
+    ".cabal" `Text.isInfixOf` p
+
+  makeColor :: Text -> Project.Color
+  makeColor txt =
+    let capitalLetter = txt
+                        |> Text.head
+                        |> Text.singleton
+                        |> Text.toUpper
+        restOfText    = txt
+                        |> Text.tail
+    in  (capitalLetter <> restOfText)
+         |> toString
+         |> read
+
+  getFieldValue field txt = txt
+                          |> lines
+                          |> filter (\t -> field `Text.isPrefixOf` Text.strip t)
+                          |> safeHead
+                          |$> Text.strip
+                          |>> Text.stripPrefix (field <> ":")
+                          |$> Text.strip
+  getAuthor txt =
+    txt
+    |> (Text.stripPrefix "\"" >=> Text.stripSuffix "\"")
+    |> fromMaybe txt
+    |> Text.takeWhile (/= '/')
+
+  parseGithubUrl txt =
+    txt
+    |>  Text.stripPrefix "\""
+    |>> Text.stripSuffix "\""
+    |>  fromMaybe txt
+
+
+
diff --git a/src/Tintin/Core.hs b/src/Tintin/Core.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Core.hs
@@ -0,0 +1,45 @@
+module Tintin.Core
+  ( module Exported
+
+  , Effectful
+  , Pure
+
+  , (|>)
+  , (|$>)
+  , (|>>)
+
+  , runEffects
+  )
+where
+
+import Universum as Exported hiding (log)
+
+import Data.Has as Exported
+import System.Directory as Exported
+import System.IO.Temp as Exported
+import System.Process as Exported
+
+import Tintin.Domain as Exported
+
+
+
+type Effectful context result =
+  ReaderT context IO result
+
+runEffects :: Effectful context result
+           -> context
+           -> IO result
+runEffects = runReaderT
+
+type Pure context result =
+  Reader context result
+
+
+(|>) :: a -> (a -> b) -> b
+(|>) = (&)
+
+(|$>) :: Functor f => f a -> (a -> b) -> f b
+(|$>) = (<&>)
+
+(|>>) :: Monad m => m a -> (a -> m b) -> m b
+(|>>) = (>>=)
diff --git a/src/Tintin/Domain.hs b/src/Tintin/Domain.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Domain.hs
@@ -0,0 +1,8 @@
+module Tintin.Domain where
+
+import Universum
+
+
+newtype DocumentationDirectory = DocumentationDirectory Text
+newtype OutputDirectory        = OutputDirectory Text
+
diff --git a/src/Tintin/Domain/DocumentationFile.hs b/src/Tintin/Domain/DocumentationFile.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Domain/DocumentationFile.hs
@@ -0,0 +1,40 @@
+module Tintin.Domain.DocumentationFile where
+
+import Tintin.Core
+
+import qualified Data.Frontmatter as FMParser
+
+import Tintin.Domain.FrontMatter as FrontMatter
+
+
+newtype Filename   = Filename Text
+newtype ParseError = ParseError Text deriving Show
+
+data Value = Value
+  { filename    :: Text
+  , content     :: Text
+  , frontMatter :: FrontMatter
+  } deriving Show
+
+
+new :: Filename -> Text -> Either ParseError Value
+new (Filename filename) rawText =
+  case parse rawText of
+    FMParser.Fail _ _ err ->
+      Left ( ParseError $ "Parse error on "
+             <> filename
+             <> " no front matter found."
+           )
+
+    FMParser.Done source frontMatter ->
+      Right $ Value
+        { filename    = filename
+        , content     = decodeUtf8 @Text @ByteString source
+        , frontMatter = frontMatter
+        }
+ where
+  parse txt =
+    encodeUtf8 @Text @ByteString txt
+    |> FMParser.parseYamlFrontmatter
+
+
diff --git a/src/Tintin/Domain/FrontMatter.hs b/src/Tintin/Domain/FrontMatter.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Domain/FrontMatter.hs
@@ -0,0 +1,22 @@
+module Tintin.Domain.FrontMatter where
+
+import Tintin.Core
+
+import Data.Yaml
+
+
+data FrontMatter = FrontMatter
+  { title :: Text
+  , subtitle :: Maybe Text
+  , layout :: Maybe Text
+  } deriving Show
+
+
+instance FromJSON FrontMatter where
+  parseJSON (Object v) =
+    FrontMatter
+    <$> v .: "title"
+    <*> v .:? "subtitle"
+    <*> v .:? "layout"
+  parseJSON _ = fail "Expected object"
+
diff --git a/src/Tintin/Domain/HtmlFile.hs b/src/Tintin/Domain/HtmlFile.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Domain/HtmlFile.hs
@@ -0,0 +1,68 @@
+module Tintin.Domain.HtmlFile where
+
+import Tintin.Core
+import qualified Tintin.Capabilities.Filesystem as Filesystem
+import qualified Tintin.Capabilities.Process as Process
+import qualified Tintin.Domain.DocumentationFile as DocumentationFile
+import qualified Tintin.Domain.FrontMatter as FrontMatter
+
+import qualified Data.Text as Text
+
+
+newtype CompilationError = CompilationError Text deriving Show
+
+data Value = Value
+  { filename :: Text
+  , title    :: Text
+  , content  :: Text
+  }
+
+
+fromDocumentationFile :: DocumentationFile.Value
+                      -> Value
+fromDocumentationFile docfile =
+  DocumentationFile.content docfile
+   |> ("{-# OPTIONS_GHC -F -pgmF inlitpp #-}\n" <>)
+   |> Value (DocumentationFile.filename docfile) docTitle
+ where
+  docTitle = docfile |> DocumentationFile.frontMatter |> FrontMatter.title
+
+
+run :: ( Has Filesystem.Capability eff
+       , Has Process.Capability eff
+       )
+    => Value
+    -> Effectful eff (Either CompilationError Value)
+run Value {..} = do
+  Filesystem.Path currentDirectory <- Filesystem.currentDirectory
+  let tintinDir = currentDirectory <> "/.stack-work/tintin/"
+  let tempDir   = tintinDir <> "temp/"
+  let hsFilename = filename
+                   |> Text.breakOn ".md"
+                   |> fst
+                   |> (<> ".hs")
+                   |> (tempDir <>)
+  let htmlFilename = filename
+                     |> Text.breakOn ".md"
+                     |> fst
+                     |> (<> ".html")
+  Filesystem.deleteIfExists (Filesystem.Path tempDir)
+  Filesystem.makeDirectory (Filesystem.Path tempDir)
+  Filesystem.writeFile (Filesystem.Path hsFilename) content
+  result <- Process.read
+    (Process.CommandName "stack")
+    (Process.Arguments ["runghc", hsFilename, "--", "--no-inlit-wrap"])
+
+
+  case result of
+    Left  (Process.StdErr err) ->
+      return (Left $ CompilationError err)
+
+    Right (Process.StdOut msg) ->
+      return . Right $ Value
+        { filename = htmlFilename
+        , content  = msg
+        , title    = title
+        }
+
+
diff --git a/src/Tintin/Domain/Project.hs b/src/Tintin/Domain/Project.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Domain/Project.hs
@@ -0,0 +1,35 @@
+module Tintin.Domain.Project where
+
+import Tintin.Core
+
+data Color
+  = Purple
+  | LightGreen
+  | DarkGreen
+  | Blue
+  | DarkBlue
+  | Bronze
+  | DarkOrange
+  | LightOrange
+  | Red
+  | Grey
+  deriving (Show, Read)
+
+
+data Page = Page
+  { title    :: Text
+  , content  :: Text
+  , filename :: Text
+  }
+
+
+data Info = Info
+  { name         :: Text
+  , synopsis     :: Text
+  , color        :: Color
+  , githubLink   :: Text
+  , githubAuthor :: Text
+  , logoUrl      :: Maybe Text
+  , pages        :: [Page]
+  }
+
diff --git a/src/Tintin/Errors.hs b/src/Tintin/Errors.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Errors.hs
@@ -0,0 +1,19 @@
+module Tintin.Errors
+  ( showAndDie
+  )
+where
+
+import Tintin.Core
+import qualified Tintin.Capabilities.Logging as Logging
+
+
+showAndDie :: ( Has Logging.Capability eff
+              , Show error
+              )
+           => [error]
+           -> Effectful eff ()
+showAndDie errors = do
+    errors
+     |> mapM_ (Logging.err . show)
+    error "Errors found. Exiting."
+
diff --git a/src/Tintin/Html/Style.hs b/src/Tintin/Html/Style.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Html/Style.hs
@@ -0,0 +1,235 @@
+module Tintin.Html.Style where
+
+import Tintin.Core as Core hiding (( & ), rem, (|>))
+
+import Clay
+import qualified Clay.Media as Media
+import Clay.Selector
+
+style :: Text
+style = toText . render $ do
+  html ? do
+    height (pct 100)
+    minHeight (pct 100)
+
+  body ? do
+    height (pct 100)
+    minHeight (pct 100)
+    fontFamily ["IBM Plex Sans"] [ sansSerif ]
+    fontSize (em 1)
+    overflowX hidden
+
+  forM_ (zip [(0::Double)..] [h1, h2, h3]) $ \(n, x) -> x ? do
+    fontFamily ["Montserrat" ] [ sansSerif ]
+    fontWeight bold
+    fontSize (em (2.441 Core.** n))
+
+  h1 ? fontSize (em 2.441)
+  h2 ? fontSize (em 1.953)
+  h3 ? fontSize (em 1.563)
+
+  "#header-container" ? do
+    marginTop (rem 5)
+    marginBottom (rem 5)
+
+  ".cover-heading" ? do
+    fontSize (pct 800)
+    maxHeight (rem 6)
+    marginBottom (rem 1.563)
+
+  ".cover-container" ? do
+   backgroundColor (rgba 255 255 255 0.0)
+
+  ".watermark" ? do
+    position absolute
+    top (px 0)
+    left (px 0)
+    maxHeight (rem 1.2504)
+    marginTop (rem 1.2504)
+    marginLeft (rem 1.2504)
+
+  ".cover-heading-subtitle" ? do
+    fontSize (rem 1.953)
+
+  ".vertical-auto" ? do
+    marginTop auto
+    marginBottom auto
+
+  ".content" ? do
+    marginTop (pct 5)
+    marginBottom (pct 5)
+
+  "#wrapper" ? do
+    paddingLeft (px 0)
+    transition "all" (sec 0.5) ease (sec 0)
+    ".toggled" & do
+      paddingLeft (px 250)
+      "#sidebar-wrapper" ? do
+        width (px 250)
+      "#page-content-wrapper" & do
+        position absolute
+        marginRight (px (-250))
+
+  "#page-content-wrapper" ? do
+    width (pct 100)
+    position absolute
+    marginTop (rem 3)
+    padding (px 15) (px 15) (px 15) (px 15)
+    img ? do
+      maxWidth (pct 100)
+
+  "#sidebar-wrapper" ? do
+    zIndex 1000
+    position fixed
+    left (px 250)
+    width (px 0)
+    marginLeft (px (-250))
+    overflowY hidden
+    overflowX hidden
+    transition "all" (sec 0.5) ease (sec 0)
+
+
+  ".sidebar-nav" ? do
+    position absolute
+    top (px 0)
+    width (px 250)
+    margin (px 0) (px 0) (px 0) (px 0)
+    padding (px 0) (px 0) (px 0) (px 0)
+    listStyleType none
+    "li" ? do
+      textIndent (indent $ px 20)
+      lineHeight (px 40)
+      "a" ? do
+        display block
+        textDecoration none
+        fontWeight bold
+        ":hover" & do
+          textDecoration none
+          color white
+        ":active" & do
+          textDecoration none
+          color white
+        ":focus" & do
+          textDecoration none
+          color white
+
+  ".tintin-doc-topbar" ? do
+    height (rem 5)
+    a ? do
+      marginLeft (rem 1)
+      width (rem 1.5)
+      img ? do
+        Clay.filter (invert $ pct 70)
+
+  ".filter-gray" ? do
+    position relative
+    bottom (px ( -3 ))
+    marginLeft (rem 0.25)
+    marginRight (rem 1)
+    height (rem 1)
+    Clay.filter (brightness 0.75)
+
+  ".footer-theam" ? do
+    position relative
+    bottom (px $ 1)
+    marginLeft (rem $ -0.25)
+    height (rem 1.75)
+    Clay.filter (invert (pct 75))
+    -- Clay.filter (brightness 0.75)
+
+  ".tintin-doc-footer" ? do
+    bottom (px 0)
+    height (rem $ -15)
+    width (pct 100)
+    color (rgba 0 0 0 0.30)
+
+  ".main-container" ? do
+    minHeight (pct 100)
+    position relative
+
+  "#content" ? do
+    minHeight (pct 95)
+
+  ".tintin-generated-with" ? do
+    pass
+
+  ".sidebar-nav" |> ".sidebar-brand" ? do
+    height (rem 3)
+    fontSize (rem 2)
+    fontFamily ["Montserrat"] [sansSerif]
+    fontWeight bold
+    paddingTop (rem 2.5)
+    paddingBottom (rem 2.5)
+    marginBottom (rem 1.5)
+    img ? do
+      height (rem 1.5)
+
+  ".tintin-navbar" ? do
+    fontWeight bold
+    backgroundColor (rgba 255 255 255 0.15)
+
+  ".tintin-bg-70" ? do
+    backgroundColor (rgba 255 255 255 0.15)
+
+  forM colorNames $ \(colorName, colorValue) ->
+    (text $ ".tintin-bg-" <> colorName) ? do
+      backgroundColor colorValue
+
+  forM colorNames $ \(colorName, colorValue) ->
+    (text $ ".tintin-fg-" <> colorName) ? do
+      color colorValue
+
+  ".tintin-fg-active" ? do
+    color (rgba 255 255 255 1.0)
+
+  ".tintin-fg-disabled" ? do
+    color (rgba 255 255 255 0.35)
+
+  footer ? do
+    position relative
+    bottom (px 0)
+    left (px 0)
+    width (pct 100)
+    paddingTop (px 30)
+    paddingBottom (px 30)
+
+  ".container" ? do
+    maxWidth (rem 50)
+
+  query Media.screen [Media.minWidth (px 768)] $ do
+    "#wrapper" ? do
+      paddingLeft (px 0)
+      ".toggled" ? do
+        paddingLeft (px 250)
+        "#sidebar-wrapper" ? do
+          width (px 250)
+        "#page-content-wrapper" ? do
+          position relative
+          marginRight (px 0)
+
+    "#sidebar-wrapper" ? do
+      width (px 0)
+
+    "#page-content-wrapper" ? do
+      padding (px 20) (px 20) (px 20) (px 20)
+      position relative
+
+
+
+
+colorNames :: [(Text, Color)]
+colorNames =
+  [ ("black"      , "#1d1f21")
+  , ("white"      , "#f5f8f6")
+  , ("grey"       , "#4D4D4D")
+  , ("red"        , "#D30228")
+  , ("darkgreen"  , "#3C8B6A")
+  , ("lightgreen" , "#A4CB58")
+  , ("darkorange" , "#FF6602")
+  , ("lightorange", "#FAA73E")
+  , ("blue"       , "#94C1E8")
+  , ("darkblue"   , "#007C99")
+  , ("purple"     , "#9F76B4")
+  , ("bronze"     , "#A4A27A")
+  , ("darkgrey"   , "#282a2e")
+  ]
diff --git a/src/Tintin/Html/Templating.hs b/src/Tintin/Html/Templating.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Html/Templating.hs
@@ -0,0 +1,198 @@
+module Tintin.Html.Templating where
+
+import Lucid
+
+import Tintin.Core
+import qualified Tintin.Html.Style as Style
+import qualified Tintin.Domain.Project as Project
+
+import qualified Data.Text as Text
+
+
+asset :: Text -> Text
+asset txt = "https://s3-eu-west-1.amazonaws.com/worldwideapps/assets/" <> txt
+
+wrap :: Project.Info -> Project.Page -> Text
+wrap info page =
+  if (Project.filename page) == "index.html"
+  then wrapHome info page
+  else wrapPage info page
+
+
+wrapPage :: Project.Info -> Project.Page -> Text
+wrapPage info page = toText . renderText $ do
+  tintinHeader info page
+  body_ [class_ "h-100 tintin-fg-black tintin-bg-white"] $ do
+    div_ [id_ "main-container", class_ "h-100"] $ do
+      section_ [ id_ "content"] $ do
+        div_ [id_ "wrapper", class_ "toggled"] $ do
+          div_ [id_ "sidebar-wrapper", class_ $ "h-100 tintin-bg-" <> bgColorOf info] $ do
+            div_ [ class_ "h-100 tintin-bg-70"] ( p_ "" )
+            ul_ [ class_ "sidebar-nav"] $ do
+              li_ [ class_ $ "sidebar-brand d-flex tintin-bg-" <> bgColorOf info] $ do
+                a_ [href_ "index.html", class_ "align-self-center tintin-fg-white"] $ do
+                  case Project.logoUrl info of
+                    Nothing -> toHtml $ Project.name info
+                    Just url -> img_ [src_ url]
+              forM_ (filter (\p -> "index.html" /= Project.filename p ) (Project.pages info) ) $ \p -> do
+                li_ $ do
+                  let classes =
+                        if Project.title page == Project.title p
+                        then "tintin-fg-active"
+                        else "tintin-fg-disabled"
+                  a_ [href_ $ Project.filename p, class_ classes] $ do
+                    ( toHtml $ Project.title p )
+
+          nav_ [class_ "navbar navbar-expand-lg tintin-doc-topbar tintin-fg-white"] $ do
+            a_ [id_ "menu-open", href_ "#menu-toggle", class_ "d-none"] $
+              img_ [ src_ $ asset "menu.png", class_ "img-fluid" ]
+            a_ [id_ "menu-close", href_ "#menu-toggle"] $
+              img_ [ src_ $ asset "close.png", class_ "img-fluid" ]
+
+          div_ [id_ "page-content-wrapper"] $ do
+            div_ [class_ "container"] $ do
+              div_ [class_ "col"] $
+                div_ [ data_ "aos" "fade-left"
+                     , data_ "aos-duration" "800"
+                     , data_ "aos-once" "true"
+                     ] $
+                  toHtmlRaw $ Project.content page
+      div_ [class_ "tintin-doc-footer clear-fix"]
+        siteGenerated
+    tintinPostInit
+
+siteGenerated = do
+  div_ [class_ "float-right"] $ do
+    div_ [class_ "d-inline", style_ "float: left"] $ do
+      p_ [class_ ""] "Site generated with "
+    a_ [ style_ "float: left", href_ "https://theam.github.io/tintin"] $ do
+      img_ [ class_ "filter-gray", src_ $ asset "logo.svg" ]
+    div_ [class_ "clear-fix float-right"] $ do
+      span_ [style_ "float:left"] $ toHtmlRaw ("&mdash; &copy; 2018 " :: Text)
+      a_ [class_ "float:left", href_ "http://theam.io"] $
+        img_ [class_ "footer-theam", src_ "http://theam.io/logo_theam.png"]
+
+
+wrapHome :: Project.Info -> Project.Page -> Text
+wrapHome info page = toText . renderText $ do
+  tintinHeader info page
+  body_ [class_ "tintin-fg-black tintin-bg-white"] $ do
+
+    div_ [class_ $ "tintin-navigation tintin-bg-" <> bgColorOf info] $ do
+      div_ [class_ "cover-container d-flex p-3 mx-auto flex-column tintin-fg-white"] $ do
+        main_ [role_ "main", class_ "masthead mb-auto"] $ do
+          div_ [class_ "container"] $ do
+            div_ [class_ "row align-items-center"] $ do
+              div_ [class_ "col", id_ "header-container"] $ do
+                case Project.logoUrl info of
+                  Just url -> img_ [ src_ url, class_ "cover-heading" ]
+                  Nothing -> h1_ [class_ "cover-heading"] $ toHtml (Project.name info)
+                p_ [class_ "cover-heading-subtitle"] (toHtml $ Project.synopsis info)
+
+      navbar
+
+    section_ [ id_ "content"
+             , data_ "aos" "fade-up"
+             , data_ "aos-duration" "800"
+             , data_ "aos-once" "true"
+             ] $ do
+      div_ [class_ "container"] $ do
+        div_ [class_ "content"
+             ] $
+          div_ [] $ do
+            toHtmlRaw $ Project.content page
+    footer
+    tintinPostInit
+ where
+  navbar =
+    nav_ [ class_ "navbar navbar-expand-lg navbar-dark tintin-navbar"
+         , style_ "width: 100%;"
+         ] $ do
+      div_ [ class_ "container" ] $ do
+        div_ [class_ "collapse navbar-collapse", id_ "navbarSupportedContent"] $ do
+          ul_ [class_ "navbar-nav mr-auto"] $ do
+            li_ [class_ "nav-item active"] $
+              a_ [class_ "nav-link active", href_ "index.html"] "Home"
+            let (page:_) = filter (\x -> "index.html" /= Project.filename x ) (Project.pages info)
+            li_ [class_ "nav-item"] $
+              a_ [class_ "nav-link", href_ (Project.filename page)] "Docs"
+        div_ $
+          ul_ [class_ "navbar-nav mr-sm-2"] $
+            li_ [class_ "nav-item"] $
+              a_ [class_ "nav-link", href_ $ "https://github.com/" <> Project.githubLink info] "View on GitHub"
+
+  footer =
+    footer_ [ class_ "tintin-bg-darkgrey tintin-fg-white"] $
+      div_ [class_ "container"] $
+        div_ [class_ "row"] $ do
+          div_ [class_ "col"] $
+            p_ [class_ "tintin-fg-lightgrey"] $ do
+              "Developed by "
+              a_ [ href_ $ "https://github.com/" <> Project.githubAuthor info
+                 , class_ $ "tintin-fg-" <> bgColorOf info
+                 ] (toHtml $ Project.githubAuthor info)
+          div_ [class_ "col", style_ ""] $ do
+            siteGenerated
+
+
+tintinHeader :: Project.Info -> Project.Page -> Html ()
+tintinHeader info@Project.Info {..} Project.Page {..} =
+  head_ $ do
+    title_ ( toHtml $ name <> " - " <> title )
+    link_ [ rel_ "stylesheet"
+          , href_ "https://cdn.rawgit.com/michalsnik/aos/2.1.1/dist/aos.css"
+          ]
+    link_ [ rel_ "stylesheet"
+          , href_ "https://fonts.googleapis.com/css?family=IBM+Plex+Sans|Montserrat:500"
+          ]
+    link_ [ rel_ "stylesheet"
+          , href_ "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
+          ]
+    link_ [ rel_ "stylesheet"
+          , href_ "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/tomorrow-night.min.css"
+          ]
+    link_ [ rel_ "shortcut icon"
+          , href_ ( asset $ "favicon-"
+                  <> bgColorOf info
+                  <> ".ico"
+                  )
+          ]
+    style_ Style.style
+
+
+tintinPostInit :: Html ()
+tintinPostInit = do
+  script_ [ src_ "https://code.jquery.com/jquery-3.2.1.slim.min.js" ] ( "" :: Text )
+  script_ [ src_ "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"] ( "" :: Text )
+  script_ [ src_ "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"] ( "" :: Text )
+  script_ [ src_ "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"] ( "" :: Text )
+  script_ [ src_ "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/haskell.min.js"] ("" :: Text)
+  script_ [ src_ "https://cdn.rawgit.com/icons8/bower-webicon/v0.10.7/jquery-webicon.min.js" ] ( "" :: Text )
+  script_ "$(function() { AOS.init(); })"
+  script_ [ src_ "https://cdn.rawgit.com/michalsnik/aos/2.1.1/dist/aos.js"] ("" :: Text)
+  script_ [ src_ "https://cdnjs.cloudflare.com/ajax/libs/animejs/2.2.0/anime.min.js" ] ("" :: Text)
+  script_ "hljs.initHighlightingOnLoad()"
+  script_ "$(function () {$(\"#menu-open\").click(function(e) {\
+        \e.preventDefault();\
+        \$(\"#wrapper\").toggleClass(\"toggled\");\
+        \anime({targets: \"#menu-open img\", scale: 0.125, rotate: '0deg'});\
+        \$(\"#menu-close\").toggleClass(\"d-none\");\
+        \$(\"#menu-open\").toggleClass(\"d-none\");\
+        \anime({targets: \"#menu-close img\", scale: 1, rotate: '180deg'});\
+    \})});"
+  script_ "$(function () {$(\"#menu-close\").click(function(e) {\
+        \e.preventDefault();\
+        \$(\"#wrapper\").toggleClass(\"toggled\");\
+        \anime({targets: \"#menu-close img\", scale: 0.125, rotate: '0deg'});\
+        \$(\"#menu-close\").toggleClass(\"d-none\");\
+        \$(\"#menu-open\").toggleClass(\"d-none\");\
+        \anime({targets: \"#menu-open img\", scale: 1, rotate: '180deg'});\
+    \})});"
+
+
+bgColorOf :: Project.Info -> Text
+bgColorOf info =
+  Project.color info
+  |> show
+  |> Text.toLower
+
diff --git a/src/Tintin/Parse.hs b/src/Tintin/Parse.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Parse.hs
@@ -0,0 +1,39 @@
+module Tintin.Parse
+  ( docs )
+where
+
+import Tintin.Core
+import qualified Tintin.Capabilities.Logging as Logging
+import qualified Tintin.Capabilities.Filesystem as Filesystem
+import qualified Tintin.Domain.DocumentationFile as DocumentationFile
+import qualified Tintin.Errors as Errors
+
+
+docs :: ( Has Logging.Capability eff
+        , Has Filesystem.Capability eff
+        )
+     => DocumentationDirectory
+     -> [Filesystem.Path]
+     -> Effectful eff [DocumentationFile.Value]
+docs docDir filenames = do
+  Logging.debug "Parsing documentation"
+  (errors, docFiles) <- filenames
+                        |>  mapM (readAndParse docDir)
+                        |$> partitionEithers
+  unless (null errors) (Errors.showAndDie errors)
+  return docFiles
+
+
+readAndParse :: ( Has Logging.Capability eff
+                , Has Filesystem.Capability eff
+                )
+             => DocumentationDirectory
+             -> Filesystem.Path
+             -> Effectful eff (Either DocumentationFile.ParseError DocumentationFile.Value)
+readAndParse ( DocumentationDirectory d ) ( Filesystem.Path f ) = do
+  contents <- Filesystem.readFile ( Filesystem.Path $ d <> "/" <> f)
+  return $ DocumentationFile.new (DocumentationFile.Filename f) contents
+
+
+
+
diff --git a/src/Tintin/Render.hs b/src/Tintin/Render.hs
new file mode 100644
--- /dev/null
+++ b/src/Tintin/Render.hs
@@ -0,0 +1,45 @@
+module Tintin.Render where
+
+import Tintin.Core
+import Tintin.Capabilities.Logging as Logging
+import Tintin.Capabilities.Filesystem as Filesystem
+import Tintin.Capabilities.Process as Process
+import Tintin.Domain.HtmlFile as HtmlFile
+import Tintin.Domain.DocumentationFile as DocumentationFile
+import Tintin.Domain.Project as Project
+import Tintin.Html.Templating as Templating
+import Tintin.Errors as Errors
+
+import qualified Data.Text as Text
+
+perform :: ( Has Logging.Capability eff
+           , Has Filesystem.Capability eff
+           , Has Process.Capability eff
+           )
+        => [DocumentationFile.Value]
+        -> Effectful eff [HtmlFile.Value]
+perform docFiles = do
+  Logging.debug "Rendering"
+  (errors, htmlFiles) <- docFiles
+                         |>  map  HtmlFile.fromDocumentationFile
+                         |>  mapM HtmlFile.run
+                         |$> partitionEithers
+  unless (null errors) (Errors.showAndDie errors)
+  return htmlFiles
+
+
+writeOutput :: ( Has Logging.Capability eff
+               , Has Filesystem.Capability eff
+               )
+            => OutputDirectory
+            -> Project.Info
+            -> Effectful eff ()
+writeOutput (OutputDirectory od) info = do
+  Filesystem.makeDirectory (Filesystem.Path od)
+  Logging.debug "Writing HTML output"
+  forM_ (Project.pages info) $ \page -> do
+    let newContent = Templating.wrap info page
+    let slash = if "/" `Text.isSuffixOf` od then "" else "/"
+    Filesystem.writeFile (Filesystem.Path $ od <> slash <> Project.filename page) newContent
+
+
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,4 @@
+import Tintin.Core
+
+main :: IO ()
+main = putTextLn "Test suite not yet implemented"
diff --git a/tintin.cabal b/tintin.cabal
new file mode 100644
--- /dev/null
+++ b/tintin.cabal
@@ -0,0 +1,96 @@
+-- This file has been generated from package.yaml by hpack version 0.27.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: cd2984d0babd6cb9da9ae174fd16b41f7dae8f9b6187282ae4278a91ceabc171
+
+name:           tintin
+version:        1.0.3
+synopsis:       A softer alternative to Haddock
+description:    Please see the website <https://theam.github.io/tintin>
+category:       Documentation
+homepage:       https://github.com/theam/tintin#readme
+bug-reports:    https://github.com/theam/tintin/issues
+author:         The Agile Monkeys
+maintainer:     hackers@theam.io
+copyright:      2018 Theam
+license:        Apache-2.0
+license-file:   LICENSE.md
+build-type:     Simple
+cabal-version:  >= 1.10
+
+extra-source-files:
+    ChangeLog.md
+    README.md
+
+source-repository head
+  type: git
+  location: https://github.com/theam/tintin
+
+library
+  exposed-modules:
+      Tintin
+      Tintin.Capabilities
+      Tintin.Capabilities.Filesystem
+      Tintin.Capabilities.Logging
+      Tintin.Capabilities.Process
+      Tintin.ConfigurationLoading
+      Tintin.Core
+      Tintin.Domain
+      Tintin.Domain.DocumentationFile
+      Tintin.Domain.FrontMatter
+      Tintin.Domain.HtmlFile
+      Tintin.Domain.Project
+      Tintin.Errors
+      Tintin.Html.Style
+      Tintin.Html.Templating
+      Tintin.Parse
+      Tintin.Render
+  other-modules:
+      Paths_tintin
+  hs-source-dirs:
+      src
+  default-extensions: DeriveGeneric RecordWildCards OverloadedStrings DataKinds TypeOperators NoImplicitPrelude StandaloneDeriving TypeApplications KindSignatures FlexibleContexts FlexibleInstances MultiParamTypeClasses TypeFamilies
+  build-depends:
+      base >=4.7 && <5
+    , clay
+    , data-has
+    , directory
+    , frontmatter
+    , inliterate
+    , lucid
+    , process
+    , temporary
+    , text
+    , universum
+    , yaml
+  default-language: Haskell2010
+
+executable tintin
+  main-is: Main.hs
+  other-modules:
+      Paths_tintin
+  hs-source-dirs:
+      app
+  default-extensions: DeriveGeneric RecordWildCards OverloadedStrings DataKinds TypeOperators NoImplicitPrelude StandaloneDeriving TypeApplications KindSignatures FlexibleContexts FlexibleInstances MultiParamTypeClasses TypeFamilies
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , optparse-generic
+    , tintin
+    , universum
+  default-language: Haskell2010
+
+test-suite tintin-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_tintin
+  hs-source-dirs:
+      test
+  default-extensions: DeriveGeneric RecordWildCards OverloadedStrings DataKinds TypeOperators NoImplicitPrelude StandaloneDeriving TypeApplications KindSignatures FlexibleContexts FlexibleInstances MultiParamTypeClasses TypeFamilies
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , tintin
+  default-language: Haskell2010
