Skip to main content

Donation v2 page upgrade kit | Bootstrap v4

Use this upgrade kit to modernize your Bootstrap v4 donation page

Updated over a week ago

This upgrade kit modernizes NationBuilder donation pages built on Bootstrap v4 by introducing Stripe’s new payment element and related enhancements. It streamlines payment processing with:

  • Direct debit payments (GBP processing only)

  • One-time, monthly, and annual donation frequency options on one form

  • Single-submit express payment options

  • Streamlined employer and occupation submissions

Before getting started

  1. Backup First: Always back up custom theme files before implementing changes.

  2. Test Incrementally: Consider implementing and testing each part separately in a cloned theme.

  3. Configure a payment processor: You must have a NationBuilder Payment processor connected to your Donation v2 page in order for the payment form to display correctly. Note that Bacs Direct Debit is only available to payment processors configured to process donations in GBP.

Applying the upgrade kit

Download the kit

This kit includes the following files:

  • _stripe.scss

  • pages_show_donation_v2_wide.html

  • readme

Theme Setup

With new custom themes

Upload new _stripe.scss and pages_show_donation_v2_wide.html files

For new custom themes based on bootstrap_v4, these two files can be uploaded to the custom theme files. By visiting Theme > Current Custom Theme > Files.

With existing custom themes

Upload or apply changes to _stripe.scss

If this file doesn’t already exist in the custom theme files, upload it by visiting Theme > Current Custom Theme > Files.

If this file already exists in the theme, the following style changes will be required to render the new payment element properly. Alternatively, if the styling does not involve extending the form-control class, no changes may be required here. This ensures the height and no bordering around the payment element appears as expected.

.StripeElement {

@extend .form-control;

padding-top: 0.475rem;

height: auto; // Such that StripeElement doesn't break out of cc-info div

border: none;

}

Apply changes to pages_show_donation_v2_wide.html

Changes applied to this donation page can be done at the page level for testing, and if there are theme template configurations to preserve for the site. The changes can be applied to the template on the custom theme once it’s ready to apply to all the donation pages. Updating the template at the theme level will apply it to all donation pages on the website that haven’t been customized.

Add new amount input improvements

This page improvement allows one-time, monthly and annual donations options to be selected from the same page.

📌 Note: the updated donation page settings to control frequency options will only display once the {% payment_field 'payments' %} liquid is present on the donation page template or site-level pages_show_donation_v2_wide.html template.

If this specific liquid is not directly present in either file, the frequency settings shown to control panel users will not allow "annual" to be selected alongside other recurrence options, and a notice will display that the donation page code is out of date.

Add changes to support one-time, monthly, and annual donation frequency options

This fieldset may be located in the else block for the condition request.current_order, around line 74. The CSS class changes noted below are an example with the bootstrap theme. Whether they are needed is dependent on the theme implementation. The end result should be that all three frequency options render as would be expected across screen sizes. Note the change in liquid below from page.donation_v2.monthly_recurring_radio_buttons to page.donation_v2.recurring_radio_buttons.

Before:

<fieldset>

<legend>Amount</legend>

<div class="form-row">

{% if page.donation_v2.amount_in_cents != 0 %}

<div class="form-group col">

<p class="h2 single-amount mb-0">{{ page.donation_v2.amount_formatted }}</p>

</div>

{% else %}

<div class="form-group col">

<div class="donation-v2-amounts">

{{ page.donation_v2.form_amount_options }}

</div>

</div>

{% endif %}

</div>

{% if page.donation_v2.accepts_variable_amounts? or page.donation_v2.donation_frequency == "any" %}

<div class="form-row align-items-center donation-v2-options {% if page.donation_v2.accepts_variable_amounts? == false %}single-amount-any-frequency{% endif %}">

{% if page.donation_v2.accepts_variable_amounts? %}

<div class="form-group col-md-6">

{{ page.donation_v2.amount_other }}

