Skip to main content

How to build a modern landing page with Flask and Bootstrap? (Continue)


In the previous tutorial, we created the foundation of our modern landing page using Flask-Frozen and Bootstrap 5. We built a responsive navigation bar, an engaging hero section, and an informative About Us section to introduce the pickleball club. In this tutorial, we will continue enhancing the landing page by adding more essential sections that improve the user experience and provide visitors with important information about the club. By the end of this tutorial, the landing page will become more complete and ready for real-world deployment.

Prerequisite:

This tutorial is part of the Flask Landing Page and Reservation System Series.

📚 View the Complete Flask Landing Page and Reservation System Series

⬅ Previous Part

Preliminary:
Before I begin, please activate the virtual environment and install the required dependencies.
python -m venv venv
venv\Scripts\activate
pip install flask frozen-flask
Then, we need to set up the file and folder structure, as below:

Since this tutorial is related to the previous tutorial, every file and folder remains the same, except this file will be amended.
(a) Under the static folder, the style.css, and
(b) Under the templates folder, the index.html. 


Step 1: Connect to Bootstrap
As I have mentioned in the previous tutorial, before I start to code, the HTML template should be connected to Bootstrap's both CSS and JavaScript as follows:
(a) CSS
<link rel="stylesheet"
     href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
(b) JavaScript
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3
/dist/js/bootstrap.bundle.min.js">
</script>
(c) Custom CSS
This will be connected to my static folder and style.css file.
<link rel="stylesheet"
          href="{{ url_for('static', filename='style.css') }}">


Step 2: Create the facilities, contact me and footer section
In the previous tutorial, I created the hero and about me sections, and in this tutorial, I will continue to create the rest of the sections for this landing page. 

(1) Facilities section
This section introduces the facilities provided by the Jersey City Pickleball Club. What I show here is a Bootstrap card with an image for every card display. These images are stored in the static and assets folder; I need to indicate where the app can retrieve the images from. Besides, I have created a row with 4 columns, and each column is equally sized. Since I used Bootstrap, it will facilitate the responsive display on both the desktop and mobile devices, and I don't need to rewrite code for small mobile devices.   


(a) style.css
The style description of each element is shown below:
html,
body {
    overflow-x: hidden;
    width: 100%;
    background: #3f496a;
}

h2 { color: white; 
    font-size: 48px; 
    font-weight: bold; 
    text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
    text-align: center;
    margin: 0;} 

 p {
    text-align: center;
    margin-top: 20px;
}

.facilities-subtitle {
    color: white;
    font-size: 18px;
    text-align: center;
    text-shadow: 1px 1px 3px rgba(0,0,0,0.3);
}

.card-body { 
    background: #f8f9fa; 
    border-radius: 10px; 
    padding: 20px; 
    box-shadow: 0 4px 8px rgba(0,0,0,0.1); 
    color: black;
}

.card-description {
    color: #212529;
    font-size: 14px;
    text-shadow: none;
}

.section-divider {
    height: 2px;
    background: #dee2e6;
    margin: 40px 0;
}
(b) index.html 
  • There is a heading and subheading on the top of this section. 
  • Every card contains an image, and below an image are a title and a brief description of what the club offers to its members. 
  • The image is sourced from the static/assets folder.
<hr class="section-divider">

<!---facilities section--->
<section id="facilities" class="section-padding">   
    <div class="text-center mb-5"> 
        <h2>Our Facilities</h2> 
        <p class="facilities-subtitle">Everything you need for 
        	an exceptional game.</p> 
    </div> 
    
    <div class="row g-4">
        <div class="col-md-3">
            <div class="card h-60 shadow-sm">
                <img
                    src="{{ url_for('static', 
                    	filename='assets/court.png') }}"
                    class="card-img-top facility-img"
                    alt="Professional Courts">

                <div class="card-body text-center">
                    <h5>Professional Courts</h5>
                    <p class="card-description">
                        Tournament-quality courts designed for
                        competitive and recreational play whether 
                        you're a beginner or an experienced player.
                    </p>
                </div>
            </div>
        </div>

  
         <div class="col-md-3">
            <div class="card h-60 shadow-sm">
                <img
                    src="{{ url_for('static', 
                    	filename='assets/night_lighting.png') }}"
                    class="card-img-top facility-img"
                    alt="Night Lighting">

                <div class="card-body text-center">
                    <h5>Night Lighting</h5>
                    <p class="card-description">
                       State-of-the-art LED lighting ensures a bright and 
                       enjoyable playing experience, 
                       allowing members to play comfortably day or night.
                    </p>
                </div>
            </div>
        </div>

        <div class="col-md-3">
            <div class="card h-60 shadow-sm">
                <img
                    src="{{ url_for('static', 
                    	filename='assets/changing_rooms.png') }}"
                    class="card-img-top facility-img"
                    alt="Changing Rooms">

                <div class="card-body text-center">
                    <h5>Changing Rooms</h5>
                    <p class="card-description">
                        Modern changing facilities provide a comfortable space 
                        to prepare for your game and unwind after 
                        an exciting match.
                    </p>
                </div>
            </div>
        </div>

         <div class="col-md-3">
            <div class="card h-60 shadow-sm">
                <img
                    src="{{ url_for('static', 
                    	filename='assets/free_parking.png') }}"
                    class="card-img-top facility-img"
                    alt="Free Parking">

                <div class="card-body text-center">
                    <h5>Free Parking</h5>
                    <p class="card-description">
                        Convenient complimentary and free parking is available 
                        for all members and guests, offering easy access 
                        to our facilities.
                    </p>
                </div>
            </div>
        </div>
    </div>
