All Bootstrap Exercises

Create a responsive three column layout with Bootstrap

Description: Create three columns. On small screens they must be stacked (full width), and from the medium breakpoint they must be side by side.

Hint:

  • Use container, row and col-12 col-md-4.

Solution

<div class="container text-center">
  <div class="row g-2">
    <div class="col-12 col-md-4"><div class="p-3 border bg-light">Column 1</div></div>
    <div class="col-12 col-md-4"><div class="p-3 border bg-light">Column 2</div></div>
    <div class="col-12 col-md-4"><div class="p-3 border bg-light">Column 3</div></div>
  </div>
</div>
Live preview

How it works

  • col-12 gives the full width on small screens, and col-md-4 gives 4 of 12 columns from 768px.
  • g-2 adds a small gutter between the columns.

Read Grid System.

Create a centered page heading and text with Bootstrap

Description: Make a centered heading “Welcome” with a lead paragraph under it, inside a container, with some space above and below.

Hint:

  • Use container, text-center, display-*, lead and my-5.

Solution

<div class="container text-center my-5">
  <h1 class="display-4">Welcome</h1>
  <p class="lead">This is a short introduction text under the heading.</p>
</div>
Live preview

How it works

  • my-5 adds margin on top and bottom. text-center centers the text.

Read Typography and Spacing.

Create a responsive navbar with a brand and three links

Description: Build a navbar with the brand “MySite” and three links. It must collapse into a toggle button on small screens.

Hint:

  • Use navbar navbar-expand-md, navbar-toggler and collapse navbar-collapse.

Solution

<nav class="navbar navbar-expand-md bg-body-tertiary">
  <div class="container-fluid">
    <a class="navbar-brand" href="#">MySite</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#menu" aria-controls="menu" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="menu">
      <ul class="navbar-nav">
        <li class="nav-item"><a class="nav-link active" href="#">Home</a></li>
        <li class="nav-item"><a class="nav-link" href="#">About</a></li>
        <li class="nav-item"><a class="nav-link" href="#">Contact</a></li>
      </ul>
    </div>
  </div>
</nav>
Live preview

document.querySelector('.navbar-toggler').click();
await new Promise(r => setTimeout(r, 600));
console.log('menu opens on a small screen: ' + document.getElementById('menu').classList.contains('show'));
console.log('links in the menu: ' + document.querySelectorAll('#menu .nav-link').length);
Console output (real Chrome)

menu opens on a small screen: true
links in the menu: 3

How it works

  • The links are hidden in the collapse part, and the toggler button opens them.
  • From the md breakpoint (768px) the menu is shown in a row.

Read Navbar.

Create a card with an image, a title, a text and a button

Description: Create a card 18rem wide with an image on top, a title, a text and a button.

Hint:

  • Use card, card-img-top, card-body, card-title and card-text.

Solution

<div class="card" style="width: 18rem;">
  <img src="images/photo.jpg" class="card-img-top" alt="A sample image">
  <div class="card-body">
    <h5 class="card-title">Learn Bootstrap</h5>
    <p class="card-text">A short text about the course, with some words.</p>
    <a href="#" class="btn btn-primary">Start now</a>
  </div>
</div>
Live preview

How it works

  • The image uses card-img-top, so it fits the top of the card with rounded corners.
  • The text and the button are inside card-body, which gives the padding.

Read Cards.

Create a responsive grid of six cards

Description: Show six cards: 1 per row on small screens, 2 per row from sm and 3 per row from lg.

Hint:

  • Use row row-cols-1 row-cols-sm-2 row-cols-lg-3 g-3 and card h-100.

Solution

<div class="container">
  <div class="row row-cols-1 row-cols-sm-2 row-cols-lg-3 g-3">
    <div class="col"><div class="card h-100"><div class="card-body"><h5 class="card-title">Card 1</h5><p class="card-text">Text</p></div></div></div>
    <div class="col"><div class="card h-100"><div class="card-body"><h5 class="card-title">Card 2</h5><p class="card-text">Text</p></div></div></div>
    <div class="col"><div class="card h-100"><div class="card-body"><h5 class="card-title">Card 3</h5><p class="card-text">Text</p></div></div></div>
    <div class="col"><div class="card h-100"><div class="card-body"><h5 class="card-title">Card 4</h5><p class="card-text">Text</p></div></div></div>
    <div class="col"><div class="card h-100"><div class="card-body"><h5 class="card-title">Card 5</h5><p class="card-text">Text</p></div></div></div>
    <div class="col"><div class="card h-100"><div class="card-body"><h5 class="card-title">Card 6</h5><p class="card-text">Text</p></div></div></div>
  </div>
