diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,1 +1,15 @@
-# awesome-project-name
+# Bulmex, reflex infused with bulma
+
+With a bunch of helper functions such as making server side 
+rendering easier.
+See related [blogpost](https://jappieklooster.nl/reflex-server-side-html-rendering.html).
+
+Reflex: 
+
+- core: https://hackage.haskell.org/package/reflex
+- DOM: https://hackage.haskell.org/package/reflex-dom-core
+
+CSS:
+
+- Bulma: https://bulma.io/
+- Baloon: https://kazzkiq.github.io/balloon.css/
diff --git a/bulmex.cabal b/bulmex.cabal
--- a/bulmex.cabal
+++ b/bulmex.cabal
@@ -2,12 +2,12 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 2b99255d36d075b833dc8c72472ac37a01a82e94c211ebc8955bb3075e4f5d12
+-- hash: ff65f5447cccce6ee9bed9f2913c5cfb43a800f93be78f0fc411b1c89f46ab8b
 
 name:           bulmex
-version:        1.0.0
+version:        2.0.0
 synopsis:       Reflex infused with bulma (css)
-description:    Please see the README on GitHub at <https://github.com/githubuser/awesome-project-name#readme>
+description:    Reflex infused with bulma, With a bunch of helper functions for making server side rendering easier. See related [blogpost](https://jappieklooster.nl/reflex-server-side-html-rendering.html).
 category:       Web
 homepage:       https://github.com/jappeace/bulmex#readme
 bug-reports:    https://github.com/jappeace/bulmex/issues
@@ -32,10 +32,14 @@
       Reflex.Bulmex.Event
       Reflex.Bulmex.Html
       Reflex.Bulmex.Input.Polymorphic
-      Reflex.Bulmex.Load
-      Reflex.Bulmex.PreventDefault
       Reflex.Bulmex.Space
       Reflex.Bulmex.Tag
+      Reflex.Bulmex.Tag.Abstract
+      Reflex.Bulmex.Tag.Bulma
+      Reflex.Bulmex.Tag.Hide
+      Reflex.Bulmex.Tag.Icon
+      Reflex.Bulmex.Tag.Odd
+      Reflex.Bulmex.Tag.Tooltip
   other-modules:
       Paths_bulmex
   hs-source-dirs:
diff --git a/src/Reflex/Bulmex/Event.hs b/src/Reflex/Bulmex/Event.hs
--- a/src/Reflex/Bulmex/Event.hs
+++ b/src/Reflex/Bulmex/Event.hs
@@ -1,5 +1,6 @@
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE RankNTypes       #-}
+{-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
 
 -- | Helper functions for dealing with events
 module Reflex.Bulmex.Event where
@@ -7,6 +8,7 @@
 import           Control.Applicative      (empty)
 import           Control.Monad            (void)
 import           Control.Monad.IO.Class   (MonadIO)
+import qualified Data.Text                as Text
 import           Data.Time.Clock          (NominalDiffTime)
 import           Data.Witherable
 import           Reflex
@@ -83,3 +85,6 @@
   delayed <- delay time event
   holdEvent defVal (leftmost [pure <$> event, empty <$ delayed]) $
     maybe (pure defVal) monadFunc
+
+evtText :: (Dom.DomBuilder t m, PostBuild t m, MonadHold t m) => Event t Text.Text -> m ()
+evtText evt = Dom.dynText =<< holdDyn "" evt
diff --git a/src/Reflex/Bulmex/Html.hs b/src/Reflex/Bulmex/Html.hs
--- a/src/Reflex/Bulmex/Html.hs
+++ b/src/Reflex/Bulmex/Html.hs
@@ -7,7 +7,8 @@
 
 -- | A root for an app,
 --   usefull for server side html rendering.
---   has a neat api for the head tag, use this if that api your needs.
+--   has a neat api for the head tag, use this if that api suits your needs.
+--   For more info see blogpost: https://jappieklooster.nl/reflex-server-side-html-rendering.html
 module Reflex.Bulmex.Html
   ( htmlWidget
   -- * Head tag stuff
@@ -21,20 +22,43 @@
   -- * Defaults
   , defScript
   , defSettings
+  -- * Load
+  , writeReadDom
   ) where
 
 import           Control.Lens
-import           Control.Monad            (void)
-import           Data.Foldable            (traverse_)
-import qualified Data.Map.Strict          as Map
-import qualified Data.Text                as Text
-import           GHC.Generics             (Generic)
+import           Control.Monad                        (void)
+import           Control.Monad                        (join)
+import           Data.Aeson
+import           Data.Aeson.Text
+import qualified Data.ByteString.Lazy                 as LBS
+import           Data.Foldable                        (traverse_)
+import qualified Data.Map.Strict                      as Map
+import           Data.Maybe
+import qualified Data.Text                            as Text
+import           Data.Text.Encoding
+import qualified Data.Text.Lazy                       as LText
+import           GHC.Generics                         (Generic)
+import           JSDOM
+import           JSDOM.Generated.Element
+import           JSDOM.Generated.NonElementParentNode
 import           Network.URI
-import qualified Reflex.Dom.Builder.Class as Dom
-import qualified Reflex.Dom.Widget        as Dom
+import           Reflex
+import qualified Reflex.Dom.Builder.Class             as Dom
+import qualified Reflex.Dom.Prerender                 as Dom
+import qualified Reflex.Dom.Widget                    as Dom
 
 -- | Adds the core html tags.
 --   we already know most of the head.
+--
+-- >  <html>
+-- >    <head>
+-- >     'HeadSettings' <!-- provided settings --!>
+-- >    </head>
+-- >    <body>
+-- >    'm a' <!-- provided monad --!>
+-- >    </body>
+-- >  </html>
 htmlWidget :: (Dom.DomBuilder t m) => HeadSettings -> m a -> m a
 htmlWidget settings content =
   Dom.el "html" $ do
@@ -108,3 +132,25 @@
          (Map.fromList -- bulmo
             [("rel", "stylesheet"), ("href", href)])
          Dom.blank) $ settings ^.. head_css . traversed . to (Text.pack . show)
+
+-- | Insert an encodable in the document body,
+--   in case of the server side rendering we encode it as script tag with jsonval,
+--   in case of ghcjsdom we read the value from that script tag
+--   first arg is the idname to connect the two up (has to be uniq for a doc)
+writeReadDom :: (FromJSON a, ToJSON a, Dom.DomBuilder t m, Dom.Prerender js t m) =>
+  Text.Text -> a -> m (Dynamic t a)
+writeReadDom comelid serverState =
+  Dom.prerender (do
+    Dom.elAttr "script" (Map.fromList [
+                            ("type", "application/json"),
+                            ("id", comelid)
+                            ]) $
+      Dom.text $ LText.toStrict $ encodeToLazyText serverState
+    pure serverState
+    ) $ do
+        mayDoc <- currentDocument
+        mayEl' <- sequence $ (getElementById <$> mayDoc) <*> pure comelid
+        mayInner  <- sequence $ getInnerHTML <$> join mayEl'
+        let result = (join $ decode . LBS.fromStrict .  encodeUtf8 <$> mayInner)
+        pure $ fromMaybe serverState  -- TODO don't fail silently
+            result
diff --git a/src/Reflex/Bulmex/Load.hs b/src/Reflex/Bulmex/Load.hs
deleted file mode 100644
--- a/src/Reflex/Bulmex/Load.hs
+++ /dev/null
@@ -1,43 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
--- | Load functions
-module Reflex.Bulmex.Load where
-
-import           Control.Monad                        (join)
-import           Data.Aeson
-import           Data.Aeson.Text
-import qualified Data.ByteString.Lazy                 as LBS
-import qualified Data.Map                             as Map
-import           Data.Maybe
-import qualified Data.Text                            as Text
-import           Data.Text.Encoding
-import qualified Data.Text.Lazy                       as LText
-import           JSDOM
-import           JSDOM.Generated.Element
-import           JSDOM.Generated.NonElementParentNode
-import           Reflex
-import qualified Reflex.Dom.Builder.Class             as Dom
-import qualified Reflex.Dom.Prerender                 as Dom
-import qualified Reflex.Dom.Widget.Basic              as Dom
-
--- | Insert an encodable in the document body,
---   in case of the server side rendering we encode it as script tag with jsonval,
---   in case of ghcjsdom we read the value from that script tag
---   first arg is the idname to connect the two up (has to be uniq for a doc)
-writeReadDom :: (FromJSON a, ToJSON a, Dom.DomBuilder t m, Dom.Prerender js t m) =>
-  Text.Text -> a -> m (Dynamic t a)
-writeReadDom comelid serverState =
-  Dom.prerender (do
-    Dom.elAttr "script" (Map.fromList [
-                            ("type", "application/json"),
-                            ("id", comelid)
-                            ]) $
-      Dom.text $ LText.toStrict $ encodeToLazyText serverState
-    pure serverState
-    ) $ do
-        mayDoc <- currentDocument
-        mayEl' <- sequence $ (getElementById <$> mayDoc) <*> pure comelid
-        mayInner  <- sequence $ getInnerHTML <$> join mayEl'
-        let result = (join $ decode . LBS.fromStrict .  encodeUtf8 <$> mayInner)
-        pure $ fromMaybe serverState  -- TODO don't fail silently
-            result
diff --git a/src/Reflex/Bulmex/PreventDefault.hs b/src/Reflex/Bulmex/PreventDefault.hs
deleted file mode 100644
--- a/src/Reflex/Bulmex/PreventDefault.hs
+++ /dev/null
@@ -1,48 +0,0 @@
-{-# LANGUAGE RankNTypes          #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-
--- | ripped from https://gist.github.com/3noch/134b1ee7fa48c347be9d164c3fac4ef7
-module Reflex.Bulmex.PreventDefault where
-
-import           Control.Lens    ((%~), (.~))
-import           Data.Map        (Map)
-import           Data.Proxy      (Proxy (..))
-import           Data.Text       (Text)
-import           Reflex.Dom.Core
-
--- | Like 'elDynAttr'' but configures "prevent default" on the given event.
-elDynAttrPrevDef ::
-     forall a en m t. (DomBuilder t m, PostBuild t m)
-  => EventName en -- ^ Event on the element to configure with 'preventDefault'
-  -> Text -- ^ Element tag
-  -> Dynamic t (Map Text Text) -- ^ Element attributes
-  -> m a -- ^ Child of element
-  -> m (Element EventResult (DomBuilderSpace m) t, a) -- An element and the result of the child
-elDynAttrPrevDef ev =
-  elDynAttrModConf
-    (\elCfg ->
-       elCfg &
-       elementConfig_eventSpec %~
-       addEventSpecFlags
-         (Proxy :: Proxy (DomBuilderSpace m))
-         ev
-         (const preventDefault))
-
--- | Like 'elDynAttr'' but allows you to modify the element configuration.
---
--- Special thanks to @luigy: https://gist.github.com/luigy/b49ce04de8462e594c9c2b5b455ae5a5#file-foo-hs
-elDynAttrModConf ::
-     (DomBuilder t m, PostBuild t m)
-  => (ElementConfig EventResult t (DomBuilderSpace m) -> ElementConfig EventResult t (DomBuilderSpace m))
-  -> Text
-  -> Dynamic t (Map Text Text)
-  -> m a
-  -> m (Element EventResult (DomBuilderSpace m) t, a)
-elDynAttrModConf f elementTag attrs child = do
-  modifyAttrs <- dynamicAttributesToModifyAttributes attrs
-  let cfg =
-        def & modifyAttributes .~ fmapCheap mapKeysToAttributeName modifyAttrs
-  result <- element elementTag (f cfg) child
-  postBuild <- getPostBuild
-  notReadyUntil postBuild
-  pure result
diff --git a/src/Reflex/Bulmex/Tag.hs b/src/Reflex/Bulmex/Tag.hs
--- a/src/Reflex/Bulmex/Tag.hs
+++ b/src/Reflex/Bulmex/Tag.hs
@@ -1,266 +1,10 @@
-{-# LANGUAGE GADTs             #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RankNTypes        #-}
-
--- | bulma divs
-module Reflex.Bulmex.Tag where
-
-import           Control.Applicative
-import           Data.Bool
-import qualified Data.Map.Strict          as Map
-import qualified Data.Text                as Text
-import           Reflex
-import           Reflex.Bulmex.Attr
-import           Reflex.Bulmex.Space
-import qualified Reflex.Dom.Builder.Class as Dom
-import qualified Reflex.Dom.Widget        as Dom
-import qualified Reflex.Tags              as T
-
-container :: Dom.DomBuilder t m => m a -> m a
-container = containerClass mempty
-
-containerClass  :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-containerClass = partialDiv "container"
-
-partialDiv :: Dom.DomBuilder t m => Text.Text -> Text.Text -> m a -> m a
-partialDiv = txtEl T.divClass
-
-txtEl :: (Text.Text -> m a -> m a) -> Text.Text -> Text.Text -> m a -> m a
-txtEl = defaultEl spaceJoin
-
--- | allows us to set a default value for tags by defining a join function
---   not a monoid because often it does it wrong, text needs a space
---    for example in case of classes, and the default map monoid is broken
-defaultEl :: (arg -> arg -> arg) -> (arg -> m a -> m a) -> arg -> arg -> m a -> m a
-defaultEl monoidF elF a b = elF $ monoidF a b
-
-buttons :: Dom.DomBuilder t m => m a -> m a
-buttons = T.divClass "buttons"
-
--- | kindoff hard to set an image tag in reflex
-image :: Dom.DomBuilder t m => Text.Text -> m()
-image url = T.imgAttr (Map.singleton "src" url) Dom.blank
-
--- | first class second src
-imageClass :: Dom.DomBuilder t m => Text.Text -> Text.Text -> m()
-imageClass clazz url = T.imgAttr (Map.fromList [("src",url), ("class", clazz)]) Dom.blank
-
--- | bulma hero sturcture
--- | <section class="hero">
--- |  <div class="hero-body">
--- |     <div class="container">
-hero :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-hero styles =
-  txtEl T.sectionClass "hero" styles . T.divClass "hero-body" . container
-
-content :: Dom.DomBuilder t m => m a -> m a
-content = T.divClass "content"
-
-sect :: (Dom.DomBuilder t m) => m a -> m a
-sect = T.divClass "section"
-
-section :: (PostBuild t m, Dom.DomBuilder t m) => m a -> m a
-section = sectionDyn $ constDyn mempty
-
-sectionDyn ::
-     (PostBuild t m, Dom.DomBuilder t m) => Dynamic t AttrMap -> m a -> m a
-sectionDyn =
-  dynAttrEl (\a b -> T.divDynAttr a $ container b) $ classAttr "section"
-
-columns :: Dom.DomBuilder t m => m a -> m a
-columns = columnsClass mempty
-
-column :: Dom.DomBuilder t m => m a -> m a
-column = columnClass mempty
-
-columnsClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-columnsClass = partialDiv "columns"
-
-columnClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-columnClass = partialDiv "column"
-
-control :: Dom.DomBuilder t m => m a -> m a
-control = controlClass mempty
-
-controlClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-controlClass = partialDiv "control"
-
-controlDyn ::
-     (PostBuild t m, Dom.DomBuilder t m) => Dynamic t AttrMap -> m a -> m a
-controlDyn = dynAttrEl T.divDynAttr $ classAttr "control"
-
-tile :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-tile = partialDiv "tile"
-
-tileChild :: Dom.DomBuilder t m => m a -> m a
-tileChild = tileChildClass mempty
-
-tileChildClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-tileChildClass = txtEl tile "is-child"
-
-tileParentClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-tileParentClass = txtEl tile "is-parent"
-
-tileParent :: Dom.DomBuilder t m => m a -> m a
-tileParent = tileParentClass mempty
-
-tileAncestor :: Dom.DomBuilder t m => m a -> m a
-tileAncestor = tile "is-ancestor"
-
-dynAttrEl ::
-     Reflex t
-  => (Dynamic t AttrMap -> m a -> m a)
-  -> AttrMap
-  -> Dynamic t AttrMap
-  -> m a
-  -> m a
-dynAttrEl f = defaultEl ((<*>) . (<$>) attrUnion) f . constDyn
-
-field :: Dom.DomBuilder t m => m a -> m a
-field = fieldClass mempty
-
-fieldClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-fieldClass = partialDiv "field"
-
-fieldGrouped :: Dom.DomBuilder t m => m a -> m a
-fieldGrouped = fieldClass "is-grouped"
-
-title :: Dom.DomBuilder t m => m a -> m a
-title = titleClazz "is-1"
-
-titleClazz :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-titleClazz = txtEl T.h1Class "title"
-
-subtitle :: Dom.DomBuilder t m => m a -> m a
-subtitle = subtitleClass "is-3"
-
-subtitleClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-subtitleClass = txtEl T.h2Class "subtitle"
-
-box :: Dom.DomBuilder t m => m a -> m a
-box = T.divClass "box"
-
-evtText :: (Dom.DomBuilder t m, PostBuild t m, MonadHold t m) => Event t Text.Text -> m ()
-evtText evt = Dom.dynText =<< holdDyn "" evt
-
-
-labeled' :: Dom.DomBuilder t m => m () -> m a -> m a
-labeled' labelM inputM = do
-  labelEl labelM
-  control inputM
-
--- | named labelEl cause didn't want to fix name clashes
-labelEl :: Dom.DomBuilder t m => m a -> m a
-labelEl = labelClass mempty
-
-labelClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
-labelClass = txtEl T.labelClass "label"
-
-data ToolDirection
-  = Top -- top is graphic bugged
-  | Lft
-  | Rght
-  | Down
-
--- | balloon css, for example: https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css
-tooltipText ::
-     (PostBuild t m, Dom.DomBuilder t m) => Dynamic t Text.Text -> m a -> m a
-tooltipText = tooltipText' Rght
-
-tooltipText' ::
-     (PostBuild t m, Dom.DomBuilder t m)
-  => ToolDirection
-  -> Dynamic t Text.Text
-  -> m a
-  -> m a
-tooltipText' dir tipDyn monad = T.spanDynAttr (tipToAttr dir <$> tipDyn) $ monad
-
-tipToAttr :: ToolDirection -> Text.Text -> AttrMap
-tipToAttr dir "" = tipToAttr dir "-"
-tipToAttr dir tip =
-  Map.fromList [("data-balloon", tip), ("data-balloon-pos", direction dir)]
-
-direction :: ToolDirection -> Text.Text
-direction Top  = "top"
-direction Lft  = "left"
-direction Rght = "right"
-direction Down = "down"
-
-icon :: (PostBuild t m, Dom.DomBuilder t m) => Text.Text -> m ()
-icon = iconClass ""
-
--- | second is name
-iconClass ::
-     (PostBuild t m, Dom.DomBuilder t m) => Text.Text -> Text.Text -> m ()
-iconClass wrapClass =
-  iconDyn (constDyn (classAttr wrapClass)) . constDyn . classAttr
-
-iconDyn ::
-     (PostBuild t m, Dom.DomBuilder t m)
-  => Dynamic t AttrMap
-  -> Dynamic t AttrMap
-  -> m ()
-iconDyn wrapClass mdiClass = do
-  textSpace
-  T.spanDynAttr (attrUnion (classAttr "icon") <$> wrapClass) $
-    T.iDynAttr (Map.unionWith (<>) (classAttr "mdi mdi-") <$> mdiClass) $
-    Dom.blank
-  textSpace
-
--- | sometimes you just need 2 pieces of text to seperate with a space
-textSpace :: Dom.DomBuilder t m => m ()
-textSpace = Dom.text space
-
-flask :: (PostBuild t m, Dom.DomBuilder t m) => m ()
-flask = icon "flask"
-
--- | a html tag that accepts any text into it's href value
-ahref :: (Dom.DomBuilder t m, PostBuild t m) => Text.Text -> m a -> m a
-ahref = ahref' mempty
-
-ahref' :: (Dom.DomBuilder t m, PostBuild t m) => AttrMap -> Text.Text -> m a -> m a
-ahref' uno = ahrefDyn (constDyn uno) . constDyn
-
-ahrefDyn :: (Dom.DomBuilder t m, PostBuild t m) => Dynamic t AttrMap -> Dynamic t Text.Text -> m a -> m a
-ahrefDyn uno txt = T.aDynAttr $ (attrUnion <$> uno) <*> (Map.singleton "href" <$> txt)
-
-switchDiv ::
-     (PostBuild t m, Dom.DomBuilder t m)
-  => Dynamic t Bool
-  -> m ()
-  -> m a
-  -> m a
-switchDiv attrDyn true false = do
-  hideDiv_ (not <$> attrDyn) true
-  hideDiv_ attrDyn false
-
--- | when dynamic is true ishidden will be added, else the attrmap is used
-hideDiv ::
-     (PostBuild t m, Dom.DomBuilder t m)
-  => Dynamic t AttrMap
-  -> Dynamic t Bool
-  -> m a
-  -> m a
-hideDiv attrDyn hide =
-  T.divDynAttr $ bool <$> attrDyn <*> constDyn isHidden <*> hide
-
-hideDiv_ :: (PostBuild t m, Dom.DomBuilder t m) => Dynamic t Bool -> m a -> m a
-hideDiv_ = hideDiv $ constDyn Map.empty
-
-loadSpinner :: (Dom.DomBuilder t m) => m ()
-loadSpinner = imageClass "loadSpinner" "spinner.svg"
-
-hideEmptyDiv ::
-     (Eq (f b), Alternative f, PostBuild t m, Dom.DomBuilder t m)
-  => Dynamic t (f b)
-  -> m a
-  -> m a
-hideEmptyDiv = hideEmptyDyn $ constDyn Map.empty
+module Reflex.Bulmex.Tag
+  ( module X
+  ) where
 
-hideEmptyDyn ::
-     (Eq (f b), Alternative f, PostBuild t m, Dom.DomBuilder t m)
-  => Dynamic t AttrMap
-  -> Dynamic t (f b)
-  -> m a
-  -> m a
-hideEmptyDyn dyn = hideDiv dyn . fmap ((==) empty)
+import           Reflex.Bulmex.Tag.Abstract as X
+import           Reflex.Bulmex.Tag.Bulma    as X
+import           Reflex.Bulmex.Tag.Hide     as X
+import           Reflex.Bulmex.Tag.Icon     as X
+import           Reflex.Bulmex.Tag.Odd      as X
+import           Reflex.Bulmex.Tag.Tooltip  as X
diff --git a/src/Reflex/Bulmex/Tag/Abstract.hs b/src/Reflex/Bulmex/Tag/Abstract.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Bulmex/Tag/Abstract.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE GADTs             #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
+
+-- | Abstract tags, most tags have very similiar overload mechanisms
+--   this provides an abstraction over that
+module Reflex.Bulmex.Tag.Abstract(
+   defaultEl
+  , txtEl
+  , dynAttrEl
+  , partialDiv
+  ) where
+
+import qualified Data.Text                as Text
+import           Reflex
+import           Reflex.Bulmex.Attr
+import           Reflex.Bulmex.Space
+import qualified Reflex.Dom.Builder.Class as Dom
+import qualified Reflex.Tags              as T
+
+partialDiv :: Dom.DomBuilder t m => Text.Text -> Text.Text -> m a -> m a
+partialDiv = txtEl T.divClass
+
+txtEl :: (Text.Text -> m a -> m a) -> Text.Text -> Text.Text -> m a -> m a
+txtEl = defaultEl spaceJoin
+
+-- | allows us to set a default value for tags by defining a join function
+--   not a monoid because often it does it wrong, text needs a space
+--    for example in case of classes, and the default map monoid is broken
+defaultEl :: (arg -> arg -> arg) -> (arg -> m a -> m a) -> arg -> arg -> m a -> m a
+defaultEl monoidF elF a b = elF $ monoidF a b
+
+dynAttrEl ::
+     Reflex t
+  => (Dynamic t AttrMap -> m a -> m a)
+  -> AttrMap
+  -> Dynamic t AttrMap
+  -> m a
+  -> m a
+dynAttrEl f = defaultEl ((<*>) . (<$>) attrUnion) f . constDyn
diff --git a/src/Reflex/Bulmex/Tag/Bulma.hs b/src/Reflex/Bulmex/Tag/Bulma.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Bulmex/Tag/Bulma.hs
@@ -0,0 +1,165 @@
+{-# LANGUAGE GADTs             #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
+
+-- | Bulma styled tags: https://bulma.io/
+module Reflex.Bulmex.Tag.Bulma
+  (
+  -- * Layout
+    container
+  , containerClass
+  , hero
+  , content
+  , sect
+  , section
+  , sectionDyn
+  , columns
+  , column
+  , columnsClass
+  , columnClass
+  -- * Tiles
+  , tile
+  , tileChild
+  , tileChildClass
+  , tileParentClass
+  , tileParent
+  , tileAncestor
+  -- * Form
+  , control
+  , controlClass
+  , controlDyn
+  , buttons
+  , field
+  , fieldClass
+  , fieldGrouped
+  , labeled'
+  , labelEl
+  , labelClass
+  -- * Title
+  , title
+  , titleClazz
+  , subtitle
+  , subtitleClass
+  -- * Box
+  , box
+  ) where
+
+import qualified Data.Text                  as Text
+import           Reflex
+import           Reflex.Bulmex.Attr
+import           Reflex.Bulmex.Tag.Abstract
+import qualified Reflex.Dom.Builder.Class   as Dom
+import qualified Reflex.Tags                as T
+
+-- | Container: https://bulma.io/documentation/layout/container/
+container :: Dom.DomBuilder t m => m a -> m a
+container = containerClass mempty
+
+containerClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+containerClass = partialDiv "container"
+
+buttons :: Dom.DomBuilder t m => m a -> m a
+buttons = T.divClass "buttons"
+
+-- | bulma hero sturcture https://bulma.io/documentation/layout/hero/
+--
+-- > <section class="hero">
+-- >  <div class="hero-body">
+-- >    <div class="container">
+
+hero :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+hero styles =
+  txtEl T.sectionClass "hero" styles . T.divClass "hero-body" . container
+
+content :: Dom.DomBuilder t m => m a -> m a
+content = T.divClass "content"
+
+sect :: (Dom.DomBuilder t m) => m a -> m a
+sect = T.divClass "section"
+
+-- | https://bulma.io/documentation/layout/section/
+section :: (PostBuild t m, Dom.DomBuilder t m) => m a -> m a
+section = sectionDyn $ constDyn mempty
+
+sectionDyn ::
+     (PostBuild t m, Dom.DomBuilder t m) => Dynamic t AttrMap -> m a -> m a
+sectionDyn =
+  dynAttrEl (\a b -> T.divDynAttr a $ container b) $ classAttr "section"
+
+-- | https://bulma.io/documentation/columns/
+columns :: Dom.DomBuilder t m => m a -> m a
+columns = columnsClass mempty
+
+column :: Dom.DomBuilder t m => m a -> m a
+column = columnClass mempty
+
+columnsClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+columnsClass = partialDiv "columns"
+
+columnClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+columnClass = partialDiv "column"
+
+control :: Dom.DomBuilder t m => m a -> m a
+control = controlClass mempty
+
+controlClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+controlClass = partialDiv "control"
+
+controlDyn ::
+     (PostBuild t m, Dom.DomBuilder t m) => Dynamic t AttrMap -> m a -> m a
+controlDyn = dynAttrEl T.divDynAttr $ classAttr "control"
+
+-- | https://bulma.io/documentation/layout/tiles/
+tile :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+tile = partialDiv "tile"
+
+tileChild :: Dom.DomBuilder t m => m a -> m a
+tileChild = tileChildClass mempty
+
+tileChildClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+tileChildClass = txtEl tile "is-child"
+
+tileParentClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+tileParentClass = txtEl tile "is-parent"
+
+tileParent :: Dom.DomBuilder t m => m a -> m a
+tileParent = tileParentClass mempty
+
+tileAncestor :: Dom.DomBuilder t m => m a -> m a
+tileAncestor = tile "is-ancestor"
+
+field :: Dom.DomBuilder t m => m a -> m a
+field = fieldClass mempty
+
+fieldClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+fieldClass = partialDiv "field"
+
+fieldGrouped :: Dom.DomBuilder t m => m a -> m a
+fieldGrouped = fieldClass "is-grouped"
+
+title :: Dom.DomBuilder t m => m a -> m a
+title = titleClazz "is-1"
+
+titleClazz :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+titleClazz = txtEl T.h1Class "title"
+
+subtitle :: Dom.DomBuilder t m => m a -> m a
+subtitle = subtitleClass "is-3"
+
+subtitleClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+subtitleClass = txtEl T.h2Class "subtitle"
+
+box :: Dom.DomBuilder t m => m a -> m a
+box = T.divClass "box"
+
+labeled' :: Dom.DomBuilder t m => m () -> m a -> m a
+labeled' labelM inputM = do
+  labelEl labelM
+  control inputM
+
+-- | named labelEl cause didn't want to fix name clashes
+labelEl :: Dom.DomBuilder t m => m a -> m a
+labelEl = labelClass mempty
+
+labelClass :: Dom.DomBuilder t m => Text.Text -> m a -> m a
+labelClass = txtEl T.labelClass "label"
diff --git a/src/Reflex/Bulmex/Tag/Hide.hs b/src/Reflex/Bulmex/Tag/Hide.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Bulmex/Tag/Hide.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE GADTs             #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
+
+-- | A div that can hide with bulma is-hidden class
+module Reflex.Bulmex.Tag.Hide where
+
+import           Control.Applicative
+import           Data.Bool
+import qualified Data.Map.Strict          as Map
+import           Reflex
+import           Reflex.Bulmex.Attr
+import qualified Reflex.Dom.Builder.Class as Dom
+import qualified Reflex.Tags              as T
+
+switchDiv ::
+     (PostBuild t m, Dom.DomBuilder t m)
+  => Dynamic t Bool
+  -> m ()
+  -> m a
+  -> m a
+switchDiv attrDyn true false = do
+  hideDiv_ (not <$> attrDyn) true
+  hideDiv_ attrDyn false
+
+-- | when dynamic is true ishidden will be added, else the attrmap is used
+hideDiv ::
+     (PostBuild t m, Dom.DomBuilder t m)
+  => Dynamic t AttrMap
+  -> Dynamic t Bool
+  -> m a
+  -> m a
+hideDiv attrDyn hide =
+  T.divDynAttr $ bool <$> attrDyn <*> constDyn isHidden <*> hide
+
+hideDiv_ :: (PostBuild t m, Dom.DomBuilder t m) => Dynamic t Bool -> m a -> m a
+hideDiv_ = hideDiv $ constDyn Map.empty
+
+hideEmptyDiv ::
+     (Eq (f b), Alternative f, PostBuild t m, Dom.DomBuilder t m)
+  => Dynamic t (f b)
+  -> m a
+  -> m a
+hideEmptyDiv = hideEmptyDyn $ constDyn Map.empty
+
+hideEmptyDyn ::
+     (Eq (f b), Alternative f, PostBuild t m, Dom.DomBuilder t m)
+  => Dynamic t AttrMap
+  -> Dynamic t (f b)
+  -> m a
+  -> m a
+hideEmptyDyn dyn = hideDiv dyn . fmap ((==) empty)
diff --git a/src/Reflex/Bulmex/Tag/Icon.hs b/src/Reflex/Bulmex/Tag/Icon.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Bulmex/Tag/Icon.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE GADTs             #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
+
+-- | Icons with material design
+module Reflex.Bulmex.Tag.Icon
+  (-- * Icon
+    icon
+  , iconClass
+  , iconDyn
+  ,-- * Flask
+    flask
+  ) where
+
+import qualified Data.Map.Strict          as Map
+import qualified Data.Text                as Text
+import           Reflex
+import           Reflex.Bulmex.Attr
+import           Reflex.Bulmex.Tag.Odd
+import qualified Reflex.Dom.Builder.Class as Dom
+import qualified Reflex.Dom.Widget        as Dom
+import qualified Reflex.Tags              as T
+
+icon :: (PostBuild t m, Dom.DomBuilder t m) => Text.Text -> m ()
+icon = iconClass ""
+
+-- | second is name
+iconClass ::
+     (PostBuild t m, Dom.DomBuilder t m) => Text.Text -> Text.Text -> m ()
+iconClass wrapClass =
+  iconDyn (constDyn (classAttr wrapClass)) . constDyn . classAttr
+
+iconDyn ::
+     (PostBuild t m, Dom.DomBuilder t m)
+  => Dynamic t AttrMap
+  -> Dynamic t AttrMap
+  -> m ()
+iconDyn wrapClass mdiClass = do
+  textSpace
+  T.spanDynAttr (attrUnion (classAttr "icon") <$> wrapClass) $
+    T.iDynAttr (Map.unionWith (<>) (classAttr "mdi mdi-") <$> mdiClass) $
+    Dom.blank
+  textSpace
+
+flask :: (PostBuild t m, Dom.DomBuilder t m) => m ()
+flask = icon "flask"
diff --git a/src/Reflex/Bulmex/Tag/Odd.hs b/src/Reflex/Bulmex/Tag/Odd.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Bulmex/Tag/Odd.hs
@@ -0,0 +1,100 @@
+{-# LANGUAGE GADTs               #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE RankNTypes          #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+
+-- | Odd tag helpers, these have uncommen html attributes
+--   They don't fit in the 'normal' pattern of Abstract
+module Reflex.Bulmex.Tag.Odd
+  (
+  -- * Image
+    image
+  , imageClass
+  -- * Link
+  , ahref
+  , ahref'
+  , ahrefDyn
+  -- * Space
+  , textSpace
+  -- * Prevent browser default
+  , elDynAttrPrevDef
+  , elDynAttrModConf
+  ) where
+
+import           Control.Lens             ((%~), (.~))
+import           Data.Map                 (Map)
+import qualified Data.Map.Strict          as Map
+import           Data.Proxy               (Proxy (..))
+import           Data.Text                (Text)
+import qualified Data.Text                as Text
+import           Reflex
+import           Reflex.Bulmex.Attr
+import           Reflex.Bulmex.Space
+import qualified Reflex.Dom.Builder.Class as Dom
+import           Reflex.Dom.Core
+import qualified Reflex.Dom.Widget        as Dom
+import qualified Reflex.Tags              as T
+
+
+-- | kindoff hard to set an image tag in reflex
+image :: Dom.DomBuilder t m => Text.Text -> m()
+image url = T.imgAttr (Map.singleton "src" url) Dom.blank
+
+-- | first class second src
+imageClass :: Dom.DomBuilder t m => Text.Text -> Text.Text -> m()
+imageClass clazz url = T.imgAttr (Map.fromList [("src",url), ("class", clazz)]) Dom.blank
+
+-- | sometimes you just need 2 pieces of text to seperate with a space
+textSpace :: Dom.DomBuilder t m => m ()
+textSpace = Dom.text space
+
+-- | a html tag that accepts any text into it's href value
+ahref :: (Dom.DomBuilder t m, PostBuild t m) => Text.Text -> m a -> m a
+ahref = ahref' mempty
+
+ahref' :: (Dom.DomBuilder t m, PostBuild t m) => AttrMap -> Text.Text -> m a -> m a
+ahref' uno = ahrefDyn (constDyn uno) . constDyn
+
+ahrefDyn :: (Dom.DomBuilder t m, PostBuild t m) => Dynamic t AttrMap -> Dynamic t Text.Text -> m a -> m a
+ahrefDyn uno txt = T.aDynAttr $ (attrUnion <$> uno) <*> (Map.singleton "href" <$> txt)
+
+-- | From https://gist.github.com/3noch/134b1ee7fa48c347be9d164c3fac4ef7
+--   Like 'elDynAttr'' but configures "prevent default" on the given event.
+--   Blocks for example a context menu from poping up on right mouse click.
+--   This should be used with caution, as it may be unexpected for end user.
+elDynAttrPrevDef ::
+     forall a en m t. (DomBuilder t m, PostBuild t m)
+  => EventName en -- ^ Event on the element to configure with 'preventDefault'
+  -> Text -- ^ Element tag
+  -> Dynamic t (Map Text Text) -- ^ Element attributes
+  -> m a -- ^ Child of element
+  -> m (Element EventResult (DomBuilderSpace m) t, a) -- An element and the result of the child
+elDynAttrPrevDef ev =
+  elDynAttrModConf
+    (\elCfg ->
+       elCfg &
+       elementConfig_eventSpec %~
+       addEventSpecFlags
+         (Proxy :: Proxy (DomBuilderSpace m))
+         ev
+         (const preventDefault))
+
+-- | Like 'elDynAttr'' but allows you to modify the element configuration.
+--
+-- Special thanks to @luigy: https://gist.github.com/luigy/b49ce04de8462e594c9c2b5b455ae5a5#file-foo-hs
+elDynAttrModConf ::
+     (DomBuilder t m, PostBuild t m)
+  => (ElementConfig EventResult t (DomBuilderSpace m) -> ElementConfig EventResult t (DomBuilderSpace m))
+  -> Text
+  -> Dynamic t (Map Text Text)
+  -> m a
+  -> m (Element EventResult (DomBuilderSpace m) t, a)
+elDynAttrModConf f elementTag attrs child = do
+  modifyAttrs <- dynamicAttributesToModifyAttributes attrs
+  let cfg =
+        def & modifyAttributes .~ fmapCheap mapKeysToAttributeName modifyAttrs
+  result <- element elementTag (f cfg) child
+  postBuild <- getPostBuild
+  notReadyUntil postBuild
+  pure result
diff --git a/src/Reflex/Bulmex/Tag/Tooltip.hs b/src/Reflex/Bulmex/Tag/Tooltip.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Bulmex/Tag/Tooltip.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE GADTs             #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
+
+-- | Tooltips with balloon css
+module Reflex.Bulmex.Tag.Tooltip(
+  tooltipText,
+  tooltipText',
+  ToolDirection(..),
+  tipToAttr
+  ) where
+
+import qualified Data.Map.Strict          as Map
+import qualified Data.Text                as Text
+import           Reflex
+import           Reflex.Bulmex.Attr
+import qualified Reflex.Dom.Builder.Class as Dom
+import qualified Reflex.Tags              as T
+
+data ToolDirection
+  = Top -- top is graphic bugged
+  | Lft
+  | Rght
+  | Down
+
+-- | balloon css, for example: https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css
+--   uses a span underneath
+tooltipText ::
+     (PostBuild t m, Dom.DomBuilder t m) => Dynamic t Text.Text -> m a -> m a
+tooltipText = tooltipText' Rght
+
+-- | Allows setting of initial direction
+tooltipText' ::
+     (PostBuild t m, Dom.DomBuilder t m)
+  => ToolDirection
+  -> Dynamic t Text.Text
+  -> m a
+  -> m a
+tooltipText' dir tipDyn monad = T.spanDynAttr (tipToAttr dir <$> tipDyn) $ monad
+
+tipToAttr :: ToolDirection -> Text.Text -> AttrMap
+tipToAttr dir "" = tipToAttr dir "-"
+tipToAttr dir tip =
+  Map.fromList [("data-balloon", tip), ("data-balloon-pos", direction dir)]
+
+direction :: ToolDirection -> Text.Text
+direction Top  = "top"
+direction Lft  = "left"
+direction Rght = "right"
+direction Down = "down"
