Index: src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 15081)
+++ src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(working copy)
@@ -866,7 +866,9 @@
             env.clearSelectorMatchingInformation();
             if (partialSelection && r.selector instanceof Selector.ChildOrParentSelector) {
                 ChildOrParentSelector sel = (Selector.ChildOrParentSelector) r.selector;
-                if (sel.type == ChildOrParentSelectorType.ELEMENT_OF && p.getDataSet() != null) {
+                boolean needEnclosing = sel.type == ChildOrParentSelectorType.SUBSET_OR_EQUAL
+                        || sel.type == ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL;
+                if (needEnclosing && p.getDataSet() != null) {
                     List<OsmPrimitive> toCheck = new ArrayList<>();
                     toCheck.addAll(p.getDataSet().searchWays(p.getBBox()));
                     toCheck.addAll(p.getDataSet().searchRelations(p.getBBox()));
Index: src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 15081)
+++ src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(working copy)
@@ -216,7 +216,10 @@
 |   < CARET: "^" >
 |   < FULLSTOP: "." >
 |   < DEG: "°" >
-|   < ELEMENT_OF: "∈" >
+|   < SUBSET_OR_EQUAL: ["∈","⊆"] >
+|   < NOT_SUBSET_OR_EQUAL: "⊈" >
+|   < SUPERSET_OR_EQUAL: "⊇" >
+|   < NOT_SUPERSET_OR_EQUAL: "⊉" >
 |   < CROSSING: "⧉" >
 |   < PERCENT: "%" >
 |   < COMMENT_START: "/*" > : COMMENT
@@ -690,8 +693,14 @@
                 )
                 ( ( c=condition(Context.LINK) | c=class_or_pseudoclass(Context.LINK) ) { conditions.add(c); } )*
             |
-                <ELEMENT_OF> { type = Selector.ChildOrParentSelectorType.ELEMENT_OF; }
+                <SUBSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.SUBSET_OR_EQUAL; }
             |
+                <NOT_SUBSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL; }
+            |
+                <SUPERSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.SUPERSET_OR_EQUAL; }
+            |
+                <NOT_SUPERSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL; }
+            |
                 <CROSSING> { type = Selector.ChildOrParentSelectorType.CROSSING; }
             )
             w()
Index: src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 15081)
+++ src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(working copy)
@@ -117,7 +117,7 @@
      * @see ChildOrParentSelector
      */
     enum ChildOrParentSelectorType {
-        CHILD, PARENT, ELEMENT_OF, CROSSING, SIBLING
+        CHILD, PARENT, SUBSET_OR_EQUAL, NOT_SUBSET_OR_EQUAL, SUPERSET_OR_EQUAL, NOT_SUPERSET_OR_EQUAL, CROSSING, SIBLING,
     }
 
     /**
@@ -312,6 +312,9 @@
             }
         }
 
+        /**
+         * Finds elements which are inside the right element, collects those in {@code children}
+         */
         private class ContainsFinder extends AbstractFinder {
             protected List<IPrimitive> toCheck;
 
@@ -348,6 +351,61 @@
             }
         }
 