</div>
Live preview

How it works

  • row-cols-* sets the number of columns per row, so each column needs only the class col.
  • h-100 makes the cards in a row the same height.

Read Grid: Gutters, Offset and Order and Cards.

Create a login form with Bootstrap

Description: Create a login form with an email, a password, a “Remember me” checkbox and a full width Login button, centered in a card.

Hint:

  • Use form-label, form-control, form-check and d-grid.

Solution

<div class="container">
  <div class="row justify-content-center">
    <div class="col-12 col-sm-10 col-md-8">
      <div class="card shadow-sm">
        <div class="card-body p-4">
          <h2 class="h4 mb-3 text-center">Login</h2>
          <form>
            <div class="mb-3">
              <label for="lemail" class="form-label">Email address</label>
              <input type="email" class="form-control" id="lemail" placeholder="name@example.com">
            </div>
            <div class="mb-3">
              <label for="lpass" class="form-label">Password</label>
              <input type="password" class="form-control" id="lpass">
            </div>
            <div class="form-check mb-3">
              <input class="form-check-input" type="checkbox" id="lremember">
              <label class="form-check-label" for="lremember">Remember me</label>
            </div>
            <div class="d-grid"><button type="submit" class="btn btn-primary">Login</button></div>
          </form>
        </div>
      </div>
    </div>
  </div>
</div>
Live preview

How it works

  • justify-content-center on the row centers the column.
  • d-grid makes the button full width.

Read Forms.

Create a registration form with two columns

Description: Make a registration form. First name and last name must be in one row, email and city in another row, and the address field must be full width.

Hint:

  • Use the grid inside the form: row g-3 and col-md-6.

Solution

<form class="row g-3">
  <div class="col-md-6">
    <label for="rfn" class="form-label">First name</label>
    <input type="text" class="form-control" id="rfn">
  </div>
  <div class="col-md-6">
    <label for="rln" class="form-label">Last name</label>
    <input type="text" class="form-control" id="rln">
  </div>
  <div class="col-md-8">
    <label for="rem" class="form-label">Email</label>
    <input type="email" class="form-control" id="rem">
  </div>
  <div class="col-md-4">
    <label for="rcity" class="form-label">City</label>
    <select id="rcity" class="form-select"><option selected>Choose...</option><option>Delhi</option><option>Pune</option></select>
  </div>
  <div class="col-12">
    <label for="radd" class="form-label">Address</label>
    <input type="text" class="form-control" id="radd" placeholder="Street and number">
  </div>
  <div class="col-12"><button type="submit" class="btn btn-primary">Register</button></div>
</form>
Live preview

How it works

  • row g-3 on the form creates the grid with a gutter. Each field is a column.
  • col-md-6 puts two fields side by side from 768px, and they are stacked on a phone.

Read Forms.

Validate a required field with Bootstrap

Description: Create a form with a required name field. When the user submits it empty, the field must be red with the message “Please write your name”.

Hint:

  • Use novalidate, .needs-validation, .invalid-feedback, checkValidity() and the class was-validated.

Solution

<form class="needs-validation" novalidate id="f">
  <label for="vn" class="form-label">Name</label>
  <input type="text" class="form-control" id="vn" required>
  <div class="invalid-feedback">Please write your name.</div>
  <button class="btn btn-primary mt-3" type="submit">Submit</button>
</form>
<script>
  document.getElementById('f').addEventListener('submit', function (e) {
    if (!this.checkValidity()) { e.preventDefault(); e.stopPropagation(); }
    this.classList.add('was-validated');
  });
</script>
Live preview

const f = document.getElementById('f');
f.requestSubmit();
await new Promise(r => setTimeout(r, 200));
console.log('was-validated class: ' + f.classList.contains('was-validated'));
console.log('field is invalid: ' + !document.getElementById('vn').checkValidity());
console.log('message color: ' + getComputedStyle(document.querySelector('.invalid-feedback')).color + ', display: ' + getComputedStyle(document.querySelector('.invalid-feedback')).display);
Console output (real Chrome)

was-validated class: true
field is invalid: true
message color: rgb(220, 53, 69), display: block

How it works

  • The browser checks the required attribute with checkValidity().
  • After was-validated is added, Bootstrap shows the red border and the .invalid-feedback text.

Read Form Validation.

Create a striped, hover and responsive table

Description: Make a table of three students with striped rows and a hover effect, which scrolls sideways on small screens.

Hint:

  • Use table table-striped table-hover and wrap it in table-responsive.

Solution

