Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java	(revision 15288)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java	(revision 15289)
@@ -25,4 +25,6 @@
 import java.util.Collection;
 import java.util.List;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
 
 import javax.swing.AbstractAction;
@@ -63,4 +65,6 @@
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.MapPaintSylesUpdateListener;
 import org.openstreetmap.josm.gui.mappaint.StyleSetting;
+import org.openstreetmap.josm.gui.mappaint.StyleSetting.StyleSettingGroup;
+import org.openstreetmap.josm.gui.mappaint.StyleSettingGroupGui;
 import org.openstreetmap.josm.gui.mappaint.StyleSettingGuiFactory;
 import org.openstreetmap.josm.gui.mappaint.StyleSource;
@@ -691,13 +695,18 @@
             add(setMenu);
 
-            int sel = tblStyles.getSelectionModel().getLeadSelectionIndex();
-            StyleSource style = null;
-            if (sel >= 0 && sel < model.getRowCount()) {
-                style = model.getRow(sel);
-            }
+            final int sel = tblStyles.getSelectionModel().getLeadSelectionIndex();
+            final StyleSource style = sel >= 0 && sel < model.getRowCount() ? model.getRow(sel) : null;
             if (style == null || style.settings.isEmpty()) {
                 setMenu.setEnabled(false);
             } else {
-                for (StyleSetting s : style.settings) {
+                // Add settings groups
+                for (Entry<StyleSettingGroup, List<StyleSetting>> e : style.settingGroups.entrySet()) {
+                    new StyleSettingGroupGui(e.getKey(), e.getValue()).addMenuEntry(setMenu);
+                }
+                // Add settings not in groups
+                final List<StyleSetting> allStylesInGroups = style.settingGroups.values().stream()
+                        .flatMap(l -> l.stream()).collect(Collectors.toList());
+                for (StyleSetting s : style.settings.stream()
+                        .filter(s -> !allStylesInGroups.contains(s)).collect(Collectors.toList())) {
                     StyleSettingGuiFactory.getStyleSettingGui(s).addMenuEntry(setMenu);
                 }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/BooleanStyleSettingGui.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/BooleanStyleSettingGui.java	(revision 15288)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/BooleanStyleSettingGui.java	(revision 15289)
@@ -7,5 +7,4 @@
 
 import javax.swing.AbstractAction;
-import javax.swing.Action;
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JMenu;
@@ -32,18 +31,31 @@
     }
 
+    static class BooleanStyleSettingCheckBoxMenuItem extends JCheckBoxMenuItem {
+        boolean noRepaint = false;
+
+        public BooleanStyleSettingCheckBoxMenuItem(BooleanStyleSetting setting) {
+            setAction(new AbstractAction(setting.label) {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    setting.setValue(isSelected());
+                    if (!noRepaint) {
+                        MainApplication.worker.submit(new MapPaintStyleLoader(Arrays.asList(setting.parentStyle)));
+                    }
+                }
+            });
+            setSelected((boolean) setting.getValue());
+            setUI(new StayOpenCheckBoxMenuItemUI());
+        }
+
+        public void doClickWithoutRepaint(int pressTime) {
+            noRepaint = true;
+            doClick(pressTime);
+            noRepaint = false;
+        }
+    }
+
     @Override
     public void addMenuEntry(JMenu menu) {
-        final JCheckBoxMenuItem item = new JCheckBoxMenuItem();
-        Action a = new AbstractAction(setting.label) {
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                setting.setValue(item.isSelected());
-                MainApplication.worker.submit(new MapPaintStyleLoader(Arrays.asList(setting.parentStyle)));
-            }
-        };
-        item.setAction(a);
-        item.setSelected((boolean) setting.getValue());
-        item.setUI(new StayOpenCheckBoxMenuItemUI());
-        menu.add(item);
+        menu.add(new BooleanStyleSettingCheckBoxMenuItem(setting));
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSetting.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSetting.java	(revision 15288)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSetting.java	(revision 15289)
@@ -2,5 +2,12 @@
 package org.openstreetmap.josm.gui.mappaint;
 
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.swing.Icon;
+
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
 import org.openstreetmap.josm.tools.Logging;
 
