diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # Haskell Stylist
 Generic CSS style engine for Haskell, intended to aid the development of new browser engines.
 
-Stylish Haskell implements CSS selection and cascade (but not inheritance) independant of the CSS at-rules and properties understood by the caller. It is intended to ease the development of new browser engines, independant of their output targets.
+Haskell Stylist implements CSS selection and cascade (but not inheritance) independant of the CSS at-rules and properties understood by the caller. It is intended to ease the development of new browser engines, independant of their output targets.
 
 For more interesting projects see: https://github.io/alcinnz/browser-engine-ganarchy/
 
diff --git a/src/Data/CSS/Preprocessor/Assets.hs b/src/Data/CSS/Preprocessor/Assets.hs
--- a/src/Data/CSS/Preprocessor/Assets.hs
+++ b/src/Data/CSS/Preprocessor/Assets.hs
@@ -3,7 +3,7 @@
 module Data.CSS.Preprocessor.Assets(StyleAssets(..), URIRewriter(..)) where
 
 -- TODO Unit test!
-import           Data.Text as Txt hiding (elem)
+import           Data.Text as Txt
 import           Network.URI
 import qualified Data.CSS.Syntax.StyleSheet as CSS
 import qualified Data.CSS.Syntax.Tokens as CSSTok
diff --git a/src/Data/CSS/Preprocessor/Conditions.hs b/src/Data/CSS/Preprocessor/Conditions.hs
--- a/src/Data/CSS/Preprocessor/Conditions.hs
+++ b/src/Data/CSS/Preprocessor/Conditions.hs
@@ -16,6 +16,7 @@
 import Data.CSS.Syntax.Selector
 import Data.CSS.Syntax.Tokens(Token(..))
 import Data.CSS.Style (PropertyParser(..))
+import Data.CSS.Syntax.AtLayer as AtLayer
 
 import Data.Text.Internal (Text(..))
 import Data.Text (unpack)
@@ -36,15 +37,22 @@
     -- | Queued style rules, to be evaluated later.
     rules :: [ConditionalRule p],
     -- | PropertyParser to test against for `@supports` rules.
-    propertyParser :: p
+    propertyParser :: p,
+    -- | Known-named @layers.
+    layers :: AtLayer.Tree,
+    -- | The current @layer, for resolving nesting
+    layerNamespace :: [Text],
+    -- | The integral path to the current @layer, for resolving nesting
+    layerPath' :: [Int]
 }
 
 -- | Constructs an empty `ConditionalStyles`.
 conditionalStyles :: PropertyParser p => URI -> String -> ConditionalStyles p
-conditionalStyles uri mediaDocument' = ConditionalStyles uri mediaDocument' False [] temp
+conditionalStyles uri mediaDocument' =
+    ConditionalStyles uri mediaDocument' False [] temp AtLayer.emptyTree [] [0]
 
 -- | Style rules that can be queued in a `ConditionalStyles`.
-data ConditionalRule p = Priority Int | StyleRule' StyleRule | AtRule Text [Token] |
+data ConditionalRule p = Priority [Int] | StyleRule' StyleRule | AtRule Text [Token] |
     External Query.Expr URI | Internal Query.Expr (ConditionalStyles p)
 
 addRule' :: ConditionalStyles p -> ConditionalRule p -> ConditionalStyles p
@@ -60,7 +68,7 @@
 parseAtBlock self [] = (self, [])
 
 instance PropertyParser p => StyleSheet (ConditionalStyles p) where
-    setPriority x self = addRule' self $ Priority x
+    setPriorities x self = addRule' self { layerPath' = x } $ Priority x
     addRule self rule = addRule' self $ StyleRule' rule
 
     addAtRule self "document" (Whitespace:toks) = addAtRule self "document" toks
@@ -107,6 +115,15 @@
             if evalSupports (propertyParser self) cond
                 then parseAtBlock self toks' else (self, skipAtRule toks')
 