+        /**
+         * Finds elements which are inside the left element, or in other words, it finds elements enclosing e.osm.
+         * The found enclosing elements are collected in {@code e.children}.
+         */
+        private class InsideOrEqualFinder extends AbstractFinder {
+
+            protected InsideOrEqualFinder(Environment e) {
+                super(e);
+            }
+
+            @Override
+            public void visit(IWay<?> w) {
+                if (left.matches(new Environment(w).withParent(e.osm))
+                        && w.getBBox().bounds(e.osm.getBBox())
+                        && !Geometry.filterInsidePolygon(Collections.singletonList(e.osm), w).isEmpty()) {
+                    addToChildren(e, w);
+                }
+            }
+
+            @Override
+            public void visit(IRelation<?> r) {
+                if (r instanceof Relation && r.isMultipolygon() && r.getBBox().bounds(e.osm.getBBox())
+                        && left.matches(new Environment(r).withParent(e.osm))
+                        && !Geometry.filterInsideMultipolygon(Collections.singletonList(e.osm), (Relation) r).isEmpty()) {
+                    addToChildren(e, r);
+                }
+            }
+        }
+
+        private void visitBBox(Environment e, AbstractFinder finder) {
+            boolean withNodes = finder instanceof ContainsFinder;
+            if (left instanceof OptimizedGeneralSelector) {
+                if (withNodes && ((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.NODE)) {
+                    finder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
+                }
+                if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.WAY)) {
+                    finder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
+                }
+                if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.RELATION)) {
+                    finder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
+                }
+            } else {
+                if (withNodes) {
+                    finder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
+                }
+                finder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
+                finder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
+            }
+        }
+
+        private static boolean isArea(IPrimitive p) {
+            return (p instanceof IWay && ((IWay<?>) p).isClosed() && ((IWay<?>) p).getNodesCount() >= 4)
+                    || (p instanceof IRelation && p.isMultipolygon() && !p.isIncomplete());
+        }
+
         @Override
         public boolean matches(Environment e) {
 
@@ -354,33 +412,32 @@
             if (!right.matches(e))
                 return false;
 
-            if (ChildOrParentSelectorType.ELEMENT_OF == type) {
+            if (ChildOrParentSelectorType.SUBSET_OR_EQUAL == type || ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL == type) {
 
-                if (e.osm instanceof INode || e.osm.getDataSet() == null) {
-                    // nodes cannot contain elements
-                    return false;
+                if (e.osm.getDataSet() == null || !isArea(e.osm)) {
+                    // only areas can contain elements
+                    return ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL == type;
                 }
-
                 ContainsFinder containsFinder = new ContainsFinder(e);
                 e.parent = e.osm;
 
-                if (left instanceof OptimizedGeneralSelector) {
-                    if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.NODE)) {
-                        containsFinder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
-                    }
-                    if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.WAY)) {
-                        containsFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
-                    }
-                    if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.RELATION)) {
-                        containsFinder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
-                    }
-                } else {
-                    // use slow test
-                    containsFinder.visit(e.osm.getDataSet().allPrimitives());
-                }
+                visitBBox(e, containsFinder);
                 containsFinder.execGeometryTests();
-                return e.children != null;
+                return ChildOrParentSelectorType.SUBSET_OR_EQUAL == type ? e.children != null : e.children == null;
 
+            } else if (ChildOrParentSelectorType.SUPERSET_OR_EQUAL == type || ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL == type) {
+
+                if (e.osm.getDataSet() == null || (e.osm instanceof INode && ((INode) e.osm).getCoor() == null)
+                        || (!(e.osm instanceof INode) && !isArea(e.osm))) {
+                    return ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL == type;
+                }
+
+                InsideOrEqualFinder insideOrEqualFinder = new InsideOrEqualFinder(e);
+                e.parent = e.osm;
+
+                visitBBox(e, insideOrEqualFinder);
+                return ChildOrParentSelectorType.SUPERSET_OR_EQUAL == type ? e.children != null : e.children == null;
+
             } else if (ChildOrParentSelectorType.CROSSING == type && e.osm instanceof IWay) {
                 e.parent = e.osm;
                 if (right instanceof OptimizedGeneralSelector
Index: test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java	(revision 15081)
+++ test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java	(working copy)
@@ -186,11 +186,31 @@
         assertTrue(selector.matches(e));
     }
 
+    /**
+     * Test inside/contains selectors (spatial test)
+     */
     @Test
     public void testContains() throws Exception {
         ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get("data_nodist/amenity-in-amenity.osm")), null);
         ChildOrParentSelector css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ∈ *[amenity] {}");
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
         assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
         assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
+        css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ⊆  *[amenity] {}");
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
+        css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ⊈  *[amenity] {}");
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
+        css = parse("*[tag(\"amenity\") = parent_tag(\"amenity\")] ⊇  *[amenity] {}");
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
+        css = parse("*[tag(\"amenity\") = parent_tag(\"amenity\")] ⊉  *[amenity] {}");
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
     }
 }