<div class="table-responsive">
  <table class="table table-striped table-hover">
    <thead class="table-dark"><tr><th>#</th><th>Name</th><th>City</th><th>Marks</th></tr></thead>
    <tbody>
      <tr><td>1</td><td>Anita</td><td>Delhi</td><td>88</td></tr>
      <tr><td>2</td><td>Ravi</td><td>Pune</td><td>92</td></tr>
      <tr><td>3</td><td>Maya</td><td>Kolkata</td><td>79</td></tr>
    </tbody>
  </table>
</div>
Live preview

How it works

  • table-striped colors every second row, table-hover highlights the row under the mouse.
  • table-responsive adds a horizontal scroll when the table is wider than the screen.

Read Tables.

Create an alert which the user can close

Description: Show a warning alert with a close button. When the user clicks the button, the alert must disappear.

Hint:

  • Use alert-dismissible fade show, btn-close and data-bs-dismiss="alert".

Solution

<div class="alert alert-warning alert-dismissible fade show" role="alert" id="al">
  <strong>Careful!</strong> Your session will end in 5 minutes.
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
Live preview

console.log('alert before: ' + document.querySelectorAll('#al').length);
document.querySelector('#al .btn-close').click();
await new Promise(r => setTimeout(r, 600));
console.log('alert after the click: ' + document.querySelectorAll('#al').length);
Console output (real Chrome)

alert before: 1
alert after the click: 0

How it works

  • data-bs-dismiss="alert" tells Bootstrap to close the alert, and fade show gives the smooth effect.

Read Alerts.

Open a modal with a button

Description: Add a button “Open”. The click must open a modal with a title, a text and a Close button.

Hint:

  • Use data-bs-toggle="modal", data-bs-target and data-bs-dismiss="modal".

Solution

<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#ex1">Open</button>
<div class="modal fade" id="ex1" tabindex="-1" aria-labelledby="ex1Label" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <div class="modal-header">
        <h1 class="modal-title fs-5" id="ex1Label">Hello</h1>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">This is a modal.</div>
      <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button></div>
    </div>
  </div>
</div>
Live preview

const m = document.getElementById('ex1');
document.querySelector('[data-bs-toggle="modal"]').click();
await new Promise(r => setTimeout(r, 700));
console.log('open: ' + m.classList.contains('show'));
m.querySelector('.modal-footer .btn').click();
await new Promise(r => setTimeout(r, 700));
console.log('open after Close: ' + m.classList.contains('show'));
Console output (real Chrome)

open: true
open after Close: false

How it works

  • The button opens the element with the id from data-bs-target.
  • modal-dialog-centered puts the box in the vertical center.

Read Modal.

Create an accordion with three items

Description: Make an accordion with three items. The first is open at the start, and opening another one must close the first.

Hint:

  • Use accordion, accordion-item, accordion-button and data-bs-parent.

Solution

<div class="accordion" id="acc">
  <div class="accordion-item">
    <h2 class="accordion-header"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#a1" aria-expanded="true" aria-controls="a1">Item 1</button></h2>
    <div id="a1" class="accordion-collapse collapse show" data-bs-parent="#acc"><div class="accordion-body">Text of item 1</div></div>
  </div>
  <div class="accordion-item">
    <h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#a2" aria-expanded="false" aria-controls="a2">Item 2</button></h2>
    <div id="a2" class="accordion-collapse collapse" data-bs-parent="#acc"><div class="accordion-body">Text of item 2</div></div>
  </div>
  <div class="accordion-item">
    <h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#a3" aria-expanded="false" aria-controls="a3">Item 3</button></h2>
    <div id="a3" class="accordion-collapse collapse" data-bs-parent="#acc"><div class="accordion-body">Text of item 3</div></div>
  </div>
</div>
Live preview

document.querySelector('[data-bs-target="#a3"]').click();
await new Promise(r => setTimeout(r, 800));
['a1', 'a2', 'a3'].forEach(id => console.log(id + ' open = ' + document.getElementById(id).classList.contains('show')));
Console output (real Chrome)

a1 open = false
a2 open = false
a3 open = true

How it works

  • data-bs-parent="#acc" connects the items, so Bootstrap closes the others.
  • After the click on item 3, only item 3 is open.

Read Collapse and Accordion.

Create tabs with three panels

Description: Create three tabs. Clicking a tab must show only its own panel.

Hint:

  • Use nav nav-tabs, data-bs-toggle="tab" and tab-content with tab-pane.

Solution

