Skip to content

feat: benefits panel primary buttons on primary background need secondary border#174

Merged
fsbraun merged 5 commits intomainfrom
feat/benefits-panel-primary-buttons-on-primary-background-need-secondary-border
Apr 6, 2026
Merged

feat: benefits panel primary buttons on primary background need secondary border#174
fsbraun merged 5 commits intomainfrom
feat/benefits-panel-primary-buttons-on-primary-background-need-secondary-border

Conversation

@maxnoelp2
Copy link
Copy Markdown
Collaborator

@maxnoelp2 maxnoelp2 commented Mar 27, 2026

This pull request adds support for an optional bottom image to the benefits card component, updates the card's template and styling to display this image, and improves the card's button styling for better visual consistency.

Enhancements to the benefits card component:

  • Added a new bottom_image field to the benefits card model, allowing editors to optionally specify an image to display at the bottom of the card.
  • Updated the benefits_card.html template to render the bottom image if provided, and refactored conditional rendering for card icon, title, and content for better robustness.

Styling and visual improvements:

  • Introduced new SCSS styles for .benefits-card-bottom-image to ensure the bottom image is displayed full-width with appropriate border radius and overflow handling.
  • Enhanced button styles within .benefits-card.bg-primary to use secondary outline styling and improved hover state for better accessibility and consistency.

Summary by Sourcery

Extend the benefits card component with optional bottom imagery and refine its conditional content rendering and visual styling.

New Features:

  • Allow benefits cards to include an optional full-width bottom image configured via a new bottom image field.

Enhancements:

  • Make rendering of card icon, title, content, and child plugins conditional for more robust benefits card layouts.
  • Adjust benefits card bottom image layout and border radius for seamless integration with the card container.
  • Update primary-background benefits cards so their buttons use secondary outline styling with improved hover contrast.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Mar 27, 2026

Reviewer's Guide

Adds optional bottom image support to the benefits card component, tightens conditional rendering for card content and actions, and updates SCSS so primary-background cards use secondary-outline button styling with a tailored hover state.

Updated class diagram for the benefits card component

classDiagram
    class CMSFrontendComponent {
    }

    class BenefitsCard {
        ImageFormField card_icon
        CharField card_title
        RichTextField card_content
        CharField text_color
        List child_plugin_instances
        ImageFormField bottom_image
        get_classes()
        get_attributes()
    }

    CMSFrontendComponent <|-- BenefitsCard
Loading

Flow diagram for conditional rendering in benefits_card.html

flowchart TD
    A[Render benefits_card.html] --> B{instance.card_icon exists}
    B -->|yes| C[Render icon wrapper and icon]
    B -->|no| D[Skip icon wrapper]

    C --> E{instance.card_title exists}
    D --> E

    E -->|yes| F[Render card_title wrapper and inline_field]
    E -->|no| G[Skip title wrapper]

    F --> H{instance.card_content stripped is nonempty}
    G --> H

    H -->|yes| I[Render benefits-card-body and inline_field card_content]
    H -->|no| J[Skip card_content block]

    I --> K{instance.child_plugin_instances exists}
    J --> K

    K -->|yes| L[Render mt-auto wrapper and childplugins]
    K -->|no| M[Skip childplugins block]

    L --> N{instance.bottom_image exists}
    M --> N

    N -->|yes| O[Render benefits-card-bottom-image wrapper and img]
    N -->|no| P[Skip bottom image]
Loading

File-Level Changes

Change Details Files
Make icon, title, content, CTA area, and new bottom image of the benefits card render only when data is present.
  • Guard icon markup with a check for the presence of instance.card_icon before rendering the element.
  • Render the card title section only when instance.card_title is set, still using inline_field for editing.
  • Render the card content section only when the stripped content has text, simplifying the inline_field usage and dropping the inner presence check.
  • Wrap child plugin rendering in a conditional to only show the CTA container when child_plugin_instances exist.
  • Add a conditional wrapper for rendering a full-width bottom image when instance.bottom_image is provided.
cms_theme/templates/benefits/benefits_card.html
Add model support for an optional bottom image on benefits cards.
  • Introduce a new ImageFormField named bottom_image on the benefits card component model.
  • Mark the field as optional and add a descriptive help text indicating it will be displayed full-width at the bottom of the card.
cms_theme/cms_components.py
Style the new bottom card image and update button styles for benefits cards on a primary background.
  • Add .benefits-card-bottom-image styles to pull the image to the card edges, apply bottom-only border radius, and ensure overflow is clipped and the image is full-width.
  • Adjust .benefits-card.bg-primary a to extend .btn-outline-secondary, set link color to the secondary color token, and define a hover state that fills the background with secondary and switches text to white.