</div>

{% endif %}

{% if page.donation_v2.donation_frequency == "any" %}

<div class="form-group {% if page.donation_v2.accepts_variable_amounts? == true %}col-md-6{%else%}col{% endif %}">

<div class="donation-v2-occurence-radio radio-inline clearfix">

{{ page.donation_v2.monthly_recurring_radio_buttons }}

</div>

</div>

{% endif %}

{% if page.donation_v2.donation_frequency == "monthly" %}

<div class="form-group col-md-6 card-monthly">

<span class="text-secondary">Paid monthly</span>

</div>

{% elsif page.donation_v2.donation_frequency == "annual" %}

<div class="form-group col-md-6 card-monthly">

<span class="text-secondary">Paid annually</span>

</div>

{% endif %}

</div>

{% endif %}

</fieldset>

After:

<fieldset aria-labelledby="cc-disclaimer">

<legend>Amount</legend>

<div class="form-row flex-column">

{% if page.donation_v2.amount_in_cents != 0 %}

<div class="form-group col-md-8">

<p class="h2 single-amount mb-0">{{ page.donation_v2.amount_formatted }}</p>

</div>

{% else %}

<div class="form-group col-md-8">

<div class="donation-v2-amounts">

{{ page.donation_v2.form_amount_options }}

</div>

</div>

{% endif %}

<div class="form-row align-items-center {% if page.donation_v2.accepts_variable_amounts? == false %}single-amount-any-frequency{% endif %}">

{% if page.donation_v2.accepts_variable_amounts? %}

<div class="form-group">

{{ page.donation_v2.amount_other }}

</div>

{% endif %}

{% if page.donation_v2.donation_frequency == "monthly" %}

<div class="form-group col card-monthly">

<span class="text-secondary">Paid monthly</span>

</div>

{% elsif page.donation_v2.donation_frequency == "annual" %}

<div class="form-group col card-monthly">

<span class="text-secondary">Paid annually</span>

</div>

{% endif %}

</div>

</div>

</fieldset>

<div

class="form-row align-items-center donation-v2-options {% if page.donation_v2.accepts_variable_amounts? == false %}single-amount-any-frequency{% endif %}">

{% if page.donation_v2.show_frequency_radio_buttons? %}

<div class="form-group col">

<div class="donation-v2-occurence-radio radio-inline clearfix">

{{ page.donation_v2.recurring_radio_buttons }}

</div>

</div>

{% endif %}

</div>

Add aria-labelledby to the amount fieldset

Within the same else block above, this accessibility improvement will allow the new payment disclaimer to be associated with the amount input before the payment element renders. An amount is required before payment method options can be loaded. This fieldset starts around line 75.

Before:

<fieldset>

<legend>Amount</legend>

After:

<fieldset aria-labelledby="cc-disclaimer">

<legend>Amount</legend>

Add new payment disclaimer

Before the payment element can be displayed on the donation page, a donation amount must be selected using one of the preset options or the custom amount input. This disclaimer, linked to the amount fieldset, appears when the page first loads and can be added around line 117, below the amount selection.

<div data-payment-disclaimer>

<p id="cc-disclaimer">Please select or enter an amount</p>

</div>

Remove legacy express payment options elements

Remove the following elements for the legacy implementation of express payment options. Express payment options such as GooglePay and ApplePay will be included in the payment element.

<div id="payment-methods" class="row">

{% if page.donation_v2.express_payment_activated? %}

<div id="donation-v2-payment-request"></div>

{% endif %}

<div id="cc-request-button">

<button type="button" class="btn btn-primary btn-lg">

<i id="credit-card-icon"></i>Credit Card</button>

</div>

<div id="apple-pay-notice" class="padtopless">

<small>If you use Apple Pay, your confirmation prompt may refer to our payment processor, "NationBuilder"</small>

</div>

</div>

<div id="selected-payment-method" class="row">