<ul class="nav nav-tabs" role="tablist">
  <li class="nav-item" role="presentation"><button class="nav-link active" id="t1" data-bs-toggle="tab" data-bs-target="#p1" type="button" role="tab" aria-controls="p1" aria-selected="true">Home</button></li>
  <li class="nav-item" role="presentation"><button class="nav-link" id="t2" data-bs-toggle="tab" data-bs-target="#p2" type="button" role="tab" aria-controls="p2" aria-selected="false">Profile</button></li>
  <li class="nav-item" role="presentation"><button class="nav-link" id="t3" data-bs-toggle="tab" data-bs-target="#p3" type="button" role="tab" aria-controls="p3" aria-selected="false">Contact</button></li>
</ul>
<div class="tab-content border border-top-0 p-3">
  <div class="tab-pane fade show active" id="p1" role="tabpanel" tabindex="0">Home panel</div>
  <div class="tab-pane fade" id="p2" role="tabpanel" tabindex="0">Profile panel</div>
  <div class="tab-pane fade" id="p3" role="tabpanel" tabindex="0">Contact panel</div>
</div>
Live preview

document.getElementById('t3').click();
await new Promise(r => setTimeout(r, 500));
console.log('active panel: ' + document.querySelector('.tab-pane.active').textContent);
console.log('active tab: ' + document.querySelector('.nav-link.active').textContent);
Console output (real Chrome)

active panel: Contact panel
active tab: Contact

How it works

  • Each tab button points to a panel with data-bs-target. Bootstrap switches the classes active and show.

Read Navs and Tabs.

Create a carousel with three slides

Description: Make a carousel with three slides, previous and next buttons and three indicators.

Hint:

  • Use carousel slide, carousel-inner, carousel-item active and the control buttons with data-bs-slide.

Solution

<div id="cx" class="carousel slide" style="max-width: 420px">
  <div class="carousel-indicators">
    <button type="button" data-bs-target="#cx" data-bs-slide-to="0" class="active" aria-label="Slide 1"></button>
    <button type="button" data-bs-target="#cx" data-bs-slide-to="1" aria-label="Slide 2"></button>
    <button type="button" data-bs-target="#cx" data-bs-slide-to="2" aria-label="Slide 3"></button>
  </div>
  <div class="carousel-inner">
    <div class="carousel-item active"><img src="images/photo.jpg" class="d-block w-100" alt="Slide 1"></div>
    <div class="carousel-item"><img src="images/photo.jpg" class="d-block w-100" alt="Slide 2"></div>
    <div class="carousel-item"><img src="images/photo.jpg" class="d-block w-100" alt="Slide 3"></div>
  </div>
  <button class="carousel-control-prev" type="button" data-bs-target="#cx" data-bs-slide="prev"><span class="carousel-control-prev-icon" aria-hidden="true"></span><span class="visually-hidden">Previous</span></button>
  <button class="carousel-control-next" type="button" data-bs-target="#cx" data-bs-slide="next"><span class="carousel-control-next-icon" aria-hidden="true"></span><span class="visually-hidden">Next</span></button>
</div>
Live preview

const cx = document.getElementById('cx');
const idx = () => [...cx.querySelectorAll('.carousel-item')].findIndex(i => i.classList.contains('active')) + 1;
cx.querySelector('.carousel-control-next').click();
await new Promise(r => setTimeout(r, 900));
console.log('after Next: slide ' + idx());
cx.querySelector('.carousel-control-next').click();
await new Promise(r => setTimeout(r, 900));
console.log('after Next again: slide ' + idx());
cx.querySelector('.carousel-control-next').click();
await new Promise(r => setTimeout(r, 900));
console.log('after Next at the end: slide ' + idx() + ' (it goes round)');
Console output (real Chrome)

after Next: slide 2
after Next again: slide 3
after Next at the end: slide 1 (it goes round)

How it works

  • Exactly one carousel-item must have the class active at the start.
  • After the last slide, the carousel goes back to the first one.

Read Carousel.

Add a tooltip to a button

Description: Add a tooltip “Save the file” to a button, which opens when the user hovers on it.

Hint:

  • Use data-bs-toggle="tooltip", data-bs-title and start it with new bootstrap.Tooltip().

Solution

<button type="button" class="btn btn-success" id="savebtn" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Save the file">Save</button>
<script>
  document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function (el) { new bootstrap.Tooltip(el); });
</script>
Live preview

const b = document.getElementById('savebtn');
b.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
await new Promise(r => setTimeout(r, 400));
const t = document.querySelector('.tooltip');
console.log('tooltip shown: ' + (t !== null) + ', text: ' + (t ? t.textContent.trim() : ''));
Console output (real Chrome)

tooltip shown: true, text: Save the file

How it works

  • Tooltips are not started automatically. The small script finds all elements with data-bs-toggle="tooltip" and starts them.

