diff --git a/csv-nptools.cabal b/csv-nptools.cabal
--- a/csv-nptools.cabal
+++ b/csv-nptools.cabal
@@ -1,6 +1,6 @@
 Name:           csv-nptools
 Cabal-Version:  >=1.4
-Version:        0.2.0
+Version:        0.3.0
 License:        GPL
 Copyright:      (c) Nicolas Pouillard
 Author:         Nicolas Pouillard
@@ -11,10 +11,10 @@
 Stability:      Experimental
 Build-Type:     Simple
 
---executable csv2html
---    main-is: csv2html.hs
---    Build-depends: base>=3&&<5, html, tabular, csv
---    ghc-options: -Wall -Odph
+executable csv2html
+    main-is: csv2html.hs
+    Build-depends: base>=3&&<5, html, tabular, csv
+    ghc-options: -Wall -Odph
 
 executable csv2json
     main-is: csv2json.hs
diff --git a/csv2html.hs b/csv2html.hs
new file mode 100644
--- /dev/null
+++ b/csv2html.hs
@@ -0,0 +1,30 @@
+import Text.Tabular
+import qualified Text.Tabular.Html as Tab
+import Text.CSV
+import qualified Text.Html as H
+import System.Environment
+
+{- <<< -}
+simpleHeader :: Properties -> [a] -> Header a
+simpleHeader p = Group p . map Header
+
+enumHeaderFromTo :: Enum a => Properties -> a -> a -> Header a
+enumHeaderFromTo p from to = Group p $ map Header [from..to]
+
+simpleTable :: Properties -> Properties -> [[String]] -> Table Int String String
+simpleTable _  _  [] = error "no header"
+simpleTable ph pv (hs:tbl) = Table rh ch tbl
+  where height = length tbl
+        rh = enumHeaderFromTo ph 1 height
+        ch = simpleHeader pv hs
+{- >>> -}
+
+main :: IO ()
+main = putStr . H.renderHtml . layout . tabular2html . csv2tabular =<< parse =<< getArgs
+
+  where tabular2html  = Tab.render (H.toHtml . show) H.toHtml H.toHtml
+        parse [arg]   = either (fail . show) return =<< parseCSVFromFile arg
+        parse _       = fail "Usage: csv2html <file.csv>"
+        csv2tabular   = simpleTable SingleLine SingleLine
+        layout b      = H.header (charset H.+++ Tab.css Tab.defaultCss) H.+++ H.body b
+        charset       = H.meta H.! [H.httpequiv "Content-type", H.content "text/html; charset=UTF-8"]