+    addAtRule self@ConditionalStyles { layers = l, layerNamespace = ns, layerPath' = xs@(x:_) }
+            "layer" toks =
+        case parseAtLayer ns toks l $ \ns' path' -> setPriorities (x:path') self {
+            layerNamespace = ns'
+        } of
+            (layers', Just self', toks') ->
+                (setPriorities xs self { rules = rules self', layers = layers' }, toks')
+            (layers', Nothing, toks') -> (setPriorities xs self { layers = layers' }, toks')
+
     addAtRule self rule tokens = let (block, rest) = scanAtRule tokens in
         (addRule' self $ AtRule rule block, rest)
 
@@ -120,11 +137,42 @@
 --------
 parseAtImport :: PropertyParser p => ConditionalStyles p -> Text ->
         [Token] -> (ConditionalStyles p, [Token])
+parseAtImport self src (Whitespace:toks) = parseAtImport self src toks
+parseAtImport self src (Function "supports":toks)
+    | (cond, RightParen:toks') <- break (== RightParen) toks =
+        if evalSupports (propertyParser self) cond
+            then parseAtImport self src toks' else (self, skipAtRule toks')
+parseAtImport self@ConditionalStyles {  layerNamespace = ns } src (Function "layer":toks)
+        | (layerToks, RightParen:toks') <- break (== RightParen) toks, validLayer layerToks =
+            parseAtImportInLayer self src (ns ++ [name | Ident name <- layerToks]) toks'
+    where
+        validLayer toks' = validLayer' (Delim '.':filter (/= Whitespace) toks')
+        validLayer' (Delim '.':Ident _:toks') = validLayer toks'
+        validLayer' [] = True
+        validLayer' _ = False
+parseAtImport self@ConditionalStyles { layers = l, layerNamespace = ns } src (Ident "layer":toks) =
+        parseAtImportInLayer self src (uniqueName ns l) toks
 parseAtImport self src toks
     | (cond, Semicolon:toks') <- Query.parse Semicolon toks, Just uri <- parseURI $ unpack src =
         (addRule' self $ External cond uri, toks')
 parseAtImport self _ toks = (self, skipAtRule toks)
 
+parseAtImportInLayer :: PropertyParser p => ConditionalStyles p -> Text -> [Text] ->
+    [Token] -> (ConditionalStyles p, [Token])
+parseAtImportInLayer self@ConditionalStyles {
+        layers = l, layerNamespace = ns, layerPath' = xs@(x:_)
+    } src layerName toks =
+        let (ret, toks') = parseAtImport self' src toks in (setPriorities xs ret, toks')
+  where
+    layers' = registerLayer layerName l
+    self' = setPriorities (x:layerPath layerName layers') self {
+        layers = layers',
+        layerNamespace = ns
+    }
+parseAtImportInLayer self src layerName toks = parseAtImportInLayer self {
+        layerPath' = [0]
+    } src layerName toks -- Shouldn't happen, recover gracefully.
+
 -- | Returns `@import` URLs that need to be imported.
 extractImports :: (Text -> Query.Datum) -> (Token -> Query.Datum) -> ConditionalStyles p -> [URI]
 extractImports vars evalToken self =
@@ -157,7 +205,7 @@
 resolve v t styles self = resolve' v t (reverse $ rules self) styles
 resolve' :: StyleSheet s => (Text -> Query.Datum) -> (Token -> Query.Datum) ->
         [ConditionalRule p] -> s -> s
-resolve' v t (Priority x:rules') styles = resolve' v t rules' $ setPriority x styles
+resolve' v t (Priority x:rules') styles = resolve' v t rules' $ setPriorities x styles
 resolve' v t (StyleRule' rule:rules') styles = resolve' v t rules' $ addRule styles rule
 resolve' v t (AtRule name block:rules') styles = resolve' v t rules' $ fst $ addAtRule styles name block
 resolve' v t (Internal cond block:rules') styles | Query.eval v t cond =
diff --git a/src/Data/CSS/Preprocessor/PsuedoClasses.hs b/src/Data/CSS/Preprocessor/PsuedoClasses.hs
--- a/src/Data/CSS/Preprocessor/PsuedoClasses.hs
+++ b/src/Data/CSS/Preprocessor/PsuedoClasses.hs
@@ -2,16 +2,18 @@
 -- | Lowers psuedoclasses to rawer syntactic forms.
 module Data.CSS.Preprocessor.PsuedoClasses(LowerPsuedoClasses(..),
     psuedoClassesFilter, htmlPsuedoFilter,
-    addRewrite, addRewrite', addPsuedoEl, addNamespace, addTest, PropertyTest) where
+    addRewrite, addRewrite', addTest, addContains, PropertyTest,
+    addPsuedoEl, addNamespace) where
 
 import Data.CSS.Syntax.StyleSheet
 import Data.CSS.Syntax.Selector
 import Data.CSS.Syntax.Tokens
 
-import Data.Text as Txt hiding (elem)
+import Data.Text as Txt
 import Data.Maybe (fromMaybe, listToMaybe)
 import Data.HashMap.Lazy as HM
 import Data.Function ((&))
+import Data.List as L (intercalate)
 
 --------
 ---- core
@@ -88,6 +90,16 @@
     spliceArgs (tok:toks) args = tok : spliceArgs toks args
     spliceArgs _ _ = [Ident "\tfail"]
 
+addContains :: Text -> [Int] -> LowerPsuedoClasses s -> LowerPsuedoClasses s
+addContains name path self =
+    addRewrite' name (L.intercalate [Comma] $ buildSelector path [Colon, Ident "root"]) self
+  where
+    buildSelector (p:ps) prefix =
+        let prefix' = prefix ++ [Delim '>', Colon, Function "nth-child", num p, RightParen]
+        in prefix' : buildSelector ps prefix'
+    buildSelector [] _ = []
+    num x = Number (Txt.pack $ show x) $ NVInteger (toInteger x)
+
 addTest :: Text -> Maybe Text -> Text -> PropertyFunc -> LowerPsuedoClasses s -> LowerPsuedoClasses s
 addTest name ns attr test self = addTest' name (noArg [Property ns attr $ Callback test]) self
     where
@@ -124,4 +136,5 @@
     addRewrite "readonly" "[readonly], [disabled]" &
     addRewrite "read-write" ":not([readonly]):not([disabled])" &
     addRewrite "required" "[required]" &
-    addRewrite "scope" ":root"
+    addRewrite "scope" ":root" &
+    addRewrite "root" "html"
diff --git a/src/Data/CSS/Style.hs b/src/Data/CSS/Style.hs
--- a/src/Data/CSS/Style.hs
+++ b/src/Data/CSS/Style.hs
@@ -19,7 +19,9 @@
 import Data.CSS.Style.Cascade (PropertyParser(..), TrivialPropertyParser, Props)
 
 import Data.CSS.Syntax.Tokens (Token(..))
-import Data.CSS.Syntax.StyleSheet (StyleSheet(..))
+import Data.CSS.Syntax.StyleSheet (StyleSheet(..), skipAtRule)
+import Data.CSS.Syntax.AtLayer as AtLayer
+
 import Data.HashMap.Strict (HashMap, lookupDefault, fromList)
 import Data.Text (isPrefixOf)
 import Data.List (elemIndex)
@@ -38,24 +40,39 @@
     -- | The "PropertyParser" to use for property syntax validation.
     parser :: parser,
     -- | Whether author, useragent, or user styles are currently being parsed.
-    priority :: Int -- author vs user agent vs user styles
+    -- The tail of this list indicates which Cascade Layer is active.
+    priority :: [Int], -- author vs user agent vs user styles, incorporates Cascade Layers
+    -- | Parse data for @layer, to give webdevs explicit control over the cascade.
+    layers :: AtLayer.Tree,
+    --- | The name of the @layer we're within.
+    layerNamespace :: [Text]
 }
 
 -- | Constructs an empty QueryableStyleSheet'.
 queryableStyleSheet :: PropertyParser p => QueryableStyleSheet p
-queryableStyleSheet = QueryableStyleSheet' {store = new, parser = temp, priority = 0}
+queryableStyleSheet = QueryableStyleSheet' {
+    store = new, parser = temp, layers = AtLayer.emptyTree,
+    priority = [0], layerNamespace = [] }
 
 instance (RuleStore s, PropertyParser p) => StyleSheet (QueryableStyleSheet' s p) where
-    setPriority v self = self {priority = v}
-    addRule self@(QueryableStyleSheet' store' _ priority') rule = self {
+    setPriorities vs self = self { priority = vs }
+    addRule self@(QueryableStyleSheet' store' _ priority' _ _) rule = self {
             store = addStyleRule store' priority' $ styleRule' rule
         }
+    addAtRule self@QueryableStyleSheet' { layerNamespace = ns, layers = layers_, priority = v:_ }
+            "layer" toks =
+        case parseAtLayer ns toks layers_ $ \ns' path -> self {
+            priority = v : path, layerNamespace = ns'
+        } of
+            (layers', Just self', toks') -> (self { store = store self', layers = layers' }, toks')
+            (layers', Nothing, toks') -> (self { layers = layers' }, toks')
+    addAtRule self _ toks = (self, skipAtRule toks)
 
 --- Reexpose cascade methods
 -- | Looks up style rules matching the specified element, grouped by psuedoelement.
 queryRules :: (PropertyParser p, RuleStore s) =>
     QueryableStyleSheet' s p -> Element -> HashMap Text [StyleRule']
-queryRules (QueryableStyleSheet' store' _ _) = Cascade.query store'
+queryRules (QueryableStyleSheet' store' _ _ _ _) = Cascade.query store'
 
 -- | Selects used property values from the given style rules,
 -- & populates into a new `PropertyParser` inheriting from the one given.
diff --git a/src/Data/CSS/Style/Cascade.hs b/src/Data/CSS/Style/Cascade.hs
--- a/src/Data/CSS/Style/Cascade.hs
+++ b/src/Data/CSS/Style/Cascade.hs
@@ -8,6 +8,7 @@
 
 import Data.CSS.Style.Common
 import Data.CSS.Syntax.Tokens
+import Stylist (PropertyParser(..), Props)
 
 -- TODO do performance tests to decide beside between strict/lazy,
 --      or is another Map implementation better?
@@ -15,37 +16,12 @@
 import qualified Data.HashMap.Lazy as HML
 import Data.Text (unpack, pack, isPrefixOf)
 
--- | Defines how to parse CSS properties into an output "style" format.
-class PropertyParser a where
-    -- | Default styles.
-    temp :: a
-    -- | Creates a style inherited from a parent style.
-    inherit :: a -> a
-    inherit = id
-
-    -- | Expand a shorthand property into longhand properties.
-    shorthand :: a -> Text -> [Token] -> [(Text, [Token])]
-    shorthand self key value | Just _ <- longhand self self key value = [(key, value)]
-        | otherwise = []
-    -- longhand parent self name value
-    longhand :: a -> a -> Text -> [Token] -> Maybe a
-
-    -- | Retrieve stored variables, optional.
-    getVars :: a -> Props
-    getVars _ = []
-    -- | Save variable values, optional.
-    setVars :: Props -> a -> a
-    setVars _ = id
-
 -- | Gather properties into a hashmap.
 data TrivialPropertyParser = TrivialPropertyParser (HashMap String [Token]) deriving (Show, Eq)
 instance PropertyParser TrivialPropertyParser where
     temp = TrivialPropertyParser empty
     longhand _ (TrivialPropertyParser self) key value =
         Just $ TrivialPropertyParser $ insert (unpack key) value self
-
--- | "key: value;" entries to be parsed into an output type.
-type Props = [(Text, [Token])]
 
 --------
 ---- Query/Psuedo-elements
diff --git a/src/Data/CSS/Style/Common.hs b/src/Data/CSS/Style/Common.hs
--- a/src/Data/CSS/Style/Common.hs
+++ b/src/Data/CSS/Style/Common.hs
@@ -11,39 +11,24 @@
 import Data.CSS.Syntax.Selector
 import Data.CSS.Syntax.Tokens
 import Data.Text.Internal (Text(..))
-
--- | An inversely-linked tree of elements, to apply CSS selectors to.
-data Element = ElementNode {
-    -- | The element's parent in the tree.
-    parent :: Maybe Element,
-    -- | The element's previous sibling in the tree.
-    previous :: Maybe Element,
-    -- | The element's name.
-    name :: Text,
-    -- | The element's namespace.
-    namespace :: Text,
-    -- | The element's attributes, in sorted order.
-    attributes :: [Attribute]
-}
--- | A key-value attribute.
-data Attribute = Attribute Text Text String deriving (Eq, Ord)
+import Stylist (Element(..), Attribute(..))
 
 class RuleStore a where
     new :: a
-    addStyleRule :: a -> Int -> StyleRule' -> a
+    addStyleRule :: a -> [Int] -> StyleRule' -> a
     lookupRules :: a -> Element -> [StyleRule']
 
 type SelectorFunc = Element -> Bool
 data StyleRule' = StyleRule' {
     inner :: StyleRule,
     compiledSelector :: SelectorFunc,
-    rank :: (Int, (Int, Int, Int), Int) -- This reads ugly, but oh well.
+    rank :: ([Int], (Int, Int, Int), Int) -- This reads ugly, but oh well.
 }
 styleRule' :: StyleRule -> StyleRule'
 styleRule' rule = StyleRule' {
     inner = rule,
     compiledSelector = \_ -> True,
-    rank = (0, (0, 0, 0), 0)
+    rank = ([0], (0, 0, 0), 0)
 }
 
 instance Eq StyleRule' where
diff --git a/src/Data/CSS/Style/Importance.hs b/src/Data/CSS/Style/Importance.hs
--- a/src/Data/CSS/Style/Importance.hs
+++ b/src/Data/CSS/Style/Importance.hs
@@ -26,7 +26,7 @@
     new = ImportanceSplitter new
     addStyleRule (ImportanceSplitter self) priority rule =
             ImportanceSplitter $ addStyleRule (
-                addStyleRule self (negate priority) $ buildRule unimportant
+                addStyleRule self (map negate priority) $ buildRule unimportant
             ) priority $ buildRule important
         where
             (unimportant, important) = splitProperties props
diff --git a/src/Data/CSS/Style/Selector/Interpret.hs b/src/Data/CSS/Style/Selector/Interpret.hs
--- a/src/Data/CSS/Style/Selector/Interpret.hs
+++ b/src/Data/CSS/Style/Selector/Interpret.hs
@@ -7,6 +7,7 @@
     ) where
 
 import Data.CSS.Style.Common
+import Stylist (compileAttrTest, matched, hasWord)
 
 import Data.Text (unpack)
 import Data.List
@@ -14,7 +15,7 @@
 import Data.Bits (xor)
 
 -- For pseudoclasses
-import Data.CSS.Syntax.Selector (parseSelectors, PropertyFunc(..))
+import Data.CSS.Syntax.Selector (parseSelectors)
 import Data.CSS.Syntax.Tokens (Token(..), NumericValue(..))
 
 -- | A compiled(?) CSS selector.
@@ -73,16 +74,6 @@
 lowerInner (Psuedoclass _ _:_) = ([Fail], [])
 lowerInner [] = ([], [])
 
-compileAttrTest :: PropertyTest -> String -> Bool
-compileAttrTest Exists = matched
-compileAttrTest (Equals val) = (== (unpack val))
-compileAttrTest (Suffix val) = isSuffixOf $ unpack val
-compileAttrTest (Prefix val) = isPrefixOf $ unpack val
-compileAttrTest (Substring val) = isInfixOf $ unpack val
-compileAttrTest (Include val) = hasWord $ unpack val
-compileAttrTest (Dash val) = hasLang $ unpack val
-compileAttrTest (Callback (PropertyFunc cb)) = cb
-
 sortAttrs :: [(Text, Maybe Text, b)] -> [(Text, Maybe Text, b)]
 sortAttrs = sortBy compareAttrs where compareAttrs (x, x', _) (y, y', _) = (x, x') `compare` (y, y')
 
@@ -106,8 +97,6 @@
     | not $ test el = False
     | upTest (fromJust $ traverser el) = True
     | otherwise = indirect traverser upTest test $ fromJust $ traverser el
-matched :: t -> Bool
-matched _ = True
 
 testAttr :: Text -> (String -> Bool) -> AttrsFunc -> AttrsFunc
 testAttr expected test next attrs@(Attribute attr _ value : attrs')
@@ -123,11 +112,6 @@
     | (attr, ns) == (expected, expectedNS) && test value = next attrs
     | otherwise = False
 testAttrNS _ _ _ _ [] = False
-
-hasWord :: String -> String -> Bool
-hasWord expected value = expected `elem` words value
-hasLang :: [Char] -> [Char] -> Bool
-hasLang expected value = expected == value || isPrefixOf (expected ++ "-") value
 
 --- Pseudoclasses
 recursiveSelect :: Bool -> [SelectorFunc] -> SelectorFunc -> SelectorFunc
diff --git a/src/Data/CSS/Style/Selector/Specificity.hs b/src/Data/CSS/Style/Selector/Specificity.hs
--- a/src/Data/CSS/Style/Selector/Specificity.hs
+++ b/src/Data/CSS/Style/Selector/Specificity.hs
@@ -40,7 +40,10 @@
     new = OrderedRuleStore new 0
     addStyleRule (OrderedRuleStore self count) priority rule = OrderedRuleStore (
             addStyleRule self priority $ rule {
-                rank = (priority, computeSpecificity (psuedoElement rule) $ selector rule, count)
+                rank = (
+                    priority ++ [maxBound], -- Ensure unlayered rules take precedance.
+                    computeSpecificity (psuedoElement rule) $ selector rule,
+                    count)
             }
         ) (count + 1)
     lookupRules (OrderedRuleStore self _) el = sort $ lookupRules self el
diff --git a/src/Data/CSS/StyleTree.hs b/src/Data/CSS/StyleTree.hs
--- a/src/Data/CSS/StyleTree.hs
+++ b/src/Data/CSS/StyleTree.hs
@@ -1,50 +1,42 @@
 -- | Abstracts away tree traversals.
 -- Mostly used by callers including (soon) XML Conduit Stylist,
 -- but also used internally for generating counter text.
+--
+-- Backwards compatability module, this API has been moved out into "stylist-traits".
+-- Though it also contains integration between the styletree & styling APIs.
+{-# LANGUAGE OverloadedStrings #-}
 module Data.CSS.StyleTree(StyleTree(..), treeOrder, treeOrder',
-    Path, treeMap, treeFlatten, preorder, preorder', postorder) where
-
-data StyleTree p = StyleTree {
-    style :: p,
-    children :: [StyleTree p]
-}
-
-type Path = [Integer]
-treeOrder :: (c -> c -> Path -> p -> (c, p')) ->
-    c -> StyleTree p -> StyleTree p'
-treeOrder cb ctxt tree = StyleTree
-    (snd $ cb ctxt ctxt [] $ style tree)
-    (snd $ treeOrder' cb ctxt ctxt [0] $ children tree)
-treeOrder' :: (c -> c -> Path -> p -> (c, p')) ->
-    c -> c -> Path -> [StyleTree p] -> (c, [StyleTree p'])
-treeOrder' cb prevContext context (num:path) (node:nodes) = (tailContext, StyleTree node' children' : nodes')
-    where
-        (selfContext, node') = cb prevContext context (num:path) $ style node
-        (childContext, children') = treeOrder' cb selfContext selfContext (0:num:path) $ children node
-        (tailContext, nodes') = treeOrder' cb selfContext childContext (num + 1:path) nodes
-treeOrder' _ _ context _ [] = (context, [])
-treeOrder' _ _ _ [] _ = error "Invalid path during tree traversal!"
+    Path, treeMap, treeFlatten, preorder, preorder', postorder,
+    stylize, inlinePseudos) where
 
-treeMap :: (p -> p') -> StyleTree p -> StyleTree p'
-treeMap cb = treeOrder (\_ _ _ p -> ((), cb p)) ()
+import Stylist.Tree -- Mainly for reexports
 
-treeFlatten :: StyleTree p -> [p]
-treeFlatten = treeFlatten' . children
-treeFlatten' :: [StyleTree p] -> [p]
-treeFlatten' (StyleTree p []:ps) = p : treeFlatten' ps
-treeFlatten' (StyleTree _ childs:sibs) = treeFlatten' childs ++ treeFlatten' sibs
-treeFlatten' [] = []
+import Stylist
+import Data.CSS.Style
+import Data.CSS.Syntax.StyleSheet (parseProperties')
+import Data.CSS.Syntax.Tokens
+import Data.Text (Text, pack)
+import Data.HashMap.Strict as M (toList)
+import Data.Maybe (fromMaybe)
 
-preorder :: (Maybe b -> Maybe b -> a -> b) -> StyleTree a -> StyleTree b
-preorder cb self = head $ preorder' cb Nothing Nothing [self]
-preorder' :: (Maybe b -> Maybe b -> a -> b) ->
-        Maybe b -> Maybe b -> [StyleTree a] -> [StyleTree b]
-preorder' cb parent previous (self:sibs) = let self' = cb parent previous $ style self
-        in StyleTree self' (preorder' cb (Just self') Nothing $ children self) :
-            preorder' cb parent (Just self') sibs
-preorder' _ _ _ [] = []
+stylize :: PropertyParser s => QueryableStyleSheet s -> StyleTree Element -> StyleTree [(Text, s)]
+stylize = preorder . stylize'
+stylize' :: PropertyParser s => QueryableStyleSheet s -> Maybe [(Text, s)] -> Maybe [(Text, s)] ->
+        Element -> [(Text, s)]
+stylize' stylesheet parent' _ el = ("", base) : [
+        (k, cascade' v [] base) | (k, v) <- M.toList $ queryRules stylesheet el
+    ] where
+        base = cascade stylesheet el overrides $ fromMaybe temp $ lookup "" =<< parent'
+        overrides = concat [fst $ parseProperties' $ tokenize $ pack val
+            | Attribute "style" _ val <- attributes el]
 
-postorder :: (a -> [b] -> [b]) -> StyleTree a -> [StyleTree b]
-postorder cb (StyleTree self childs) =
-    [StyleTree self' children' | self' <- cb self $ Prelude.map style children']
-  where children' = concat $ Prelude.map (postorder cb) childs
+inlinePseudos :: PropertyParser s => StyleTree [(Text, VarParser s)] -> StyleTree s
+inlinePseudos (StyleTree self childs) = StyleTree {
+        style = fromMaybe temp $ innerParser <$> lookup "" self,
+        children = pseudo "before" ++ map inlinePseudos childs ++ pseudo "after"
+    } where
+        pseudo n
+            | Just sty <- innerParser <$> lookup n self,
+                Just style' <- longhand sty sty "::" [Ident n] = [StyleTree style' []]
+            | Just sty <- innerParser <$> lookup n self = [StyleTree sty []]
+            | otherwise = []
diff --git a/src/Data/CSS/Syntax/AtLayer.hs b/src/Data/CSS/Syntax/AtLayer.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/CSS/Syntax/AtLayer.hs
@@ -0,0 +1,74 @@
+module Data.CSS.Syntax.AtLayer(parseAtLayer, Tree(..),
+    registerLayer, layerPath, uniqueName, emptyTree) where
+
+import Data.HashMap.Lazy as M (HashMap, (!?), insert, size, empty)
+import Data.Text as T hiding (reverse, replicate, length)
+import Data.CSS.Syntax.Tokens
+
+import Stylist.Parse
+
+parseAtLayer :: StyleSheet s => [Text] -> [Token] -> Tree ->
+    ([Text] -> [Int] -> s) -> (Tree, Maybe s, [Token])
+parseAtLayer namespace (Whitespace:toks) tree cb = parseAtLayer namespace toks tree cb
+parseAtLayer namespace (Ident layer:toks) tree cb = inner toks [layer] tree
+    where
+        inner (Delim '.':Ident sublayer:toks') layers tree' =  inner toks' (sublayer:layers) tree'
+        inner (Whitespace:toks') layers tree' = inner toks' layers tree'
+        inner (Comma:toks') layers tree' =
+            let (ret, tail') = parseLayerStmt namespace toks' $registerLayer (namespaced layers) tree'
+            in (ret, Nothing, tail')
+        inner (LeftCurlyBracket:toks') layers  tree' =
+            let (ret, styles, tail') = parseLayerBlock (namespaced layers) toks' tree' cb
+            in (ret, Just styles, tail')
+        inner (Semicolon:toks') layers tree' = (registerLayer (namespaced layers) tree', Nothing, toks')
+        inner [] layers tree' = (registerLayer (namespaced layers) tree', Nothing, [])
+        inner toks' _ _ = (tree, Nothing, skipAtRule toks')
+        namespaced layers = namespace ++ reverse layers
+parseAtLayer ns (LeftCurlyBracket:toks) tree cb = 
+    let (ret, styles, tail') = parseLayerBlock (uniqueName ns tree) toks tree cb
+    in (ret, Just styles, tail')
+parseAtLayer _ toks tree _ = (tree, Nothing, skipAtRule toks)
+
+parseLayerStmt :: [Text] -> [Token] -> Tree -> (Tree, [Token])
+parseLayerStmt namespace (Whitespace:toks) tree = parseLayerStmt namespace toks tree
+parseLayerStmt namespace (Ident layer:toks) tree = inner toks [layer] tree
+    where
+        inner (Delim '.':Ident sublayer:toks') layers tree' = inner toks' (sublayer:layers) tree'
+        inner (Comma:toks') layers tree' =
+            parseLayerStmt namespace toks' $ registerLayer (namespaced layers) tree'
+        inner (Whitespace:toks') layers tree' = inner toks' layers tree'
+        inner (Semicolon:toks') layers tree' = (registerLayer (namespaced layers) tree', toks')
+        inner [] layers tree' = (registerLayer (namespaced layers) tree', [])
+        inner toks' _ _ = (tree, skipAtRule toks')
+        namespaced layers = namespace ++ reverse layers
+parseLayerStmt _ toks tree = (tree, skipAtRule toks)
+
+parseLayerBlock :: StyleSheet s => [Text] -> [Token] -> Tree ->
+    ([Text] -> [Int] -> s) -> (Tree, s, [Token])
+parseLayerBlock layers toks tree cb = (tree', parse' styles block, toks')
+    where
+        (block, toks') = scanBlock toks
+        tree' = registerLayer layers tree
+        styles = cb layers $ layerPath layers tree'
+
+newtype Tree = Tree (HashMap Text (Int, Tree))
+registerLayer :: [Text] -> Tree -> Tree
+registerLayer (layer:sublayers) (Tree self)
+    | Just (ix, subtree) <- self !? layer = Tree $ insert layer (ix, registerLayer sublayers subtree) self
+    | otherwise = Tree $ insert layer (succ $ size self, registerLayer sublayers $ Tree M.empty) self
+registerLayer [] self = self
+
+layerPath :: [Text] -> Tree -> [Int]
+layerPath (layer:sublayers) (Tree self)
+    | Just (ix, subtree) <- self !? layer = ix:layerPath sublayers subtree
+    | otherwise = [] -- Should have registered first...
+layerPath [] _ = []
+
+uniqueName :: [Text] -> Tree -> [Text]
+uniqueName (namespace:namespaces) (Tree self) 
+    | Just (_, subtree) <- self !? namespace = namespace:uniqueName namespaces subtree
+    | otherwise = replicate (length namespaces + 2) T.empty -- Should have registered first
+uniqueName [] (Tree self) = [T.pack $ show $ size self]
+
+emptyTree :: Tree
+emptyTree = Tree M.empty
diff --git a/src/Data/CSS/Syntax/Selector.hs b/src/Data/CSS/Syntax/Selector.hs
--- a/src/Data/CSS/Syntax/Selector.hs
+++ b/src/Data/CSS/Syntax/Selector.hs
@@ -1,119 +1,10 @@
 -- | Parses CSS selectors
 -- See `parseSelectors`
+--
+-- Backwards-compatibility module, this API has been moved out into "stylist-traits".
 module Data.CSS.Syntax.Selector(
         Selector(..), SimpleSelector(..), PropertyTest(..), PropertyFunc(..),
         parseSelectors
     ) where
 
-import Data.CSS.Syntax.Tokens
-import Data.CSS.Syntax.StylishUtil
-
-import Data.Text.Internal (Text(..))
-
--- | A CSS "selector" indicating which elements should be effected by CSS.
-data Selector = Element [SimpleSelector] -- ^ Selects a single element.
-    | Child Selector [SimpleSelector] -- ^ Represents "a > b" operator.
-    | Descendant Selector [SimpleSelector] -- ^ Represents "a b" operator.
-    | Adjacent Selector [SimpleSelector] -- ^ Represents "a + b" operator.
-    | Sibling Selector [SimpleSelector] -- ^ Represents "a ~ b" operator.
-    deriving (Show, Eq)
--- | An individual test comprising a CSS stylesheet.
-data SimpleSelector = Tag Text -- ^ Matches a tagname, e.g. "a"
-    | Namespace Text
-    | Id Text -- ^ Matches the "id" attribute, e.g. "#header"
-    | Class Text -- ^ Matches the "class" attribute, e.g. ".ad"
-    | Property (Maybe Text) Text PropertyTest -- ^ Matches a specified property
-    | Psuedoclass Text [Token] -- ^ Matches psuedoclasses provided by the caller (via a nameless property).
-    deriving (Show, Eq)
--- | How should a property be matched.
-data PropertyTest = Exists -- ^ Matches whether an attribute actually exists, e.g. "[title]"
-    | Equals Text -- ^ Matches whether the attribute is exactly equal to the value, e.g. "="
-    | Suffix Text -- ^ Matches whether attribute ends with the given value, e.g. "$="
-    | Prefix Text -- ^ Matches whether attribute starts with the given value, e.g. "^="
-    | Substring Text -- ^ Matches whether the attribute contains the given value, e.g. "*="
-    | Include Text -- ^ Is one of the whitespace-seperated values the one specified? e.g. "~="
-    | Dash Text -- ^ Matches whitespace seperated values, or their "-"-seperated prefixes. e.g. "|="
-    | Callback PropertyFunc -- ^ Calls the given function to test this property.
-    deriving (Show, Eq)
--- | Caller-specified functions to extend property selection.
--- Has incorrect Show/Eq implementations so this rare exception doesn't break things.
-data PropertyFunc = PropertyFunc (String -> Bool)
-instance Show PropertyFunc where
-    show _ = "xx"
-instance Eq PropertyFunc where
-    _ == _ = False
-
--- | Parses a CSS selector.
-parseSelectors :: Parser [Selector]
-parseSelectors tokens = concatP (:) parseCompound parseSelectorsTail $ skipSpace tokens
-parseSelectorsTail :: Parser [Selector]
-parseSelectorsTail (Comma:tokens) = parseSelectors tokens
-parseSelectorsTail tokens = ([], tokens)
-parseCompound :: Parser Selector
-parseCompound tokens = parseCombinators (Element selector) tokens'
-    where (selector, tokens') = parseSelector tokens
-
-parseSelector' :: SimpleSelector -> Parser [SimpleSelector]
-parseSelector' op tokens = (op:selector, tokens')
-    where (selector, tokens') = parseSelector tokens
-
-parseSelector :: Parser [SimpleSelector]
-parseSelector (Ident ns:Delim '|':tokens) = parseSelector' (Namespace ns) tokens
-parseSelector (Delim '*':tokens) = parseSelector tokens
-parseSelector (Ident tag:tokens) = parseSelector' (Tag tag) tokens
-parseSelector (Hash _ i:tokens) = parseSelector' (Id i) tokens
-parseSelector (Delim '.':Ident class_:tokens) = parseSelector' (Class class_) tokens
-parseSelector (LeftSquareBracket:Ident ns:Delim '|':Ident prop:tokens) =
-        concatP appendPropertySel parsePropertySel parseSelector tokens
-    where appendPropertySel test selector = Property (Just ns) prop test : selector
-parseSelector (LeftSquareBracket:Ident prop:tokens) =
-        concatP appendPropertySel parsePropertySel parseSelector tokens
-    where appendPropertySel test selector = Property Nothing prop test : selector
-parseSelector (Colon:Ident p:ts) = parseSelector' (Psuedoclass p []) ts
-parseSelector (Colon:Function fn:tokens) =
-        concatP appendPseudo scanBlock parseSelector tokens
-    where appendPseudo args selector = Psuedoclass fn args : selector
-parseSelector tokens = ([], tokens)
-
-parseCombinators' :: Selector -> Parser Selector
-parseCombinators' selector tokens = parseCombinators selector' tokens'
-    where (selector', tokens') = parseCombinator selector tokens
-parseCombinators :: Selector -> Parser Selector
-parseCombinators selector (Whitespace:tokens) = parseCombinators' selector tokens
-parseCombinators selector tokens@(Delim _:_) = parseCombinators' selector tokens
-parseCombinators selector tokens = (selector, tokens)
-
-parseCombinator' :: (Selector -> [SimpleSelector] -> Selector)
-                    -> Selector -> Parser Selector
-parseCombinator' cb selector tokens = (cb selector selector', tokens')
-    where (selector', tokens') = parseSelector $ skipSpace tokens
-parseCombinator :: Selector -> [Token] -> (Selector, [Token])
-parseCombinator selector (Whitespace:tokens) = parseCombinator selector tokens
-parseCombinator selector (Delim '>':tokens) = parseCombinator' Child selector tokens
-parseCombinator selector (Delim '~':tokens) = parseCombinator' Sibling selector tokens
-parseCombinator selector (Delim '+':tokens) = parseCombinator' Adjacent selector tokens
--- Take special care to avoid adding a trailing Descendant when not needed.
-parseCombinator selector tokens@(LeftCurlyBracket:_) = (selector, tokens)
-parseCombinator selector tokens@(RightCurlyBracket:_) = (selector, tokens)
-parseCombinator selector tokens@(RightSquareBracket:_) = (selector, tokens)
-parseCombinator selector tokens@(Comma:_) = (selector, tokens)
-
-parseCombinator selector tokens@(RightParen:_) = (selector, tokens)
-parseCombinator selector [] = (selector, [])
-
-parseCombinator selector tokens = parseCombinator' Descendant selector tokens
-
-parsePropertySel :: Parser PropertyTest
-parsePropertySel (RightSquareBracket:tokens) = (Exists, tokens)
-parsePropertySel (Delim '=':tokens) = parsePropertyVal (Equals) tokens
-parsePropertySel (SuffixMatch:tokens) = parsePropertyVal (Suffix) tokens
-parsePropertySel (PrefixMatch:tokens) = parsePropertyVal (Prefix) tokens
-parsePropertySel (SubstringMatch:tokens) = parsePropertyVal (Substring) tokens
-parsePropertySel (IncludeMatch:tokens) = parsePropertyVal (Include) tokens
-parsePropertySel (DashMatch:tokens) = parsePropertyVal (Dash) tokens
-parsePropertySel tokens = (Exists, skipBlock tokens)
-
-parsePropertyVal :: (Text -> PropertyTest) -> Parser PropertyTest
-parsePropertyVal wrapper (Ident val:RightSquareBracket:tokens) = (wrapper val, tokens)
-parsePropertyVal wrapper (String val:RightSquareBracket:tokens) = (wrapper val, tokens)
-parsePropertyVal _ tokens = (Exists, skipBlock tokens)
+import Stylist.Parse.Selector
diff --git a/src/Data/CSS/Syntax/StyleSheet.hs b/src/Data/CSS/Syntax/StyleSheet.hs
--- a/src/Data/CSS/Syntax/StyleSheet.hs
+++ b/src/Data/CSS/Syntax/StyleSheet.hs
@@ -1,6 +1,8 @@
 {-# LANGUAGE OverloadedStrings #-}
 -- | Parses a CSS stylesheet
 -- See `StyleSheet` & `parseForURL`.
+--
+-- Backwards-compatability module, this API has been moved out into "stylist-traits".
 module Data.CSS.Syntax.StyleSheet (
         parse, parse', parseForURL, TrivialStyleSheet(..),
         StyleSheet(..), skipAtRule, scanAtRule, scanBlock, skipSpace,
@@ -11,141 +13,4 @@
         scanValue
     ) where
 
-import Data.CSS.Syntax.Tokens
-import Data.CSS.Syntax.Selector
-import Data.CSS.Syntax.StylishUtil
-
-import Data.Text.Internal (Text(..))
-import Data.Text (pack, unpack)
-import Network.URI (parseRelativeReference, relativeTo, uriToString, URI(..))
-
---------
----- Output type class
---------
--- | Describes how to store, and to some extent parse, CSS stylesheets.
--- These methods are used to construct the results from `parse`, etc.
-class StyleSheet s where
-    -- | Sets the stylesheet priority (useragent vs user vs author), optional.
-    setPriority :: Int -> s -> s
-    setPriority _ = id
-    -- | Stores a parsed selector+properties rule.
-    addRule :: s -> StyleRule -> s
-    -- | Stores and parses an identified at-rule.
-    addAtRule :: s -> Text -> [Token] -> (s, [Token])
-    addAtRule self _ tokens = (self, skipAtRule tokens)
-
--- | Stores the parsed selector*s*+proeprties rule.
-addRules :: StyleSheet ss => ss -> ([Selector], ([(Text, [Token])], Text)) -> ss
-addRules self (selector:selectors, val@(props, psuedoel)) = addRules self' (selectors, val)
-    where self' = addRule self $ StyleRule selector props psuedoel
-addRules self ([], _) = self
-
--- | The properties to set for elements matching the given selector.
-data StyleRule = StyleRule Selector [(Text, [Token])] Text deriving (Show, Eq)
-
--- | Gathers StyleRules into a list, mainly for testing.
-data TrivialStyleSheet = TrivialStyleSheet [StyleRule] deriving (Show, Eq)
-instance StyleSheet TrivialStyleSheet where
-    addRule (TrivialStyleSheet self) rule = TrivialStyleSheet $ rule:self
-
---------
----- Basic parsing
---------
--- | Parse a CSS stylesheet
-parse :: StyleSheet s => s -> Text -> s
-parse stylesheet source = parse' stylesheet $ tokenize source
-
--- | Parse a CSS stylesheet, resolving all URLs to absolute form.
-parseForURL :: StyleSheet s => s -> URI -> Text -> s
-parseForURL stylesheet base source = parse' stylesheet $ rewriteURLs $ tokenize source
-    where
-        rewriteURLs (Url text:toks)
-            | Just url <- parseRelativeReference $ unpack text =
-                Url (pack $ uriToString id (relativeTo url base) "") : rewriteURLs toks
-            | otherwise = Function "url" : RightParen : rewriteURLs toks
-        rewriteURLs (tok:toks) = tok : rewriteURLs toks
-        rewriteURLs [] = []
-
--- | Parse a tokenized (via `css-syntax`) CSS stylesheet
-parse' :: StyleSheet t => t -> [Token] -> t
--- Things to skip.
-parse' stylesheet (Whitespace:tokens) = parse' stylesheet tokens
-parse' stylesheet (CDO:tokens) = parse' stylesheet tokens
-parse' stylesheet (CDC:tokens) = parse' stylesheet tokens
-parse' stylesheet (Comma:tokens) = parse' stylesheet tokens -- TODO issue warnings.
-
-parse' stylesheet [] = stylesheet
-
-parse' stylesheet (AtKeyword kind:tokens) = parse' stylesheet' tokens'
-    where (stylesheet', tokens') = addAtRule stylesheet kind $ skipSpace tokens
-parse' stylesheet tokens = parse' (addRules stylesheet rule) tokens'
-    where (rule, tokens') = concatP (,) parseSelectors parseProperties tokens
-
---------
----- Property parsing
---------
--- | Parse "{key: value; ...}" property values, with a psuedoelement.
-parseProperties :: Parser ([(Text, [Token])], Text)
-parseProperties (LeftCurlyBracket:tokens) = noPsuedoel $ parseProperties' tokens
-parseProperties (Whitespace:tokens) = parseProperties tokens
-parseProperties (Colon:Colon:Ident n:tokens) = ((val, n), tokens')
-    where ((val, _), tokens') = parseProperties tokens
--- This error recovery is a bit overly conservative, but it's simple.
-parseProperties (_:tokens) = noPsuedoel ([], skipAtRule tokens)
-parseProperties [] = noPsuedoel ([], [])
-
-noPsuedoel :: (x, y) -> ((x, Text), y)
-noPsuedoel (val, tokens) = ((val, ""), tokens)
-
--- | Parse "key: value;"... property values, as per the HTML "style" property.
-parseProperties' :: Parser [(Text, [Token])]
-parseProperties' (Whitespace:tokens) = parseProperties' tokens
-parseProperties' (Ident name:tokens)
-    | Colon:tokens' <- skipSpace tokens =
-        concatP appendProp scanValue parseProperties' tokens'
-    where appendProp value props = (name, value):props
-parseProperties' (RightCurlyBracket:tokens) = ([], tokens)
-parseProperties' [] = ([], [])
-parseProperties' tokens = parseProperties' (skipValue tokens)
-
---------
----- Skipping/Scanning utilities
---------
--- | Returns tokens before & after an at-rule value, terminated after a curly-bracketed block or a semicolon.
-scanAtRule :: Parser [Token]
-scanAtRule (Semicolon:tokens) = ([Semicolon], tokens)
-scanAtRule tokens@(LeftCurlyBracket:_) = scanInner tokens $ \rest -> ([], rest)
-
-scanAtRule tokens@(LeftParen:_) = scanInner tokens scanValue
-scanAtRule tokens@(Function _:_) = scanInner tokens scanValue
-scanAtRule tokens@(LeftSquareBracket:_) = scanInner tokens scanValue
--- To ensure parens are balanced, should already be handled.
-scanAtRule (RightCurlyBracket:tokens) = ([], RightCurlyBracket:tokens)
-scanAtRule (RightParen:tokens) = ([], RightParen:tokens)
-scanAtRule (RightSquareBracket:tokens) = ([], RightSquareBracket:tokens)
-
-scanAtRule tokens = capture scanAtRule tokens
-
--- | Returns tokens after an at-rule, as per `scanAtRule`.
-skipAtRule :: [Token] -> [Token]
-skipAtRule tokens = snd $ scanAtRule tokens
-
--- | Returns tokens before & after a semicolon.
-scanValue :: Parser [Token]
-scanValue (Semicolon:tokens) = ([], tokens)
-scanValue (Whitespace:tokens) = scanValue tokens
-
-scanValue tokens@(LeftCurlyBracket:_) = scanInner tokens scanValue
-scanValue tokens@(LeftParen:_) = scanInner tokens scanValue
-scanValue tokens@(Function _:_) = scanInner tokens scanValue
-scanValue tokens@(LeftSquareBracket:_) = scanInner tokens scanValue
--- To ensure parens are balanced, should already be handled.
-scanValue (RightCurlyBracket:tokens) = ([], RightCurlyBracket:tokens)
-scanValue (RightParen:tokens) = ([], RightParen:tokens)
-scanValue (RightSquareBracket:tokens) = ([], RightSquareBracket:tokens)
-
-scanValue tokens = capture scanValue tokens
-
--- | Returns tokens after a semicolon.
-skipValue :: [Token] -> [Token]
-skipValue tokens = snd $ scanValue tokens
+import Stylist.Parse
diff --git a/src/Data/CSS/Syntax/StylishUtil.hs b/src/Data/CSS/Syntax/StylishUtil.hs
deleted file mode 100644
--- a/src/Data/CSS/Syntax/StylishUtil.hs
+++ /dev/null
@@ -1,54 +0,0 @@
--- | Utility parser combinators for parsing CSS stylesheets.
-module Data.CSS.Syntax.StylishUtil(
-        concatP, capture, skipSpace,
-        scanBlock, skipBlock, scanInner,
-        Parser
-    ) where
-
-import Data.CSS.Syntax.Tokens
-
--- | A simple parser combinator type.
-type Parser x = [Token] -> (x, [Token])
-
--- | Chains two parser combinators together.
-concatP :: (a -> b -> c) -> Parser a -> Parser b -> Parser c
-concatP join left right tokens = (join x y, remainder)
-    where
-        (x, tokens') = left tokens
-        (y, remainder) = right tokens'
-
--- | "captures" the token being parsed into the returned output.
-capture :: Parser [Token] -> Parser [Token]
-capture cb (token:tokens) = (token:captured, tokens')
-   where (captured, tokens') = cb tokens
-capture _ [] = ([], [])
-
--- | Removes preceding `Whitespace` tokens.
-skipSpace :: [Token] -> [Token]
-skipSpace (Whitespace:tokens) = skipSpace tokens
-skipSpace tokens = tokens
-
--- | Returns tokens until the next unbalanced closing brace.
-scanBlock :: Parser [Token]
--- TODO assert closing tags are correct
---    But what should the error recovery be?
-scanBlock (RightCurlyBracket:tokens) = ([RightCurlyBracket], tokens)
-scanBlock (RightParen:tokens) = ([RightParen], tokens)
-scanBlock (RightSquareBracket:tokens) = ([RightSquareBracket], tokens)
-
-scanBlock tokens@(LeftCurlyBracket:_) = scanInner tokens scanBlock
-scanBlock tokens@(LeftParen:_) = scanInner tokens scanBlock
-scanBlock tokens@(Function _:_) = scanInner tokens scanBlock
-scanBlock tokens@(LeftSquareBracket:_) = scanInner tokens scanBlock
-
-scanBlock tokens = capture scanBlock tokens
-
--- | Returns tokens after the next unbalanced closing brace.
-skipBlock :: [Token] -> [Token]
-skipBlock tokens = snd $ scanBlock tokens
-
--- | Parses a block followed by the given combinator, returning the tokens the matched.
-scanInner :: [Token] -> Parser [Token] -> ([Token], [Token])
-scanInner (token:tokens) cb = concatP gather scanBlock cb tokens
-    where gather x y = token : x ++ y
-scanInner [] _ = error "Expected a token to capture."
diff --git a/stylist.cabal b/stylist.cabal
--- a/stylist.cabal
+++ b/stylist.cabal
@@ -10,7 +10,7 @@
 -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             2.4.0.2
+version:             2.5.0.0
 
 -- A short (one-line) description of the package.
 synopsis:            Apply CSS styles to a document tree.
@@ -59,10 +59,10 @@
                        Data.CSS.Preprocessor.Assets, Data.CSS.Preprocessor.Text, Data.CSS.Preprocessor.PsuedoClasses
   
   -- Modules included in this library but not exported.
-  other-modules:       Data.CSS.Syntax.StylishUtil,
-                       Data.CSS.Style.Importance, Data.CSS.Style.Common, Data.CSS.Style.Cascade,
+  other-modules:       Data.CSS.Style.Importance, Data.CSS.Style.Common, Data.CSS.Style.Cascade,
                        Data.CSS.Style.Selector.Index, Data.CSS.Style.Selector.Interpret,
-                           Data.CSS.Style.Selector.Specificity, Data.CSS.Style.Selector.LowerWhere
+                           Data.CSS.Style.Selector.Specificity, Data.CSS.Style.Selector.LowerWhere,
+                       Data.CSS.Syntax.AtLayer
   
   -- LANGUAGE extensions used by modules in this package.
   -- other-extensions:    
@@ -71,7 +71,7 @@
   build-depends:       base >=4.9 && <5, css-syntax >=0.1 && <0.2, text,
                         unordered-containers >= 0.2 && <0.3, hashable,
                         network-uri >= 2.6 && <2.7, async >= 2.1 && <2.3,
-                        regex-tdfa >= 1.3
+                        regex-tdfa >= 1.3, stylist-traits >= 0.1 && < 0.2
   
   -- Directories containing source files.
   hs-source-dirs:      src
@@ -82,6 +82,13 @@
   ghc-options: -Wall
   
 test-suite test-stylist
+  other-modules:
+    Data.CSS.Preprocessor.Conditions, Data.CSS.Preprocessor.Conditions.Expr, Data.CSS.Preprocessor.Text,
+    Data.CSS.Style.Cascade, Data.CSS.Style.Common, Data.CSS.Style.Importance,
+    Data.CSS.Style.Selector.Index, Data.CSS.Style.Selector.Interpret,
+        Data.CSS.Style.Selector.LowerWhere, Data.CSS.Style.Selector.Specificity,
+    Data.CSS.StyleTree, Data.CSS.Syntax.AtLayer
+
   hs-source-dirs:       src test
   default-language:     Haskell2010
   type:     exitcode-stdio-1.0
@@ -91,5 +98,4 @@
                         unordered-containers >= 0.2 && <0.3, hashable,
                         network-uri >= 2.6 && <2.7, async >= 2.1 && <2.3,
                         regex-tdfa >= 1.3, hspec, QuickCheck,
-                        scientific >= 0.3 && <1.0, regex-tdfa >= 1.3
-  ghc-options: -Wall
+                        scientific >= 0.3 && <1.0, regex-tdfa >= 1.3, stylist-traits >= 0.1 && < 0.2
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -3,6 +3,7 @@
 
 import Test.Hspec
 import Data.HashMap.Strict
+import qualified Data.HashMap.Lazy as L
 import Data.Maybe (fromJust)
 import Network.URI
 import Data.Scientific (toRealFloat)
@@ -11,6 +12,8 @@
 import Data.CSS.Syntax.StyleSheet (parse, StyleSheet(..), TrivialStyleSheet(..), scanAtRule, scanValue)
 import Data.CSS.Syntax.Selector
 
+import Data.CSS.Syntax.AtLayer
+
 import Data.CSS.Style.Common
 import Data.CSS.Style.Selector.Index
 import Data.CSS.Style.Selector.Interpret
@@ -98,7 +101,7 @@
                 ]
     describe "Style Index" $ do
         it "Retrieves appropriate styles" $ do
-            let index = addStyleRule styleIndex 0 $ styleRule' sampleRule
+            let index = addStyleRule styleIndex [0] $ styleRule' sampleRule
             let element = ElementNode {
                 name = "a",
                 namespace = "",
@@ -121,17 +124,17 @@
             rulesForElement index element2 `shouldBe` []
 
             let rule1 = StyleRule (Element [Class "external"]) [("color", [Ident "green"])] ""
-            let index1 = addStyleRule styleIndex 0 $ styleRule' rule1
+            let index1 = addStyleRule styleIndex [0] $ styleRule' rule1
             rulesForElement index1 element `shouldBe` [rule1]
             rulesForElement index1 element2 `shouldBe` []
 
             let rule2 = StyleRule (Element [Id "mysite"]) [("color", [Ident "green"])] ""
-            let index2 = addStyleRule styleIndex 0 $ styleRule' rule2
+            let index2 = addStyleRule styleIndex [0] $ styleRule' rule2
             rulesForElement index2 element `shouldBe` [rule2]
             rulesForElement index2 element2 `shouldBe` []
 
             let rule3 = StyleRule (Element [Property Nothing "href" $ Prefix "https://"]) [("color", [Ident "green"])] ""
-            let index3 = addStyleRule styleIndex 0 $ styleRule' rule3
+            let index3 = addStyleRule styleIndex [0] $ styleRule' rule3
             rulesForElement index3 element `shouldBe` [rule3]
             rulesForElement index3 element2 `shouldBe` []
     describe "Selector Compiler" $ do
@@ -274,8 +277,8 @@
                 previous = Nothing,
                 attributes = [Attribute "class" "" "link"]
             }
-            let rules = parse (queryable {priority = 1}) "a {color: green}"
-            let rules2 = parse (rules {priority = 2}) "a {color: red}" :: QueryableStyleSheet (VarParser TrivialPropertyParser)
+            let rules = parse (queryable {priority = [1]}) "a {color: green}"
+            let rules2 = parse (rules {priority = [2]}) "a {color: red}" :: QueryableStyleSheet (VarParser TrivialPropertyParser)
             let VarParser _ (TrivialPropertyParser style) = cascade rules2 el [] temp::(VarParser TrivialPropertyParser)
             style ! "color" `shouldBe` [Ident "green"]
 
@@ -286,8 +289,8 @@
                 previous = Nothing,
                 attributes = [Attribute "class" "" "link"]
             }
-            let rules' = parse (queryable {priority = 1}) "a {color: red}"
-            let rules2' = parse (rules' {priority = 2}) "a {color: green !important}" :: QueryableStyleSheet (VarParser TrivialPropertyParser)
+            let rules' = parse (queryable {priority = [1]}) "a {color: red}"
+            let rules2' = parse (rules' {priority = [2]}) "a {color: green !important}" :: QueryableStyleSheet (VarParser TrivialPropertyParser)
             let VarParser _ (TrivialPropertyParser style') = cascade rules2' el' [] temp::(VarParser TrivialPropertyParser)
             style' ! "color" `shouldBe` [Ident "green"]
         it "respects overrides" $ do
@@ -504,6 +507,18 @@
 
             let textStyle4 = fromJust $ longhand temp textStyle1 "counter-increment" [Ident "-rhaps-ol"]
             style (Txt.resolve $ StyleTree textStyle4 []) `shouldBe` TrivialPropertyParser (fromList [("content", [String "1"])])
+    describe "@layer" $ do
+        it "Deduplicates names" $ do
+            let init = Tree L.empty
+            let tree2 = registerLayer ["LeagueOfGentlemenAdventurers", "The Stranger"] init
+            let tree3 = registerLayer ["JusticeUnion", "TomTomorrow"] tree2
+            let tree4 = registerLayer ["HomeTeam", "DocRocket"] tree3
+            let tree5 = registerLayer ["JusticeUnion", "TheOgre"] tree4
+
+            layerPath ["JusticeUnion", "TheOgre"] tree5 `shouldBe` [2, 2]
+            layerPath ["HomeTeam"] tree5 `shouldBe` [3]
+            layerPath ["LeagueOfGentlemenAdventurers"] tree5 `shouldBe` [1]
+            uniqueName ["HomeTeam"] tree5 `shouldBe` ["HomeTeam", "1"]
 
 styleIndex :: StyleIndex
 styleIndex = new