</section>
<hr class="section-divider">

(2) Contact Me section

This is the final section, where the user can always connect to the club at anytime and anywhere. Since the Python code will be frozen, no more backend logic will be applicable. Therefore, I could not use the normal contact form route function; I used Formspree instead.

   
(i) What is Formspree?
Formspree is a form backend service that allows me to collect data from HTML and JavaScript forms without writing any server-side code.

(ii) How to set up Formspree?
First, I need to sign up for Formspree. Once I'm signed in, at the top left sidebar, click the '+ Add New' button. Then, pop up a create form window; I can amend the form name, project, and email address. However, I am satisfied with the default setting; therefore, I click the red 'Create Form' button located at the bottom right corner.

The endpoint field displays the URL link. I simply copy the provided link and paste it into my index.html, which I will discuss later.

The contact me is divided into 2 sections; on the left are a brief discussion, email, and phone. Meanwhile, the right section is a contact form.

This contact form displays a name, email, message, and a submit button. As I mentioned above, the website does not handle the process of data; instead, it relies on the Formspree backend. What I need is to replace the form URL with the above copy of the form URL and paste it into
action="https://formspree.io/f/your_form_id." 

(a) style.css
.hero p,
#about p,
#contact p,
footer p {
    color: white;
    text-shadow: 1px 1px 3px rgba(0,0,0,0.3);
     font-size: 18px;
}

label { 
    color: white; 
    font-size: 16px; 
    margin-bottom: 5px;
}
(b) index.html
<!---call to action section--->
<section id="contact" class="section-padding">
    <div class="row">
        <div class="col-lg-6">
            <h2>Keep In Touch</h2>
            <p>
                Find us in Jersey City and start your
                health journey with us.
            </p>
            <p>
                Email: info@jerseycitypickleball.com
            </p>
            <p>
                Phone: (123) 456-7890
            </p>
        </div>
  

    <div class="col-lg-6">
        <form action="https://formspree.io/f/your_form_id" method="POST">

            <div class="mb-3">
                <label for="name" class="form-label">
                    Name
                </label>
                <input
                    type="text"
                    class="form-control"
                    id="name"
                    name="Name"
                    required>
            </div>

            <div class="mb-3">
                <label for="email" class="form-label">
                    Email
                </label>
                <input
                    type="email"
                    class="form-control"
                    id="email"
                    name="Email"
                    required>
            </div>

            <div class="mb-3">
                <label for="message" class="form-label">
                    Message
                </label>
                <textarea
                    class="form-control"
                    id="message"
                    name="Message"
                    rows="5"
                    required></textarea>
            </div>

            <button type="submit"
                    class="btn btn-primary">
                Send Message
            </button>

        </form>
    </div>
</section>
<hr class="section-divider">
(3) Footer 
Reinforcement of the landing page by displaying the club name, slogan, email, and copyright so the user remembers the club details.


(a) style.css
.footer {
    background: #212529;
    color: white;
}

#jc {
    font-weight: bold;
    color: #ffc107; /* Pickleball yellow */
}
(b) index.html
<!--footer section--->
<footer class="footer py-4">
    <div class="container">

        <div class="row">

            <div class="col-md-6 text-center text-md-start">
                <p class="mb-0" id="jc">Jersey City Pickleball Club</p>
                <p class="mb-0">
                    Play. Compete. Connect.
                </p>
            </div>

            <div class="col-md-6 text-center text-md-end">
                <p class="mb-0">
                    Email: info@jerseycitypickleball.com
                </p>
                <p class="mb-0">
                    © 2026 All Rights Reserved
                </p>
            </div>

        </div>

    </div>
