diff --git a/happstack-yui.cabal b/happstack-yui.cabal
--- a/happstack-yui.cabal
+++ b/happstack-yui.cabal
@@ -1,5 +1,5 @@
 Name          : happstack-yui
-Version       : 7351.3.0
+Version       : 7351.4.0
 Category      : Web, Happstack
 Synopsis      : Utilities for using YUI3 with Happstack.
 Homepage      : https://github.com/dag/happstack-yui
@@ -28,10 +28,11 @@
   of this package itself, as defined by the Package Versioning Policy.
   .
   The package also includes some utilities for working with the YUI CSS
-  modules.  In a future release, the plan is to add more utilities, for
-  example a combo handler for YUI modules written using JMacro, a bridge
-  between HSX and YUI Node objects and tools for making it easier to work
-  with modules like Uploader and Pjax.
+  modules and for using HSX to create YUI Node objects with JMacro.  In a
+  future release, the plan is to add more utilities, for example a combo
+  handler for YUI modules written using JMacro, tools for making it
+  easier to work with modules like Uploader and Pjax and a quasi-quoter for
+  compile-time syntax checked YQL queries.
   .
   For an example application, see:
   <https://github.com/dag/happstack-yui/blob/master/demo/happstack-yui-demo.hs>
@@ -2848,20 +2849,23 @@
   Other-Modules    :
     Happstack.Server.YUI.Bundle
   Build-Depends    :
-    base                  >= 4.3 && < 4.6,
-    boomerang             == 1.3.*,
-    bytestring            == 0.9.*,
-    containers            >= 0.4 && < 0.6,
-    happstack-jmacro      == 7.0.*,
-    happstack-server      == 7.0.*,
-    jmacro                == 0.5.*,
-    mtl                   >= 2.0 && < 2.2,
-    pretty                == 1.1.*,
-    template-haskell      >= 2.5 && < 2.8,
-    text                  == 0.11.*,
-    web-routes            == 0.27.*,
-    web-routes-boomerang  == 0.26.*,
-    web-routes-happstack  == 0.23.*
+    base                     >= 4.3 && < 4.6,
+    boomerang                == 1.3.*,
+    bytestring               == 0.9.*,
+    containers               >= 0.4 && < 0.6,
+    happstack-jmacro         == 7.0.*,
+    happstack-server         == 7.0.*,
+    hsp                      == 0.7.*,
+    interpolatedstring-perl6 == 0.9.*,
+    jmacro                   == 0.5.*,
+    mtl                      >= 2.0 && < 2.2,
+    pretty                   == 1.1.*,
+    template-haskell         >= 2.5 && < 2.8,
+    text                     == 0.11.*,
+    web-routes               == 0.27.*,
+    web-routes-boomerang     == 0.26.*,
+    web-routes-happstack     == 0.23.*,
+    web-routes-th            == 0.21.*
 
   if !flag(embed)
     CPP-Options   :