@@ -34,16 +41,58 @@
 
     /**
+     * Superclass of style settings and groups.
+     * @since 15289
+     */
+    abstract class LabeledStyleSetting implements Comparable<LabeledStyleSetting> {
+        public final StyleSource parentStyle;
+        public final String label;
+
+        LabeledStyleSetting(StyleSource parentStyle, String label) {
+            this.parentStyle = Objects.requireNonNull(parentStyle);
+            this.label = Objects.requireNonNull(label);
+        }
+
+        @Override
+        public int compareTo(LabeledStyleSetting o) {
+            return label.compareTo(o.label);
+        }
+    }
+
+    /**
+     * A style setting group.
+     * @since 15289
+     */
+    class StyleSettingGroup extends LabeledStyleSetting {
+        public final String key;
+        public final Icon icon;
+
+        public StyleSettingGroup(StyleSource parentStyle, String label, String key, Icon icon) {
+            super(parentStyle, label);
+            this.key = Objects.requireNonNull(key);
+            this.icon = icon;
+        }
+
+        public static StyleSettingGroup create(Cascade c, StyleSource parentStyle, String key) {
+            String label = c.get("label", null, String.class);
+            if (label == null) {
+                Logging.warn("property 'label' required for boolean style setting");
+                return null;
+            }
+            Icon icon = Optional.ofNullable(c.get("icon", null, String.class))
+                    .map(s -> ImageProvider.get(s, ImageSizes.MENU)).orElse(null);
+            return new StyleSettingGroup(parentStyle, label, key, icon);
+        }
+    }
+
+    /**
      * A style setting for boolean value (yes / no).
      */
-    class BooleanStyleSetting implements StyleSetting, Comparable<BooleanStyleSetting> {
-        public final StyleSource parentStyle;
+    class BooleanStyleSetting extends LabeledStyleSetting implements StyleSetting {
         public final String prefKey;
-        public final String label;
         public final boolean def;
 
         public BooleanStyleSetting(StyleSource parentStyle, String prefKey, String label, boolean def) {
-            this.parentStyle = parentStyle;
-            this.prefKey = prefKey;
-            this.label = label;
+            super(parentStyle, label);
+            this.prefKey = Objects.requireNonNull(prefKey);
             this.def = def;
         }
@@ -82,9 +131,4 @@
             }
         }
