diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,16 @@
+### 1.1.0.0
+
+- Reduce memory usage by sharing wreq session among image widget instances.
+- Add sizeUpdater helpers. Support multiple handlers in box, grid and stack.
+- Add `dpr` field to `WidgetEnv`.
+- Add `RunInRenderThread` to support initialization of low level OpenGL resources.
+- Set correct `WidgetEnv` viewport from scroll (it looked good because of scissoring).
+- Fix issue with scrollbars using child coordinates for detecting clicks.
+- Add OpenGL example.
+- Add `containerDrawDecorations`. Simplify button/externalLink internals.
+- Add ThemeState entries for optionButton and toggleButton widgets.
+- Add `singleDrawDecorations`. Make it consistent with Container.
+
 ### 1.0.0.3
 
 - Consume and forward all available messages from Producers on each cycle.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -54,6 +54,7 @@
 - [Books](docs/examples/02-books.md)
 - [Ticker](docs/examples/03-ticker.md)
 - [Generative](docs/examples/04-generative.md)
+- [Custom OpenGL](docs/examples/05-opengl.md)
 
 ### Haddock
 
diff --git a/cbits/fontmanager.c b/cbits/fontmanager.c
--- a/cbits/fontmanager.c
+++ b/cbits/fontmanager.c
@@ -2,6 +2,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <memory.h>
 
 #include "fontstash.h"
diff --git a/examples/books/BookTypes.hs b/examples/books/BookTypes.hs
--- a/examples/books/BookTypes.hs
+++ b/examples/books/BookTypes.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : BookTypes
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Types for the 'Books' example.
+-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE ScopedTypeVariables #-}
diff --git a/examples/books/Main.hs b/examples/books/Main.hs
--- a/examples/books/Main.hs
+++ b/examples/books/Main.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Main
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the 'Books' example.
+-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE ScopedTypeVariables #-}
@@ -14,6 +24,7 @@
 
 import qualified Data.Text as T
 import qualified Network.Wreq as W
+import qualified Network.Wreq.Session as Sess
 
 import BookTypes
 import Monomer
@@ -123,16 +134,17 @@
     ]
 
 handleEvent
-  :: WidgetEnv BooksModel BooksEvt
+  :: Sess.Session
+  -> WidgetEnv BooksModel BooksEvt
   -> WidgetNode BooksModel BooksEvt
   -> BooksModel
   -> BooksEvt
   -> [EventResponse BooksModel BooksEvt BooksModel ()]
