diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,47 @@
+## 0.3.21
+
+### New features
+
+-   Switch to using `subcaption` for LaTeX subfigures
+
+    `subfig` is reportedly deprecated and has some issues (e.g.
+    [#182](https://github.com/lierdakil/pandoc-crossref/issues/182))
+
+    This isn't as straightforward as one would like, there may be side effects.
+    Please report any issues you encounter.
+
+-   Add subfigColumns option
+
+    See <http://lierdakil.github.io/pandoc-crossref/#subfigure-columns> for more
+    info
+
+### Fixes
+
+-   Ensure automatic reference keys are unique
+
+    Fixes edge case issues with list-of-stuff
+
+-   Properly render citations in list-of-stuff
+
+### Maintenance
+
+-   Support Pandoc 3.8
+
+-   Minimal supported Pandoc version is 3.8
+
+-   Relax upper bounds on some dependencies:
+
+    - containers
+    - data-default
+    - open-browser
+    - optparse-applicative
+
+-   Large internal refactor to avoid explicit two-pass parse
+
+    This isn't tested exhaustively, some edge case issues may or may not be
+    present. As always, please report any issues you encounter, including
+    non-termination and/or abnormal RAM consumption.
+
 ## 0.3.20
 
 -   Support Pandoc 3.7
diff --git a/docs/demo/demo.md b/docs/demo/demo.md
--- a/docs/demo/demo.md
+++ b/docs/demo/demo.md
@@ -12,6 +12,7 @@
   *$$tableTitle$$ $$i$$*$$titleDelim$$ $$t$$
 autoSectionLabels: True
 title: pandoc-crossref demo document
+subfigColumns: True
 ---
 
 This is a demo file for pandoc-crossref. With this filter, you can cross-reference figures (see [@fig:figure1;@fig:figure2;@fig:figure3]), display equations (see @eq:eqn1), tables (see [@tbl:table1]) and sections ([@sec:sec1; @sec:sec2; @sec:caption-attr; @sec:table-capts; @sec:wrapping-div])
@@ -48,6 +49,14 @@
 ![Subfigure b](img1.jpg){#fig:subfigureB}
 
 Subfigures caption
+</div>
+
+<div id="fig:subfigures-side-by-side">
+![Subfigure a](img1.jpg)![Subfigure b](img2.jpg)
+
+![Subfigure c](img3.jpg)
+
+Subfigures side by side
 </div>
 
 # Chapter 2. Equations {#sec:sec2}
diff --git a/docs/index.md b/docs/index.md
--- a/docs/index.md
+++ b/docs/index.md
@@ -183,7 +183,7 @@
 If you put more than one figure in the paragraph, those will still be
 rendered, but Pandoc will omit subfigure caption in most outputs (but it
 will work as expected with LaTeX). You can use output-specific hacks to
-work around that, or use `subfigGrid` (see below).
+work around that, or use `subfigGrid` or `subfigColumn` (see below).
 
 Output is customizable, with metadata fields. See
 [Customization](#customization) for more information.
@@ -263,6 +263,17 @@
 don't need `subfigGrid` enabled for it to work with LaTeX, but you can
 still enable it.
 
+#### Subfigure columns
+
+Similar to `subfigGrid`, `subfigColumns` option will align each subfigure row as
+Pandoc's columns environment. This primarily works well with HTML output.
+
+All the caveats applicable to `subfigGrid` are equally applicable to
+`subfigColumns` as well.
+
+`subfigGrid` and `subfigColumns` are mutually exclusive. If both are specified,
+the former takes precedence.
+
 ## Equation labels
 
 ``` markdown
@@ -677,7 +688,7 @@
 -   `listings`: if True, generate code blocks for `listings` package.
     Only relevant for LaTeX output. `\usepackage{listings}` will be
     automatically added to `header-includes`. You need to specify
-    `--listings` option as well.
+    `--syntax-highlighting=idiomatic` option as well.
 -   `codeBlockCaptions`: if True, parse table-style code block captions.
 -   `autoSectionLabels`, default `false`: Automatically prefix all
     section labels with `sec:`. Note that this messes with pandoc's
@@ -722,6 +733,8 @@
     [Subfigures](#subfigures) and [Templates](#templates)
 -   `subfigGrid`, default `false`. If true, typeset subfigures inside a
     table. Ignored with LaTeX output. See [Subfigures](#subfigures)
+-   `subfigColumns`, default `false`. If true, typeset subfigure rows as column
+    environment. Ignored with LaTeX output. See [Subfigures](#subfigures)
 
 ### List titles
 
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Blocks.hs b/lib-internal/Text/Pandoc/CrossRef/References/Blocks.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Blocks.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Blocks.hs
@@ -22,7 +22,7 @@
   ( replaceAll
   ) where
 
-import Control.Monad.Reader
+import Lens.Micro.Mtl
 import Data.List
 import qualified Data.Text as T
 import Lens.Micro
@@ -30,6 +30,7 @@
 import Text.Pandoc.Shared (blocksToInlines)
 
 import Text.Pandoc.CrossRef.References.Types
+import Text.Pandoc.CrossRef.References.List
 import Text.Pandoc.CrossRef.References.Blocks.CodeBlock
 import Text.Pandoc.CrossRef.References.Blocks.Header
 import Text.Pandoc.CrossRef.References.Blocks.Math
@@ -37,22 +38,24 @@
 import Text.Pandoc.CrossRef.References.Blocks.Table
 import Text.Pandoc.CrossRef.References.Blocks.Util
 import Text.Pandoc.CrossRef.References.Monad
+import Text.Pandoc.CrossRef.Util.CodeBlockCaptions
 import Text.Pandoc.CrossRef.Util.Options
+import Text.Pandoc.CrossRef.Util.Generic
 import Text.Pandoc.CrossRef.Util.Util
 
 replaceAll :: (Data a) => a -> WS a
 replaceAll x = do
-  opts <- ask
-  x & runReplace (mkRR replaceBlock
-    `extRR` replaceInlineMany
-    )
-    . runSplitMath opts
-    . everywhere (mkT divBlocks `extT` spanInlines opts)
+  opts <- use wsOptions
+  x & everywhere (mkT (spanInlines opts) `extT` doSplitMath opts) -- bottom-up pass
+    & runReplace (mkRR replaceBlock -- top-down pass
+      `extRR` replaceInlineMany
+      `extRR` replaceBlockMany
+      )
   where
-    runSplitMath opts
+    doSplitMath opts
       | tableEqns opts
       , not $ isLatexFormat opts
-      = everywhere (mkT splitMath)
+      = splitMath
       | otherwise = id
 
 extractCaption :: Block -> Maybe [Inline]
@@ -94,14 +97,14 @@
   = runCodeBlock (label, nub $ divClasses <> cbClasses, divAttrs <> cbAttrs) code $ Right caption
 replaceBlock (Para [Span attr [Math DisplayMath eq]])
   = runBlockMath attr eq
-replaceBlock _ = noReplaceRecurse
+replaceBlock x = maybe noReplaceRecurse replaceBlock $ divBlocks x
 
 replaceInlineMany :: [Inline] -> WS (ReplacedResult [Inline])
 replaceInlineMany (Span spanAttr@(label,clss,attrs) [Math DisplayMath eq]:xs) = do
-  opts <- ask
+  opts <- use wsOptions
   if label `hasPfx` PfxEqn || T.null label && autoEqnLabels opts
   then do
-    replaceRecurse . (<> xs) =<< if isLatexFormat opts
+    res <- if isLatexFormat opts
       then
         pure [RawInline (Format "latex") "\\begin{equation}"
         , Span spanAttr [RawInline (Format "latex") eq]
@@ -109,18 +112,28 @@
       else do
         ReplaceEqn{replaceEqnEq, replaceEqnIdx} <- replaceEqn eqnDisplayTemplate spanAttr eq
         pure [Span (label,clss,setLabel opts replaceEqnIdx attrs) replaceEqnEq]
+    replaceList res xs
   else noReplaceRecurse
-replaceInlineMany _ = noReplaceRecurse
+replaceInlineMany (x:xs) = fixRefs' x xs
+replaceInlineMany [] = noReplaceRecurse
 
-divBlocks :: Block -> Block
+replaceBlockMany :: [Block] -> WS (ReplacedResult [Block])
+replaceBlockMany bs@(x:xs) = do
+  opts <- use wsOptions
+  case mkCodeBlockCaptions opts bs of
+    Just res' -> replaceRecurse res'
+    Nothing -> liftF (listOf x opts) `fixRefs` xs
+replaceBlockMany [] = noReplaceRecurse
+
+divBlocks :: Block -> Maybe Block
 divBlocks (Table tattr (Caption short (btitle:rest)) colspec header cells foot)
   | not $ null title
   , Just label <- getRefLabel PfxTbl [last title]
-  = Div (label,[],[]) [
+  = Just $ Div (label,[],[]) [
     Table tattr (Caption short $ walkReplaceInlines (dropWhileEnd isSpace (init title)) title btitle:rest) colspec header cells foot]
   where
     title = blocksToInlines [btitle]
-divBlocks x = x
+divBlocks _ = Nothing
 
 spanInlines :: Options -> [Inline] -> [Inline]
 spanInlines opts (math@(Math DisplayMath _eq):ils)
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/CodeBlock.hs b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/CodeBlock.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/CodeBlock.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/CodeBlock.hs
@@ -20,7 +20,7 @@
 
 module Text.Pandoc.CrossRef.References.Blocks.CodeBlock where
 
-import Control.Monad.Reader.Class
+import Lens.Micro.Mtl
 import qualified Data.Text as T
 import Text.Pandoc.Definition
 import qualified Text.Pandoc.Builder as B
@@ -30,11 +30,11 @@
 import Text.Pandoc.CrossRef.References.Blocks.Util
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.Util.Template
-import Text.Pandoc.CrossRef.Util.Util
+import Text.Pandoc.CrossRef.Util.Generic
 
 runCodeBlock :: Attr -> T.Text -> Either T.Text [Inline] -> WS (ReplacedResult Block)
 runCodeBlock (label, classes, attrs) code eCaption = do
-  opts <- ask
+  opts <- use wsOptions
       --if used with listings package,nothing should be done
   if  | isLatexFormat opts, listings opts -> do
           let cap = either (B.toList . B.text) id eCaption
@@ -47,8 +47,8 @@
       | isLatexFormat opts -> do
           let cap = either (B.toList . B.text) id eCaption
           ref <- replaceAttr (Just label) attrs cap SPfxLst
-          replaceNoRecurse $ Div nullAttr [
-              RawBlock (Format "latex") $ "\\begin{codelisting}"
+          replaceRecurse $ Div nullAttr [
+              RawBlock (Format "latex") "\\begin{codelisting}"
             , Plain $ latexCaption ref
             , CodeBlock ("", classes, attrs) code
             , RawBlock (Format "latex") "\\end{codelisting}"
@@ -58,7 +58,7 @@
           ref <- replaceAttr (Just label) attrs cap SPfxLst
           idxStr <- chapIndex ref
           let caption' = applyTemplate idxStr cap $ listingTemplate opts
-          replaceNoRecurse $ Div (label, "listing":classes, []) [
+          replaceRecurse $ Div (label, "listing":classes, []) [
               mkCaption opts "Caption" caption'
             , CodeBlock ("", classes, filter ((/="caption") . fst) $ setLabel opts idxStr attrs) code
             ]
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Header.hs b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Header.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Header.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Header.hs
@@ -20,7 +20,8 @@
 
 module Text.Pandoc.CrossRef.References.Blocks.Header where
 
-import Control.Monad.Reader.Class
+import Control.Applicative
+import Lens.Micro.Mtl
 import Control.Monad (when)
 import qualified Data.Map as M
 import qualified Data.Sequence as S
@@ -29,35 +30,34 @@
 import Text.Pandoc.Definition
 import Text.Read (readMaybe)
 
-import Control.Applicative
-import Lens.Micro.Mtl
 import Text.Pandoc.CrossRef.References.Types
 import Text.Pandoc.CrossRef.References.Monad
 import Text.Pandoc.CrossRef.References.Blocks.Util (setLabel, checkHidden)
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.Util.Template
 import Text.Pandoc.CrossRef.Util.Util
+import Text.Pandoc.CrossRef.Util.Generic
 
 runHeader :: Int -> Attr -> [Inline] -> WS (ReplacedResult Block)
 runHeader n (label, cls, attrs) text' = do
-  stHiddenHeaderLevel %= filter \HiddenHeader{hhLevel} -> hhLevel < n
+  wsReferences . stHiddenHeaderLevel %= filter \HiddenHeader{hhLevel} -> hhLevel < n
   hhHidden <- checkHidden attrs
-  stHiddenHeaderLevel %= (HiddenHeader { hhLevel = n, hhHidden }:)
+  wsReferences . stHiddenHeaderLevel %= (HiddenHeader { hhLevel = n, hhHidden }:)
 
   if "unnumbered" `elem` cls
     then do
       label' <- mangleLabel
-      replaceNoRecurse $ Header n (label', cls, attrs) text'
+      replaceRecurse $ Header n (label', cls, attrs) text'
     else do
-      opts@Options{..} <- ask
+      opts@Options{..} <- use wsOptions
       label' <- mangleLabel
-      ctrsAt PfxSec %= \cc ->
+      wsReferences . ctrsAt PfxSec %= \cc ->
         let ln = length cc
             cl i = lookup "label" attrs <|> customHeadingLabel n i <|> customLabel (Pfx PfxSec) i
             inc l = case S.viewr l of
               EmptyR -> error "impossible"
               init' :> last' ->
-                let i = succ $ fst $ last'
+                let i = succ $ fst last'
                 in init' S.|> (i, cl i)
             cc' | Just num <- readMaybe . T.unpack =<< lookup "number" attrs
                 = S.take (n - 1) cc S.|> (num, cl num)
@@ -68,10 +68,10 @@
               | numberSections = S.replicate (n-ln-1) (1, Nothing)
               | otherwise = S.replicate (n-ln-1) (0, Nothing)
         in cc'
-      cc <- use $ ctrsAt PfxSec
-      globCtr <- stGlob <<%= (+ 1)
+      cc <- use $ wsReferences . ctrsAt PfxSec
+      globCtr <- wsReferences . stGlob <<%= (+ 1)
       when (label' `hasPfx` PfxSec) $
-        refsAt PfxSec %= M.insert label' RefRec {
+        wsReferences . refsAt PfxSec %= M.insert (IdxRef PfxSec label') RefRec {
           refIndex = cc
         , refGlobal = globCtr
         , refTitle = text'
@@ -87,14 +87,14 @@
                 ("i", idxStr)
               , ("n", [Str $ T.pack $ show $ n - 1])
               , ("t", text')
-              ]) $ secHeaderTemplate
+              ]) secHeaderTemplate
             | otherwise = text'
           idxStr = chapPrefix chapDelim cc
           attrs' = setLabel opts idxStr attrs
-      replaceNoRecurse $ Header n (label', cls, attrs') textCC
+      replaceRecurse $ Header n (label', cls, attrs') textCC
   where
     mangleLabel = do
-      Options{autoSectionLabels} <- ask
+      Options{autoSectionLabels} <- use wsOptions
       pure $
         if autoSectionLabels && not (label `hasPfx` PfxSec)
         then pfxTextCol PfxSec <> label
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Math.hs b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Math.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Math.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Math.hs
@@ -20,7 +20,7 @@
 
 module Text.Pandoc.CrossRef.References.Blocks.Math where
 
-import Control.Monad.Reader.Class
+import Lens.Micro.Mtl
 import qualified Data.Map as M
 import qualified Data.Text as T
 import Text.Pandoc.Definition
@@ -31,14 +31,15 @@
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.Util.Template
 import Text.Pandoc.CrossRef.Util.Util
+import Text.Pandoc.CrossRef.Util.Generic
 
 runBlockMath :: Attr -> T.Text -> WS (ReplacedResult Block)
 runBlockMath (label, cls, attrs) eq = do
-  opts <- ask
+  opts <- use wsOptions
   if tableEqns opts && not (isLatexFormat opts)
   then do
     ReplaceEqn{..} <- replaceEqn eqnBlockTemplate (label, cls, attrs) eq
-    replaceNoRecurse $ Div (label,cls,setLabel opts replaceEqnIdx attrs) replaceEqnEq
+    replaceRecurse $ Div (label,cls,setLabel opts replaceEqnIdx attrs) replaceEqnEq
   else noReplaceRecurse
 
 data ReplaceEqn a = ReplaceEqn
@@ -48,7 +49,7 @@
 
 replaceEqn :: MkTemplate a t => (Options -> t) -> Attr -> T.Text -> WS (ReplaceEqn a)
 replaceEqn eqTemplate (label, _, attrs) eq = do
-  opts <- ask
+  opts <- use wsOptions
   let label' | T.null label = Nothing
              | otherwise = Just label
   ref <- replaceAttr label' attrs [] SPfxEqn
@@ -60,7 +61,7 @@
         then eqnInlineTableTemplate opts
         else eqnInlineTemplate opts
       wrapMath x = [Math mathfmt $ stringify x]
-      commonVars eqn = M.fromList $
+      commonVars eqn = M.fromList
         [ ("e", eqn)
         , ("t", eqn) -- backwards compatibility for eqnBlockTemplate
         , ("i", wrapMath idxStr)
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Subfigures.hs b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Subfigures.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Subfigures.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Subfigures.hs
@@ -20,8 +20,6 @@
 
 module Text.Pandoc.CrossRef.References.Blocks.Subfigures where
 
-import Control.Monad.Reader
-import Control.Monad.State hiding (get, modify)
 import qualified Data.Map as M
 import qualified Data.Text as T
 import qualified Data.Text.Read as T
@@ -33,7 +31,6 @@
 import Lens.Micro
 import Lens.Micro.Mtl
 import Text.Pandoc.Shared (blocksToInlines)
-import Control.Monad ((<=<))
 
 import Text.Pandoc.CrossRef.References.Types
 import Text.Pandoc.CrossRef.References.Monad
@@ -41,32 +38,28 @@
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.Util.Template
 import Text.Pandoc.CrossRef.Util.Util
+import Text.Pandoc.CrossRef.Util.Generic
 
 runSubfigures :: Attr -> [Block] -> [Inline] -> WS (ReplacedResult Block)
 runSubfigures (label, cls, attrs) images caption = do
-  opts <- ask
+  opts <- use wsOptions
   ref <- replaceAttr (Just label) attrs caption SPfxImg
   idxStr <- chapIndex ref
-  glob <- use stGlob
-  hiddenHdr <- use stHiddenHeaderLevel
-  let (cont, st) = flip runState (References def def glob hiddenHdr)
-        $ flip runReaderT opts'
-        $ runWS
-        $ runReplace (mkRR replaceSubfigs `extRR` doFigure) images
-      doFigure :: Block -> WS (ReplacedResult Block)
-      doFigure (Figure attr caption' content) = runFigure True attr caption' content
-      doFigure _ = noReplaceRecurse
-      opts' = opts
+  glob <- use $ wsReferences . stGlob
+  hiddenHdr <- use $ wsReferences . stHiddenHeaderLevel
+  let opts' = opts
           { figureTemplate = subfigureChildTemplate opts
-          , customLabel = \r i -> customLabel opts (Sub r) i
+          , customLabel = customLabel opts . Sub
           }
-      collectedCaptions = B.toList $
+  (cont, st) <- embedWS (WState opts' (References def def glob hiddenHdr))
+    $ runReplace (mkRR replaceSubfigs) images
+  let collectedCaptions = B.toList $
           intercalate' (B.fromList $ ccsDelim opts)
         $ map (B.fromList . collectCaps . snd)
         $ sortOn (refIndex . snd)
         $ filter (not . null . refTitle . snd)
         $ M.toList
-        $ st ^. refsAt PfxImg
+        $ st ^. wsReferences . refsAt PfxImg
       collectCaps v =
             applyTemplate
               (chapPrefix (chapDelim opts) (refIndex v))
@@ -78,56 +71,48 @@
                 , ("t", caption)
                 ]
       capt = applyTemplate' vars $ subfigureTemplate opts
-  lastRef <- fromJust . M.lookup label <$> use (refsAt PfxImg)
-  let mangledSubfigures = mangleSubfigure <$> st ^. refsAt PfxImg
-      mangleSubfigure v = v{refIndex = refIndex lastRef, refSubfigure = Just $ refIndex v}
-  refsAt PfxImg %= (<> mangledSubfigures)
-  stGlob .= st ^. stGlob
+  let mangledSubfigures = mangleSubfigure <$> st ^. wsReferences . refsAt PfxImg
+      mangleSubfigure v = v{refIndex = refIndex ref, refSubfigure = Just $ refIndex v}
+  wsReferences . refsAt PfxImg %= (<> mangledSubfigures)
+  wsReferences . stGlob .= st ^. wsReferences . stGlob
   -- stHiddenHeaderLevel shouldn't have changed, but for future-proofing...
-  stHiddenHeaderLevel .= st ^. stHiddenHeaderLevel
-  if  | isLatexFormat opts -> do
-          replaceNoRecurse $ Div nullAttr $
-            [ RawBlock (Format "latex") "\\begin{pandoccrossrefsubfigures}" ]
-            <> cont <>
-            [ Para $ latexCaption ref
-            , RawBlock (Format "latex") "\\end{pandoccrossrefsubfigures}"]
-      | otherwise ->
-          replaceNoRecurse
-            $ Figure (label, "subfigures":cls, setLabel opts idxStr attrs)
-                     (Caption Nothing [Para capt])
-            $ toTable opts cont
+  wsReferences . stHiddenHeaderLevel .= st ^. wsReferences . stHiddenHeaderLevel
+  replaceNoRecurse $ if isLatexFormat opts
+    then Div nullAttr $
+      [ RawBlock (Format "latex") "\\begin{pandoccrossrefsubfigures}" ]
+      <> cont <>
+      [ Para $ latexCaption ref
+      , RawBlock (Format "latex") "\\end{pandoccrossrefsubfigures}"]
+    else Figure (label, "subfigures":cls, setLabel opts idxStr attrs)
+                (Caption Nothing [Para capt])
+      $ toTable opts cont
   where
     toTable :: Options -> [Block] -> [Block]
     toTable opts blks
-      | subfigGrid opts = [simpleTable align (map ColWidth widths) (map (fmap pure . blkToRow) blks)]
+      | subfigGrid opts = [simpleTable align (map ColWidth widths') (map (fmap pure . blkToRow) blks)]
+      | subfigColumns opts = mapMaybe figurize blks
       | otherwise = blks
       where
+        figurize x
+          | Para is <- x = case (mapMaybe getImg is) `zip` widths' of
+              [] -> Nothing -- skip empty Paras
+              [_] -> error "The impossible happened: single-element Para in \
+                \subfigure which is not a figure"
+              xs -> Just $ Div ("", ["columns"], []) $ map columnize xs
+          | otherwise = Just x
+        columnize ((id', cs, as, alt, tgt), width) =
+          Div ("", ["column"], [("width", T.pack (show (width * 100.0)) <> "%")]) [
+            implicitFigure (id', cs, filter ((/="width") . fst) as) alt tgt Figure
+          ]
+        widths' = widths blks
         align | b:_ <- blks = let ils = blocksToInlines [b] in replicate (length $ mapMaybe getWidth ils) AlignCenter
               | otherwise = error "Misformatted subfigures block"
-        widths | b:_ <- blks = let ils = blocksToInlines [b] in fixZeros $ mapMaybe getWidth ils
-               | otherwise = error "Misformatted subfigures block"
-        getWidth (Image (_id, _class, as) _ _)
-          = Just $ maybe 0 percToDouble $ lookup "width" as
-        getWidth _ = Nothing
-        fixZeros :: [Double] -> [Double]
-        fixZeros ws
-          = let nz = length $ filter (== 0) ws
-                rzw = (0.99 - sum ws) / fromIntegral nz
-            in if nz>0
-                then map (\x -> if x == 0 then rzw else x) ws
-                else ws
-        percToDouble :: T.Text -> Double
-        percToDouble percs
-          | Right (perc, "%") <- T.double percs
-          = perc/100.0
-          | otherwise = error "Only percent allowed in subfigure width!"
         blkToRow :: Block -> [Block]
-        blkToRow (Para inls) = zipWith inlToCell widths $ mapMaybe getImg inls
+        blkToRow (Para inls) = zipWith inlToCell widths' $ mapMaybe getImg inls
         blkToRow x = [x]
         getImg (Image (id', cs, as) txt tgt) = Just (id', cs, as, txt, tgt)
         getImg _ = Nothing
-        inlToCell w (id', cs, as, txt, tgt) =
-          Figure (id', cs, []) (Caption Nothing [Para txt]) [Plain [Image ("", cs, setW w as) txt tgt]]
+        inlToCell w (id', cs, as, txt, tgt) = implicitFigure (id', cs, setW w as) txt tgt Figure
         setW w as = ("width", width):filter ((/="width") . fst) as
           where
             -- With docx, since pandoc 3.0, 100% is interpreted as "page width",
@@ -136,37 +121,87 @@
               | isDocxFormat opts = T.pack (show $ w * 100) <> "%"
               | otherwise = "100%"
 
-replaceSubfigs :: [Inline] -> WS (ReplacedResult [Inline])
-replaceSubfigs = (replaceNoRecurse . concat) <=< mapM replaceSubfig
+getWidth :: Inline -> Maybe Double
+getWidth (Image (_id, _class, as) _ _)
+  = Just $ maybe 0 percToDouble $ lookup "width" as
+  where
+    percToDouble :: T.Text -> Double
+    percToDouble percs
+      | Right (perc, "%") <- T.double percs
+      = perc/100.0
+      | otherwise = error "Only percent allowed in subfigure width!"
+getWidth _ = Nothing
 
-replaceSubfig :: Inline -> WS [Inline]
-replaceSubfig x@(Image (label,cls,attrs) alt tgt) = do
-  opts <- ask
+widths :: [Block] -> [Double]
+widths (b:_) = let ils = blocksToInlines [b] in fixZeros $ mapMaybe getWidth ils
+  where
+    fixZeros :: [Double] -> [Double]
+    fixZeros ws
+      = let nz = length $ filter (== 0) ws
+            rzw = (0.99 - sum ws) / fromIntegral nz
+        in if nz>0
+            then map (\x -> if x == 0 then rzw else x) ws
+            else ws
+widths _ = error "Misformatted subfigures block"
+
+replaceSubfigs :: Block -> WS (ReplacedResult Block)
+replaceSubfigs (Figure attr caption' content) = runFigure True attr caption' content
+-- this triggers when implicit_figures is disabled
+replaceSubfigs (Para [Image attr caption' tgt]) =
+  implicitFigure attr caption' tgt $ runFigure True
+replaceSubfigs blk@(Para images) =
+  let go _ [] = pure []
+      go (w:ws) (x@Image{}:xs) = liftA2 (:) (replaceSubfig w x) (go ws xs)
+      go ws (x:xs) = ([x] :) <$> go ws xs
+  in replaceNoRecurse . Para . concat =<< go (widths [blk]) images
+replaceSubfigs _ = noReplaceRecurse
+
+-- this is mostly copy-paste from pandoc markdown reader
+implicitFigure :: Attr -> [Inline] -> (T.Text, T.Text) -> (Attr -> Caption -> [Block] -> a) -> a
+implicitFigure (ident, classes, attribs) capt' (url, title) f =
+  let alt = maybe capt B.text $ lookup "alt" attribs
+      capt = B.fromList capt'
+      attribs' = filter (liftA2 (&&) (/= "latex-placement") (/= "alt") . fst) attribs
+      figattribs = filter ((=="latex-placement") . fst) attribs
+      figattr = (ident, mempty, figattribs)
+      caption = B.simpleCaption $ B.plain capt
+      figbody = B.plain $ B.imageWith ("", classes, attribs') url title alt
+  in f figattr caption (B.toList figbody)
+
+replaceSubfigs' :: [(Double, Inline)] -> WS [Inline]
+replaceSubfigs' = fmap concat . mapM (uncurry replaceSubfig)
+
+replaceSubfig :: Double -> Inline -> WS [Inline]
+replaceSubfig width x@(Image (label,cls,attrs) alt tgt) = do
+  opts <- use wsOptions
   ref <- replaceAttr (normalizeLabel label) attrs alt SPfxImg
   idxStr <- chapIndex ref
   let alt' = applyTemplate idxStr alt $ figureTemplate opts
   pure $ if isLatexFormat opts
-    then latexSubFigure x ref
+    then latexSubFigure width x ref
     else [Image (fromMaybe "" (refLabel ref), cls, setLabel opts idxStr attrs) alt' tgt]
-replaceSubfig x = pure [x]
+replaceSubfig _ _ = pure []
 
-latexSubFigure :: Inline -> RefRec -> [Inline]
-latexSubFigure (Image (_, cls, attrs) alt (src, title)) ref =
+latexSubFigure :: Double -> Inline -> RefRec -> [Inline]
+latexSubFigure width (Image (_, cls, attrs) alt (src, title)) ref =
   let
     title' = fromMaybe title $ T.stripPrefix "fig:" title
     texalt | "nocaption" `elem` cls  = []
            | otherwise = concat
-              [ [ RawInline (Format "latex") "["]
+              [ [ RawInline (Format "latex") "\\caption{"]
               , alt
-              , [ RawInline (Format "latex") "]"]
+              , latexLabel ref
+              , [ RawInline (Format "latex") "}"]
               ]
-    img = Image (fromMaybe "" (refLabel ref), cls, attrs) alt (src, title')
+    filterWidth = filter $ (/= "width") . fst
+    img = Image (fromMaybe "" (refLabel ref), cls, filterWidth attrs) alt (src, title')
   in concat [
-      [ RawInline (Format "latex") "\\subfloat" ]
+      [ RawInline (Format "latex") ("\\begin{subfigure}{" <> T.pack (show width) <> "\\linewidth}") ]
+      , [ img ]
       , texalt
-      , [Span nullAttr $ img : latexLabel ref]
+      , [RawInline (Format "latex") "\\end{subfigure}%\n"]
       ]
-latexSubFigure x _ = [x]
+latexSubFigure _ x _ = [x]
 
 normalizeLabel :: T.Text -> Maybe T.Text
 normalizeLabel label
@@ -179,14 +214,14 @@
   where
   mkBody xs = TableBody nullAttr (RowHeadColumns 0) [] (map mkRow xs)
   mkRow xs = Row nullAttr (map mkCell xs)
-  mkCell xs = Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) xs
+  mkCell = Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1)
   noCaption = Caption Nothing mempty
   noTableHead = TableHead nullAttr []
   noTableFoot = TableFoot nullAttr []
 
 runFigure :: Bool -> Attr -> Caption -> [Block] -> WS (ReplacedResult Block)
 runFigure subFigure (label, cls, fattrs) (Caption short (btitle : rest)) content = do
-  opts <- ask
+  opts <- use wsOptions
   let label' = normalizeLabel label
   let title = blocksToInlines [btitle]
       (attrs, content') = case blocksToInlines content of
@@ -207,7 +242,7 @@
   replaceNoRecurse $
     if subFigure && isLatexFormat opts
     then Plain $ case blocksToInlines content of
-      ctHead:_ -> latexSubFigure ctHead ref
+      ctHead:_ -> latexSubFigure 1.0 ctHead ref
       _ -> error "The impossible happened: empty content in subfigures"
     else Figure (label,cls,setLabel opts idxStr fattrs) caption' (content' title')
-runFigure _ _ _ _ = noReplaceNoRecurse
+runFigure _ _ _ _ = noReplaceRecurse
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Table.hs b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Table.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Table.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Table.hs
@@ -20,7 +20,7 @@
 
 module Text.Pandoc.CrossRef.References.Blocks.Table where
 
-import Control.Monad.Reader.Class
+import Lens.Micro.Mtl
 import Text.Pandoc.Definition
 import Text.Pandoc.Shared (blocksToInlines)
 import Data.Function ((&))
@@ -30,11 +30,11 @@
 import Text.Pandoc.CrossRef.References.Types
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.Util.Template
-import Text.Pandoc.CrossRef.Util.Util
+import Text.Pandoc.CrossRef.Util.Generic
 
 runTable :: Attr -> Maybe Attr -> Maybe ShortCaption -> Block -> [Block] -> [ColSpec] -> TableHead -> [TableBody] -> TableFoot -> WS (ReplacedResult Block)
 runTable (label, clss, attrs) mtattr short btitle rest colspec header cells foot = do
-  opts <- ask
+  opts <- use wsOptions
   ref <- replaceAttr (Just label) attrs title SPfxTbl
   idxStr <- chapIndex ref
   let short' | refHideFromList ref = Just mempty
@@ -45,7 +45,7 @@
       caption' = Caption short' (walkReplaceInlines title' title btitle:rest)
       label' | isLatexFormat opts = ""
              | otherwise = label
-  replaceNoRecurse $ (mtattr &
+  replaceRecurse $ (mtattr &
     maybe
       (Table (label, clss, setLabel opts idxStr attrs))
       (\tattr a b c d -> Div (label', clss, setLabel opts idxStr attrs) . pure . Table tattr a b c d)
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Util.hs b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Util.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Util.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Blocks/Util.hs
@@ -31,13 +31,12 @@
   , walkReplaceInlines
   ) where
 
-import Control.Monad.Reader.Class
 import qualified Data.Map as M
 import qualified Data.Set as Set
 import Data.Set (Set)
 import qualified Data.Text as T
 import Text.Pandoc.Definition
-import Text.Pandoc.Shared (stringify)
+import Text.Pandoc.Shared (stringify, deNote)
 import Text.Pandoc.Walk (walk)
 import Control.Monad (when)
 import Data.Maybe (fromMaybe)
@@ -46,7 +45,6 @@
 
 import Control.Applicative
 import Lens.Micro.Mtl
-import Text.Pandoc.Shared (deNote)
 import Text.Pandoc.CrossRef.References.Types
 import Text.Pandoc.CrossRef.References.Monad
 import Text.Pandoc.CrossRef.Util.Options
@@ -93,21 +91,22 @@
 replaceAttr label attrs title (toPrefix -> pfx) = do
   let refLabel = lookup "label" attrs
       number = readMaybe . T.unpack =<< lookup "number" attrs
-  Options{..} <- ask
-  chap  <- S.take chaptersDepth <$> use (ctrsAt PfxSec)
-  prop' <- use $ refsAt pfx
-  curIdx <- use $ ctrsAt pfx
+  Options{..} <- use wsOptions
+  chap  <- S.take chaptersDepth <$> use (wsReferences . ctrsAt PfxSec)
+  prop' <- use $ wsReferences . refsAt pfx
+  curIdx <- use $ wsReferences . ctrsAt pfx
+  globCtr <- wsReferences . stGlob <<%= (+ 1)
   let i | Just n <- number = n
         | chap' :> last' <- S.viewr curIdx
         , chap' == chap
         = succ . fst $ last'
         | otherwise = 1
       index = chap S.|> (i, refLabel <|> customLabel (Pfx pfx) i)
-      label' = fromMaybe (T.pack (show pfx <> (':' : show index))) label
+      synthLabel = IdxSynth globCtr
+      label' = fromMaybe synthLabel $ IdxRef pfx <$> label
   when (M.member label' prop') $
-    error . T.unpack $ "Duplicate label: " <> label'
-  globCtr <- stGlob <<%= (+ 1)
-  ctrsAt pfx .= index
+    error $ "Duplicate label: " <> displayIdx label'
+  wsReferences . ctrsAt pfx .= index
   refHideFromList <- checkHidden attrs
   let rec' = RefRec {
       refIndex= index
@@ -117,12 +116,12 @@
     , refHideFromList
     , refLabel = label
     }
-  refsAt pfx %= M.insert label' rec'
+  wsReferences . refsAt pfx %= M.insert label' rec'
   pure rec'
 
 chapIndex :: RefRec -> WS [Inline]
 chapIndex r = do
-  Options{chapDelim} <- ask
+  Options{chapDelim} <- use wsOptions
   pure $ chapPrefix chapDelim $ refIndex r
 
 mkCaption :: Options -> T.Text -> [Inline] -> Block
@@ -180,9 +179,9 @@
 
 checkHidden :: [(T.Text, T.Text)] -> WS Bool
 checkHidden attrs = do
-  hiddenHdr <- use stHiddenHeaderLevel
-  pure . fromMaybe (isHdrHidden hiddenHdr) $
-    not . isFalseValue <$> lookup "hidden" attrs
+  hiddenHdr <- use $ wsReferences . stHiddenHeaderLevel
+  pure $ maybe (isHdrHidden hiddenHdr) (not . isFalseValue)
+    $ lookup "hidden" attrs
 
 isFalseValue :: T.Text -> Bool
 isFalseValue = (`Set.member` falseValues) . T.strip . T.toLower
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/List.hs b/lib-internal/Text/Pandoc/CrossRef/References/List.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/List.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/List.hs
@@ -21,35 +21,29 @@
 module Text.Pandoc.CrossRef.References.List (listOf, listOfFigures, listOfTables, listOfListings) where
 
 import Data.List
-import Control.Monad.Reader
 import qualified Data.Map as M
 import Text.Pandoc.Definition
 import Text.Pandoc.Builder qualified as B
-import Data.Maybe
 
-import Lens.Micro.Mtl
+import Lens.Micro
 import Text.Pandoc.CrossRef.References.Types
-import Text.Pandoc.CrossRef.References.Monad
+import Text.Pandoc.CrossRef.Util.Generic
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.Util.Util
 import Text.Pandoc.CrossRef.Util.Template
 
-listOf :: [Block] -> WS [Block]
-listOf blocks = asks isLatexFormat >>= \case
-  True -> pure blocks
-  False -> case blocks of
-    (RawBlock fmt "\\listoffigures":xs)
-      | isLaTeXRawBlockFmt fmt
-      -> (<> xs) <$> listOfFigures
-    (RawBlock fmt "\\listoftables":xs)
-      | isLaTeXRawBlockFmt fmt
-      -> (<> xs) <$> listOfTables
-    (RawBlock fmt "\\listoflistings":xs)
-      | isLaTeXRawBlockFmt fmt
-      -> (<> xs) <$> listOfListings
-    _ -> pure blocks
+listOf :: Block -> Options -> References -> Maybe [Block]
+listOf _ opts | isLatexFormat opts = pure Nothing
+listOf (RawBlock fmt content) opts | isLaTeXRawBlockFmt fmt =
+  case content of
+    "\\listoffigures" -> Just <$> listOfFigures opts
+    "\\listoftables" -> Just <$> listOfTables opts
+    "\\listoflistings" -> Just <$> listOfListings opts
+    _ -> pure Nothing
+listOf _ _ = pure Nothing
 
-listOfFigures, listOfTables, listOfListings:: WS [Block]
+
+listOfFigures, listOfTables, listOfListings:: Options -> References -> [Block]
 listOfFigures = makeList PfxImg lofItemTemplate lofTitle
 listOfTables = makeList PfxTbl lotItemTemplate lotTitle
 listOfListings = makeList PfxLst lolItemTemplate lolTitle
@@ -58,12 +52,13 @@
   :: Prefix
   -> (Options -> BlockTemplate)
   -> (Options -> [Block])
-  -> WS [Block]
-makeList pfx tf titlef = do
-  o <- ask
-  refs <- use $ refsAt pfx
+  -> Options
+  -> References
+  -> [Block]
+makeList pfx tf titlef o refsMap = do
   let
-    items = fromMaybe items' $ pure <$> mergeList Nothing [] items'
+    refs = refsMap ^. refsAt pfx
+    items = maybe items' pure $ mergeList Nothing [] items'
     items' = concatMap (itemChap . snd) refsSorted
     mergeList Nothing acc (OrderedList style item : ys) =
       mergeList (Just $ OrderedList style) (item <> acc) ys
@@ -85,16 +80,17 @@
       = compare i j
     itemChap :: RefRec -> [Block]
     itemChap ref@RefRec{..} =
-      let vars = M.fromDistinctAscList $
+      let vars = M.fromDistinctAscList
             [ ("i", numWithChap vars ref)
-            , ("lt", linked refLabel refTitle)
+            , ("lt", linked refLabel title)
             , ("ri", chapPrefix chapDelim refIndex)
             , ("s", maybe mempty (chapPrefix chapDelim) refSubfigure)
             , ("suf", mempty)
-            , ("t", refTitle)
+            , ("t", title)
             ]
       in applyTemplate' vars $ tf o
       where
+        title = replaceRefsWalk o refsMap refTitle
         Options{chapDelim} = o
     linked (Just lab) = B.toList . B.link ("#" <> lab) "" . B.fromList
     linked Nothing = id
@@ -104,4 +100,4 @@
       where
         vars' = M.insert "i" (chapPrefix chapDelim refIndex) vars
         Options{chapDelim, refIndexTemplate, subfigureRefIndexTemplate} = o
-  pure $ titlef o <> [Div ("", ["list", "list-of-" <> pfxText pfx], []) items]
+  titlef o <> [Div ("", ["list", "list-of-" <> pfxText pfx], []) items]
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Monad.hs b/lib-internal/Text/Pandoc/CrossRef/References/Monad.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Monad.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Monad.hs
@@ -21,10 +21,31 @@
 module Text.Pandoc.CrossRef.References.Monad where
 
 import Control.Monad.State
-import Control.Monad.Reader
+import Data.Function
 import Text.Pandoc.CrossRef.References.Types
 import Text.Pandoc.CrossRef.Util.Options
+import Lens.Micro.TH
+import Lens.Micro.Extras
 
+data WState = WState
+  { _wsOptions :: Options
+  , _wsReferences :: References
+  }
+
+makeLenses ''WState
+
 --state monad
-newtype WS a = WS { runWS :: ReaderT Options (State References) a }
-  deriving (Functor, Applicative, Monad, MonadReader Options, MonadState References)
+newtype WS a = WS (StateT WState ((->) References) a)
+  deriving (Functor, Applicative, Monad, MonadState WState)
+
+unwrapWS :: WS a -> WState -> References -> (a, WState)
+unwrapWS (WS a) = runStateT a
+
+embedWS :: WState -> WS a -> WS (a, WState)
+embedWS wst a = WS $ StateT \oldst refs -> (unwrapWS a wst refs, oldst)
+
+runWS :: WState -> WS a -> (a, WState)
+runWS st a = fix $ unwrapWS a st . view wsReferences . snd
+
+liftF :: (References -> a) -> WS a
+liftF x = WS $ StateT \wst refs -> (x refs, wst)
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Refs.hs b/lib-internal/Text/Pandoc/CrossRef/References/Refs.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Refs.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Refs.hs
@@ -21,7 +21,6 @@
 module Text.Pandoc.CrossRef.References.Refs (replaceRefs) where
 
 import Control.Arrow as A
-import Control.Monad.Reader
 import Data.Function
 import Numeric.Natural
 import Data.List
@@ -37,23 +36,20 @@
 import Data.List.NonEmpty (NonEmpty)
 
 import Debug.Trace
-import Lens.Micro.Mtl
+import Lens.Micro
 import Text.Pandoc.CrossRef.References.Types
-import Text.Pandoc.CrossRef.References.Monad
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.Util.Template
 import Text.Pandoc.CrossRef.Util.Util
 
-replaceRefs :: [Inline] -> WS [Inline]
-replaceRefs (Cite cits _:xs) = do
-  opts <- ask :: WS Options
-  toList . (<> fromList xs) . intercalate' (text ", ") . map fromList <$> mapM (replaceRefs' opts) (NE.groupBy eqPrefix cits)
+replaceRefs :: [Citation] -> Options -> References -> Inlines
+replaceRefs cits opts = intercalate' (text ", ") . map fromList <$> mapM replaceRefs' (NE.groupBy eqPrefix cits)
   where
     eqPrefix = (==) `on` getLabelPrefix . citationId
-    replaceRefs' :: Options -> NonEmpty Citation -> WS [Inline]
-    replaceRefs' opts cits'
+    replaceRefs' :: NonEmpty Citation -> References -> [Inline]
+    replaceRefs' cits'
       | Just prefix <- getLabelPrefix . citationId $ NE.head cits'
-      = replaceRefs'' opts prefix cits'
+      = replaceRefs'' prefix cits'
       | otherwise = return [Cite (NE.toList cits') il']
         where
           il' = toList $
@@ -63,12 +59,11 @@
           citationToInlines c =
             fromList (citationPrefix c) <> text ("@" <> citationId c)
               <> fromList (citationSuffix c)
-    replaceRefs'' :: Options -> Prefix -> NonEmpty Citation -> WS [Inline]
-    replaceRefs'' opts = ($ opts) . flip $
+    replaceRefs'' :: Prefix -> NonEmpty Citation -> References -> [Inline]
+    replaceRefs'' = ($ opts) . flip $
       if isLatexFormat opts
       then replaceRefsLatex
       else replaceRefsOther
-replaceRefs x = return x
 
 -- accessors to options
 prefMap :: Prefix -> (Options -> Bool -> Int -> [Inline], Options -> Template)
@@ -89,7 +84,7 @@
   where (refprefixf, reftempl) = prefMap prefix
         refprefix = refprefixf opts capitalize num
 
-replaceRefsLatex :: Prefix -> Options -> NonEmpty Citation -> WS [Inline]
+replaceRefsLatex :: Prefix -> Options -> NonEmpty Citation -> References -> [Inline]
 replaceRefsLatex prefix opts cits
   | cref opts
   = replaceRefsLatex' prefix opts cits
@@ -97,7 +92,7 @@
   = toList . intercalate' (text ", ") . map fromList <$>
       mapM (replaceRefsLatex' prefix opts) (NE.groupBy citationGroupPred cits)
 
-replaceRefsLatex' :: Prefix -> Options -> NonEmpty Citation -> WS [Inline]
+replaceRefsLatex' :: Prefix -> Options -> NonEmpty Citation -> References -> [Inline]
 replaceRefsLatex' prefix opts cits =
   return $ p [texcit]
   where
@@ -135,17 +130,17 @@
   where
     p = uncapitalizeFirst $ flip T.snoc ':' . T.takeWhile (/=':') $ lab
 
-replaceRefsOther :: Prefix -> Options -> NonEmpty Citation -> WS [Inline]
+replaceRefsOther :: Prefix -> Options -> NonEmpty Citation -> References -> [Inline]
 replaceRefsOther prefix opts cits = toList . intercalate' (text ", ") . map fromList <$>
     traverse (replaceRefsOther' prefix opts) (NE.groupBy citationGroupPred cits)
 
 citationGroupPred :: Citation -> Citation -> Bool
 citationGroupPred = (==) `on` liftM2 (,) citationPrefix citationMode
 
-replaceRefsOther' :: Prefix -> Options -> NonEmpty Citation -> WS [Inline]
-replaceRefsOther' prefix opts cits = do
-  indices <- mapM (getRefIndex prefix opts) $ NE.toList cits
+replaceRefsOther' :: Prefix -> Options -> NonEmpty Citation -> References -> [Inline]
+replaceRefsOther' prefix opts cits refs =
   let
+    indices = getRefIndex prefix refs <$> NE.toList cits
     cap = isFirstUpper . citationId . NE.head $ cits
     writePrefix | all ((==SuppressAuthor) . citationMode) cits
                 = id
@@ -156,7 +151,7 @@
     cmap f [Link attr t w]
       | nameInLink opts = [Link attr (f t) w]
     cmap f x = f x
-  return $ writePrefix (makeIndices opts indices)
+  in writePrefix (makeIndices opts indices)
 
 data RefData = RefData { rdGlob :: Maybe Natural
                        , rdLabel :: T.Text
@@ -167,15 +162,14 @@
                        , rdPfx :: Prefix
                        } deriving (Eq)
 
-getRefIndex :: Prefix -> Options -> Citation -> WS RefData
-getRefIndex prefix _opts Citation{citationId=cid,citationSuffix=suf}
-  = do
-    ref <- M.lookup lab <$> use prop
-    let sub = refSubfigure <$> ref
+getRefIndex :: Prefix -> References -> Citation -> RefData
+getRefIndex prefix refs Citation{citationId=cid,citationSuffix=suf}
+  = let ref = M.lookup (IdxRef prefix lab) $ refs ^. prop
+        sub = refSubfigure <$> ref
         idx = refIndex <$> ref
         tit = refTitle <$> ref
         glob = refGlobal <$> ref
-    return RefData
+    in RefData
       { rdGlob = glob
       , rdLabel = lab
       , rdIdx = idx
diff --git a/lib-internal/Text/Pandoc/CrossRef/References/Types.hs b/lib-internal/Text/Pandoc/CrossRef/References/Types.hs
--- a/lib-internal/Text/Pandoc/CrossRef/References/Types.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/References/Types.hs
@@ -40,7 +40,14 @@
                      , refLabel :: Maybe Text
                      } deriving (Show, Eq)
 
-type RefMap = M.Map Text RefRec
+type RefMap = M.Map RefIndex RefRec
+
+data RefIndex = IdxSynth Natural | IdxRef Prefix Text
+  deriving (Eq, Ord, Show)
+
+displayIdx :: RefIndex -> String
+displayIdx (IdxSynth ctr) = "#" <> show ctr
+displayIdx (IdxRef _ txt) = T.unpack txt
 
 data Prefix
   = PfxImg
diff --git a/lib-internal/Text/Pandoc/CrossRef/Util/CodeBlockCaptions.hs b/lib-internal/Text/Pandoc/CrossRef/Util/CodeBlockCaptions.hs
--- a/lib-internal/Text/Pandoc/CrossRef/Util/CodeBlockCaptions.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/Util/CodeBlockCaptions.hs
@@ -23,28 +23,18 @@
     mkCodeBlockCaptions
     ) where
 
-import Control.Monad.Reader (ask)
 import Data.List (stripPrefix)
-import Data.Maybe (fromMaybe)
 import qualified Data.Text as T
 import Text.Pandoc.CrossRef.References.Types
-import Text.Pandoc.CrossRef.References.Monad
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.Util.Util
 import Text.Pandoc.Definition
 
-mkCodeBlockCaptions :: [Block] -> WS [Block]
-mkCodeBlockCaptions = \case
-  x@(cb@CodeBlock{}:p@Para{}:xs) -> go x p cb xs
-  x@(p@Para{}:cb@CodeBlock{}:xs) -> go x p cb xs
-  x -> pure x
-  where
-    go :: [Block] -> Block -> Block -> [Block] -> WS [Block]
-    go x p cb xs = do
-      opts <- ask
-      return $ fromMaybe x $ orderAgnostic opts $ p:cb:xs
+mkCodeBlockCaptions :: Options -> [Block] -> Maybe [Block]
+mkCodeBlockCaptions = orderAgnostic
 
 orderAgnostic :: Options -> [Block] -> Maybe [Block]
+orderAgnostic opts (a@CodeBlock{}:b@Para{}:xs) = orderAgnostic opts (b:a:xs)
 orderAgnostic opts (Para ils:CodeBlock (label,classes,attrs) code:xs)
   | codeBlockCaptions opts
   , Just caption <- getCodeBlockCaption ils
diff --git a/lib-internal/Text/Pandoc/CrossRef/Util/Generic.hs b/lib-internal/Text/Pandoc/CrossRef/Util/Generic.hs
new file mode 100644
--- /dev/null
+++ b/lib-internal/Text/Pandoc/CrossRef/Util/Generic.hs
@@ -0,0 +1,131 @@
+{-
+pandoc-crossref is a pandoc filter for numbering figures,
+equations, tables and cross-references to them.
+Copyright (C) 2015  Nikolay Yakimov <root@livid.pp.ru>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+-}
+
+{-# LANGUAGE MonoLocalBinds, TypeAbstractions #-}
+
+module Text.Pandoc.CrossRef.Util.Generic
+  ( module Data.Generics
+  , runReplace
+  , replaceRecurse
+  , replaceNoRecurse
+  , ReplacedResult
+  , noReplaceRecurse
+  , mkRR
+  , extRR
+  , replaceList
+  , fixRefs
+  , fixRefs'
+  , replaceRefsWalk
+  ) where
+
+import Data.Generics
+import Text.Pandoc.Builder hiding ((<>))
+import Text.Pandoc.CrossRef.References.Monad
+import Text.Pandoc.CrossRef.References.Types
+import Text.Pandoc.CrossRef.References.Refs
+import Text.Pandoc.CrossRef.Util.Options
+import Lens.Micro.Mtl
+import Lens.Micro
+import qualified Text.Pandoc.Builder as B
+
+data ReplacedResult a where
+  Replaced :: Recurse -> a -> ReplacedResult a
+  ReplacedList :: FixRefs a b => ([a] -> WS [a]) -> b -> b -> ReplacedResult [a]
+  NotReplaced :: ReplacedResult a
+data Recurse = Recurse | NoRecurse
+newtype RR m a = RR {unRR :: a -> m (ReplacedResult a)}
+
+class Monoid b => FixRefs a b where
+  lens' :: Lens' b [a]
+
+instance FixRefs Inline Inlines where
+  lens' = lens B.toList (const B.fromList)
+
+instance FixRefs Block Blocks where
+  lens' = lens B.toList (const B.fromList)
+
+instance FixRefs a [a] where
+  lens' = id
+
+fixRefs :: (Data a, FixRefs a b, Monad m) => m (Maybe b) -> [a] -> m (ReplacedResult [a])
+fixRefs x rest =
+  -- NB: must be very-very careful to not inspect the result beyond Maybe,
+  -- otherwise fix in deferred part will loop
+  x >>= \case
+    Just res' -> replaceList' pure res' rest
+    Nothing -> noReplaceRecurse
+
+fixRefs' :: Inline -> [Inline] -> WS (ReplacedResult [Inline])
+fixRefs' = fixRefs . replaceRefsInline
+
+replaceRefsInline :: Inline -> WS (Maybe Inlines)
+replaceRefsInline (Cite cits _) = Just <$> do
+  opts <- use wsOptions
+  -- recurse into prefix/suffix
+  cits' <- doReplaceRefs cits
+  liftF $ replaceRefs cits' opts
+replaceRefsInline _ = pure Nothing
+
+replaceRefsWalk :: Data a => Options -> References -> a -> a
+replaceRefsWalk o r x = fst $ unwrapWS (doReplaceRefs x) (WState o r) r
+
+doReplaceRefs :: Data a => a -> WS a
+doReplaceRefs = runReplace $ mkRR \case
+  (x:xs) -> fixRefs' x xs
+  [] -> noReplaceRecurse
+
+runReplace
+  :: Data a
+  => (forall d. Data d => (d -> WS (ReplacedResult d)))
+  -> a -> WS a
+runReplace f x = f x >>= \case
+  Replaced Recurse x' -> gmapM (runReplace f) x'
+  Replaced NoRecurse x' -> pure x'
+  NotReplaced -> gmapM (runReplace f) x
+  ReplacedList @a hdT hd tl -> do
+    hd' <- hdT `lens'` hd
+    tl' <- runReplace @[a] f `lens'` tl
+    pure $ view lens' $ hd' <> tl'
+
+mkRR :: (Monad m, Typeable a, Typeable b)
+     => (b -> m (ReplacedResult b))
+     -> (a -> m (ReplacedResult a))
+mkRR = extRR (const noReplaceRecurse)
+
+extRR :: ( Monad m, Typeable a, Typeable b)
+     => (a -> m (ReplacedResult a))
+     -> (b -> m (ReplacedResult b))
+     -> (a -> m (ReplacedResult a))
+extRR def' ext = unRR (RR def' `ext0` RR ext)
+
+replaceRecurse :: Monad m => a -> m (ReplacedResult a)
+replaceRecurse = pure . Replaced Recurse
+
+replaceNoRecurse :: Data a => a -> WS (ReplacedResult a)
+replaceNoRecurse = fmap (Replaced NoRecurse) . doReplaceRefs
+
+replaceList :: (FixRefs a b, Monad m, Monoid b, Data a) => b -> [a] -> m (ReplacedResult [a])
+replaceList = replaceList' doReplaceRefs
+
+replaceList' :: (FixRefs a b, Monad m, Monoid b, Data a) => ([a] -> WS [a]) -> b -> [a] -> m (ReplacedResult [a])
+replaceList' hdT hd tl = pure $ ReplacedList hdT hd (mempty & lens' .~ tl)
+
+noReplaceRecurse :: Monad m => m (ReplacedResult a)
+noReplaceRecurse = pure NotReplaced
diff --git a/lib-internal/Text/Pandoc/CrossRef/Util/ModifyMeta.hs b/lib-internal/Text/Pandoc/CrossRef/Util/ModifyMeta.hs
--- a/lib-internal/Text/Pandoc/CrossRef/Util/ModifyMeta.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/Util/ModifyMeta.hs
@@ -24,7 +24,7 @@
     ) where
 
 import Control.Monad.Writer
-import Control.Monad (when, unless, (>=>))
+import Control.Monad (when, unless)
 import Data.Function ((&))
 import qualified Data.Text as T
 import Text.Pandoc
@@ -32,26 +32,21 @@
 import Text.Pandoc.CrossRef.Util.Meta
 import Text.Pandoc.CrossRef.Util.Options
 import Text.Pandoc.CrossRef.References.List
-import Text.Pandoc.CrossRef.References.Monad
+import Text.Pandoc.CrossRef.References.Types
 
-modifyMeta :: Options -> Meta -> WS Meta
-modifyMeta opts meta
+modifyMeta :: Options -> Meta -> References -> Meta
+modifyMeta opts meta refs
   = meta
     & opt isLatexFormat (setMeta "header-includes" (headerInc $ lookupMeta "header-includes" meta))
-    & optM listOfMetadata
-        ( setMetaM "list-of-figures"    (fromList <$> listOfFigures)
-        >=> setMetaM "list-of-tables"   (fromList <$> listOfTables)
-        >=> setMetaM "list-of-listings" (fromList <$> listOfListings)
+    & opt listOfMetadata
+        ( setMeta "list-of-figures"    (fromList $ listOfFigures opts refs)
+        . setMeta "list-of-tables"   (fromList $ listOfTables opts refs)
+        . setMeta "list-of-listings" (fromList $ listOfListings opts refs)
         )
   where
-    setMetaM :: ToMetaValue v => T.Text -> WS v -> Meta -> WS Meta
-    setMetaM k m meta' = m >>= \v -> pure $ setMeta k v meta'
     opt q f
       | q opts = f
       | otherwise = id
-    optM q f
-      | q opts = f
-      | otherwise = pure
     headerInc :: Maybe MetaValue -> MetaValue
     headerInc Nothing = incList
     headerInc (Just (MetaList x)) = MetaList $ x <> [incList]
@@ -73,9 +68,9 @@
       where
         atEndPreamble = censor (\c -> "\\AtEndPreamble{%":c <> ["}"])
         subfig = [
-            usepackage [] "subfig"
+            usepackage [] "subcaption"
           , usepackage [] "caption"
-          , "\\captionsetup[subfloat]{margin=0.5em}"
+          , "\\captionsetup[subfigure]{margin=0.5em}"
           ]
         floatnames = [
             "\\AtBeginDocument{%"
diff --git a/lib-internal/Text/Pandoc/CrossRef/Util/Options.hs b/lib-internal/Text/Pandoc/CrossRef/Util/Options.hs
--- a/lib-internal/Text/Pandoc/CrossRef/Util/Options.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/Util/Options.hs
@@ -76,6 +76,7 @@
                        , tableEqns :: Bool
                        , autoEqnLabels :: Bool
                        , subfigGrid :: Bool
+                       , subfigColumns :: Bool
                        , linkReferences :: Bool
                        , nameInLink :: Bool
                        , setLabelAttribute :: Bool
@@ -84,7 +85,7 @@
                        }
 
 isLatexFormat :: Options -> Bool
-isLatexFormat = ((||) <$> (isFormat "latex") <*> (isFormat "beamer")) . outFormat
+isLatexFormat = ((||) <$> isFormat "latex" <*> isFormat "beamer") . outFormat
 
 isDocxFormat :: Options -> Bool
 isDocxFormat = isFormat "docx" . outFormat
diff --git a/lib-internal/Text/Pandoc/CrossRef/Util/Util.hs b/lib-internal/Text/Pandoc/CrossRef/Util/Util.hs
--- a/lib-internal/Text/Pandoc/CrossRef/Util/Util.hs
+++ b/lib-internal/Text/Pandoc/CrossRef/Util/Util.hs
@@ -65,45 +65,6 @@
   . S.filter (not . T.null)
   . fmap (uncurry (fromMaybe . T.pack . show))
 
-data ReplacedResult a = Replaced Bool a | NotReplaced Bool
-type GenRR m = forall a. Data a => (a -> m (ReplacedResult a))
-newtype RR m a = RR {unRR :: a -> m (ReplacedResult a)}
-
-runReplace :: (Monad m) => GenRR m -> GenericM m
-runReplace f x = do
-  res <- f x
-  case res of
-    Replaced True x' -> gmapM (runReplace f) x'
-    Replaced False x' -> return x'
-    NotReplaced True -> gmapM (runReplace f) x
-    NotReplaced False -> return x
-
-mkRR :: (Monad m, Typeable a, Typeable b)
-     => (b -> m (ReplacedResult b))
-     -> (a -> m (ReplacedResult a))
-mkRR = extRR (const noReplaceRecurse)
-
-extRR :: ( Monad m, Typeable a, Typeable b)
-     => (a -> m (ReplacedResult a))
-     -> (b -> m (ReplacedResult b))
-     -> (a -> m (ReplacedResult a))
-extRR def' ext = unRR (RR def' `ext0` RR ext)
-
-replaceRecurse :: Monad m => a -> m (ReplacedResult a)
-replaceRecurse = return . Replaced True
-
-replaceNoRecurse :: Monad m => a -> m (ReplacedResult a)
-replaceNoRecurse = return . Replaced False
-
-noReplace :: Monad m => Bool -> m (ReplacedResult a)
-noReplace recurse = return $ NotReplaced recurse
-
-noReplaceRecurse :: Monad m => m (ReplacedResult a)
-noReplaceRecurse = noReplace True
-
-noReplaceNoRecurse :: Monad m => m (ReplacedResult a)
-noReplaceNoRecurse = noReplace False
-
 mkLaTeXLabel :: T.Text -> T.Text
 mkLaTeXLabel l
  | T.null l = ""
diff --git a/lib/Text/Pandoc/CrossRef.hs b/lib/Text/Pandoc/CrossRef.hs
--- a/lib/Text/Pandoc/CrossRef.hs
+++ b/lib/Text/Pandoc/CrossRef.hs
@@ -79,13 +79,12 @@
   , CrossRefEnv(..)
   ) where
 
-import qualified Control.Monad.Reader as R
-import Control.Monad.State
+import Lens.Micro.Mtl
+import Control.Monad.Reader
 import Text.Pandoc
 
 import Text.Pandoc.CrossRef.References
 import Text.Pandoc.CrossRef.References.Monad
-import Text.Pandoc.CrossRef.Util.CodeBlockCaptions
 import Text.Pandoc.CrossRef.Util.ModifyMeta
 import Text.Pandoc.CrossRef.Util.Settings
 import Text.Pandoc.CrossRef.Util.Settings.Gen as SG
@@ -96,40 +95,29 @@
 
 Works in 'CrossRefM' monad. -}
 crossRefBlocks :: [Block] -> CrossRefM [Block]
-crossRefBlocks blocks = CrossRefM $ R.withReaderT creOptions $ runWS doWalk
-  where
-    doWalk =
-      bottomUpM mkCodeBlockCaptions blocks
-      >>= replaceAll
-      >>= bottomUpM replaceRefs
-      >>= bottomUpM listOf
+crossRefBlocks = CrossRefM . lift . replaceAll
 
 {- | Modifies metadata, adding header-includes instructions to setup custom and
 builtin environments, plus list-of-x metadata fields if
 'Text.Pandoc.CrossRef.Util.Settings.Gen.listOfMetadata' is enabled.
 
-Note for @listOfMetadata@ to work properly, this MUST be invoked AFTER
-'crossRefBlocks'.
-
 Works in 'CrossRefM' monad. -}
 crossRefMeta :: CrossRefM Meta
-crossRefMeta = do
-  opts <- R.asks creOptions
-  dtv <- R.asks creSettings
-  CrossRefM $ R.withReaderT creOptions $ runWS $ modifyMeta opts dtv
+crossRefMeta = CrossRefM do
+  settings <- ask
+  lift do
+    opts <- use wsOptions
+    liftF $ modifyMeta opts settings
 
 {- | Combines 'crossRefMeta' and 'crossRefBlocks'
 
 Works in 'CrossRefM' monad. -}
 defaultCrossRefAction :: Pandoc -> CrossRefM Pandoc
-defaultCrossRefAction (Pandoc _ bs) = do
-  bs' <- crossRefBlocks bs
-  meta' <- crossRefMeta
-  return $ Pandoc meta' bs'
+defaultCrossRefAction (Pandoc _ bs) = Pandoc <$> crossRefMeta <*> crossRefBlocks bs
 
 {- | Run an action in 'CrossRefM' monad with argument, and return pure result.
 
-This is primary function to work with 'CrossRefM' -}
+This is the primary function to work with 'CrossRefM' -}
 runCrossRef :: forall a b. Meta -> Maybe Format -> (a -> CrossRefM b) -> a -> b
 runCrossRef meta fmt action arg = runCrossRefInternal env $ action arg
   where
@@ -137,6 +125,7 @@
     env = CrossRefEnv {
             creSettings = settings
           , creOptions = getOptions settings fmt
+          , creReferences = def
          }
 
 {- | Run an action in 'CrossRefM' monad with argument, and return 'IO' result.
@@ -150,10 +139,10 @@
     env = CrossRefEnv {
             creSettings = settings
           , creOptions = getOptions settings fmt
+          , creReferences = def
          }
   pure $ runCrossRefInternal env $ action arg
 
 runCrossRefInternal :: CrossRefEnv -> CrossRefM b -> b
-runCrossRefInternal env (CrossRefM action) =
-  let (res, st) = flip runState def $ flip R.runReaderT env action
-   in st `seq` res
+runCrossRefInternal CrossRefEnv { creOptions, creReferences, creSettings } (CrossRefM action) =
+  fst $ runWS (WState creOptions creReferences) (runReaderT action creSettings)
diff --git a/lib/Text/Pandoc/CrossRef/Internal.hs b/lib/Text/Pandoc/CrossRef/Internal.hs
--- a/lib/Text/Pandoc/CrossRef/Internal.hs
+++ b/lib/Text/Pandoc/CrossRef/Internal.hs
@@ -33,10 +33,10 @@
 
 module Text.Pandoc.CrossRef.Internal (CrossRefEnv(..), CrossRefM(..)) where
 
-import qualified Control.Monad.Reader as R
-import Control.Monad.State
+import Control.Monad.Reader
 import Text.Pandoc
 
+import Text.Pandoc.CrossRef.References.Monad
 import Text.Pandoc.CrossRef.References
 import Text.Pandoc.CrossRef.Util.Options
 
@@ -44,8 +44,12 @@
 data CrossRefEnv = CrossRefEnv {
                       creSettings :: Meta -- ^Metadata settings
                     , creOptions :: Options -- ^Internal pandoc-crossref options
+                    , creReferences :: References -- ^ Internal state tracking references
                    }
 
--- | Reader + State monad for pandoc-crossref.
-newtype CrossRefM a = CrossRefM (R.ReaderT CrossRefEnv (State References) a)
-  deriving (Functor, Applicative, Monad, R.MonadReader CrossRefEnv, MonadState References)
+-- | Bit of a weird self-recursive State monad (not MonadFix). The inner reader
+-- (function) monad takes the final state (or part of it anyway) as its input.
+-- This is all carefully choreographed to lazily converge to a fixpoint, but it
+-- is also feasible to evaluate this in two passes instead.
+newtype CrossRefM a = CrossRefM (ReaderT Meta WS a)
+  deriving (Functor, Applicative, Monad, MonadReader Meta)
diff --git a/pandoc-crossref.cabal b/pandoc-crossref.cabal
--- a/pandoc-crossref.cabal
+++ b/pandoc-crossref.cabal
@@ -1,11 +1,11 @@
 cabal-version: 2.0
 
--- This file has been generated from package.yaml by hpack version 0.37.0.
+-- This file has been generated from package.yaml by hpack version 0.38.1.
 --
 -- see: https://github.com/sol/hpack
 
 name:           pandoc-crossref
-version:        0.3.20
+version:        0.3.21
 synopsis:       Pandoc filter for cross-references
 description:    pandoc-crossref is a pandoc filter for numbering figures, equations, tables and cross-references to them.
 category:       Text
@@ -102,6 +102,12 @@
     test/m2m/numberOverride/expect.md
     test/m2m/numberOverride/expect.tex
     test/m2m/numberOverride/input.md
+    test/m2m/refsInListOf/expect.md
+    test/m2m/refsInListOf/expect.tex
+    test/m2m/refsInListOf/input.md
+    test/m2m/refsInObjects/expect.md
+    test/m2m/refsInObjects/expect.tex
+    test/m2m/refsInObjects/input.md
     test/m2m/secLabels/expect.md
     test/m2m/secLabels/expect.tex
     test/m2m/secLabels/input.md
@@ -114,9 +120,15 @@
     test/m2m/setLabelAttribute/expect.md
     test/m2m/setLabelAttribute/expect.tex
     test/m2m/setLabelAttribute/input.md
+    test/m2m/standalone-listOfMetadata/expect.md
+    test/m2m/standalone-listOfMetadata/expect.tex
+    test/m2m/standalone-listOfMetadata/input.md
     test/m2m/subfigures-ccsDelim/expect.md
     test/m2m/subfigures-ccsDelim/expect.tex
     test/m2m/subfigures-ccsDelim/input.md
+    test/m2m/subfigures-columns/expect.md
+    test/m2m/subfigures-columns/expect.tex
+    test/m2m/subfigures-columns/input.md
     test/m2m/subfigures-grid/expect.md
     test/m2m/subfigures-grid/expect.tex
     test/m2m/subfigures-grid/input.md
@@ -157,8 +169,10 @@
   ghc-options: -Wall
   build-depends:
       base >=4.16 && <5
+    , microlens >=0.4.12.0 && <0.5.0.0
+    , microlens-mtl >=0.2.0.1 && <0.3.0.0
     , mtl >=1.1 && <2.4
-    , pandoc >=3.7.0.2 && <3.8
+    , pandoc ==3.8.*
     , pandoc-crossref-internal
     , pandoc-types ==1.23.*
     , text >=1.2.2 && <2.2
@@ -181,6 +195,7 @@
       Text.Pandoc.CrossRef.References.Types
       Text.Pandoc.CrossRef.Util.CodeBlockCaptions
       Text.Pandoc.CrossRef.Util.CustomLabels
+      Text.Pandoc.CrossRef.Util.Generic
       Text.Pandoc.CrossRef.Util.Meta
       Text.Pandoc.CrossRef.Util.ModifyMeta
       Text.Pandoc.CrossRef.Util.Options
@@ -202,8 +217,8 @@
       BlockArguments
   build-depends:
       base >=4.16 && <5
-    , containers >=0.1 && <0.7
-    , data-default >=0.4 && <0.8
+    , containers >=0.1 && <0.9
+    , data-default >=0.4 && <0.9
     , directory >=1 && <1.4
     , filepath >=1.1 && <1.6
     , microlens >=0.4.12.0 && <0.5.0.0
@@ -211,7 +226,7 @@
     , microlens-mtl >=0.2.0.1 && <0.3.0.0
     , microlens-th >=0.4.3.10 && <0.5.0.0
     , mtl >=1.1 && <2.4
-    , pandoc >=3.7.0.2 && <3.8
+    , pandoc ==3.8.*
     , pandoc-types ==1.23.*
     , syb >=0.4 && <0.8
     , template-haskell >=2.7.0.0 && <3.0.0.0
@@ -239,9 +254,9 @@
       base >=4.16 && <5
     , deepseq >=1.4 && <1.6
     , gitrev >=1.3.1 && <1.4
-    , open-browser ==0.2.*
-    , optparse-applicative >=0.13 && <0.19
-    , pandoc >=3.7.0.2 && <3.8
+    , open-browser >=0.2 && <0.4
+    , optparse-applicative >=0.13 && <0.20
+    , pandoc ==3.8.*
     , pandoc-crossref
     , pandoc-types ==1.23.*
     , template-haskell >=2.7.0.0 && <3.0.0.0
@@ -271,7 +286,7 @@
     , directory >=1 && <1.4
     , filepath >=1.1 && <1.6
     , hspec >=2.4.4 && <3
-    , pandoc >=3.7.0.2 && <3.8
+    , pandoc ==3.8.*
     , pandoc-crossref
     , pandoc-types ==1.23.*
     , text >=1.2.2 && <2.2
@@ -301,12 +316,13 @@
   ghc-options: -Wall -fno-warn-unused-do-bind -threaded
   build-depends:
       base >=4.16 && <5
-    , containers >=0.1 && <0.7
-    , data-default >=0.4 && <0.8
+    , containers >=0.1 && <0.9
+    , data-default >=0.4 && <0.9
     , hspec >=2.4.4 && <3
     , microlens >=0.4.12.0 && <0.5.0.0
+    , microlens-mtl >=0.2.0.1 && <0.3.0.0
     , mtl >=1.1 && <2.4
-    , pandoc >=3.7.0.2 && <3.8
+    , pandoc ==3.8.*
     , pandoc-crossref
     , pandoc-crossref-internal
     , pandoc-types ==1.23.*
@@ -335,7 +351,7 @@
   build-depends:
       base >=4.16 && <5
     , criterion >=1.5.9.0 && <1.7
-    , pandoc >=3.7.0.2 && <3.8
+    , pandoc ==3.8.*
     , pandoc-crossref
     , pandoc-types ==1.23.*
     , text >=1.2.2 && <2.2
diff --git a/src/ManData.hs b/src/ManData.hs
--- a/src/ManData.hs
+++ b/src/ManData.hs
@@ -62,7 +62,7 @@
           $   P.compileDefaultTemplate "html5"
   embedManual $ P.writeHtml5String P.def{
     P.writerTemplate = Just tt
-  , P.writerHighlightStyle = Just pygments
+  , P.writerHighlightMethod = P.Skylighting pygments
   , P.writerTOCDepth = 6
   , P.writerTableOfContents = True
   }
diff --git a/test/demo-chapters.inc b/test/demo-chapters.inc
--- a/test/demo-chapters.inc
+++ b/test/demo-chapters.inc
@@ -462,6 +462,82 @@
               [ Image ( "" , [] , [] ) [ Str "b" ] ( "img1.jpg" , "" ) ]
           ]
       ]
+  , Figure
+      ( "fig:subfigures-side-by-side" , [ "subfigures" ] , [] )
+      (Caption
+         Nothing
+         [ Para
+             [ Str "Figure"
+             , Space
+             , Str "#"
+             , Space
+             , Str "1.5:"
+             , Space
+             , Str "Subfigures"
+             , Space
+             , Str "side"
+             , Space
+             , Str "by"
+             , Space
+             , Str "side."
+             , Space
+             , Str "a"
+             , Space
+             , Str "\8212"
+             , Space
+             , Str "Subfigure"
+             , Space
+             , Str "a,"
+             , Space
+             , Str "b"
+             , Space
+             , Str "\8212"
+             , Space
+             , Str "Subfigure"
+             , Space
+             , Str "b,"
+             , Space
+             , Str "c"
+             , Space
+             , Str "\8212"
+             , Space
+             , Str "Subfigure"
+             , Space
+             , Str "c"
+             ]
+         ])
+      [ Div
+          ( "" , [ "columns" ] , [] )
+          [ Div
+              ( "" , [ "column" ] , [ ( "width" , "49.5%" ) ] )
+              [ Figure
+                  ( "" , [] , [] )
+                  (Caption Nothing [ Plain [ Str "a" ] ])
+                  [ Plain
+                      [ Image
+                          ( "" , [] , [] ) [ Str "a" ] ( "img1.jpg" , "" )
+                      ]
+                  ]
+              ]
+          , Div
+              ( "" , [ "column" ] , [ ( "width" , "49.5%" ) ] )
+              [ Figure
+                  ( "" , [] , [] )
+                  (Caption Nothing [ Plain [ Str "b" ] ])
+                  [ Plain
+                      [ Image
+                          ( "" , [] , [] ) [ Str "b" ] ( "img2.jpg" , "" )
+                      ]
+                  ]
+              ]
+          ]
+      , Figure
+          ( "" , [] , [] )
+          (Caption Nothing [ Plain [ Str "c" ] ])
+          [ Plain
+              [ Image ( "" , [] , [] ) [ Str "c" ] ( "img3.jpg" , "" ) ]
+          ]
+      ]
   , Header
       1
       ( "sec:sec2" , [] , [] )
@@ -1104,6 +1180,48 @@
           , Str "Subfigure"
           , Space
           , Str "b"
+          , LineBreak
+          ]
+      , Plain
+          [ Str "1.5."
+          , Space
+          , Str "Subfigures"
+          , Space
+          , Str "side"
+          , Space
+          , Str "by"
+          , Space
+          , Str "side"
+          , LineBreak
+          ]
+      , Plain
+          [ Str "1.5"
+          , Space
+          , Str "(a)."
+          , Space
+          , Str "Subfigure"
+          , Space
+          , Str "a"
+          , LineBreak
+          ]
+      , Plain
+          [ Str "1.5"
+          , Space
+          , Str "(b)."
+          , Space
+          , Str "Subfigure"
+          , Space
+          , Str "b"
+          , LineBreak
+          ]
+      , Plain
+          [ Str "1.5"
+          , Space
+          , Str "(c)."
+          , Space
+          , Str "Subfigure"
+          , Space
+          , Str "c"
           , LineBreak
           ]
       ]
diff --git a/test/demo.inc b/test/demo.inc
--- a/test/demo.inc
+++ b/test/demo.inc
@@ -462,6 +462,82 @@
               [ Image ( "" , [] , [] ) [ Str "b" ] ( "img1.jpg" , "" ) ]
           ]
       ]
+  , Figure
+      ( "fig:subfigures-side-by-side" , [ "subfigures" ] , [] )
+      (Caption
+         Nothing
+         [ Para
+             [ Str "Figure"
+             , Space
+             , Str "#"
+             , Space
+             , Str "6:"
+             , Space
+             , Str "Subfigures"
+             , Space
+             , Str "side"
+             , Space
+             , Str "by"
+             , Space
+             , Str "side."
+             , Space
+             , Str "a"
+             , Space
+             , Str "\8212"
+             , Space
+             , Str "Subfigure"
+             , Space
+             , Str "a,"
+             , Space
+             , Str "b"
+             , Space
+             , Str "\8212"
+             , Space
+             , Str "Subfigure"
+             , Space
+             , Str "b,"
+             , Space
+             , Str "c"
+             , Space
+             , Str "\8212"
+             , Space
+             , Str "Subfigure"
+             , Space
+             , Str "c"
+             ]
+         ])
+      [ Div
+          ( "" , [ "columns" ] , [] )
+          [ Div
+              ( "" , [ "column" ] , [ ( "width" , "49.5%" ) ] )
+              [ Figure
+                  ( "" , [] , [] )
+                  (Caption Nothing [ Plain [ Str "a" ] ])
+                  [ Plain
+                      [ Image
+                          ( "" , [] , [] ) [ Str "a" ] ( "img1.jpg" , "" )
+                      ]
+                  ]
+              ]
+          , Div
+              ( "" , [ "column" ] , [ ( "width" , "49.5%" ) ] )
+              [ Figure
+                  ( "" , [] , [] )
+                  (Caption Nothing [ Plain [ Str "b" ] ])
+                  [ Plain
+                      [ Image
+                          ( "" , [] , [] ) [ Str "b" ] ( "img2.jpg" , "" )
+                      ]
+                  ]
+              ]
+          ]
+      , Figure
+          ( "" , [] , [] )
+          (Caption Nothing [ Plain [ Str "c" ] ])
+          [ Plain
+              [ Image ( "" , [] , [] ) [ Str "c" ] ( "img3.jpg" , "" ) ]
+          ]
+      ]
   , Header
       1
       ( "sec:sec2" , [] , [] )
@@ -1103,6 +1179,48 @@
           , Str "Subfigure"
           , Space
           , Str "b"
+          , LineBreak
+          ]
+      , Plain
+          [ Str "6."
+          , Space
+          , Str "Subfigures"
+          , Space
+          , Str "side"
+          , Space
+          , Str "by"
+          , Space
+          , Str "side"
+          , LineBreak
+          ]
+      , Plain
+          [ Str "6"
+          , Space
+          , Str "(a)."
+          , Space
+          , Str "Subfigure"
+          , Space
+          , Str "a"
+          , LineBreak
+          ]
+      , Plain
+          [ Str "6"
+          , Space
+          , Str "(b)."
+          , Space
+          , Str "Subfigure"
+          , Space
+          , Str "b"
+          , LineBreak
+          ]
+      , Plain
+          [ Str "6"
+          , Space
+          , Str "(c)."
+          , Space
+          , Str "Subfigure"
+          , Space
+          , Str "c"
           , LineBreak
           ]
       ]
diff --git a/test/m2m/equations-tables-auto/expect.md b/test/m2m/equations-tables-auto/expect.md
--- a/test/m2m/equations-tables-auto/expect.md
+++ b/test/m2m/equations-tables-auto/expect.md
@@ -1,46 +1,38 @@
 This is a test file with some referenced equations, line
 
-<div>
-
+::: {}
 +:-------------------------------------------------------------:+--------:+
 | $$ this $$                                                    | $$(1)$$ |
 +---------------------------------------------------------------+---------+
-
-</div>
+:::
 
 Some equations might be inside of text,
 
-<div>
-
+::: {}
 +:-------------------------------------------------------------:+--------:+
 | $$ for example $$                                             | $$(2)$$ |
 +---------------------------------------------------------------+---------+
-
-</div>
+:::
 
 this one.
 
 Some equations might be on start of paragraphs:
 
-<div>
-
+::: {}
 +:-------------------------------------------------------------:+--------:+
 | $$ start $$                                                   | $$(3)$$ |
 +---------------------------------------------------------------+---------+
-
-</div>
+:::
 
 of paragraph.
 
 Other might be on separate paragraphs of their own:
 
-<div>
-
+::: {}
 +:-------------------------------------------------------------:+--------:+
 | $$ separate $$                                                | $$(4)$$ |
 +---------------------------------------------------------------+---------+
-
-</div>
+:::
 
 Some of those can be labelled:
 
diff --git a/test/m2m/listOfHidden/expect.tex b/test/m2m/listOfHidden/expect.tex
--- a/test/m2m/listOfHidden/expect.tex
+++ b/test/m2m/listOfHidden/expect.tex
@@ -119,10 +119,10 @@
 
 \begin{pandoccrossrefsubfigures}
 
-\subfloat[hidden!
-subfigure1]{\pandocbounded{\includegraphics[keepaspectratio,alt={hidden! subfigure1}]{figs/fig_s2.jpg}}\label{fig:fig_sss1}}
-\subfloat[hidden!
-subfigure2]{\pandocbounded{\includegraphics[keepaspectratio,alt={hidden! subfigure2}]{figs/fig_s2.jpg}}\label{fig:fig_sss2}}
+\begin{subfigure}{0.495\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={hidden! subfigure1}]{figs/fig_s2.jpg}}\caption{hidden!
+subfigure1\label{fig:fig_sss1}}\end{subfigure}%
+\begin{subfigure}{0.495\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={hidden! subfigure2}]{figs/fig_s2.jpg}}\caption{hidden!
+subfigure2\label{fig:fig_sss2}}\end{subfigure}%
 
 \caption[]{hidden! subfigures}\label{fig:subfigs}
 
diff --git a/test/m2m/refsInListOf/expect.md b/test/m2m/refsInListOf/expect.md
new file mode 100644
--- /dev/null
+++ b/test/m2m/refsInListOf/expect.md
@@ -0,0 +1,32 @@
+# Chapter 1
+
+![Figure 1: first figure caption with a citation to fig. 2 in
+it](fig1.jpg){#fig:fig1}
+
+Some filler text, with in-text citations of both fig. 1 and fig. 2, and
+also (functionally) citing tbl. 1
+
+![Figure 2: second figure caption with a citation to fig. 1 in
+it](fig2.jpg){#fig:fig2}
+
+There's actually a new problem where it breaks *other* valid references
+if you try to cite them in main text next to one of the ones cited in a
+figure caption, e.g. (fig. 1, tbl. 1) or (fig. 2, tbl. 1), although if
+you cite the thing alone it'll work just fine (tbl. 1)
+
+::: {#tbl:tbl1}
+  col1   col2
+  ------ ------
+  row1   row1
+  row2   ro2
+
+  : Table 1: Table 1
+:::
+
+# List of Figures
+
+::: {.list .list-of-fig}
+1\. first figure caption with a citation to fig. 2 in it\
+
+2\. second figure caption with a citation to fig. 1 in it\
+:::
diff --git a/test/m2m/refsInListOf/expect.tex b/test/m2m/refsInListOf/expect.tex
new file mode 100644
--- /dev/null
+++ b/test/m2m/refsInListOf/expect.tex
@@ -0,0 +1,42 @@
+\section{Chapter 1}\label{chapter-1}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={first figure caption with a citation to fig.~ in it}]{fig1.jpg}}
+\caption{first figure caption with a citation to fig.~\ref{fig:fig2} in
+it}\label{fig:fig1}
+\end{figure}
+
+Some filler text, with in-text citations of both fig.~\ref{fig:fig1} and
+fig.~\ref{fig:fig2}, and also (functionally) citing tbl.~\ref{tbl:tbl1}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={second figure caption with a citation to fig.~ in it}]{fig2.jpg}}
+\caption{second figure caption with a citation to fig.~\ref{fig:fig1} in
+it}\label{fig:fig2}
+\end{figure}
+
+There's actually a new problem where it breaks \emph{other} valid
+references if you try to cite them in main text next to one of the ones
+cited in a figure caption, e.g.~(fig.~\ref{fig:fig1}) or
+(fig.~\ref{fig:fig2}), although if you cite the thing alone it'll work
+just fine (tbl.~\ref{tbl:tbl1})
+
+\begin{longtable}[]{@{}ll@{}}
+\caption{\label{tbl:tbl1}Table 1}\tabularnewline
+\toprule\noalign{}
+col1 & col2 \\
+\midrule\noalign{}
+\endfirsthead
+\toprule\noalign{}
+col1 & col2 \\
+\midrule\noalign{}
+\endhead
+\bottomrule\noalign{}
+\endlastfoot
+row1 & row1 \\
+row2 & ro2 \\
+\end{longtable}
+
+\listoffigures
diff --git a/test/m2m/refsInListOf/input.md b/test/m2m/refsInListOf/input.md
new file mode 100644
--- /dev/null
+++ b/test/m2m/refsInListOf/input.md
@@ -0,0 +1,20 @@
+# Chapter 1
+
+![first figure caption with a citation to [@fig:fig2] in it](fig1.jpg){#fig:fig1}
+
+Some filler text, with in-text citations of both [@fig:fig1] and [@fig:fig2], and also (functionally) citing [@tbl:tbl1]
+
+![second figure caption with a citation to [@fig:fig1] in it](fig2.jpg){#fig:fig2}
+
+There's actually a new problem where it breaks *other* valid references if you try to cite them in main text next to one of the ones cited in a figure caption, e.g. ([@fig:fig1, @tbl:tbl1]) or ([@fig:fig2, @tbl:tbl1]), although if you cite the thing alone it'll work just fine ([@tbl:tbl1])
+
+
+| col1 | col2 |
+|------|------|
+| row1 | row1 |
+| row2 | ro2  |
+
+: Table 1 {#tbl:tbl1}
+
+
+\listoffigures
diff --git a/test/m2m/refsInObjects/expect.md b/test/m2m/refsInObjects/expect.md
new file mode 100644
--- /dev/null
+++ b/test/m2m/refsInObjects/expect.md
@@ -0,0 +1,29 @@
+# sec. 1 {#sec:hdr}
+
+![Figure 1: fig. 1](foo.png){#fig:fig}
+
+::: {#tbl:table}
+  -- -- --
+        
+  -- -- --
+
+  : Table 1: tbl. 1 fig. 2 fig. 2 (a) fig. 2 (b) lst. 1
+:::
+
+::: {#lst:lst .listing .cpp}
+Listing 1: Listing tbl. 1 fig. 2 fig. 2 (a) fig. 2 (b) lst. 1
+
+``` cpp
+foo
+```
+:::
+
+:::: {#fig:subfigures .figure .subfigures}
+![a](fig1.png){#fig:subfig1} ![b](fig2.png){#fig:subfig2} ![c](fig3.png)
+
+::: caption
+Figure 2: Caption tbl. 1 fig. 2 fig. 2 (a) fig. 2 (b) lst. 1. a ---
+tbl. 1 fig. 2 fig. 2 (a) fig. 2 (b) lst. 1, b --- tbl. 1 fig. 2 fig. 2
+(a) fig. 2 (b) lst. 1, c --- 3
+:::
+::::
diff --git a/test/m2m/refsInObjects/expect.tex b/test/m2m/refsInObjects/expect.tex
new file mode 100644
--- /dev/null
+++ b/test/m2m/refsInObjects/expect.tex
@@ -0,0 +1,49 @@
+\section{\texorpdfstring{sec.~\ref{sec:hdr}}{sec.~}}\label{sec:hdr}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={fig.~}]{foo.png}}
+\caption{fig.~\ref{fig:fig}}\label{fig:fig}
+\end{figure}
+
+\begin{longtable}[]{@{}lll@{}}
+\caption{\label{tbl:table}tbl.~\ref{tbl:table} fig.~\ref{fig:subfigures}
+fig.~\ref{fig:subfig1} fig.~\ref{fig:subfig2}
+lst.~\ref{lst:lst}}\tabularnewline
+\toprule\noalign{}
+\endfirsthead
+\endhead
+\bottomrule\noalign{}
+\endlastfoot
+& & \\
+\end{longtable}
+
+\begin{codelisting}
+
+\caption{Listing tbl.~\ref{tbl:table} fig.~\ref{fig:subfigures}
+fig.~\ref{fig:subfig1} fig.~\ref{fig:subfig2}
+lst.~\ref{lst:lst}}\label{lst:lst}
+
+\begin{Shaded}
+\begin{Highlighting}[]
+\NormalTok{foo}
+\end{Highlighting}
+\end{Shaded}
+
+\end{codelisting}
+
+\begin{pandoccrossrefsubfigures}
+
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={tbl.~ fig.~ fig.~ fig.~ lst.~}]{fig1.png}}\caption{tbl.~\ref{tbl:table}
+fig.~\ref{fig:subfigures} fig.~\ref{fig:subfig1} fig.~\ref{fig:subfig2}
+lst.~\ref{lst:lst}\label{fig:subfig1}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={tbl.~ fig.~ fig.~ fig.~ lst.~}]{fig2.png}}\caption{tbl.~\ref{tbl:table}
+fig.~\ref{fig:subfigures} fig.~\ref{fig:subfig1} fig.~\ref{fig:subfig2}
+lst.~\ref{lst:lst}\label{fig:subfig2}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
+
+\caption{Caption tbl.~\ref{tbl:table} fig.~\ref{fig:subfigures}
+fig.~\ref{fig:subfig1} fig.~\ref{fig:subfig2}
+lst.~\ref{lst:lst}}\label{fig:subfigures}
+
+\end{pandoccrossrefsubfigures}
diff --git a/test/m2m/refsInObjects/input.md b/test/m2m/refsInObjects/input.md
new file mode 100644
--- /dev/null
+++ b/test/m2m/refsInObjects/input.md
@@ -0,0 +1,29 @@
+---
+codeBlockCaptions: true
+...
+
+# @sec:hdr {#sec:hdr}
+
+![@fig:fig](foo.png){#fig:fig}
+
+| | | |
+|-|-|-|
+| | | |
+
+: @tbl:table @fig:subfigures @fig:subfig1 @fig:subfig2 @lst:lst {#tbl:table}
+
+<!---->
+
+```{#lst:lst .cpp}
+foo
+```
+
+: Listing @tbl:table @fig:subfigures @fig:subfig1 @fig:subfig2 @lst:lst
+
+<div id="fig:subfigures">
+  ![@tbl:table @fig:subfigures @fig:subfig1 @fig:subfig2 @lst:lst](fig1.png){#fig:subfig1}
+  ![@tbl:table @fig:subfigures @fig:subfig1 @fig:subfig2 @lst:lst](fig2.png){#fig:subfig2}
+  ![3](fig3.png)
+
+  Caption @tbl:table @fig:subfigures @fig:subfig1 @fig:subfig2 @lst:lst
+</div>
diff --git a/test/m2m/standalone-listOfMetadata/expect.md b/test/m2m/standalone-listOfMetadata/expect.md
new file mode 100644
--- /dev/null
+++ b/test/m2m/standalone-listOfMetadata/expect.md
@@ -0,0 +1,245 @@
+---
+autoEqnLabels: false
+autoSectionLabels: false
+ccsDelim: ", "
+ccsLabelSep: " --- "
+ccsTemplate: $$i$$$$ccsLabelSep$$$$t$$
+chapDelim: .
+chapters: false
+chaptersDepth: 1
+codeBlockCaptions: true
+cref: false
+crossrefYaml: pandoc-crossref.yaml
+eqLabels: arabic
+eqnBlockInlineMath: false
+eqnBlockTemplate: |
+  +:-------------------------------------------------------------:+------:+
+  | $$t$$                                                         | $$i$$ |
+  +---------------------------------------------------------------+-------+
+eqnDisplayTemplate: $$e$$
+eqnIndexTemplate: ($$i$$)
+eqnInlineTableTemplate: $$e$$
+eqnInlineTemplate: $$e$$$$equationNumberTeX$${$$i$$}
+eqnPrefix:
+- eq.
+- eqns.
+eqnPrefixTemplate: $$p$$ $$i$$
+equationNumberTeX: \\qquad
+figLabels: arabic
+figPrefix:
+- fig.
+- figs.
+figPrefixTemplate: $$p$$ $$i$$
+figureTemplate: $$figureTitle$$ $$i$$$$titleDelim$$ $$t$$
+figureTitle: Figure
+lastDelim: ", "
+linkReferences: false
+list-of-figures: |
+  # List of Figures
+
+  ::: {.list .list-of-fig}
+  1\. 1\
+
+  2\. 2\
+
+  3\. 3\
+
+  4\. 4\
+
+  5\. 5\
+
+  6\. 6\
+
+  7\. 7\
+
+  8\. 8\
+
+  9\. 9\
+  :::
+list-of-listings: |
+  # List of Listings
+
+  ::: {.list .list-of-lst}
+  1\. Listing caption 1\
+
+  2\. Listing caption 2\
+
+  3\. Listing caption 3\
+
+  4\. Listing caption 4\
+  :::
+list-of-tables: |
+  # List of Tables
+
+  ::: {.list .list-of-tbl}
+  1\. My table\
+
+  2\. Table\
+  :::
+listings: false
+listingTemplate: $$listingTitle$$ $$i$$$$titleDelim$$ $$t$$
+listingTitle: Listing
+listItemTitleDelim: .
+listOfMetadata: true
+lofItemTemplate: |
+  $$lofItemTitle$$$$i$$$$listItemTitleDelim$$ $$t$$\
+lofTitle: |
+  # List of Figures
+lolItemTemplate: |
+  $$lolItemTitle$$$$i$$$$listItemTitleDelim$$ $$t$$\
+lolTitle: |
+  # List of Listings
+lotItemTemplate: |
+  $$lotItemTitle$$$$i$$$$listItemTitleDelim$$ $$t$$\
+lotTitle: |
+  # List of Tables
+lstLabels: arabic
+lstPrefix:
+- lst.
+- lsts.
+lstPrefixTemplate: $$p$$ $$i$$
+nameInLink: false
+numberSections: false
+pairDelim: ", "
+rangeDelim: "-"
+refDelim: ", "
+refIndexTemplate: $$i$$$$suf$$
+secHeaderDelim:
+secHeaderTemplate: $$i$$$$secHeaderDelim[n]$$$$t$$
+secLabels: arabic
+secPrefix:
+- sec.
+- secs.
+secPrefixTemplate: $$p$$ $$i$$
+sectionsDepth: 0
+standalone: true
+subfigGrid: false
+subfigLabels: alpha a
+subfigureChildTemplate: $$i$$
+subfigureRefIndexTemplate: $$i$$$$suf$$ ($$s$$)
+subfigureTemplate: $$figureTitle$$ $$i$$$$titleDelim$$ $$t$$. $$ccs$$
+tableEqns: false
+tableTemplate: $$tableTitle$$ $$i$$$$titleDelim$$ $$t$$
+tableTitle: Table
+tblLabels: arabic
+tblPrefix:
+- tbl.
+- tbls.
+tblPrefixTemplate: $$p$$ $$i$$
+titleDelim: ":"
+---
+
+![Figure 1: 1](fig1.png){#fig:1}
+
+![Figure 2: 2](fig2.png){#fig:2}
+
+![Figure 3: 3](fig3.png){#fig:3}
+
+![Figure 4: 4](fig4.png){#fig:4}
+
+![Figure 5: 5](fig5.png){#fig:5}
+
+![Figure 6: 6](fig6.png){#fig:6}
+
+![Figure 7: 7](fig7.png){#fig:7}
+
+![Figure 8: 8](fig8.png){#fig:8}
+
+![Figure 9: 9](fig9.png){#fig:9}
+
+::: {#lst:code1 .listing .haskell}
+Listing 1: Listing caption 1
+
+``` haskell
+main :: IO ()
+main = putStrLn "Hello World!"
+```
+:::
+
+::: {#lst:code2 .listing .haskell}
+Listing 2: Listing caption 2
+
+``` haskell
+main :: IO ()
+main = putStrLn "Hello World!"
+```
+:::
+
+::: {#lst:code3 .listing .haskell}
+Listing 3: Listing caption 3
+
+``` haskell
+main :: IO ()
+main = putStrLn "Hello World!"
+```
+:::
+
+::: {#lst:code4 .listing .haskell}
+Listing 4: Listing caption 4
+
+``` haskell
+main :: IO ()
+main = putStrLn "Hello World!"
+```
+:::
+
+------------------------------------------------------------------------
+
+::: {#tbl:mytable}
+  a   b   c
+  --- --- ---
+  1   2   3
+  4   5   6
+
+  : Table 1: My table
+:::
+
+::: {#tbl:1}
+  a   b
+  --- ---
+  1   2
+
+  : Table 2: Table
+:::
+
+# List of Figures
+
+::: {.list .list-of-fig}
+1\. 1\
+
+2\. 2\
+
+3\. 3\
+
+4\. 4\
+
+5\. 5\
+
+6\. 6\
+
+7\. 7\
+
+8\. 8\
+
+9\. 9\
+:::
+
+# List of Tables
+
+::: {.list .list-of-tbl}
+1\. My table\
+
+2\. Table\
+:::
+
+# List of Listings
+
+::: {.list .list-of-lst}
+1\. Listing caption 1\
+
+2\. Listing caption 2\
+
+3\. Listing caption 3\
+
+4\. Listing caption 4\
+:::
diff --git a/test/m2m/standalone-listOfMetadata/expect.tex b/test/m2m/standalone-listOfMetadata/expect.tex
new file mode 100644
--- /dev/null
+++ b/test/m2m/standalone-listOfMetadata/expect.tex
@@ -0,0 +1,144 @@
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}
+\caption{1}\label{fig:1}
+\end{figure}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}
+\caption{2}\label{fig:2}
+\end{figure}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}
+\caption{3}\label{fig:3}
+\end{figure}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}
+\caption{4}\label{fig:4}
+\end{figure}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}
+\caption{5}\label{fig:5}
+\end{figure}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}
+\caption{6}\label{fig:6}
+\end{figure}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}
+\caption{7}\label{fig:7}
+\end{figure}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}
+\caption{8}\label{fig:8}
+\end{figure}
+
+\begin{figure}
+\centering
+\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}
+\caption{9}\label{fig:9}
+\end{figure}
+
+\begin{codelisting}
+
+\caption{Listing caption 1}\label{lst:code1}
+
+\begin{Shaded}
+\begin{Highlighting}[]
+\OtherTok{main ::} \DataTypeTok{IO}\NormalTok{ ()}
+\NormalTok{main }\OtherTok{=} \FunctionTok{putStrLn} \StringTok{"Hello World!"}
+\end{Highlighting}
+\end{Shaded}
+
+\end{codelisting}
+
+\begin{codelisting}
+
+\caption{Listing caption 2}\label{lst:code2}
+
+\begin{Shaded}
+\begin{Highlighting}[]
+\OtherTok{main ::} \DataTypeTok{IO}\NormalTok{ ()}
+\NormalTok{main }\OtherTok{=} \FunctionTok{putStrLn} \StringTok{"Hello World!"}
+\end{Highlighting}
+\end{Shaded}
+
+\end{codelisting}
+
+\begin{codelisting}
+
+\caption{Listing caption 3}\label{lst:code3}
+
+\begin{Shaded}
+\begin{Highlighting}[]
+\OtherTok{main ::} \DataTypeTok{IO}\NormalTok{ ()}
+\NormalTok{main }\OtherTok{=} \FunctionTok{putStrLn} \StringTok{"Hello World!"}
+\end{Highlighting}
+\end{Shaded}
+
+\end{codelisting}
+
+\begin{codelisting}
+
+\caption{Listing caption 4}\label{lst:code4}
+
+\begin{Shaded}
+\begin{Highlighting}[]
+\OtherTok{main ::} \DataTypeTok{IO}\NormalTok{ ()}
+\NormalTok{main }\OtherTok{=} \FunctionTok{putStrLn} \StringTok{"Hello World!"}
+\end{Highlighting}
+\end{Shaded}
+
+\end{codelisting}
+
+\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
+
+\begin{longtable}[]{@{}lll@{}}
+\caption{\label{tbl:mytable}My table}\tabularnewline
+\toprule\noalign{}
+a & b & c \\
+\midrule\noalign{}
+\endfirsthead
+\toprule\noalign{}
+a & b & c \\
+\midrule\noalign{}
+\endhead
+\bottomrule\noalign{}
+\endlastfoot
+1 & 2 & 3 \\
+4 & 5 & 6 \\
+\end{longtable}
+
+\begin{longtable}[]{@{}ll@{}}
+\caption{\label{tbl:1}Table}\tabularnewline
+\toprule\noalign{}
+a & b \\
+\midrule\noalign{}
+\endfirsthead
+\toprule\noalign{}
+a & b \\
+\midrule\noalign{}
+\endhead
+\bottomrule\noalign{}
+\endlastfoot
+1 & 2 \\
+\end{longtable}
+
+\listoffigures
+
+\listoftables
+
+\listoflistings
diff --git a/test/m2m/standalone-listOfMetadata/input.md b/test/m2m/standalone-listOfMetadata/input.md
new file mode 100644
--- /dev/null
+++ b/test/m2m/standalone-listOfMetadata/input.md
@@ -0,0 +1,70 @@
+---
+standalone: true
+listOfMetadata: true
+codeBlockCaptions: true
+---
+
+![1](fig1.png){#fig:1}
+
+![2](fig2.png){#fig:2}
+
+![3](fig3.png){#fig:3}
+
+![4](fig4.png){#fig:4}
+
+![5](fig5.png){#fig:5}
+
+![6](fig6.png){#fig:6}
+
+![7](fig7.png){#fig:7}
+
+![8](fig8.png){#fig:8}
+
+![9](fig9.png){#fig:9}
+
+```haskell
+main :: IO ()
+main = putStrLn "Hello World!"
+```
+: Listing caption 1 {#lst:code1}
+
+```haskell
+main :: IO ()
+main = putStrLn "Hello World!"
+```
+
+: Listing caption 2 {#lst:code2}
+
+```{#lst:code3 .haskell}
+main :: IO ()
+main = putStrLn "Hello World!"
+```
+: Listing caption 3
+
+```{#lst:code4 .haskell}
+main :: IO ()
+main = putStrLn "Hello World!"
+```
+
+: Listing caption 4
+
+***
+
+a   b   c
+--- --- ---
+1   2   3
+4   5   6
+
+: My table {#tbl:mytable}
+
+| a | b |
+|---|---|
+| 1 | 2 |
+
+: Table {#tbl:1}
+
+\listoffigures
+
+\listoftables
+
+\listoflistings
diff --git a/test/m2m/subfigures-ccsDelim/expect.tex b/test/m2m/subfigures-ccsDelim/expect.tex
--- a/test/m2m/subfigures-ccsDelim/expect.tex
+++ b/test/m2m/subfigures-ccsDelim/expect.tex
@@ -2,17 +2,17 @@
 
 \begin{pandoccrossrefsubfigures}
 
-\subfloat[1]{\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\label{fig:subfig1}}
-\subfloat[2]{\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\label{fig:subfig2}}
-\subfloat[3]{\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}}
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\caption{1\label{fig:subfig1}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\caption{2\label{fig:subfig2}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
 
-\subfloat[4]{\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\label{fig:subfig4}}
-\subfloat[5]{\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}}
-\subfloat[6]{\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\label{fig:subfig6}}
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\caption{4\label{fig:subfig4}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}\caption{5}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\caption{6\label{fig:subfig6}}\end{subfigure}%
 
-\subfloat[7]{\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\label{fig:subfig7}}
-\subfloat[8]{\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}}
-\subfloat[9]{\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\label{fig:subfig9}}
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\caption{7\label{fig:subfig7}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}\caption{8}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\caption{9\label{fig:subfig9}}\end{subfigure}%
 
 \caption{Caption}\label{fig:subfigures}
 
@@ -20,23 +20,23 @@
 
 \begin{pandoccrossrefsubfigures}
 
-\subfloat[1]{\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\label{fig:subfig21}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\caption{1\label{fig:subfig21}}\end{subfigure}%
 
-\subfloat[2]{\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\label{fig:subfig22}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\caption{2\label{fig:subfig22}}\end{subfigure}%
 
-\subfloat[3]{\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
 
-\subfloat[4]{\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\label{fig:subfig24}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\caption{4\label{fig:subfig24}}\end{subfigure}%
 
-\subfloat[5]{\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}\caption{5}\end{subfigure}%
 
-\subfloat[6]{\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\label{fig:subfig26}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\caption{6\label{fig:subfig26}}\end{subfigure}%
 
-\subfloat[7]{\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\label{fig:subfig27}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\caption{7\label{fig:subfig27}}\end{subfigure}%
 
-\subfloat[8]{\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}\caption{8}\end{subfigure}%
 
-\subfloat[9]{\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\label{fig:subfig29}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\caption{9\label{fig:subfig29}}\end{subfigure}%
 
 \caption{Caption}\label{fig:subfigures2}
 
diff --git a/test/m2m/subfigures-columns/expect.md b/test/m2m/subfigures-columns/expect.md
new file mode 100644
--- /dev/null
+++ b/test/m2m/subfigures-columns/expect.md
@@ -0,0 +1,78 @@
+You can define subfigures:
+
+:::::::::::::::: {#fig:subfigures .figure .subfigures}
+:::::: columns
+::: {.column width="33.0%"}
+![a](fig1.png){#fig:subfig1}
+:::
+
+::: {.column width="33.0%"}
+![b](fig2.png){#fig:subfig2}
+:::
+
+::: {.column width="33.0%"}
+![c](fig3.png)
+:::
+::::::
+
+:::::: columns
+::: {.column width="33.0%"}
+![d](fig4.png){#fig:subfig4}
+:::
+
+::: {.column width="33.0%"}
+![e](fig5.png)
+:::
+
+::: {.column width="33.0%"}
+![f](fig6.png){#fig:subfig6}
+:::
+::::::
+
+:::::: columns
+::: {.column width="33.0%"}
+![g](fig7.png){#fig:subfig7}
+:::
+
+::: {.column width="33.0%"}
+![h](fig8.png)
+:::
+
+::: {.column width="33.0%"}
+![i](fig9.png){#fig:subfig9}
+:::
+::::::
+
+::: caption
+Figure 1: Caption. a --- 1, b --- 2, c --- 3, d --- 4, e --- 5, f --- 6,
+g --- 7, h --- 8, i --- 9
+:::
+::::::::::::::::
+
+:::: {#fig:subfigures2 .figure .subfigures}
+![a](fig1.png){#fig:subfig21}
+
+![b](fig2.png){#fig:subfig22}
+
+![c](fig3.png)
+
+![d](fig4.png){#fig:subfig24}
+
+![e](fig5.png)
+
+![f](fig6.png){#fig:subfig26}
+
+![g](fig7.png){#fig:subfig27}
+
+![h](fig8.png)
+
+![i](fig9.png){#fig:subfig29}
+
+::: caption
+Figure 2: Caption. a --- 1, b --- 2, c --- 3, d --- 4, e --- 5, f --- 6,
+g --- 7, h --- 8, i --- 9
+:::
+::::
+
+Figures themselves can be referenced fig. 2, as well as individual
+subfigures: figs. 1 (a), 1 (b), 2 (i)
diff --git a/test/m2m/subfigures-columns/expect.tex b/test/m2m/subfigures-columns/expect.tex
new file mode 100644
--- /dev/null
+++ b/test/m2m/subfigures-columns/expect.tex
@@ -0,0 +1,47 @@
+You can define subfigures:
+
+\begin{pandoccrossrefsubfigures}
+
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\caption{1\label{fig:subfig1}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\caption{2\label{fig:subfig2}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
+
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\caption{4\label{fig:subfig4}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}\caption{5}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\caption{6\label{fig:subfig6}}\end{subfigure}%
+
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\caption{7\label{fig:subfig7}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}\caption{8}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\caption{9\label{fig:subfig9}}\end{subfigure}%
+
+\caption{Caption}\label{fig:subfigures}
+
+\end{pandoccrossrefsubfigures}
+
+\begin{pandoccrossrefsubfigures}
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\caption{1\label{fig:subfig21}}\end{subfigure}%
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\caption{2\label{fig:subfig22}}\end{subfigure}%
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\caption{4\label{fig:subfig24}}\end{subfigure}%
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}\caption{5}\end{subfigure}%
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\caption{6\label{fig:subfig26}}\end{subfigure}%
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\caption{7\label{fig:subfig27}}\end{subfigure}%
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}\caption{8}\end{subfigure}%
+
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\caption{9\label{fig:subfig29}}\end{subfigure}%
+
+\caption{Caption}\label{fig:subfigures2}
+
+\end{pandoccrossrefsubfigures}
+
+Figures themselves can be referenced fig.~\ref{fig:subfigures2}, as well
+as individual subfigures:
+figs.~\ref{fig:subfig1}, \ref{fig:subfig2}, \ref{fig:subfig29}
diff --git a/test/m2m/subfigures-columns/input.md b/test/m2m/subfigures-columns/input.md
new file mode 100644
--- /dev/null
+++ b/test/m2m/subfigures-columns/input.md
@@ -0,0 +1,44 @@
+---
+subfigColumns: true
+---
+You can define subfigures:
+
+<div id="fig:subfigures">
+  ![1](fig1.png){#fig:subfig1}
+  ![2](fig2.png){#fig:subfig2}
+  ![3](fig3.png)
+
+  ![4](fig4.png){#fig:subfig4}
+  ![5](fig5.png)
+  ![6](fig6.png){#fig:subfig6}
+
+  ![7](fig7.png){#fig:subfig7}
+  ![8](fig8.png)
+  ![9](fig9.png){#fig:subfig9}
+
+  Caption
+</div>
+
+<div id="fig:subfigures2">
+  ![1](fig1.png){#fig:subfig21}
+
+  ![2](fig2.png){#fig:subfig22}
+
+  ![3](fig3.png)
+
+  ![4](fig4.png){#fig:subfig24}
+
+  ![5](fig5.png)
+
+  ![6](fig6.png){#fig:subfig26}
+
+  ![7](fig7.png){#fig:subfig27}
+
+  ![8](fig8.png)
+
+  ![9](fig9.png){#fig:subfig29}
+
+  Caption
+</div>
+
+Figures themselves can be referenced @fig:subfigures2, as well as individual subfigures: [@fig:subfig1; @fig:subfig2; @fig:subfig29]
diff --git a/test/m2m/subfigures-grid/expect.tex b/test/m2m/subfigures-grid/expect.tex
--- a/test/m2m/subfigures-grid/expect.tex
+++ b/test/m2m/subfigures-grid/expect.tex
@@ -2,17 +2,17 @@
 
 \begin{pandoccrossrefsubfigures}
 
-\subfloat[1]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={1}]{fig1.png}\label{fig:subfig1}}
-\subfloat[2]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={2}]{fig2.png}\label{fig:subfig2}}
-\subfloat[3]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={3}]{fig3.png}}
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\caption{1\label{fig:subfig1}}\end{subfigure}%
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\caption{2\label{fig:subfig2}}\end{subfigure}%
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
 
-\subfloat[4]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={4}]{fig4.png}\label{fig:subfig4}}
-\subfloat[5]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={5}]{fig5.png}}
-\subfloat[6]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={6}]{fig6.png}\label{fig:subfig6}}
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\caption{4\label{fig:subfig4}}\end{subfigure}%
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}\caption{5}\end{subfigure}%
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\caption{6\label{fig:subfig6}}\end{subfigure}%
 
-\subfloat[7]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={7}]{fig7.png}\label{fig:subfig7}}
-\subfloat[8]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={8}]{fig8.png}}
-\subfloat[9]{\includegraphics[width=0.3\linewidth,height=\textheight,keepaspectratio,alt={9}]{fig9.png}\label{fig:subfig9}}
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\caption{7\label{fig:subfig7}}\end{subfigure}%
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}\caption{8}\end{subfigure}%
+\begin{subfigure}{0.3\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\caption{9\label{fig:subfig9}}\end{subfigure}%
 
 \caption{Caption}\label{fig:subfigures}
 
@@ -20,23 +20,23 @@
 
 \begin{pandoccrossrefsubfigures}
 
-\subfloat[1]{\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\label{fig:subfig21}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\caption{1\label{fig:subfig21}}\end{subfigure}%
 
-\subfloat[2]{\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\label{fig:subfig22}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\caption{2\label{fig:subfig22}}\end{subfigure}%
 
-\subfloat[3]{\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
 
-\subfloat[4]{\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\label{fig:subfig24}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\caption{4\label{fig:subfig24}}\end{subfigure}%
 
-\subfloat[5]{\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}\caption{5}\end{subfigure}%
 
-\subfloat[6]{\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\label{fig:subfig26}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\caption{6\label{fig:subfig26}}\end{subfigure}%
 
-\subfloat[7]{\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\label{fig:subfig27}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\caption{7\label{fig:subfig27}}\end{subfigure}%
 
-\subfloat[8]{\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}\caption{8}\end{subfigure}%
 
-\subfloat[9]{\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\label{fig:subfig29}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\caption{9\label{fig:subfig29}}\end{subfigure}%
 
 \caption{Caption}\label{fig:subfigures2}
 
diff --git a/test/m2m/subfigures-one-row/expect.tex b/test/m2m/subfigures-one-row/expect.tex
--- a/test/m2m/subfigures-one-row/expect.tex
+++ b/test/m2m/subfigures-one-row/expect.tex
@@ -5,10 +5,10 @@
 
 \begin{pandoccrossrefsubfigures}
 
-\subfloat[cool
-caption]{\includegraphics[width=0.4\linewidth,height=\textheight,keepaspectratio,alt={cool caption}]{/tmp/LaTeX_logo.svg.png}\label{fig:logo1}}
-\subfloat[cooler
-caption]{\includegraphics[width=0.4\linewidth,height=\textheight,keepaspectratio,alt={cooler caption}]{/tmp/LaTeX_logo.svg.png}\label{fig:logo2}}
+\begin{subfigure}{0.4\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={cool caption}]{/tmp/LaTeX_logo.svg.png}}\caption{cool
+caption\label{fig:logo1}}\end{subfigure}%
+\begin{subfigure}{0.4\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={cooler caption}]{/tmp/LaTeX_logo.svg.png}}\caption{cooler
+caption\label{fig:logo2}}\end{subfigure}%
 
 \caption{Copies of the LaTeX logo}\label{fig:latex}
 
diff --git a/test/m2m/subfigures/expect.tex b/test/m2m/subfigures/expect.tex
--- a/test/m2m/subfigures/expect.tex
+++ b/test/m2m/subfigures/expect.tex
@@ -2,17 +2,17 @@
 
 \begin{pandoccrossrefsubfigures}
 
-\subfloat[1]{\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\label{fig:subfig1}}
-\subfloat[2]{\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\label{fig:subfig2}}
-\subfloat[3]{\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}}
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\caption{1\label{fig:subfig1}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\caption{2\label{fig:subfig2}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
 
-\subfloat[4]{\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\label{fig:subfig4}}
-\subfloat[5]{\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}}
-\subfloat[6]{\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\label{fig:subfig6}}
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\caption{4\label{fig:subfig4}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}\caption{5}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\caption{6\label{fig:subfig6}}\end{subfigure}%
 
-\subfloat[7]{\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\label{fig:subfig7}}
-\subfloat[8]{\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}}
-\subfloat[9]{\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\label{fig:subfig9}}
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\caption{7\label{fig:subfig7}}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}\caption{8}\end{subfigure}%
+\begin{subfigure}{0.33\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\caption{9\label{fig:subfig9}}\end{subfigure}%
 
 \caption{Caption}\label{fig:subfigures}
 
@@ -20,23 +20,23 @@
 
 \begin{pandoccrossrefsubfigures}
 
-\subfloat[1]{\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\label{fig:subfig21}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={1}]{fig1.png}}\caption{1\label{fig:subfig21}}\end{subfigure}%
 
-\subfloat[2]{\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\label{fig:subfig22}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={2}]{fig2.png}}\caption{2\label{fig:subfig22}}\end{subfigure}%
 
-\subfloat[3]{\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={3}]{fig3.png}}\caption{3}\end{subfigure}%
 
-\subfloat[4]{\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\label{fig:subfig24}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={4}]{fig4.png}}\caption{4\label{fig:subfig24}}\end{subfigure}%
 
-\subfloat[5]{\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={5}]{fig5.png}}\caption{5}\end{subfigure}%
 
-\subfloat[6]{\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\label{fig:subfig26}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={6}]{fig6.png}}\caption{6\label{fig:subfig26}}\end{subfigure}%
 
-\subfloat[7]{\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\label{fig:subfig27}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={7}]{fig7.png}}\caption{7\label{fig:subfig27}}\end{subfigure}%
 
-\subfloat[8]{\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={8}]{fig8.png}}\caption{8}\end{subfigure}%
 
-\subfloat[9]{\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\label{fig:subfig29}}
+\begin{subfigure}{1.0\linewidth}\pandocbounded{\includegraphics[keepaspectratio,alt={9}]{fig9.png}}\caption{9\label{fig:subfig29}}\end{subfigure}%
 
 \caption{Caption}\label{fig:subfigures2}
 
diff --git a/test/test-integrative.hs b/test/test-integrative.hs
--- a/test/test-integrative.hs
+++ b/test/test-integrative.hs
@@ -26,6 +26,7 @@
 import System.FilePath
 import System.Directory
 import Control.Monad
+import Data.Maybe
 import Text.Pandoc.Highlighting
 import qualified Data.Text as T
 
@@ -41,17 +42,26 @@
     input <- runIO $ readFile ("test" </> "m2m" </> dir </> "input.md")
     expect_md <- runIO $ readFile ("test" </> "m2m" </> dir </> "expect.md")
     let ro = def { readerExtensions = pandocExtensions }
-        wo = def { writerExtensions = disableExtension Ext_raw_html $ disableExtension Ext_raw_attribute pandocExtensions
-                 , writerHighlightStyle=Just pygments
-                 , writerListings = dir `elem` listingsDirs }
     p@(Pandoc meta _) <- runIO $ either (error . show) id <$> P.runIO (readMarkdown ro $ T.pack input)
+    let standalone = isJust $ lookupMeta "standalone" meta
+    template <- runIO $ either (error . show) id <$> P.runIO (compileDefaultTemplate "markdown")
+    let wo = def { writerExtensions
+                    = disableExtension Ext_raw_html
+                    $ disableExtension Ext_raw_attribute
+                    $ pandocExtensions
+                , writerHighlightMethod = if dir `elem` listingsDirs
+                      then IdiomaticHighlighting
+                      else Skylighting pygments
+                , writerTemplate = if standalone then Just template else Nothing
+                }
+        woTex = wo { writerTemplate = Nothing }
     let actual_md = either (fail . show) T.unpack $ runPure $ writeMarkdown wo $ runCrossRef meta (Just $ Format "markdown") defaultCrossRefAction p
     it "Markdown" $ do
       zipWithM_ shouldBe (lines' actual_md) (lines' expect_md)
       length' (lines' actual_md) `shouldBe` length' (lines' expect_md)
 #ifdef FLAKY
     expect_tex <- runIO $ readFile ("test" </> "m2m" </> dir </> "expect.tex")
-    let actual_tex = either (fail . show) T.unpack $ runPure $ writeLaTeX wo $ runCrossRef meta (Just $ Format "latex") defaultCrossRefAction p
+    let actual_tex = either (fail . show) T.unpack $ runPure $ writeLaTeX woTex $ runCrossRef meta (Just $ Format "latex") defaultCrossRefAction p
     it "LaTeX" $ do
       zipWithM_ shouldBe (lines' actual_tex) (lines' expect_tex)
       length' (lines' actual_tex) `shouldBe` length' (lines' expect_tex)
diff --git a/test/test-pandoc-crossref.hs b/test/test-pandoc-crossref.hs
--- a/test/test-pandoc-crossref.hs
+++ b/test/test-pandoc-crossref.hs
@@ -19,17 +19,17 @@
 -}
 
 {-# LANGUAGE CPP, OverloadedLists #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
 import Test.Hspec
 import Text.Pandoc hiding (getDataFileName)
 import Text.Pandoc.Builder hiding (figure)
 import qualified Text.Pandoc.Builder as B
-import Control.Monad.Reader
-import Control.Monad.State
 import Data.List
 import Control.Arrow
 import qualified Data.Map as M
 import qualified Data.Text as T
 import Data.Maybe
+import Lens.Micro.Mtl
 
 import Text.Pandoc.CrossRef
 import Text.Pandoc.CrossRef.Util.Options
@@ -42,6 +42,7 @@
 import qualified Text.Pandoc.CrossRef.References.List as References.List
 import qualified Text.Pandoc.CrossRef.Util.Template as Util.Template
 import qualified Text.Pandoc.CrossRef.Util.CodeBlockCaptions as Util.CodeBlockCaptions
+import Data.String (IsString(..))
 
 import qualified Native
 import Paths_pandoc_crossref
@@ -107,13 +108,13 @@
         (
           figureWith ("fig:subfigure",["subfigures"],[])
             (caption Nothing $ para (text "Figure 1: figure caption. a — Test figure 1, b — Test figure 2"))
-            ( para (figure' "test1.jpg" "" "a" "figure1")
-            <> para (figure' "test2.jpg" "" "b" "figure2")
+            (  figure "test1.jpg" "" "a" Nothing "figure1"
+            <> figure "test2.jpg" "" "b" Nothing "figure2"
             ) <>
           figureWith ("fig:subfigure2",["subfigures"],[])
             (caption Nothing $ para (text "Figure 2: figure caption 2. a — Test figure 21, b — Test figure 22"))
-            (  para (figure' "test21.jpg" "" "a" "figure21")
-            <> para (figure' "test22.jpg" "" "b" "figure22")
+            (  figure "test21.jpg" "" "a" Nothing "figure21"
+            <> figure "test22.jpg" "" "b" Nothing "figure22"
             )
         , PfxImg =: M.fromList [("fig:figure1",RefRec {
                                             refIndex = [(1,Nothing)],
@@ -362,24 +363,30 @@
             <> para (citeGen "lst:some_codeblock" [1])
             `test1` "\\begin{codelisting}\n\n\\caption{A code block}\\label{lst:some_codeblock1}\n\n\\begin{Shaded}\n\\begin{Highlighting}[]\n\\OtherTok{main ::} \\DataTypeTok{IO}\\NormalTok{ ()}\n\\end{Highlighting}\n\\end{Shaded}\n\n\\end{codelisting}\n\nlst.~\\ref{lst:some_codeblock1}"
 
+instance IsString RefIndex where
+  fromString (T.pack -> s) =
+    case find @[] (\pfx -> s `hasPfx` pfx) [minBound..] of
+      Just pfx -> IdxRef pfx s
+      Nothing -> error $ "Invalid reference: " <> T.unpack s
+
 citeGen :: T.Text -> [Int] -> Inlines
 citeGen p l = cite (mconcat $ map (cit . (p<>) . T.pack . show) l) $ text $
   "[" <> T.intercalate "; " (map (("@"<>) . (p<>) . T.pack . show) l) <> "]"
 
-refGen :: T.Text -> [Int] -> [Int] -> M.Map T.Text RefRec
+refGen :: T.Text -> [Int] -> [Int] -> M.Map RefIndex RefRec
 refGen p l1 l2 = M.fromList $ mconcat $ zipWith refRec'' (((uncapitalizeFirst p<>) . T.pack . show) `map` l1) l2
 
-refGen' :: T.Text -> [Int] -> [(Int, Int)] -> M.Map T.Text RefRec
+refGen' :: T.Text -> [Int] -> [(Int, Int)] -> M.Map RefIndex RefRec
 refGen' p l1 l2 = M.fromList $ mconcat $ zipWith refRec''' (((uncapitalizeFirst p<>) . T.pack . show) `map` l1) l2
 
-refRec' :: T.Text -> Int -> T.Text -> [(T.Text, RefRec)]
-refRec' ref i tit = [(ref, RefRec{refLabel=Just ref,refIndex=[(i,Nothing)],refGlobal=0,refHideFromList=False,refTitle=toList $ text tit,refSubfigure=Nothing})]
+refRec' :: T.Text -> Int -> T.Text -> [(RefIndex, RefRec)]
+refRec' ref i tit = [(fromString $ T.unpack ref, RefRec{refLabel=Just ref,refIndex=[(i,Nothing)],refGlobal=0,refHideFromList=False,refTitle=toList $ text tit,refSubfigure=Nothing})]
 
-refRec'' :: T.Text -> Int -> [(T.Text, RefRec)]
+refRec'' :: T.Text -> Int -> [(RefIndex, RefRec)]
 refRec'' ref i = refRec' ref i ""
 
-refRec''' :: T.Text -> (Int, Int) -> [(T.Text, RefRec)]
-refRec''' ref (c,i) = [(ref, RefRec{refLabel=Just ref,refIndex=[(c,Nothing), (i,Nothing)],refGlobal=0,refHideFromList=False,refTitle=toList $ text "",refSubfigure=Nothing})]
+refRec''' :: T.Text -> (Int, Int) -> [(RefIndex, RefRec)]
+refRec''' ref (c,i) = [(fromString $ T.unpack ref, RefRec{refLabel=Just ref,refIndex=[(c,Nothing), (i,Nothing)],refGlobal=0,refHideFromList=False,refTitle=toList $ text "",refSubfigure=Nothing})]
 
 testRefs' :: T.Text -> [Int] -> [Int] -> Prefix -> T.Text -> Expectation
 testRefs' p l1 l2 prop res = testRefs (para $ citeGen p l1) (set (refsAt prop) (refGen p l1 l2) def) (para $ text res)
@@ -394,25 +401,40 @@
 testState f init' arg res = runWSWithOptsInit defaultOptions init' (f $ toList arg) `shouldBe` first toList res
 
 runWSWithOptsInit :: Options -> References -> WS a -> (a, References)
-runWSWithOptsInit opts st = flip runState st . flip runReaderT opts . runWS
-
-runWSWithOpts :: Options -> WS a -> (a, References)
-runWSWithOpts opts = runWSWithOptsInit opts def
+runWSWithOptsInit opts st act = runWS (WState opts st) act & _2 %~ view wsReferences
 
 testRefs :: Blocks -> References -> Blocks -> Expectation
-testRefs bs st rbs = testState (bottomUpM References.Refs.replaceRefs) st bs (rbs, st)
+testRefs bs st rbs = testState (bottomUpM replaceRefs') st bs (rbs, st)
 
+replaceRefs' :: [Inline] -> WS [Inline]
+replaceRefs' (x:xs) = do
+  x' <- replaceRefOne x
+  pure $ toList $ x' <> fromList xs
+replaceRefs' [] = pure []
+
+replaceRefOne :: Inline -> WS Inlines
+replaceRefOne = \case
+  Cite cits _ -> References.Refs.replaceRefs cits <$> use wsOptions <*> use wsReferences
+  x -> pure $ fromList [x]
+
 testCBCaptions :: Blocks -> Blocks -> Expectation
-testCBCaptions bs res = runWSWithOpts defaultOptions{Text.Pandoc.CrossRef.Util.Options.codeBlockCaptions=True}
-  (bottomUpM (Util.CodeBlockCaptions.mkCodeBlockCaptions) (toList bs)) `shouldBe` (toList res,def)
+testCBCaptions bs res =
+  let opts = defaultOptions{Text.Pandoc.CrossRef.Util.Options.codeBlockCaptions=True}
+  in (bottomUp (\bs' -> fromMaybe bs' $ Util.CodeBlockCaptions.mkCodeBlockCaptions opts bs') (toList bs)) `shouldBe` toList res
 
 testList :: Blocks -> References -> Blocks -> Expectation
-testList bs st res = runWSWithOptsInit defaultOptions st (bottomUpM References.List.listOf (toList bs))
+testList bs st res = runWSWithOptsInit defaultOptions st (bottomUpM listOf' (toList bs))
    `shouldBe` (toList res,st)
 
+listOf' :: [Block] -> WS [Block]
+listOf' (x:xs) = References.List.listOf x <$> use wsOptions <*> use wsReferences <&> \case
+  Just res' -> res' <> xs
+  Nothing -> x : xs
+listOf' [] = pure []
+
 figure :: T.Text -> T.Text -> T.Text -> Maybe T.Text -> T.Text -> Blocks
 figure src title cap malt ref = B.figureWith ("fig:" <> ref, [], [])
-  (caption Nothing $ para $ text cap) $ plain $ figure' src title (fromMaybe cap malt) ""
+  (caption Nothing $ plain $ text cap) $ plain $ figure' src title (fromMaybe cap malt) ""
 
 figure' :: T.Text -> T.Text -> T.Text -> T.Text -> Inlines
 figure' src title alt ref = imageWith (if T.null ref then mempty else "fig:" <> ref, [], []) src title (text alt)
@@ -475,7 +497,7 @@
 cit r = [defCit{citationId=r}]
 
 infixr 0 =:
-(=:) :: Prefix -> M.Map T.Text RefRec -> References
+(=:) :: Prefix -> M.Map RefIndex RefRec -> References
 a =: b = def
   & ctrsAt a .~ (refIndex $ last $ M.elems b)
   & refsAt a .~ b
