Skip to main content

How to build a Modern Landing Page with Flask and Bootstrap? (Part 1)

How to create Frozen-Flask Landing Page And Flask Reservation System (Part 1)

In this tutorial, we will create a modern and responsive landing page for a pickleball club using Frozen-Flask and Bootstrap 5, featuring a navigation bar, hero banner, about section, facilities showcase, contact form integration, and footer. This approach combines the simplicity of Flask development with the performance and cost benefits of static site hosting, making it an excellent choice for clubs, portfolios, small businesses, and promotional websites.

Prerequisite:

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

📚 View the Complete Flask Landing Page and Reservation System Series

Continue to Part 2

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:
Files and folders
I need to store all the images under the static and assets folders, including 
  • about.png, 
  • changing_rooms.png, 
  • court.png, 
  • free_parking.png, 
  • hero.png, 
  • logo.png, and 
  • night_lighting.png
While app.py and freeze.py are my working files.

Why use Frozen-Flask to build a landing page?
Flask is traditionally used for dynamic websites; it communicates between the browser and the server using the Jinja2 template engine. However, frozen-flask is a tool to "freeze" a dynamic app into a static app, simply a few lines of code. 

The benefits are shown below: 
1) Minimal Hosting Cost—the static file can be hosted on any static site cloud with no cost, such as a GitHub page, Netlify, or an Amazon S3 bucket. 
2) No database required—the static file does not need to maintain a database to store the user data; therefore, no security issues, including hackers or ransomware attacks. 
3) Performance and Scalability—static files load faster and are capable of handling heavy traffic of visitors. 
4) Smart URL link—thanks to Jinja2, it helps to map to every page. Even though the HTML is "frozen" to a static file, every link remains intact. 

Therefore, in this tutorial, I will create a normal Flask website and later on "freeze" it into a static site.

Why use Bootstrap 5?
As you have noticed, my website is not only viewed on a desktop or laptop! This is a result of using Bootstrap 5 for a responsive view on any device, including desktop, tablet, or mobile devices. Once I use Bootstrap 5, I need not code for small-screen devices, and the code will take care of it. Simply copy these 2 lines of code below:
CSS:
<link rel="stylesheet"
     href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
JS:
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3
/dist/js/bootstrap.bundle.min.js">
</script>
Custom CSS
This will be connected to my static folder and style.css file.
<link rel="stylesheet"
          href="{{ url_for('static', filename='style.css') }}">

The benefits of using Bootstrap 5 are as follows:
  • Faster Loading Times: Bootstrap 5 dropped a heavy code helper called jQuery. This makes the framework lighter and faster. Think of it like taking heavy weights off a race car so it can drive faster. 
  • Responsive by Default: Websites will automatically adjust to look great on phones, tablets, and desktop computers. 
  • Pre-built Components: You get ready-to-use pieces like buttons, forms, and navigation bars. Instead of building a button from scratch, you just use a pre-made one. It is like using Lego blocks to build a castle instead of carving it out of wood. 
  • Built-in Dark Mode: It allows developers to easily add a dark theme to websites. This is helpful for users who want to save battery on their phones or reduce eye strain at night. 
  • Customisation: It is easy to change colours, fonts, and sizes to match your own style. 

What is the difference between custom CSS and Bootstrap CSS?
  • Custom CSS: I am the author of every rule. I write the specific code to style your elements (e.g., background-color: blue;). This gives you complete control and allows me a unique, brand-specific design, but it requires significantly more time to build from scratch.

  • Bootstrap CSS: This is a pre-written, open-source framework. It provides a library of ready-to-use components (buttons, navbars, forms) and a mobile-first grid system. It is designed for speed and consistency, allowing you to build responsive layouts without reinventing the wheel.

How to avoid both CSS overlaps?

