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 PartPreliminary:
Before I begin, please activate the virtual environment and install the required dependencies.
python -m venv venv
venv\Scripts\activate
pip install flask frozen-flaskThen, 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.
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">
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.
(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.
.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>
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.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
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.
.png)








Comments
Post a Comment