-handleEvent wenv node model evt = case evt of
+handleEvent sess wenv node model evt = case evt of
   BooksInit -> [setFocusOnKey wenv "query"]
   BooksSearch -> [
     Model $ model & searching .~ True,
-    Task $ searchBooks (model ^. query)
+    Task $ searchBooks sess (model ^. query)
     ]
   BooksSearchResult resp -> [
     Message "mainScroll" ScrollReset,
@@ -151,8 +163,8 @@
   BooksCloseDetails -> [Model $ model & selected .~ Nothing]
   BooksCloseError -> [Model $ model & errorMsg .~ Nothing]
 
-searchBooks :: Text -> IO BooksEvt
-searchBooks query = do
+searchBooks :: Sess.Session -> Text -> IO BooksEvt
+searchBooks sess query = do
   putStrLn . T.unpack $ "Searching: " <> query
   result <- catchAny (fetch url) (return . Left . T.pack . show)
 
@@ -165,7 +177,7 @@
       | null (resp ^. docs) = Nothing
       | otherwise = Just resp
     fetch url = do
-      resp <- W.get url
+      resp <- Sess.get sess url
         >>= W.asJSON
         >>= return . preview (W.responseBody . _Just)
 
@@ -173,7 +185,8 @@
 
 main :: IO ()
 main = do
-  startApp initModel handleEvent buildUI config
+  sess <- Sess.newAPISession
+  startApp initModel (handleEvent sess) buildUI config
   where
     config = [
       appWindowTitle "Book search",
diff --git a/examples/generative/GenerativeTypes.hs b/examples/generative/GenerativeTypes.hs
--- a/examples/generative/GenerativeTypes.hs
+++ b/examples/generative/GenerativeTypes.hs
@@ -1,3 +1,14 @@
+{-|
+Module      : GenerativeTypes
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Types for the 'Generative' example.
+-}
+
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/examples/generative/Main.hs b/examples/generative/Main.hs
--- a/examples/generative/Main.hs
+++ b/examples/generative/Main.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Main
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the 'Generative' example.
+-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
 module Main where
diff --git a/examples/generative/Widgets/BoxesPalette.hs b/examples/generative/Widgets/BoxesPalette.hs
--- a/examples/generative/Widgets/BoxesPalette.hs
+++ b/examples/generative/Widgets/BoxesPalette.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Widgets.BoxesPalette
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Widget representing a set of boxes with varying colors.
+-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
diff --git a/examples/generative/Widgets/CirclesGrid.hs b/examples/generative/Widgets/CirclesGrid.hs
--- a/examples/generative/Widgets/CirclesGrid.hs
+++ b/examples/generative/Widgets/CirclesGrid.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Widgets.CirclesGrid
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Widget representing a grid of circles.
+-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
diff --git a/examples/opengl/Main.hs b/examples/opengl/Main.hs
new file mode 100644
--- /dev/null
+++ b/examples/opengl/Main.hs
@@ -0,0 +1,101 @@
+{-|
+Module      : Main
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the 'Custom OpenGL' example.
+-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Main where
+
+import Control.Lens
+import Data.Text (Text)
+import Monomer
+
+import qualified Data.Map as M
+import qualified Monomer.Lens as L
+
+import OpenGLWidget
+
+data AppModel = AppModel {
+  _color1 :: Color,
+  _color2 :: Color,
+  _color3 :: Color,
+  _color4 :: Color
+} deriving (Eq, Show)
+
+data AppEvent
+  = AppInit
+  deriving (Eq, Show)
+
+makeLenses 'AppModel
+
+buildUI
+  :: WidgetEnv AppModel AppEvent
+  -> AppModel
+  -> WidgetNode AppModel AppEvent
+buildUI wenv model = widgetTree where
+  colorsMap = M.fromList [(red, "Red"), (green, "Green"), (blue, "Blue"), (orange, "Orange")]
+  colors = M.keys colorsMap
+  colorDropdown field = textDropdown_ field colors (colorsMap M.!) []
+
+  widgetTree = vstack [
+      hstack [
+        label "Color 1:",
+        spacer,
+        colorDropdown color1,
+        spacer,
+        label "Color 2:",
+        spacer,
+        colorDropdown color2,
+        spacer,
+        label "Color 3:",
+        spacer,
+        colorDropdown color3,
+        spacer,
+        label "Color 4:",
+        spacer,
+        colorDropdown color4
+      ],
+      spacer,
+      vgrid [
+        hgrid [
+          openGLWidget (model ^. color1)
+            `styleBasic` [padding 20],
+          scroll (openGLWidget (model ^. color2) `styleBasic` [width 800, height 800])
+            `styleBasic` [padding 20]
+        ],
+        hgrid [
+          openGLWidget (model ^. color3)
+            `styleBasic` [padding 20],
+          openGLWidget (model ^. color4)
+            `styleBasic` [padding 20]
+        ]
+      ]
+    ] `styleBasic` [padding 10]
+
+handleEvent
+  :: WidgetEnv AppModel AppEvent
+  -> WidgetNode AppModel AppEvent
+  -> AppModel
+  -> AppEvent
+  -> [AppEventResponse AppModel AppEvent]
+handleEvent wenv node model evt = case evt of
+  AppInit -> []
+
+main :: IO ()
+main = do
+  startApp model handleEvent buildUI config
+  where
+    config = [
+      appWindowTitle "OpenGL",
+      appTheme darkTheme,
+      appFontDef "Regular" "./assets/fonts/Roboto-Regular.ttf",
+      appInitEvent AppInit
+      ]
+    model = AppModel red green blue orange
diff --git a/examples/opengl/OpenGLWidget.hs b/examples/opengl/OpenGLWidget.hs
new file mode 100644
--- /dev/null
+++ b/examples/opengl/OpenGLWidget.hs
@@ -0,0 +1,239 @@
+{-|
+Module      : Main
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Widget implementing low level rendering for the 'Custom OpenGL' example.
+
+OpenGL code adapted from dmjio's [example](https://github.com/dmjio/LearnOpenGL.hs/blob/master/GettingStarted/ShadersEx2/Main.hs).
+-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+module OpenGLWidget (
+  openGLWidget
+) where
+
+import Control.Lens ((&), (^.), (.~))
+import Control.Monad
+import Data.Default
+import Data.Typeable (cast)
+import Data.Vector.Storable (Vector)
+import Foreign.C.String
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import Foreign.Storable
+import Graphics.GL
+
+import qualified Data.Vector.Storable as V
+
+import Monomer
+import Monomer.Widgets.Single
+
+import qualified Monomer.Lens as L
+
+data OpenGLWidgetMsg
+  = OpenGLWidgetInit GLuint (Ptr GLuint) (Ptr GLuint)
+  deriving (Show, Eq)
+
+data OpenGLWidgetState = OpenGLWidgetState {
+  _ogsLoaded :: Bool,
+  _ogsShaderId :: GLuint,
+  _ogsVao :: Ptr GLuint,
+  _ogsVbo :: Ptr GLuint
+} deriving (Show, Eq)
+
+openGLWidget :: Color -> WidgetNode s e
+openGLWidget color = defaultWidgetNode "openGLWidget" widget where
+  widget = makeOpenGLWidget color state
+  state = OpenGLWidgetState False 0 nullPtr nullPtr
+
+makeOpenGLWidget :: Color -> OpenGLWidgetState -> Widget s e
+makeOpenGLWidget color state = widget where
+  widget = createSingle state def {
+    singleInit = init,
+    singleMerge = merge,
+    singleDispose = dispose,
+    singleHandleMessage = handleMessage,
+    singleGetSizeReq = getSizeReq,
+    singleRender = render
+  }
+
+  init wenv node = resultReqs node reqs where
+    widgetId = node ^. L.info . L.widgetId
+    path = node ^. L.info . L.path
+    buffers = 2
+
+    initOpenGL = do
+      -- This needs to run in render thread
+      program <- createShaderProgram
+      vaoPtr <- malloc
+      vboPtr <- malloc
+      glGenVertexArrays buffers vaoPtr
+      glGenBuffers buffers vboPtr
+
+      return $ OpenGLWidgetInit program vaoPtr vboPtr
+
+    reqs = [RunInRenderThread widgetId path initOpenGL]
+
+  merge wenv node oldNode oldState = resultNode newNode where
+    newNode = node
+      & L.widget .~ makeOpenGLWidget color oldState
+
+  dispose wenv node = resultReqs node reqs where
+    OpenGLWidgetState _ shaderId vaoPtr vboPtr = state
+    widgetId = node ^. L.info . L.widgetId
+    path = node ^. L.info . L.path
+    buffers = 2
+
+    disposeOpenGL = do
+      -- This needs to run in render thread
+      glDeleteProgram shaderId
+      glDeleteVertexArrays buffers vaoPtr
+      glDeleteBuffers buffers vboPtr
+      free vaoPtr
+      free vboPtr
+
+    reqs = [RunInRenderThread widgetId path disposeOpenGL]
+
+  handleMessage wenv node target msg = case cast msg of
+    Just (OpenGLWidgetInit shaderId vao vbo) -> Just result where
+      newState = OpenGLWidgetState True shaderId vao vbo
+      newNode = node
+        & L.widget .~ makeOpenGLWidget color newState
+      result = resultReqs newNode [RenderOnce]
+    _ -> Nothing
+
+  getSizeReq wenv node = (sizeReqW, sizeReqH) where
+    sizeReqW = expandSize 100 1
+    sizeReqH = expandSize 100 1
+
+  render wenv node renderer =
+    when (_ogsLoaded state) $
+      createRawTask renderer $
+        doInScissor winSize dpr offset activeVp $
+          drawVertices state (toVectorVAO winSize offset color triangle)
+    where
+      dpr = wenv ^. L.dpr
+      winSize = wenv ^. L.windowSize
+      activeVp = wenv ^. L.viewport
+      offset = wenv ^. L.offset
+
+      -- Simple triangle
+      style = currentStyle wenv node
+      nodeVp = getContentArea node style
+
+      Rect rx ry rw rh = nodeVp
+      triangle = [(rx + rw, ry + rh), (rx, ry + rh), (rx + rw / 2, ry)]
+
+doInScissor :: Size -> Double -> Point -> Rect -> IO () -> IO ()
+doInScissor winSize dpr offset vp action = do
+  glEnable GL_SCISSOR_TEST
+  -- OpenGL's Y axis increases from bottom to top
+  glScissor (round (rx + ox)) (round $ winH - ry - oy - rh) (round rw) (round rh)
+  action
+  glDisable GL_SCISSOR_TEST
+  where
+    winH = winSize ^. L.h * dpr
+    Point ox oy = mulPoint dpr offset
+    Rect rx ry rw rh = mulRect dpr vp
+
+toVectorVAO :: Size -> Point -> Color -> [(Double, Double)] -> Vector Float
+toVectorVAO (Size w h) (Point ox oy) (Color r g b a) points = vec where
+  px x = realToFrac $ (x + ox - w / 2) / (w / 2)
+  -- OpenGL's Y axis increases from bottom to top
+  py y = realToFrac $ (h / 2 - y - oy) / (h / 2)
+  col c = realToFrac (fromIntegral c / 255)
+  row (x, y) = [px x, py y, 0, col r, col g, col b]
+  vec = V.fromList . concat $ row <$> points
+
+drawVertices
+  :: forall a . Storable a
+  => OpenGLWidgetState
+  -> Vector a
+  -> IO ()
+drawVertices state vertices = do
+  vao <- peek vaoPtr
+  vbo <- peek vboPtr
+
+  glBindVertexArray vao
+  glBindBuffer GL_ARRAY_BUFFER vbo
+
+  -- Copies raw data from vector to OpenGL memory
+  V.unsafeWith vertices $ \vertsPtr ->
+    glBufferData
+      GL_ARRAY_BUFFER
+      (fromIntegral (V.length vertices * floatSize))
+      (castPtr vertsPtr)
+      GL_STATIC_DRAW
+
+  -- The vertex shader expects two arguments. Position:
+  glVertexAttribPointer 0 3 GL_FLOAT GL_FALSE (fromIntegral (floatSize * 6)) nullPtr
+  glEnableVertexAttribArray 0
+
+  -- Color:
+  glVertexAttribPointer 1 3 GL_FLOAT GL_FALSE (fromIntegral (floatSize * 6)) (nullPtr `plusPtr` (floatSize * 3))
+  glEnableVertexAttribArray 1
+
+  glUseProgram shaderId
+  glBindVertexArray vao
+  glDrawArrays GL_TRIANGLES 0 3
+
+  where
+    floatSize = sizeOf (undefined :: Float)
+    OpenGLWidgetState _ shaderId vaoPtr vboPtr = state
+
+createShaderProgram :: IO GLuint
+createShaderProgram = do
+  shaderProgram <- glCreateProgram
+  vertexShader <- compileShader GL_VERTEX_SHADER "examples/opengl/shaders/vert.glsl"
+  fragmentShader <- compileShader GL_FRAGMENT_SHADER "examples/opengl/shaders/frag.glsl"
+
+  glAttachShader shaderProgram vertexShader
+  glAttachShader shaderProgram fragmentShader
+
+  glLinkProgram shaderProgram
+  checkProgramLink shaderProgram
+
+  glDeleteShader vertexShader
+  glDeleteShader fragmentShader
+
+  return shaderProgram
+
+compileShader :: GLenum -> FilePath -> IO GLuint
+compileShader shaderType shaderFile = do
+  shader <- glCreateShader shaderType
+  shaderSource <- readFile shaderFile >>= newCString
+
+  alloca $ \shadersStr -> do
+    shadersStr `poke` shaderSource
+    glShaderSource shader 1 shadersStr nullPtr
+    glCompileShader shader
+    checkShaderCompile shader
+
+  return shader
+
+checkProgramLink :: GLuint -> IO ()
+checkProgramLink programId = do
+  alloca $ \successPtr -> do
+    alloca $ \infoLogPtr -> do
+      glGetProgramiv programId GL_LINK_STATUS successPtr
+      success <- peek successPtr
+
+      when (success <= 0) $ do
+        glGetProgramInfoLog programId 512 nullPtr infoLogPtr
+        putStrLn =<< peekCString infoLogPtr
+
+checkShaderCompile :: GLuint -> IO ()
+checkShaderCompile shaderId = do
+  alloca $ \successPtr ->
+    alloca $ \infoLogPtr -> do
+      glGetShaderiv shaderId GL_COMPILE_STATUS successPtr
+      success <- peek successPtr
+
+      when (success <= 0) $ do
+        glGetShaderInfoLog shaderId 512 nullPtr infoLogPtr
+        putStrLn "Failed to compile shader "
diff --git a/examples/ticker/BinanceTypes.hs b/examples/ticker/BinanceTypes.hs
--- a/examples/ticker/BinanceTypes.hs
+++ b/examples/ticker/BinanceTypes.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : BinanceTypes
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Types for the Binance Exchange in the 'Ticker' example.
+-}
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
diff --git a/examples/ticker/Main.hs b/examples/ticker/Main.hs
--- a/examples/ticker/Main.hs
+++ b/examples/ticker/Main.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Main
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the 'Ticker' example.
+-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE LambdaCase #-}
diff --git a/examples/ticker/TickerTypes.hs b/examples/ticker/TickerTypes.hs
--- a/examples/ticker/TickerTypes.hs
+++ b/examples/ticker/TickerTypes.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Main
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Types for the 'Ticker' example.
+-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE RecordWildCards #-}
diff --git a/examples/todo/Main.hs b/examples/todo/Main.hs
--- a/examples/todo/Main.hs
+++ b/examples/todo/Main.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Main
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the 'Todo' example.
+-}
 {-# LANGUAGE BinaryLiterals #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE ScopedTypeVariables #-}
diff --git a/examples/todo/TodoTypes.hs b/examples/todo/TodoTypes.hs
--- a/examples/todo/TodoTypes.hs
+++ b/examples/todo/TodoTypes.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : TodoTypes
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Types for the 'Todo' example.
+-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/examples/tutorial/Main.hs b/examples/tutorial/Main.hs
--- a/examples/tutorial/Main.hs
+++ b/examples/tutorial/Main.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Main
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the tutorials.
+-}
 module Main where
 
 import qualified Tutorial01_Basics
diff --git a/examples/tutorial/Tutorial01_Basics.hs b/examples/tutorial/Tutorial01_Basics.hs
--- a/examples/tutorial/Tutorial01_Basics.hs
+++ b/examples/tutorial/Tutorial01_Basics.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Tutorial01_Basics
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the '01 - Basics' tutorial.
+-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/examples/tutorial/Tutorial02_Styling.hs b/examples/tutorial/Tutorial02_Styling.hs
--- a/examples/tutorial/Tutorial02_Styling.hs
+++ b/examples/tutorial/Tutorial02_Styling.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Tutorial02_Styling
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the '02 - Styling' tutorial.
+-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/examples/tutorial/Tutorial03_LifeCycle.hs b/examples/tutorial/Tutorial03_LifeCycle.hs
--- a/examples/tutorial/Tutorial03_LifeCycle.hs
+++ b/examples/tutorial/Tutorial03_LifeCycle.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Tutorial03_LifeCycle
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the '03 - LifeCycle' tutorial.
+-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/examples/tutorial/Tutorial04_Tasks.hs b/examples/tutorial/Tutorial04_Tasks.hs
--- a/examples/tutorial/Tutorial04_Tasks.hs
+++ b/examples/tutorial/Tutorial04_Tasks.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Tutorial04_Tasks
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the '04 - Tasks' tutorial.
+-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/examples/tutorial/Tutorial05_Producers.hs b/examples/tutorial/Tutorial05_Producers.hs
--- a/examples/tutorial/Tutorial05_Producers.hs
+++ b/examples/tutorial/Tutorial05_Producers.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Tutorial05_Producers
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the '05 - Producers' tutorial.
+-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/examples/tutorial/Tutorial06_Composite.hs b/examples/tutorial/Tutorial06_Composite.hs
--- a/examples/tutorial/Tutorial06_Composite.hs
+++ b/examples/tutorial/Tutorial06_Composite.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Tutorial06_Composite
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the '06 - Composite' tutorial.
+-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/examples/tutorial/Tutorial07_CustomWidget.hs b/examples/tutorial/Tutorial07_CustomWidget.hs
--- a/examples/tutorial/Tutorial07_CustomWidget.hs
+++ b/examples/tutorial/Tutorial07_CustomWidget.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Tutorial07_CustomWidget
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the '07 - Custom Widget' tutorial.
+-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
diff --git a/examples/tutorial/Tutorial08_Themes.hs b/examples/tutorial/Tutorial08_Themes.hs
--- a/examples/tutorial/Tutorial08_Themes.hs
+++ b/examples/tutorial/Tutorial08_Themes.hs
@@ -1,3 +1,13 @@
+{-|
+Module      : Tutorial08_Themes
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Main module for the '08 - Themes' tutorial.
+-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell #-}
 
diff --git a/monomer.cabal b/monomer.cabal
--- a/monomer.cabal
+++ b/monomer.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           monomer
-version:        1.0.0.3
+version:        1.1.0.0
 synopsis:       A GUI library for writing native Haskell applications.
 description:    Monomer is an easy to use, cross platform, GUI library for writing native
                 Haskell applications.
@@ -274,6 +274,49 @@
     , wreq >=0.5.2 && <0.6
   default-language: Haskell2010
 
+executable opengl
+  main-is: Main.hs
+  other-modules:
+      OpenGLWidget
+      Paths_monomer
+  hs-source-dirs:
+      examples/opengl
+  default-extensions:
+      OverloadedStrings
+  ghc-options: -threaded
+  build-depends:
+      JuicyPixels >=3.2.9 && <3.5
+    , OpenGL ==3.0.*
+    , OpenGLRaw ==3.3.*
+    , async >=2.1 && <2.3
+    , attoparsec >=0.12 && <0.15
+    , base >=4.11 && <5
+    , bytestring >=0.10 && <0.12
+    , bytestring-to-vector ==0.3.*
+    , containers >=0.5.11 && <0.7
+    , data-default >=0.5 && <0.8
+    , exceptions ==0.10.*
+    , extra >=1.6 && <1.9
+    , formatting >=6.0 && <8.0
+    , http-client >=0.6 && <0.9
+    , lens >=4.16 && <5.1
+    , monomer
+    , mtl >=2.1 && <2.3
+    , nanovg >=0.8 && <1.0
+    , process ==1.6.*
+    , random >=1.1 && <1.3
+    , safe ==0.3.*
+    , sdl2 >=2.4.0 && <2.6
+    , stm ==2.5.*
+    , text ==1.2.*
+    , text-show >=3.7 && <3.10
+    , time >=1.8 && <1.13
+    , transformers >=0.5 && <0.7
+    , unordered-containers >=0.2.8 && <0.3
+    , vector >=0.12 && <0.14
+    , wreq >=0.5.2 && <0.6
+  default-language: Haskell2010
+
 executable ticker
   main-is: Main.hs
   other-modules:
@@ -415,6 +458,7 @@
   main-is: Spec.hs
   other-modules:
       Monomer.Common.CursorIconSpec
+      Monomer.Core.SizeReqSpec
       Monomer.Graphics.UtilSpec
       Monomer.TestEventUtil
       Monomer.TestUtil
diff --git a/src/Monomer/Common/BasicTypes.hs b/src/Monomer/Common/BasicTypes.hs
--- a/src/Monomer/Common/BasicTypes.hs
+++ b/src/Monomer/Common/BasicTypes.hs
@@ -131,6 +131,10 @@
 moveRect :: Point -> Rect -> Rect
 moveRect (Point x y) (Rect rx ry rw rh) = Rect (rx + x) (ry + y) rw rh
 
+-- | Scales a rect by the provided factor.
+mulRect :: Double -> Rect -> Rect
+mulRect f (Rect rx ry rw rh) = Rect (f * rx) (f * ry) (f * rw) (f * rh)
+
 -- | Returns the middle point of a rect.
 rectCenter :: Rect -> Point
 rectCenter (Rect rx ry rw rh) = Point (rx + rw / 2) (ry + rh / 2)
diff --git a/src/Monomer/Core/Combinators.hs b/src/Monomer/Core/Combinators.hs
--- a/src/Monomer/Core/Combinators.hs
+++ b/src/Monomer/Core/Combinators.hs
@@ -23,7 +23,6 @@
 import Control.Lens (ALens')
 import Data.Text (Text)
 
-import Monomer.Common
 import Monomer.Core.StyleTypes
 import Monomer.Core.WidgetTypes
 import Monomer.Event.Types
diff --git a/src/Monomer/Core/SizeReq.hs b/src/Monomer/Core/SizeReq.hs
--- a/src/Monomer/Core/SizeReq.hs
+++ b/src/Monomer/Core/SizeReq.hs
@@ -13,6 +13,12 @@
 module Monomer.Core.SizeReq (
   SizeReqUpdater(..),
   clearExtra,
+  fixedToMinW,
+  fixedToMinH,
+  fixedToMaxW,
+  fixedToMaxH,
+  fixedToExpandW,
+  fixedToExpandH,
   sizeReqBounded,
   sizeReqValid,
   sizeReqAddStyle,
@@ -43,9 +49,51 @@
 -- | Transforms a SizeReq pair by applying an arbitrary operation.
 type SizeReqUpdater = (SizeReq, SizeReq) -> (SizeReq, SizeReq)
 
--- | Clears the extra field of a SizeReq.
+-- | Clears the extra field of a pair of SizeReqs.
 clearExtra :: SizeReqUpdater
-clearExtra (req1, req2) = (req1 & L.extra .~ 0, req2 & L.extra .~ 0)
+clearExtra (reqW, reqH) = (reqW & L.extra .~ 0, reqH & L.extra .~ 0)
+
+-- | Switches a SizeReq pair from fixed width to minimum width.
+fixedToMinW
+  :: Double          -- ^ The resize factor.
+  -> SizeReqUpdater  -- ^ The updated SizeReq.
+fixedToMinW fw (SizeReq fixed _ _ _, reqH) = (newReqH, reqH) where
+  newReqH = SizeReq fixed 0 fixed fw
+
+-- | Switches a SizeReq pair from fixed height to minimum height.
+fixedToMinH
+  :: Double          -- ^ The resize factor.
+  -> SizeReqUpdater  -- ^ The updated SizeReq.
+fixedToMinH fh (reqW, SizeReq fixed _ _ _) = (reqW, newReqH) where
+  newReqH = SizeReq fixed 0 fixed fh
+
+-- | Switches a SizeReq pair from fixed width to maximum width.
+fixedToMaxW
+  :: Double          -- ^ The resize factor.
+  -> SizeReqUpdater  -- ^ The updated SizeReq.
+fixedToMaxW fw (SizeReq fixed _ _ _, reqH) = (newReqH, reqH) where
+  newReqH = SizeReq 0 fixed 0 fw
+
+-- | Switches a SizeReq pair from fixed height to maximum height.
+fixedToMaxH
+  :: Double          -- ^ The resize factor.
+  -> SizeReqUpdater  -- ^ The updated SizeReq.
+fixedToMaxH fh (reqW, SizeReq fixed _ _ _) = (reqW, newReqH) where
+  newReqH = SizeReq 0 fixed 0 fh
+
+-- | Switches a SizeReq pair from fixed width to expand width.
+fixedToExpandW
+  :: Double          -- ^ The resize factor.
+  -> SizeReqUpdater  -- ^ The updated SizeReq.
+fixedToExpandW fw (SizeReq fixed _ _ _, reqH) = (newReqH, reqH) where
+  newReqH = SizeReq 0 fixed fixed fw
+
+-- | Switches a SizeReq pair from fixed height to expand height.
+fixedToExpandH
+  :: Double          -- ^ The resize factor.
+  -> SizeReqUpdater  -- ^ The updated SizeReq.
+fixedToExpandH fh (reqW, SizeReq fixed _ _ _) = (reqW, newReqH) where
+  newReqH = SizeReq 0 fixed fixed fh
 
 -- | Returns a bounded value by the SizeReq, starting from value and offset.
 sizeReqBounded :: SizeReq -> Double -> Double -> Double
diff --git a/src/Monomer/Core/ThemeTypes.hs b/src/Monomer/Core/ThemeTypes.hs
--- a/src/Monomer/Core/ThemeTypes.hs
+++ b/src/Monomer/Core/ThemeTypes.hs
@@ -74,6 +74,8 @@
   _thsExternalLinkStyle :: StyleState,
   _thsLabelStyle :: StyleState,
   _thsNumericFieldStyle :: StyleState,
+  _thsOptionBtnOnStyle :: StyleState,
+  _thsOptionBtnOffStyle :: StyleState,
   _thsRadioStyle :: StyleState,
   _thsRadioWidth :: Double,
   _thsScrollOverlay :: Bool,
@@ -97,6 +99,8 @@
   _thsTextAreaStyle :: StyleState,
   _thsTextFieldStyle :: StyleState,
   _thsTimeFieldStyle :: StyleState,
+  _thsToggleBtnOnStyle :: StyleState,
+  _thsToggleBtnOffStyle :: StyleState,
   _thsTooltipStyle :: StyleState,
   _thsUserStyleMap :: M.Map String StyleState,
   _thsUserColorMap :: M.Map String Color
@@ -126,6 +130,8 @@
     _thsExternalLinkStyle = def,
     _thsLabelStyle = def,
     _thsNumericFieldStyle = def,
+    _thsOptionBtnOnStyle = def,
+    _thsOptionBtnOffStyle = def,
     _thsRadioStyle = def,
     _thsRadioWidth = 20,
     _thsScrollOverlay = False,
@@ -149,6 +155,8 @@
     _thsTextAreaStyle = def,
     _thsTextFieldStyle = def,
     _thsTimeFieldStyle = def,
+    _thsToggleBtnOnStyle = def,
+    _thsToggleBtnOffStyle = def,
     _thsTooltipStyle = def,
     _thsUserStyleMap = M.empty,
     _thsUserColorMap = M.empty
@@ -178,6 +186,8 @@
     _thsExternalLinkStyle = _thsExternalLinkStyle t1 <> _thsExternalLinkStyle t2,
     _thsLabelStyle = _thsLabelStyle t1 <> _thsLabelStyle t2,
     _thsNumericFieldStyle = _thsNumericFieldStyle t1 <> _thsNumericFieldStyle t2,
+    _thsOptionBtnOnStyle = _thsOptionBtnOnStyle t1 <> _thsOptionBtnOnStyle t2,
+    _thsOptionBtnOffStyle = _thsOptionBtnOffStyle t1 <> _thsOptionBtnOffStyle t2,
     _thsRadioStyle = _thsRadioStyle t1 <> _thsRadioStyle t2,
     _thsRadioWidth = _thsRadioWidth t2,
     _thsScrollOverlay = _thsScrollOverlay t2,
@@ -201,6 +211,8 @@
     _thsTextAreaStyle = _thsTextAreaStyle t1 <> _thsTextAreaStyle t2,
     _thsTextFieldStyle = _thsTextFieldStyle t1 <> _thsTextFieldStyle t2,
     _thsTimeFieldStyle = _thsTimeFieldStyle t1 <> _thsTimeFieldStyle t2,
+    _thsToggleBtnOnStyle = _thsToggleBtnOnStyle t1 <> _thsToggleBtnOnStyle t2,
+    _thsToggleBtnOffStyle = _thsToggleBtnOffStyle t1 <> _thsToggleBtnOffStyle t2,
     _thsTooltipStyle = _thsTooltipStyle t1 <> _thsTooltipStyle t2,
     _thsUserStyleMap = _thsUserStyleMap t1 <> _thsUserStyleMap t2,
     _thsUserColorMap = _thsUserColorMap t1 <> _thsUserColorMap t2
diff --git a/src/Monomer/Core/WidgetTypes.hs b/src/Monomer/Core/WidgetTypes.hs
--- a/src/Monomer/Core/WidgetTypes.hs
+++ b/src/Monomer/Core/WidgetTypes.hs
@@ -18,7 +18,6 @@
 
 import Control.Concurrent (MVar)
 import Control.Lens (ALens')
-import Data.ByteString.Lazy (ByteString)
 import Data.Default
 import Data.Map.Strict (Map)
 import Data.Sequence (Seq)
@@ -226,6 +225,11 @@
   --   for WebSockets and similar data sources. It receives a function that
   --   can be used to send messages back to the producer owner.
   | forall i . Typeable i => RunProducer WidgetId Path ((i -> IO ()) -> IO ())
+  -- | Runs an asynchronous tasks in the render thread. It is mandatory to
+  --   return a message that will be sent to the task owner (this is the only
+  --   way to feed data back). This should only be used when implementing low
+  --   level rendering widgets that need to create API specific resources.
+  | forall i . Typeable i => RunInRenderThread WidgetId Path (IO i)
 
 instance Eq e => Eq (WidgetRequest s e) where
   IgnoreParentEvents == IgnoreParentEvents = True
@@ -287,6 +291,8 @@
 data WidgetEnv s e = WidgetEnv {
   -- | The OS of the host.
   _weOs :: Text,
+  -- | Device pixel rate.
+  _weDpr :: Double,
   -- | Provides helper funtions for calculating text size.
   _weFontManager :: FontManager,
   -- | Returns the information of a node given a path from root, if any.
@@ -704,6 +710,7 @@
   show SendMessage{} = "SendMessage"
   show RunTask{} = "RunTask"
   show RunProducer{} = "RunProducer"
+  show RunInRenderThread{} = "RunInRenderThread"
 
 instance Show (WidgetResult s e) where
   show result = "WidgetResult "
diff --git a/src/Monomer/Graphics/Types.hs b/src/Monomer/Graphics/Types.hs
--- a/src/Monomer/Graphics/Types.hs
+++ b/src/Monomer/Graphics/Types.hs
@@ -40,7 +40,7 @@
   _colorG :: {-# UNPACK #-} !Int,
   _colorB :: {-# UNPACK #-} !Int,
   _colorA :: {-# UNPACK #-} !Double
-} deriving (Eq, Show, Generic)
+} deriving (Eq, Show, Ord, Generic)
 
 instance Default Color where
   def = Color 255 255 255 1.0
diff --git a/src/Monomer/Helper.hs b/src/Monomer/Helper.hs
--- a/src/Monomer/Helper.hs
+++ b/src/Monomer/Helper.hs
@@ -11,6 +11,7 @@
 -}
 module Monomer.Helper where
 
+import Control.Exception (SomeException, catch)
 import Control.Monad.IO.Class (MonadIO)
 import Data.Sequence (Seq(..))
 
@@ -48,6 +49,10 @@
   Just val -> val :<| seqCatMaybes xs
   _ -> seqCatMaybes xs
 
+-- | Folds a list of functions over an initial value.
+applyFnList :: [a -> a] -> a -> a
+applyFnList fns initial = foldl (flip ($)) initial fns
+
 -- | Returns the maximum value of a given floating type.
 maxNumericValue :: (RealFloat a) => a
 maxNumericValue = x where
@@ -59,3 +64,7 @@
 -- | Restricts a value to a given range.
 clamp :: (Ord a) => a -> a -> a -> a
 clamp mn mx = max mn . min mx
+
+-- | Catches any exception thrown by the provided action
+catchAny :: IO a -> (SomeException -> IO a) -> IO a
+catchAny = catch
diff --git a/src/Monomer/Main/Core.hs b/src/Monomer/Main/Core.hs
--- a/src/Monomer/Main/Core.hs
+++ b/src/Monomer/Main/Core.hs
@@ -47,6 +47,7 @@
 import Monomer.Main.Util
 import Monomer.Main.WidgetTask
 import Monomer.Graphics
+import Monomer.Helper (catchAny)
 import Monomer.Widgets.Composite
 
 import qualified Monomer.Lens as L
@@ -152,6 +153,7 @@
 
   let wenv = WidgetEnv {
     _weOs = os,
+    _weDpr = dpr,
     _weFontManager = fontManager,
     _weFindByPath = const Nothing,
     _weMainButton = mainBtn,
@@ -258,6 +260,7 @@
   let contextBtn = fromMaybe BtnRight (_apcContextButton config)
   let wenv = WidgetEnv {
     _weOs = _mlOS,
+    _weDpr = dpr,
     _weFontManager = fontManager,
     _weFindByPath = findWidgetByPath wenv _mlWidgetRoot,
     _weMainButton = mainBtn,
@@ -409,6 +412,11 @@
   return state
 handleRenderMsg window renderer fontMgr state (MsgRemoveImage name) = do
   deleteImage renderer name
+  return state
+handleRenderMsg window renderer fontMgr state (MsgRunInRender chan task) = do
+  flip catchAny print $ do
+    value <- task
+    atomically $ writeTChan chan value
   return state
 
 renderWidgets
diff --git a/src/Monomer/Main/Handlers.hs b/src/Monomer/Main/Handlers.hs
--- a/src/Monomer/Main/Handlers.hs
+++ b/src/Monomer/Main/Handlers.hs
@@ -29,7 +29,7 @@
 import Control.Lens
   ((&), (^.), (^?), (.~), (?~), (%~), (.=), (?=), (%=), (%%~), _Just, _1, _2, ix, at, use)
 import Control.Monad.STM (atomically)
-import Control.Concurrent.STM.TChan (TChan, newTChanIO, writeTChan)
+import Control.Concurrent.STM.TChan (TChan, newTChanIO, readTChan, writeTChan)
 import Control.Applicative ((<|>))
 import Control.Monad
 import Control.Monad.IO.Class
@@ -251,6 +251,7 @@
     SendMessage wid msg -> handleSendMessage wid msg step
     RunTask wid path handler -> handleRunTask wid path handler step
     RunProducer wid path handler -> handleRunProducer wid path handler step
+    RunInRenderThread wid path handler -> handleRunInRenderThread wid path handler step
 
 -- | Resizes the current root, and marks the render and resized flags.
 handleResizeWidgets
@@ -653,6 +654,23 @@
 
   return previousStep
 
+handleRunInRenderThread
+  :: forall s e m i . (MonomerM s e m, Typeable i)
+  => WidgetId
+  -> Path
+  -> IO i
+  -> HandlerStep s e
+  -> m (HandlerStep s e)
+handleRunInRenderThread widgetId path handler previousStep = do
+  renderChannel <- use L.renderChannel
+
+  handleRunTask widgetId path (taskWrapper renderChannel) previousStep
+  where
+    taskWrapper renderChannel = do
+      msgChan <- newTChanIO
+      atomically $ writeTChan renderChannel (MsgRunInRender msgChan handler)
+      atomically $ readTChan msgChan
+
 sendMessage :: TChan e -> e -> IO ()
 sendMessage channel message = atomically $ writeTChan channel message
 
@@ -690,9 +708,7 @@
   Move point -> do
     (target, hoverEvts) <- addHoverEvents wenv widgetRoot point
     -- Update input status
-    status <- use L.inputStatus
-    L.inputStatus . L.mousePosPrev .= status ^. L.mousePos
-    L.inputStatus . L.mousePos .= point
+    updateInputStatusMousePos point
     -- Drag event
     mainPress <- use L.mainBtnPress
     draggedMsg <- getDraggedMsgInfo
@@ -716,6 +732,7 @@
     when (btn == mainBtn) $
       L.mainBtnPress .= fmap (, point) curr
 
+    updateInputStatusMousePos point
     L.inputStatus . L.buttons . at btn ?= BtnPressed
 
     SDLE.captureMouse True
@@ -739,6 +756,7 @@
           Just (path, msg) -> [(Drop point path msg, target) | not isPressed]
           _ -> []
 
+    updateInputStatusMousePos point
     L.inputStatus . L.buttons . at btn ?= BtnReleased
 
     SDLE.captureMouse False
@@ -753,6 +771,13 @@
   -- This will only be reached from `handleSystemEvents`
   Click point btn clicks -> findEvtTargetByPoint wenv widgetRoot evt point
   _ -> return [(evt, Nothing)]
+
+updateInputStatusMousePos :: MonomerM s e m => Point -> m ()
+updateInputStatusMousePos point = do
+  -- Update input status
+  status <- use L.inputStatus
+  L.inputStatus . L.mousePosPrev .= status ^. L.mousePos
+  L.inputStatus . L.mousePos .= point
 
 addHoverEvents
   :: MonomerM s e m
diff --git a/src/Monomer/Main/Types.hs b/src/Monomer/Main/Types.hs
--- a/src/Monomer/Main/Types.hs
+++ b/src/Monomer/Main/Types.hs
@@ -50,7 +50,7 @@
   = MsgRender (WidgetEnv s e) (WidgetNode s e)
   | MsgResize Size
   | MsgRemoveImage Text
-  deriving Show
+  | forall i . MsgRunInRender (TChan i) (IO i)
 
 {-|
 Requirements for periodic rendering by a widget. Start time is stored to
diff --git a/src/Monomer/Widgets/Composite.hs b/src/Monomer/Widgets/Composite.hs
--- a/src/Monomer/Widgets/Composite.hs
+++ b/src/Monomer/Widgets/Composite.hs
@@ -70,7 +70,6 @@
 import Data.Sequence (Seq(..), (|>), (<|), fromList)
 import Data.Typeable (Typeable, cast, typeOf)
 
-import qualified Data.ByteString.Lazy as BSL
 import qualified Data.Map.Strict as M
 import qualified Data.Sequence as Seq
 
@@ -867,6 +866,7 @@
 toParentReq _ (SendMessage wid message) = Just (SendMessage wid message)
 toParentReq _ (RunTask wid path action) = Just (RunTask wid path action)
 toParentReq _ (RunProducer wid path action) = Just (RunProducer wid path action)
+toParentReq _ (RunInRenderThread wid path action) = Just (RunInRenderThread wid path action)
 
 collectWidgetKeys
   :: Map WidgetKey (WidgetNode s e)
@@ -883,6 +883,7 @@
 convertWidgetEnv :: WidgetEnv sp ep -> WidgetKeyMap s e -> s -> WidgetEnv s e
 convertWidgetEnv wenv widgetKeyMap model = WidgetEnv {
   _weOs = _weOs wenv,
+  _weDpr = _weDpr wenv,
   _weFontManager = _weFontManager wenv,
   _weFindByPath = _weFindByPath wenv,
   _weMainButton = _weMainButton wenv,
diff --git a/src/Monomer/Widgets/Container.hs b/src/Monomer/Widgets/Container.hs
--- a/src/Monomer/Widgets/Container.hs
+++ b/src/Monomer/Widgets/Container.hs
@@ -329,6 +329,9 @@
   -- | Scissor to apply to child widgets. This is not the same as the widget
   --   enabled by containerUseScissor
   containerChildrenScissor :: Maybe Rect,
+  -- | If True, the container will render its background and border. In some
+  --   cases passing rendering control to children is useful. Defaults to True.
+  containerDrawDecorations :: Bool,
   -- | The layout direction generated by this widget. If one is indicated, it
   --   can be used by widgets such as "Monomer.Widgets.Singles.Spacer"
   containerLayoutDirection :: LayoutDirection,
@@ -391,6 +394,7 @@
     containerAddStyleReq = True,
     containerChildrenOffset = Nothing,
     containerChildrenScissor = Nothing,
+    containerDrawDecorations = True,
     containerLayoutDirection = LayoutNone,
     containerIgnoreEmptyArea = False,
     containerUseCustomCursor = False,
@@ -471,11 +475,17 @@
   cOffset = containerChildrenOffset container
   updateCWenv = containerUpdateCWenv container
   layoutDirection = containerLayoutDirection container
+
+  pViewport = node ^. L.info . L.viewport
+  cViewport = cnode ^. L.info . L.viewport
+  newViewport = fromMaybe def (intersectRects pViewport cViewport)
+
   offsetWenv !wenv
-    | isJust cOffset = updateWenvOffset container wenv node
+    | isJust cOffset = updateWenvOffset container wenv node newViewport
     | otherwise = wenv
   !directionWenv = wenv
     & L.layoutDirection .~ layoutDirection
+
   !newWenv = updateCWenv (offsetWenv directionWenv) node cnode cidx
 
 {-|
@@ -487,15 +497,16 @@
   :: Container s e a  -- ^ The container config
   -> WidgetEnv s e    -- ^ The widget environment.
   -> WidgetNode s e   -- ^ The widget node.
+  -> Rect             -- ^ The target viewport.
   -> WidgetEnv s e    -- ^ THe updated widget environment.
-updateWenvOffset container wenv node = newWenv where
+updateWenvOffset container wenv node viewport = newWenv where
   cOffset = containerChildrenOffset container
   offset = fromMaybe def cOffset
-  accumOffset = addPoint offset (wenv ^. L.offset)
-  viewport = node ^. L.info . L.viewport
+
   updateMain (path, point)
     | isNodeParentOfPath node path = (path, addPoint (negPoint offset) point)
     | otherwise = (path, point)
+
   newWenv = wenv
     & L.viewport .~ moveRect (negPoint offset) viewport
     & L.inputStatus . L.mousePos %~ addPoint (negPoint offset)
@@ -1089,7 +1100,7 @@
   -> IO ()
 renderWrapper container wenv !node !renderer =
   drawInScissor renderer useScissor viewport $
-    drawStyledAction renderer viewport style $ \_ -> do
+    drawStyledAction_ renderer drawDecorations viewport style $ \_ -> do
       renderBefore wenv node renderer
 
       drawInScissor renderer useChildrenScissor childrenScissorRect $ do
@@ -1109,6 +1120,7 @@
   where
     style = containerGetCurrentStyle container wenv node
     updateCWenv = getUpdateCWenv container
+    drawDecorations = containerDrawDecorations container
     useScissor = containerUseScissor container
     childrenScissor = containerChildrenScissor container
     offset = containerChildrenOffset container
diff --git a/src/Monomer/Widgets/Containers/Box.hs b/src/Monomer/Widgets/Containers/Box.hs
--- a/src/Monomer/Widgets/Containers/Box.hs
+++ b/src/Monomer/Widgets/Containers/Box.hs
@@ -42,6 +42,7 @@
 
 import qualified Data.Sequence as Seq
 
+import Monomer.Helper (applyFnList)
 import Monomer.Widgets.Container
 import Monomer.Widgets.Containers.Stack
 
@@ -78,7 +79,7 @@
 data BoxCfg s e = BoxCfg {
   _boxExpandContent :: Maybe Bool,
   _boxIgnoreEmptyArea :: Maybe Bool,
-  _boxSizeReqUpdater :: Maybe SizeReqUpdater,
+  _boxSizeReqUpdater :: [SizeReqUpdater],
   _boxMergeRequired :: Maybe (s -> s -> Bool),
   _boxAlignH :: Maybe AlignH,
   _boxAlignV :: Maybe AlignV,
@@ -96,7 +97,7 @@
   def = BoxCfg {
     _boxExpandContent = Nothing,
     _boxIgnoreEmptyArea = Nothing,
-    _boxSizeReqUpdater = Nothing,
+    _boxSizeReqUpdater = [],
     _boxMergeRequired = Nothing,
     _boxAlignH = Nothing,
     _boxAlignV = Nothing,
@@ -114,7 +115,7 @@
   (<>) t1 t2 = BoxCfg {
     _boxExpandContent = _boxExpandContent t2 <|> _boxExpandContent t1,
     _boxIgnoreEmptyArea = _boxIgnoreEmptyArea t2 <|> _boxIgnoreEmptyArea t1,
-    _boxSizeReqUpdater = _boxSizeReqUpdater t2 <|> _boxSizeReqUpdater t1,
+    _boxSizeReqUpdater = _boxSizeReqUpdater t1 <> _boxSizeReqUpdater t2,
     _boxMergeRequired = _boxMergeRequired t2 <|> _boxMergeRequired t1,
     _boxAlignH = _boxAlignH t2 <|> _boxAlignH t1,
     _boxAlignV = _boxAlignV t2 <|> _boxAlignV t1,
@@ -138,7 +139,7 @@
 
 instance CmbSizeReqUpdater (BoxCfg s e) where
   sizeReqUpdater updater = def {
-    _boxSizeReqUpdater = Just updater
+    _boxSizeReqUpdater = [updater]
   }
 
 instance CmbMergeRequired (BoxCfg s e) s where
@@ -384,11 +385,11 @@
 
   getSizeReq :: ContainerGetSizeReqHandler s e
   getSizeReq wenv node children = newSizeReq where
-    updateSizeReq = fromMaybe id (_boxSizeReqUpdater config)
+    sizeReqFns = _boxSizeReqUpdater config
     child = Seq.index children 0
     newReqW = child ^. L.info . L.sizeReqW
     newReqH = child ^. L.info . L.sizeReqH
-    newSizeReq = updateSizeReq (newReqW, newReqH)
+    newSizeReq = applyFnList sizeReqFns (newReqW, newReqH)
 
   resize wenv node viewport children = resized where
     style = getCurrentStyle wenv node
diff --git a/src/Monomer/Widgets/Containers/Dropdown.hs b/src/Monomer/Widgets/Containers/Dropdown.hs
--- a/src/Monomer/Widgets/Containers/Dropdown.hs
+++ b/src/Monomer/Widgets/Containers/Dropdown.hs
@@ -496,7 +496,7 @@
       scOffset = wenv ^. L.offset
       offset = _ddsOffset state
       totalOffset = addPoint scOffset offset
-      cwenv = updateWenvOffset container wenv node
+      cwenv = updateWenvOffset container wenv node listOverlayVp
         & L.viewport .~ listOverlayVp
 
   renderArrow renderer style contentArea =
diff --git a/src/Monomer/Widgets/Containers/Grid.hs b/src/Monomer/Widgets/Containers/Grid.hs
--- a/src/Monomer/Widgets/Containers/Grid.hs
+++ b/src/Monomer/Widgets/Containers/Grid.hs
@@ -32,6 +32,7 @@
 
 import qualified Data.Sequence as Seq
 
+import Monomer.Helper (applyFnList)
 import Monomer.Widgets.Container
 
 import qualified Monomer.Lens as L
@@ -42,17 +43,17 @@
 - 'sizeReqUpdater': allows modifying the 'SizeReq' generated by the grid.
 -}
 newtype GridCfg = GridCfg {
-  _grcSizeReqUpdater :: Maybe SizeReqUpdater
+  _grcSizeReqUpdater :: [SizeReqUpdater]
 }
 
 instance Default GridCfg where
   def = GridCfg {
-    _grcSizeReqUpdater = Nothing
+    _grcSizeReqUpdater = []
   }
 
 instance Semigroup GridCfg where
   (<>) s1 s2 = GridCfg {
-    _grcSizeReqUpdater = _grcSizeReqUpdater s2 <|> _grcSizeReqUpdater s1
+    _grcSizeReqUpdater = _grcSizeReqUpdater s1 <> _grcSizeReqUpdater s2
   }
 
 instance Monoid GridCfg where
@@ -60,7 +61,7 @@
 
 instance CmbSizeReqUpdater GridCfg where
   sizeReqUpdater updater = def {
-    _grcSizeReqUpdater = Just updater
+    _grcSizeReqUpdater = [updater]
   }
 
 -- | Creates a grid of items with the same width.
@@ -96,11 +97,11 @@
   isVertical = not isHorizontal
 
   getSizeReq wenv node children = newSizeReq where
-    updateSizeReq = fromMaybe id (_grcSizeReqUpdater config)
+    sizeReqFns = _grcSizeReqUpdater config
     vchildren = Seq.filter (_wniVisible . _wnInfo) children
     newSizeReqW = getDimSizeReq isHorizontal (_wniSizeReqW . _wnInfo) vchildren
     newSizeReqH = getDimSizeReq isVertical (_wniSizeReqH . _wnInfo) vchildren
-    newSizeReq = updateSizeReq (newSizeReqW, newSizeReqH)
+    newSizeReq = applyFnList sizeReqFns (newSizeReqW, newSizeReqH)
 
   getDimSizeReq mainAxis accesor vchildren
     | Seq.null vreqs = fixedSize 0
diff --git a/src/Monomer/Widgets/Containers/Scroll.hs b/src/Monomer/Widgets/Containers/Scroll.hs
--- a/src/Monomer/Widgets/Containers/Scroll.hs
+++ b/src/Monomer/Widgets/Containers/Scroll.hs
@@ -350,6 +350,7 @@
     containerLayoutDirection = layoutDirection,
     containerGetBaseStyle = getBaseStyle,
     containerGetCurrentStyle = scrollCurrentStyle,
+    containerUpdateCWenv = updateCWenv,
     containerInit = init,
     containerMerge = merge,
     containerFindByPoint = findByPoint,
@@ -385,6 +386,28 @@
         & L.children . ix 0 . L.info . L.style .~ childStyle
       | otherwise = node
 
+  -- This is overriden to account for space used by scroll bars
+  updateCWenv wenv node cnode cidx = newWenv where
+    theme = currentTheme wenv node
+    barW = fromMaybe (theme ^. L.scrollBarWidth) (_scBarWidth config)
+    overlay = fromMaybe (theme ^. L.scrollOverlay) (_scScrollOverlay config)
+
+    ScrollContext{..} = scrollStatus config wenv node state (Point 0 0)
+    style = currentStyle wenv node
+    carea = getContentArea node style
+
+    -- barH consumes vertical space, barV consumes horizontal space
+    barH
+      | hScrollRequired && not overlay = barW
+      | otherwise = 0
+    barV
+      | vScrollRequired && not overlay = barW
+      | otherwise = 0
+    clientArea = subtractFromRect carea 0 barV 0 barH
+
+    newWenv = wenv
+      & L.viewport .~ moveRect (negPoint offset) (fromMaybe carea clientArea)
+
   init wenv node = resultNode newNode where
     newNode = checkFwdStyle wenv node
 
@@ -393,14 +416,15 @@
       & L.widget .~ makeScroll config oldState
 
   findByPoint wenv node start point = result where
-    sctx = scrollStatus config wenv node state point
+    -- The point argument already has offset applied
+    scrollPoint = subPoint point offset
+    sctx = scrollStatus config wenv node state scrollPoint
     mouseInScroll
       =  (hMouseInScroll sctx && hScrollRequired sctx)
       || (vMouseInScroll sctx && vScrollRequired sctx)
-    childPoint = addPoint point offset
 
     child = Seq.index (node ^. L.children) 0
-    childHovered = isPointInNodeVp child childPoint
+    childHovered = isPointInNodeVp child point
     childDragged = isNodePressed wenv child
     result
       | (not mouseInScroll && childHovered) || childDragged = Just 0
@@ -425,33 +449,34 @@
         | otherwise = Nothing
 
     ButtonAction point btn status _ -> result where
-      leftPressed = status == BtnPressed && btn == wenv ^. L.mainButton
-      btnReleased = status == BtnReleased && btn == wenv ^. L.mainButton
+      mainPressed = status == BtnPressed && btn == wenv ^. L.mainButton
+      mainReleased = status == BtnReleased && btn == wenv ^. L.mainButton
 
       isDragging = isJust $ _sstDragging state
-      startDrag = leftPressed && not isDragging
 
-      jumpScrollH = btnReleased && not isDragging && hMouseInScroll sctx
-      jumpScrollV = btnReleased && not isDragging && vMouseInScroll sctx
+      startDragH = mainPressed && not isDragging && hMouseInThumb sctx
+      startDragV = mainPressed && not isDragging && vMouseInThumb sctx
 
-      mouseInScroll = hMouseInScroll sctx || vMouseInScroll sctx
+      jumpScrollH = mainPressed && not isDragging && hMouseInScroll sctx
+      jumpScrollV = mainPressed && not isDragging && vMouseInScroll sctx
+
       mouseInThumb = hMouseInThumb sctx || vMouseInThumb sctx
+      mouseInScroll = hMouseInScroll sctx || vMouseInScroll sctx
 
       newState
-        | startDrag && hMouseInThumb sctx = state { _sstDragging = Just HBar }
-        | startDrag && vMouseInThumb sctx = state { _sstDragging = Just VBar }
+        | startDragH = state { _sstDragging = Just HBar }
+        | startDragV = state { _sstDragging = Just VBar }
         | jumpScrollH = updateScrollThumb state HBar point contentArea sctx
         | jumpScrollV = updateScrollThumb state VBar point contentArea sctx
-        | btnReleased = state { _sstDragging = Nothing }
+        | mainReleased = state { _sstDragging = Nothing }
         | otherwise = state
 
       newRes = rebuildWidget wenv node newState
       handledResult = Just $ newRes
         & L.requests <>~ Seq.fromList scrollReqs
       result
-        | leftPressed && mouseInThumb = handledResult
-        | btnReleased && mouseInScroll = handledResult
-        | btnReleased && isDragging = handledResult
+        | mainPressed && (mouseInThumb || mouseInScroll) = handledResult
+        | mainReleased && isDragging = handledResult
         | otherwise = Nothing
 
     Move point | isJust dragging -> result where
diff --git a/src/Monomer/Widgets/Containers/Stack.hs b/src/Monomer/Widgets/Containers/Stack.hs
--- a/src/Monomer/Widgets/Containers/Stack.hs
+++ b/src/Monomer/Widgets/Containers/Stack.hs
@@ -35,6 +35,7 @@
 
 import qualified Data.Sequence as Seq
 
+import Monomer.Helper (applyFnList)
 import Monomer.Widgets.Container
 
 import qualified Monomer.Lens as L
@@ -49,19 +50,19 @@
 -}
 data StackCfg = StackCfg {
   _stcIgnoreEmptyArea :: Maybe Bool,
-  _stcSizeReqUpdater :: Maybe SizeReqUpdater
+  _stcSizeReqUpdater :: [SizeReqUpdater]
 }
 
 instance Default StackCfg where
   def = StackCfg {
     _stcIgnoreEmptyArea = Nothing,
-    _stcSizeReqUpdater = Nothing
+    _stcSizeReqUpdater = []
   }
 
 instance Semigroup StackCfg where
   (<>) s1 s2 = StackCfg {
     _stcIgnoreEmptyArea = _stcIgnoreEmptyArea s2 <|> _stcIgnoreEmptyArea s1,
-    _stcSizeReqUpdater = _stcSizeReqUpdater s2 <|> _stcSizeReqUpdater s1
+    _stcSizeReqUpdater = _stcSizeReqUpdater s1 <> _stcSizeReqUpdater s2
   }
 
 instance Monoid StackCfg where
@@ -74,7 +75,7 @@
 
 instance CmbSizeReqUpdater StackCfg where
   sizeReqUpdater updater = def {
-    _stcSizeReqUpdater = Just updater
+    _stcSizeReqUpdater = [updater]
   }
 
 -- | Creates a horizontal stack.
@@ -121,11 +122,11 @@
   ignoreEmptyArea = fromMaybe False (_stcIgnoreEmptyArea config)
 
   getSizeReq wenv node children = newSizeReq where
-    updateSizeReq = fromMaybe id (_stcSizeReqUpdater config)
+    sizeReqFns = _stcSizeReqUpdater config
     vchildren = Seq.filter (_wniVisible . _wnInfo) children
     newSizeReqW = getDimSizeReq isHorizontal (_wniSizeReqW . _wnInfo) vchildren
     newSizeReqH = getDimSizeReq isVertical (_wniSizeReqH . _wnInfo) vchildren
-    newSizeReq = updateSizeReq (newSizeReqW, newSizeReqH)
+    newSizeReq = applyFnList sizeReqFns (newSizeReqW, newSizeReqH)
 
   getDimSizeReq mainAxis accesor vchildren
     | Seq.null vreqs = fixedSize 0
diff --git a/src/Monomer/Widgets/Single.hs b/src/Monomer/Widgets/Single.hs
--- a/src/Monomer/Widgets/Single.hs
+++ b/src/Monomer/Widgets/Single.hs
@@ -236,11 +236,14 @@
   -- | True if border and padding should be added to size requirement. Defaults
   --   to True.
   singleAddStyleReq :: Bool,
+  -- | If True, the widget will render its background and border. Defaults to
+  --   True.
+  singleDrawDecorations :: Bool,
   -- | True if focus should be requested when mouse button is pressed (before
   --   click). Defaults to True.
   singleFocusOnBtnPressed :: Bool,
-  -- | True if style cursor should be ignored. If it's False, cursor changes need
-  --   to be handled in custom code. Defaults to False.
+  -- | True if style cursor should be ignored. If it's False, cursor changes
+  --   need to be handled in custom code. Defaults to False.
   singleUseCustomCursor :: Bool,
   -- | If true, it will ignore extra space assigned by the parent container, but
   --   it will not use more space than assigned. Defaults to False.
@@ -277,6 +280,7 @@
   def = Single {
     singleAddStyleReq = True,
     singleFocusOnBtnPressed = True,
+    singleDrawDecorations = True,
     singleUseCustomCursor = False,
     singleUseCustomSize = False,
     singleUseScissor = False,
@@ -597,10 +601,11 @@
   -> IO ()
 renderWrapper single wenv node renderer =
   drawInScissor renderer useScissor viewport $
-    drawStyledAction renderer viewport style $ \_ ->
+    drawStyledAction_ renderer drawDecorations viewport style $ \_ ->
       handler wenv node renderer
   where
     handler = singleRender single
+    drawDecorations = singleDrawDecorations single
     useScissor = singleUseScissor single
     style = singleGetCurrentStyle single wenv node
     viewport = node ^. L.info . L.viewport
diff --git a/src/Monomer/Widgets/Singles/Button.hs b/src/Monomer/Widgets/Singles/Button.hs
--- a/src/Monomer/Widgets/Singles/Button.hs
+++ b/src/Monomer/Widgets/Singles/Button.hs
@@ -50,15 +50,15 @@
 - 'ellipsis': if ellipsis should be used for overflown text.
 - 'multiline': if text may be split in multiple lines.
 - 'maxLines': maximum number of text lines to show.
+- 'resizeFactor': flexibility to have more or less spaced assigned.
+- 'resizeFactorW': flexibility to have more or less horizontal spaced assigned.
+- 'resizeFactorH': flexibility to have more or less vertical spaced assigned.
 - 'onFocus': event to raise when focus is received.
 - 'onFocusReq': 'WidgetRequest' to generate when focus is received.
 - 'onBlur': event to raise when focus is lost.
 - 'onBlurReq': 'WidgetRequest' to generate when focus is lost.
 - 'onClick': event to raise when button is clicked.
 - 'onClickReq': 'WidgetRequest' to generate when button is clicked.
-- 'resizeFactor': flexibility to have more or less spaced assigned.
-- 'resizeFactorW': flexibility to have more or less horizontal spaced assigned.
-- 'resizeFactorH': flexibility to have more or less vertical spaced assigned.
 -}
 data ButtonCfg s e = ButtonCfg {
   _btnButtonType :: Maybe ButtonType,
@@ -190,13 +190,12 @@
 makeButton !caption !config = widget where
   widget = createContainer () def {
     containerAddStyleReq = False,
+    containerDrawDecorations = False,
     containerUseScissor = True,
     containerGetBaseStyle = getBaseStyle,
-    containerGetCurrentStyle = getCurrentStyle,
     containerInit = init,
     containerMerge = merge,
     containerHandleEvent = handleEvent,
-    containerGetSizeReq = getSizeReq,
     containerResize = resize
   }
 
@@ -210,14 +209,6 @@
     where
       ignoreTheme = _btnIgnoreTheme config == Just True
 
-  getCurrentStyle wenv node = styleState where
-    style = node ^. L.info . L.style
-    isEnabled = node ^. L.info . L.enabled
-    isActive = isNodeTreeActive wenv node
-    styleState
-      | isEnabled && isActive = fromMaybe def (_styleActive style)
-      | otherwise = currentStyle wenv node
-
   createChildNode wenv node = newNode where
     nodeStyle = node ^. L.info . L.style
     labelCfg = _btnLabelCfg config
@@ -256,13 +247,6 @@
       reqs = _btnOnClickReq config
       result = resultReqs node reqs
       resultFocus = resultReqs node [SetFocus (node ^. L.info . L.widgetId)]
-
-  getSizeReq :: ContainerGetSizeReqHandler s e
-  getSizeReq wenv node children = (newReqW, newReqH) where
-    -- Main section reqs
-    child = Seq.index children 0
-    newReqW = child ^. L.info . L.sizeReqW
-    newReqH = child ^. L.info . L.sizeReqH
 
   resize wenv node viewport children = resized where
     assignedAreas = Seq.fromList [viewport]
diff --git a/src/Monomer/Widgets/Singles/ExternalLink.hs b/src/Monomer/Widgets/Singles/ExternalLink.hs
--- a/src/Monomer/Widgets/Singles/ExternalLink.hs
+++ b/src/Monomer/Widgets/Singles/ExternalLink.hs
@@ -24,7 +24,6 @@
 ) where
 
 import Control.Applicative ((<|>))
-import Control.Exception (SomeException, catch)
 import Control.Lens ((&), (^.), (.~))
 import Data.Default
 import Data.Maybe
@@ -34,6 +33,7 @@
 import qualified Data.Sequence as Seq
 import qualified Data.Text as T
 
+import Monomer.Helper (catchAny)
 import Monomer.Widgets.Container
 import Monomer.Widgets.Singles.Label
 
@@ -46,15 +46,15 @@
 - 'ellipsis': if ellipsis should be used for overflown text.
 - 'multiline': if text may be split in multiple lines.
 - 'maxLines': maximum number of text lines to show.
+- 'resizeFactor': flexibility to have more or less spaced assigned.
+- 'resizeFactorW': flexibility to have more or less horizontal spaced assigned.
+- 'resizeFactorH': flexibility to have more or less vertical spaced assigned.
 - 'onFocus': event to raise when focus is received.
 - 'onFocusReq': 'WidgetRequest' to generate when focus is received.
 - 'onBlur': event to raise when focus is lost.
 - 'onBlurReq': 'WidgetRequest' to generate when focus is lost.
 - 'onClick': event to raise when button is clicked.
 - 'onClickReq': 'WidgetRequest' to generate when button is clicked.
-- 'resizeFactor': flexibility to have more or less spaced assigned.
-- 'resizeFactorW': flexibility to have more or less horizontal spaced assigned.
-- 'resizeFactorH': flexibility to have more or less vertical spaced assigned.
 -}
 data ExternalLinkCfg s e = ExternalLinkCfg {
   _elcLabelCfg :: LabelCfg s e,
@@ -150,12 +150,12 @@
 makeExternalLink !caption !url !config = widget where
   widget = createContainer () def {
     containerAddStyleReq = False,
+    containerDrawDecorations = False,
     containerUseScissor = True,
     containerGetBaseStyle = getBaseStyle,
     containerInit = init,
     containerMerge = merge,
     containerHandleEvent = handleEvent,
-    containerGetSizeReq = getSizeReq,
     containerResize = resize
   }
 
@@ -209,13 +209,6 @@
       result = resultReqs node requests
       resultFocus = resultReqs node [SetFocus (node ^. L.info . L.widgetId)]
 
-  getSizeReq :: ContainerGetSizeReqHandler s e
-  getSizeReq wenv node children = (newReqW, newReqH) where
-    -- Main section reqs
-    child = Seq.index children 0
-    newReqW = child ^. L.info . L.sizeReqW
-    newReqH = child ^. L.info . L.sizeReqH
-
   resize wenv node viewport children = resized where
     assignedAreas = Seq.fromList [viewport]
     resized = (resultNode node, assignedAreas)
@@ -232,6 +225,3 @@
 
 catchIgnore :: IO () -> IO ()
 catchIgnore task = catchAny task (const $ return ())
-
-catchAny :: IO a -> (SomeException -> IO a) -> IO a
-catchAny = catch
diff --git a/src/Monomer/Widgets/Singles/Image.hs b/src/Monomer/Widgets/Singles/Image.hs
--- a/src/Monomer/Widgets/Singles/Image.hs
+++ b/src/Monomer/Widgets/Singles/Image.hs
@@ -51,13 +51,14 @@
 import GHC.Generics
 import Network.HTTP.Client (HttpException(..), HttpExceptionContent(..))
 import Network.Wreq
+import Network.Wreq.Session (Session)
 
 import qualified Codec.Picture as Pic
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as BSL
 import qualified Data.Map as Map
 import qualified Data.Text as T
-import qualified Network.Wreq as Wreq
+import qualified Network.Wreq.Session as Sess
 
 import Monomer.Widgets.Single
 
@@ -438,7 +439,7 @@
   -- Get the image's MVar. One MVar per image name/path is created, to allow
   -- loading images in parallel. The main MVar is only taken until the image's
   -- MVar is retrieved/created.
-  sharedMap <- takeMVar sharedMapMVar
+  (sharedMap, sess) <- takeMVar sharedMapMVar >>= getImagesSession
   sharedImgMVar <- case useShared (Map.lookup key sharedMap) of
     Just mvar -> return mvar
     Nothing -> newMVar emptyImgState
@@ -450,7 +451,7 @@
     Just (oldState, oldCount) -> do
       return (ImageLoaded oldState, Just (oldState, oldCount + 1))
     Nothing -> do
-      res <- loadImage path
+      res <- loadImage sess path
 
       case res >>= decodeImage of
         Left loadError -> return (ImageFailed loadError, Nothing)
@@ -486,10 +487,10 @@
 imgKey :: Text -> Text
 imgKey path = "image-widget-key-" <> path
 
-loadImage :: Text -> IO (Either ImageLoadError ByteString)
-loadImage path
+loadImage :: Session -> Text -> IO (Either ImageLoadError ByteString)
+loadImage sess path
   | not (isUrl path) = loadLocal path
-  | otherwise = loadRemote path
+  | otherwise = loadRemote sess path
 
 decodeImage :: ByteString -> Either ImageLoadError DynamicImage
 decodeImage bs = either (Left . ImageInvalid) Right (Pic.decodeImage bs)
@@ -503,8 +504,8 @@
     then return . Left . ImageLoadFailed $ "Failed to load: " ++ path
     else return . Right $ content
 
-loadRemote :: Text -> IO (Either ImageLoadError ByteString)
-loadRemote name = do
+loadRemote :: Session -> Text -> IO (Either ImageLoadError ByteString)
+loadRemote sess name = do
   let path = T.unpack name
   eresp <- try $ getUrl path
 
@@ -513,7 +514,7 @@
     Right r -> Right $ respBody r
   where
     respBody r = BSL.toStrict $ r ^. responseBody
-    getUrl = getWith (defaults & checkResponse ?~ (\_ _ -> return ()))
+    getUrl = Sess.getWith (defaults & checkResponse ?~ (\_ _ -> return ())) sess
 
 remoteException
   :: String -> HttpException -> Either ImageLoadError ByteString
@@ -544,6 +545,17 @@
     isImageSource = ImagePath name,
     isImageData = Just (bs, size)
   }
+
+getImagesSession
+  :: Map Text WidgetShared
+  -> IO (Map Text WidgetShared, Sess.Session)
+getImagesSession sharedMap = case useShared (Map.lookup key sharedMap) of
+  Just sess -> return (sharedMap, sess)
+  Nothing -> do
+    sess <- Sess.newAPISession
+    return (sharedMap & at key ?~ WidgetShared sess, sess)
+  where
+    key = "image-widget-wreq-session"
 
 isUrl :: Text -> Bool
 isUrl url = T.isPrefixOf "http://" lurl || T.isPrefixOf "https://" lurl where
diff --git a/src/Monomer/Widgets/Singles/Label.hs b/src/Monomer/Widgets/Singles/Label.hs
--- a/src/Monomer/Widgets/Singles/Label.hs
+++ b/src/Monomer/Widgets/Singles/Label.hs
@@ -133,8 +133,8 @@
 labelCurrentStyle
   :: (WidgetEnv s e -> WidgetNode s e -> StyleState)
   -> LabelCfg s e
-labelCurrentStyle style = def {
-  _lscCurrentStyle = Just style
+labelCurrentStyle styleFn = def {
+  _lscCurrentStyle = Just styleFn
 }
 
 data LabelState = LabelState {
diff --git a/src/Monomer/Widgets/Util/Drawing.hs b/src/Monomer/Widgets/Util/Drawing.hs
--- a/src/Monomer/Widgets/Util/Drawing.hs
+++ b/src/Monomer/Widgets/Util/Drawing.hs
@@ -28,6 +28,7 @@
   drawArrowDown,
   drawTimesX,
   drawStyledAction,
+  drawStyledAction_,
   drawRoundedRect,
   drawRectRoundedBorder
 ) where
@@ -35,7 +36,6 @@
 import Control.Applicative ((<|>))
 import Control.Lens ((&), (^.), (^?), (^?!), (.~), non)
 import Control.Monad (forM_, void, when)
-import Data.ByteString (ByteString)
 import Data.Default
 import Data.Maybe
 import Data.Text (Text)
@@ -341,12 +341,27 @@
   -> StyleState       -- ^ The style defining background and border.
   -> (Rect -> IO ())  -- ^ The drawing actions. They receive the content area.
   -> IO ()            -- ^ The resulting action.
-drawStyledAction renderer rect style action = do
-  drawRect renderer rect _sstBgColor _sstRadius
+drawStyledAction renderer rect style action =
+  drawStyledAction_ renderer True rect style action
 
+{-|
+Runs a set of rendering operations after conditionally drawing the style's
+background, and before drawing the style's border.
+-}
+drawStyledAction_
+  :: Renderer         -- ^ The renderer.
+  -> Bool             -- ^ Whether background and border should be drawn.
+  -> Rect             -- ^ The rect where background and border will be drawn.
+  -> StyleState       -- ^ The style defining background and border.
+  -> (Rect -> IO ())  -- ^ The drawing actions. They receive the content area.
+  -> IO ()            -- ^ The resulting action.
+drawStyledAction_ renderer drawDecorations rect style action = do
+  when drawDecorations $
+    drawRect renderer rect _sstBgColor _sstRadius
+
   forM_ contentRect action
 
-  when (isJust _sstBorder) $
+  when (drawDecorations && isJust _sstBorder) $
     drawRectBorder renderer rect (fromJust _sstBorder) _sstRadius
   where
     StyleState{..} = style
diff --git a/src/Monomer/Widgets/Util/Widget.hs b/src/Monomer/Widgets/Util/Widget.hs
--- a/src/Monomer/Widgets/Util/Widget.hs
+++ b/src/Monomer/Widgets/Util/Widget.hs
@@ -40,7 +40,6 @@
 
 import Control.Concurrent (threadDelay)
 import Control.Lens ((&), (^#), (#~), (^.), (^?), (.~), (%~), _Just)
-import Data.ByteString.Lazy (ByteString)
 import Data.Default
 import Data.Maybe
 import Data.Map.Strict (Map)
diff --git a/test/unit/Monomer/Core/SizeReqSpec.hs b/test/unit/Monomer/Core/SizeReqSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/unit/Monomer/Core/SizeReqSpec.hs
@@ -0,0 +1,30 @@
+{-|
+Module      : Monomer.Core.SizeReqSpec
+Copyright   : (c) 2018 Francisco Vallarino
+License     : BSD-3-Clause (see the LICENSE file)
+Maintainer  : fjvallarino@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Unit tests for SizeReq functions.
+-}
+module Monomer.Core.SizeReqSpec (spec) where
+
+import Test.Hspec
+
+import Monomer.Core.Combinators
+import Monomer.Core.SizeReq
+
+spec :: Spec
+spec = describe "SizeReq" $ do
+  sizeReqUpdaterSpec
+
+sizeReqUpdaterSpec :: Spec
+sizeReqUpdaterSpec = describe "SizeReqUpdater" $ do
+  it "should update fixed size to minimum size" $ do
+    fixedToMinW 1 (width 100, height 100) `shouldBe` (minWidth 100, height 100)
+    fixedToMinH 1 (width 100, height 100) `shouldBe` (width 100, minHeight 100)
+
+  it "should update fixed size to expand size" $ do
+    fixedToExpandW 1 (width 100, height 100) `shouldBe` (expandWidth 100, height 100)
+    fixedToExpandH 1 (width 100, height 100) `shouldBe` (width 100, expandHeight 100)
diff --git a/test/unit/Monomer/Graphics/UtilSpec.hs b/test/unit/Monomer/Graphics/UtilSpec.hs
--- a/test/unit/Monomer/Graphics/UtilSpec.hs
+++ b/test/unit/Monomer/Graphics/UtilSpec.hs
@@ -1,5 +1,5 @@
 {-|
-Module      : Monomer.Common.CursorIconSpec
+Module      : Monomer.Graphics.UtilSpec
 Copyright   : (c) 2018 Francisco Vallarino
 License     : BSD-3-Clause (see the LICENSE file)
 Maintainer  : fjvallarino@gmail.com
diff --git a/test/unit/Monomer/TestUtil.hs b/test/unit/Monomer/TestUtil.hs
--- a/test/unit/Monomer/TestUtil.hs
+++ b/test/unit/Monomer/TestUtil.hs
@@ -159,6 +159,7 @@
 mockWenv :: s -> WidgetEnv s e
 mockWenv model = WidgetEnv {
   _weOs = "Mac OS X",
+  _weDpr = 2,
   _weFontManager = mockFontManager,
   _weFindByPath = const Nothing,
   _weMainButton = BtnLeft,
diff --git a/test/unit/Monomer/Widgets/Containers/BoxSpec.hs b/test/unit/Monomer/Widgets/Containers/BoxSpec.hs
--- a/test/unit/Monomer/Widgets/Containers/BoxSpec.hs
+++ b/test/unit/Monomer/Widgets/Containers/BoxSpec.hs
@@ -170,8 +170,7 @@
 
   where
     wenv = mockWenvEvtUnit ()
-    updater (rw, rh) = (minSize (rw ^. L.fixed) 2, maxSize (rh ^. L.fixed) 3)
-    boxNode = box_ [sizeReqUpdater updater] (label "Label")
+    boxNode = box_ [sizeReqUpdater (fixedToMinW 2), sizeReqUpdater (fixedToMaxH 3)] (label "Label")
     (sizeReqW, sizeReqH) = nodeGetSizeReq wenv boxNode
 
 resize :: Spec
diff --git a/test/unit/Monomer/Widgets/Containers/GridSpec.hs b/test/unit/Monomer/Widgets/Containers/GridSpec.hs
--- a/test/unit/Monomer/Widgets/Containers/GridSpec.hs
+++ b/test/unit/Monomer/Widgets/Containers/GridSpec.hs
@@ -133,8 +133,7 @@
 
   where
     wenv = mockWenv ()
-    updater (rw, rh) = (minSize (rw ^. L.fixed) 2, maxSize (rh ^. L.fixed) 3)
-    vgridNode = vgrid_ [sizeReqUpdater updater] [label "Label"]
+    vgridNode = vgrid_ [sizeReqUpdater (fixedToMinW 2), sizeReqUpdater (fixedToMaxH 3)] [label "Label"]
     (sizeReqW, sizeReqH) = nodeGetSizeReq wenv vgridNode
 
 resize :: Spec
diff --git a/test/unit/Monomer/Widgets/Containers/ScrollSpec.hs b/test/unit/Monomer/Widgets/Containers/ScrollSpec.hs
--- a/test/unit/Monomer/Widgets/Containers/ScrollSpec.hs
+++ b/test/unit/Monomer/Widgets/Containers/ScrollSpec.hs
@@ -19,6 +19,7 @@
 
 import Monomer.Core
 import Monomer.Core.Combinators
+import Monomer.Core.Themes.SampleThemes
 import Monomer.Event
 import Monomer.Graphics.ColorTable
 import Monomer.TestEventUtil
@@ -47,9 +48,48 @@
 
 handleEvent :: Spec
 handleEvent = describe "handleEvent" $ do
+  handleBarClick
   handleChildrenFocus
   handleNestedWheel
   handleMessageReset
+
+handleBarClick :: Spec
+handleBarClick = describe "handleBarClick" $ do
+  it "should click the first button" $ do
+    evts [evtClick point] `shouldBe` Seq.fromList [Button1]
+
+  it "should scroll right and click the third button" $ do
+    evts [evtPress midHBar, evtClick point] `shouldBe` Seq.fromList [Button2]
+
+  it "should scroll down and click the third button" $ do
+    evts [evtPress midVBar, evtClick point] `shouldBe` Seq.fromList [Button3]
+
+  it "should scroll down and click the third button" $ do
+    evts [evtPress midVBar, evtClick point] `shouldBe` Seq.fromList [Button3]
+
+  it "should scroll down and right and click the fourth button" $ do
+    evts [evtPress midHBar, evtPress midVBar, evtClick point] `shouldBe` Seq.fromList [Button4]
+
+  where
+    wenv = mockWenv ()
+      & L.theme .~ darkTheme
+      & L.windowSize .~ Size 640 480
+    point = Point 320 200
+    midHBar = Point 630 476
+    midVBar = Point 636 470
+    st = [width 640, height 480]
+    stackNode = vstack [
+        hstack [
+          button "Button 1" Button1 `styleBasic` st,
+          button "Button 2" Button2 `styleBasic` st
+        ],
+        hstack [
+          button "Button 3" Button3 `styleBasic` st,
+          button "Button 4" Button4 `styleBasic` st
+        ]
+      ]
+    scrollNode = scroll stackNode
+    evts es = nodeHandleEventEvts wenv es scrollNode
 
 handleChildrenFocus :: Spec
 handleChildrenFocus = describe "handleChildrenFocus" $ do
diff --git a/test/unit/Monomer/Widgets/Containers/StackSpec.hs b/test/unit/Monomer/Widgets/Containers/StackSpec.hs
--- a/test/unit/Monomer/Widgets/Containers/StackSpec.hs
+++ b/test/unit/Monomer/Widgets/Containers/StackSpec.hs
@@ -73,16 +73,24 @@
 getSizeReqUpdater :: Spec
 getSizeReqUpdater = describe "getSizeReqUpdater" $ do
   it "should return width = Min 50 2" $
-    sizeReqW `shouldBe` minSize 50 2
+    sizeReqW1 `shouldBe` minSize 50 2
 
   it "should return height = Max 20" $
-    sizeReqH `shouldBe` maxSize 20 3
+    sizeReqH1 `shouldBe` maxSize 20 3
 
+  it "should return width = Min 50 10" $
+    sizeReqW2 `shouldBe` minSize 50 10
+
+  it "should return height = Max 20 15" $
+    sizeReqH2 `shouldBe` maxSize 20 15
+
   where
     wenv = mockWenv ()
     updater (rw, rh) = (minSize (rw ^. L.fixed) 2, maxSize (rh ^. L.fixed) 3)
-    vstackNode = vstack_ [sizeReqUpdater updater] [label "Label"]
-    (sizeReqW, sizeReqH) = nodeGetSizeReq wenv vstackNode
+    vstackNode1 = vstack_ [sizeReqUpdater updater] [label "Label"]
+    vstackNode2 = vstack_ [sizeReqUpdater (fixedToMinW 10), sizeReqUpdater (fixedToMaxH 15)] [label "Label"]
+    (sizeReqW1, sizeReqH1) = nodeGetSizeReq wenv vstackNode1
+    (sizeReqW2, sizeReqH2) = nodeGetSizeReq wenv vstackNode2
 
 resize :: Spec
 resize = describe "resize" $ do
diff --git a/test/unit/Spec.hs b/test/unit/Spec.hs
--- a/test/unit/Spec.hs
+++ b/test/unit/Spec.hs
@@ -6,6 +6,7 @@
 import qualified SDL.Raw as Raw
 
 import qualified Monomer.Common.CursorIconSpec as CursorIconSpec
+import qualified Monomer.Core.SizeReqSpec as SizeReqSpec
 import qualified Monomer.Graphics.UtilSpec as GraphicsUtilSpec
 
 import qualified Monomer.Widgets.CompositeSpec as CompositeSpec
@@ -65,6 +66,7 @@
 spec :: Spec
 spec = do
   common
+  core
   graphics
   widgets
   widgetsUtil
@@ -72,6 +74,10 @@
 common :: Spec
 common = describe "Common" $ do
   CursorIconSpec.spec
+
+core :: Spec
+core = describe "Core" $ do
+  SizeReqSpec.spec
 
 graphics :: Spec
 graphics = describe "Graphics" $ do