It is common to use both, but you should do so carefully to keep your code maintainable:

  1. Don't Modify Bootstrap Directly: Avoid editing the bootstrap.css file itself. If you ever update the Bootstrap version, your changes will be overwritten.

  2. Use a Separate Custom File: Always keep your overrides in a separate file (e.g., custom.css or style.css). In my tutorial, I used styles.css.

  3. Correct Loading Order: Ensure your custom stylesheet is linked in your HTML after the Bootstrap stylesheet so that the browser applies my custom styles as the final authority.

  4. Leverage Utility Classes: Bootstrap 5 includes many "utility" classes (for spacing, colours, borders, etc.) that can often prevent me from needing to write custom CSS at all. Try to use these first to keep your project lightweight.


Step 1: Create a normal Flask application
First and foremost, create a folder and a basic Flask web app as usual. So, in my bash terminal, I input the following line:
mkdir JCPC
cd JCPC
touch app.py
Next, in my app.py file, I will create a few lines of code as below:
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)
Since the landing page is a single page, I only need a route.

Step 2: Create a template, a static folder
Before creating files, I need to set up the folders and files as follows in the bash terminal:
mkdir templates
cd templates
touch index.html
cd ..
mkdir static
cd static touch style.css mkdir assets
cd..
The folder structure is as shown in the preliminary section above. Once the assets folder has been created, go ahead and copy the images to the assets folder.

Step 3: Configure the index.html and style.css files for every section
In my landing page, I have divided it into the following 5 sections:
  1. Navigation and hero section
  2. About section
  3. Facilities section
  4. Contact form
  5. Footer section
(1) Navigation bar and hero section 
The top navigation displays the logo and title of the website. While the Hero section is the most amazing part, where users are attracted to surfing further. This part included an image, a heading, a subheading, and a call-to-action button.
Navigation title and hero section

So, my code is in 2 files, one in style.css (custom stylesheet) under the static folder and another in index.html under the templates folder, as follows:

(a) style.css
The background image is sourced from the 'static/assets' folder.
html,
body {
    overflow-x: hidden;
    width: 100%;
    background: #3f496a;
}

.h3  {
    color: #ffc107;
}

.hero {
    min-height: 500px;
    height: 70vh;

    background:
        linear-gradient(
            rgba(0,0,0,.5),
            rgba(0,0,0,.5)
        ),
        url("../static/assets/hero.png");

    background-size: cover;
    background-position: center center;
}