Read Tooltips and Popovers.

Create a progress bar at 60 percent

Description: Show a progress bar which is filled to 60% with the text “60%”.

Hint:

  • Use progress, progress-bar and the width style. Add the aria attributes.

Solution

<div class="progress" role="progressbar" aria-label="Course progress" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100">
  <div class="progress-bar bg-success" style="width: 60%">60%</div>
</div>
Live preview

How it works

  • The width of .progress-bar is the percent. The role and aria values are on the wrapper for screen readers.

Read Progress Bars and Spinners.

Create a pricing section with three cards

Description: Build three pricing cards in one row (stacked on phones). Each has a title, a price, a list of features and a button. The middle card must be highlighted.

Hint:

  • Use row row-cols-1 row-cols-md-3, card, list-unstyled and border-primary.

Solution

<div class="container">
  <div class="row row-cols-1 row-cols-md-3 g-3 text-center">
    <div class="col">
      <div class="card h-100">
        <div class="card-header"><h3 class="h5 my-1">Free</h3></div>
        <div class="card-body">
          <p class="card-title display-6">$0<small class="fs-6 text-body-secondary"> / month</small></p>
          <ul class="list-unstyled mt-3 mb-4"><li>1 project</li><li>Basic support</li></ul>
          <button type="button" class="btn btn-outline-primary w-100">Sign up</button>
        </div>
      </div>
    </div>
    <div class="col">
      <div class="card h-100 border-primary">
        <div class="card-header text-bg-primary"><h3 class="h5 my-1">Pro</h3></div>
        <div class="card-body">
          <p class="card-title display-6">$15<small class="fs-6 text-body-secondary"> / month</small></p>
          <ul class="list-unstyled mt-3 mb-4"><li>10 projects</li><li>Email support</li></ul>
          <button type="button" class="btn btn-primary w-100">Get started</button>
        </div>
      </div>
    </div>
    <div class="col">
      <div class="card h-100">
        <div class="card-header"><h3 class="h5 my-1">Business</h3></div>
        <div class="card-body">
          <p class="card-title display-6">$49<small class="fs-6 text-body-secondary"> / month</small></p>
          <ul class="list-unstyled mt-3 mb-4"><li>Unlimited projects</li><li>Phone support</li></ul>
          <button type="button" class="btn btn-outline-primary w-100">Contact us</button>
        </div>
      </div>
    </div>
  </div>
</div>
Live preview

How it works

  • row-cols-md-3 makes three equal columns from the md breakpoint, and h-100 makes the cards the same height.
  • The Pro card is highlighted with border-primary and a colored header.

Read Cards and Grid: Gutters, Offset and Order.

Create a button group

Description: Make one group of three outline buttons (Left, Middle, Right), and a vertical group of two buttons.

Hint:

  • Use btn-group and btn-group-vertical.

Solution

<div class="btn-group mb-3" role="group" aria-label="Alignment">
  <button type="button" class="btn btn-outline-primary">Left</button>
  <button type="button" class="btn btn-outline-primary">Middle</button>
  <button type="button" class="btn btn-outline-primary">Right</button>
</div>
<br>
<div class="btn-group-vertical" role="group" aria-label="Vertical group">
  <button type="button" class="btn btn-secondary">Top</button>
  <button type="button" class="btn btn-secondary">Bottom</button>
</div>
Live preview

How it works

  • btn-group joins the buttons and removes the space and the inner radius.

Read Buttons.

Center a box horizontally and vertically with flex utilities

Description: A parent has a height of 200px. Put a blue box in the exact center, horizontally and vertically.

Hint:

  • Use d-flex, justify-content-center and align-items-center on the parent.

Solution

<div class="d-flex justify-content-center align-items-center bg-body-secondary" style="height: 200px">
  <div class="p-3 bg-primary text-white rounded">I am in the center</div>
</div>
Live preview

How it works

  • justify-content-center centers on the main axis (horizontal). align-items-center centers on the other axis (vertical).
  • The parent needs a height, or there is no free space to center in.

Read Flex Utilities.

Add a notification count badge to a button

Description: Add a red round badge with the number 5 on the top right corner of an “Inbox” button.

Hint:

  • Use position-relative on the button and position-absolute top-0 start-100 translate-middle on the badge.

Solution

<button type="button" class="btn btn-primary position-relative">
  Inbox
  <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
    5
    <span class="visually-hidden">unread messages</span>
  </span>
</button>
Live preview

How it works

  • top-0 start-100 puts the badge on the corner, and translate-middle moves it half of its size back, so it sits on the corner.
  • The hidden text tells screen reader users what “5” means.

Read Badges and Utilities.