<div class="padtop">

<i id="payment-method-icon"></i><a href="#" class="change-payment-method">Change payment method</a>

</div>

<p>You're almost done! Submit {% if request.current_order %}payment {% else %}donation {% endif %}below.</p>

</div>

Add accessibility fix for changing submitted payment method

This change correctly updates the div with id submitted-payment-method to use a button instead of a link for screen readers when changing a payment method. This can be found around 143.

Before:

<div id="submitted-payment-method" class="row">

<div class="col-sm-12">

<p>Payment method information has been saved. <a href="#" class="change-submitted-payment-method">Change payment method</a></p>

</div>

</div>

After:

<div id="submitted-payment-method" class="row">

<div class="col-sm-12">

<p>Payment method information has been saved. <button type="button" class="btn btn-link change-submitted-payment-method">Change payment method</button></p>

</div>

</div>

Replace card element with payment element

This replaces the legacy Stripe card element with the payment element. This is located around line 147. The key change is rendering the element with payment_field 'payments' instead of payment_field 'card'. The ApplePay notice removed in the previous step is added again here, below the payment element.

Before:

<div id="cc-info" class="row">

<div class="col-sm-12">

<div class="form-group">

<label>Credit Card</label>

{% payment_field 'card' %}

</div>

<p class="padtop text-center">

<a href="#" class="change-payment-method">Change payment method</a>

</p>

</div>

</div>

After:

<div id="cc-info" class="row">

<div class="col-sm-12">

<div class="form-group">

{% payment_field 'payments' %}

</div>

</div>

</div>

<small id="apple-pay-notice">If you use Apple Pay, your confirmation prompt may refer to our payment processor, "NationBuilder"</small>

Remove #your-info id from div wrapping demographic information fields

This removes an id that is not required when using the payment element on the donation page. This id is only used for the legacy payment request button to hide demographic information fields when accepting payments with EPO. Leaving this id on the div can cause unexpected behavior with the new payment element. This is located around line 159.

Before:

<div id="your-info">

<fieldset>

<legend>Yourinformation</legend>

<divclass="row">

After:

<div>

<fieldset>

<legend>Your information</legend>

<divclass="row">

Remove the div wrapping demographic information fields (optional)

Optionally, if the div above is not used for any other styling or needs in the theme, it can be completely removed. The closing div tag will need to be removed as well. This is around line 428. It may be labeled with the following comment.

Before:

<div>

<fieldset>

<legend>Your information</legend>

<divclass="row">

Before removing closing div tag around line 428:

</div> <!-- /#your-info -->

After:

<fieldset>

<legend>Your information</legend>

<div class="row">

Remove read-only elements related to legacy express payment options

Remove the following elements used by the legacy implementation of express payment options. The demographics-read-only elements are located around line 166.

<div class="col-lg-12">

<p id="demographics-read-only">

<span id="demographics-name"></span><a href="#" id="edit-demographics">Edit</a><br />

<span id="demographics-email"></span><br />

<span id="demographics-phone"></span>

</p>

</div>

The address-read-only elements to remove are located around line 194.

<div class="row">

<div class="col-sm-12">

<p id="address-read-only">

<span class="address-line">

<span id="address-line1"></span><a href="#" id="edit-address">Edit</a>

</span>

<span class="address-line">

<span id="address-line2"></span>

</span>

<span class="address-line">

<span id="address-city"></span>, <span id="address-state"></span> <span id="address-zip"></span>

</span>

<span class="address-line">

<span id="address-country"></span>

</span>

</p>

</div>

</div>

The billing address fields will be hidden if an express payment option is selected, as these fields will be autocollected from the donor’s digital wallet. Billing address fields will display for payment methods where this feature isn’t available, such as credit cards and direct debit.

Update demographic information fields to be required attributes

