diff --git a/Graphics/Layout.hs b/Graphics/Layout.hs
--- a/Graphics/Layout.hs
+++ b/Graphics/Layout.hs
@@ -23,6 +23,7 @@
 import Graphics.Layout.Flow as F
 import Graphics.Layout.Inline as I
 import Graphics.Layout.CSS.Font (Font'(..))
+import Graphics.Layout.Flex as Fl
 
 import Data.Maybe (fromMaybe)
 
@@ -57,6 +58,7 @@
     | LayoutConst x (PaddedBox m n) [LayoutItem m n x]
     -- | Children of a `LayoutInline` or `LayoutInline'`.
     | LayoutSpan (FragmentTree (UserData m n x))
+    | LayoutFlex x (Flex (LayoutItem m n x) m)
     deriving (Show, Eq)
 -- | An empty box.
 nullLayout :: (PropertyParser x, Zero m, Zero n) => LayoutItem m n x
@@ -86,13 +88,19 @@
 }
 layoutGetBox (LayoutSpan self) = treeBox self
 layoutGetBox (LayoutConst _ ret _) = ret
+layoutGetBox (LayoutFlex _ self) = flexGetBox layoutGetBox' self
+layoutGetBox' :: (Zero m, Zero n, CastDouble m, CastDouble n) =>
+        LayoutItem m n x -> PaddedBox Double Double
+layoutGetBox' = mapX' toDouble . mapY' toDouble . layoutGetBox
 -- | Retrieve the subtree under a node.
 layoutGetChilds (LayoutFlow _ _ ret) = ret
 layoutGetChilds (LayoutGrid _ _ _ ret) = ret
