Repository: equinor/webviz-config

Issue:
Missing global font in webviz default theme
After talking to @rubenthoms it is discovered that the current default theme is missing global font (https://github.com/equinor/webviz-config/tree/master/webviz_config/themes/default_assets).

This is not the case in (the currently only?) non-default theme https://github.com/equinor/webviz-config-equinor.

The default theme probably should be improved (has been as-is since the beginning of the project), including improved CSS, default global font as well as improved logo.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9169c6b..0ebac23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 - [#503](https://github.com/equinor/webviz-config/pull/503) - Added `__main__.py`. This will allow users to do `python -m webviz_config [...]` in addition to `webviz [...]`.
 - [#510](https://github.com/equinor/webviz-config/pull/510) - Added command line option `--debug` which enables selected underlying Dash development features.
+- [#496](https://github.com/equinor/webviz-config/pull/496) - Implemented automatic menu creation from YAML file with new `wcc` Menu component (see https://github.com/equinor/webviz-core-components/pull/154).
 
 ## [0.3.4] - 2021-08-30
 
diff --git a/examples/basic_example.yaml b/examples/basic_example.yaml
index 58f9940..a77077a 100644
--- a/examples/basic_example.yaml
+++ b/examples/basic_example.yaml
@@ -3,9 +3,13 @@
 
 title: Reek Webviz Demonstration
 
-pages:
+options:
+  menu:
+    initially_pinned: True
 
- - title: Front page
+layout:
+
+ - page: Front page
    content:
     - BannerImage:
         image: ./example_banner.png
@@ -13,27 +17,27 @@ pages:
     - Webviz created from configuration file.
     - Some other text, potentially with strange letters like Åre, Smørbukk Sør.
 
- - title: Markdown example
+ - page: Markdown example
    content:
     - Markdown:
         markdown_file: ./example-markdown.md
 
- - title: Table example
+ - page: Table example
    content:
     - DataTable:
         csv_file: ./example_data.csv
 
- - title: PDF example
+ - page: PDF example
    content:
     - EmbedPdf:
         pdf_file: ./example.pdf
 
- - title: Syntax highlighting example
+ - page: Syntax highlighting example
    content:
     - SyntaxHighlighter:
         filename: ./basic_example.yaml
 
- - title: Plot a table
+ - page: Plot a table
    content:
     - TablePlotter:
         csv_file: ./example_data.csv
@@ -64,7 +68,7 @@ pages:
           phone: +47 12345678
           email: some@email.com
 
- - title: Plot a table (locked)
+ - page: Plot a table (locked)
    content:
     - TablePlotter:
         csv_file: ./example_data.csv
@@ -79,7 +83,7 @@ pages:
           phone: 12345678
           email: someother@email.com
 
- - title: Pivot Table
+ - page: Pivot Table
    content:
     - PivotTable:
         csv_file: ./example_data.csv
diff --git a/examples/basic_example_advanced_menu.yaml b/examples/basic_example_advanced_menu.yaml
new file mode 100644
index 0000000..bc566ca
--- /dev/null
+++ b/examples/basic_example_advanced_menu.yaml
@@ -0,0 +1,109 @@
+# This file demonstrates the most basic usage of webviz in a FMU setting
+# The configuration files uses YAML (https://en.wikipedia.org/wiki/YAML).
+
+title: Reek Webviz Demonstration
+
+options:
+  menu:
+    show_logo: True
+    bar_position: left
+    drawer_position: left
+    initially_pinned: True
+
+layout:
+ - section: Section
+   content:
+    - page: Front page
+      icon: home
+      content:
+       - BannerImage:
+           image: ./example_banner.png
+           title: My banner image
+       - Webviz created from configuration file.
+       - Some other text, potentially with strange letters like Åre, Smørbukk Sør.
+
+    - group: Other
+      icon: label
+      content:
+      - page: Markdown example
+        content:
+         - Markdown:
+             markdown_file: ./example-markdown.md
+      
+      - page: PDF example
+        content:
+         - EmbedPdf:
+             pdf_file: ./example.pdf
+      
+      - page: Syntax highlighting example
+        content:
+         - SyntaxHighlighter:
+             filename: ./basic_example.yaml
+
+    - group: Tables
+      icon: table_chart
+      content:
+      - page: Table example
+        content:
+         - DataTable:
+             csv_file: ./example_data.csv
+      
+      - page: Plot a table
+        content:
+         - TablePlotter:
+             csv_file: ./example_data.csv
+             # Everything below are examples of optional settings
+             filter_cols:
+               - Well
+               - Segment
+               - Average permeability (D)
+             plot_options:        
+               type: bar
+               facet_col: Well
+               color: Segment
+               barmode: group
+             filter_defaults:
+               Well:
+                 - A-1H
+                 - A-2H
+                 - C-1H
+             column_color_discrete_maps:
+             # Supports css color codes, rgb and hex code.
+             # Note that hex code needs quotes '' to not be read as a comment
+               Segment:
+                 A: '#ff0000'
+                 B: rgb(0,255,0)
+                 C: blue
+             contact_person:
+               name: Ola Nordmann
+               phone: +47 12345678
+               email: some@email.com
+      
+      - page: Plot a table (locked)
+        content:
+         - TablePlotter:
+             csv_file: ./example_data.csv
+             lock: true
+             plot_options:
+               x: Well
+               y: Initial reservoir pressure (bar)
+               size: Average permeability (D)
+               facet_col: Segment
+             contact_person:
+               name: Kari Nordmann
+               phone: 12345678
+               email: someother@email.com
+      
+      - page: Pivot Table
+        content:
+         - PivotTable:
+             csv_file: ./example_data.csv
+             options:
+               cols:
+                 - Well
+               rows:
+                 - Segment
+               vals:
+                 - Average permeability (D)
+               aggregatorName: Average
+               rendererName: Table Heatmap
diff --git a/webviz_config/_config_parser.py b/webviz_config/_config_parser.py
index d26541b..56f2eb9 100644
--- a/webviz_config/_config_parser.py
+++ b/webviz_config/_config_parser.py
@@ -284,7 +284,6 @@ class ConfigParser:
         return page_id
 
     def clean_configuration(self) -> None:
-        # pylint: disable=too-many-branches
         """Various cleaning and checks of the raw configuration read
         from the user provided yaml configuration file.
         """
@@ -299,7 +298,7 @@ class ConfigParser:
         if "title" not in self.configuration:
             self.configuration["title"] = "Webviz - Powered by Dash"
 
-        if "pages" not in self.configuration:
+        if "pages" not in self.configuration and "layout" not in self.configuration:
             raise ParserError(
                 f"{terminal_colors.RED}{terminal_colors.BOLD}"
                 "The configuration file does not have "
@@ -307,7 +306,9 @@ class ConfigParser:
                 f"{terminal_colors.END}"
             )
 
-        if not isinstance(self.configuration["pages"], list):
+        if "pages" in self.configuration and not isinstance(
+            self.configuration["pages"], list
+        ):
             raise ParserError(
                 f"{terminal_colors.RED}{terminal_colors.BOLD}"
                 "The configuration input belonging to the "
@@ -315,65 +316,27 @@ class ConfigParser:
                 f"{terminal_colors.END}"
             )
 
-        for page_number, page in enumerate(self.configuration["pages"]):
-
-            if "title" not in page:
-                raise ParserError(
-                    f"{terminal_colors.RED}{terminal_colors.BOLD}"
-                    f"Page number {page_number + 1} does "
-                    "not have the title specified."
-                    f"{terminal_colors.END}"
-                )
-
-            if "id" not in page:
-                page["id"] = self._generate_page_id(page["title"])
-            elif page["id"] in self._page_ids:
-                raise ParserError(
-                    f"{terminal_colors.RED}{terminal_colors.BOLD}"
-                    "You have more than one page "
-                    "with the same `id`."
-                    f"{terminal_colors.END}"
-                )
-
-            self._page_ids.append(page["id"])
-
-            if "content" not in page:
-                page["content"] = []
-            elif not isinstance(page["content"], list):
-                raise ParserError(
-                    f"{terminal_colors.RED}{terminal_colors.BOLD}"
-                    "The content of page number "
-                    f"{page_number + 1} should be a list."
-                    f"{terminal_colors.END}"
-                )
-
-            plugins = [e for e in page["content"] if isinstance(e, dict)]
-
-            for plugin in plugins:
-                plugin_name = next(iter(plugin))
-                plugin_variables = next(iter(plugin.values()))
-                kwargs = {} if plugin_variables is None else {**plugin_variables}
+        if "pages" in self.configuration:
+            warnings.warn(
+                "The configuration keyword `pages` is going to be deprecated. "
+                "Consider using the new `layout` keyword instead.",
+                PendingDeprecationWarning,
+            )
 
-                if plugin_name not in ConfigParser.STANDARD_PLUGINS:
-                    raise ParserError(
-                        f"{terminal_colors.RED}{terminal_colors.BOLD}"
-                        "You have included a plugin with "
-                        f"name `{plugin_name}` in your "
-                        "configuration file. This is not a "
-                        "standard plugin."
-                        f"{terminal_colors.END}"
-                    )
+        if "layout" in self.configuration and not isinstance(
+            self.configuration["layout"], list
+        ):
+            raise ParserError(
+                f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                "The configuration input belonging to the "
+                "`layout` keyword should be a list."
+                f"{terminal_colors.END}"
+            )
 
-                plugin["_call_signature"] = _call_signature(
-                    plugin_name,
-                    kwargs,
-                    self._config_folder,
-                )
+        if "pageContents" not in self.configuration:
+            self.configuration["pageContents"] = []
 
-                self._assets.update(getattr(webviz_config.plugins, plugin_name).ASSETS)
-                self._plugin_metadata[
-                    plugin_name
-                ] = webviz_config.plugins.PLUGIN_METADATA[plugin_name]
+        self._parse_navigation()
 
     @property
     def configuration(self) -> dict:
@@ -393,3 +356,189 @@ class ConfigParser:
         plugins included in the configuration file.
         """
         return self._plugin_metadata
+
+    def _recursively_parse_navigation_item(self, items: dict, level: int) -> list:
+        navigation_items: list = []
+        for item_number, item in enumerate(items):
+            if "section" in item:
+                if level > 0:
+                    raise ParserError(
+                        f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                        "Sections can only be defined on the first level"
+                        f"{terminal_colors.END}"
+                    )
+                navigation_items.append(
+                    {
+                        "type": "section",
+                        "title": item["section"],
+                        "icon": item["icon"] if "icon" in item.keys() else None,
+                        "content": self._recursively_parse_navigation_item(
+                            item["content"], level + 1
+                        )
+                        if "content" in item.keys()
+                        else [],
+                    }
+                )
+            elif "group" in item:
+                navigation_items.append(
+                    {
+                        "type": "group",
+                        "title": item["group"],
+                        "icon": item["icon"] if "icon" in item.keys() else None,
+                        "content": self._recursively_parse_navigation_item(
+                            item["content"], level + 1
+                        )
+                        if "content" in item.keys()
+                        else [],
+                    }
+                )
+            elif "page" in item or "title" in item:
+
+                page_title = item["page"] if "page" in item else item["title"]
+                if "id" not in item:
+                    item["id"] = self._generate_page_id(page_title)
+                elif item["id"] in self._page_ids:
+                    raise ParserError(
+                        f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                        "You have more than one page "
+                        "with the same `id`."
+                        f"{terminal_colors.END}"
+                    )
+
+                self._page_ids.append(item["id"])
+
+                if "content" not in item:
+                    item["content"] = []
+                elif not isinstance(item["content"], list):
+                    raise ParserError(
+                        f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                        "The content of page number "
+                        f"{item_number + 1} should be a list."
+                        f"{terminal_colors.END}"
+                    )
+
+                self.configuration["pageContents"].append(item)
+
+                navigation_items.append(
+                    {
+                        "type": "page",
+                        "title": page_title,
+                        "href": "/" + item["id"],
+                        "icon": item["icon"] if "icon" in item.keys() else None,
+                    }
+                )
+
+                plugins = [e for e in item["content"] if isinstance(e, dict)]
+                for plugin in plugins:
+                    plugin_name = next(iter(plugin))
+                    plugin_variables = next(iter(plugin.values()))
+                    kwargs = {} if plugin_variables is None else {**plugin_variables}
+
+                    if plugin_name not in ConfigParser.STANDARD_PLUGINS:
+                        raise ParserError(
+                            f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                            "You have included a plugin with "
+                            f"name `{plugin_name}` in your "
+                            "configuration file. This is not a "
+                            "standard plugin."
+                            f"{terminal_colors.END}"
+                        )
+
+                    plugin["_call_signature"] = _call_signature(
+                        plugin_name,
+                        kwargs,
+                        self._config_folder,
+                    )
+
+                    self._assets.update(
+                        getattr(webviz_config.plugins, plugin_name).ASSETS
+                    )
+                    self._plugin_metadata[
+                        plugin_name
+                    ] = webviz_config.plugins.PLUGIN_METADATA[plugin_name]
+        return navigation_items
+
+    def _parse_navigation(self) -> None:
+        # pylint: disable=too-many-branches
+        """Returns a list of navigation items"""
+
+        if "layout" in self.configuration:
+            self.configuration[
+                "navigation_items"
+            ] = self._recursively_parse_navigation_item(self.configuration["layout"], 0)
+        elif "pages" in self.configuration:
+            self.configuration[
+                "navigation_items"
+            ] = self._recursively_parse_navigation_item(self.configuration["pages"], 0)
+
+        options_found = False
+        if "options" in self.configuration:
+            if "menu" in self.configuration["options"]:
+                options_found = True
+
+                if "bar_position" not in self.configuration["options"]["menu"]:
+                    self.configuration["options"]["menu"]["bar_position"] = "left"
+                elif self.configuration["options"]["menu"]["bar_position"] not in [
+                    "left",
+                    "top",
+                    "right",
+                    "bottom",
+                ]:
+                    raise ParserError(
+                        f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                        "Invalid option for options > menu > bar_position: "
+                        f"{self.configuration['options']['menu']['bar_position']}. "
+                        "Please select one of the following options: left, top, right, bottom."
+                        f"{terminal_colors.END}"
+                    )
+
+                if "drawer_position" not in self.configuration["options"]["menu"]:
+                    self.configuration["options"]["menu"]["drawer_position"] = "left"
+                elif self.configuration["options"]["menu"]["drawer_position"] not in [
+                    "left",
+                    "right",
+                ]:
+                    raise ParserError(
+                        f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                        "Invalid option for options > menu > drawer_position: "
+                        f"{self.configuration['options']['menu']['drawer_position']}. "
+                        "Please select one of the following options: left, right."
+                        f"{terminal_colors.END}"
+                    )
+
+                if "initially_pinned" not in self.configuration["options"]["menu"]:
+                    self.configuration["options"]["menu"]["initially_pinned"] = False
+                elif not isinstance(
+                    self.configuration["options"]["menu"]["initially_pinned"], bool
+                ):
+                    raise ParserError(
+                        f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                        "Invalid option for options > menu > initially_pinned: "
+                        f"{self.configuration['options']['menu']['initially_pinned']}. "
+                        "Please select a boolean value: True, False"
+                        f"{terminal_colors.END}"
+                    )
+
+                if "show_logo" not in self.configuration["options"]["menu"]:
+                    self.configuration["options"]["menu"]["show_logo"] = True
+                elif not isinstance(
+                    self.configuration["options"]["menu"]["show_logo"], bool
+                ):
+                    raise ParserError(
+                        f"{terminal_colors.RED}{terminal_colors.BOLD}"
+                        "Invalid option for options > menu > initially_pinned: "
+                        f"{self.configuration['options']['menu']['show_logo']}. "
+                        "Please select a boolean value: True, False"
+                        f"{terminal_colors.END}"
+                    )
+
+        if not options_found:
+            if "options" not in self.configuration:
+                self.configuration["options"] = {}
+
+            self.configuration["options"]["menu"] = {
+                "bar_position": "left",
+                "drawer_position": "left",
+                "initially_pinned": False,
+                "show_logo": True,
+            }
diff --git a/webviz_config/_docs/_create_schema.py b/webviz_config/_docs/_create_schema.py
index 395dbed..46c92a1 100644
--- a/webviz_config/_docs/_create_schema.py
+++ b/webviz_config/_docs/_create_schema.py
@@ -8,35 +8,126 @@ from ._build_docs import get_plugin_documentation
 JSON_SCHEMA = {
     "$id": "https://github.com/equinor/webviz-config",
     "$schema": "http://json-schema.org/draft-07/schema#",
-    "title": "Person",
+    "title": "Webviz",
     "type": "object",
     "properties": {
         "title": {"description": "Title of your Webviz application.", "type": "string"},
         "shared_settings": {"type": "object"},
-        "pages": {
-            "description": "Define the pages in your Webviz application.",
-            "type": "array",
-            "minLength": 1,
-            "items": {
+        "options": {
+            "type": "object",
+            "menu": {
+                "description": "Define the menu options.",
                 "type": "object",
                 "properties": {
-                    "title": {"description": "Title of the page", "type": "string"},
-                    "content": {
-                        "description": "Content on the page",
-                        "type": "array",
-                        "items": {
-                            "oneOf": [
-                                {"type": "string"},
-                            ]
-                        },
+                    "show_logo": {
+                        "description": "State if a logo shall be shown in the menu.",
+                        "type": "boolean",
+                    },
+                    "bar_position": {
+                        "description": "Define where the menu bar shall be positioned:"
+                        " left, top, right, bottom.",
+                        "type": "string",
+                    },
+                    "drawer_position": {
+                        "description": "Define where the menu drawer shall be positioned:"
+                        " left or right.",
+                        "type": "string",
+                    },
+                    "initially_pinned": {
+                        "description": "State if the menu shall be pinned when initially showing.",
+                        "type": "boolean",
                     },
                 },
-                "required": ["title", "content"],
                 "additionalProperties": False,
             },
         },
+        "layout": {
+            "description": "Define the pages (and potential sections and groups)"
+            " in your Webviz application.",
+            "type": "array",
+            "minLength": 1,
+            "items": {
+                "oneOf": [
+                    {
+                        "type": "object",
+                        "properties": {
+                            "section": {
+                                "description": "Title of the section.",
+                                "type": "string",
+                            },
+                            "icon": {
+                                "description": "Optionally set an icon for the section.",
+                                "type": "string",
+                            },
+                            "content": {
+                                "description": "Define the pages (and potential subgroups)"
+                                " of this section.",
+                                "type": "array",
+                                "minLength": 1,
+                                "items": {
+                                    "type": "object",
+                                    "oneOf": [
+                                        {"$ref": "#/properties/layout/items/oneOf/1"},
+                                        {"$ref": "#/properties/layout/items/oneOf/2"},
+                                    ],
+                                },
+                            },
+                        },
+                        "required": ["section", "content"],
+                        "additionalProperties": False,
+                    },
+                    {
+                        "type": "object",
+                        "properties": {
+                            "group": {
+                                "description": "Title of the group.",
+                                "type": "string",
+                            },
+                            "icon": {
+                                "description": "Optionally define an icon for the group.",
+                                "type": "string",
+                            },
+                            "content": {
+                                "description": "Content of the group.",
+                                "type": "array",
+                                "minLength": 1,
+                                "items": {
+                                    "oneOf": [
+                                        {"$ref": "#/properties/layout/items/oneOf/1"},
+                                        {"$ref": "#/properties/layout/items/oneOf/2"},
+                                    ]
+                                },
+                            },
+                        },
+                        "required": ["group", "content"],
+                        "additionalProperties": False,
+                    },
+                    {
+                        "type": "object",
+                        "properties": {
+                            "page": {
+                                "description": "Title of the page.",
+                                "type": "string",
+                            },
+                            "icon": {
+                                "description": "Optionally define an icon for the page.",
+                                "type": "string",
+                            },
+                            "content": {
+                                "description": "Content of the page.",
+                                "type": "array",
+                                "minLength": 1,
+                                "items": {"oneOf": [{"type": "string"}]},
+                            },
+                        },
+                        "required": ["page", "content"],
+                        "additionalProperties": False,
+                    },
+                ],
+            },
+        },
     },
-    "required": ["title", "pages"],
+    "required": ["title", "layout"],
     "additionalProperties": False,
 }
 
@@ -73,8 +164,8 @@ def create_schema() -> dict:
     json_schema = copy.deepcopy(JSON_SCHEMA)
 
     # fmt: off
-    content_schemas = json_schema["properties"]["pages"][  # type: ignore
-        "items"]["properties"]["content"]["items"]["oneOf"]
+    content_schemas = json_schema["properties"]["layout"][  # type: ignore
+        "items"]["oneOf"][2]["properties"]["content"]["items"]["oneOf"]
     # fmt: on
 
     for package_doc in get_plugin_documentation().values():
diff --git a/webviz_config/templates/copy_data_template.py.jinja2 b/webviz_config/templates/copy_data_template.py.jinja2
index 3a9a0ce..ec16fa0 100644
--- a/webviz_config/templates/copy_data_template.py.jinja2
+++ b/webviz_config/templates/copy_data_template.py.jinja2
@@ -69,7 +69,7 @@ WEBVIZ_FACTORY_REGISTRY.initialize(app_instance_info, factory_settings_dict)
 
 plugins = []
 
-{% for page in pages %}
+{% for page in pageContents %}
 {% for content in page.content %}
 {% if content is not string %}
 plugins.append(webviz_config.plugins.{{ content._call_signature[0] }})
diff --git a/webviz_config/templates/webviz_template.py.jinja2 b/webviz_config/templates/webviz_template.py.jinja2
index ad40084..cf89099 100644
--- a/webviz_config/templates/webviz_template.py.jinja2
+++ b/webviz_config/templates/webviz_template.py.jinja2
@@ -11,7 +11,8 @@ import threading
 import datetime
 from pathlib import Path, PosixPath, WindowsPath
 
-from dash import html, dcc, Dash, Input, Output
+from dash import html, dcc, Dash, Input, Output, callback_context
+import webviz_core_components as wcc
 from flask_talisman import Talisman
 import webviz_config
 import webviz_config.plugins
@@ -111,7 +112,7 @@ if {{ not portable }} and not webviz_config.is_reload_process():
     app.layout = html.Div()
 else:
     page_content = {}
-    {% for page in pages %}
+    {% for page in pageContents %}
     page_content["{{page.id}}"] = []
     {% for content in page.content -%}
     {% if content is string %}
@@ -126,45 +127,40 @@ else:
     {% endfor %}
     app.layout = html.Div(
         className="layoutWrapper", 
-        children=[html.Div(
-            children=[dcc.Location(
-                id='url', refresh=True),
-        html.Div(
-            className="sideWrapper",
-            children=[
-            {% for page in pages %}
-                dcc.Link(
-                    {%- if loop.first -%}
-                    "",
-                    id="logo",
-                    className="styledLogo",
-                    href="/",
-                    {%- else -%}
-                    "{{page.title}}",
-                    # We will create a webviz-core-components
-                    # component instead of styling dcc.Link's,
-                    # then we can more easily change className to
-                    # selectedButton for current page.
-                    className="styledButton",
-                    id="{{page.id}}",
-                    href="/{{page.id}}",
-                    {%- endif -%}
+        children=[
+            html.Div(
+                children=[
+                    dcc.Location(
+                        id='location', refresh=True),
+                    wcc.Menu(
+                        id="main-menu",
+                        showLogo={{options.menu.show_logo}},
+                        menuBarPosition="{{options.menu.bar_position}}",
+                        menuDrawerPosition="{{options.menu.drawer_position}}",
+                        initiallyPinned={{options.menu.initially_pinned}},
+                        navigationItems={{navigation_items}},
                     )
-                 {{- "" if loop.last else "," -}}
-            {% endfor %}
-            ])]),
-        html.Div(className="pageContent", id="page-content")])
+                ]
+            ),
+            html.Div(className="pageContent", id="page-content")
+        ]
+    )
 
 WEBVIZ_FACTORY_REGISTRY.cleanup_resources_after_plugin_init()
 
 oauth2 = webviz_config.Oauth2(app.server) if use_oauth2 else None
 
 @app.callback(Output("page-content", "children"),
-              Input("url", "pathname"))
-def update_page(pathname):
-    pathname = pathname.replace("/", "")
-    if not pathname:
-        pathname = list(page_content.keys())[0] 
+              [Input("main-menu", "url"), Input("location", "pathname")])
+def update_page(url, pathname):
+    ctx = callback_context
+    if ctx.triggered:
+        if ctx.triggered[0]["prop_id"].split('.')[0] == "main-menu":
+            pathname = url.replace("/", "")
+        else:
+            pathname = pathname.replace("/", "")
+        if not pathname:
+            pathname = next(iter(page_content)) 
     return page_content.get(pathname, "Oooppss... Page not found.")
 
 {{ "WEBVIZ_ASSETS.directly_host_assets(app)" if not portable else ""}}
diff --git a/webviz_config/themes/_default_theme.py b/webviz_config/themes/_default_theme.py
index 404f648..88b566e 100644
--- a/webviz_config/themes/_default_theme.py
+++ b/webviz_config/themes/_default_theme.py
@@ -10,5 +10,13 @@ default_theme = WebvizConfigTheme(theme_name="default")
 default_theme.assets = glob.glob(
     str(pathlib.Path(__file__).resolve().parent / "default_assets" / "*")
 )
+default_theme.assets.append(
+    str(
+        pathlib.Path(__file__).resolve().parent.parent
+        / "_docs"
+        / "static"
+        / "webviz-logo.svg"
+    )
+)
 
 default_theme.plotly_theme = templates["plotly"].to_plotly_json()
diff --git a/webviz_config/themes/default_assets/default_logo.svg b/webviz_config/themes/default_assets/default_logo.svg
deleted file mode 100644
index 29eea18..0000000
--- a/webviz_config/themes/default_assets/default_logo.svg
+++ /dev/null
@@ -1,281 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:xlink="http://www.w3.org/1999/xlink"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   viewBox="0 0 715.78658 284.8641"
-   version="1.1"
-   id="svg3902"
-   sodipodi:docname="webviz_logo.svg"
-   inkscape:version="0.92.4 (33fec40, 2019-01-16)"
-   width="715.78656"
-   height="284.86407">
-  <metadata
-     id="metadata3906">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <sodipodi:namedview
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1"
-     objecttolerance="10"
-     gridtolerance="10"
-     guidetolerance="10"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:window-width="1853"
-     inkscape:window-height="1025"
-     id="namedview3904"
-     showgrid="false"
-     inkscape:zoom="1"
-     inkscape:cx="534.79148"
-     inkscape:cy="327.00516"
-     inkscape:window-x="67"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:current-layer="layer5"
-     inkscape:lockguides="true"
-     showguides="false"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0" />
-  <!-- Safari requires the xlink namespace and can't use href directly -->
-  <defs
-     id="defs3897">
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient6546">
-      <stop
-         style="stop-color:#000000;stop-opacity:1;"
-         offset="0"
-         id="stop6542" />
-      <stop
-         style="stop-color:#000000;stop-opacity:0;"
-         offset="1"
-         id="stop6544" />
-    </linearGradient>
-    <style
-       id="style3876">
-            use, use:target ~ use:last-child {
-                display: none;
-            }
-            use:target, use:last-child {
-                display: inline;
-            }
-        </style>
-    <symbol
-       id="logo"
-       viewBox="0 0 285 235">
-      <path
-         d="m 241.665,182.894 c -0.301,-0.754 -0.943,-1.029 -1.658,-0.604 -0.716,0.427 -14.17,8.443 -14.17,8.443 v -7.387 c 0,-0.829 -0.528,-1.131 -1.281,-0.829 l -4.372,1.582 c -0.603,0.227 -0.83,0.679 -0.83,1.282 v 32.478 c 0,0.678 0.453,1.13 1.131,1.13 h 4.221 c 0.679,0 1.131,-0.452 1.131,-1.13 v -19.894 c 0,0 15.864,-9.79 16.507,-10.175 0.642,-0.385 0.971,-0.71 0.677,-1.432 -0.321,-0.791 -1.356,-3.464 -1.356,-3.464"
-         id="path3878"
-         inkscape:connector-curvature="0" />
-      <path
-         d="m 7.993,196.603 c 1.432,-5.2 5.502,-8.366 11.23,-8.366 6.256,0 9.573,3.693 9.874,8.366 z m 27.963,1.96 c 0,-9.045 -6.18,-16.657 -16.506,-16.657 -10.176,0 -18.618,7.311 -18.618,19.219 0,10.854 7.085,18.768 18.994,18.768 5.427,0 10.854,-1.884 14.924,-5.502 0.528,-0.452 0.528,-1.055 0.151,-1.583 l -2.035,-3.165 c -0.377,-0.603 -0.98,-0.679 -1.583,-0.227 -3.693,2.714 -7.462,4.071 -11.457,4.071 -7.612,0 -11.758,-4.975 -12.286,-11.231 h 27.302 c 0.698,0 1.114,-0.506 1.114,-1.115 z"
-         id="path3880"
-         inkscape:connector-curvature="0" />
-      <path
-         d="m 59.733,213.487 c -6.406,0 -11.909,-4.372 -11.909,-12.663 0,-8.291 5.503,-12.587 11.909,-12.587 6.708,0 11.909,4.296 11.909,12.587 0,8.367 -5.201,12.663 -11.909,12.663 m 12.965,20.577 h 4.221 c 0.678,0 1.13,-0.452 1.13,-1.131 v -49.595 c 0,-0.83 -0.527,-1.13 -1.357,-0.83 l -4.296,1.583 c -0.603,0.227 -0.829,0.678 -0.829,1.282 v 1.583 c -2.336,-3.166 -6.935,-5.05 -11.909,-5.05 -9.874,0 -18.467,7.386 -18.467,18.994 0,10.25 6.633,18.993 18.09,18.993 5.201,0 10.176,-2.336 12.286,-4.973 v 18.013 c 0,0.679 0.452,1.131 1.131,1.131"
-         id="path3882"
-         inkscape:connector-curvature="0" />
-      <path
-         d="m 99.52,219.893 c 3.919,0 8.216,-1.281 11.005,-5.351 v 3.317 c 0,0.678 0.452,1.13 1.13,1.13 h 4.221 c 0.679,0 1.131,-0.452 1.131,-1.13 v -34.446 c 0,-0.905 -0.528,-1.206 -1.282,-0.905 l -4.372,1.583 c -0.602,0.227 -0.828,0.678 -0.828,1.281 v 18.09 c 0,6.407 -4.146,10.025 -9.799,10.025 -5.578,0 -9.346,-3.543 -9.346,-10.025 v -20.125 c 0,-0.829 -0.528,-1.13 -1.281,-0.829 l -4.372,1.583 c -0.603,0.227 -0.829,0.678 -0.829,1.281 v 18.316 c 0,10.703 6.482,16.205 14.622,16.205"
-         id="path3884"
-         inkscape:connector-curvature="0" />
-      <path
-         d="m 125.825,178.664 4.598,-1.508 c 0.754,-0.225 1.055,-0.602 1.055,-1.356 v -5.502 c 0,-0.679 -0.528,-1.206 -1.357,-0.905 l -4.523,1.659 c -0.677,0.225 -1.054,0.527 -1.054,1.28 v 5.276 c 0,0.829 0.452,1.358 1.281,1.056"
-         id="path3886"
-         inkscape:connector-curvature="0" />
-      <path
-         d="m 125.9,218.989 h 4.22 c 0.679,0 1.132,-0.452 1.132,-1.13 v -34.521 c 0,-0.829 -0.528,-1.13 -1.282,-0.829 l -4.372,1.583 c -0.603,0.226 -0.829,0.677 -0.829,1.281 v 32.486 c 0,0.678 0.452,1.13 1.131,1.13"
-         id="path3888"
-         inkscape:connector-curvature="0" />
-      <path
-         d="m 140.144,218.989 h 4.221 c 0.678,0 1.131,-0.452 1.131,-1.13 v -19.522 c 0,-6.407 4.145,-10.025 9.798,-10.025 5.578,0 9.346,3.543 9.346,10.025 v 19.522 c 0,0.678 0.453,1.13 1.131,1.13 h 4.221 c 0.678,0 1.131,-0.452 1.131,-1.13 v -19.748 c 0,-10.703 -6.483,-16.205 -14.623,-16.205 -3.919,0 -8.215,1.281 -11.004,5.352 v -3.92 c 0,-0.829 -0.528,-1.13 -1.282,-0.829 l -4.372,1.583 c -0.602,0.226 -0.828,0.677 -0.828,1.281 v 32.486 c 0,0.678 0.452,1.13 1.13,1.13"
-         id="path3890"
-         inkscape:connector-curvature="0" />
-      <path
-         d="m 195.276,213.562 c -7.462,0 -11.984,-5.577 -11.984,-12.662 0,-7.086 4.522,-12.663 11.984,-12.663 7.387,0 11.909,5.577 11.909,12.663 0,7.085 -4.522,12.662 -11.909,12.662 m 0,-31.656 c -10.854,0 -18.617,8.064 -18.617,18.994 0,10.929 7.763,18.993 18.617,18.993 10.854,0 18.542,-8.064 18.542,-18.993 0,-10.93 -7.688,-18.994 -18.542,-18.994"
-         id="path3892"
-         inkscape:connector-curvature="0" />
-      <path
-         d="m 176.919,31.419 v 38.515 c 0,1.14 0.572,2.195 1.561,2.764 l 33.372,19.227 c 1.416,0.815 3.222,-0.206 3.223,-1.84 V 51.57 c 0.001,-1.141 -0.608,-2.196 -1.597,-2.765 L 180.106,29.579 c -1.416,-0.815 -3.185,0.205 -3.187,1.84 M 279.71,1.345 231.664,29.024 c -1.424,0.82 -2.301,2.338 -2.299,3.982 v 55.448 c 0.003,2.353 2.601,3.823 4.639,2.649 l 48.047,-27.679 c 1.423,-0.82 2.246,-2.339 2.245,-3.982 L 284.297,3.993 C 284.295,1.641 281.749,0.171 279.71,1.345 m -65.91,117.932 -13.352,7.693 c -0.396,0.227 -0.639,0.649 -0.639,1.106 l -0.001,15.409 c 0.001,0.655 0.724,1.063 1.291,0.737 l 13.352,-7.693 c 0.396,-0.227 0.625,-0.649 0.624,-1.106 v -15.41 c 0,-0.654 -0.708,-1.062 -1.275,-0.736 m -7.781,-14.219 -19.997,-11.57 c -0.592,-0.342 -1.323,-0.342 -1.916,0 l -19.996,11.57 c -0.848,0.491 -0.848,1.716 0,2.207 l 19.996,11.57 c 0.593,0.343 1.324,0.343 1.916,0 l 19.997,-11.57 c 0.848,-0.491 0.848,-1.716 0,-2.207 m 32.192,2.086 8.884,5.14 c 0.527,0.305 1.176,0.305 1.703,0 l 8.884,-5.14 c 0.754,-0.437 0.754,-1.526 0,-1.962 l -8.884,-5.141 c -0.527,-0.305 -1.176,-0.305 -1.703,0 l -8.884,5.141 c -0.754,0.436 -0.754,1.525 0,1.962 m -7.147,12.372 8.894,5.124 c 0.527,0.303 0.852,0.866 0.851,1.474 l -0.01,10.264 c -0.001,0.872 -0.943,1.416 -1.699,0.981 l -8.893,-5.124 c -0.528,-0.303 -0.841,-0.867 -0.841,-1.474 l -0.001,-10.264 c 0,-0.87 0.944,-1.416 1.699,-0.981"
-         id="path3894"
-         inkscape:connector-curvature="0" />
-    </symbol>
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient6546"
-       id="linearGradient6548"
-       x1="6.5278263"
-       y1="120.77546"
-       x2="280.81451"
-       y2="120.77546"
-       gradientUnits="userSpaceOnUse" />
-    <filter
-       inkscape:label="Air Spray"
-       inkscape:menu="Scatter"
-       inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
-       width="2"
-       height="2"
-       y="-0.5"
-       x="-0.5"
-       style="color-interpolation-filters:sRGB"
-       id="filter6760">
-      <feGaussianBlur
-         stdDeviation="0.01"
-         result="result1"
-         id="feGaussianBlur6748" />
-      <feBlend
-         in2="result1"
-         result="fbSourceGraphic"
-         mode="multiply"
-         id="feBlend6750" />
-      <feTurbulence
-         baseFrequency="0.8"
-         type="fractalNoise"
-         seed="0"
-         numOctaves="3"
-         result="result3"
-         id="feTurbulence6752" />
-      <feDisplacementMap
-         in="fbSourceGraphic"
-         xChannelSelector="R"
-         yChannelSelector="G"
-         scale="50"
-         result="result2"
-         in2="result3"
-         id="feDisplacementMap6754" />
-      <feMorphology
-         radius="1"
-         operator="dilate"
-         result="result4"
-         id="feMorphology6756" />
-      <feBlend
-         mode="screen"
-         in2="result2"
-         id="feBlend6758" />
-    </filter>
-  </defs>
-  <g
-     inkscape:groupmode="layer"
-     id="layer5"
-     inkscape:label="Layer 4"
-     transform="translate(98.56382,-33.091224)">
-    <rect
-       style="fill:#14213d;fill-opacity:1;stroke:#14213d;stroke-width:1.3328917;stroke-opacity:1"
-       id="rect4820"
-       width="584.85217"
-       height="258.66086"
-       x="31.618668"
-       y="43.542519"
-       ry="75.307571" />
-  </g>
-  <g
-     inkscape:groupmode="layer"
-     id="layer1"
-     inkscape:label="Layer 1"
-     style="display:inline"
-     transform="translate(180.02707,-38.070044)">
-    <rect
-       style="fill:#00bf7c;fill-opacity:0;stroke-width:1.41496205"
-       id="rect4732"
-       width="412.94983"
-       height="309.34695"
-       x="-87.522377"
-       y="-39.568913" />
-  </g>
-  <g
-     inkscape:groupmode="layer"
-     id="layer2"
-     inkscape:label="Layer 2"
-     style="display:inline"
-     transform="translate(180.02707,-38.070044)">
-    <rect
-       style="fill:#080d18;fill-opacity:0;stroke-width:1.84365869"
-       id="rect4743"
-       width="337.51715"
-       height="182.09853"
-       x="35.87693"
-       y="42.808533" />
-    <path
-       style="fill:#080d18;fill-opacity:0;stroke-width:1.40896642"
-       d="m 98.027052,305.92043 c -0.35349,-0.92871 -5.5714,-19.46927 -11.59536,-41.20124 l -10.95264,-39.51265 h 5.66415 c 4.12401,0 5.87166,0.6428 6.42728,2.36402 0.41972,1.30019 4.1681,16.04146 8.32974,32.75835 4.161638,16.71689 8.077448,30.89477 8.701808,31.5064 0.62436,0.61161 1.58509,-0.60415 2.13495,-2.70171 0.54987,-2.09759 5.04298,-17.36924 9.9847,-33.93703 l 8.98493,-30.12326 5.88531,0.40434 c 4.80941,0.33042 6.09194,1.02171 7.01557,3.78148 0.62165,1.85742 5.24722,16.92278 10.27906,33.47854 5.03183,16.55577 9.61572,29.62534 10.18642,29.04349 0.5707,-0.58185 4.59352,-15.79833 8.93961,-33.8144 l 7.90197,-32.7565 5.51089,-0.001 c 3.26205,-0.001 5.5109,0.64994 5.5109,1.59538 0,1.26287 -12.36618,46.47945 -20.73411,75.81366 -1.42742,5.00391 -1.62613,5.12898 -7.51169,4.72801 l -6.04549,-0.412 -10.31437,-34.12851 c -5.67291,-18.77068 -10.76678,-33.66391 -11.31972,-33.09608 -0.55293,0.56785 -4.44439,12.88623 -8.64769,27.37422 -12.96888,44.70128 -11.12734,40.52579 -17.87338,40.52579 -3.33362,0 -6.094718,-0.72139 -6.462838,-1.68857 z"
-       id="path4747"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:#080d18;fill-opacity:0;stroke-width:1.40896642"
-       d="m 98.027052,305.92043 c -0.35349,-0.92871 -5.5714,-19.46927 -11.59536,-41.20124 l -10.95264,-39.51265 h 5.66415 c 4.12401,0 5.87166,0.6428 6.42728,2.36402 0.41972,1.30019 4.1681,16.04146 8.32974,32.75835 4.161638,16.71689 8.077448,30.89477 8.701808,31.5064 0.62436,0.61161 1.58509,-0.60415 2.13495,-2.70171 0.54987,-2.09759 5.04298,-17.36924 9.9847,-33.93703 l 8.98493,-30.12326 5.88531,0.40434 c 4.80941,0.33042 6.09194,1.02171 7.01557,3.78148 0.62165,1.85742 5.24722,16.92278 10.27906,33.47854 5.03183,16.55577 9.61572,29.62534 10.18642,29.04349 0.5707,-0.58185 4.59352,-15.79833 8.93961,-33.8144 l 7.90197,-32.7565 5.51089,-0.001 c 3.26205,-0.001 5.5109,0.64994 5.5109,1.59538 0,1.26287 -12.36618,46.47945 -20.73411,75.81366 -1.42742,5.00391 -1.62613,5.12898 -7.51169,4.72801 l -6.04549,-0.412 -10.31437,-34.12851 c -5.67291,-18.77068 -10.76678,-33.66391 -11.31972,-33.09608 -0.55293,0.56785 -4.44439,12.88623 -8.64769,27.37422 -12.96888,44.70128 -11.12734,40.52579 -17.87338,40.52579 -3.33362,0 -6.094718,-0.72139 -6.462838,-1.68857 z"
-       id="path4749"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:#080d18;fill-opacity:0;stroke-width:1.40896642"
-       d="m 98.027052,305.92043 c -0.35349,-0.92871 -5.5714,-19.46927 -11.59536,-41.20124 l -10.95264,-39.51265 h 5.66415 c 4.12401,0 5.87166,0.6428 6.42728,2.36402 0.41972,1.30019 4.1681,16.04146 8.32974,32.75835 4.161638,16.71689 8.077448,30.89477 8.701808,31.5064 0.62436,0.61161 1.58509,-0.60415 2.13495,-2.70171 0.54987,-2.09759 5.04298,-17.36924 9.9847,-33.93703 l 8.98493,-30.12326 5.88531,0.40434 c 4.80941,0.33042 6.09194,1.02171 7.01557,3.78148 0.62165,1.85742 5.24722,16.92278 10.27906,33.47854 5.03183,16.55577 9.61572,29.62534 10.18642,29.04349 0.5707,-0.58185 4.59352,-15.79833 8.93961,-33.8144 l 7.90197,-32.7565 5.51089,-0.001 c 3.26205,-0.001 5.5109,0.64994 5.5109,1.59538 0,1.26287 -12.36618,46.47945 -20.73411,75.81366 -1.42742,5.00391 -1.62613,5.12898 -7.51169,4.72801 l -6.04549,-0.412 -10.31437,-34.12851 c -5.67291,-18.77068 -10.76678,-33.66391 -11.31972,-33.09608 -0.55293,0.56785 -4.44439,12.88623 -8.64769,27.37422 -12.96888,44.70128 -11.12734,40.52579 -17.87338,40.52579 -3.33362,0 -6.094718,-0.72139 -6.462838,-1.68857 z"
-       id="path4751"
-       inkscape:connector-curvature="0" />
-    <rect
-       style="display:inline;fill:#e5e5e5;fill-opacity:1;stroke:#14213d;stroke-width:2.09343624;stroke-opacity:0"
-       id="rect4820-5-3"
-       width="400.2291"
-       height="263.43909"
-       x="-168.03557"
-       y="46.481056"
-       ry="131.71954" />
-    <text
-       xml:space="preserve"
-       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:176.99124146px;line-height:1.25;font-family:helvetica;-inkscape-font-specification:helvetica;letter-spacing:0px;word-spacing:0.02px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.84365869"
-       x="246.9684"
-       y="256.33847"
-       id="text3785-5"
-       transform="scale(0.94767139,1.0552181)"><tspan
-         sodipodi:role="line"
-         id="tspan3783-3"
-         x="246.9684"
-         y="256.33847"
-         style="font-size:186.66667175px;letter-spacing:22.70001411px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke-width:1.84365869"
-         rotate="0 347.79996 347.79996 0">Viz</tspan></text>
-  </g>
-  <g
-     inkscape:groupmode="layer"
-     id="layer3"
-     inkscape:label="Layer 3"
-     transform="translate(180.02707,-38.070044)">
-    <text
-       xml:space="preserve"
-       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:176.99124146px;line-height:1.25;font-family:helvetica;-inkscape-font-specification:helvetica;letter-spacing:0px;word-spacing:0px;fill:#14213d;fill-opacity:1;stroke:none;stroke-width:1.84365869"
-       x="-160.24173"
-       y="214.4633"
-       id="text3785"
-       transform="scale(0.94767139,1.0552181)"><tspan
-         sodipodi:role="line"
-         id="tspan3783"
-         x="-160.24173"
-         y="214.4633"
-         style="font-size:186.66667175px;writing-mode:lr-tb;direction:ltr;fill:#14213d;fill-opacity:1;stroke-width:1.84365869"
-         rotate="12.200003 12.200003 12.200003 0">Web</tspan></text>
-    <flowRoot
-       xml:space="preserve"
-       id="flowRoot8447"
-       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:helvetica;-inkscape-font-specification:helvetica;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"><flowRegion
-         id="flowRegion8449"><rect
-           id="rect8451"
-           width="341.53256"
-           height="168.99852"
-           x="67.175148"
-           y="155.19768" /></flowRegion><flowPara
-         id="flowPara8453"></flowPara></flowRoot>  </g>
-</svg>
diff --git a/webviz_config/themes/default_assets/default_theme.css b/webviz_config/themes/default_assets/default_theme.css
index 2f6089f..6b19291 100644
--- a/webviz_config/themes/default_assets/default_theme.css
+++ b/webviz_config/themes/default_assets/default_theme.css
@@ -17,18 +17,6 @@
 - Clearing
 - Media Queries
 */
-h1, h2, h3, h4, h5, h6, p, a, div, button {
-  font-family: helvetica;
-}
-
-#logo {
-  height: 100px;
-  width: 200px;
-  margin: 20px 0px;
-  background-size: contain;
-  background-image: url("default_logo.svg");
-  background-repeat: no-repeat;
-}
 
 
 /* Style Variables
@@ -50,6 +38,30 @@ h1, h2, h3, h4, h5, h6, p, a, div, button {
 }
 /* csslint ignore:end */
 
+body {
+  line-height: 1.6;
+  font-weight: 400;
+  font-size: 16px;
+  font-family: var(--menuLinkFont);
+  color: #323232;
+}
+
+#LogoLarge {
+  height: 100px;
+  width: 200px;
+  background-size: contain;
+  background-image: url("webviz-logo.svg");
+  background-repeat: no-repeat;
+}
+
+#LogoSmall {
+  height: 64px;
+  width: 64px;
+  background-size: contain;
+  background-image: url("webviz-logo.svg");
+  background-repeat: no-repeat;
+}
+
 /* Plotly.js
 –––––––––––––––––––––––––––––––––––––––––––––––––– */
 /* plotly.js's modebar's z-index is 1001 by default
@@ -467,4 +479,4 @@ there.
 /* Larger than phablet (also point when grid becomes active) */
 /* Larger than tablet */
 /* Larger than desktop */
-/* Larger than Desktop HD */
\ No newline at end of file
+/* Larger than Desktop HD */


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..af74f84
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,8 @@
+# Sets the window size of the browser (crucial in --headless mode).
+from selenium.webdriver.chrome.options import Options
+
+
+def pytest_setup_options():
+    options = Options()
+    options.add_argument("--window-size=1920,1080")
+    return options
diff --git a/tests/test_portable.py b/tests/test_portable.py
index 2924538..8255052 100644
--- a/tests/test_portable.py
+++ b/tests/test_portable.py
@@ -23,5 +23,5 @@ def test_portable(dash_duo, tmp_path):
         "plot-a-table",
         "pivot-table",
     ]:
-        dash_duo.wait_for_element(f"#{page}").click()
+        dash_duo.wait_for_element(f".Menu__Page[href='/{page}']").click()
     assert dash_duo.get_logs() == [], "browser console should contain no error"
diff --git a/tests/test_schema.py b/tests/test_schema.py
index 5bf96fa..6d2bdbb 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1,17 +1,22 @@
 import pathlib
-
+import pytest
 import yaml
 import jsonschema
 
 from webviz_config._docs._create_schema import create_schema
 
 
-def test_schema():
+@pytest.mark.parametrize(
+    "config_file_path",
+    [
+        (pathlib.Path("examples") / "basic_example.yaml"),
+        (pathlib.Path("examples") / "basic_example_advanced_menu.yaml"),
+    ],
+)
+def test_schema(config_file_path: pathlib.Path):
     """Tests both that the generated schema is valid,
     and that the input configuration is valid according to the schema.
     """
 
-    config = yaml.safe_load(
-        (pathlib.Path("examples") / "basic_example.yaml").read_text()
-    )
+    config = yaml.safe_load(config_file_path.read_text())
     jsonschema.validate(instance=config, schema=create_schema())