The following fields within the div with the id demographics-info are also updated to be required attributes. This will help when submitting donations with express payment options using the payment element and allow the form to rely on validations of these fields when collecting data from the donor’s wallet. This is located around line 173. This is only required for the email, first name, and last name fields.

Before:

<div id="demographics-info">

<div class="form-row">

<div class="form-group col-md-6">

<label for="donation_first_name">First Name</label>

{% text_field "first_name", class:"form-control" %}

</div>

<div class="form-group col-md-6">

<label for="donation_last_name">Last Name</label>

{% text_field "last_name", class:"form-control" %}

</div>

</div>

<div class="form-group">

<label for="donation_email">Email</label>

{% email_field "email", class:"form-control" %}

</div>

<div class="form-group">

<label for="donation_billing_address_phone_number">Phone</label>

{% phone_field "billing_address.phone_number", class:"form-control" %}

</div>

</div>

After:

<div id="demographics-info">

<div class="form-row">

<div class="form-group col-md-6">

<label for="donation_first_name">First Name</label>

{% text_field "first_name", class: "form-control", required: "required" %}

</div>

<div class="form-group col-md-6">

<label for="donation_last_name">Last Name</label>

{% text_field "last_name", class: "form-control", required: "required" %}

</div>

</div>

<div class="form-group">

<label for="donation_email">Email</label>

{% email_field "email", class: "form-control", required: "required" %}

</div>

<div class="form-group">

<label for="donation_billing_address_phone_number">Phone</label>

{% phone_field "billing_address.phone_number", class:"form-control" %}

</div>

</div>

Add new “I do not have an employer” checkbox

This page improvement now allows donors to select a “I do not have an employer” checkbox to hide the fields from the form and autopopulate any required fields on the donation page.

Add the “I do not have an employer” checkbox

The checkbox can be added in the if block with the condition page.donation_v2.merchant_account.is_employer_and_occupation_required? around line 243. The class w-100 is only as needed for the theme.

<div class="form-group w-100">

<div class="custom-control custom-checkbox">

{% check_box "no_employer", class:"custom-control-input checkbox" %}

<label class="custom-control-label" for="donation_no_employer">I do not have an employer</label>

</div>

</div>

Include updated legal disclaimer

This update to the legal disclaimer at the bottom of the block reflects the new checkbox behavior. This is located around line 306.

Before:

<div class="form-group">

<p class="text-secondary">

Law requires we ask for your employer and occupation. If you don't have an employer

or are retired, put N/A, and if you are self-employed put "self-employed" in employer

and describe your occupation.

</p>

</div>

After:

<div class="form-group">

<p class="text-secondary">

Law requires we ask for your employer and occupation. If you do not have an employer

or are retired, check "I do not have an employer". If you are self-employed put

"self-employed" in employer and describe your occupation.

</p>

</div>

Add new activity stream consent checkbox behavior

This donation improvement presents the option "Don't publish my donation on the website” only if the “Show stream” page and site settings are enabled. The change adds a new condition and page.show_stream? around line 376.

{% if site.ask_to_publish_to_stream? and page.show_stream? %}

<div class="custom-control custom-checkbox">

{% check_box "is_private", class:"custom-control-input checkbox" %}

<label for="donation_is_private" class="custom-control-label checkbox">Don't publish my donation on the website.</label>

</div>

{%endif%}

Change donation amount confirmation message to include new frequencies (optional)

This is optional for themes that include the donation total at the bottom of the form. The following changes will be needed to support one-time, monthly, and annual donation frequency options in the submit confirmation. This implementation may vary depending on the theme. Note the changes from div tags to p tags and the changes in the final else block. The end result is that the donation confirmation will display with the correct frequency, either monthly or annually, based on the selection. This group of elements can be found around line 400.

Before:

{% if page.donation_v2.confirmation_amount %}

<span class="pt-1 align-self-start">{{ page.donation_v2.currency_symbol }}</span>