backend/static/scss/_benefits_panel.scss

Assessment against linked issues

Issue Objective Addressed Explanation
#171 Update benefits panel styling so that primary buttons displayed on a primary background gain a visible secondary border (or outline) to improve contrast and visibility.

Possibly linked issues

  • #(not specified): They match: PR changes .benefits-card.bg-primary buttons to secondary outline, fixing visibility on primary background.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@maxnoelp2 maxnoelp2 requested a review from fsbraun March 27, 2026 10:03
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • You’ve wrapped the icon markup in an {% if instance.card_icon %} but still unconditionally call {% add_css_for_icon instance.card_icon %} above; consider guarding that tag with the same condition to avoid issues when card_icon is missing.
  • The selector .benefits-card.bg-primary a will affect all links in the card, not just the primary button; consider scoping this styling to a more specific class (e.g. .btn-primary or a custom button class) to avoid unintended link styles.
  • The new bottom image renders with an empty alt attribute; if it conveys meaning, consider exposing an alt/description field on the model, or explicitly mark it as decorative (e.g. alt="" with appropriate role) to be intentional about accessibility.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- You’ve wrapped the icon markup in an `{% if instance.card_icon %}` but still unconditionally call `{% add_css_for_icon instance.card_icon %}` above; consider guarding that tag with the same condition to avoid issues when `card_icon` is missing.
- The selector `.benefits-card.bg-primary a` will affect all links in the card, not just the primary button; consider scoping this styling to a more specific class (e.g. `.btn-primary` or a custom button class) to avoid unintended link styles.
- The new bottom image renders with an empty `alt` attribute; if it conveys meaning, consider exposing an alt/description field on the model, or explicitly mark it as decorative (e.g. `alt=""` with appropriate role) to be intentional about accessibility.

## Individual Comments

### Comment 1
<location path="cms_theme/templates/benefits/benefits_card.html" line_range="11-10" />
<code_context>
+                <i class="{{ instance.card_icon.iconClass }} display-1"></i>
+            </div>
+        {% endif %}
+        {% if instance.card_title %}
         <div class="benefits-card-wrapper benefits-card-margin-bottom">
             <h3 class="h4 fw-bold mb-4">{% inline_field instance "card_title" %}</h3>
         </div>
+        {% endif %}
+        {% if instance.card_content|striptags %}
         <div class="benefits-card-spacing flex-grow-1 benefits-card-body mt-auto">
</code_context>
<issue_to_address>
**issue:** Conditionally hiding the title block may disable inline editing when the field is empty.

Because the `inline_field` is only rendered when `instance.card_title` is truthy, an empty title removes the field and its inline controls, preventing editors from adding a title in place. Instead, keep the `inline_field` always rendered and handle the empty visual state with CSS, a placeholder, or a different condition that still allows inline editing.
</issue_to_address>

### Comment 2
<location path="cms_theme/templates/benefits/benefits_card.html" line_range="26-28" />
<code_context>
-        </div>
+            </div>
+        {% endif %}
+        {% if instance.bottom_image %}
+            <div class="benefits-card-bottom-image">
+                <img src="{{ instance.bottom_image.url }}" alt="" class="w-100 d-block">
+            </div>
+        {% endif %}
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Empty `alt` attribute on the bottom image may be problematic for accessibility depending on its purpose.

Empty `alt` should only be used for purely decorative images. If this image can ever convey information (e.g., product, meaningful logo, etc.), add a field for its description on `bottom_image` and use that value for `alt` so screen reader users don’t lose context.

Suggested implementation:

```
        {% if instance.bottom_image %}
            <div class="benefits-card-bottom-image">
                <img
                    src="{{ instance.bottom_image.url }}"
                    alt="{{ instance.bottom_image.description|default_if_none:'' }}"
                    class="w-100 d-block"
                >
            </div>
        {% endif %}

```

To fully support this in the codebase, you will also need to:
1. Add a `description` (or `alt_text`) field to whatever model backs `instance.bottom_image` (e.g. the image model or the plugin model) to store meaningful alt text.
2. Expose that field in the appropriate admin / CMS forms so editors can provide a description.
3. If `bottom_image` is meant to be decorative in some cases, document that editors can leave the description blank when it is purely decorative, in which case the template will render an empty `alt` attribute.
</issue_to_address>