-layoutGetChilds (LayoutSpan _) = []
+layoutGetChilds (LayoutSpan (Leaf _)) = []
+layoutGetChilds (LayoutSpan (Branch _ ret)) = map LayoutSpan ret
 layoutGetChilds (LayoutInline _ self _) = map LayoutSpan $ inlineChildren self
 layoutGetChilds (LayoutInline' _ self _) = map LayoutSpan $ layoutChildren self
 layoutGetChilds (LayoutConst _ _ childs) = childs
+layoutGetChilds (LayoutFlex _ x) = map Fl.flexInner $ concat $ Fl.children x
 -- | Retrieve the caller-specified data attached to a layout node.
 layoutGetInner (LayoutFlow ret _ _) = ret
 layoutGetInner (LayoutGrid ret _ _ _) = ret
@@ -100,6 +108,7 @@
 layoutGetInner (LayoutInline' ret _ _) = ret
 layoutGetInner (LayoutConst ret _ _) = ret
 layoutGetInner (LayoutSpan x) = treeInner x
+layoutGetInner (LayoutFlex ret _ ) = ret
 
 -- | Retrieve the font associated with inline layout.
 fragmentFont x = let (ret, _, _) = treeInner' x in ret
@@ -134,6 +143,7 @@
 boxMinWidth _ (LayoutConst val self' childs) =
     LayoutConst val self' $ map (boxMinWidth Nothing) childs
 boxMinWidth _ self@(LayoutSpan _) = self
+boxMinWidth size self@(LayoutFlex a b) = LayoutFlex a $ flexMap (boxMinWidth size) b
 -- | Update a (sub)tree to compute & cache ideal width.
 boxNatWidth :: (Zero y, CastDouble y, NFData y) =>
         Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x
@@ -161,6 +171,7 @@
 boxNatWidth _ (LayoutConst val self' childs) =
     LayoutConst val self' $ map (boxNatWidth Nothing) childs
 boxNatWidth _ self@(LayoutSpan _) = self
+boxNatWidth size (LayoutFlex a b ) = LayoutFlex a $ flexMap (boxNatWidth size) b
 -- | Update a (sub)tree to compute & cache maximum legible width.
 boxMaxWidth :: (CastDouble y, Zero y, NFData y) =>
         PaddedBox a Double -> LayoutItem y Length x -> LayoutItem y Length x
@@ -181,6 +192,9 @@
 boxMaxWidth _ (LayoutConst val self' childs) = LayoutConst val self' $
     map (boxMaxWidth $ mapY' toDouble $ mapX' toDouble self') childs
 boxMaxWidth parent self@(LayoutSpan _) = self
+boxMaxWidth parent (LayoutFlex a b) = LayoutFlex a $ (case Fl.direction b of
+    Fl.Row -> flip flexWrap (inline $ B.size parent)
+    Fl.Column -> id) $ flexMap (boxMaxWidth parent) b
 -- | Update a (sub)tree to compute & cache final width.
 boxWidth :: (Zero y, CastDouble y, NFData y) =>
         PaddedBox b Double -> LayoutItem y Length x -> LayoutItem y Double x
@@ -216,6 +230,7 @@
   where cb = lowerLength $ width p
 boxWidth parent (LayoutSpan self') =
     LayoutSpan $ treeMap (mapX' $ lowerLength $ width parent) self'
+boxWidth parent (LayoutFlex a b) = LayoutFlex a $ flexMap (boxWidth parent) b
 
 -- | Update a (sub)tree to compute & cache ideal legible height.
 boxNatHeight :: Double -> LayoutItem Length Double x -> LayoutItem Length Double x
@@ -237,6 +252,7 @@
 boxNatHeight p (LayoutConst val self' childs) = LayoutConst val self' $
     map (boxNatHeight $ width $ mapY' (lowerLength p) self') childs
 boxNatHeight parent self@(LayoutSpan _) = self
+boxNatHeight parent (LayoutFlex a b) = LayoutFlex a $ flexMap (boxNatHeight parent) b
 -- | Update a (sub)tree to compute & cache minimum legible height.
 boxMinHeight :: Double -> LayoutItem Length Double x -> LayoutItem Length Double x
 boxMinHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'
@@ -259,6 +275,7 @@
 boxMinHeight p (LayoutConst val self' childs) = LayoutConst val self' $
     map (boxMinHeight $ width $ mapY' (lowerLength p) self') childs
 boxMinHeight parent self@(LayoutSpan _) = self
+boxMinHeight parent self@(LayoutFlex a b) = LayoutFlex a $ flexMap (boxMinHeight parent) b
 -- | Update a subtree to compute & cache maximum legible height.
 boxMaxHeight :: PaddedBox Double Double -> LayoutItem Length Double x ->
         LayoutItem Length Double x
@@ -282,6 +299,7 @@
 boxMaxHeight p (LayoutConst val self' childs) = LayoutConst val self' $
     map (boxMaxHeight $ mapY' (lowerLength $ width p) self') childs
 boxMaxHeight parent (LayoutSpan self') = LayoutSpan self'
+boxMaxHeight parent (LayoutFlex a b) = LayoutFlex a $ flexMap (boxMaxHeight parent) b
 -- | Update a (sub)tree to compute & cache final height.
 boxHeight :: PaddedBox Double Double -> LayoutItem Length Double x -> LayoutItem Double Double x
 boxHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'
@@ -317,6 +335,8 @@
     in LayoutConst val self' $ map (boxHeight self') childs
 boxHeight p (LayoutSpan self') =
     LayoutSpan $ treeMap (mapY' $ lowerLength $ width p) self'
+boxHeight p (LayoutFlex a b) = LayoutFlex a $
+    flexResolve (innerMain . layoutGetBox) (width p) $ flexMap (boxHeight p) b
 
 -- | Split a (sub)tree to fit within max-height.
 -- May take full page height into account.
@@ -357,6 +377,12 @@
   where
     wrap self' = LayoutInline' a self' paging
 boxSplit _ _ self@(LayoutSpan _) = (self, Nothing) -- Can't split!
+boxSplit maxheight pageheight (LayoutFlex a self) =
+    -- FIXME: What if any children are too big for the page?
+    let (p0, p1) = flexSplit ( B.size . layoutGetBox ) maxheight pageheight self
+    in if null $ Fl.children p1
+    then (LayoutFlex a p0, Nothing)
+    else (LayoutFlex a p0, Just $ LayoutFlex a p1)
 -- | Generate a list of pages from a node, splitting subtrees where necessary.
 boxPaginate pageheight node
     | (page, Just overflow) <- boxSplit pageheight pageheight node =
@@ -366,6 +392,19 @@
 -- | Compute position of all nodes in the (sub)tree relative to a base coordinate.
 boxPosition :: (PropertyParser x, Eq x) => (Double, Double) ->
     LayoutItem Double Double x -> LayoutItem Double Double ((Double, Double), x)
+boxPosition (x,y) (LayoutFlow val box [LayoutFlex val' self]) =
+    LayoutFlow ((x,y), val) box [ -- Obtaining size from parent
+        LayoutFlex (pos', val') $
+            flexPosition boxPosition boxSize pos' (B.size box) self
+    ]
+  where
+    boxSize box' = let b = layoutGetBox box' in B.width b `Size` B.height b
+    pos' = (x + B.leftSpace box, y + B.rightSpace box)
+boxPosition pos self@(LayoutFlex val self') =
+    LayoutFlex (pos, val) $ flexPosition boxPosition boxSize pos size self'
+  where
+    boxSize box' = let b = layoutGetBox box' in B.width b `Size` B.height b
+    size = B.size $ layoutGetBox self
 boxPosition pos@(x, y) (LayoutFlow val self childs) = LayoutFlow (pos, val) self childs'
   where
     childs' = parMap' recurse $ zip pos' childs
@@ -406,7 +445,7 @@
 -- Useful for assembling glyph atlases.
 glyphsPerFont :: (CastDouble x, CastDouble y, Eq x, Eq y, Eq z) =>
         LayoutItem x y z -> M.Map (Pattern, Double) IS.IntSet
-glyphsPerFont (LayoutSpan self) | (_:_) <- glyphs =
+glyphsPerFont (LayoutSpan self@(Leaf _)) | (_:_) <- glyphs =
         (pattern font, fontSize font) `M.singleton` IS.fromList glyphs
     | otherwise = M.empty
   where
diff --git a/Graphics/Layout/Box.hs b/Graphics/Layout/Box.hs
--- a/Graphics/Layout/Box.hs
+++ b/Graphics/Layout/Box.hs
@@ -181,10 +181,17 @@
     fromDouble :: Double -> a
     -- | Convert a double or length to a double.
     toDouble :: a -> Double
+    toDouble = toDoubleWithin 0
+    toDoubleWithin :: Double -> a -> Double
+    toDoubleWithin _ = toDouble
+    toDoubleWithinAuto :: Double -> Double -> a -> Double
+    toDoubleWithinAuto _ = toDoubleWithin
 
 instance CastDouble Double where
     fromDouble = id
     toDouble = id
 instance CastDouble Length where
     fromDouble = Pixels
-    toDouble = lowerLength 0
+    toDoubleWithin = lowerLength
+    toDoubleWithinAuto x _ Auto = x
+    toDoubleWithinAuto _ x y = toDoubleWithin x y
diff --git a/Graphics/Layout/CSS.hs b/Graphics/Layout/CSS.hs
--- a/Graphics/Layout/CSS.hs
+++ b/Graphics/Layout/CSS.hs
@@ -20,6 +20,7 @@
 import Graphics.Layout.Grid
 import Graphics.Layout.Grid.Table
 import Graphics.Layout.Inline.CSS
+import Graphics.Layout.Flex.CSS
 
 import Data.Char (isSpace)
 import Graphics.Layout.CSS.Parse
@@ -55,7 +56,7 @@
         (finalizeBox (collapseTBorders' self') font_)
         ([finalizeCSS root font_ child { style = child' { display = Block } }
             | child@StyleTree { style = child'@CSSBox { display = TableCaption } } <- childs] ++
-        [finalizeTable root font_ (inner self') opts childs])
+        [finalizeTable root font_ temp opts childs])
   where
     font_ = pattern2font (font self') (font' self') parent root
 finalizeCSS root parent self@StyleTree {
@@ -69,6 +70,16 @@
             | child@StyleTree { style = child'@CSSBox { display = TableCaption } } <- childs])
   where
     font_ = pattern2font (font self') (font' self') parent root
+finalizeCSS root parent self@StyleTree {
+        style = self'@CSSBox { display = Flex, flexOptions = flex },
+        children = childs
+    } = LayoutFlow (inner' font_ self' ) (finalizeBox self' font_) [
+        LayoutFlex temp $ lowerFlex flex font_ (map flexOptions childs')
+            (flip map childs $ finalizeCSS root font_) (map style2font childs')]
+  where
+    font_ = style2font self'
+    style2font style = pattern2font (font style) (font' style) parent root
+    childs' = map style childs
 finalizeCSS root parent self@StyleTree {
     style = self', children = childs
   } = LayoutFlow (inner' font_ self') (finalizeBox self' font_)
diff --git a/Graphics/Layout/CSS/Parse.hs b/Graphics/Layout/CSS/Parse.hs
--- a/Graphics/Layout/CSS/Parse.hs
+++ b/Graphics/Layout/CSS/Parse.hs
@@ -16,6 +16,7 @@
 import Graphics.Layout.Grid.CSS (CSSGrid(..), CSSCell(..), Placement(..))
 import Graphics.Layout.Grid.Table (TableOptions)
 import Graphics.Layout.Inline.CSS (CSSInline(..))
+import Graphics.Layout.Flex.CSS (CSSFlex(..))
 
 import Data.Maybe (isJust, fromMaybe)
 import Text.Read (readMaybe)
@@ -54,8 +55,12 @@
     paragraphOptions :: ParagraphOptions,
     -- | (Semi-)parsed CSS properties & HTML attributes relating to laying out
     -- HTML table elements.
-    tableOptions :: TableOptions
+    tableOptions :: TableOptions,
+    -- | Semi-parsed CSS properties relating to FlexBox layouts.
+    flexOptions :: CSSFlex
 }
+-- | FlexOptions getter with `textLTR` set
+flexOpts' self@CSSBox { flexOptions = ret } = ret { textRTL = direction self == DirRTL }
 -- | Accessor for inlineStyle's `textDirection` attribute.
 direction CSSBox { inlineStyles = CSSInline _ opts _ } = textDirection opts
 -- | Accessor for inlineStyle's options.
@@ -68,7 +73,7 @@
 -- | Possibly values for CSS display property.
 data Display = Block | Grid | Inline | Table | None |
     TableRow | TableHeaderGroup | TableRowGroup | TableFooterGroup | TableCell |
-    TableColumn | TableColumnGroup | TableCaption deriving Eq
+    TableColumn | TableColumnGroup | TableCaption | Flex deriving Eq
 -- | Can the display value contain table-rows?
 rowContainer CSSBox { display = d } =
     d `elem` [Table, TableHeaderGroup, TableRowGroup, TableFooterGroup]
@@ -99,7 +104,8 @@
         paragraphOptions = defaultParagraphOptions {
             paragraphAlignment = AlignStart
         },
-        tableOptions = temp
+        tableOptions = temp,
+        flexOptions = temp
       }
     inherit parent = CSSBox {
         boxSizing = boxSizing parent,
@@ -115,9 +121,11 @@
         inlineStyles = inherit $ inlineStyles parent,
         pageOptions = pageOptions parent,
         paragraphOptions = paragraphOptions parent,
-        tableOptions = inherit $ tableOptions parent
+        tableOptions = inherit $ tableOptions parent,
+        flexOptions = inherit $ flexOptions parent
       }
-    priority self = concat [x font, x font', x gridStyles, x cellStyles, x inner]
+    priority self = concat [x inlineStyles, x font, x font', x gridStyles,
+        x cellStyles, x flexOptions, x inner]
       where x getter = priority $ getter self
 
     -- Wasn't sure how to implement in FontConfig-Pure
@@ -254,6 +262,7 @@
     longhand CSSBox { display = Table } self "display" [Ident "table-caption"] =
         Just self { display=TableCaption }
     longhand _ self "display" [Ident "inline"] = Just self { display = Inline }
+    longhand _ self "display" [Ident "flex"] = Just self { display = Flex }
     longhand _ self "display" [Ident "initial"] = Just self { display = Inline }
 
     longhand _ self "orphans" [Number _ (NVInteger x)] =
@@ -294,12 +303,20 @@
       }
     longhand a b c d | Just inline' <- longhand (inlineStyles a) (inlineStyles b) c d =
         Just b { inlineStyles = inline' }
+    longhand a b c d | Just grid' <- longhand (gridStyles a) (gridStyles b) c d,
+            Just flex' <- longhand (flexOpts' a) (flexOpts' b) c d =
+        Just b { gridStyles = grid', flexOptions = flex' }
+    longhand a b c d | Just cell' <- longhand (cellStyles a) (cellStyles b) c d,
+            Just flex' <- longhand (flexOpts' a) (flexOpts' b) c d =
+        Just b { cellStyles = cell', flexOptions = flex' }
     longhand a b c d | Just grid' <- longhand (gridStyles a) (gridStyles b) c d =
         Just b { gridStyles = grid' }
     longhand a b c d | Just cell' <- longhand (cellStyles a) (cellStyles b) c d =
         Just b { cellStyles = cell' }
     longhand a b c d | Just table'<-longhand (tableOptions a) (tableOptions b) c d
         = Just b { tableOptions = table' }
+    longhand a b c d | Just flex' <- longhand (flexOpts' a) (flexOpts' b) c d =
+        Just b { flexOptions = flex' }
     longhand a b c d
         | (d', _:_)<-testLengthProp d, Just _<-longhand (inner a) (inner b) c d' =
             Just b {
diff --git a/Graphics/Layout/Flex.hs b/Graphics/Layout/Flex.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/Layout/Flex.hs
@@ -0,0 +1,293 @@
+-- | Layout formula positioning children horizontally or vertically, with or without wrapping.
+module Graphics.Layout.Flex(
+        Flex(..), FlexChild(..), Direction(..), FlexWrapping(..), Justification(..), Alignment(..),
+        flexMap, flexResolve, flexMaxBasis, flexSumBasis, flexWrap, flexRowSize, flexRowsSize,
+        justifyOffset, justifySpacing, flexJustify, alignOffset,
+        outerMinMain, outerMain, outerMaxMain, outerMinCross, outerCross, outerMaxCross,
+        innerMinMain, innerMain, innerMaxMain, innerMinCross, innerCross, innerMaxCross,
+        flexGetBox, flexSplit, flexPosition) where
+
+import Graphics.Layout.Box as B (Length(..), lowerLength, Size(..), PaddedBox(..),
+        maxWidth, width, minWidth, maxHeight, height, minHeight, CastDouble(..), Zero(..))
+import Data.List (intersperse)
+import GHC.Real (infinity)
+import Data.Maybe (fromMaybe)
+
+-- | Parameters to flexbox layout
+data Flex a b = Flex {
+    -- | On which axis to position children
+    direction :: Direction,
+    -- | Whether to reverse each row
+    reverseRows :: Bool,
+    -- | Whether to wrap children to multiple lines
+    wrap :: FlexWrapping,
+    -- | How to justify children within lines
+    justify :: Justification,
+    -- | How to justify lines within the flexbox. Nothing is CSS "stretch".
+    alignLines :: Maybe Justification,
+    -- | Gap between children
+    baseGap :: b,
+    -- | Gap between lines
+    crossGap :: b,
+    -- | The children to layout, 2D list so as to store lines once split.
+    children :: [[FlexChild a b]],
+    -- | How wide to consider the page when paginating.
+    pageWidth :: Double
+} deriving (Eq, Show, Read)
+-- | Properties for positioning a child of a flexbox
+data FlexChild a b = FlexChild {
+    grow :: Double,
+    shrink :: Double,
+    basis :: b,
+    alignment :: Alignment,
+    flexInner :: a
+} deriving (Eq, Show, Read)
+
+-- | Which axis to position children along
+data Direction = Row | Column deriving (Eq, Show, Read)
+-- | Whether to wrap or reverse the wrapped lines
+data FlexWrapping = NoWrap | Wrap | WrapReverse deriving (Eq, Show, Read)
+-- | How to position lines within a flexbox, or children within a line
+data Justification = JStart | JEnd | JCenter | JSpaceBetween | JSpaceAround | JSpaceEvenly
+    deriving (Eq, Show, Read)
+-- | How to align children along the cross-axis (opposite axis from which they're laid out)
+data Alignment = AlStretch | AlStart | AlEnd | AlCenter | AlBaseline
+    deriving (Eq, Show, Read)
+
+-- | Apply an operation to every child
+flexMap :: (a -> b) -> Flex a c -> Flex b c
+flexMap cb self = Flex {
+    direction = direction self, reverseRows = reverseRows self, wrap = wrap self,
+    justify = justify self, alignLines = alignLines self,
+    baseGap = baseGap self, crossGap = crossGap self, pageWidth = pageWidth self,
+    children = [[FlexChild {
+        grow = grow kid, shrink = shrink kid, basis = basis kid,
+        alignment = alignment kid,
+        flexInner = cb $ flexInner kid -- The important line!
+    } | kid <- row] | row <- children self]
+  }
+-- | Resolve lengths in the flexbox to doubles.
+flexResolve :: CastDouble b => (a -> Direction -> Double) -> Double ->
+        Flex a b -> Flex a Double
+flexResolve cb size self = Flex {
+    direction = direction self, reverseRows = reverseRows self, wrap = wrap self,
+    justify = justify self, alignLines = alignLines self,
+    baseGap = toDoubleWithin size $ baseGap self,
+    crossGap = toDoubleWithin size $ crossGap self,
+    pageWidth = pageWidth self,
+    children = [[FlexChild {
+        grow = grow kid, shrink = shrink kid,
+        basis = toDoubleWithinAuto (flexInner kid `cb` direction self) size $ basis kid,
+        alignment = alignment kid, flexInner = flexInner kid
+    } | kid <- row] | row <- children self]
+  }
+
+-- | The minimum size of the flexbox along `direction`, i.e. maximum size of a child.
+flexMaxBasis :: Flex a Double -> Double
+flexMaxBasis self = maximum [basis child | row <- children self, child <- row]
+-- | The maximum width of each row of the flexbox.
+flexSumBasis :: Flex a Double -> Double
+flexSumBasis self = maximum [Prelude.sum $
+        intersperse (baseGap self) $ map basis row | row <- children self]
+
+-- | Wrap all lines to a given size reassigning overflow or underflow space.
+-- NOTE: shrink propery may yield negative sizes. Caller will want to enforce min-sizes.
+flexWrap :: CastDouble b => Flex a b -> Double -> Flex a b
+flexWrap self size
+    | NoWrap <- wrap self = post self
+    | Wrap <- wrap self = post self'
+    | WrapReverse <- wrap self = post self' { children=reverse $ children self' }
+  where
+    self' = self {
+        children = concatMap wrapRow $ children self
+    }
+    wrapRow :: CastDouble b => [FlexChild a b] -> [[FlexChild a b]]
+    wrapRow [] = []
+    wrapRow kids@(kid:_) = let (row, rest) = splitRow' kids $ basis' kid
+        in row:wrapRow rest
+    splitRow, splitRow' :: CastDouble b => [FlexChild a b] -> Double ->
+            ([FlexChild a b], [FlexChild a b])
+    -- This wrapper function ensures we don't end up with empty rows, or infinite loops.
+    splitRow' (kid:kids) end =
+        let (kids', rest) = splitRow kids (end + baseGap' self + basis' kid)
+        in (kid:kids', rest)
+    splitRow' [] _ = ([], [])
+    splitRow (kid:kids) end
+        | end > size = ([], kid:kids)
+        | otherwise = let (kids', rest) = splitRow kids (end + baseGap' self + basis' kid)
+            in (kid:kids', rest)
+    splitRow [] _ = ([], [])
+
+    post :: CastDouble b => Flex a b -> Flex a b
+    post flex
+        | reverseRows self = post' flex { children = map reverse $ children flex }
+        | otherwise = post' flex
+    post' :: CastDouble b => Flex a b -> Flex a b
+    post' flex = flex { children = map resizeRow $ children flex }
+    resizeRow :: CastDouble b => [FlexChild a b] -> [FlexChild a b]
+    resizeRow row
+        | rowSize > size = [kid {
+                basis = fromDouble $ basis' kid - shrink kid * nanguard sfr
+            } | kid <- row]
+        | rowSize < size = [kid {
+                basis = fromDouble $ basis' kid + grow kid * nanguard gfr
+            } | kid <- row]
+        | otherwise = row
+      where
+        rowSize = Prelude.sum $ intersperse (baseGap' self) $ map basis' row
+        sfr = (rowSize - size)/(Prelude.sum $ map shrink row)
+        gfr = (size - rowSize)/(Prelude.sum $ map grow row)
+        nanguard x | isNaN x = 0
+            | isInfinite x = 0
+            | otherwise = x
+    baseGap' :: CastDouble b => Flex a b -> Double
+    baseGap' = toDouble . baseGap
+    basis' :: CastDouble b => FlexChild a b -> Double
+    basis' = toDouble . basis
+
+-- | The cross (opposite from `direction` axis) size of a row.
+flexRowSize :: (a -> Double) -> [FlexChild a b] -> Double
+flexRowSize cb row = maximum $ map (cb . flexInner) row
+-- | The cross (opposite from `direction` axis) size of all rows.
+flexRowsSize :: (a -> Double) -> Flex a Double -> Double
+flexRowsSize cb Flex { crossGap = gap, children = kids } =
+    sum $ intersperse gap $ flexRowSize cb `map` kids
+
+justifyOffset, justifySpacing :: Double -> [Double] -> Double -> Justification -> Double
+-- | How far right to shift some elements to achieve desired justification.
+justifyOffset _ _ _ JStart = 0
+justifyOffset outersize ks g JEnd = outersize - innersize g ks
+justifyOffset outersize ks g JCenter = half $ outersize - innersize g ks
+justifyOffset _ _ _ JSpaceBetween = 0
+justifyOffset outersize ks g JSpaceAround =
+    half $ (outersize - innersize g ks)/length' ks
+justifyOffset _ ks _ _ | length ks <= 1 = 0 -- No gaps to space, avoid numeric errors.
+justifyOffset size ks g JSpaceEvenly = (size - innersize g ks)/(length' ks + 1)
+-- | How much space to add between elements.
+justifySpacing size ks g JSpaceBetween = (size - innersize g ks)/(length' ks - 1)
+justifySpacing size ks g JSpaceAround = (size - innersize g ks)/length' ks
+justifySpacing size ks g JSpaceEvenly = (size - innersize g ks)/(length' ks + 1)
+justifySpacing _ _ _ _ = 0
+
+-- | Position new positions for the given items according to the given justification.
+flexJustify :: (a -> Double) -> Double -> [a] -> Double -> Justification -> [(Double, a)]
+flexJustify cb size kids gap just = inner kids offs
+  where
+    offs = justifyOffset size kids' gap just
+    spacing = justifySpacing size kids' gap just
+    kids' = map cb kids
+    inner (k:ks) start = (start, k):inner ks (start + cb k + gap)
+    inner [] _ = []
+
+-- | How far right to shift some elements to achieve desired alignment.
+alignOffset :: Double -> Double -> Alignment -> Double
+alignOffset _ _ AlStretch = 0 -- Needs special handling elsewhere
+alignOffset _ _ AlStart = 0
+alignOffset outer inner AlEnd = outer - inner
+alignOffset outer inner AlCenter = half $ outer - inner
+alignOffset outer inner AlBaseline = half $ outer - inner -- FIXME: Implement properly!
+
+-- | Sum given sizes with a specified gap between them.
+innersize gap = sum . intersperse gap
+-- | divide by 2, can be syntactically in certain circumstances.
+half = (/2)
+-- | length of an list, as a double.
+length' :: [a] -> Double
+length' = toEnum . length
+
+------
+--- Mapping Box Model axes <-> Flex Box axes
+------
+
+outerMinMain, outerMain, outerMaxMain :: Num m => PaddedBox m m -> Direction -> m
+outerMinMain box Row = minWidth box
+outerMinMain box Column = minHeight box
+outerMain box Row = width box
+outerMain box Column = height box
+outerMaxMain box Row = maxWidth box
+outerMaxMain box Column = maxHeight box
+
+outerMinCross, outerCross, outerMaxCross :: Num m => PaddedBox m m -> Direction -> m
+outerMinCross box Row = minHeight box
+outerMinCross box Column = minWidth box
+outerCross box Row = height box
+outerCross box Column = width box
+outerMaxCross box Row = maxHeight box
+outerMaxCross box Column = maxWidth box
+
+innerMinMain, innerMain, innerMaxMain :: Num m => PaddedBox m m -> Direction -> m
+innerMinMain box = sizeMain $ B.min box
+innerMain box = sizeMain $ B.size box
+innerMaxMain box = sizeMain $ B.max box
+
+innerMinCross, innerCross, innerMaxCross :: Num m => PaddedBox m m -> Direction -> m
+innerMinCross box = sizeCross $ B.min box
+innerCross box = sizeCross $ B.size box
+innerMaxCross box = sizeCross $ B.max box
+
+sizeMain, sizeCross :: Num m => Size m m -> Direction -> m
+sizeMain self Row = inline self
+sizeMain self Column = block self
+sizeCross self Row = block self
+sizeCross self Column = inline self
+
+-- | Compute the size bounds of a flexbox.
+flexGetBox :: (Zero m, CastDouble m, Zero n, CastDouble n) =>
+    (a -> PaddedBox Double Double) -> Flex a m -> PaddedBox m n
+flexGetBox cb self = zero {
+    B.min = flexMaxBasis self' `size` flexRowsSize (cb' innerMinCross) self',
+    B.max = fromRational infinity `size` fromRational infinity,
+    B.nat = flexSumBasis self' `size` flexRowsSize (cb' innerCross) self',
+    B.size = flexSumBasis self' `size` flexRowsSize (cb' innerCross) self'
+  } where
+    size main cross
+        | Row <- direction self = fromDouble main `Size` fromDouble cross
+        | otherwise = fromDouble cross `Size` fromDouble main
+    cb' cb_ = flip cb_ (direction self) . cb
+    self' = flexResolve (innerMain . cb) 0 self
+
+-- | Split a flexbox over multiple pages.
+flexSplit :: (a -> Size Double Double) -> Double -> Double -> Flex a Double ->
+    (Flex a Double, Flex a Double)
+flexSplit cb h _ self@Flex { direction = Row, pageWidth = w } =
+    (self' { children = page0 }, self' { children = page1 })
+  where
+    self' = flexWrap self w
+    (page0, page1) = splitRows (-crossGap self) $ children self
+    splitRows start (row:rows)
+        | start >= h = ([], row:rows)
+        | otherwise =
+            let (rows', rest) = flip splitRows rows $
+                    start + crossGap self + flexRowSize (inline . cb) row
+            in (row:rows', rest)
+    splitRows _ [] = ([], [])
+flexSplit cb h h' self@Flex { direction = Column, pageWidth = w }
+    | measure h = (flexWrap self h, self { children = [] })
+    -- If it fits on neither page... Row-direction is more versatile!
+    | not $ measure h' = flexSplit cb h h' self { direction = Row }
+    | otherwise = (self { children = [] }, flexWrap self h')
+  where
+    measure space = (block . cb) `flexRowsSize` flexWrap self space <= w
+
+-- | Compute final position of a flexbox's children.
+flexPosition :: ((Double, Double) -> a -> b) -> (a -> Size Double Double) ->
+        (Double, Double) -> Size Double Double ->
+        Flex a Double -> Flex b Double
+flexPosition cb cb' (x,y) size self@Flex { direction = dir } = self {
+    children = map rowPosition $ flexJustify rowsize (sizeCross size dir)
+            (children self) (crossGap self) (justify self)
+  } where
+    rowsize = flexRowSize $ flip sizeCross dir . cb'
+    -- TODO: Handle stretch properly
+    rowPosition (rpos, row) =
+        let rsize = flexRowSize (flip sizeCross dir . cb') row
+        in map (alignChild rsize rpos) $ flexJustify basis rsize row
+                (baseGap self) (fromMaybe JSpaceAround $ alignLines self)
+    alignChild rsize rpos (kpos, kid@FlexChild {
+        flexInner = kid', alignment = align'
+      }) = kid {
+        flexInner = flip cb kid' $ sz kpos $
+                rpos + alignOffset rsize (flip sizeCross dir $ cb' kid') align'
+      }
+    sz m c | Row <- direction self = (x + m, y + c)
+        | otherwise = (x + c, y + m)
diff --git a/Graphics/Layout/Flex/CSS.hs b/Graphics/Layout/Flex/CSS.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/Layout/Flex/CSS.hs
@@ -0,0 +1,212 @@
+{-# LANGUAGE OverloadedStrings #-}
+-- | Parse FlexBox-related CSS properties
+module Graphics.Layout.Flex.CSS(CSSFlex(..), lowerFlex) where
+
+import Data.CSS.Syntax.Tokens (Token(..), NumericValue(..))
+import Stylist (PropertyParser(..), TrivialPropertyParser, parseOperands,
+                parseUnorderedShorthand', parseUnorderedShorthand)
+import Graphics.Layout.Flex as F
+import Graphics.Layout.CSS.Length (parseLength, finalizeLength, n2f, Unitted, Font')
+import Graphics.Layout.Box (Length)
+import Data.Maybe (isJust)
+
+-- | Parsed FlexBox-related CSS properties.
+data CSSFlex = CSSFlex {
+    -- | Parsed CSS flex-direction, axis component.
+    directionCSS :: Direction,
+    -- | Parsed CSS flex-direction, reversed flag
+    reverseRowsCSS :: Bool,
+    -- | Parsed CSS flex-wrap
+    wrapCSS :: FlexWrapping,
+    -- | Parsed CSS justify-content
+    justifyCSS :: Maybe Justification,
+    -- | Parsed CSS align-items
+    alignItemsCSS :: Alignment,
+    -- | Parsed CSS align-content, `Nothing` is "stretch"
+    alignLinesCSS :: Maybe Justification,
+    -- | Parsed CSS row-gap
+    rowGapCSS :: Unitted,
+    -- | Parsed CSS column-gap
+    columnGapCSS :: Unitted,
+
+    -- flex children
+    -- | Parsed CSS order
+    orderCSS :: Integer,
+    -- | Parsed CSS flex-grow
+    growCSS :: Double,
+    -- | Parsed CSS flex-shrink
+    shrinkCSS :: Double,
+    -- | Parsed CSS flex-basis
+    basisCSS :: Unitted,
+    -- | Parsed CSS align-self
+    alignSelfCSS :: Alignment,
+    -- | Whether justification or alignment properties should be parsed as right-to-left or left-to-right.
+    textRTL :: Bool -- Extra parameter from caller.
+}
+
+setDir self dir rev = Just self { directionCSS = dir, reverseRowsCSS = rev }
+
+parseJustify self "flex-start" | reverseRowsCSS self = Just JEnd
+    | otherwise = Just JStart
+parseJustify self "flex-end" | reverseRowsCSS self = Just JStart
+    | otherwise = Just JEnd
+parseJustify self "start" | textRTL self = Just JEnd
+    | otherwise = Just JStart
+parseJustify self "end" | textRTL self = Just JStart
+    | otherwise = Just JEnd
+parseJustify _ "left" = Just JStart
+parseJustify _ "right" = Just JEnd
+parseJustify _ "center" = Just JCenter
+parseJustify _ "space-between" = Just JSpaceBetween
+parseJustify _ "space-around" = Just JSpaceAround
+parseJustify _ "space-evenly" = Just JSpaceEvenly
+parseJustify _ _ = Nothing
+
+parseAlign _ "stretch" = Just AlStretch
+parseAlign self "flex-start" | reverseRowsCSS self = Just AlEnd
+    | otherwise = Just AlStart
+parseAlign _ "start" = Just AlStart
+parseAlign self "self-start" | textRTL self = Just AlEnd
+    | otherwise = Just AlStart
+parseAlign self "flex-end" | reverseRowsCSS self = Just AlStart
+    | otherwise = Just AlEnd
+parseAlign _ "end" = Just AlEnd
+parseAlign self "self-end" | textRTL self = Just AlStart
+    | otherwise = Just AlEnd
+parseAlign _ "center" = Just AlCenter
+parseAlign _ "baseline" = Just AlBaseline
+parseAlign _ _ = Nothing
+
+instance PropertyParser CSSFlex where
+    temp = CSSFlex {
+        directionCSS = Row,
+        reverseRowsCSS = False,
+        wrapCSS = NoWrap,
+        justifyCSS = Nothing, -- flex-start, conditional on directionCSS
+        alignItemsCSS = AlStretch,
+        alignLinesCSS = Just JStart,
+        rowGapCSS = (0,"px"),
+        columnGapCSS = (0,"px"),
+        orderCSS = 0,
+        growCSS = 0,
+        shrinkCSS = 1,
+        basisCSS = (0,"auto"),
+        alignSelfCSS = AlStretch, -- Should be auto, but we're implementing that in `inherit`.
+        textRTL = False
+      }
+    inherit parent = temp { alignSelfCSS = alignItemsCSS parent }
+    priority _ = ["flex-direction"]
+
+    longhand _ self "flex-direction" [Ident "row"] = setDir self Row False
+    longhand _ self "flex-direction" [Ident "row-reverse"] = setDir self Row True
+    longhand _ self "flex-direction" [Ident "column"] = setDir self F.Column False
+    longhand _ self "flex-direction" [Ident "column-reverse"] =
+        setDir self F.Column True
+    longhand _ self "flex-direction" [Ident "initial"] = setDir self Row False
+
+    longhand _ self "flex-wrap" [Ident "no-wrap"] = Just self { wrapCSS = NoWrap }
+    longhand _ self "flex-wrap" [Ident "wrap"] = Just self { wrapCSS = Wrap }
+    longhand _ self "flex-wrap" [Ident "wrap-reverse"] =
+        Just self { wrapCSS = WrapReverse }
+    longhand _ self "flex-wrap" [Ident "initial"] = Just self { wrapCSS = NoWrap }
+
+    longhand _ self "justify-content" [Ident x]
+        | x == "initial" = Just self { justifyCSS = parseJustify self "flex-start" }
+        | y@(Just _) <- parseJustify self x = Just self { justifyCSS = y }
+
+    longhand _ self "align-items" [Ident x]
+        | x == "initial" = Just self { alignItemsCSS = AlStretch }
+        | Just y <- parseAlign self x = Just self { alignItemsCSS = y }
+
+    longhand _ self "align-content" [Ident x] | x `elem` ["initial", "normal"] =
+            Just self { alignLinesCSS = parseJustify self "start" }
+        | x == "stretch" = Just self { alignLinesCSS = Nothing }
+        | y@(Just _) <- parseJustify self x = Just self { alignLinesCSS = y }
+
+    longhand _ self "row-gap" [Ident x]
+        | x `elem` ["initial", "normal"] = Just self { rowGapCSS = (0,"px") }
+        | otherwise = Nothing
+    longhand _ self "row-gap" toks
+        | Just x <- parseLength toks = Just self { rowGapCSS = x}
+    longhand _ self "column-gap" [Ident x]
+        | x `elem` ["initial", "normal"] = Just self { columnGapCSS = (0,"px") }
+        | otherwise = Nothing
+    longhand _ self "column-gap" toks
+        | Just x <- parseLength toks = Just self { columnGapCSS = x }
+
+    longhand _ self "order" [Number _ (NVInteger x)] = Just self { orderCSS = x }
+    longhand _ self "order" [Ident "initial"] = Just self { orderCSS = 0 }
+
+    longhand _ self "flex-grow" [Number _ x] | n2f x>0 = Just self {growCSS=n2f x}
+    longhand _ self "flex-grow" [Ident "initial"] = Just self { growCSS = 0 }
+    longhand _ self "flex-shrink" [Number _ x] | n2f x > 0 =
+        Just self { shrinkCSS = n2f x }
+    longhand _ self "flex-shrink" [Ident "initial"] = Just self { shrinkCSS = 1 }
+
+    longhand _ self "flex-basis" toks | Just x <- parseLength toks =
+        Just self { basisCSS = x }
+
+    longhand parent self "align-self" [Ident x] | x `elem` ["initial", "auto"] =
+            Just self { alignSelfCSS = alignItemsCSS parent }
+        | Just y <- parseAlign self x = Just self { alignSelfCSS = y }
+
+    longhand _ _ _ _ = Nothing
+
+    shorthand self "flex-flow" toks =
+        parseUnorderedShorthand self ["flex-direction", "flex-wrap"] toks
+    shorthand self "gap" toks | [x] <- parseOperands toks =
+            parseUnorderedShorthand' self ["row-gap", "column-gap"] [x, x]
+        | otherwise = parseUnorderedShorthand self ["row-gap", "column-gap"] toks
+    shorthand self "flex" toks
+        | [Ident "initial"] <- toks =
+            [("flex-grow", init), ("flex-shrink", init), ("flex-basis", px0)]
+        | [Ident "auto"] <- toks =
+            [("flex-grow", n1), ("flex-shrink", n1), ("flex-basis", init)]
+        | [Ident "none"] <- toks =
+            [("flex-grow", n0), ("flex-shrink", n0), ("flex-basis", init)]
+        | [a] <- operands, test "flex-grow" a =
+            [("flex-grow", a), ("flex-shrink", init), ("flex-basis", px0)]
+        | [a] <- operands, test "flex-basis" a =
+            [("flex-grow", n1), ("flex-shrink", init), ("flex-basis", a)]
+        | [a, b] <- operands, test "flex-grow" a, test "flex-shrink" b =
+            [("flex-grow", a), ("flex-shrink", b), ("flex-basis", px0)]
+        | [a, b] <- operands, test "flex-grow" a, test "flex-basis" b =
+            [("flex-grow", a), ("flex-shrink", init), ("flex-basis", b)]
+        | [a, b, c] <- operands, test "flex-grow" a, test "flex-shrink" b, test "flex-basis" c =
+            [("flex-grow", a), ("flex-shrink", b), ("flex-basis", c)]
+      where
+        operands = parseOperands toks
+        test a = isJust . longhand self self a
+        init = [Ident "initial"]
+        px0 = [Dimension "0" (NVInteger 0) "px"]
+        n1 = [Number "1" (NVInteger 1)]
+        n0 = [Number "0" (NVInteger 0)]
+    shorthand self k v | Just _ <- longhand self self k v = [(k, v)]
+        | otherwise = []
+
+-- | Lower the Flexbox styling tree to the Layout tree.
+lowerFlex :: CSSFlex -> Font' -> [CSSFlex] -> [a] -> [Font'] -> Flex a Length
+lowerFlex self font kids kids' fonts' = Flex {
+    direction = directionCSS self,
+    reverseRows = reverseRowsCSS self,
+    wrap = wrapCSS self,
+    justify = case justifyCSS self of
+        Just x -> x
+        Nothing | reverseRowsCSS self -> JEnd
+        Nothing -> JStart,
+    alignLines = alignLinesCSS self,
+    baseGap = case directionCSS self of
+        Row -> flip finalizeLength font $ rowGapCSS self
+        F.Column -> flip finalizeLength font $ columnGapCSS self,
+    crossGap = case directionCSS self of
+        Row -> flip finalizeLength font $ columnGapCSS self
+        F.Column -> flip finalizeLength font $ rowGapCSS self,
+    pageWidth = 0,
+    children = [[FlexChild {
+        grow = growCSS kid,
+        shrink = shrinkCSS kid,
+        basis = flip finalizeLength font' $ rowGapCSS kid,
+        alignment = alignSelfCSS kid,
+        flexInner = kid'
+      } | (kid, kid', font') <- zip3 kids kids' fonts']]
+  }
diff --git a/app/Integration.hs b/app/Integration.hs
--- a/app/Integration.hs
+++ b/app/Integration.hs
@@ -12,7 +12,7 @@
 
 import Graphics.Layout.CSS (CSSBox(..), finalizeCSS')
 import Graphics.Layout.CSS.Font (placeholderFont)
-import Graphics.Layout (LayoutItem, boxLayout,
+import Graphics.Layout (LayoutItem, boxLayout, glyphsPerFont,
                         layoutGetBox, layoutGetChilds, layoutGetInner)
 import Graphics.Layout.Box (zeroBox)
 import qualified Graphics.Layout.Box as B
@@ -42,6 +42,7 @@
 import Foreign.C.Types (CInt)
 import Data.Function (fix)
 import Control.Monad (unless)
+import Control.Exception (evaluate)
 import qualified Graphics.Text.Font.Choose as FC
 
 initReferer :: IO (Page (CSSCond.ConditionalStyles (CSSBox Nil)))
@@ -140,7 +141,9 @@
 
         pages <- tryReadMVar pages'
         case pages of
-            Just (display:_) -> renderDisplay renderer display
+            Just (display:_) -> do
+                evaluate $ glyphsPerFont display 
+                renderDisplay renderer display
             _ -> return ()
 
         present renderer
diff --git a/app/Integration2.hs b/app/Integration2.hs
--- a/app/Integration2.hs
+++ b/app/Integration2.hs
@@ -14,7 +14,7 @@
 
 import Graphics.Layout.CSS.Font (placeholderFont)
 import Graphics.Layout.CSS (finalizeCSS', CSSBox)
-import Graphics.Layout (LayoutItem, boxLayout)
+import Graphics.Layout (LayoutItem, boxLayout, glyphsPerFont)
 import Graphics.Layout.Box (Length, Size(..), PaddedBox(..), zeroBox)
 
 import Control.Exception (evaluate)
@@ -51,7 +51,13 @@
         layout = finalizeCSS' placeholderFont styles
     layout `seq` print "Laying out page!"
     res <- forkCompute $ boxLayout zeroBox { size = Size 1280 480 } layout False
-    readMVar res
+    res' <- readMVar res
+    case res' of
+        (page:_) -> do
+            print "Gathering atlas"
+            evaluate $ glyphsPerFont page
+            return ()
+        _ -> return ()
     --performGC
     --FC.fini -- FIXME: GC still left FontConfig references...
     return ()
diff --git a/cattrap.cabal b/cattrap.cabal
--- a/cattrap.cabal
+++ b/cattrap.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                cattrap
-version:             0.4.0.0
+version:             0.5.0.0
 synopsis:            Lays out boxes according to the CSS Box Model.
 description:         Computes where to place e.g. images, paragraphs, containers, tables, etc onscreen given desired amounts of whitespace.
 homepage:            https://argonaut-constellation.org/
@@ -27,7 +27,8 @@
                         Graphics.Layout.Box, Graphics.Layout.Arithmetic,
                         Graphics.Layout.CSS.Length, Graphics.Layout.CSS.Font,
                         Graphics.Layout.Inline, Graphics.Layout.Inline.CSS,
-                        Graphics.Layout.Grid.Table
+                        Graphics.Layout.Grid.Table,
+                        Graphics.Layout.Flex, Graphics.Layout.Flex.CSS
   other-modules:        Graphics.Layout.CSS.Parse
   -- other-extensions:
   build-depends:       base >=4.12 && <5, containers >= 0.6 && < 1, parallel >= 3 && <4,
@@ -58,7 +59,7 @@
                         stylist-traits , stylist>=2.7.0.1 && <3,
                         hurl-xml >= 0.2 && < 1, hurl >= 2.3 && < 3,
                         sdl2 >= 2.5.4 && < 2.6, containers, network-uri >=2.6 && <3,
-                        xml-conduit, directory >= 1.3 && < 2, xml-conduit-stylist,
+                        xml-conduit >= 1.8 && < 2, directory >= 1.3 && < 2, xml-conduit-stylist,
                         bytestring, file-embed >= 0.0.15 && < 0.1, deepseq, fontconfig-pure
   hs-source-dirs:      app
   default-language:    Haskell2010
@@ -69,8 +70,9 @@
   -- other-modules:
   -- other-extensions:
   build-depends:       base >=4.12 && <5, cattrap, text>=2.0.2 && <3, css-syntax,
-                        stylist-traits, stylist>=2.7.0.1 && <3, network-uri, html-conduit >=1.3 && <2,
-                        xml-conduit, xml-conduit-stylist >= 3 && < 4, deepseq, fontconfig-pure
+                        stylist-traits, stylist>=2.7.0.1 && <3, network-uri,
+                        html-conduit >=1.3 && <2, xml-conduit >= 1.8 && < 2,
+                        xml-conduit-stylist >= 3 && < 4, deepseq, fontconfig-pure
   hs-source-dirs:      app
   default-language:    Haskell2010
 
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -10,7 +10,7 @@
 import Data.Maybe (fromJust)
 
 import Graphics.Layout.Box as B
-import Graphics.Layout.Grid
+import Graphics.Layout.Grid as Grid
 import Graphics.Layout.Flow
 import Graphics.Layout
 
@@ -18,6 +18,7 @@
 import Graphics.Layout.Grid.Table
 import Graphics.Layout.Inline.CSS
 import Graphics.Layout.CSS.Font (placeholderFont)
+import Graphics.Layout.Flex as Flex
 
 import Data.Text.ParagraphLayout.Rich (constructParagraph,
         defaultParagraphOptions, defaultTextOptions,
@@ -360,7 +361,7 @@
             let defaultPageOptions = PageOptions 0 0 2 2
             let gridItem x y = GridItem {
                 cellStart = x, cellEnd = y,
-                alignment = Start,
+                Grid.alignment = Start,
                 minSize = 0, natSize = 0
               }
             let track cells' = Track {
@@ -410,6 +411,399 @@
                             LayoutFlow () lengthBox [inline "H"]
                         ]
                     ]
+    describe "Flexbox" $ do
+        it "wraps properly" $ do
+            let child l = FlexChild {
+                grow = 0,
+                shrink = 0,
+                basis = l,
+                Flex.alignment = AlStart,
+                flexInner = ()
+              }
+            let baseFlex = Flex.Flex {
+                direction = Row,
+                reverseRows = False,
+                wrap = NoWrap,
+                justify = JStart,
+                alignLines = Just JStart,
+                baseGap = 2,
+                crossGap = 2,
+                Flex.children = [[(child 10) { grow = 1 }, (child 20) { shrink = 1 },
+                        (child 30) { grow = 2 }, (child 40) { shrink = 2 }]],
+                pageWidth = 0
+              } :: Flex.Flex () Double
+            -- These test results don't look right...
+            flexWrap baseFlex 50 `shouldBe` baseFlex {
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child 1.3333333333333321) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child 2.6666666666666643) { shrink = 2}]]
+              }
+            flexWrap baseFlex 40 `shouldBe` baseFlex {
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child $ -2) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child $ -4) { shrink = 2}]]
+              }
+            flexWrap baseFlex 30 `shouldBe` baseFlex {
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child $ -5.333333333333332) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child $ -10.666666666666664) { shrink = 2}]]
+              }
+            flexWrap baseFlex 20 `shouldBe` baseFlex {
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child $ -8.666666666666668) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child $ -17.333333333333336) { shrink = 2}]]
+              }
+            flexWrap baseFlex 10 `shouldBe` baseFlex {
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child $ -12) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child $ -24) { shrink = 2}]]
+              }
+            flexWrap baseFlex { direction = Flex.Column } 50 `shouldBe` baseFlex {
+                direction = Flex.Column,
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child 1.3333333333333321) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child 2.6666666666666643) { shrink = 2}]]
+              }
+            flexWrap baseFlex { direction = Flex.Column } 40 `shouldBe` baseFlex {
+                direction = Flex.Column,
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child $ -2) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child $ -4) { shrink = 2}]]
+              }
+            flexWrap baseFlex { direction = Flex.Column } 30 `shouldBe` baseFlex {
+                direction = Flex.Column,
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child $ -5.333333333333332) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child $ -10.666666666666664) { shrink = 2}]]
+              }
+            flexWrap baseFlex { direction = Flex.Column } 20 `shouldBe` baseFlex {
+                direction = Flex.Column,
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child $ -8.666666666666668) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child $ -17.333333333333336) { shrink = 2}]]
+              }
+            flexWrap baseFlex { direction = Flex.Column } 10 `shouldBe` baseFlex {
+                direction = Flex.Column,
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child $ -12) { shrink = 1 }, (child 30) { grow = 2 },
+                        (child $ -24) { shrink = 2}]]
+              }
+            flexWrap baseFlex { reverseRows = True } 50 `shouldBe` baseFlex {
+                reverseRows = True,
+                Flex.children = [[
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 2.0,
+                        basis = 2.6666666666666643,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 2.0,
+                        shrink = 0.0,
+                        basis = 30.0,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 1.0,
+                        basis = 1.3333333333333321,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                       grow = 1.0,
+                       shrink = 0.0,
+                       basis = 10.0,
+                       Flex.alignment = AlStart,
+                       flexInner = ()
+                    }
+                ]]
+              }
+            flexWrap baseFlex { reverseRows = True } 40 `shouldBe` baseFlex {
+                reverseRows = True,
+                Flex.children = [[
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 2.0,
+                        basis = -4,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 2.0,
+                        shrink = 0.0,
+                        basis = 30.0,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 1.0,
+                        basis = -2,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                       grow = 1.0,
+                       shrink = 0.0,
+                       basis = 10.0,
+                       Flex.alignment = AlStart,
+                       flexInner = ()
+                    }
+                ]]
+            }
+            flexWrap baseFlex { reverseRows = True } 30 `shouldBe` baseFlex {
+                reverseRows = True,
+                Flex.children = [[
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 2.0,
+                        basis = -10.666666666666664,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 2.0,
+                        shrink = 0.0,
+                        basis = 30.0,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 1.0,
+                        basis = -5.333333333333332,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                       grow = 1.0,
+                       shrink = 0.0,
+                       basis = 10.0,
+                       Flex.alignment = AlStart,
+                       flexInner = ()
+                    }
+                ]]
+            }
+            flexWrap baseFlex { reverseRows = True } 20 `shouldBe` baseFlex {
+                reverseRows = True,
+                Flex.children = [[
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 2.0,
+                        basis = -17.333333333333336,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 2.0,
+                        shrink = 0.0,
+                        basis = 30.0,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 1.0,
+                        basis = -8.666666666666668,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                       grow = 1.0,
+                       shrink = 0.0,
+                       basis = 10.0,
+                       Flex.alignment = AlStart,
+                       flexInner = ()
+                    }
+                ]]
+              }
+            flexWrap baseFlex { reverseRows = True } 10 `shouldBe` baseFlex {
+                reverseRows = True,
+                Flex.children = [[
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 2.0,
+                        basis = -24,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 2.0,
+                        shrink = 0.0,
+                        basis = 30.0,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                        grow = 0.0,
+                        shrink = 1.0,
+                        basis = -12,
+                        Flex.alignment = AlStart,
+                        flexInner = ()
+                    },
+                    FlexChild {
+                       grow = 1.0,
+                       shrink = 0.0,
+                       basis = 10.0,
+                       Flex.alignment = AlStart,
+                       flexInner = ()
+                    }
+                ]]
+              }
+            flexWrap baseFlex { wrap = Wrap } 50 `shouldBe` baseFlex {
+                wrap = Wrap,
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child 6) { shrink = 1 }, (child 30) { grow = 2 }], [
+                        (child 40) { shrink = 2}]]
+              }
+            flexWrap baseFlex { wrap = Wrap } 40 `shouldBe` baseFlex {
+                wrap = Wrap,
+                Flex.children = [[(child 18) { grow = 1 },
+                        (child 20) { shrink = 1 }], [(child 40) { grow = 2 }],
+                        [(child 40) { shrink = 2}]]
+              }
+            flexWrap baseFlex { wrap = Wrap } 30 `shouldBe` baseFlex {
+                wrap = Wrap,
+                Flex.children = [[(child 10) { grow = 1 },
+                        (child 18) { shrink = 1 }], [(child 30) { grow = 2 }],
+                        [(child 30) { shrink = 2}]]
+              }
+            flexWrap baseFlex { wrap = Wrap } 20 `shouldBe` baseFlex {
+                wrap = Wrap,
+                Flex.children = [[(child 20) { grow = 1 }],
+                        [(child 20) { shrink = 1 }], [(child 30) { grow = 2 }],
+                        [(child 20) { shrink = 2}]]
+              }
+            flexWrap baseFlex { wrap = Wrap } 10 `shouldBe` baseFlex {
+                wrap = Wrap,
+                Flex.children = [[(child 10) { grow = 1 }], [(child 10) { shrink = 1 }],
+                        [(child 30) { grow = 2 }], [(child 10) { shrink = 2}]]
+              }
+            flexWrap baseFlex { wrap = WrapReverse } 50 `shouldBe` baseFlex {
+                wrap = WrapReverse,
+                Flex.children = [[(child 40) { shrink = 2}], [(child 10) { grow = 1 },
+                        (child 6) { shrink = 1 }, (child 30) { grow = 2 }]]
+              }
+            flexWrap baseFlex { wrap = WrapReverse } 40 `shouldBe` baseFlex {
+                wrap = WrapReverse,
+                Flex.children = [[(child 40) { shrink = 2}], [(child 40) { grow = 2 }],
+                        [(child 18) { grow = 1 }, (child 20) { shrink = 1 }]]
+              }
+            flexWrap baseFlex { wrap = WrapReverse } 30 `shouldBe` baseFlex {
+                wrap = WrapReverse,
+                Flex.children = [[(child 30) { shrink = 2}], [(child 30) { grow = 2 }],
+                        [(child 10) { grow = 1 }, (child 18) { shrink = 1 }]]
+              }
+            flexWrap baseFlex { wrap = WrapReverse } 20 `shouldBe` baseFlex {
+                wrap = WrapReverse,
+                Flex.children = [[(child 20) { shrink = 2}], [(child 30) { grow = 2 }],
+                        [(child 20) { shrink = 1 }], [(child 20) { grow = 1 }]]
+              }
+            flexWrap baseFlex { wrap = WrapReverse } 10 `shouldBe` baseFlex {
+                wrap = WrapReverse,
+                Flex.children = [[(child 10) { shrink = 2}], [(child 30) { grow = 2 }],
+                        [(child 10) { shrink = 1 }], [(child 10) { grow = 1 }]]
+              }
+        it "Paginates properly" $ do
+            let child l align = FlexChild {
+                grow = 0,
+                shrink = 0,
+                basis = l,
+                Flex.alignment = align,
+                flexInner = l
+              }
+            let baseFlex = Flex.Flex {
+                direction = Row,
+                reverseRows = False,
+                wrap = Wrap,
+                justify = JStart,
+                alignLines = Just JStart,
+                baseGap = 2,
+                crossGap = 2,
+                Flex.children = [[(child 10 AlStart) { grow = 1 },
+                        (child 20 AlCenter) { shrink = 1 },
+                        (child 30 AlEnd) { grow = 2 },
+                        (child 40 AlStretch) { shrink = 2 }]],
+                pageWidth = 80
+              } :: Flex.Flex Double Double
+            let self = flexWrap baseFlex 50
+            self `shouldBe` baseFlex {
+                Flex.children = [[(child 10 AlStart) { grow = 1 },
+                        (child 6 AlCenter) { shrink = 1, flexInner = 20 },
+                        (child 30 AlEnd) { grow = 2 }], [
+                        (child 40 AlStretch) { shrink = 2}]]
+              }
+            flexSplit (\x -> Size x x) 30 50 self `shouldBe` (baseFlex {
+                Flex.children = [[(child 10 AlStart) { grow = 1 },
+                        (child 6 AlCenter) { shrink = 1, flexInner = 20 },
+                        (child 30 AlEnd) { grow = 2 }]]
+              }, baseFlex {
+                Flex.children = [[(child 40 AlStretch) { shrink = 2}]]
+              })
+            print self { direction = Flex.Column }
+            flexSplit (\x -> Size x x) 40 50 self { direction = Flex.Column } `shouldBe` (baseFlex {
+                direction = Flex.Column,
+                Flex.children = [[(child 10 AlStart) { grow = 1 },
+                        (child (-4) AlCenter) { shrink = 1, flexInner = 20 },
+                        (child 30 AlEnd) { grow = 2 }], [(child 40 AlStretch) { shrink = 2}]]
+              }, baseFlex {
+                direction = Flex.Column,
+                Flex.children = []
+              })
+            flexSplit (\x -> Size x x) 10 50 self { direction = Flex.Column } `shouldBe` (baseFlex {
+                direction = Flex.Column,
+                Flex.children = []
+              }, baseFlex {
+                direction = Flex.Column,
+                Flex.children = [[(child 10 AlStart) { grow = 1 },
+                        (child 6 AlCenter) { shrink = 1, flexInner = 20 },
+                        (child 30 AlEnd) { grow = 2 }], [(child 40 AlStretch) { shrink = 2}]]
+              })
+            flexSplit (\x -> Size x x) 10 50 self { direction = Flex.Column, pageWidth = 40 } `shouldBe` (baseFlex {
+                Flex.children = [[(child 10 AlStart) { grow = 1 },
+                        (child 6 AlCenter) { shrink = 1, flexInner = 20 },
+                        (child 30 AlEnd) { grow = 2 }]],
+                pageWidth = 40
+              }, baseFlex {
+                Flex.children = [[(child 40 AlStretch) { shrink = 2}]],
+                pageWidth = 40
+              })
+        it "positions correctly" $ do
+            let child l align = FlexChild {
+                grow = 0,
+                shrink = 0,
+                basis = l,
+                Flex.alignment = align,
+                flexInner = l
+              }
+            let child' l align pos = FlexChild {
+                grow = 0, shrink = 0, basis = l,
+                Flex.alignment = align, flexInner = (pos, l)
+              }
+            let flex childs = Flex.Flex {
+                direction = Row,
+                reverseRows = False,
+                wrap = Wrap,
+                justify = JStart,
+                alignLines = Just JStart,
+                baseGap = 2,
+                crossGap = 2,
+                Flex.children = childs,
+                pageWidth = 80
+              }
+            let baseFlex = flex [[(child 10 AlStart) { grow = 1 },
+                    (child 20 AlCenter) { shrink = 1 },
+                    (child 30 AlEnd) { grow = 2 },
+                    (child 40 AlStretch) { shrink = 2 }]] :: Flex.Flex Double Double
+            flexPosition (,) (\a -> Size a a) (10, 10) (Size 50 50) baseFlex `shouldBe` flex [[
+                (child' 10 AlStart (10, 10)) { grow = 1 },
+                (child' 20 AlCenter (22, 20)) { shrink = 1 },
+                (child' 30 AlEnd (44, 20)) { grow = 2 },
+                (child' 40 AlStretch (76, 10)) { shrink = 2 }
+              ]]
 
 
 runMath = flip evalCalc [] . mapCalc fst . flip parseCalc [] . filter (/= Whitespace) . tokenize
