diff --git a/doc/_static/altair-gallery.css b/doc/_static/altair-gallery.css
index 2dfe9e8a4..d51fb7886 100644
--- a/doc/_static/altair-gallery.css
+++ b/doc/_static/altair-gallery.css
@@ -122,6 +122,69 @@ div.bottomnav {
min-width: 140px;
}
+.gallery .image {
+ box-shadow:
+ 0 0 10px rgba(110, 116, 124, 0.35),
+ 0 0 20px rgba(110, 116, 124, 0.2);
+ transition: background-position 2s, box-shadow 200ms ease;
+}
+
+.gallery .image:hover {
+ box-shadow:
+ 0 0 14px rgba(110, 116, 124, 0.5),
+ 0 0 28px rgba(110, 116, 124, 0.3);
+}
+
+.gallery .imagegroup-new .image {
+ box-shadow:
+ 0 0 14px rgba(255, 143, 63, 0.56),
+ 0 0 28px rgba(255, 143, 63, 0.38);
+}
+
+.gallery .imagegroup-new .image:hover {
+ box-shadow:
+ 0 0 17px rgba(255, 143, 63, 0.7),
+ 0 0 34px rgba(255, 143, 63, 0.45);
+}
+
+.gallery .imagegroup-recent .image:hover {
+ box-shadow:
+ 0 0 17px rgba(255, 143, 63, 0.7),
+ 0 0 34px rgba(255, 143, 63, 0.45);
+}
+
+.gallery .image-tag {
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ z-index: 2;
+ padding: 2px 8px;
+ border-radius: 999px;
+ font-size: 0.72rem;
+ font-weight: 700;
+ line-height: 1.3;
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+ color: #ffffff;
+ background: linear-gradient(135deg, #f46f2b 0%, #ff8f3f 100%);
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.22);
+}
+
+.gallery-inline-tag {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 999px;
+ font-size: 0.72rem;
+ font-weight: 700;
+ line-height: 1.3;
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+ color: #ffffff;
+ background: linear-gradient(135deg, #f46f2b 0%, #ff8f3f 100%);
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.22);
+ vertical-align: center;
+}
+
.gallery .imagegroup:hover {
text-decoration: none;
}
@@ -134,7 +197,6 @@ div.bottomnav {
background-repeat: no-repeat;
margin-bottom: 5px;
overflow: hidden;
- transition: background-position 2s;
}
.gallery .image:hover {
background-position: right bottom;
diff --git a/sphinxext/altairgallery.py b/sphinxext/altairgallery.py
index 1ca43ba13..4112f26e6 100644
--- a/sphinxext/altairgallery.py
+++ b/sphinxext/altairgallery.py
@@ -59,6 +59,40 @@
This change also introduced updated column names in some datasets (e.g., spaces
instead of underscores).
+{% if recent_examples %}
+
+.. _gallery-category-recently-added:
+
+.. |gallery-new-pill| raw:: html
+
+ new
+
+Recently Added |gallery-new-pill|
+~~~~~~~~~~~~~~
+
+.. raw:: html
+
+
+ {% for example in recent_examples %}
+
+
+
+ {{ example.title }}
+
+ {% endfor %}
+
+
+
+
+{% endif %}
+
{% for grouper, group in examples %}
.. _gallery-category-{{ grouper }}:
@@ -70,9 +104,12 @@
{% for example in group %}
-
+
+ {% if example['is_new'] %}
+ new
+ {% endif %}
list[dict[str, Any]]:
method_examples = {x["name"]: x for x in iter_examples_methods_syntax()}
for example in examples:
- docstring, category, code, lineno = get_docstring_and_rest(example["filename"])
+ docstring, category, code, lineno, is_new = get_docstring_and_rest(
+ example["filename"]
+ )
if example["name"] in method_examples:
- _, _, method_code, _ = get_docstring_and_rest(
+ _, _, method_code, _, _ = get_docstring_and_rest(
method_examples[example["name"]]["filename"]
)
else:
@@ -250,6 +289,7 @@ def populate_examples(**kwds: Any) -> list[dict[str, Any]]:
"method_code": method_code,
"category": category.title(),
"lineno": lineno,
+ "is_new": is_new,
}
)
@@ -369,6 +409,8 @@ def main(app) -> None:
for d in examples:
examples_toc[d["category"]].append(d)
+ recent_examples = [example for example in examples if example["is_new"]]
+
encoding = "utf-8"
# Write the gallery index file
@@ -376,6 +418,7 @@ def main(app) -> None:
index_text = GALLERY_TEMPLATE.render(
title=gallery_title,
examples=examples_toc.items(),
+ recent_examples=recent_examples,
image_dir="/_static",
gallery_ref=gallery_ref,
)
diff --git a/sphinxext/utils.py b/sphinxext/utils.py
index d89d40866..2b27f2fa7 100644
--- a/sphinxext/utils.py
+++ b/sphinxext/utils.py
@@ -88,7 +88,9 @@ def _parse_source_file(filename: str | Path) -> tuple[ast.Module | None, str]:
return node, content
-def get_docstring_and_rest(filename: str | Path) -> tuple[str, str | None, str, int]:
+def get_docstring_and_rest( # noqa: C901
+ filename: str | Path,
+) -> tuple[str, str | None, str, int, bool]:
"""
Separate ``filename`` content between docstring and the rest.
@@ -109,6 +111,8 @@ def get_docstring_and_rest(filename: str | Path) -> tuple[str, str | None, str,
``filename`` content without the docstring
lineno: int
the line number on which the code starts
+ is_new: bool
+ whether the file includes a ``# :new:`` marker before ``# category:``
Notes
-----
@@ -120,6 +124,15 @@ def get_docstring_and_rest(filename: str | Path) -> tuple[str, str | None, str,
# Find the category comment
find_category = re.compile(r"^#\s*category:\s*(.*)$", re.MULTILINE)
match = find_category.search(content)
+ find_new = re.compile(r"^#\s*:new:\s*$", re.MULTILINE)
+ match_new = find_new.search(content)
+
+ is_new = bool(
+ match is not None
+ and match_new is not None
+ and match_new.start() < match.start()
+ )
+
if match is not None:
category = match.groups()[0]
# remove this comment from the content
@@ -127,10 +140,13 @@ def get_docstring_and_rest(filename: str | Path) -> tuple[str, str | None, str,
else:
category = None
+ if is_new and match_new is not None:
+ content = content[: match_new.start()] + content[match_new.end() :]
+
lineno = 1
if node is None:
- return SYNTAX_ERROR_DOCSTRING, category, content, lineno
+ return SYNTAX_ERROR_DOCSTRING, category, content, lineno, is_new
if not isinstance(node, ast.Module):
msg = f"This function only supports modules. You provided {node.__class__.__name__}"
@@ -186,7 +202,7 @@ def get_docstring_and_rest(filename: str | Path) -> tuple[str, str | None, str,
"A docstring is required for the example gallery."
)
raise ValueError(msg)
- return docstring, category, rest, lineno
+ return docstring, category, rest, lineno, is_new
def prev_this_next(
diff --git a/tests/examples_arguments_syntax/bar_chart_with_labels_measured_luminance.py b/tests/examples_arguments_syntax/bar_chart_with_labels_measured_luminance.py
index 01664a3db..ef64b63a4 100644
--- a/tests/examples_arguments_syntax/bar_chart_with_labels_measured_luminance.py
+++ b/tests/examples_arguments_syntax/bar_chart_with_labels_measured_luminance.py
@@ -3,6 +3,7 @@
=================================================
This example shows a basic horizontal bar chart with labels where the measured luminance to decides if the text overlay is be colored ``black`` or ``white``.
"""
+# :new:
# category: bar charts
import altair as alt
from altair.datasets import data
diff --git a/tests/examples_arguments_syntax/calculate_residuals.py b/tests/examples_arguments_syntax/calculate_residuals.py
index 7714e211f..f548f33c3 100644
--- a/tests/examples_arguments_syntax/calculate_residuals.py
+++ b/tests/examples_arguments_syntax/calculate_residuals.py
@@ -4,7 +4,6 @@
A dot plot showing each movie in the database, and the difference from the average movie rating.
The display is sorted by year to visualize everything in sequential order.
The graph is for all Movies before 2019.
-
Adapted from `Calculate Residuals `_.
"""
# category: advanced calculations
@@ -33,4 +32,4 @@
),
)
)
-chart
\ No newline at end of file
+chart
diff --git a/tests/examples_arguments_syntax/deviation_ellipses.py b/tests/examples_arguments_syntax/deviation_ellipses.py
index 83d60bbff..a402612f1 100644
--- a/tests/examples_arguments_syntax/deviation_ellipses.py
+++ b/tests/examples_arguments_syntax/deviation_ellipses.py
@@ -12,7 +12,7 @@
.. _@essicolo:
https://github.com/essicolo
"""
-
+# :new:
# category: case studies
import numpy as np
import pandas as pd
diff --git a/tests/examples_arguments_syntax/grouped_bar_chart_overlapping_bars.py b/tests/examples_arguments_syntax/grouped_bar_chart_overlapping_bars.py
index 06e4e3d65..99bf6aa16 100644
--- a/tests/examples_arguments_syntax/grouped_bar_chart_overlapping_bars.py
+++ b/tests/examples_arguments_syntax/grouped_bar_chart_overlapping_bars.py
@@ -3,6 +3,7 @@
---------------------------------------------------
Like :ref:`gallery_grouped_bar_chart2`, this example shows a grouped bar chart using the ``xOffset`` encoding channel, but in this example the bars are partly overlapping within each group.
"""
+# :new:
# category: bar charts
import altair as alt
import pandas as pd
@@ -24,4 +25,4 @@
alt.layer(
base.mark_bar(size=20, stroke="white", fillOpacity=0.9).encode(fill="group:N"),
base.mark_text(dy=-5).encode(text="value:Q"),
-)
\ No newline at end of file
+)
diff --git a/tests/examples_arguments_syntax/interactive_aggregation.py b/tests/examples_arguments_syntax/interactive_aggregation.py
index aa024c688..bab2af00c 100644
--- a/tests/examples_arguments_syntax/interactive_aggregation.py
+++ b/tests/examples_arguments_syntax/interactive_aggregation.py
@@ -7,6 +7,7 @@
The ability to slide back and fourth may help you understand how the visualization
represents the aggregation. Adapted from an example by @dwootton.
"""
+# :new:
# category: interactive charts
import altair as alt
from altair.datasets import data
@@ -36,4 +37,4 @@
strokeWidth=alt.StrokeWidth(value=6),
x=alt.X(datum=alt.expr(threshold.name), type="quantitative")
)
-).add_params(threshold)
\ No newline at end of file
+).add_params(threshold)
diff --git a/tests/examples_arguments_syntax/interactive_bar_select_highlight.py b/tests/examples_arguments_syntax/interactive_bar_select_highlight.py
index 953d36857..412522a86 100644
--- a/tests/examples_arguments_syntax/interactive_bar_select_highlight.py
+++ b/tests/examples_arguments_syntax/interactive_bar_select_highlight.py
@@ -5,7 +5,7 @@
Based on https://vega.github.io/vega-lite/examples/interactive_bar_select_highlight.html
"""
-
+# :new:
# category: interactive charts
import altair as alt
diff --git a/tests/examples_arguments_syntax/interactive_column_selection.py b/tests/examples_arguments_syntax/interactive_column_selection.py
index dc3c0331f..c7d026b2d 100644
--- a/tests/examples_arguments_syntax/interactive_column_selection.py
+++ b/tests/examples_arguments_syntax/interactive_column_selection.py
@@ -7,6 +7,7 @@
It also illustrates how to use `indexof` to filter
columns based on active selection values.
"""
+# :new:
# category: interactive charts
import pandas as pd
diff --git a/tests/examples_arguments_syntax/interactive_reorder_stacked_bars.py b/tests/examples_arguments_syntax/interactive_reorder_stacked_bars.py
index 5e4744a2e..e69b68bfc 100644
--- a/tests/examples_arguments_syntax/interactive_reorder_stacked_bars.py
+++ b/tests/examples_arguments_syntax/interactive_reorder_stacked_bars.py
@@ -10,6 +10,7 @@
which here allows for multiple segments to be reordered
by holding down the shift key while clicking the legend.
"""
+# :new:
# category: interactive charts
import altair as alt
from altair.datasets import data
diff --git a/tests/examples_arguments_syntax/interval_selection_map_quakes.py b/tests/examples_arguments_syntax/interval_selection_map_quakes.py
index 6ac0bff54..0184bb786 100644
--- a/tests/examples_arguments_syntax/interval_selection_map_quakes.py
+++ b/tests/examples_arguments_syntax/interval_selection_map_quakes.py
@@ -5,6 +5,7 @@
This is an example of a binned bar chart on the right where the filtered overlay
is adjusted by interacting with the map on the left.
"""
+# :new:
# category: interactive charts
import altair as alt
from altair.datasets import data
@@ -58,4 +59,4 @@
right_bars = alt.layer(bars, bars_overlay)
# vertical concatenate map and bars
-left_map | right_bars
\ No newline at end of file
+left_map | right_bars
diff --git a/tests/examples_arguments_syntax/maps_faceted_species.py b/tests/examples_arguments_syntax/maps_faceted_species.py
index 5679500a5..f598092f3 100644
--- a/tests/examples_arguments_syntax/maps_faceted_species.py
+++ b/tests/examples_arguments_syntax/maps_faceted_species.py
@@ -1,10 +1,9 @@
"""
Faceted County-Level Choropleth Maps
------------------------------------
-A set of maps arranged in a grid, each showing the distribution of a species' projected habitat across US counties.
-
-Each choropleth map uses color intensity to represent the percentage values within county boundaries.
+A set of maps arranged in a grid, each showing the distribution of a species' projected habitat across US counties. Each choropleth map uses color intensity to represent the percentage values within county boundaries.
"""
+# :new:
# category: maps
import altair as alt
diff --git a/tests/examples_arguments_syntax/mosaic_with_labels.py b/tests/examples_arguments_syntax/mosaic_with_labels.py
index 11a2dc871..191fad794 100644
--- a/tests/examples_arguments_syntax/mosaic_with_labels.py
+++ b/tests/examples_arguments_syntax/mosaic_with_labels.py
@@ -2,6 +2,7 @@
Mosaic Chart with Labels
------------------------
"""
+# :new:
# category: tables
import altair as alt
diff --git a/tests/examples_arguments_syntax/polar_bar_chart.py b/tests/examples_arguments_syntax/polar_bar_chart.py
index a32738678..54da44b5e 100644
--- a/tests/examples_arguments_syntax/polar_bar_chart.py
+++ b/tests/examples_arguments_syntax/polar_bar_chart.py
@@ -6,6 +6,7 @@
but is more commonly referred to as a polar bar chart.
The axis lines are created using pie charts with only the stroke visible.
"""
+# :new:
# category: circular plots
import math
diff --git a/tests/examples_arguments_syntax/scatter_point_paths_hover.py b/tests/examples_arguments_syntax/scatter_point_paths_hover.py
index 98a72e919..f27094ceb 100644
--- a/tests/examples_arguments_syntax/scatter_point_paths_hover.py
+++ b/tests/examples_arguments_syntax/scatter_point_paths_hover.py
@@ -1,6 +1,6 @@
"""
-Scatter plot with point paths on hover with search box
-======================================================
+Scatter plot with point paths on hover and search box
+=====================================================
This example combines cross-sectional analysis (comparing countries at a single point in time)
with longitudinal analysis (tracking changes in individual countries over time), using
an interactive visualization technique inspired by [this Vega example](https://vega.github.io/vega/examples/global-development/).
@@ -11,6 +11,7 @@
2. Search Box. Implements a case-insensitive regex filter for country names,
enabling dynamic, flexible data point selection to enhance exploratory analysis.
"""
+# :new:
# category: interactive charts
import altair as alt
from altair.datasets import data
@@ -147,4 +148,4 @@
titleFontSize=12
).add_params(search_box)
-chart
\ No newline at end of file
+chart
diff --git a/tests/examples_arguments_syntax/selection_zorder.py b/tests/examples_arguments_syntax/selection_zorder.py
index 7d2769327..a6005ad71 100644
--- a/tests/examples_arguments_syntax/selection_zorder.py
+++ b/tests/examples_arguments_syntax/selection_zorder.py
@@ -7,6 +7,7 @@
This prevents that the selected points are obscured
by those that are not selected.
"""
+# :new:
# category: interactive charts
import altair as alt
diff --git a/tests/examples_methods_syntax/maps_faceted_species.py b/tests/examples_methods_syntax/maps_faceted_species.py
index 091bc9c78..e50478d66 100644
--- a/tests/examples_methods_syntax/maps_faceted_species.py
+++ b/tests/examples_methods_syntax/maps_faceted_species.py
@@ -2,7 +2,6 @@
Faceted County-Level Choropleth Maps
------------------------------------
A set of maps arranged in a grid, each showing the distribution of a species' projected habitat across US counties.
-
Each choropleth map uses color intensity to represent the percentage values within county boundaries.
"""
# category: maps
@@ -34,4 +33,4 @@
).project(type='albers').properties(width=300, height=200)
# Display the chart
-chart
\ No newline at end of file
+chart
diff --git a/tests/examples_methods_syntax/scatter_point_paths_hover.py b/tests/examples_methods_syntax/scatter_point_paths_hover.py
index a1829afdd..51bac03a0 100644
--- a/tests/examples_methods_syntax/scatter_point_paths_hover.py
+++ b/tests/examples_methods_syntax/scatter_point_paths_hover.py
@@ -1,6 +1,6 @@
"""
-Scatter plot with point paths on hover with search box
-======================================================
+Scatter plot with point paths on hover and search box
+=====================================================
This example combines cross-sectional analysis (comparing countries at a single point in time)
with longitudinal analysis (tracking changes in individual countries over time), using
an interactive visualization technique inspired by [this Vega example](https://vega.github.io/vega/examples/global-development/)
@@ -140,4 +140,4 @@
titleFontSize=12
).add_params(search_box)
-chart
\ No newline at end of file
+chart