-
-        @Override
-        public int compareTo(BooleanStyleSetting o) {
-            return label.compareTo(o.label);
-        }
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSettingGroupGui.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSettingGroupGui.java	(revision 15289)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSettingGroupGui.java	(revision 15289)
@@ -0,0 +1,70 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.swing.AbstractAction;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.mappaint.BooleanStyleSettingGui.BooleanStyleSettingCheckBoxMenuItem;
+import org.openstreetmap.josm.gui.mappaint.StyleSetting.StyleSettingGroup;
+import org.openstreetmap.josm.gui.mappaint.loader.MapPaintStyleLoader;
+import org.openstreetmap.josm.gui.util.StayOpenCheckBoxMenuItemUI;
+
+/**
+ * GUI elements for a {@link StyleSettingGroup} class.
+ * @since 15289
+ */
+public class StyleSettingGroupGui implements StyleSettingGui {
+
+    private final StyleSettingGroup group;
+    private final List<StyleSetting> settings;
+
+    /**
+     * Constructs a new {@code StyleSettingGroupGui}.
+     * @param group style setting group
+     * @param settings list of style settings in this group
+     */
+    public StyleSettingGroupGui(StyleSettingGroup group, List<StyleSetting> settings) {
+        this.group = Objects.requireNonNull(group);
+        this.settings = Objects.requireNonNull(settings);
+    }
+
+    @Override
+    public void addMenuEntry(JMenu menu) {
+        final JMenu submenu = new JMenu();
+        submenu.setText(group.label);
+        submenu.setIcon(group.icon);
+        // Add the "toggle all settings" action
+        if (settings.size() >= 2) {
+            JMenuItem item = new JMenuItem(new AbstractAction(tr("Toggle all settings")) {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    List<BooleanStyleSettingCheckBoxMenuItem> items = Arrays.stream(submenu.getMenuComponents())
+                            .filter(c -> c instanceof BooleanStyleSettingCheckBoxMenuItem)
+                            .map(c -> (BooleanStyleSettingCheckBoxMenuItem) c)
+                            .collect(Collectors.toList());
+                    final boolean select = items.stream().anyMatch(cbi -> !cbi.isSelected());
+                    items.stream().filter(cbi -> select != cbi.isSelected()).forEach(cbi -> cbi.doClickWithoutRepaint(0));
+                    MainApplication.worker.submit(new MapPaintStyleLoader(Arrays.asList(group.parentStyle)));
+                }
+            });
+            item.setUI(new StayOpenCheckBoxMenuItemUI());
+            submenu.add(item);
+            submenu.addSeparator();
+        }
+        // Add individual settings
+        for (StyleSetting s : settings) {
+            StyleSettingGuiFactory.getStyleSettingGui(s).addMenuEntry(submenu);
+        }
+        menu.add(submenu);
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSource.java	(revision 15288)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSource.java	(revision 15289)
@@ -15,4 +15,5 @@
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -24,4 +25,5 @@
 import org.openstreetmap.josm.data.preferences.sources.SourceType;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
+import org.openstreetmap.josm.gui.mappaint.StyleSetting.StyleSettingGroup;
 import org.openstreetmap.josm.io.CachedFile;
 import org.openstreetmap.josm.tools.ImageOverlay;
@@ -65,4 +67,8 @@
      */
     public Map<String, Object> settingValues = new HashMap<>();
+    /**
+     * Map of settings per group.
+     */
+    public final Map<StyleSettingGroup, List<StyleSetting>> settingGroups = new TreeMap<>();
 
     /**
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 15288)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 15289)
@@ -49,4 +49,5 @@
 import org.openstreetmap.josm.gui.mappaint.StyleSetting;
 import org.openstreetmap.josm.gui.mappaint.StyleSetting.BooleanStyleSetting;
+import org.openstreetmap.josm.gui.mappaint.StyleSetting.StyleSettingGroup;
 import org.openstreetmap.josm.gui.mappaint.StyleSource;
 import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.KeyCondition;
@@ -502,4 +503,5 @@
                     case Selector.BASE_META:
                     case Selector.BASE_SETTING:
+                    case Selector.BASE_SETTINGS:
                         break;
                     default:
@@ -576,27 +578,44 @@
     }
 
+    private static void loadSettings(MapCSSRule r, GeneralSelector gs, Environment env) {
+        if (gs.matchesConditions(env)) {
+            env.layer = null;
+            env.layer = gs.getSubpart().getId(env);
+            r.execute(env);
+        }
+    }
+
     private void loadSettings() {
         settings.clear();
         settingValues.clear();
+        settingGroups.clear();
         MultiCascade mc = new MultiCascade();
+        MultiCascade mcGroups = new MultiCascade();
         Node n = new Node();
-        String code = LanguageInfo.getJOSMLocaleCode();
-        n.put("lang", code);
+        n.put("lang", LanguageInfo.getJOSMLocaleCode());
         // create a fake environment to read the meta data block
         Environment env = new Environment(n, mc, "default", this);
-
+        Environment envGroups = new Environment(n, mcGroups, "default", this);
+
+        // Parse rules
         for (MapCSSRule r : rules) {
             if (r.selector instanceof GeneralSelector) {
                 GeneralSelector gs = (GeneralSelector) r.selector;
                 if (Selector.BASE_SETTING.equals(gs.getBase())) {
-                    if (!gs.matchesConditions(env)) {
-                        continue;
-                    }
-                    env.layer = null;
-                    env.layer = gs.getSubpart().getId(env);
-                    r.execute(env);
-                }
-            }
-        }
+                    loadSettings(r, gs, env);
+                } else if (Selector.BASE_SETTINGS.equals(gs.getBase())) {
+                    loadSettings(r, gs, envGroups);
+                }
+            }
+        }
+        // Load groups
+        for (Entry<String, Cascade> e : mcGroups.getLayers()) {
+            if ("default".equals(e.getKey())) {
+                Logging.warn("settings requires layer identifier e.g. 'settings::settings_group {...}'");
+                continue;
+            }
+            settingGroups.put(StyleSettingGroup.create(e.getValue(), this, e.getKey()), new ArrayList<>());
+        }
+        // Load settings
         for (Entry<String, Cascade> e : mc.getLayers()) {
             if ("default".equals(e.getKey())) {
@@ -610,9 +629,14 @@
                 set = BooleanStyleSetting.create(c, this, e.getKey());
             } else {
-                Logging.warn("Unknown setting type: "+type);
+                Logging.warn("Unknown setting type: {0}", type);
             }
             if (set != null) {
                 settings.add(set);
                 settingValues.put(e.getKey(), set.getValue());
+                String groupId = c.get("group", null, String.class);
+                if (groupId != null) {
+                    settingGroups.get(settingGroups.keySet().stream().filter(g -> g.key.equals(groupId)).findAny()
+                            .orElseThrow(() -> new IllegalArgumentException("Unknown settings group: " + groupId))).add(set);
+                }
             }
         }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 15288)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 15289)
@@ -74,4 +74,7 @@
     /** selector base for artificial bases created to use preferences. */
     String BASE_SETTING = "setting";
+
+    /** selector base for grouping settings. */
+    String BASE_SETTINGS = "settings";
 
     /**
@@ -703,4 +706,5 @@
             case "canvas": return BASE_CANVAS;
             case "setting": return BASE_SETTING;
+            case "settings": return BASE_SETTINGS;
             default:
                 throw new IllegalArgumentException(MessageFormat.format("Unknown MapCSS base selector {0}", base));
@@ -779,5 +783,5 @@
         @Override
         public String toString() {
-            return base + (Range.ZERO_TO_INFINITY.equals(range) ? "" : range) + Utils.join("", conds)
+            return base + (Range.ZERO_TO_INFINITY.equals(range) ? "" : range) + (conds != null ? Utils.join("", conds) : "")
                     + (subpart != null && subpart != Subpart.DEFAULT_SUBPART ? ("::" + subpart) : "");
         }