<span class="nb_donation_v2_amount h2 mb-0 mr-1">{{ page.donation_v2.confirmation_amount }}</span>

{% else %}

<span class="hidden hide pt-1 align-self-start">{{ page.donation_v2.currency_symbol }}</span>

<span class="nb_donation_v2_amount h2 mb-0 mr-1"><strong class="h6 text-danger">Total will show here after an amount has been selected</strong></span>

{% endif %}

{% if page.donation_v2.donation_frequency == "one-time" %}

{% elsif page.donation_v2.donation_frequency == "monthly" %}

<div class="text-muted nb_donation_v2_interval pt-1" data-placeholder="paid monthly">paid monthly</div>

{% elsif page.donation_v2.donation_frequency == "annual" %}

<div class="text-muted nb_donation_v2_interval pt-1" data-placeholder="paid annually">paid annually</div>

{% else %}

<div class="text-muted nb_donation_v2_interval pt-1" data-placeholder="paid monthly">{% if page.donation_v2.interval_monthly? %}paid monthly{% endif %}</div>

{% endif %}

After:

{% if page.donation_v2.confirmation_amount %}

<span class="pt-1 align-self-start">{{ page.donation_v2.currency_symbol }}</span>

<span class="nb_donation_v2_amount h2 mb-0 mr-1">{{page.donation_v2.confirmation_amount }}</span>

{% else %}

<span class="hidden hide pt-1 align-self-start">{{ page.donation_v2.currency_symbol }}</span>

<span class="nb_donation_v2_amount h2 mb-0 mr-1"><strong class="h6 text-danger">Total will show here after an amount has been selected</strong></span>

{% endif %}

{% if page.donation_v2.donation_frequency == "one-time" %}

{% elsif page.donation_v2.donation_frequency == "monthly" %}

<p class="text-muted nb_donation_v2_interval mb-0" data-placeholder="paid monthly">paid monthly</p>

{% elsif page.donation_v2.donation_frequency == "annual" %}

<p class="text-muted nb_donation_v2_interval mb-0" data-placeholder="paid annually">paid annually</p>

{% else %}

<p class="text-muted nb_donation_v2_interval mb-0" data-placeholder="paid monthly">{% if page.donation_v2.interval_monthly? %}paid monthly{% endif %}</p>

<p class="text-muted nb_donation_v2_interval mb-0" data-placeholder="paid annually">{% if page.donation_v2.interval_annual? %}paid annually{% endif %}</p>

{% endif %}

Remove unused form-submit div

This may not be present in the custom theme, but this removes an unused form-submit div at the bottom of the template around line 424.

<div class="form-submit"></div>

Include new template comments (optional)

The template provided in the upgrade kit includes new comments around elements to delineate sections of the form more clearly. This optional change can be added to the custom theme template to help with maintainability.

Clear the site cache

After all the changes are done, they can be saved and published to the desired donation page or the theme itself. Clear the site cache to ensure that all the latest code changes are picked up when displaying the page.

Staged donation pages

To upgrade staged donation pages, follow these additional steps.

Testing and Troubleshooting

Troubleshooting steps

  • Missing Files: Ensure the two files are uploaded and published to your theme.

  • Cache Issues: Clear your browser cache if you do not see the donation page changes.

  • Payment Field Not Working: Double-check that you've changed 'card' to 'payments' in the payment_field tag.

  • Styling Issues: Verify that the _stripe.scss file has been saved and published correctly.

Testing Recommendations

  • Try out the new donation page improvements

    • Toggle between frequency options on the page’s donation settings and see them display appropriately on the donation page

    • Enable/disable employer field requirements to see the new checkbox

  • Confirm the new payment element renders as expected after selecting a donation amount

  • Test responsive behavior on mobile

  • Validate accessibility with screen readers

  • Verify all form sections render correctly

  • Validate HTML structure after removing and modifying elements with browser inspect

  • Test form submission works as expected

Did this answer your question?