### Comment 3
<location path="backend/static/scss/_benefits_panel.scss" line_range="28-33" />
<code_context>
+	}
+}
+
+.benefits-card.bg-primary {
+	a {
+		@extend .btn-outline-secondary;
+		color: var(--bs-secondary);
+
+		&:hover {
+			background-color: var(--bs-secondary);
+			color: white;
</code_context>
<issue_to_address>
**suggestion:** Using `@extend .btn-outline-secondary` inside `.benefits-card.bg-primary a` may cause CSS selector bloat or unexpected coupling.

`@extend` will inject `.benefits-card.bg-primary a` into every selector that uses `.btn-outline-secondary`, which can bloat the compiled CSS and tightly couple this link style to Bootstrap internals. To reuse the styles more safely, prefer a mixin or shared utility class instead of extending the Bootstrap button class directly.

Suggested implementation:

```
.benefits-card.bg-primary {
	a {
		// Use Bootstrap's mixin instead of extending the class to avoid selector bloat
		@include button-outline-variant($secondary);
		color: var(--bs-secondary);

		&:hover {
			background-color: var(--bs-secondary);
			color: white;
		}
	}
}

```

If `button-outline-variant` is not yet in scope in this SCSS file, ensure that:
1. The main Bootstrap SCSS (or the file that defines the button mixins) is imported before `_benefits_panel.scss`.
2. If your project does not use Bootstrap’s SCSS mixins, you can instead create a local mixin (e.g. `@mixin benefits-outline-secondary-button { ... }`) that encapsulates the desired button styles, and replace `@include button-outline-variant($secondary);` with `@include benefits-outline-secondary-button;`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

<div class="mb-4 pb-1">
<i class="{{ instance.card_icon.iconClass }} display-1"></i>
</div>
{% endif %}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Conditionally hiding the title block may disable inline editing when the field is empty.

Because the inline_field is only rendered when instance.card_title is truthy, an empty title removes the field and its inline controls, preventing editors from adding a title in place. Instead, keep the inline_field always rendered and handle the empty visual state with CSS, a placeholder, or a different condition that still allows inline editing.

Comment on lines +26 to +28
{% if instance.bottom_image %}
<div class="benefits-card-bottom-image">
<img src="{{ instance.bottom_image.url }}" alt="" class="w-100 d-block">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Empty alt attribute on the bottom image may be problematic for accessibility depending on its purpose.

Empty alt should only be used for purely decorative images. If this image can ever convey information (e.g., product, meaningful logo, etc.), add a field for its description on bottom_image and use that value for alt so screen reader users don’t lose context.

Suggested implementation:

        {% if instance.bottom_image %}
            <div class="benefits-card-bottom-image">
                <img
                    src="{{ instance.bottom_image.url }}"
                    alt="{{ instance.bottom_image.description|default_if_none:'' }}"
                    class="w-100 d-block"
                >
            </div>
        {% endif %}

To fully support this in the codebase, you will also need to:

  1. Add a description (or alt_text) field to whatever model backs instance.bottom_image (e.g. the image model or the plugin model) to store meaningful alt text.
  2. Expose that field in the appropriate admin / CMS forms so editors can provide a description.
  3. If bottom_image is meant to be decorative in some cases, document that editors can leave the description blank when it is purely decorative, in which case the template will render an empty alt attribute.

Comment on lines +28 to +33
.benefits-card.bg-primary {
a {
@extend .btn-outline-secondary;
color: var(--bs-secondary);

&:hover {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Using @extend .btn-outline-secondary inside .benefits-card.bg-primary a may cause CSS selector bloat or unexpected coupling.

@extend will inject .benefits-card.bg-primary a into every selector that uses .btn-outline-secondary, which can bloat the compiled CSS and tightly couple this link style to Bootstrap internals. To reuse the styles more safely, prefer a mixin or shared utility class instead of extending the Bootstrap button class directly.

Suggested implementation:

.benefits-card.bg-primary {
	a {
		// Use Bootstrap's mixin instead of extending the class to avoid selector bloat
		@include button-outline-variant($secondary);
		color: var(--bs-secondary);

		&:hover {
			background-color: var(--bs-secondary);
			color: white;
		}
	}
}

If button-outline-variant is not yet in scope in this SCSS file, ensure that:

  1. The main Bootstrap SCSS (or the file that defines the button mixins) is imported before _benefits_panel.scss.
  2. If your project does not use Bootstrap’s SCSS mixins, you can instead create a local mixin (e.g. @mixin benefits-outline-secondary-button { ... }) that encapsulates the desired button styles, and replace @include button-outline-variant($secondary); with @include benefits-outline-secondary-button;.

@fsbraun fsbraun changed the title Feat/benefits panel primary buttons on primary background need secondary border feat: benefits panel primary buttons on primary background need secondary border Mar 27, 2026
@fsbraun fsbraun merged commit 9ea7cee into main Apr 6, 2026
1 check passed
@fsbraun fsbraun deleted the feat/benefits-panel-primary-buttons-on-primary-background-need-secondary-border branch April 6, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benefits panel: Primary buttons on primary background need secondary border

2 participants