diff --git a/src/Happstack/Server/YUI.hs b/src/Happstack/Server/YUI.hs
--- a/src/Happstack/Server/YUI.hs
+++ b/src/Happstack/Server/YUI.hs
@@ -3,9 +3,15 @@
 module Happstack.Server.YUI
   ( -- * Combo Handler
     implYUISite
+  , YUISitemap(..)
+  , sitemap
+  , route
+  , showCSSComboURL
     -- * CSS utilities
   , gridUnit
   , fontSize
+    -- * JS utilities
+  , createNode
     -- * Bundle utilities
   , isYUIFile
   , readYUIFile
@@ -15,92 +21,113 @@
 
 import qualified Data.ByteString as B
 import qualified Data.Text       as T
+import qualified Web.Routes      as WR
 
-import Control.Category             (Category((.)))
-import Control.Monad                (guard, liftM, void)
-import Control.Monad.Trans          (liftIO)
-import Data.List                    (intercalate)
-import Data.Ratio                   ((%), numerator,denominator)
-import Data.Text.Encoding           (encodeUtf8)
-import Happstack.Server             (ServerPartT, Response, neverExpires, setHeaderM, badRequest, ok, toResponse, guessContentTypeM, mimeTypes, lookPairs)
-import Happstack.Server.Compression (compressedResponseFilter)
-import Happstack.Server.JMacro      ()
-import Happstack.Server.YUI.Bundle  (isYUIFile, readYUIFile)
-import Language.Javascript.JMacro   (JStat(BlockStat), jmacro, renderJs, jhFromList, toJExpr)
-import Text.Boomerang.TH            (derivePrinterParsers)
-import Text.PrettyPrint             (Style(mode), Mode(OneLineMode), renderStyle, style)
-import Text.Printf                  (printf)
-import Web.Routes                   (Site, RouteT, showURL)
-import Web.Routes.Boomerang         (Router, (<>), (</>), rList, anyString, eos, boomerangSiteRouteT)
-import Web.Routes.Happstack         (implSite)
+import Control.Category              (Category((.)))
+import Control.Monad                 (guard, liftM, void)
+import Control.Monad.Trans           (liftIO)
+import Data.List                     (intercalate)
+import Data.Ratio                    ((%), numerator,denominator)
+import Data.Text.Encoding            (encodeUtf8)
+import Happstack.Server              (Happstack, Response, neverExpires, setHeaderM, badRequest, ok, toResponse, guessContentTypeM, mimeTypes, lookPairs)
+import Happstack.Server.Compression  (compressedResponseFilter)
+import Happstack.Server.JMacro       ()
+import Happstack.Server.YUI.Bundle   (isYUIFile, readYUIFile)
+import HSP                           (XML, renderAsHTML)
+import Language.Javascript.JMacro    (JStat(BlockStat), JExpr, jmacro, jmacroE, renderJs, jhFromList, toJExpr)
+import Text.Boomerang.TH             (derivePrinterParsers)
+import Text.InterpolatedString.Perl6 (qq)
+import Text.PrettyPrint              (Style(mode), Mode(OneLineMode), renderStyle, style)
+import Text.Printf                   (printf)
+import Web.Routes                    (Site, RouteT)
+import Web.Routes.Boomerang          (Router, (<>), (</>), rList, anyString, eos, boomerangSiteRouteT)
+import Web.Routes.Happstack          (implSite)
+import Web.Routes.TH                 (derivePathInfo)
 
 #if !MIN_VERSION_template_haskell(2,7,0)
 import Language.Javascript.JMacro   (JStat(..), JExpr(..), JVal(..), Ident(..))
 #endif
 
-data Sitemap
-    = ComboHandlerURL
+-- | The @web-routes@ sitemap for the handler that serves up the YUI
+-- bundle.  You can embed this in your own sitemap, something like:
+--
+-- >data Sitemap = YUI YUISitemap | Home
+--
+-- The version number of the bundled YUI release is included in the routes
+-- for sake of cache-busting: the routes all respond with far-future
+-- expiration dates.
+--
+-- A 'WR.PathInfo' instance is provided in case you're using @web-routes@
+-- without @boomerang@.  This isn't recommended for production, however,
+-- since the YUI version number is not included in this case.
+data YUISitemap
+    = SeedURL
+    -- ^ [@\/YUI_VERSION\/@]
+    --     The YUI seed file plus the configuration for using our own
+    --     combo loader.
+    | ComboURL
+    -- ^ [@\/YUI_VERSION\/combo@]
+    --     The combo loader.
     | BundleURL [String]
+    -- ^ [@\/YUI_VERSION\/bundle\/\<filename\>@]
+    --     Get an individual file without combo loading.
     | ConfigURL
+    -- ^ [@\/YUI_VERSION\/config@]
+    --     The code for configuring YUI to use our own combo loader.  Not needed
+    --     if you use the seed file mentioned above.
     | CSSComboURL
-    | SeedURL
+    -- ^ [@\/YUI_VERSION\/css@]
+    --     A specialized combo loader for CSS modules, for use in @\<link\/\>@
+    --     tags.  Simply list the CSS modules in the query string by name rather
+    --     than file path, for example
+    --     @\"\/YUI_VERSION\/css?reset&base&fonts&grids\"@.  Order matters;
+    --     you'll usually want reset first if you use it.
 
-derivePrinterParsers ''Sitemap
+derivePathInfo ''YUISitemap
 
-sitemap :: Router Sitemap
+derivePrinterParsers ''YUISitemap
+
+-- | A @boomerang@ 'Router' for 'YUISitemap'.  If you embed the
+-- @YUISitemap@ in your own, you can also embed this router in your own:
+--
+-- >import qualified Happstack.Server.YUI as Y
+-- >sitemap = (rYUI . (lit "yui" </> Y.sitemap)) <> rHome
+sitemap :: Router YUISitemap
 sitemap =
     YUI_VERSION_STR </>
-       ( rComboHandlerURL . "combo"
+       ( rComboURL . "combo"
       <> rCSSComboURL . "css"
       <> rBundleURL . "bundle" </> rList (anyString . eos)
       <> rConfigURL . "config"
       <> rSeedURL
        )
 
-site :: Site Sitemap (ServerPartT IO Response)
+site :: Happstack m => Site YUISitemap (m Response)
 site = boomerangSiteRouteT route sitemap
 
--- | Mounts a handler for serving YUI.
---
--- The handler responds to these routes:
---
--- [@\/YUI_VERSION\/@]
---   The YUI seed file plus the configuration for using our own
---   combo loader.
---
--- [@\/YUI_VERSION\/combo@]
---   The combo loader.
---
--- [@\/YUI_VERSION\/css@]
---   A specialized combo loader for CSS modules, for use in @\<link\/\>@
---   tags.  Simply list the CSS modules in the query string by name rather
---   than file path, for example
---   @\"\/YUI_VERSION\/css?reset&base&fonts&grids\"@.  Order matters;
---   you'll usually want reset first if you use it.
---
--- [@\/YUI_VERSION\/bundle\/\<filename\>@]
---   Get an individual file without combo loading.
---
--- [@\/YUI_VERSION\/config@]
---   The code for configuring YUI to use our own combo loader.  Not needed
---   if you use the seed file mentioned above.
---
--- The version number of the bundled YUI release is included in the routes
--- for sake of cache-busting: the routes all respond with far-future
--- expiration dates.
-implYUISite :: T.Text  -- ^ The URL of your application, e.g. @\"http:\/\/localhost:8000\"@.
+-- | Mounts a handler for serving YUI.  You can use this if you're not
+-- using @web-routes@ in your own application.  See 'YUISitemap' for the
+-- routes the mounted handler responds to.
+implYUISite :: Happstack m
+            => T.Text  -- ^ The URL of your application, e.g. @\"http:\/\/localhost:8000\"@.
             -> T.Text  -- ^ The path under which to mount the YUI handler, e.g. @\"/yui\"@.
-            -> ServerPartT IO Response
+            -> m Response
 implYUISite domain approot = implSite domain approot site
 
-mkConfig :: RouteT Sitemap (ServerPartT IO) JStat
+mkConfig :: Happstack m => RouteT YUISitemap m JStat
 mkConfig = do
-    comboURL <- showURL ComboHandlerURL
+    comboURL <- WR.showURL ComboURL
     return [jmacro|
        YUI.applyConfig { comboBase: `((T.unpack comboURL) ++ "?")`, root: "" }
     |]
 
-route :: Sitemap -> RouteT Sitemap (ServerPartT IO) Response
+-- | Routes a 'YUISitemap' to its handler.  If you embed @YUISitemap@ in
+-- your own sitemap, you can use 'WR.nestURL' in your own routing function
+-- to dispatch to this one:
+--
+-- >import qualified Happstack.Server.YUI as Y
+-- >route (YUI url) = nestURL YUI (Y.route url)
+route :: Happstack m => YUISitemap -> RouteT YUISitemap m Response
 route url = do
     neverExpires
     void compressedResponseFilter
@@ -113,10 +140,10 @@
            setHeaderM "Content-Type" mime
            bytes <- liftIO $ readYUIFile name
            ok . toResponse $ bytes
-      ComboHandlerURL ->
+      ComboURL ->
         do qs <- liftM (map fst) lookPairs
            exists <- liftIO $ mapM isYUIFile qs
-           if null qs || any (== False) exists
+           if null qs || any not exists
              then badRequest $ toResponse ()
              else do mime <- guessContentTypeM mimeTypes $ head qs
                      setHeaderM "Content-Type" mime
@@ -125,7 +152,7 @@
       CSSComboURL ->
         do qs <- liftM (map (css . fst)) lookPairs
            exists <- liftIO $ mapM isYUIFile qs
-           if null qs || any (== False) exists
+           if null qs || any not exists
              then badRequest $ toResponse ()
              else do setHeaderM "Content-Type" "text/css"
                      bytes <- liftIO $ mapM readYUIFile qs
@@ -143,6 +170,22 @@
     encode = encodeUtf8 . T.pack
     css fn = "css" ++ fn ++ "/css" ++ fn ++ "-min.css"
 
+-- | Helper for building a URL to 'CSSComboURL'.
+--
+-- >do cssURL <- showCSSComboURL YUI ["reset", "base", "fonts", "grids"]
+-- >   unXMLGenT
+-- >     <html>
+-- >       <head>
+-- >         <link href=cssURL rel="stylesheet"/>
+-- >       </head>
+-- >     </html>
+showCSSComboURL :: WR.MonadRoute m
+                => (YUISitemap -> WR.URL m)  -- ^ Constructor for 'YUISitemap' inside your own sitemap.
+                -> [T.Text]                  -- ^ Names of CSS modules to include, in order.
+                -> m T.Text
+showCSSComboURL yui ms =
+    WR.showURLParams (yui CSSComboURL) [(m,Nothing) | m <- ms]
+
 -- | Gets the class name for the grid unit of the ratio of the two argument
 -- integers.  YUI doesn't define redundant classes like \"6\/24\" because
 -- that is the same as 1\/4 and presumably for sake of a smaller CSS file.
@@ -150,6 +193,8 @@
 --
 -- >>> gridUnit 6 24
 -- "yui3-u-1-4"
+-- >>> gridUnit 24 24
+-- "yui3-u-1"
 --
 -- The intention is for this function to be used in templates to create
 -- values for class attributes, for example with HSP:
@@ -158,12 +203,13 @@
 -- >  <% someContent %>
 -- ></div>
 gridUnit :: Integer -> Integer -> T.Text
-gridUnit n d =
-    T.concat [ "yui3-u-"
-             , T.pack . show . numerator $ n % d
-             , "-"
-             , T.pack . show . denominator $ n % d
-             ]
+gridUnit n d
+  | num == 0           = "yui3-u"
+  | (num,den) == (1,1) = "yui3-u-1"
+  | otherwise          = [qq|yui3-u-$num-$den|]
+  where
+    num = numerator $ n % d
+    den = denominator $ n % d
 
 -- | Converts a pixel size to a percentage suitable for use
 -- with the CSS fonts module:
@@ -199,3 +245,18 @@
   where
     percentage :: Double
     percentage = fromIntegral px * (100 / 13)
+
+-- | Creates a YUI Node object from XML created using HSP, for use with
+-- JMacro.  This generates less code than using the @hsx-jmacro@ package to
+-- achieve the same effect, since it goes straight to YUI without directly
+-- using the DOM itself.  The first argument is the YUI object that gets
+-- passed to the function you give to @YUI().use()@.  Such variables are
+-- available in antiquotation splices with JMacro:
+--
+-- @
+--do html \<- unXMLGenT \<p>Hello, World!\</p>
+--   ok [jmacro| YUI().use \"node\" \\y ->
+--                 y.one(\"body\").append(`(y ``createNode`` html`)) |]
+-- @
+createNode :: JExpr -> XML -> JExpr
+createNode y xml = [jmacroE| `(y)`.Node.create(`(renderAsHTML xml)`) |]
