diff --git a/bedrock/mozorg/blocks/common.py b/bedrock/mozorg/blocks/common.py
index a8a2ae78023..7f7a67f7dbb 100644
--- a/bedrock/mozorg/blocks/common.py
+++ b/bedrock/mozorg/blocks/common.py
@@ -340,13 +340,23 @@ class ShowcaseBlock(blocks.StructBlock):
)
sub_heading = blocks.CharBlock(
+ required=False,
max_length=255,
help_text="Section sub heading heading. Use sentence case.",
)
+ two_column_layout = blocks.BooleanBlock(
+ required=False,
+ default=False,
+ label="Make it two column layout",
+ inline_form=True,
+ help_text="Make the title and body content into a two-column layout.",
+ )
+
cta_divider = DividerBlock(label="Call-to-action")
cta_text = blocks.CharBlock(
+ required=False,
max_length=50,
label="Link text",
help_text="Use sentence case (e.g., 'Read the report', 'Read more').",
@@ -486,6 +496,26 @@ class Meta:
label_format = "{heading} ({width})"
+class ShowcaseGalleryImageBlock(blocks.StructBlock):
+ """A single image with alt text for the gallery."""
+
+ image = ImageChooserBlock()
+
+ image_alt = blocks.CharBlock(
+ max_length=255,
+ required=False,
+ help_text=(
+ "A concise description of the image for someone who can't see it. "
+ "See alt text guidelines for tips."
+ ),
+ )
+
+ class Meta:
+ icon = "image"
+ label = "Showcase Gallery Image"
+ label_format = "{image}"
+
+
class GalleryBlock(blocks.StructBlock):
"""Block for a gallery section with multiple tiles."""
@@ -512,3 +542,78 @@ class Meta:
icon = "grip"
label = "Gallery Section"
label_format = "{heading}"
+
+
+class ShowcaseGalleryBlockSettings(blocks.StructBlock):
+ """Settings for the showcase gallery block."""
+
+ anchor_id = blocks.CharBlock(
+ required=False,
+ max_length=100,
+ help_text="Optional: Add an ID to make this section linkable (e.g., 'news', 'gallery').",
+ )
+
+ background_color = blocks.ChoiceBlock(
+ choices=[
+ ("", "White"),
+ ("m24-t-dark", "Dark"),
+ ("m24-t-green", "Green"),
+ ("m24-t-orange", "Orange"),
+ ("m24-t-pink", "Pink"),
+ ("m24-t-gray", "Gray"),
+ ],
+ required=False,
+ help_text="What color should the background be?",
+ )
+
+ class Meta:
+ icon = "cog"
+ collapsed = True
+ label = "Settings"
+ label_format = "ID: {anchor_id} - Background: {background_color}"
+ form_classname = "compact-form struct-block"
+
+
+class ShowcaseGalleryBlock(blocks.StructBlock):
+ """A showcase block with a gallery as media."""
+
+ settings = ShowcaseGalleryBlockSettings()
+
+ text_divider = DividerBlock(label="Text")
+
+ heading = blocks.CharBlock(
+ required=False,
+ max_length=255,
+ help_text="Use sentence case.",
+ )
+
+ image_divider = DividerBlock(label="Image")
+
+ tiles = blocks.ListBlock(
+ ShowcaseGalleryImageBlock(),
+ min_num=1,
+ help_text="Add gallery tiles. For best results, ensure tile widths add up to 100% per row.",
+ )
+
+ body = blocks.CharBlock(
+ max_length=1000,
+ label="Content for section body",
+ help_text="Use sentence case.",
+ )
+
+ cta_text = blocks.CharBlock(
+ required=False,
+ max_length=50,
+ label="Link text",
+ help_text="Use sentence case (e.g., 'Read the report', 'Read more').",
+ )
+
+ cta_link = LinkBlock(
+ label="Link destination",
+ )
+
+ class Meta:
+ template = "mozorg/cms/blocks/showcase_gallery_block.html"
+ icon = "grip"
+ label = "Showcase Gallery section"
+ label_format = "{heading}"
diff --git a/bedrock/mozorg/fixtures/showcase_gallery_fixtures.py b/bedrock/mozorg/fixtures/showcase_gallery_fixtures.py
new file mode 100644
index 00000000000..dc8388f01e7
--- /dev/null
+++ b/bedrock/mozorg/fixtures/showcase_gallery_fixtures.py
@@ -0,0 +1,140 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+
+from bedrock.mozorg.fixtures.base_fixtures import get_placeholder_image, get_test_index_page
+from bedrock.mozorg.models import AboutUsPage
+
+
+def get_showcase_gallery_variants(image_id: int) -> list[dict]:
+ """Return list of ShowcaseGalleryBlock data variants for testing.
+
+ Args:
+ image_id: ID of the placeholder image to use
+
+ Returns:
+ List of block data dictionaries representing different configurations
+ """
+ return [
+ # Variant 1: Default white background, no anchor, multiple tiles
+ {
+ "type": "showcase_gallery_block",
+ "value": {
+ "settings": {
+ "background_color": "",
+ "anchor_id": "",
+ },
+ "heading": "Working at Mozilla",
+ "tiles": [
+ {"image": image_id, "image_alt": "Mozilla team working together"},
+ {"image": image_id, "image_alt": "Mozilla office space"},
+ {"image": image_id, "image_alt": "Mozilla team event"},
+ {"image": image_id, "image_alt": ""},
+ ],
+ "body": "Join a team that believes the internet is for everyone.",
+ "cta_text": "See open roles",
+ "cta_link": {
+ "link_to": "custom_url",
+ "custom_url": "https://www.mozilla.org/careers",
+ "new_window": False,
+ },
+ },
+ "id": "showcase-gallery-variant-1",
+ },
+ # Variant 2: Dark background
+ {
+ "type": "showcase_gallery_block",
+ "value": {
+ "settings": {
+ "background_color": "m24-t-dark",
+ "anchor_id": "",
+ },
+ "heading": "Our Culture",
+ "tiles": [
+ {"image": image_id, "image_alt": "Mozilla culture photo"},
+ {"image": image_id, "image_alt": ""},
+ ],
+ "body": "A mission-driven organization where your work matters.",
+ "cta_text": "Learn about our culture",
+ "cta_link": {
+ "link_to": "custom_url",
+ "custom_url": "https://www.mozilla.org/about/culture",
+ "new_window": False,
+ },
+ },
+ "id": "showcase-gallery-variant-2",
+ },
+ # Variant 3: Green background with anchor_id
+ {
+ "type": "showcase_gallery_block",
+ "value": {
+ "settings": {
+ "background_color": "m24-t-green",
+ "anchor_id": "careers-section",
+ },
+ "heading": "Benefits and Perks",
+ "tiles": [
+ {"image": image_id, "image_alt": "Employee benefits"},
+ {"image": image_id, "image_alt": "Flexible working"},
+ ],
+ "body": "We offer competitive benefits that support your whole life.",
+ "cta_text": "View benefits",
+ "cta_link": {
+ "link_to": "custom_url",
+ "custom_url": "https://www.mozilla.org/careers/benefits",
+ "new_window": False,
+ },
+ },
+ "id": "showcase-gallery-variant-3",
+ },
+ # Variant 4: Orange background, new_window=True
+ {
+ "type": "showcase_gallery_block",
+ "value": {
+ "settings": {
+ "background_color": "m24-t-orange",
+ "anchor_id": "",
+ },
+ "heading": "Join Mozilla",
+ "tiles": [
+ {"image": image_id, "image_alt": "Mozilla team photo"},
+ {"image": image_id, "image_alt": "Mozilla campus"},
+ ],
+ "body": "Help us keep the internet open and accessible.",
+ "cta_text": "Apply now",
+ "cta_link": {
+ "link_to": "custom_url",
+ "custom_url": "https://www.mozilla.org/careers/apply",
+ "new_window": True,
+ },
+ },
+ "id": "showcase-gallery-variant-4",
+ },
+ ]
+
+
+def get_showcase_gallery_test_page() -> AboutUsPage:
+ """Create an AboutUsPage with all showcase gallery block variants for testing.
+
+ Returns:
+ AboutUsPage instance with showcase gallery blocks populated
+ """
+ placeholder_image = get_placeholder_image()
+ variants = get_showcase_gallery_variants(placeholder_image.id)
+ index_page = get_test_index_page()
+
+ test_page = AboutUsPage.objects.filter(slug="showcase-gallery-block-test").first()
+ if test_page:
+ return test_page
+
+ test_page = AboutUsPage(
+ title="Showcase Gallery Block Test Page",
+ slug="showcase-gallery-block-test",
+ content=variants,
+ )
+
+ index_page.add_child(instance=test_page)
+ test_page.save_revision().publish()
+
+ return test_page
diff --git a/bedrock/mozorg/migrations/0027_aboutuspage_alter_homepage_content.py b/bedrock/mozorg/migrations/0027_aboutuspage_alter_homepage_content.py
new file mode 100644
index 00000000000..f2b9a65aefd
--- /dev/null
+++ b/bedrock/mozorg/migrations/0027_aboutuspage_alter_homepage_content.py
@@ -0,0 +1,37 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+# Generated by Django 5.2.13 on 2026-04-16 15:37
+
+import django.db.models.deletion
+import wagtail.admin.forms.choosers
+import wagtail.fields
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('mozorg', '0026_alter_notificationsnippet_notification_text'),
+ ('wagtailcore', '0096_referenceindex_referenceindex_source_object_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AboutUsPage',
+ fields=[
+ ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
+ ('content', wagtail.fields.StreamField([('donate_block', 20), ('gallery_block', 34), ('showcase_block', 42), ('showcase_gallery_block', 48), ('transition_block', 50)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'donate', 'support').", 'max_length': 100, 'required': False}), 1: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('', 'White'), ('m24-t-dark', 'Dark'), ('m24-t-green', 'Green'), ('m24-t-orange', 'Orange'), ('m24-t-pink', 'Pink'), ('m24-t-gray', 'Gray')], 'help_text': 'What color should the background be?', 'required': False}), 2: ('wagtail.blocks.StructBlock', [[('anchor_id', 0), ('background_color', 1)]], {}), 3: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Text'}), 4: ('wagtail.blocks.CharBlock', (), {'help_text': 'Use sentence case.', 'max_length': 255}), 5: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'link'], 'help_text': 'Keep this to 2 paragraphs or fewer.'}), 6: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Image'}), 7: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Ideal image size is 1400 x 700. Image will be cropped to a 2:1 aspect ratio.'}), 8: ('wagtail.blocks.CharBlock', (), {'help_text': "A concise description of the image for someone who can't see it. See alt text guidelines for tips.", 'max_length': 255, 'required': False}), 9: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Call-to-action'}), 10: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Donate', 'Read more').", 'label': 'Link text', 'max_length': 50}), 11: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('page', 'Page'), ('file', 'File'), ('custom_url', 'Custom URL'), ('email', 'Email'), ('anchor', 'Anchor'), ('phone', 'Phone')], 'classname': 'link_choice_type_selector', 'label': 'Link to', 'required': False}), 12: ('wagtail.blocks.PageChooserBlock', (), {'form_classname': 'page_link', 'label': 'Page', 'required': False}), 13: ('wagtail.documents.blocks.DocumentChooserBlock', (), {'form_classname': 'file_link', 'label': 'File', 'required': False}), 14: ('wagtail.blocks.CharBlock', (), {'form_classname': 'custom_url_link url_field', 'label': 'Custom URL', 'max_length': 300, 'required': False, 'validators': [wagtail.admin.forms.choosers.URLOrAbsolutePathValidator()]}), 15: ('wagtail.blocks.CharBlock', (), {'form_classname': 'anchor_link', 'label': '#', 'max_length': 300, 'required': False}), 16: ('wagtail.blocks.EmailBlock', (), {'required': False}), 17: ('wagtail.blocks.CharBlock', (), {'form_classname': 'phone_link', 'label': 'Phone', 'max_length': 30, 'required': False}), 18: ('wagtail.blocks.BooleanBlock', (), {'form_classname': 'new_window_toggle', 'label': 'Open in new window', 'required': False}), 19: ('wagtail.blocks.StructBlock', [[('link_to', 11), ('page', 12), ('file', 13), ('custom_url', 14), ('anchor', 15), ('email', 16), ('phone', 17), ('new_window', 18)]], {'label': 'Link destination'}), 20: ('wagtail.blocks.StructBlock', [[('settings', 2), ('text_divider', 3), ('heading', 4), ('body', 5), ('image_divider', 6), ('image', 7), ('image_alt', 8), ('cta_divider', 9), ('cta_text', 10), ('cta_link', 19)]], {}), 21: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'news', 'gallery').", 'max_length': 100, 'required': False}), 22: ('wagtail.blocks.StructBlock', [[('anchor_id', 21), ('background_color', 1)]], {}), 23: ('wagtail.blocks.CharBlock', (), {'max_length': 255, 'required': False}), 24: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'link'], 'required': False}), 25: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fifth', 'Fifth (20%)'), ('quarter', 'Quarter (25%)'), ('third', 'Third (33%)'), ('half', 'Half (50%)'), ('three-quarters', 'Three-quarters (75%)')], 'help_text': 'Width of the tile in the gallery grid at desktop sizes.'}), 26: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Link'}), 27: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Upload a 2:1 aspect ratio image at 1400×700px - this is displayed on mobile browsers. Wagtail will crop it for displaying at your chosen aspect ratios for desktop.'}), 28: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('2:1', '2:1 Wide landscape'), ('1:1', '1:1 Square'), ('5:4', '5:4 Landscape'), ('4:5', '4:5 Portrait'), ('2:3', '2:3 Tall portrait')], 'help_text': 'Aspect ratio for the image on desktop. The image will be cropped to fit.'}), 29: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('', 'None'), ('community', 'Community'), ('event', 'Event'), ('impact', 'Impact'), ('partnership', 'Partnership'), ('policy', 'Policy'), ('product', 'Product'), ('program', 'Program'), ('project', 'Project'), ('research', 'Research')], 'required': False}), 30: ('wagtail.blocks.TextBlock', (), {'help_text': "Short blurb about what you're linking to.", 'required': False}), 31: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Read more', 'Watch now').", 'label': 'Call to action text (optional)', 'max_length': 100, 'required': False}), 32: ('wagtail.blocks.StructBlock', [[('width', 25), ('link_divider', 26), ('cta_link', 19), ('image_divider', 6), ('image', 27), ('image_ratio', 28), ('image_alt', 8), ('text_divider', 3), ('tag', 29), ('heading', 4), ('body', 30), ('cta_text', 31)]], {}), 33: ('wagtail.blocks.ListBlock', (32,), {'help_text': 'Add gallery tiles. For best results, ensure tile widths add up to 100% per row.', 'min_num': 1}), 34: ('wagtail.blocks.StructBlock', [[('settings', 22), ('heading', 23), ('intro', 24), ('tiles', 33)]], {}), 35: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'showcase', 'support').", 'max_length': 100, 'required': False}), 36: ('wagtail.blocks.StructBlock', [[('anchor_id', 35), ('background_color', 1)]], {}), 37: ('wagtail.blocks.CharBlock', (), {'help_text': 'Section heading. Use sentence case.', 'max_length': 255}), 38: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Ideal image size is 1376 * 515.'}), 39: ('wagtail.blocks.CharBlock', (), {'help_text': 'Section sub heading heading. Use sentence case.', 'max_length': 255, 'required': False}), 40: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Make the title and body content into a two-column layout.', 'inline_form': True, 'label': 'Make it two column layout', 'required': False}), 41: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Read the report', 'Read more').", 'label': 'Link text', 'max_length': 50, 'required': False}), 42: ('wagtail.blocks.StructBlock', [[('settings', 36), ('text_divider', 3), ('heading', 37), ('body', 5), ('image_divider', 6), ('image', 38), ('image_alt', 8), ('sub_heading', 39), ('two_column_layout', 40), ('cta_divider', 9), ('cta_text', 41), ('cta_link', 19)]], {}), 43: ('wagtail.blocks.CharBlock', (), {'help_text': 'Use sentence case.', 'max_length': 255, 'required': False}), 44: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 45: ('wagtail.blocks.StructBlock', [[('image', 44), ('image_alt', 8)]], {}), 46: ('wagtail.blocks.ListBlock', (45,), {'help_text': 'Add gallery tiles. For best results, ensure tile widths add up to 100% per row.', 'min_num': 1}), 47: ('wagtail.blocks.CharBlock', (), {'help_text': 'Use sentence case.', 'label': 'Content for section body', 'max_length': 1000}), 48: ('wagtail.blocks.StructBlock', [[('settings', 22), ('text_divider', 3), ('heading', 43), ('image_divider', 6), ('tiles', 46), ('body', 47), ('cta_text', 41), ('cta_link', 19)]], {}), 49: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('light', 'White'), ('dark', 'Dark'), ('green', 'Green'), ('orange', 'Orange'), ('pink', 'Pink'), ('gray', 'Gray')]}), 50: ('wagtail.blocks.StructBlock', [[('top_color', 49), ('bottom_color', 49)]], {})}, help_text='Add content blocks for the homepage. Blocks will render in the order shown.', null=True)),
+ ],
+ options={
+ 'verbose_name': 'About Us Page',
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.AlterField(
+ model_name='homepage',
+ name='content',
+ field=wagtail.fields.StreamField([('springboard_block', 18), ('donate_block', 37), ('gallery_block', 51), ('showcase_block', 59), ('transition_block', 61)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'media', 'support').", 'max_length': 100, 'required': False}), 1: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('', 'White'), ('m24-t-dark', 'Dark'), ('m24-t-green', 'Green'), ('m24-t-orange', 'Orange'), ('m24-t-pink', 'Pink'), ('m24-t-gray', 'Gray')], 'help_text': 'What color should the background be?', 'required': False}), 2: ('wagtail.blocks.StructBlock', [[('anchor_id', 0), ('background_color', 1)]], {}), 3: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Text'}), 4: ('wagtail.blocks.CharBlock', (), {'help_text': 'Use sentence case.', 'max_length': 255, 'required': False}), 5: ('wagtail.blocks.CharBlock', (), {'help_text': 'Column name, e.g.: Type', 'label': 'Title for column one', 'max_length': 255}), 6: ('wagtail.blocks.CharBlock', (), {'help_text': 'Column name, e.g.: Author(s)', 'label': 'Title for column two', 'max_length': 255}), 7: ('wagtail.blocks.CharBlock', (), {'help_text': 'Column name, e.g.: Topic', 'label': 'Title for column three', 'max_length': 255}), 8: ('wagtail.blocks.CharBlock', (), {'help_text': 'Column name, e.g.: Intro', 'label': 'Title for column four', 'max_length': 255}), 9: ('wagtail.blocks.URLBlock', (), {'char_max_length': 255, 'help_text': "Link to the person's website or social media account with UTMs.", 'required': True}), 10: ('wagtail.blocks.CharBlock', (), {'char_max_length': 255, 'help_text': 'Link attributes limited to data-* (e.g., data-link-text="...", data-link-position="...")', 'required': False}), 11: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('Article', 'Article'), ('Podcast', 'Podcast'), ('Video', 'Video')], 'help_text': 'Selects a visual icon type for the link.', 'required': False}), 12: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('article', 'Article'), ('podcast', 'Podcast'), ('video', 'Video')], 'help_text': 'Selects an icon for the row.', 'required': False}), 13: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('News', 'News'), ('Products', 'Products'), ('Artificial Intelligence', 'Artificial Intelligence'), ('Open Source AI', 'Open Source AI'), ('Privacy & Security', 'Privacy & Security')], 'help_text': 'Selects a topic.', 'required': False}), 14: ('wagtail.blocks.CharBlock', (), {'char_max_length': 255, 'help_text': 'Author name(s), website name', 'required': False}), 15: ('wagtail.blocks.CharBlock', (), {'char_max_length': 255, 'help_text': 'Short preview of the content', 'required': False}), 16: ('wagtail.blocks.StructBlock', [[('url', 9), ('link_attributes', 10), ('type', 11), ('icon', 12), ('topic', 13), ('author', 14), ('preview', 15)]], {}), 17: ('wagtail.blocks.ListBlock', (16,), {'min_num': 1}), 18: ('wagtail.blocks.StructBlock', [[('settings', 2), ('text_divider', 3), ('heading', 4), ('column_one', 5), ('column_two', 6), ('column_three', 7), ('column_four', 8), ('springboard_items', 17)]], {}), 19: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'donate', 'support').", 'max_length': 100, 'required': False}), 20: ('wagtail.blocks.StructBlock', [[('anchor_id', 19), ('background_color', 1)]], {}), 21: ('wagtail.blocks.CharBlock', (), {'help_text': 'Use sentence case.', 'max_length': 255}), 22: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'link'], 'help_text': 'Keep this to 2 paragraphs or fewer.'}), 23: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Image'}), 24: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Ideal image size is 1400 x 700. Image will be cropped to a 2:1 aspect ratio.'}), 25: ('wagtail.blocks.CharBlock', (), {'help_text': "A concise description of the image for someone who can't see it. See alt text guidelines for tips.", 'max_length': 255, 'required': False}), 26: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Call-to-action'}), 27: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Donate', 'Read more').", 'label': 'Link text', 'max_length': 50}), 28: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('page', 'Page'), ('file', 'File'), ('custom_url', 'Custom URL'), ('email', 'Email'), ('anchor', 'Anchor'), ('phone', 'Phone')], 'classname': 'link_choice_type_selector', 'label': 'Link to', 'required': False}), 29: ('wagtail.blocks.PageChooserBlock', (), {'form_classname': 'page_link', 'label': 'Page', 'required': False}), 30: ('wagtail.documents.blocks.DocumentChooserBlock', (), {'form_classname': 'file_link', 'label': 'File', 'required': False}), 31: ('wagtail.blocks.CharBlock', (), {'form_classname': 'custom_url_link url_field', 'label': 'Custom URL', 'max_length': 300, 'required': False, 'validators': [wagtail.admin.forms.choosers.URLOrAbsolutePathValidator()]}), 32: ('wagtail.blocks.CharBlock', (), {'form_classname': 'anchor_link', 'label': '#', 'max_length': 300, 'required': False}), 33: ('wagtail.blocks.EmailBlock', (), {'required': False}), 34: ('wagtail.blocks.CharBlock', (), {'form_classname': 'phone_link', 'label': 'Phone', 'max_length': 30, 'required': False}), 35: ('wagtail.blocks.BooleanBlock', (), {'form_classname': 'new_window_toggle', 'label': 'Open in new window', 'required': False}), 36: ('wagtail.blocks.StructBlock', [[('link_to', 28), ('page', 29), ('file', 30), ('custom_url', 31), ('anchor', 32), ('email', 33), ('phone', 34), ('new_window', 35)]], {'label': 'Link destination'}), 37: ('wagtail.blocks.StructBlock', [[('settings', 20), ('text_divider', 3), ('heading', 21), ('body', 22), ('image_divider', 23), ('image', 24), ('image_alt', 25), ('cta_divider', 26), ('cta_text', 27), ('cta_link', 36)]], {}), 38: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'news', 'gallery').", 'max_length': 100, 'required': False}), 39: ('wagtail.blocks.StructBlock', [[('anchor_id', 38), ('background_color', 1)]], {}), 40: ('wagtail.blocks.CharBlock', (), {'max_length': 255, 'required': False}), 41: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'link'], 'required': False}), 42: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fifth', 'Fifth (20%)'), ('quarter', 'Quarter (25%)'), ('third', 'Third (33%)'), ('half', 'Half (50%)'), ('three-quarters', 'Three-quarters (75%)')], 'help_text': 'Width of the tile in the gallery grid at desktop sizes.'}), 43: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Link'}), 44: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Upload a 2:1 aspect ratio image at 1400×700px - this is displayed on mobile browsers. Wagtail will crop it for displaying at your chosen aspect ratios for desktop.'}), 45: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('2:1', '2:1 Wide landscape'), ('1:1', '1:1 Square'), ('5:4', '5:4 Landscape'), ('4:5', '4:5 Portrait'), ('2:3', '2:3 Tall portrait')], 'help_text': 'Aspect ratio for the image on desktop. The image will be cropped to fit.'}), 46: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('', 'None'), ('community', 'Community'), ('event', 'Event'), ('impact', 'Impact'), ('partnership', 'Partnership'), ('policy', 'Policy'), ('product', 'Product'), ('program', 'Program'), ('project', 'Project'), ('research', 'Research')], 'required': False}), 47: ('wagtail.blocks.TextBlock', (), {'help_text': "Short blurb about what you're linking to.", 'required': False}), 48: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Read more', 'Watch now').", 'label': 'Call to action text (optional)', 'max_length': 100, 'required': False}), 49: ('wagtail.blocks.StructBlock', [[('width', 42), ('link_divider', 43), ('cta_link', 36), ('image_divider', 23), ('image', 44), ('image_ratio', 45), ('image_alt', 25), ('text_divider', 3), ('tag', 46), ('heading', 21), ('body', 47), ('cta_text', 48)]], {}), 50: ('wagtail.blocks.ListBlock', (49,), {'help_text': 'Add gallery tiles. For best results, ensure tile widths add up to 100% per row.', 'min_num': 1}), 51: ('wagtail.blocks.StructBlock', [[('settings', 39), ('heading', 40), ('intro', 41), ('tiles', 50)]], {}), 52: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'showcase', 'support').", 'max_length': 100, 'required': False}), 53: ('wagtail.blocks.StructBlock', [[('anchor_id', 52), ('background_color', 1)]], {}), 54: ('wagtail.blocks.CharBlock', (), {'help_text': 'Section heading. Use sentence case.', 'max_length': 255}), 55: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Ideal image size is 1376 * 515.'}), 56: ('wagtail.blocks.CharBlock', (), {'help_text': 'Section sub heading heading. Use sentence case.', 'max_length': 255, 'required': False}), 57: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Make the title and body content into a two-column layout.', 'inline_form': True, 'label': 'Make it two column layout', 'required': False}), 58: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Read the report', 'Read more').", 'label': 'Link text', 'max_length': 50, 'required': False}), 59: ('wagtail.blocks.StructBlock', [[('settings', 53), ('text_divider', 3), ('heading', 54), ('body', 22), ('image_divider', 23), ('image', 55), ('image_alt', 25), ('sub_heading', 56), ('two_column_layout', 57), ('cta_divider', 26), ('cta_text', 58), ('cta_link', 36)]], {}), 60: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('light', 'White'), ('dark', 'Dark'), ('green', 'Green'), ('orange', 'Orange'), ('pink', 'Pink'), ('gray', 'Gray')]}), 61: ('wagtail.blocks.StructBlock', [[('top_color', 60), ('bottom_color', 60)]], {})}, help_text='Add content blocks for the homepage. Blocks will render in the order shown.', null=True),
+ ),
+ ]
diff --git a/bedrock/mozorg/models.py b/bedrock/mozorg/models.py
index 08c03984608..33ce5eef72e 100644
--- a/bedrock/mozorg/models.py
+++ b/bedrock/mozorg/models.py
@@ -20,7 +20,7 @@
SectionBlock,
TwoColumnDetailBlock,
)
-from bedrock.mozorg.blocks.common import DonateBlock, GalleryBlock, ShowcaseBlock, SpringboardBlock, TransitionBlock
+from bedrock.mozorg.blocks.common import DonateBlock, GalleryBlock, ShowcaseBlock, ShowcaseGalleryBlock, SpringboardBlock, TransitionBlock
from bedrock.mozorg.blocks.leadership import LeadershipSectionBlock
from bedrock.mozorg.blocks.navigation import NavigationLinkBlock
@@ -461,3 +461,47 @@ def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
context["utm_parameters"] = self.get_utm_parameters()
return context
+
+
+class AboutUsPage(AbstractBedrockCMSPage):
+ subpage_types = [
+ LeadershipPage,
+ ]
+
+ max_count = 1 # Ensure there's only one instance of this page
+ ftl_files = ["mozorg/about-m24"]
+
+ content = StreamField(
+ [
+ ("donate_block", DonateBlock()),
+ ("gallery_block", GalleryBlock()),
+ ("showcase_block", ShowcaseBlock()),
+ ("showcase_gallery_block", ShowcaseGalleryBlock()),
+ ("transition_block", TransitionBlock()),
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ help_text="Add content blocks for the homepage. Blocks will render in the order shown.",
+ )
+
+ content_panels = [
+ FieldPanel("title", help_text="Help identify this page for other editors."),
+ FieldPanel("content"),
+ ]
+
+ template = "mozorg/cms/about/about-us.html"
+
+ def get_utm_parameters(self):
+ return {
+ **BASE_UTM_PARAMETERS,
+ "utm_campaign": self.slug or "about-us",
+ }
+
+ def get_context(self, request, *args, **kwargs):
+ context = super().get_context(request, *args, **kwargs)
+ context["utm_parameters"] = self.get_utm_parameters()
+ return context
+
+ class Meta:
+ verbose_name = "About Us Page"
diff --git a/bedrock/mozorg/templates/mozorg/cms/about/about-us.html b/bedrock/mozorg/templates/mozorg/cms/about/about-us.html
new file mode 100644
index 00000000000..f4747db5600
--- /dev/null
+++ b/bedrock/mozorg/templates/mozorg/cms/about/about-us.html
@@ -0,0 +1,37 @@
+{#
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
+#}
+
+{% extends "base-protocol-mozilla.html" %}
+
+{% block page_title %}{{ ftl('m24-about-page-title') }}{% endblock %}
+{% block page_title_suffix %}{% endblock %}
+
+{% block page_desc %}
+ {{ ftl('m24-about-page-desc') }}
+{% endblock %}
+
+{% block body_id %}about{% endblock %}
+
+{% block page_css %}
+ {{ css_bundle('m24-root') }}
+ {{ css_bundle('m24-base') }}
+ {{ css_bundle('m24-about') }}
+{% endblock %}
+
+{% set utm_params = '?utm_source=www.mozilla.org&utm_medium=referral&utm_campaign=m24-about' %}
+
+{% block content %}
+
{{ value.body }}
+ +