.hero-overlay {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.hero-overlay {
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.lead {
    color: white;
    font-size: 25px;
    font-weight: bold;
}

.section-divider {
    height: 2px;
    background: #dee2e6;
    margin: 40px 0;
}
(b) index.html
On top of the section containing the stylesheet, besides linking to Bootstrap 5, I also code a custom CSS under the 'static/style.css' file.

The button is currently left blank. When the reservation web app is ready, it will be linked to this button later on.
<link rel="stylesheet"
     href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet"
     href="{{ url_for('static', filename='style.css') }}">


<!-- Navigation -->
<nav class="navbar navbar-dark bg-dark">
  <div class="container-fluid">
    <a class="navbar-brand d-flex align-items-center" href="#">
      <img src="static/assets/logo.png" alt="JCPC Logo" 
      width="30" height="30" class="d-inline-block align-text-top me-2">
      <span class="h3 mb-0">Jersey City Pickleball Club</span>
    </a>
  </div>
</nav>

<!---hero section--->
<section class="hero">
    <div class="hero-overlay">
        <div class="container text-center text-white">
            <h1 class="display-3 fw-bold">
                Jersey City Pickleball Club
            </h1>

            <p class="lead">
                Play. Compete. Connect.
            </p>

            <a href="/#" class="btn btn-warning btn-lg">
                Book a Court
            </a>
        </div>
    </div>
</section
<hr class="section-divider">

-----------------------------------------------------------------------------------------------------------------------------

🎁 Get Your FREE Flask Cheat Sheet

Get Your FREE Flask Cheat Sheet

Join 500+ Python learners and receive this printable Flask Cheat Sheet instantly. Plus, get new Flask tutorials delivered to your inbox.

✓ Practical coding tutorials
✓ Automation tips for SMEs
✓ New project ideas and templates

Download my FREE Flask Cheat Sheet (PDF)

-----------------------------------------------------------------------------------------------------------------------------

Understanding the Navigation Bar

The navigation bar is usually the first element visitors see when they open a website. It provides branding and helps users identify the website immediately.

(i) Navbar

Bootstrap classes used here are:

  • navbar Initializes Bootstrap's navigation bar component and applies the necessary spacing and layout.
  • navbar-dark adjusts the text and icon colours for use on dark backgrounds, ensuring good readability.
  • bg-dark applies Bootstrap's built-in dark background colour.
(ii) Container-fluid

The container-fluid class creates a full-width container that stretches across the entire browser window.

Unlike the standard container class, which has fixed maximum widths at different screen sizes, it container-fluid allows the navigation bar to span the full width of the page. This is useful for modern landing pages where the header extends from edge to edge.

(iii) img

The <img> element displays the club's logo.

The attributes specify:

  • src – the location of the image file.
  • alt – alternative text used by screen readers and displayed if the image cannot be loaded.
  • width and height – set the displayed dimensions of the image.
(iv) d-inline-block

These Bootstrap utility classes control the image alignment and spacing.

  • d-inline-block allows the image to appear inline with the text while maintaining block-like properties.
  • align-text-top aligns the image with the top of the surrounding text.
  • me-2 adds horizontal spacing (margin-end) between the logo and the club name, preventing them from touching.
(v) span class
The mb-0 class removes the default bottom margin, helping keep the navigation bar compact.

When combined, these elements produce a responsive navigation bar that:

  • Displays the club logo.
  • Shows the website name beside the logo.
  • Uses Flexbox for proper alignment.
  • Applies Bootstrap's dark theme.
  • Spans the full width of the browser.
  • Maintains consistent spacing and typography. 

Creating the Hero Section

The hero section is the most prominent area of a landing page. It is typically the first section visitors see and is designed to communicate the website's purpose while encouraging users to take an action.

(i) Overlay

The overlay sits above the background image but beneath the text.

Its purpose is to improve text readability.

Without an overlay, white text can become difficult to read when displayed on bright or detailed background images.

(ii) Centring the Content

Bootstrap provides several utility classes that simplify page layout.

  • container keeps the content centered and limits the maximum width on larger screens.
  • text-center horizontally centers all text inside the container.
  • text-white changes the text color to white so it contrasts well against the darker overlay.

Using Bootstrap utilities reduces the amount of custom CSS required.

(iii) Displaying the Main Heading

Bootstrap classes enhance the appearance:

  • display-3 creates a large, attention-grabbing heading suitable for hero sections.
  • fw-bold applies a bold font weight, making the heading more visually prominent.

Because this is the first thing visitors read, it should clearly communicate what the website offers.

(iv) Adding a Supporting Tagline

The lead class increases the font size and improves readability, making it suitable for introductory text.

Instead of overwhelming visitors with long descriptions, a short and memorable slogan communicates the club's purpose quickly.

(v) Creating a Call-to-Action Button

Bootstrap provides several utility classes:

  • btn styles the anchor as a button.
  • btn-warning applies Bootstrap's warning colour, helping the button stand out from the darker background.
  • btn-lg creates a larger button that is easier to notice and click.
(vi) Separating Sections

The <hr> element creates a horizontal rule that visually separates the hero section from the content that follows.

Applying a custom section-divider class allows you to control:

  • Thickness
  • Width
  • Color
  • Margins
  • Overall spacing

This helps create a cleaner transition between different sections of the landing page.


(2) About section: 
This is an introduction to who we are and what we do, so in this tutorial, I wrote a simple introduction to what the Jersey City Pickleball Club offers to the community. 
About section

 (a) style.css
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;
}

.section-divider {
    height: 2px;
    background: #dee2e6;
    margin: 40px 0;
}
(b) index.html
The image is sourced from the static/assets folder.
<!---about section--->
<section id="about" class="section-padding">
    <div class="row align-items-center"> 
        <div class="col-lg-6"> 
            <img src="{{ url_for('static', filename='assets/about.png') }}" 
            class="img-fluid rounded shadow" alt="About Image"> 
        </div> 

        <div class="col-lg-6"> 
            <h2>About Us</h2> 
            <p> 
                Jersey City Pickleball Club is a vibrant community where players of 
                all skill levels come together to enjoy pickleball. 
            </p> 
            <p> 
                Whether you're a beginner or an experienced competitor, you'll find 
                a welcoming place to play, improve, and connect with others. 
            </p> 
        </div> 
    </div>
</section>
<hr class="section-divider">

Creating the About Section

The About section introduces the organisation and gives visitors a brief overview of its purpose. Unlike the Hero section, which is designed to capture attention, the About section builds trust by providing background information about the club.

(i) Creating a Bootstrap Row

Bootstrap uses a 12-column grid system to build responsive layouts.

The row class creates a horizontal container for columns.

The align-items-center class uses Flexbox to vertically centre all columns within the row. This ensures that the image and text remain aligned even when their heights differ.

Without this class, the text would align to the top of the image instead of appearing centred beside it.

(ii) Creating the Left Column

The col-lg-6 class tells Bootstrap to allocate half of the available width to this column on large screens.

Since Bootstrap uses a 12-column grid:

  • 6 + 6 = 12

The image occupies half of the row while the text occupies the other half.

On smaller devices such as tablets and smartphones, Bootstrap automatically stacks the columns vertically, making the layout responsive without requiring additional CSS.

(iii) Displaying the Image

Bootstrap utility classes enhance the appearance:

  • img-fluid makes the image responsive by allowing it to scale with its parent container.
  • rounded adds rounded corners for a softer, more modern design.
  • shadow applies a subtle drop shadow that helps the image stand out from the background.

The alt attribute provides alternative text for screen readers and is displayed if the image cannot be loaded. Including descriptive alt text also improves accessibility.

Final Wrap-up: 
In this tutorial, we explored why Frozen-Flask is an excellent choice for building fast, lightweight, and easy-to-deploy static websites while still benefiting from Flask's familiar development workflow. We also leveraged Bootstrap 5 to create a responsive and professional-looking user interface and demonstrated how to build two essential landing page components: a navigation bar and a hero section, followed by a content section. These foundational elements provide the structure and styling needed for a modern landing page. In the next part of this project, we will continue enhancing the website by adding a facilities showcase, a contact section, and a footer to complete the landing page experience. 

Published: June 2026
Last Updated: June 2026

---------------------------------------------------------------------------------------------------------------------------------------------------

Thanks for reading! 

If you haven't subscribed yet, join my newsletter to receive future Python and Flask tutorials.

---------------------------------------------------------------------------------------------------------------------------------------------------

Need a similar system for your business? 
I build custom Flask web applications and Python automation solutions for SMEs and solopreneurs. 


-----------------------------------------------------------------------------------------------------------------------------

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 Build an Audiobook Workflow Desktop System with Python?

In this tutorial, we will build a simple audiobook player using Python and CustomTkinter. You will learn how to convert text into speech using gTTS and play it with PyGame. We will also implement play, pause, and stop controls, like those in a real audio player. By the end, you will have a clean and functional desktop audiobook app. 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 customtkinter pillow gTTS pygame CTkMessagebox pypdf Then, the following steps include setting up the file structure, app.py, and two additional folders: the uploads and media folders. The media folder contains the icons necessary to build the app; there are read, pause, and stop icons, as shown on the diagram. Step 1: Build up the app interface I have 4 sections here: the...

How to Set Up PgAdmin and Adminer Using Docker Compose?

If you are new to database management with Docker, this tutorial will guide you through setting up both PgAdmin and Adminer using Docker containers. By containerising these tools, you can quickly launch lightweight and portable database management environments without installing them directly on your operating system. This approach also makes it easier to manage configurations, updates, and multiple projects across different devices.