</footer>


Step 3: Freeze the Flask app.
An ordinary Flask app is dynamic; only if it is frozen into a static app can it be deployed to static hosting such as GitHub Pages, Netlify, or Cloudflare Pages.

To freeze it, my code is as follows:
from app import app
from flask_frozen import Freezer

app.config['FREEZER_RELATIVE_URLS'] = True

# Output directory
app.config['FREEZER_DESTINATION'] = 'build'  

 # Use relative paths for links and assets
app.config['FREEZER_BASE_URL'] = './' 

# Preserve static folder structure
app.config['FREEZER_STATIC_IGNORE'] = ['*.scss', '*.less']

freezer = Freezer(app)

if __name__ == '__main__':
    freezer.freeze()
    
    # Optional: Copy any additional static files
    print("Freezing complete! Check the 'build' folder")
Then, I need to execute the following command:
# direct to my directory
cd JCPC 

# Freeze the flask app
python freeze.py
My terminal will show "Freezing complete! Check the 'build' folder, and in my files and folders, a new build folder will appear.

                           
This build folder contains an "index.html" and a static folder, and under the static folder are an assets folder and "style.css". All images are stored inside the assets folder.
Now, I am ready to deploy to the cloud!


Step 4: Test-run the contact form submission
Let me test out the contact form submission. From the build folder, I had double-clicked the index.html, and then the landing page was successfully launched.

Next, I filled out the form details, including the name, email, and text message, then clicked the send message button, and it redirected to the thanks page. Finally, I go back to the Formspree website. 

After I sign in, I click the sidebar 'A New Form' and select the 'Submission' tab. All the submitted information was stored here.



Final Wrap-up:

In this tutorial, we completed the pickleball club landing page by adding the Facilities, Contact Me, and Footer sections. These additions make the website more informative, professional, and user-friendly. We also demonstrated how to integrate a Formspree contact form and perform a live submission test. The submitted messages can be viewed directly from the Formspree portal or processed by your web application. With these sections in place, the landing page is now ready to serve as a complete front-end foundation for future Flask-powered features.

Published: Jun 2026
Last Updated: Jun 2026

About the Author 

Kelvin Loh is a Python developer focused on Flask, desktop applications, and business automation solutions. He shares practical tutorials and real-world coding projects to help developers and small businesses build useful applications.

Comments

Popular Posts

How to Design a Location Tracking Module for Desktop Business Systems Using Python?

This tutorial will show you how to create an interactive map application that allows users to input geographic coordinates and visualize locations on an interactive map using Python's Tkinter GUI framework enhanced with TTKBootstrap styling and the TkinterMapView widget.  Prerequisite: This tutorial is part of the standalone tutorial. 📚 View the  standalone tutorial Preliminary   Before I begin, it is recommended to activate the virtual environment before installing the relevant dependencies. python -m venv venv venv\Scripts\activate pip install ttkbootstrap tkintermapview As usual, we need to import the relevant module and set the root values from tkinter import * import ttkbootstrap as tb import tkintermapview from ttkbootstrap.dialogs import Messagebox root = tb.Window(themename='darkly') root.title('Find My Map') root.geometry("1200x1200") I would like to divide it into three sections.  a) Input Panel Layout,  The headers are mainly a label...

How to Create Flask Forms with CKEditor and Flask-WTF?

In this tutorial, we will be integrating Flask-WTF and Flask-CKEditor into your Flask application! You'll learn how to set up the editor, securely handle formatted HTML content, and create a seamless user experience that enhances any project that requires user-generated content. Let's get started! Prerequisite: This tutorial is part of the Flask CKEditor Project Series. 📚 View the Complete Flask CKEditor Series                                                                                                                                                  ➡ Next Part Preliminary Before I begin, it is recommended to activate the v...

How to Store Application Data with SQLite in Python?

  In the previous tutorial, I used TinyDB as our storage solution to keep application data in a simple JSON-based format. While TinyDB is lightweight and easy to use, many web applications require a more structured and scalable database system. In this tutorial, we will switch to SQLite , a powerful relational database that integrates well with Python and web frameworks. By using SQLite, you will learn how to store, manage, and query data in a more structured way for real-world applications. Prerequisite: This tutorial is part of the Flask CKEditor Project Series. 📚 View the Complete Flask CKEditor Series ⬅ Previous Part                                                                                                         ...