diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..79a54175fe7c60417488c047b78c912cedbc4bc4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,164 @@
+
+# npm node_modules
+node_modules/
+
+# Ipynb
+ipynb_checkpoints
+*/.ipynb_checkpoints/*
+
+# IPython
+profile_default/
+ipython_config.py
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# macOS
+*.DS_Store
+.DS_Store
+.AppleDouble
+.LSOverride
+.Trashes
+
+# PyInstaller
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask
+instance/
+.webassets-cache
+
+# Scrapy
+.scrapy
+
+# mkdocs
+vizro-core/site
+vizro-ai/site
+
+# PyBuilder
+.pybuilder/
+target/
+
+# IntelliJ
+.idea/
+*.iml
+out/
+.idea_modules/
+
+# Vscode
+.vscode/
+
+# PEP 582
+__pypackages__/
+
+# Celery
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder
+.spyderproject
+.spyproject
+
+# Rope
+.ropeproject
+
+# Mkdocs
+/site
+
+# Mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre
+.pyre/
+
+# Pytype
+.pytype/
+
+# Cython
+cython_debug/
+
+# Ruff
+.ruff_cache/
+
+# Ignore data files except for white-listed folders
+*.csv
+*.xls
+*.xlsx
+*.parquet
+*.tsv
+*.hdf5
+*.h5
+*.pickle
+*.pkl
+*.db
+*.sqlite
+*.sqlite3
+*.orc
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..a4070bc197b4427c2a1b05bb679116bf5be7e26e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,18 @@
+# read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
+# you will also find guides on how best to write your Dockerfile
+
+FROM python:3.9
+
+RUN useradd -m -u 1000 user
+
+WORKDIR /app
+
+COPY --chown=user ./requirements.txt requirements.txt
+
+RUN pip install --no-cache-dir --upgrade -r requirements.txt
+
+COPY --chown=user . /app
+
+EXPOSE 7860
+
+CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:7860", "app:server"]
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..42383880001ed528599f4d0a66d4c82d6b34c8dd
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 FT Interactive News
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 68b8d21f1aae642e7b0e13906ec2d7555d2c3eb8..a7c064538f50a7ee453d1dd59efaf91ad6f905c8 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,103 @@
----
-title: Demo Visual Vocabulary
-emoji: 🌖
-colorFrom: indigo
-colorTo: indigo
-sdk: docker
-pinned: false
-license: apache-2.0
----
-
-Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
+# Chart gallery dashboard
+
+This dashboard shows a gallery of charts. It includes guidance on when to use each chart type and sample Python code
+to create them using [Plotly](https://plotly.com/python/) and [Vizro](https://github.com/mckinsey/vizro).
+
+**Created by:**
+
+- [Huong Li Nguyen](https://github.com/huong-li-nguyen) and [Antony Milne](https://github.com/antonymilne)
+
+- Images created by QuantumBlack
+
+**Inspired by:**
+
+- [The FT Visual Vocabulary](https://github.com/Financial-Times/chart-doctor/blob/main/visual-vocabulary/README.md): Alan Smith, Chris Campbell, Ian Bott, Liz Faunce, Graham Parrish, Billy Ehrenberg, Paul McCallum,
+ Martin Stabe.
+
+- [The Graphic Continuum](https://www.informationisbeautifulawards.com/showcase/611-the-graphic-continuum): Jon Swabish and Severino Ribecca
+
+**Credits, tutorials and resources:**
+
+- [Plotly](https://plotly.com/python/plotly-express/)
+- [Guide to data chart mastery](https://www.atlassian.com/data/charts)
+
+## Chart types
+
+The dashboard is still in development. Below is an overview of the chart types for which a completed page is available.
+
+| Chart Type | Status | Category |
+| --------------------- | ------ | ------------------------ |
+| Arc | ❌ | Part-to-whole |
+| Area | ✅ | Time |
+| Bar | ✅ | Magnitude |
+| Barcode | ❌ | Distribution |
+| Beeswarm | ❌ | Distribution |
+| Boxplot | ✅ | Distribution |
+| Bubble | ✅ | Correlation |
+| Bubble map | ❌ | Spatial |
+| Bubble timeline | ❌ | Time |
+| Bullet | ❌ | Magnitude |
+| Bump | ❌ | Ranking |
+| Butterfly | ✅ | Deviation, Distribution |
+| Chord | ❌ | Flow |
+| Choropleth | ✅ | Spatial |
+| Column | ✅ | Magnitude, Time |
+| Column and line | ✅ | Correlation, Time |
+| Connected scatter | ✅ | Correlation, Time |
+| Cumulative curve | ❌ | Distribution |
+| Diverging bar | ❌ | Deviation |
+| Diverging stacked bar | ❌ | Deviation |
+| Donut | ✅ | Part-to-whole |
+| Dot map | ❌ | Spatial |
+| Dot plot | ❌ | Distribution |
+| Fan | ❌ | Time |
+| Flow map | ❌ | Spatial |
+| Funnel | ✅ | Part-to-whole |
+| Gantt | ❌ | Time |
+| Gridplot | ❌ | Part-to-whole |
+| Heatmap | ✅ | Time |
+| Heatmap matrix | ❌ | Correlation |
+| Histogram | ✅ | Distribution |
+| Line | ✅ | Time |
+| Lollipop | ❌ | Ranking, Magnitude |
+| Marimekko | ❌ | Magnitude, Part-to-whole |
+| Network | ❌ | Flow |
+| Ordered bar | ✅ | Ranking |
+| Ordered bubble | ❌ | Ranking |
+| Ordered column | ✅ | Ranking |
+| Paired bar | ✅ | Magnitude |
+| Paired column | ✅ | Magnitude |
+| Parallel coordinates | ✅ | Magnitude |
+| Pictogram | ❌ | Magnitude |
+| Pie | ✅ | Part-to-whole |
+| Radar | ❌ | Magnitude |
+| Radial | ❌ | Magnitude |
+| Sankey | ✅ | Flow |
+| Scatter | ✅ | Correlation |
+| Scatter matrix | ✅ | Correlation |
+| Slope | ❌ | Ranking, Time |
+| Sparkline | ❌ | Time |
+| Stacked bar | ✅ | Part-to-whole |
+| Stacked column | ✅ | Part-to-whole |
+| Stepped line | ✅ | Time |
+| Surplus deficit line | ❌ | Deviation |
+| Treemap | ✅ | Part-to-whole |
+| Venn | ❌ | Part-to-whole |
+| Violin | ✅ | Distribution |
+| Waterfall | ❌ | Part-to-whole, Flow |
+
+To contribute a chart, follow the steps below:
+
+1. Place an `svg` file named after the chart type in the `assets` folder if it doesn't already exist.
+2. Add the data set to `_pages_utils.py` if it doesn't already exist.
+3. Create a new page for the chart type and add it to the relevant category `.py` file such as `correlation.py`,
+ `deviation.py`, `distribution.py`, etc.
+4. Add a `.py` file containing a code example of the chart type in the `pages/examples` folder, for instance, `area.py`
+5. Remove the `IncompletePage(..)` entry for that chart type in `chart_groups.py`.
+6. Update this `README.md` with the new chart type.
+
+## How to run the example locally
+
+1. If you have `hatch` set up, run the example with the command `hatch run example _chart-gallery`.
+ Otherwise, with a virtual Python environment activated, run `pip install -r requirements.txt` and then `python app.py`.
+2. You should now be able to access the app locally via http://127.0.0.1:8050/.
diff --git a/app.py b/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..a6eeff4a648a758659ea893768b7f9913eb44e93
--- /dev/null
+++ b/app.py
@@ -0,0 +1,111 @@
+"""App configuration for chart gallery dashboard."""
+
+from typing import List, Union
+
+import vizro.models as vm
+from chart_groups import ALL_CHART_GROUP, CHART_GROUPS, ChartGroup, IncompletePage
+from custom_components import FlexContainer, Markdown
+from vizro import Vizro
+
+
+def make_chart_card(page: Union[vm.Page, IncompletePage]) -> vm.Card:
+ """Makes a card with svg icon, linked to the right page if page is complete.
+
+ Args:
+ page: page to make card for
+
+ Returns: card with svg icon, linked to the right page if page is complete.
+
+ """
+ # There's one SVG per chart title, so that e.g. pages distribution-butterfly and deviation-butterfly, which both
+ # have title "Butterfly", correspond to butterfly.svg.
+ # Incomplete pages have page.path = "" so won't be linked to here.
+ svg_name = page.title.lower().replace(" ", "-")
+ return vm.Card(
+ text=f"""
+ ![](assets/images/charts/{svg_name}.svg#chart-icon)
+
+ #### {page.title}
+ """,
+ href=page.path,
+ )
+
+
+def make_homepage_container(chart_group: ChartGroup) -> vm.Container:
+ """Makes a container with cards for each completed and incomplete chart in chart_group.
+
+ Args:
+ chart_group: group of charts to make container for.
+
+ Returns: container with cards for each chart in chart_group.
+
+ """
+ # Pages are sorted in title's alphabetical order and deduplicated so that e.g. pages distribution-butterfly and
+ # deviation-butterfly, which both have title "Butterfly", correspond to a single card.
+ return vm.Container(
+ title=chart_group.name,
+ layout=vm.Layout(grid=[[0, 1, 1]], col_gap="40px"),
+ components=[
+ Markdown(text=chart_group.intro_text, classname="intro-text"),
+ FlexContainer(
+ components=[
+ make_chart_card(page)
+ for page in sorted(
+ _remove_duplicates(chart_group.pages + chart_group.incomplete_pages),
+ key=lambda page: page.title,
+ )
+ ],
+ ),
+ ],
+ )
+
+
+def _remove_duplicates(pages: List[Union[vm.Page, IncompletePage]]) -> List[Union[vm.Page, IncompletePage]]:
+ # Deduplicate pages that have the same title. Using reversed means that the page that is kept is the first one
+ # in the dashboard. This will be the one that the card on the homepage links to.
+ return list({page.title: page for page in reversed(pages)}.values())
+
+
+def make_navlink(chart_group: ChartGroup) -> vm.NavLink:
+ """Makes a navlink with icon and links to every complete page within chart_group.
+
+ Args:
+ chart_group: chart_group to make a navlink for.
+
+ Returns: navlink for chart_group.
+
+ """
+ # Pages are sorted in alphabetical order within each chart group.
+ return vm.NavLink(
+ label=chart_group.name,
+ pages={chart_group.name: [page.id for page in sorted(chart_group.pages, key=lambda page: page.title)]},
+ icon=chart_group.icon,
+ )
+
+
+homepage = vm.Page(
+ title="Overview",
+ components=[
+ vm.Tabs(tabs=[make_homepage_container(chart_group) for chart_group in [ALL_CHART_GROUP, *CHART_GROUPS]]),
+ ],
+)
+
+# TODO: consider whether each chart group should have its own individual homepage,
+# e.g. at http://localhost:8050/deviation/. This could just repeat the content of the tab from the homepage and would
+# work nicely with the hierarchical navigation.
+dashboard = vm.Dashboard(
+ # ALL_CHART_GROUP.pages has duplicated pages, e.g. both distribution-butterfly and deviation-butterfly.
+ title="Vizro - Visual Vocabulary",
+ pages=[homepage, *ALL_CHART_GROUP.pages],
+ navigation=vm.Navigation(
+ nav_selector=vm.NavBar(
+ items=[
+ vm.NavLink(label="Overview", pages=[homepage.id], icon="Home"),
+ ]
+ + [make_navlink(chart_group) for chart_group in CHART_GROUPS]
+ )
+ ),
+)
+
+if __name__ == "__main__":
+ Vizro().build(dashboard).run()
diff --git a/assets/app.svg b/assets/app.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9d07d6372b2a023eafe9e1a7a408e89c93cbfe1e
--- /dev/null
+++ b/assets/app.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/css/custom.css b/assets/css/custom.css
new file mode 100644
index 0000000000000000000000000000000000000000..3d441b1f9175f272bece23d601b67fc2dacbf653
--- /dev/null
+++ b/assets/css/custom.css
@@ -0,0 +1,71 @@
+#page-header {
+ padding-left: 8px;
+}
+
+#left-main {
+ width: 288px;
+}
+
+img[src*="#chart-icon"] {
+ width: 100%;
+}
+
+.code-clipboard {
+ font-size: 20px;
+ position: absolute;
+ right: 14px;
+ top: 12px;
+}
+
+.code-clipboard-container {
+ background: var(--surfaces-bg-card);
+ font-family: monospace;
+ max-height: 500px;
+ overflow: auto;
+ padding: 1rem;
+ position: relative;
+}
+
+.code-clipboard-container::-webkit-scrollbar-thumb {
+ border-color: var(--surfaces-bg-card);
+}
+
+.flex-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 24px;
+ justify-content: flex-start;
+}
+
+.flex-container h4 {
+ color: var(--text-secondary);
+ margin: 0;
+ padding-top: 12px;
+ text-align: center;
+}
+
+.flex-container .card {
+ height: 232px;
+ margin: 0 0 auto;
+ opacity: 0.3;
+ width: 176px;
+}
+
+.flex-container .card-nav {
+ opacity: 1;
+}
+
+.flex-container .card img {
+ width: 100%;
+}
+
+.intro-text {
+ border-left: 4px solid var(--text-secondary);
+ padding: 4px 12px;
+}
+
+.intro-text p {
+ font-size: 16px;
+ line-height: 20px;
+ margin: 0;
+}
diff --git a/assets/favicon.ico b/assets/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..240c9f541de1e47599bb047e63b008ccb268a4c3
Binary files /dev/null and b/assets/favicon.ico differ
diff --git a/assets/images/charts/arc.svg b/assets/images/charts/arc.svg
new file mode 100644
index 0000000000000000000000000000000000000000..35a5c81f874cbf3468c17b43b7a9e855383525e4
--- /dev/null
+++ b/assets/images/charts/arc.svg
@@ -0,0 +1,25 @@
+
+
diff --git a/assets/images/charts/area.svg b/assets/images/charts/area.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f51c7d036a13a466643ed417ccf0fc18b6941b89
--- /dev/null
+++ b/assets/images/charts/area.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/bar.svg b/assets/images/charts/bar.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b2491fb527eec5b90ceccdadab0b6503ed7b7f7f
--- /dev/null
+++ b/assets/images/charts/bar.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/barcode.svg b/assets/images/charts/barcode.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cbe2ffc23c29ee65110482d93e24fb38808bd9d8
--- /dev/null
+++ b/assets/images/charts/barcode.svg
@@ -0,0 +1,136 @@
+
+
diff --git a/assets/images/charts/beeswarm.svg b/assets/images/charts/beeswarm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7d29af852261286407ee69a390d69d2b774e3bed
--- /dev/null
+++ b/assets/images/charts/beeswarm.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/boxplot.svg b/assets/images/charts/boxplot.svg
new file mode 100644
index 0000000000000000000000000000000000000000..224deddd82bc003b1656c54942a761d94500e78d
--- /dev/null
+++ b/assets/images/charts/boxplot.svg
@@ -0,0 +1,78 @@
+
+
diff --git a/assets/images/charts/bubble-map.svg b/assets/images/charts/bubble-map.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cade9887df283370b9a696b4fea04ab456de817d
--- /dev/null
+++ b/assets/images/charts/bubble-map.svg
@@ -0,0 +1,467 @@
+
+
diff --git a/assets/images/charts/bubble-timeline.svg b/assets/images/charts/bubble-timeline.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b98e5af323d7cc656e15dc0f8cf8312252e163ed
--- /dev/null
+++ b/assets/images/charts/bubble-timeline.svg
@@ -0,0 +1,35 @@
+
+
diff --git a/assets/images/charts/bubble.svg b/assets/images/charts/bubble.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a939659fc377cfb12eb1d228e5e93827ae90c54d
--- /dev/null
+++ b/assets/images/charts/bubble.svg
@@ -0,0 +1,32 @@
+
+
diff --git a/assets/images/charts/bullet.svg b/assets/images/charts/bullet.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d7f7081738e390a953bd210489b5b9e15f90f4d0
--- /dev/null
+++ b/assets/images/charts/bullet.svg
@@ -0,0 +1,41 @@
+
+
diff --git a/assets/images/charts/bump.svg b/assets/images/charts/bump.svg
new file mode 100644
index 0000000000000000000000000000000000000000..509f6cb0a2c796e652efca526bc9d64a4e1cda7a
--- /dev/null
+++ b/assets/images/charts/bump.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/butterfly.svg b/assets/images/charts/butterfly.svg
new file mode 100644
index 0000000000000000000000000000000000000000..5f0281a6071a0c55187ffa196ff7e97c192d1fc7
--- /dev/null
+++ b/assets/images/charts/butterfly.svg
@@ -0,0 +1,27 @@
+
+
diff --git a/assets/images/charts/chord.svg b/assets/images/charts/chord.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8eecba457b6c38324d9a6a8f64e7832568489472
--- /dev/null
+++ b/assets/images/charts/chord.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/assets/images/charts/choropleth.svg b/assets/images/charts/choropleth.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6a00da7bd89829c455367ab37a4ae513777e7cc9
--- /dev/null
+++ b/assets/images/charts/choropleth.svg
@@ -0,0 +1,126 @@
+
+
diff --git a/assets/images/charts/column-and-line.svg b/assets/images/charts/column-and-line.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c4af7f626056858f06c3859ba5a8d748ad8cdecf
--- /dev/null
+++ b/assets/images/charts/column-and-line.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/assets/images/charts/column.svg b/assets/images/charts/column.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ae4a75e22bc1072bb007063228ada935c3505a20
--- /dev/null
+++ b/assets/images/charts/column.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/connected-scatter.svg b/assets/images/charts/connected-scatter.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b3e46261af06a554be0df177d1fb016cb2122b9b
--- /dev/null
+++ b/assets/images/charts/connected-scatter.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/cumulative-curve.svg b/assets/images/charts/cumulative-curve.svg
new file mode 100644
index 0000000000000000000000000000000000000000..93cb42b5bfb38b83995bf95b835cdcea7409fc45
--- /dev/null
+++ b/assets/images/charts/cumulative-curve.svg
@@ -0,0 +1,18 @@
+
+
diff --git a/assets/images/charts/diverging-bar.svg b/assets/images/charts/diverging-bar.svg
new file mode 100644
index 0000000000000000000000000000000000000000..bb0b80977495142a3c4fb7a553162683f6854943
--- /dev/null
+++ b/assets/images/charts/diverging-bar.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/assets/images/charts/diverging-stacked-bar.svg b/assets/images/charts/diverging-stacked-bar.svg
new file mode 100644
index 0000000000000000000000000000000000000000..42cbdadc7deb63c7d8037604b4e211409c87229c
--- /dev/null
+++ b/assets/images/charts/diverging-stacked-bar.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/donut.svg b/assets/images/charts/donut.svg
new file mode 100644
index 0000000000000000000000000000000000000000..df72ab3ca96c3f863190da2bd31eb380d0bb308f
--- /dev/null
+++ b/assets/images/charts/donut.svg
@@ -0,0 +1,25 @@
+
+
diff --git a/assets/images/charts/dot-map.svg b/assets/images/charts/dot-map.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f733721059983419239d49c30323a18e19763733
--- /dev/null
+++ b/assets/images/charts/dot-map.svg
@@ -0,0 +1,265 @@
+
+
diff --git a/assets/images/charts/dot-plot.svg b/assets/images/charts/dot-plot.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6640e2813670b66e5dd39c3433f486404577972f
--- /dev/null
+++ b/assets/images/charts/dot-plot.svg
@@ -0,0 +1,30 @@
+
+
diff --git a/assets/images/charts/fan.svg b/assets/images/charts/fan.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cd0c938c3ba69ca8922ca685808bfe37545b7ce2
--- /dev/null
+++ b/assets/images/charts/fan.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/assets/images/charts/flow-map.svg b/assets/images/charts/flow-map.svg
new file mode 100644
index 0000000000000000000000000000000000000000..238505d2a5bbe38eb99a00ac3f30fcf3ee9c48d9
--- /dev/null
+++ b/assets/images/charts/flow-map.svg
@@ -0,0 +1,80 @@
+
+
diff --git a/assets/images/charts/funnel.svg b/assets/images/charts/funnel.svg
new file mode 100644
index 0000000000000000000000000000000000000000..4d33728ad70c1c3ebc58b8905f7f4509c30cdf19
--- /dev/null
+++ b/assets/images/charts/funnel.svg
@@ -0,0 +1,19 @@
+
+
diff --git a/assets/images/charts/gantt.svg b/assets/images/charts/gantt.svg
new file mode 100644
index 0000000000000000000000000000000000000000..543e0ed8553f2949f2a902aff0a5e3b947431c47
--- /dev/null
+++ b/assets/images/charts/gantt.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/assets/images/charts/gridplot.svg b/assets/images/charts/gridplot.svg
new file mode 100644
index 0000000000000000000000000000000000000000..276deb3a1583b660a41cecd0d20336d9a14727c6
--- /dev/null
+++ b/assets/images/charts/gridplot.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/heatmap-matrix.svg b/assets/images/charts/heatmap-matrix.svg
new file mode 100644
index 0000000000000000000000000000000000000000..84c13f73a8986b75f70019193a99c641b4acc317
--- /dev/null
+++ b/assets/images/charts/heatmap-matrix.svg
@@ -0,0 +1,179 @@
+
+
diff --git a/assets/images/charts/heatmap.svg b/assets/images/charts/heatmap.svg
new file mode 100644
index 0000000000000000000000000000000000000000..fb40db53834a703ef28d2f7f5e2bf2e04c1a1ed5
--- /dev/null
+++ b/assets/images/charts/heatmap.svg
@@ -0,0 +1,27 @@
+
+
diff --git a/assets/images/charts/histogram.svg b/assets/images/charts/histogram.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b61f86c45b090e81d4d4196851acc30ff32dfc2b
--- /dev/null
+++ b/assets/images/charts/histogram.svg
@@ -0,0 +1,25 @@
+
+
diff --git a/assets/images/charts/line.svg b/assets/images/charts/line.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e6a042d249f9aa11a1e7c7dc5a544864092b8252
--- /dev/null
+++ b/assets/images/charts/line.svg
@@ -0,0 +1,17 @@
+
+
diff --git a/assets/images/charts/lollipop.svg b/assets/images/charts/lollipop.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f04bc8ed7ec991246cd691da7cc8b4a3ee407ec1
--- /dev/null
+++ b/assets/images/charts/lollipop.svg
@@ -0,0 +1,26 @@
+
+
diff --git a/assets/images/charts/marimekko.svg b/assets/images/charts/marimekko.svg
new file mode 100644
index 0000000000000000000000000000000000000000..38fb0de7796da695242ab565fdde7936bf5f78c5
--- /dev/null
+++ b/assets/images/charts/marimekko.svg
@@ -0,0 +1,27 @@
+
+
diff --git a/assets/images/charts/network.svg b/assets/images/charts/network.svg
new file mode 100644
index 0000000000000000000000000000000000000000..604485ef08c4bd704035ae6f09d6fbc720eb987e
--- /dev/null
+++ b/assets/images/charts/network.svg
@@ -0,0 +1,44 @@
+
+
diff --git a/assets/images/charts/ordered-bar.svg b/assets/images/charts/ordered-bar.svg
new file mode 100644
index 0000000000000000000000000000000000000000..342907fe412aa8264e4e6ee636f99dec8f1ef678
--- /dev/null
+++ b/assets/images/charts/ordered-bar.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/assets/images/charts/ordered-bubble.svg b/assets/images/charts/ordered-bubble.svg
new file mode 100644
index 0000000000000000000000000000000000000000..bef4e69296b10faae39c3a6a5450d17074b9c15e
--- /dev/null
+++ b/assets/images/charts/ordered-bubble.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/assets/images/charts/ordered-column.svg b/assets/images/charts/ordered-column.svg
new file mode 100644
index 0000000000000000000000000000000000000000..fca0509d637f095c2d881f6496610c55bb6abd6a
--- /dev/null
+++ b/assets/images/charts/ordered-column.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/assets/images/charts/paired-bar.svg b/assets/images/charts/paired-bar.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2f75406e7fb3d6815083a92a05600922b0069ab1
--- /dev/null
+++ b/assets/images/charts/paired-bar.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/paired-column.svg b/assets/images/charts/paired-column.svg
new file mode 100644
index 0000000000000000000000000000000000000000..925d74f7e00ed697586234bb43e88263ec594206
--- /dev/null
+++ b/assets/images/charts/paired-column.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/charts/parallel-coordinates.svg b/assets/images/charts/parallel-coordinates.svg
new file mode 100644
index 0000000000000000000000000000000000000000..16c16d68c11e4ca810be7dfec3cfab177ec80477
--- /dev/null
+++ b/assets/images/charts/parallel-coordinates.svg
@@ -0,0 +1,26 @@
+
+
diff --git a/assets/images/charts/pictogram.svg b/assets/images/charts/pictogram.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c7a787b0a5ee2e44631a19da9d1e98ee2c24f932
--- /dev/null
+++ b/assets/images/charts/pictogram.svg
@@ -0,0 +1,63 @@
+
+
diff --git a/assets/images/charts/pie.svg b/assets/images/charts/pie.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c32e7c8b10e7aa6afae2b73e8c6ac298afa82a55
--- /dev/null
+++ b/assets/images/charts/pie.svg
@@ -0,0 +1,29 @@
+
+
diff --git a/assets/images/charts/radar.svg b/assets/images/charts/radar.svg
new file mode 100644
index 0000000000000000000000000000000000000000..5906b0740b38318e67eb4deb5e485232c24c0ee3
--- /dev/null
+++ b/assets/images/charts/radar.svg
@@ -0,0 +1,33 @@
+
+
diff --git a/assets/images/charts/radial.svg b/assets/images/charts/radial.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0b8f96a90767250d7acab6669b769c18021e8fa8
--- /dev/null
+++ b/assets/images/charts/radial.svg
@@ -0,0 +1,18 @@
+
+
diff --git a/assets/images/charts/sankey.svg b/assets/images/charts/sankey.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9e9ad9d6837437b13125754c0f171ec0b8e90fa4
--- /dev/null
+++ b/assets/images/charts/sankey.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/assets/images/charts/scatter-matrix.svg b/assets/images/charts/scatter-matrix.svg
new file mode 100644
index 0000000000000000000000000000000000000000..add1a0995ea6a7b32e8999c994b365efe5809677
--- /dev/null
+++ b/assets/images/charts/scatter-matrix.svg
@@ -0,0 +1,176 @@
+
+
diff --git a/assets/images/charts/scatter.svg b/assets/images/charts/scatter.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3993f505f707a84c91f91fbeac0310a693dc7e3e
--- /dev/null
+++ b/assets/images/charts/scatter.svg
@@ -0,0 +1,56 @@
+
+
diff --git a/assets/images/charts/slope.svg b/assets/images/charts/slope.svg
new file mode 100644
index 0000000000000000000000000000000000000000..01b770af55c148ac2a2dba1a3d9f7788eb50f8b3
--- /dev/null
+++ b/assets/images/charts/slope.svg
@@ -0,0 +1,32 @@
+
+
diff --git a/assets/images/charts/sparkline.svg b/assets/images/charts/sparkline.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ed269cd5380c4b8e9c09d243034cadbdc7ddf89f
--- /dev/null
+++ b/assets/images/charts/sparkline.svg
@@ -0,0 +1,33 @@
+
+
diff --git a/assets/images/charts/stacked-bar.svg b/assets/images/charts/stacked-bar.svg
new file mode 100644
index 0000000000000000000000000000000000000000..432a2e4a6bd1c3d6cd04f17dd8a1d12d72848859
--- /dev/null
+++ b/assets/images/charts/stacked-bar.svg
@@ -0,0 +1,27 @@
+
+
diff --git a/assets/images/charts/stacked-column.svg b/assets/images/charts/stacked-column.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7afbe494c9fbfab991898a8e5b35f77729e19b28
--- /dev/null
+++ b/assets/images/charts/stacked-column.svg
@@ -0,0 +1,27 @@
+
+
diff --git a/assets/images/charts/stepped-line.svg b/assets/images/charts/stepped-line.svg
new file mode 100644
index 0000000000000000000000000000000000000000..5bb9c361e40c4da3e413035bd39f2f3e00269651
--- /dev/null
+++ b/assets/images/charts/stepped-line.svg
@@ -0,0 +1,19 @@
+
+
diff --git a/assets/images/charts/surplus-deficit-filled-line.svg b/assets/images/charts/surplus-deficit-filled-line.svg
new file mode 100644
index 0000000000000000000000000000000000000000..5f6baaf234ca44f999a6179c4d51a6582772909d
--- /dev/null
+++ b/assets/images/charts/surplus-deficit-filled-line.svg
@@ -0,0 +1,29 @@
+
+
diff --git a/assets/images/charts/treemap.svg b/assets/images/charts/treemap.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a09929e943478c2ed60f06df24bacfaafb2ae218
--- /dev/null
+++ b/assets/images/charts/treemap.svg
@@ -0,0 +1,24 @@
+
+
diff --git a/assets/images/charts/venn.svg b/assets/images/charts/venn.svg
new file mode 100644
index 0000000000000000000000000000000000000000..753928cf7cd1491e3e16c7d4bd2845583111c0b1
--- /dev/null
+++ b/assets/images/charts/venn.svg
@@ -0,0 +1,18 @@
+
+
diff --git a/assets/images/charts/violin.svg b/assets/images/charts/violin.svg
new file mode 100644
index 0000000000000000000000000000000000000000..537b0ecfef641bec3129cc243f0283f9fff6f0d2
--- /dev/null
+++ b/assets/images/charts/violin.svg
@@ -0,0 +1,131 @@
+
+
diff --git a/assets/images/charts/waterfall.svg b/assets/images/charts/waterfall.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3b90721897daf8203fd643ba57351e7a14cb39ab
--- /dev/null
+++ b/assets/images/charts/waterfall.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/assets/logo.svg b/assets/logo.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0904b87dea0bc5507a56061c9c6aec6c1c89446b
--- /dev/null
+++ b/assets/logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/chart_groups.py b/chart_groups.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c78f98580bda1716991a545001642e603a13ef4
--- /dev/null
+++ b/chart_groups.py
@@ -0,0 +1,261 @@
+"""Defines chart groups."""
+
+import itertools
+from dataclasses import dataclass
+from typing import List
+
+import pages.correlation
+import pages.deviation
+import pages.distribution
+import pages.flow
+import pages.magnitude
+import pages.part_to_whole
+import pages.ranking
+import pages.spatial
+import pages.time
+import vizro.models as vm
+
+
+class IncompletePage:
+ """Fake vm.Page-like class.
+
+ This has the properties required to make it function sufficiently like a page when generating the navigation cards.
+ Only the title is configurable; path is fixed to "".
+ """
+
+ def __init__(self, title): # noqa: D107
+ self.title = title
+
+ @property
+ def path(self): # noqa: D102
+ return ""
+
+
+@dataclass
+class ChartGroup:
+ """Represents a group of charts like "Deviation"."""
+
+ name: str
+ pages: List[vm.Page]
+ incomplete_pages: List[IncompletePage]
+ intro_text: str
+ icon: str = "" # ALL_CHART_GROUP is the only one that doesn't require an icon.
+
+
+deviation_intro_text = """
+Deviation enables you to draw attention to variations (+/-) from a fixed reference point.
+Often this reference point is zero, but you might also show a target or a long term average.
+You can also use deviation to express a positive, neutral or negative sentiment.
+"""
+deviation_chart_group = ChartGroup(
+ name="Deviation",
+ pages=pages.deviation.pages,
+ incomplete_pages=[
+ IncompletePage(title="Diverging bar"),
+ IncompletePage("Diverging stacked bar"),
+ IncompletePage(title="Surplus deficit filled line"),
+ ],
+ icon="Contrast Square",
+ intro_text=deviation_intro_text,
+)
+
+
+correlation_intro_text = """
+Correlation helps you show the relationship between two or more variables. It is important that you
+make it clear to your audience whether or not the relationship is causal, i.e., whether one causes the
+other.
+"""
+correlation_chart_group = ChartGroup(
+ name="Correlation",
+ pages=pages.correlation.pages,
+ incomplete_pages=[
+ IncompletePage("Heatmap matrix"),
+ ],
+ icon="Bubble Chart",
+ intro_text=correlation_intro_text,
+)
+
+
+ranking_intro_text = """
+Ranking enables you to present items in an ordered list. Use this when you want to highlight the
+position of an item rather than its absolute or relative value. You might want to emphasize the most
+interesting points with highlighting or labels to ensure the reader understands what matters most.
+"""
+ranking_chart_group = ChartGroup(
+ name="Ranking",
+ pages=pages.ranking.pages,
+ incomplete_pages=[
+ IncompletePage("Ordered bubble"),
+ IncompletePage("Slope"),
+ IncompletePage("Lollipop"),
+ IncompletePage("Bump"),
+ ],
+ icon="Stacked Bar Chart",
+ intro_text=ranking_intro_text,
+)
+
+
+distribution_intro_text = """
+Distribution helps you to present all the possible values (or intervals) of your data and how often they
+occur. You can organize the data to show the number or percentage of items in a specified group, what shape
+the group takes, where the center lies, and how much variability there is in the data. This shape
+(or _skew_) of a distribution can be a powerful way for you to highlight either the existence or lack of
+uniformity or equality in the data.
+"""
+distribution_chart_group = ChartGroup(
+ name="Distribution",
+ pages=pages.distribution.pages,
+ incomplete_pages=[
+ IncompletePage("Dot plot"),
+ IncompletePage("Barcode"),
+ IncompletePage("Cumulative curve"),
+ IncompletePage("Beeswarm"),
+ ],
+ icon="Waterfall Chart",
+ intro_text=distribution_intro_text,
+)
+
+magnitude_intro_text = """
+Magnitude allows you to emphasize size comparisons of **counted** items in your data set. You can show
+relative comparisons (whether something is larger or smaller) or absolute differences (where the nuances
+are most interesting). Typically, you will use magnitude for actual numbers versus calculated rates or
+percentages.
+"""
+magnitude_chart_group = ChartGroup(
+ name="Magnitude",
+ pages=pages.magnitude.pages,
+ incomplete_pages=[
+ IncompletePage("Marimekko"),
+ IncompletePage("Lollipop"),
+ IncompletePage("Radar"),
+ IncompletePage("Pictogram"),
+ IncompletePage("Bullet"),
+ IncompletePage("Radial"),
+ ],
+ icon="Bar Chart",
+ intro_text=magnitude_intro_text,
+)
+
+time_intro_text = """
+Time helps you draw attention to important trends emerging over a specified period. The time period you
+select could be as short as seconds or as long as centuries. What matters most is selecting the correct
+period of time to best show your audience the message they need to take away.
+"""
+time_chart_group = ChartGroup(
+ name="Time",
+ pages=pages.time.pages,
+ incomplete_pages=[
+ IncompletePage("Gantt"),
+ IncompletePage("Slope"),
+ IncompletePage("Fan"),
+ IncompletePage("Bubble timeline"),
+ IncompletePage("Sparkline"),
+ ],
+ icon="Timeline",
+ intro_text=time_intro_text,
+)
+
+
+part_to_whole_intro_text = """
+Part-to-whole helps you show how one whole item breaks down into its component parts. If you consider the
+size of the parts to be most important, a magnitude chart may be more appropriate.
+"""
+part_to_whole_chart_group = ChartGroup(
+ name="Part-to-whole",
+ pages=pages.part_to_whole.pages,
+ incomplete_pages=[
+ IncompletePage("Marimekko"),
+ IncompletePage("Arc"),
+ IncompletePage("Gridplot"),
+ IncompletePage("Venn"),
+ IncompletePage("Waterfall"),
+ ],
+ icon="Donut Small",
+ intro_text=part_to_whole_intro_text,
+)
+
+flow_intro_text = """
+With flow charts, you can highlight the quantity or the intensity of the movement between more than one
+state or condition. The flow might be steps in a logical sequence or movement between different geographical
+locations.
+"""
+flow_chart_group = ChartGroup(
+ name="Flow",
+ pages=pages.flow.pages,
+ incomplete_pages=[
+ IncompletePage("Waterfall"),
+ IncompletePage("Chord"),
+ IncompletePage("Network"),
+ ],
+ icon="Air",
+ intro_text=flow_intro_text,
+)
+
+spatial_intro_text = """
+Spatial charts allow you to demonstrate precise locations or geographical patterns in your data.
+"""
+spatial_chart_group = ChartGroup(
+ name="Spatial",
+ pages=pages.spatial.pages,
+ incomplete_pages=[
+ IncompletePage("Dot map"),
+ IncompletePage("Flow map"),
+ IncompletePage("Bubble map"),
+ ],
+ icon="Map",
+ intro_text=spatial_intro_text,
+)
+
+
+CHART_GROUPS = [
+ deviation_chart_group,
+ correlation_chart_group,
+ ranking_chart_group,
+ distribution_chart_group,
+ magnitude_chart_group,
+ time_chart_group,
+ part_to_whole_chart_group,
+ flow_chart_group,
+ spatial_chart_group,
+]
+
+all_intro_text = """
+This dashboard shows a gallery of charts. It includes guidance on when to use each chart type and sample Python code
+to create them using [Plotly](https://plotly.com/python/) and [Vizro](https://github.com/mckinsey/vizro).
+
+
+
+
+
+
+
+**Created by:**
+- [Huong Li Nguyen](https://github.com/huong-li-nguyen) and [Antony Milne](https://github.com/antonymilne)
+
+- Images created by QuantumBlack
+
+**Inspired by:**
+- [The FT Visual Vocabulary](https://github.com/Financial-Times/chart-doctor/blob/main/visual-vocabulary/README.md):
+Alan Smith, Chris Campbell, Ian Bott, Liz Faunce, Graham Parrish, Billy Ehrenberg, Paul McCallum,Martin Stabe.
+
+- [The Graphic Continuum](https://www.informationisbeautifulawards.com/showcase/611-the-graphic-continuum):
+Jon Swabish and Severino Ribecca
+
+
+
+**Credits and sources:**
+
+- [Plotly](https://plotly.com/python/plotly-express/)
+- [Guide to data chart mastery](https://www.atlassian.com/data/charts)
+
+"""
+
+
+# This contains all pages used across all chart groups, without de-duplicating. De-duplication is done where required
+# by remove_duplicates.
+ALL_CHART_GROUP = ChartGroup(
+ name="All",
+ pages=list(itertools.chain(*(chart_group.pages for chart_group in CHART_GROUPS))),
+ incomplete_pages=list(itertools.chain(*(chart_group.incomplete_pages for chart_group in CHART_GROUPS))),
+ intro_text=all_intro_text,
+)
diff --git a/custom_charts.py b/custom_charts.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a99bb5fdc4d5effc433c202e8455805dfecba4a
--- /dev/null
+++ b/custom_charts.py
@@ -0,0 +1,144 @@
+"""Contains custom charts used inside the dashboard."""
+
+from typing import List
+
+import pandas as pd
+import vizro.plotly.express as px
+from plotly import graph_objects as go
+from plotly.subplots import make_subplots
+from vizro.models.types import capture
+
+
+# TODO: consider how this should be represented in the code example files. Since the code is copy and pasted
+# it can get out of sync. But probably we don't want the docstrings in the short code snippet.
+# Ultimately these charts will probably move to vizro.charts anyway.
+@capture("graph")
+def butterfly(data_frame: pd.DataFrame, x1: str, x2: str, y: str) -> go.Figure:
+ """Creates a custom butterfly chart using Plotly's go.Figure.
+
+ A butterfly chart is a type of bar chart where two sets of bars are displayed back-to-back, often used to compare
+ two sets of data.
+
+ Args:
+ data_frame (pd.DataFrame): The data source for the chart.
+ x1 (str): The name of the column in the data frame for the first set of bars (negative values).
+ x2 (str): The name of the column in the data frame for the second set of bars (positive values).
+ y (str): The name of the column in the data frame for the y-axis (categories).
+
+ Returns:
+ go.Figure: A Plotly Figure object representing the butterfly chart.
+
+ """
+ fig = go.Figure()
+ fig.add_trace(
+ go.Bar(
+ x=-data_frame[x1],
+ y=data_frame[y],
+ orientation="h",
+ name=x1,
+ )
+ )
+ fig.add_trace(
+ go.Bar(
+ x=data_frame[x2],
+ y=data_frame[y],
+ orientation="h",
+ name=x2,
+ )
+ )
+ fig.update_layout(barmode="relative")
+ return fig
+
+
+@capture("graph")
+def sankey(data_frame: pd.DataFrame, source: str, target: str, value: str, labels: List[str]) -> go.Figure:
+ """Creates a custom sankey chart using Plotly's `go.Sankey`.
+
+ A Sankey chart is a type of flow diagram where the width of the arrows is proportional to the flow rate.
+ It is used to visualize the flow of resources or data between different stages or categories.
+
+ Args:
+ data_frame (pd.DataFrame): The data source for the chart.
+ source (str): The name of the column in the data frame for the source nodes.
+ target (str): The name of the column in the data frame for the target nodes.
+ value (str): The name of the column in the data frame for the values representing the flow between nodes.
+ labels (List[str]): A list of labels for the nodes.
+
+ Returns:
+ go.Figure: A Plotly Figure object representing the Sankey chart.
+
+ For detailed information on additional parameters and customization, refer to the Plotly documentation:
+ https://plotly.com/python/reference/sankey/
+
+ """
+ fig = go.Figure(
+ data=[
+ go.Sankey(
+ node={
+ "pad": 16,
+ "thickness": 16,
+ "label": labels,
+ },
+ link={
+ "source": data_frame[source],
+ "target": data_frame[target],
+ "value": data_frame[value],
+ "label": labels,
+ "color": "rgba(205, 209, 228, 0.4)",
+ },
+ )
+ ]
+ )
+ fig.update_layout(barmode="relative")
+ return fig
+
+
+@capture("graph")
+def column_and_line(data_frame: pd.DataFrame, x: str, y_column: str, y_line: str) -> go.Figure:
+ """Creates a combined column and line chart using Plotly.
+
+ This function generates a chart with a bar graph for one variable (y-axis 1) and a line graph for another variable
+ (y-axis 2), sharing the same x-axis. The y-axes for the bar and line graphs are synchronized and overlaid.
+
+ Args:
+ data_frame (pd.DataFrame): The data source for the chart.
+ x (str): The column name to be used for the x-axis.
+ y_column (str): The column name to be used for the y-axis 1, representing the column chart.
+ y_line (str): The column name to be used for the y-axis 2, representing the line chart.
+
+ Returns:
+ go.Figure: : A Plotly Figure object representing the combined column and line chart.
+
+ """
+ fig = make_subplots(specs=[[{"secondary_y": True}]])
+
+ fig.add_trace(
+ go.Bar(x=data_frame[x], y=data_frame[y_column], name=y_column),
+ secondary_y=False,
+ )
+
+ fig.add_trace(
+ go.Scatter(x=data_frame[x], y=data_frame[y_line], name=y_line),
+ secondary_y=True,
+ )
+
+ fig.update_layout(
+ xaxis={"type": "category", "title": x},
+ yaxis={"tickmode": "sync", "title": y_column},
+ yaxis2={"tickmode": "sync", "overlaying": "y", "title": y_line},
+ )
+
+ return fig
+
+
+@capture("graph")
+def categorical_column(data_frame: pd.DataFrame, x: str, y: str):
+ """Creates a column chart where the x-axis values are converted to category type."""
+ fig = px.bar(
+ data_frame,
+ y=y,
+ x=x,
+ )
+ # So ticks are aligned with bars when xaxes values are numbers (e.g. years)
+ fig.update_xaxes(type="category")
+ return fig
diff --git a/custom_components.py b/custom_components.py
new file mode 100644
index 0000000000000000000000000000000000000000..26ec8e0e4a9d1a7a55e2378bae2c500e407cfe7a
--- /dev/null
+++ b/custom_components.py
@@ -0,0 +1,71 @@
+"""Contains custom components used inside the dashboard."""
+
+from typing import Literal
+
+import dash_bootstrap_components as dbc
+import vizro.models as vm
+from dash import dcc, html
+
+try:
+ from pydantic.v1 import Field
+except ImportError: # pragma: no cov
+ from pydantic import Field
+
+
+class CodeClipboard(vm.VizroBaseModel):
+ """Code snippet with a copy to clipboard button."""
+
+ type: Literal["code_clipboard"] = "code_clipboard"
+ code: str
+ language: str = ""
+
+ def build(self):
+ """Returns the code clipboard component inside an accordion."""
+ markdown_code = "\n".join([f"```{self.language}", self.code, "```"])
+ return dbc.Accordion(
+ [
+ dbc.AccordionItem(
+ html.Div(
+ [
+ dcc.Markdown(markdown_code, id=self.id),
+ dcc.Clipboard(target_id=self.id, className="code-clipboard"),
+ ],
+ className="code-clipboard-container",
+ ),
+ title="SHOW CODE",
+ )
+ ],
+ start_collapsed=False,
+ )
+
+
+class Markdown(vm.VizroBaseModel):
+ """Markdown component."""
+
+ type: Literal["markdown"] = "markdown"
+ text: str = Field(
+ ..., description="Markdown string to create card title/text that should adhere to the CommonMark Spec."
+ )
+ classname: str = ""
+
+ def build(self):
+ """Returns a markdown component with an optional classname."""
+ return dcc.Markdown(id=self.id, children=self.text, dangerously_allow_html=False, className=self.classname)
+
+
+class FlexContainer(vm.Container):
+ """Custom flex `Container`."""
+
+ type: Literal["flex_container"] = "flex_container"
+ title: str = None # Title exists in vm.Container but we don't want to use it here.
+
+ def build(self):
+ """Returns a flex container."""
+ return html.Div(
+ id=self.id, children=[component.build() for component in self.components], className="flex-container"
+ )
+
+
+vm.Container.add_type("components", FlexContainer)
+vm.Container.add_type("components", Markdown)
+vm.Page.add_type("components", CodeClipboard)
diff --git a/pages/__init__.py b/pages/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc121d915ec5e666e2ce087b9740ee41c5f54664
--- /dev/null
+++ b/pages/__init__.py
@@ -0,0 +1,5 @@
+# TODO: eventually deduplicate page generation into a function rather than copying and pasting across files?
+# TODO: think about the best way to do code examples, e.g.
+# - do we want full dashboard example or plot-only example?
+# - or both? Could be done using a toggle switch or multiple tabs.
+# - a link to py.cafe showing the dashboard code?
diff --git a/pages/_factories.py b/pages/_factories.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4b6b4ffdcab18369f915ad4458ebd523fe9f368
--- /dev/null
+++ b/pages/_factories.py
@@ -0,0 +1,112 @@
+"""Contains reusable page functions to create identical content with a different `id`.
+
+Note: Since each page can only belong to one navigation group, we need a new page with a unique ID for
+each chart type used in different groups.
+"""
+
+import vizro.models as vm
+import vizro.plotly.express as px
+from custom_charts import butterfly, column_and_line
+
+from pages._pages_utils import PAGE_GRID, ages, gapminder, make_code_clipboard_from_py_file
+
+
+def butterfly_factory(group: str):
+ """Reusable function to create the page content for the butterfly chart with a unique ID."""
+ return vm.Page(
+ id=f"{group}-butterfly",
+ path=f"{group}/butterfly",
+ title="Butterfly",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a butterfly chart?
+
+ A butterfly chart (also called a tornado chart) is a bar chart for displaying two sets of data series
+ side by side.
+
+
+
+ #### When should I use it?
+
+ Use a butterfly chart when you wish to emphasize the comparison between two data sets sharing the same
+ parameters. Sharing this chart with your audience will help them see at a glance how two groups differ
+ within the same parameters. You can also **stack** two bars on each side if you wish to divide your
+ categories.
+ """
+ ),
+ vm.Graph(figure=butterfly(ages, x1="Male", x2="Female", y="Age")),
+ make_code_clipboard_from_py_file("butterfly.py"),
+ ],
+ )
+
+
+def connected_scatter_factory(group: str):
+ """Reusable function to create the page content for the column chart with a unique ID."""
+ return vm.Page(
+ id=f"{group}-connected-scatter",
+ path=f"{group}/connected-scatter",
+ title="Connected scatter",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a connected scatter chart?
+
+ A connected scatter chart visualizes two variables (x and y) using dots, with lines connecting the dots
+ in the order of the data points. One variable is plotted along the x-axis and the other along the
+ y-axis, showing both the relationship and a sequence of the data.
+
+
+
+ #### When should I use it?
+
+ Use connected scatter charts to show the relationship between two variables and the sequence of data
+ points. They are ideal for paired numerical data, helping to reveal trends and patterns over time or in
+ a specific order. Remember, correlation is not causation, so ensure your audience understands this to
+ avoid misinterpretation.
+ """
+ ),
+ vm.Graph(figure=px.line(gapminder.query("country == 'Australia'"), x="year", y="lifeExp", markers=True)),
+ make_code_clipboard_from_py_file("connected_scatter.py"),
+ ],
+ )
+
+
+def column_and_line_factory(group: str):
+ """Reusable function to create the page content for the column+line chart with a unique ID."""
+ return vm.Page(
+ id=f"{group}-column-and-line",
+ path=f"{group}/column-and-line",
+ title="Column and line",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a column and line chart?
+
+ A combined column and line chart helps you demonstrate the relationship between an amount
+ (displayed in columns) and a trend or rate (displayed as a line running across the columns).
+
+
+
+ #### When should I use it?
+
+ Use this type of chart when you wish to compare quantities of one item with changes in another item.
+ It's ideal for showing patterns over time (e.g., monthly sales and growth rates) but can also be used
+ for other types of data comparisons.
+ """
+ ),
+ vm.Graph(
+ figure=column_and_line(
+ gapminder.query("country == 'Vietnam'"),
+ y_column="gdpPercap",
+ y_line="lifeExp",
+ x="year",
+ )
+ ),
+ make_code_clipboard_from_py_file("column_and_line.py"),
+ ],
+ )
diff --git a/pages/_pages_utils.py b/pages/_pages_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae917617c838fdf30304a476b9a160361ba2913d
--- /dev/null
+++ b/pages/_pages_utils.py
@@ -0,0 +1,53 @@
+"""Contains reusable data sets and constants."""
+
+from pathlib import Path
+
+import black
+import pandas as pd
+import vizro.plotly.express as px
+from custom_components import CodeClipboard
+
+
+def make_code_clipboard_from_py_file(filepath: str):
+ # Black doesn't yet have a Python API, so format_str might not work at some point in the future.
+ # https://black.readthedocs.io/en/stable/faq.html#does-black-have-an-api
+ filepath = Path(__file__).parents[1] / "pages/examples" / filepath
+ return CodeClipboard(
+ code=black.format_str(filepath.read_text(encoding="utf-8"), mode=black.Mode(line_length=80)),
+ language="python",
+ )
+
+
+PAGE_GRID = [[0, 0, 0, 0, 0, 0, 0]] * 2 + [[1, 1, 1, 1, 2, 2, 2]] * 5
+
+# DATA --------------------------------------------------------------
+gapminder = px.data.gapminder()
+iris = px.data.iris()
+stocks = px.data.stocks()
+tips = px.data.tips()
+
+ages = pd.DataFrame(
+ {
+ "Age": ["0-19", "20-29", "30-39", "40-49", "50-59", ">=60"],
+ "Male": [800, 2000, 4200, 5000, 2100, 800],
+ "Female": [1000, 3000, 3500, 3800, 3600, 700],
+ }
+)
+sankey_data = pd.DataFrame(
+ {
+ "Origin": [0, 1, 0, 2, 3, 3],
+ "Destination": [2, 3, 3, 4, 4, 5],
+ "Value": [8, 4, 2, 8, 4, 2],
+ }
+)
+
+funnel_data = pd.DataFrame(
+ {"Stage": ["Leads", "Sales calls", "Follow-up", "Conversion", "Sales"], "Value": [10, 7, 4, 2, 1]}
+)
+
+stepped_line_data = pd.DataFrame(
+ {
+ "year": [1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003],
+ "rate": [0.10, 0.12, 0.15, 0.13, 0.14, 0.13, 0.14, 0.16, 0.15],
+ }
+)
diff --git a/pages/correlation.py b/pages/correlation.py
new file mode 100644
index 0000000000000000000000000000000000000000..199042261b69f346cf40da233c4186336d43e5c0
--- /dev/null
+++ b/pages/correlation.py
@@ -0,0 +1,100 @@
+"""Correlation charts."""
+
+import vizro.models as vm
+import vizro.plotly.express as px
+
+from pages._factories import column_and_line_factory, connected_scatter_factory
+from pages._pages_utils import PAGE_GRID, gapminder, iris, make_code_clipboard_from_py_file
+
+scatter = vm.Page(
+ title="Scatter",
+ path="correlation/scatter",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a scatter chart?
+
+ A scatter plot is a two-dimensional data visualization using dots to represent the values obtained for two
+ different variables - one plotted along the x-axis and the other plotted along the y-axis.
+
+
+
+ #### When should I use it?
+
+ Use scatter plots when you want to show the relationship between two variables. Scatter plots are sometimes
+ called _Correlation plots_ because they show how two variables are correlated. Scatter plots are ideal when
+ you have paired numerical data and you want to see if one variable impacts the other. However, do remember
+ that correlation is not causation. Make sure your audience does not draw the wrong conclusions.
+ """
+ ),
+ vm.Graph(figure=px.scatter(iris, x="sepal_width", y="sepal_length", color="species")),
+ make_code_clipboard_from_py_file("scatter.py"),
+ ],
+)
+
+connected_scatter = connected_scatter_factory("correlation")
+
+scatter_matrix = vm.Page(
+ title="Scatter matrix",
+ path="correlation/scatter-matrix",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a scatter matrix?
+
+ A scatter matrix, also known as a SPLOM chart, is a multi-dimensional data visualization that displays
+ scatter plots for every pair of variables in a dataset. Each scatter plot is positioned in a matrix format,
+ where rows and columns represent different variables.
+
+
+
+ #### When should I use it?
+
+ Use a scatter matrix when you want to explore relationships between multiple pairs of variables
+ simultaneously. They are particularly useful for identifying correlations, patterns, and potential outliers
+ within a dataset containing multiple numerical variables. Carefully select the most relevant variables to
+ ensure clarity and readability of the chart.
+ """
+ ),
+ vm.Graph(
+ figure=px.scatter_matrix(iris, dimensions=["sepal_length", "sepal_width", "petal_length", "petal_width"])
+ ),
+ make_code_clipboard_from_py_file("scatter_matrix.py"),
+ ],
+)
+
+bubble = vm.Page(
+ title="Bubble",
+ path="correlation/bubble",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a bubble chart?
+
+ A bubble chart is a type of data visualization that displays three dimensions of data. Each point on the
+ chart is represented by a bubble, where the x-axis and y-axis denote two of the data dimensions, and the
+ size of the bubble represents the third dimension.
+
+
+
+ #### When should I use it?
+
+ Use a bubble chart when you want to explore and compare relationships between three variables
+ simultaneously. They are particularly useful for identifying patterns, trends, and outliers in
+ multi-dimensional data. Bubble charts can help you visualize the impact of a third variable,
+ providing deeper insights than a standard scatter plot.
+ """
+ ),
+ vm.Graph(figure=px.scatter(gapminder.query("year==2007"), x="gdpPercap", y="lifeExp", size="pop", size_max=60)),
+ make_code_clipboard_from_py_file("bubble.py"),
+ ],
+)
+
+column_and_line = column_and_line_factory("correlation")
+pages = [scatter, connected_scatter, scatter_matrix, bubble, column_and_line]
diff --git a/pages/deviation.py b/pages/deviation.py
new file mode 100644
index 0000000000000000000000000000000000000000..93b4f731f6ede284de68d812e0d2bb09fcb79d33
--- /dev/null
+++ b/pages/deviation.py
@@ -0,0 +1,7 @@
+"""Deviation charts."""
+
+from pages._factories import butterfly_factory
+
+butterfly = butterfly_factory("deviation")
+
+pages = [butterfly]
diff --git a/pages/distribution.py b/pages/distribution.py
new file mode 100644
index 0000000000000000000000000000000000000000..91c0116cdb6397cad5b0249aa20d569c3ad956b4
--- /dev/null
+++ b/pages/distribution.py
@@ -0,0 +1,110 @@
+"""Distribution charts."""
+
+import vizro.models as vm
+import vizro.plotly.express as px
+
+from pages._factories import butterfly_factory
+from pages._pages_utils import PAGE_GRID, make_code_clipboard_from_py_file, tips
+
+violin = vm.Page(
+ title="Violin",
+ path="distribution/violin",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a violin chart?
+
+ A violin chart is similar to a box plot, but works better for visualizing more complex distributions and
+ their probability density at different values.
+
+
+
+ #### When should I use it?
+
+ Use this chart to go beyond the simple box plot and show the distribution shape of the data, the
+ inter-quartile range, the confidence intervals and the median.
+ """
+ ),
+ vm.Graph(
+ figure=px.violin(
+ tips,
+ y="total_bill",
+ x="day",
+ color="day",
+ box=True,
+ )
+ ),
+ make_code_clipboard_from_py_file("violin.py"),
+ ],
+)
+
+boxplot = vm.Page(
+ title="Boxplot",
+ path="distribution/boxplot",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a boxplot?
+
+ A box plot (also known as whisker plot) provides a visual display of multiple datasets,
+ indicating the median (center) and the range of the data for each.
+
+
+
+ #### When should I use it?
+
+ Choose a box plot when you need to summarize distributions between many groups or datasets. It takes up
+ less space than many other charts.
+
+ Create boxes to display the median, and the upper and lower quartiles. Add whiskers to highlight
+ variability outside the upper and lower quartiles. You can add outliers as dots beyond, but in line with
+ the whiskers.
+ """
+ ),
+ vm.Graph(
+ figure=px.box(
+ tips,
+ y="total_bill",
+ x="day",
+ color="day",
+ )
+ ),
+ make_code_clipboard_from_py_file("boxplot.py"),
+ ],
+)
+
+butterfly = butterfly_factory("distribution")
+
+histogram = vm.Page(
+ title="Histogram",
+ path="distribution/histogram",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a histogram?
+
+ A histogram organizes numerical data into columns, with the size of each column representing how frequently
+ values fall within specified ranges. It visualizes data across a continuous interval.
+
+
+
+ #### When should I use it?
+
+ A histogram is useful for showing your audience where specific values are concentrated, identifying the
+ extremes, and spotting any gaps or outliers. It can also help you visualize a rough probability
+ distribution. Ensure that the gaps between columns are minimal to make the 'shape' of your data
+ immediately clear.
+ """
+ ),
+ vm.Graph(figure=px.histogram(tips, x="total_bill")),
+ make_code_clipboard_from_py_file("histogram.py"),
+ ],
+)
+
+
+pages = [violin, boxplot, butterfly, histogram]
diff --git a/pages/examples/__init__.py b/pages/examples/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b1cac5fb954bf52640acde29fbe7f6ab8a851a66
--- /dev/null
+++ b/pages/examples/__init__.py
@@ -0,0 +1 @@
+"""Contains code examples inserted into `CodeClipboard`."""
diff --git a/pages/examples/area.py b/pages/examples/area.py
new file mode 100644
index 0000000000000000000000000000000000000000..50d0d3ae27ffce1ca1946d655476c7e83414b421
--- /dev/null
+++ b/pages/examples/area.py
@@ -0,0 +1,13 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+stocks = px.data.stocks()
+
+page = vm.Page(
+ title="Area",
+ components=[vm.Graph(figure=px.area(stocks, x="date", y="GOOG"))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/bar.py b/pages/examples/bar.py
new file mode 100644
index 0000000000000000000000000000000000000000..6afdd3040341e4f441255a958700483334bb140c
--- /dev/null
+++ b/pages/examples/bar.py
@@ -0,0 +1,24 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+gapminder = px.data.gapminder()
+
+page = vm.Page(
+ title="Bar",
+ components=[
+ vm.Graph(
+ figure=px.bar(
+ gapminder.query(
+ "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])"
+ ),
+ x="pop",
+ y="country",
+ orientation="h",
+ )
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/boxplot.py b/pages/examples/boxplot.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce9c79e836824c35e47f1df11c225f2683d1b89c
--- /dev/null
+++ b/pages/examples/boxplot.py
@@ -0,0 +1,15 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+page = vm.Page(
+ title="Boxplot",
+ components=[
+ vm.Graph(figure=px.boxplot(tips, y="total_bill", x="day", color="day")),
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/bubble.py b/pages/examples/bubble.py
new file mode 100644
index 0000000000000000000000000000000000000000..6da0aa8166486b037c304cbef70eba2607000149
--- /dev/null
+++ b/pages/examples/bubble.py
@@ -0,0 +1,15 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+gapminder = px.data.gapminder()
+
+page = vm.Page(
+ title="Bubble",
+ components=[
+ vm.Graph(figure=px.scatter(gapminder.query("year==2007"), x="gdpPercap", y="lifeExp", size="pop", size_max=60))
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/butterfly.py b/pages/examples/butterfly.py
new file mode 100644
index 0000000000000000000000000000000000000000..69c66ed8db82c96d0452d57bf7f2027988fe9912
--- /dev/null
+++ b/pages/examples/butterfly.py
@@ -0,0 +1,47 @@
+import pandas as pd
+import plotly.graph_objects as go
+import vizro.models as vm
+from vizro import Vizro
+from vizro.models.types import capture
+
+ages = pd.DataFrame(
+ {
+ "Age": ["0-19", "20-29", "30-39", "40-49", "50-59", ">=60"],
+ "Male": [800, 2000, 4200, 5000, 2100, 800],
+ "Female": [1000, 3000, 3500, 3800, 3600, 700],
+ }
+)
+
+
+@capture("graph")
+def butterfly(data_frame: pd.DataFrame, x1: str, x2: str, y: str):
+ fig = go.Figure()
+ fig.add_trace(
+ go.Bar(
+ x=-data_frame[x1],
+ y=data_frame[y],
+ orientation="h",
+ name=x1,
+ )
+ )
+ fig.add_trace(
+ go.Bar(
+ x=data_frame[x2],
+ y=data_frame[y],
+ orientation="h",
+ name=x2,
+ )
+ )
+ fig.update_layout(barmode="relative")
+ return fig
+
+
+dashboard = vm.Dashboard(
+ pages=[
+ vm.Page(
+ title="Butterfly",
+ components=[vm.Graph(figure=butterfly(ages, x1="Male", x2="Female", y="Age"))],
+ )
+ ]
+)
+Vizro().build(dashboard).run()
diff --git a/pages/examples/choropleth.py b/pages/examples/choropleth.py
new file mode 100644
index 0000000000000000000000000000000000000000..95c5cac5f7104e62fe0968db549d770f3ac28923
--- /dev/null
+++ b/pages/examples/choropleth.py
@@ -0,0 +1,19 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+gapminder = px.data.gapminder()
+
+page = vm.Page(
+ title="Choropleth",
+ components=[
+ vm.Graph(
+ figure=px.choropleth(
+ gapminder.query("year == 2007"), locations="iso_alpha", color="lifeExp", hover_name="country"
+ )
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/column_and_line.py b/pages/examples/column_and_line.py
new file mode 100644
index 0000000000000000000000000000000000000000..85a5fb11688f7303dafaadbd546348d8ca38de75
--- /dev/null
+++ b/pages/examples/column_and_line.py
@@ -0,0 +1,53 @@
+import pandas as pd
+import plotly.graph_objects as go
+import vizro.models as vm
+import vizro.plotly.express as px
+from plotly.subplots import make_subplots
+from vizro import Vizro
+from vizro.models.types import capture
+
+gapminder = px.data.gapminder()
+
+
+@capture("graph")
+def column_and_line(data_frame: pd.DataFrame, x: str, y_column: str, y_line: str) -> go.Figure:
+ """Creates a combined column and line chart using Plotly."""
+ fig = make_subplots(specs=[[{"secondary_y": True}]])
+
+ fig.add_trace(
+ go.Bar(x=data_frame[x], y=data_frame[y_column], name=y_column),
+ secondary_y=False,
+ )
+
+ fig.add_trace(
+ go.Scatter(x=data_frame[x], y=data_frame[y_line], name=y_line),
+ secondary_y=True,
+ )
+
+ fig.update_layout(
+ xaxis={"type": "category", "title": x},
+ yaxis={"tickmode": "sync", "title": y_column},
+ yaxis2={"tickmode": "sync", "overlaying": "y", "title": y_line},
+ )
+
+ return fig
+
+
+dashboard = vm.Dashboard(
+ pages=[
+ vm.Page(
+ title="Column and line",
+ components=[
+ vm.Graph(
+ figure=column_and_line(
+ gapminder.query("country == 'Vietnam'"),
+ y_column="gdpPercap",
+ y_line="lifeExp",
+ x="year",
+ )
+ )
+ ],
+ )
+ ]
+)
+Vizro().build(dashboard).run()
diff --git a/pages/examples/connected_scatter.py b/pages/examples/connected_scatter.py
new file mode 100644
index 0000000000000000000000000000000000000000..68da341c940f1c9d988364279b5d8d44a1870353
--- /dev/null
+++ b/pages/examples/connected_scatter.py
@@ -0,0 +1,15 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+gapminder = px.data.gapminder()
+
+page = vm.Page(
+ title="Connected scatter",
+ components=[
+ vm.Graph(figure=px.line(gapminder.query("country == 'Australia'"), x="year", y="lifeExp", markers=True))
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/donut.py b/pages/examples/donut.py
new file mode 100644
index 0000000000000000000000000000000000000000..3005c2fdcd8332d50e8625c125c6650ecf42c41b
--- /dev/null
+++ b/pages/examples/donut.py
@@ -0,0 +1,13 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+page = vm.Page(
+ title="Donut",
+ components=[vm.Graph(figure=px.pie(tips, values="tip", names="day", hole=0.4))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/funnel.py b/pages/examples/funnel.py
new file mode 100644
index 0000000000000000000000000000000000000000..221430cbf654f51e848437a4239da61058e0bf72
--- /dev/null
+++ b/pages/examples/funnel.py
@@ -0,0 +1,16 @@
+import pandas as pd
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+funnel_data = pd.DataFrame(
+ {"Stage": ["Leads", "Sales calls", "Follow-up", "Conversion", "Sales"], "Value": [10, 7, 4, 2, 1]}
+)
+
+page = vm.Page(
+ title="Funnel",
+ components=[vm.Graph(figure=px.funnel_area(funnel_data, names="Stage", values="Value"))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/heatmap.py b/pages/examples/heatmap.py
new file mode 100644
index 0000000000000000000000000000000000000000..350e9e8234ab7c790bf63c1c8aaecb5caa8110b8
--- /dev/null
+++ b/pages/examples/heatmap.py
@@ -0,0 +1,15 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+page = vm.Page(
+ title="Heatmap",
+ components=[
+ vm.Graph(figure=px.density_heatmap(tips, x="day", y="size", z="tip", histfunc="avg", text_auto="$.2f")),
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/histogram.py b/pages/examples/histogram.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc5b3856d2db70b2b887dd200fa2c696a7769e6b
--- /dev/null
+++ b/pages/examples/histogram.py
@@ -0,0 +1,13 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+page = vm.Page(
+ title="Histogram",
+ components=[vm.Graph(px.histogram(tips, x="total_bill"))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/line.py b/pages/examples/line.py
new file mode 100644
index 0000000000000000000000000000000000000000..c442a89949f229fe3383f0ce41bbf11a2198e441
--- /dev/null
+++ b/pages/examples/line.py
@@ -0,0 +1,13 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+stocks = px.data.stocks()
+
+page = vm.Page(
+ title="Line",
+ components=[vm.Graph(figure=px.line(stocks, x="date", y="GOOG"))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/magnitude_column.py b/pages/examples/magnitude_column.py
new file mode 100644
index 0000000000000000000000000000000000000000..04635ca85ac5d491db67586d96bb6a5c6dbbccf6
--- /dev/null
+++ b/pages/examples/magnitude_column.py
@@ -0,0 +1,23 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+gapminder = px.data.gapminder()
+
+page = vm.Page(
+ title="Column",
+ components=[
+ vm.Graph(
+ figure=px.bar(
+ gapminder.query(
+ "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])"
+ ),
+ y="pop",
+ x="country",
+ )
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/ordered_bar.py b/pages/examples/ordered_bar.py
new file mode 100644
index 0000000000000000000000000000000000000000..316b0b031a838e2cfc36a6b936dea9ace50763f5
--- /dev/null
+++ b/pages/examples/ordered_bar.py
@@ -0,0 +1,25 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+gapminder = px.data.gapminder()
+
+
+page = vm.Page(
+ title="Ordered bar",
+ components=[
+ vm.Graph(
+ figure=px.bar(
+ gapminder.query(
+ "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])"
+ ).sort_values("pop"),
+ x="pop",
+ y="country",
+ orientation="h",
+ )
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/ordered_column.py b/pages/examples/ordered_column.py
new file mode 100644
index 0000000000000000000000000000000000000000..07bb37fe77c3b5b72c520e02fc9025fc3e485a6d
--- /dev/null
+++ b/pages/examples/ordered_column.py
@@ -0,0 +1,24 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+gapminder = px.data.gapminder()
+
+
+page = vm.Page(
+ title="Ordered column",
+ components=[
+ vm.Graph(
+ figure=px.bar(
+ gapminder.query(
+ "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])"
+ ).sort_values("pop"),
+ y="pop",
+ x="country",
+ )
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/paired_bar.py b/pages/examples/paired_bar.py
new file mode 100644
index 0000000000000000000000000000000000000000..0d15e480c0156f981aa4a906a9bb5b124a9e4720
--- /dev/null
+++ b/pages/examples/paired_bar.py
@@ -0,0 +1,26 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+
+page = vm.Page(
+ title="Paired bar",
+ components=[
+ vm.Graph(
+ figure=px.histogram(
+ tips,
+ y="day",
+ x="total_bill",
+ color="sex",
+ barmode="group",
+ orientation="h",
+ category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]},
+ ),
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/paired_column.py b/pages/examples/paired_column.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6292c14c20878b070ba848ab743312cd7899e7b
--- /dev/null
+++ b/pages/examples/paired_column.py
@@ -0,0 +1,25 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+
+page = vm.Page(
+ title="Paired column",
+ components=[
+ vm.Graph(
+ figure=px.histogram(
+ tips,
+ x="day",
+ y="total_bill",
+ color="sex",
+ barmode="group",
+ category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]},
+ ),
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/parallel_coordinates.py b/pages/examples/parallel_coordinates.py
new file mode 100644
index 0000000000000000000000000000000000000000..c372ec9a775e08c5ed4b17bb79f3b0bccf0373ba
--- /dev/null
+++ b/pages/examples/parallel_coordinates.py
@@ -0,0 +1,19 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+iris = px.data.iris()
+
+page = vm.Page(
+ title="Parallel coordinates",
+ components=[
+ vm.Graph(
+ figure=px.parallel_coordinates(
+ iris, color="species_id", dimensions=["sepal_width", "sepal_length", "petal_width", "petal_length"]
+ )
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/pie.py b/pages/examples/pie.py
new file mode 100644
index 0000000000000000000000000000000000000000..596abb2ff2dc87fb167c0d848bf7888ee9c86897
--- /dev/null
+++ b/pages/examples/pie.py
@@ -0,0 +1,13 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+page = vm.Page(
+ title="Pie",
+ components=[vm.Graph(figure=px.pie(tips, values="tip", names="day"))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/sankey.py b/pages/examples/sankey.py
new file mode 100644
index 0000000000000000000000000000000000000000..21789018a898fffd11dbd2bdd4f2ca248151c072
--- /dev/null
+++ b/pages/examples/sankey.py
@@ -0,0 +1,64 @@
+from typing import List
+
+import pandas as pd
+import plotly.graph_objects as go
+import vizro.models as vm
+from vizro import Vizro
+from vizro.models.types import capture
+
+sankey_data = pd.DataFrame(
+ {
+ "Origin": [0, 1, 0, 2, 3, 3], # indices inside labels
+ "Destination": [2, 3, 3, 4, 4, 5], # indices inside labels
+ "Value": [8, 4, 2, 8, 4, 2],
+ }
+)
+
+
+@capture("graph")
+def sankey(
+ data_frame: pd.DataFrame,
+ source: str,
+ target: str,
+ value: str,
+ labels: List[str],
+):
+ fig = go.Figure(
+ data=[
+ go.Sankey(
+ node={
+ "pad": 16,
+ "thickness": 16,
+ "label": labels,
+ },
+ link={
+ "source": data_frame[source],
+ "target": data_frame[target],
+ "value": data_frame[value],
+ "label": labels,
+ "color": "rgba(205, 209, 228, 0.4)",
+ },
+ )
+ ]
+ )
+ fig.update_layout(barmode="relative")
+ return fig
+
+
+page = vm.Page(
+ title="Sankey",
+ components=[
+ vm.Graph(
+ figure=sankey(
+ sankey_data,
+ labels=["A1", "A2", "B1", "B2", "C1", "C2"],
+ source="Origin",
+ target="Destination",
+ value="Value",
+ ),
+ ),
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/scatter.py b/pages/examples/scatter.py
new file mode 100644
index 0000000000000000000000000000000000000000..c82c95cc644fc658b9a81403ff9f6755018e55de
--- /dev/null
+++ b/pages/examples/scatter.py
@@ -0,0 +1,13 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+iris = px.data.iris()
+
+page = vm.Page(
+ title="Scatter",
+ components=[vm.Graph(figure=px.scatter(iris, x="sepal_width", y="sepal_length", color="species"))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/scatter_matrix.py b/pages/examples/scatter_matrix.py
new file mode 100644
index 0000000000000000000000000000000000000000..01b355945a5737d6f2430289c97b56874150f6e8
--- /dev/null
+++ b/pages/examples/scatter_matrix.py
@@ -0,0 +1,17 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+iris = px.data.iris()
+
+page = vm.Page(
+ title="Scatter matrix",
+ components=[
+ vm.Graph(
+ figure=px.scatter_matrix(iris, dimensions=["sepal_length", "sepal_width", "petal_length", "petal_width"])
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/stacked_bar.py b/pages/examples/stacked_bar.py
new file mode 100644
index 0000000000000000000000000000000000000000..81b38ac1f6baeb8f22563bd757de8e1bc324898a
--- /dev/null
+++ b/pages/examples/stacked_bar.py
@@ -0,0 +1,14 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+
+page = vm.Page(
+ title="Stacked bar",
+ components=[vm.Graph(figure=px.histogram(tips, y="sex", x="total_bill", color="day", orientation="h"))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/stacked_column.py b/pages/examples/stacked_column.py
new file mode 100644
index 0000000000000000000000000000000000000000..68bb9eddc8bd064bbe0bd5edc0a59a1eb89bea16
--- /dev/null
+++ b/pages/examples/stacked_column.py
@@ -0,0 +1,14 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+
+page = vm.Page(
+ title="Stacked column",
+ components=[vm.Graph(figure=px.histogram(tips, x="sex", y="total_bill", color="day"))],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/stepped_line.py b/pages/examples/stepped_line.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b97d4d4febcd30e79016f9a2181273bead9718d
--- /dev/null
+++ b/pages/examples/stepped_line.py
@@ -0,0 +1,27 @@
+import pandas as pd
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+stepped_line_data = pd.DataFrame(
+ {
+ "year": [1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003],
+ "rate": [0.10, 0.12, 0.15, 0.13, 0.14, 0.13, 0.14, 0.16, 0.15],
+ }
+)
+
+page = vm.Page(
+ title="Stepped line",
+ components=[
+ vm.Graph(
+ figure=px.line(
+ data_frame=stepped_line_data,
+ x="year",
+ y="rate",
+ line_shape="vh",
+ ),
+ )
+ ],
+)
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/time_column.py b/pages/examples/time_column.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee6f86cb799dbed2beedc859aeea0f61e30ff947
--- /dev/null
+++ b/pages/examples/time_column.py
@@ -0,0 +1,36 @@
+import pandas as pd
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+from vizro.models.types import capture
+
+gapminder = px.data.gapminder()
+
+
+@capture("graph")
+def categorical_column(data_frame: pd.DataFrame, x: str, y: str):
+ fig = px.bar(
+ data_frame,
+ y=y,
+ x=x,
+ )
+ # So ticks are aligned with bars when xaxes values are numbers (e.g. years)
+ fig.update_xaxes(type="category")
+ return fig
+
+
+page = vm.Page(
+ title="Column",
+ components=[
+ vm.Graph(
+ figure=categorical_column(
+ gapminder.query("country == 'Nigeria' and year > 1970"),
+ y="lifeExp",
+ x="year",
+ )
+ )
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/treemap.py b/pages/examples/treemap.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce94bdb07f113118b919db0614112f7cae1ef3f0
--- /dev/null
+++ b/pages/examples/treemap.py
@@ -0,0 +1,22 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+gapminder = px.data.gapminder()
+
+page = vm.Page(
+ title="Treemap",
+ components=[
+ vm.Graph(
+ figure=px.treemap(
+ gapminder.query("year == 2007"),
+ path=[px.Constant("world"), "continent", "country"],
+ values="pop",
+ color="lifeExp",
+ )
+ ),
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/examples/violin.py b/pages/examples/violin.py
new file mode 100644
index 0000000000000000000000000000000000000000..f3edcffe515de04cbc8da3cac5279016e27c5964
--- /dev/null
+++ b/pages/examples/violin.py
@@ -0,0 +1,15 @@
+import vizro.models as vm
+import vizro.plotly.express as px
+from vizro import Vizro
+
+tips = px.data.tips()
+
+page = vm.Page(
+ title="Violin",
+ components=[
+ vm.Graph(figure=px.violin(tips, y="total_bill", x="day", color="day", box=True)),
+ ],
+)
+
+dashboard = vm.Dashboard(pages=[page])
+Vizro().build(dashboard).run()
diff --git a/pages/flow.py b/pages/flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ceaed6e1c6bf77ae0d12a3313949c00a34526c1
--- /dev/null
+++ b/pages/flow.py
@@ -0,0 +1,47 @@
+"""Flow charts."""
+
+import vizro.models as vm
+from custom_charts import sankey
+
+from pages._pages_utils import PAGE_GRID, make_code_clipboard_from_py_file, sankey_data
+
+sankey = vm.Page(
+ title="Sankey",
+ path="flow/sankey",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a sankey chart?
+
+ A Sankey chart is a type of flow diagram that illustrates how resources or values move between different
+ stages or entities. The width of the arrows in the chart is proportional to the quantity of the flow,
+ making it easy to see where the largest movements occur.
+
+
+
+ #### When should I use it?
+
+ Use a Sankey chart when you want to visualize the flow of resources, energy, money, or other values from
+ one point to another. It is particularly useful for showing distributions and transfers within a system,
+ such as energy usage, cost breakdowns, or material flows.
+
+ Be mindful that Sankey charts can become cluttered if there are too many nodes or flows.
+ To maintain clarity, focus on highlighting the most significant flows and keep the chart as simple as
+ possible.
+ """
+ ),
+ vm.Graph(
+ figure=sankey(
+ sankey_data,
+ labels=["A1", "A2", "B1", "B2", "C1", "C2"],
+ source="Origin",
+ target="Destination",
+ value="Value",
+ ),
+ ),
+ make_code_clipboard_from_py_file("sankey.py"),
+ ],
+)
+
+pages = [sankey]
diff --git a/pages/magnitude.py b/pages/magnitude.py
new file mode 100644
index 0000000000000000000000000000000000000000..a720a189358db10eff4fc1af3f9563bc18f10aa6
--- /dev/null
+++ b/pages/magnitude.py
@@ -0,0 +1,193 @@
+"""Magnitude charts."""
+
+import vizro.models as vm
+import vizro.plotly.express as px
+
+from pages._pages_utils import PAGE_GRID, gapminder, iris, make_code_clipboard_from_py_file, tips
+
+bar = vm.Page(
+ title="Bar",
+ path="magnitude/bar",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a bar chart?
+
+ A bar chart displays bars with lengths proportional to the values they represent. One axis shows the
+ categories to compare, and the other provides a value scale starting from zero.
+
+
+
+ #### When should I use it?
+
+ Use a bar chart to help your audience compare sizes and identify patterns in categorical data, such as
+ **how many?** in each category. Arrange the bars in any order to fit the message you want to emphasize.
+ Ensure clear labeling, especially with many bars, and consider using a legend or abbreviations with fuller
+ descriptions below.
+ """
+ ),
+ vm.Graph(
+ figure=px.bar(
+ gapminder.query(
+ "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])"
+ ),
+ x="pop",
+ y="country",
+ orientation="h",
+ )
+ ),
+ make_code_clipboard_from_py_file("bar.py"),
+ ],
+)
+
+# Note: Code example for magnitude/column differs from time/column. The text description is the same.
+column = vm.Page(
+ id="magnitude-column",
+ path="magnitude/column",
+ title="Column",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a column chart?
+
+ A column chart is a type of bar chart where data is represented with vertical columns. Each
+ column's height corresponds to the value it represents, with the y-axis starting from zero.
+
+
+
+ #### When should I use it?
+
+ Use a column chart to compare sizes and identify patterns in categorical data, including time-based
+ data. Arrange columns to fit your message, and for time-based data, order them chronologically to
+ highlight trends. Ensure clear labeling, especially with many categories, and consider using a legend
+ or abbreviations with fuller descriptions below.
+ """
+ ),
+ vm.Graph(
+ figure=px.bar(
+ gapminder.query(
+ "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])"
+ ),
+ y="pop",
+ x="country",
+ )
+ ),
+ make_code_clipboard_from_py_file("magnitude_column.py"),
+ ],
+)
+
+paired_bar = vm.Page(
+ title="Paired bar",
+ path="magnitude/paired-bar",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a paired bar chart?
+
+ A paired bar chart, also known as a grouped bar chart, displays bars grouped together in pairs for each
+ category, with the lengths of the bars proportional to the values they represent. One axis shows the
+ categories to compare, while the other provides a value scale starting from zero.
+
+
+
+ #### When should I use it?
+
+ Use a paired bar chart to compare multiple sets of data within the same categories. This type of chart is
+ particularly useful for highlighting differences and similarities between groups, such as **how many?** in
+ each category across different groups. Arrange the paired bars clearly to fit the message you want to
+ emphasize. Ensure clear labeling, especially with many bars, and consider using a legend or abbreviations
+ with fuller descriptions below.
+ """
+ ),
+ vm.Graph(
+ figure=px.histogram(
+ tips,
+ y="day",
+ x="total_bill",
+ color="sex",
+ barmode="group",
+ orientation="h",
+ category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]},
+ ),
+ ),
+ make_code_clipboard_from_py_file("paired_bar.py"),
+ ],
+)
+
+paired_column = vm.Page(
+ title="Paired column",
+ path="magnitude/paired-column",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a paired column chart?
+
+ A paired column chart, also known as a grouped column chart, displays columns grouped together in pairs for
+ each category, with the heights of the columns proportional to the values they represent. One axis shows the
+ categories to compare, while the other provides a value scale starting from zero.
+
+
+
+ #### When should I use it?
+
+ Use a paired column chart to compare multiple sets of data within the same categories. This type of chart is
+ particularly useful for highlighting differences and similarities between groups, such as **how many?** in
+ each category across different groups. Arrange the paired columns clearly to fit the message you want to
+ emphasize. Ensure clear labeling, especially with many columns, and consider using a legend or abbreviations
+ with fuller descriptions below.
+ """
+ ),
+ vm.Graph(
+ figure=px.histogram(
+ tips,
+ x="day",
+ y="total_bill",
+ color="sex",
+ barmode="group",
+ category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]},
+ ),
+ ),
+ make_code_clipboard_from_py_file("paired_column.py"),
+ ],
+)
+
+parallel_coordinates = vm.Page(
+ path="magnitude/parallel-coordinates ",
+ title="Parallel coordinates",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a parallel coordinates chart?
+
+ A parallel coordinates chart is a type of data visualization used to plot multivariate numerical data.
+ Each axis represents a different variable, and lines connecting the axes indicate the values of
+ individual data points across these variables.
+
+
+
+ #### When should I use it?
+
+ Use a parallel coordinates chart to explore relationships and patterns in high-dimensional data.
+ This chart is particularly useful for comparing multiple variables simultaneously and identifying
+ correlations or clusters within the data. Ensure clear labeling of each axis and consider using color
+ coding to distinguish between different data points or groups.
+ """
+ ),
+ vm.Graph(
+ figure=px.parallel_coordinates(
+ iris, color="species_id", dimensions=["sepal_width", "sepal_length", "petal_width", "petal_length"]
+ )
+ ),
+ make_code_clipboard_from_py_file("parallel_coordinates.py"),
+ ],
+)
+
+pages = [bar, column, paired_bar, paired_column, parallel_coordinates]
diff --git a/pages/part_to_whole.py b/pages/part_to_whole.py
new file mode 100644
index 0000000000000000000000000000000000000000..19541b8eebc04efe5e425d67e19e2a1af7f266f7
--- /dev/null
+++ b/pages/part_to_whole.py
@@ -0,0 +1,207 @@
+"""Part-to-whole charts."""
+
+import vizro.models as vm
+import vizro.plotly.express as px
+
+from pages._pages_utils import PAGE_GRID, funnel_data, gapminder, make_code_clipboard_from_py_file, tips
+
+pie = vm.Page(
+ title="Pie",
+ path="part-to-whole/pie",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a pie chart?
+
+ A pie chart is a circular chart divided into segments to show proportions and percentages between
+ categories. The circle represents the whole.
+
+
+
+ #### When should I use it?
+
+ Use the pie chart when you need to show your audience a quick view of how data is distributed
+ proportionately, with percentages highlighted. The different values you present must add up to a total and
+ equal 100%.
+
+ The downsides are that pie charts tend to occupy more space than other charts, they don't
+ work well with more than a few values because labeling small segments is challenging, and it can be
+ difficult to accurately compare the sizes of the segments.
+ """
+ ),
+ vm.Graph(
+ figure=px.pie(
+ tips,
+ values="tip",
+ names="day",
+ )
+ ),
+ make_code_clipboard_from_py_file("pie.py"),
+ ],
+)
+
+donut = vm.Page(
+ title="Donut",
+ path="part-to-whole/donut",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a donut chart?
+
+ A donut chart looks like a pie chart, but has a blank space in the center which may contain additional
+ information.
+
+
+
+ #### When should I use it?
+
+ A donut chart can be used in place of a pie chart, particularly when you are short of space or have extra
+ information you would like to share about the data. It may also be more effective if you wish your audience
+ to focus on the length of the arcs of the sections instead of the proportions of the segment sizes.
+ """
+ ),
+ vm.Graph(
+ figure=px.pie(
+ tips,
+ values="tip",
+ names="day",
+ hole=0.4,
+ )
+ ),
+ make_code_clipboard_from_py_file("donut.py"),
+ ],
+)
+
+treemap = vm.Page(
+ title="Treemap",
+ path="part-to-whole/treemap",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a treemap?
+
+ A treemap shows hierarchical data arranged as a set of nested rectangles: rectangles are sized
+ proportionately to the quantity they represent, combined together to form larger parent category
+ rectangles.
+
+
+
+ #### When should I use it?
+
+ It's helpful to use a treemap when you wish to display hierarchical part-to-whole relationships. You can
+ compare groups and single elements nested within them. Consider using them instead of Pie charts when
+ you have a higher number of categories. Treemaps are very compact and allow audiences to get a quick
+ overview of the data.
+ """
+ ),
+ vm.Graph(
+ figure=px.treemap(
+ gapminder.query("year == 2007"),
+ path=[px.Constant("world"), "continent", "country"],
+ values="pop",
+ color="lifeExp",
+ )
+ ),
+ make_code_clipboard_from_py_file("treemap.py"),
+ ],
+)
+
+stacked_bar = vm.Page(
+ title="Stacked bar",
+ path="part-to-whole/stacked-bar",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a stacked bar chart?
+
+ A stacked bar chart displays bars divided into segments, with each segment's length proportional to the
+ value it represents. One axis shows the categories being compared, while the other provides a value scale
+ starting from zero. The segments within each bar are stacked on top of each other, allowing for a cumulative
+ comparison.
+
+
+
+ #### When should I use it?
+
+ Use a stacked bar chart to help your audience compare the total sizes of categories as well as the
+ individual components within those categories. This chart type is ideal for visualizing part-to-whole
+ relationships and identifying patterns within categorical data. Ensure clear labeling for each segment,
+ especially when there are many categories, and consider using a legend or abbreviations with fuller
+ descriptions below.
+ """
+ ),
+ vm.Graph(figure=px.histogram(tips, y="sex", x="total_bill", color="day", orientation="h")),
+ make_code_clipboard_from_py_file("stacked_bar.py"),
+ ],
+)
+
+stacked_column = vm.Page(
+ title="Stacked column",
+ path="part-to-whole/stacked-column",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a stacked column chart?
+
+ A stacked column chart displays columns divided into segments, with each segment's height proportional to
+ the value it represents. One axis shows the categories being compared, while the other provides a value
+ scale starting from zero. The segments within each column are stacked on top of each other, allowing for a
+ cumulative comparison.
+
+
+
+ #### When should I use it?
+
+ Use a stacked column chart to help your audience compare the total sizes of categories as well as the
+ individual components within those categories. This chart type is ideal for visualizing part-to-whole
+ relationships and identifying patterns within categorical data. Ensure clear labeling for each segment,
+ especially when there are many categories, and consider using a legend or abbreviations with fuller
+ descriptions below.
+ """
+ ),
+ vm.Graph(figure=px.histogram(tips, x="sex", y="total_bill", color="day")),
+ make_code_clipboard_from_py_file("stacked_bar.py"),
+ ],
+)
+
+funnel = vm.Page(
+ title="Funnel",
+ path="part-to-whole/funnel",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a funnel chart?
+
+ A funnel area chart is a type of data visualization that represents stages in a process, with the size of
+ each area proportional to its value. The chart typically narrows as it progresses, visually depicting the
+ reduction in numbers through each stage. One axis represents the stages of the process, while the other axis
+ indicates the values or quantities at each stage.
+
+
+
+ #### When should I use it?
+
+ Use a funnel area chart to help your audience understand and compare the progression of data through
+ different stages of a process. This chart type is particularly effective for visualizing conversion rates,
+ sales processes, or any sequential data where you want to highlight drop-offs or reductions between stages.
+ """
+ ),
+ vm.Graph(figure=px.funnel_area(funnel_data, names="Stage", values="Value")),
+ make_code_clipboard_from_py_file("funnel.py"),
+ ],
+)
+
+
+pages = [donut, pie, treemap, stacked_bar, stacked_column, funnel]
diff --git a/pages/ranking.py b/pages/ranking.py
new file mode 100644
index 0000000000000000000000000000000000000000..745a5d01f8f2bfab29e268b237da33029fa54d54
--- /dev/null
+++ b/pages/ranking.py
@@ -0,0 +1,83 @@
+"""Ranking charts."""
+
+import vizro.models as vm
+import vizro.plotly.express as px
+
+from pages._pages_utils import PAGE_GRID, gapminder, make_code_clipboard_from_py_file
+
+ordered_bar = vm.Page(
+ title="Ordered bar",
+ path="ranking/ordered-bar",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is an ordered bar chart?
+
+ An ordered bar chart displays bars with lengths proportional to their values, arranged in descending or
+ ascending order. One axis shows the categories, and the other provides a value scale starting from zero.
+
+
+
+ #### When should I use it?
+
+ Use an ordered bar chart to help your audience compare sizes and identify patterns in categorical data,
+ emphasizing the order of categories. This is ideal for showing rankings or priorities.
+ Ensure clear labeling, especially with many bars, and consider using a legend or abbreviations with fuller
+ descriptions below.
+ """
+ ),
+ vm.Graph(
+ figure=px.bar(
+ gapminder.query(
+ "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])"
+ ).sort_values("pop"),
+ x="pop",
+ y="country",
+ orientation="h",
+ )
+ ),
+ make_code_clipboard_from_py_file("ordered_bar.py"),
+ ],
+)
+
+ordered_column = vm.Page(
+ title="Ordered column",
+ path="ranking/ordered-column",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is an ordered column chart?
+
+ An ordered column chart is a vertical bar chart where columns are arranged in descending or ascending order
+ based on their values. The column lengths vary according to the categorical value they represent, with the
+ scale on the y-axis starting from zero.
+
+
+
+ #### When should I use it?
+
+ Use an ordered column chart to help your audience compare sizes and identify patterns in categorical data,
+ emphasizing the order of categories. This is ideal for showing rankings or progressions. Ensure clear
+ labeling, especially with many columns, and consider using a legend or abbreviations with fuller
+ descriptions below.
+ """
+ ),
+ vm.Graph(
+ figure=px.bar(
+ gapminder.query(
+ "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])"
+ ).sort_values("pop"),
+ y="pop",
+ x="country",
+ )
+ ),
+ make_code_clipboard_from_py_file("ordered_column.py"),
+ ],
+)
+
+
+pages = [ordered_bar, ordered_column]
diff --git a/pages/spatial.py b/pages/spatial.py
new file mode 100644
index 0000000000000000000000000000000000000000..880150c1193c18a457cdcadf238704e77e555bc1
--- /dev/null
+++ b/pages/spatial.py
@@ -0,0 +1,45 @@
+"""Spatial charts."""
+
+import vizro.models as vm
+import vizro.plotly.express as px
+
+from pages._pages_utils import PAGE_GRID, gapminder, make_code_clipboard_from_py_file
+
+choropleth = vm.Page(
+ title="Choropleth",
+ path="spatial/choropleth",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a choropleth map?
+
+ A choropleth map is a map in which geographical areas are colored, shaded or patterned in relation to a
+ specific data variable.
+
+
+
+ #### When should I use it?
+
+ Use a chloropleth map when you wish to show how a measurement varies across a geographic area, or to show
+ variability or patterns within a region. Typically, you will blend one color into another, take a color
+ shade from light to dark, or introduce patterns to depict the variation in the data.
+
+ Be aware that it may be difficult for your audience to accurately read or compare values on the map
+ depicted by color.
+
+ """
+ ),
+ vm.Graph(
+ figure=px.choropleth(
+ gapminder.query("year == 2007"),
+ locations="iso_alpha",
+ color="lifeExp",
+ hover_name="country",
+ )
+ ),
+ make_code_clipboard_from_py_file("choropleth.py"),
+ ],
+)
+
+pages = [choropleth]
diff --git a/pages/time.py b/pages/time.py
new file mode 100644
index 0000000000000000000000000000000000000000..2d43bfd6ce1a265d3be31af202a2bf39368e101a
--- /dev/null
+++ b/pages/time.py
@@ -0,0 +1,163 @@
+"""Time charts."""
+
+import vizro.models as vm
+import vizro.plotly.express as px
+from custom_charts import categorical_column
+
+from pages._factories import column_and_line_factory, connected_scatter_factory
+from pages._pages_utils import PAGE_GRID, gapminder, make_code_clipboard_from_py_file, stepped_line_data, stocks, tips
+
+line = vm.Page(
+ title="Line",
+ path="time/line",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a line chart?
+
+ A line chart presents a series of data points over a continuous interval or time period, joined together
+ with straight lines.
+
+
+
+ #### When should I use it?
+
+ You should select a line chart when you want to show trends over time. Usually, your y-axis will show a
+ quantitative value and your x-axis will be marked as a timescale or a sequence of intervals. You can also
+ display negative values below the x-axis. If you wish to group several lines (different data series) in the
+ same chart, try to limit yourself to 3-4 to avoid cluttering up your chart.
+ """
+ ),
+ vm.Graph(figure=px.line(stocks, x="date", y="GOOG")),
+ make_code_clipboard_from_py_file("line.py"),
+ ],
+)
+
+# Note: Code example for magnitude/column differs from time/column. The text description is the same.
+column = vm.Page(
+ id="time-column",
+ path="time/column",
+ title="Column",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a column chart?
+
+ A column chart is a type of bar chart where data is represented with vertical columns. Each
+ column's height corresponds to the value it represents, with the y-axis starting from zero.
+
+
+
+ #### When should I use it?
+
+ Use a column chart to compare sizes and identify patterns in categorical data, including time-based
+ data. Arrange columns to fit your message, and for time-based data, order them chronologically to
+ highlight trends. Ensure clear labeling, especially with many categories, and consider using a legend
+ or abbreviations with fuller descriptions below.
+ """
+ ),
+ vm.Graph(
+ figure=categorical_column(
+ gapminder.query("country == 'Nigeria' and year > 1970"),
+ y="gdpPercap",
+ x="year",
+ )
+ ),
+ make_code_clipboard_from_py_file("time_column.py"),
+ ],
+)
+
+area = vm.Page(
+ title="Area",
+ path="time/area",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is an area chart?
+
+ An area chart displays data points over a continuous interval or time period, with the area between the line
+ and the axis filled in to emphasize the magnitude of the values.
+
+
+
+ #### When should I use it?
+
+ An area chart is ideal for showing trends over time and emphasizing the volume of data. Typically,
+ the y-axis represents a quantitative value, while the x-axis is marked with a timescale or sequence of
+ intervals. Area charts can also display negative values below the x-axis. If you need to compare multiple
+ data series in the same chart, try to limit yourself to 3-4 to maintain clarity and avoid clutter.
+ """
+ ),
+ vm.Graph(figure=px.area(stocks, x="date", y="GOOG")),
+ make_code_clipboard_from_py_file("area.py"),
+ ],
+)
+
+connected_scatter = connected_scatter_factory("time")
+column_and_line = column_and_line_factory("time")
+
+stepped_line = vm.Page(
+ title="Stepped line",
+ path="time/stepped-line",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+
+ #### What is a stepped line chart?
+
+ A stepped line chart is much like a standard line chart but, instead of connecting two points with the
+ shortest line, the line forms a series of steps between data points.
+
+
+
+ #### When should I use it?
+
+ You should use a stepped line chart when you wish to draw attention to changes occurring at specific points.
+ By contrast, a line chart would suggest that changes occur gradually.
+ """
+ ),
+ vm.Graph(
+ figure=px.line(
+ data_frame=stepped_line_data,
+ x="year",
+ y="rate",
+ line_shape="vh",
+ ),
+ ),
+ make_code_clipboard_from_py_file("stepped_line.py"),
+ ],
+)
+
+heatmap = vm.Page(
+ title="Heatmap",
+ path="time/heatmap",
+ layout=vm.Layout(grid=PAGE_GRID),
+ components=[
+ vm.Card(
+ text="""
+ #### What is a heatmap?
+ A heatmap chart depicts values for a main variable of interest across two axis variables as a grid of
+ colored squares. The color intensity of each cell represents the value of the main variable within a
+ specific range.
+
+
+
+ #### When should I use it?
+
+ Use a heatmap chart to visualize time patterns and identify trends between two variables.
+ Typically, the x-axis represents time intervals (e.g., hours, days, months), while the y-axis represents
+ categories or different variables. By observing color changes across the grid, you can easily spot
+ patterns and correlations.
+ """
+ ),
+ vm.Graph(figure=px.density_heatmap(tips, x="day", y="size", z="tip", histfunc="avg", text_auto="$.2f")),
+ make_code_clipboard_from_py_file("heatmap.py"),
+ ],
+)
+pages = [line, column, area, connected_scatter, column_and_line, stepped_line, heatmap]
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..736f3e23a7ed18ec2c83234d644c23075d2e94b6
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+black==24.4.2
+vizro
+gunicorn