Skip to content

Bootstrap Aide-mémoire

Powerful, extensible, and feature-packed frontend toolkit built on CSS and JavaScript.

01

Getting Started

Including Bootstrap

Include Bootstrap via CDN for quick start — the CSS goes in the <head>, the JS bundle (which includes Popper) goes before </body>. The bundle.min.js includes Popper for tooltips/dropdowns/popovers. With npm, import the CSS and JS in your entry file. Bootstrap 5 dropped jQuery dependency — all components work with vanilla JS.

bootstrap
<!-- CSS only (in <head>) -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">

<!-- JS bundle (before </body>) -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>

<!-- or install via npm -->
<!-- npm install [email protected] -->

<!-- import in JS -->
// import 'bootstrap/dist/css/bootstrap.min.css'
// import 'bootstrap'  // imports all JS

Containers

Containers are the most basic layout element — they center content and add horizontal padding. .container is fixed-width at each breakpoint (max-width changes); .container-fluid is always 100% wide. Responsive containers (container-sm/md/lg/xl/xxl) are 100% wide until the named breakpoint, then become fixed-width. Use .container for most layouts; .container-fluid for full-width apps.

bootstrap
<!-- fixed-width container (responsive breakpoints) -->
<div class="container">
  <!-- content centered with max-width -->
</div>

<!-- full-width container -->
<div class="container-fluid">
  <!-- spans entire viewport width -->
</div>

<!-- responsive containers (max-width at breakpoint) -->
<div class="container-sm">  <!-- 100% until sm, then fixed -->
<div class="container-md">
<div class="container-xl">

<!-- 100% wide until the breakpoint, then max-width applied -->
<div class="container-xxl">

Responsive Breakpoints

Bootstrap uses a mobile-first, min-width media query system. There are 6 breakpoints: xs (default), sm (576px), md (768px), lg (992px), xl (1200px), xxl (1400px). Classes like col-md-6 apply at md AND above. To show/hide at breakpoints, use d-none (display:none) and d-{breakpoint}-block. This mobile-first approach means you style for small screens first, then add complexity for larger ones.

bootstrap
/* Bootstrap 5 breakpoints (min-width, mobile-first) */
/* xs: <576px  (default, no media query needed) */
/* sm: >=576px */
/* md: >=768px */
/* lg: >=992px */
/* xl: >=1200px */
/* xxl: >=1400px */

<!-- classes apply AT and ABOVE the breakpoint -->
<div class="col-12 col-md-6 col-lg-4">
  <!-- full width on mobile, half on md, third on lg -->
</div>

<!-- hidden/visible at breakpoints -->
<div class="d-none d-md-block">  <!-- hidden below md -->
<div class="d-block d-md-none">  <!-- visible below md only -->

Reboot & Base Styles

Reboot is Bootstrap's CSS reset — it normalizes browser styles using rem units (root em, based on the html font-size of 16px). All elements get box-sizing: border-box. Headings, paragraphs, lists, tables, and links get sensible defaults. Reboot removes top margins from headings and lists, setting them to 0. This gives a consistent baseline across browsers before Bootstrap's components layer on top.

bootstrap
<!-- Reboot normalizes styles: box-sizing, margins, etc. -->
<!-- body gets: font-family, font-size (1rem), line-height (1.5), color -->

<!-- headings use rem units, margin-top removed -->
<h1>Heading 1 (2.5rem)</h1>
<h2>Heading 2 (2rem)</h2>

<!-- links get color and underline on hover -->
<a href="#">Default link</a>

<!-- tables get basic styling -->
<table class="table">...</table>

<!-- <ul>/<ol> have padding-left removed -->
<ul>
  <li>Item</li>
</ul>

Customizing with CSS Variables

Bootstrap 5 exposes hundreds of CSS custom properties (variables) prefixed with --bs-. Override them in :root to theme your site globally. Dark mode is built-in via data-bs-theme='dark' on the <html> element, which swaps a set of variables. This makes theming much easier than the old Sass-variable approach — you can change colors, fonts, spacing, and radii with plain CSS, no build step needed.

bootstrap
:root {
  /* override Bootstrap's CSS variables */
  --bs-primary: #0d6efd;
  --bs-body-bg: #ffffff;
  --bs-body-color: #212529;
  --bs-font-sans-serif: 'Inter', system-ui, sans-serif;
  --bs-border-radius: 0.375rem;
}

/* dark mode via data attribute */
[data-bs-theme="dark"] {
  --bs-body-bg: #212529;
  --bs-body-color: #f8f9fa;
}

<!-- use in HTML -->
<div style="background: var(--bs-primary); color: var(--bs-white);">
  Custom styled
</div>
02

Layout Basics

Rows & Columns

.row is a flex container (display: flex) with negative horizontal margins to offset column padding. Direct .col children flex equally to fill the row. Specify col-{1-12} for fixed proportions (out of 12). Without a number, .col takes equal remaining space. The 12-column grid is the core of Bootstrap layouts — combine col-{n} and auto .col for flexible layouts.

bootstrap
<!-- .row creates a flex container with negative margins -->
<!-- .col children flex equally -->
<div class="container">
  <div class="row">
    <div class="col">Column 1</div>
    <div class="col">Column 2</div>
    <div class="col">Column 3</div>
  </div>
  <!-- 3 equal columns -->
</div>

<!-- sized columns -->
<div class="row">
  <div class="col-8">8/12 width</div>
  <div class="col-4">4/12 width</div>
</div>

<!-- auto + sized -->
<div class="row">
  <div class="col">auto width (content-based)</div>
  <div class="col-6">exactly half</div>
  <div class="col">auto width</div>
</div>

Gutters (Spacing Between Columns)

Gutters are the padding between columns, controlled by the g-* classes (g-0 to g-5). g-0 removes all gutters (edge-to-edge columns). g-3 is the default. Gutters work both horizontally (between columns) and vertically (between rows when columns wrap). Use responsive variants (g-2 g-md-4) for different spacing at breakpoints. The gutter system replaced the old gutter-only horizontal padding approach.

bootstrap
<!-- default gutters (1.5rem padding on each side of columns) -->
<div class="row">
  <div class="col-6">A</div>
  <div class="col-6">B</div>
</div>

<!-- remove gutters -->
<div class="row g-0">
  <div class="col-6">A</div>
  <div class="col-6">B</div>
</div>

<!-- custom gutter sizes: g-0 to g-5 -->
<div class="row g-3">
  <div class="col-6">A</div>
  <div class="col-6">B</div>
</div>

<!-- responsive gutters -->
<div class="row g-2 g-md-4">
  <div class="col-6">A</div>
  <div class="col-6">B</div>
</div>

<!-- vertical gutters (row spacing) -->
<div class="row g-3">
  <div class="col-12">Row 1</div>
  <div class="col-12">Row 2 (g-3 gap above)</div>
</div>

Column Wrapping & Alignment

Columns wrap to a new line when their total exceeds 12. Control vertical alignment with align-items-* on the row (start/center/end). Control horizontal distribution with justify-content-* (start/center/end/between/around/evenly). These use flexbox under the hood. Wrapping is automatic — no need for explicit row breaks. This makes masonry-like layouts easy with just col-* classes.

bootstrap
<!-- columns wrap when they exceed 12 total -->
<div class="row">
  <div class="col-9">9 cols</div>
  <div class="col-4">4 cols → wraps to next line (9+4=13)</div>
  <div class="col-6">6 cols (new line)</div>
</div>

<!-- vertical alignment -->
<div class="row align-items-center">
  <div class="col">Vertically centered</div>
</div>

<!-- options: align-items-start, -center, -end -->
<div class="row align-items-end">...</div>

<!-- horizontal alignment -->
<div class="row justify-content-center">
  <div class="col-6">Centered horizontally</div>
</div>

<!-- options: justify-content-start, -center, -end, -between, -around, -evenly -->

Column Ordering

Order classes reorder columns visually without changing HTML. order-first, order-{1-5}, order-last provide 7 levels. Use responsive variants (order-md-first) to change order at breakpoints — useful for SEO (important content first in HTML) while showing it differently on mobile. Offsets (offset-md-4) add left margin to push columns right — useful for centering or indenting. Both rely on flexbox order and margin utilities.

bootstrap
<!-- order classes: order-first, order-{1-5}, order-last -->
<div class="row">
  <div class="col order-2">First in HTML, second visually</div>
  <div class="col order-1">Second in HTML, first visually</div>
  <div class="col order-3">Third in both</div>
</div>

<!-- responsive ordering -->
<div class="row">
  <div class="col order-last order-md-first">
    Last on mobile, first on desktop
  </div>
  <div class="col">Normal</div>
</div>

<!-- column offset (push right) -->
<div class="row">
  <div class="col-md-4 offset-md-4">Centered (offset 4)</div>
</div>

<!-- offset responsive -->
<div class="col-md-4 offset-md-2 offset-lg-4">...</div>

Z-Index & Stacking

Bootstrap uses a z-index scale for stacking: z-1, z-2, z-3 (and negative z-n0, z-n1, z-n2). Components like modals, dropdowns, and tooltips have higher built-in z-index values (1000-1080). For z-index to work, the element needs position (relative, absolute, fixed, or sticky). Avoid arbitrary z-index values — use Bootstrap's scale for consistency. The stacking order is designed so modals always appear above dropdowns, which appear above navbars.

bootstrap
/* Bootstrap's z-index scale */
/* z-1 to z-3, plus z-n0, z-n1, z-n2 for negatives */

<div class="z-3 position-relative">High z-index</div>
<div class="z-1 position-relative">Low z-index</div>

<!-- components have built-in z-index: -->
/* dropdown: 1000 */
/* sticky: 1020 */
/* fixed: 1030 */
/* modal-backdrop: 1050 */
/* modal: 1055 */
/* popover: 1070 */
/* tooltip: 1080 */

<!-- use position utilities for stacking context -->
<div class="position-relative z-3">
  Needs position for z-index to apply
</div>
03

Grid System

Basic Grid

Bootstrap's grid is a 12-column system. col-{1-12} specifies how many of the 12 columns an element spans. col-12 = full width, col-6 = half, col-4 = third, col-3 = quarter. Columns within a row should sum to 12 (or less — remaining space is distributed). Without a number, .col divides remaining space equally. The grid uses flexbox — rows are flex containers, columns are flex items.

bootstrap
<!-- 12-column grid -->
<div class="container">
  <div class="row">
    <div class="col-12">Full width (12/12)</div>
  </div>
  <div class="row">
    <div class="col-6">Half (6/12)</div>
    <div class="col-6">Half (6/12)</div>
  </div>
  <div class="row">
    <div class="col-4">Third (4/12)</div>
    <div class="col-4">Third (4/12)</div>
    <div class="col-4">Third (4/12)</div>
  </div>
  <div class="row">
    <div class="col-3">Quarter (3/12)</div>
    <div class="col-9">Three-quarters (9/12)</div>
  </div>
</div>

Responsive Columns

Responsive column classes (col-sm-*, col-md-*, col-lg-*, col-xl-*, col-xxl-*) apply at that breakpoint AND above (mobile-first). The smallest applicable class wins. col-12 col-md-6 col-lg-4 means: full-width on mobile, half on tablets, third on desktop. row-cols-* sets the number of equal-width columns per row at a breakpoint — simpler than specifying col-* on each child when all columns are equal.

bootstrap
<!-- different widths at different breakpoints -->
<div class="row">
  <!-- full on mobile, half on md, third on lg -->
  <div class="col-12 col-md-6 col-lg-4">Card 1</div>
  <div class="col-12 col-md-6 col-lg-4">Card 2</div>
  <div class="col-12 col-md-6 col-lg-4">Card 3</div>
</div>

<!-- mobile-first: base class applies to all, -->
<!-- larger breakpoints override -->
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
  1 per row on xs, 2 on sm, 3 on md, 4 on lg, 6 on xl
</div>

<!-- equal-width columns at breakpoint -->
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3">
  <div class="col">Auto-width card</div>
  <div class="col">Auto-width card</div>
  <div class="col">Auto-width card</div>
</div>

Nesting Columns

To nest grids, put a .row inside a .col — the nested row creates a new 12-column context within the parent column's width. The nested columns (col-4, col-8) divide the parent's width, not the full page. Nesting is essential for complex layouts like sidebars with sub-sections. Keep nesting shallow (2-3 levels max) to avoid overly narrow columns and maintain readability.

bootstrap
<!-- nest a row inside a column -->
<div class="row">
  <div class="col-6">
    <!-- outer column -->
    <h3>Left half</h3>
    <!-- nested row: new 12-column context -->
    <div class="row">
      <div class="col-4">Nested 1/3 of parent</div>
      <div class="col-8">Nested 2/3 of parent</div>
    </div>
  </div>
  <div class="col-6">
    <h3>Right half</h3>
  </div>
</div>

<!-- the nested row creates a new 12-col grid -->
<!-- within the parent column's width -->

Column Offsets & Margins

offset-{breakpoint}-{n} adds n columns of left margin, useful for centering or indentation. offset-md-4 on a col-md-4 centers it (4 + 4 + 4 = 12). For simpler centering, use mx-auto (margin: 0 auto) which works regardless of column count. Responsive offsets (offset-sm-2 offset-md-0) let you indent on mobile but not desktop. Offsets are cleaner than empty spacer columns.

bootstrap
<!-- offset: add left margin (empty columns) -->
<div class="row">
  <div class="col-md-4 offset-md-4">
    Centered (4 + 4 offset each side = 12)
  </div>
</div>

<!-- offset only on one side -->
<div class="row">
  <div class="col-md-6 offset-md-3">
    Centered with more left space
  </div>
</div>

<!-- responsive offset -->
<div class="row">
  <div class="col-sm-8 offset-sm-2 offset-md-0 col-md-12">
    Offset on mobile, none on desktop
  </div>
</div>

<!-- margin utilities for auto centering -->
<div class="row">
  <div class="col-md-6 mx-auto">
    Auto-centered (margin: 0 auto)
  </div>
</div>

Row Columns & Auto Layout

row-cols-{n} sets how many equal-width columns per row — much cleaner than adding col-* to each child when they're all the same width. row-cols-2 means 2 columns per row; items wrap automatically. Responsive variants (row-cols-1 row-cols-md-3) change the count at breakpoints. Plain .col (no number) makes columns divide space equally. Use row-cols for card grids; use col-{n} when columns need specific proportions.

bootstrap
<!-- row-cols: set number of columns per row -->
<div class="row row-cols-2">
  <div class="col">Item 1</div>
  <div class="col">Item 2</div>
  <div class="col">Item 3</div>  <!-- wraps to new row -->
  <div class="col">Item 4</div>
</div>
<!-- 2 per row automatically -->

<!-- responsive row-cols -->
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4">
  <div class="col">Card</div>
  <div class="col">Card</div>
  <div class="col">Card</div>
  <div class="col">Card</div>
</div>

<!-- auto-fit columns: .col without number -->
<div class="row">
  <div class="col">Equal</div>
  <div class="col">Equal</div>
  <div class="col">Equal</div>
</div>
<!-- each takes 1/3, regardless of content -->
04

Typography

Headings & Display

Bootstrap styles h1-h6 with rem-based sizes (h1 = 2.5rem, h6 = 1rem). The .h1-.h6 classes apply heading styles to any element. Display headings (display-1 to display-6) are larger and thinner — for hero sections. .lead makes a paragraph slightly larger and lighter — typically for the first paragraph. Headings have no top margin by default (Reboot removes it) to avoid layout gaps.

bootstrap
<!-- standard headings (h1-h6) -->
<h1>h1 heading</h1>
<h2>h2 heading</h2>
<h3>h3 heading</h3>

<!-- heading classes for matching styles -->
<p class="h1">Looks like h1</p>
<p class="h2">Looks like h2</p>

<!-- display headings (larger, thinner) -->
<h1 class="display-1">Display 1</h1>
<h1 class="display-2">Display 2</h1>
<h1 class="display-3">Display 3</h1>
<h1 class="display-4">Display 4</h1>
<h1 class="display-5">Display 5</h1>
<h1 class="display-6">Display 6</h1>

<!-- lead paragraph -->
<p class="lead">This is a lead paragraph.</p>

Inline Text Elements

Bootstrap styles semantic HTML elements: <mark> (highlight), <del>/<s> (strikethrough), <ins>/<u> (underline), <small> (fine print). Text utilities provide styling: text-uppercase/lowercase/capitalize, fw-bold/light/normal, fst-italic, text-muted (gray). text-decoration-underline/line-through control underlines. These utilities are preferred over inline styles for consistency.

bootstrap
<!-- text styling -->
<p>You can use <mark>highlight</mark> text.</p>
<p><del>Deleted</del> and <s>strikethrough</s> text.</p>
<p><ins>Inserted</ins> and <u>underline</u> text.</p>
<p><small>Small text</small> for fine print.</p>
<p><strong>Bold</strong> and <em>italic</em> text.</p>

<!-- text utilities -->
<p class="text-decoration-underline">Underlined</p>
<p class="text-decoration-line-through">Line through</p>
<p class="text-lowercase">lowercase text</p>
<p class="text-uppercase">UPPERCASE TEXT</p>
<p class="text-capitalize">Capitalized Text</p>
<p class="fw-bold">Font weight bold</p>
<p class="fw-light">Font weight light</p>
<p class="fst-italic">Font style italic</p>
<p class="text-muted">Muted/gray text</p>

Lists

list-unstyled removes default list styling (bullets and left padding) — useful for navigation menus. list-inline makes list items display inline (for tag clouds, breadcrumbs). Description lists (dl/dt/dd) get horizontal layout with the row class — dt on left, dd on right, aligned via the grid. Lists in Bootstrap have margin-top: 0 and margin-bottom: 1rem for consistent spacing.

bootstrap
<!-- unordered list -->
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
</ul>

<!-- unstyled list (no bullets, no padding) -->
<ul class="list-unstyled">
  <li>No bullets</li>
  <li>No left padding</li>
</ul>

<!-- inline list -->
<ul class="list-inline">
  <li class="list-inline-item">Inline 1</li>
  <li class="list-inline-item">Inline 2</li>
  <li class="list-inline-item">Inline 3</li>
</ul>

<!-- description list -->
<dl>
  <dt>Term</dt>
  <dd>Definition</dd>
</dl>

<!-- horizontal description list -->
<dl class="row">
  <dt class="col-sm-3">Term</dt>
  <dd class="col-sm-9">Definition</dd>
</dl>

Blockquotes & Code

Blockquotes use the <figure>/<blockquote>/<figcaption> structure for semantics. blockquote-footer styles the citation. Code styling: <code> for inline code, <pre><code> for code blocks, <kbd> for keyboard keys, <var> for variables. Always HTML-escape angle brackets in code (&lt; for <). Blockquotes can be aligned with text-center/text-end. The kbd styling gives keys a keyboard-like appearance.

bootstrap
<!-- blockquote with source -->
<figure>
  <blockquote class="blockquote">
    <p>A well-known quote.</p>
  </blockquote>
  <figcaption class="blockquote-footer">
    Someone famous in <cite title="Source Title">Source Title</cite>
  </figcaption>
</figure>

<!-- blockquote alignment -->
<blockquote class="blockquote text-center">Centered</blockquote>
<blockquote class="blockquote text-end">Right-aligned</blockquote>

<!-- inline code -->
<p>Use <code>&lt;section&gt;</code> tag.</p>

<!-- code block -->
<pre><code>&lt;div class="container"&gt;
  Hello
&lt;/div&gt;
</code></pre>

<!-- variables -->
<var>y</var> = <var>mx</var> + <var>b</var>

<!-- keyboard input -->
<kbd>Ctrl</kbd> + <kbd>C</kbd>

Text Alignment & Wrapping

Text alignment: text-start/center/end (Bootstrap 5 renamed left/right to start/end for RTL support). Responsive variants (text-md-start) change alignment at breakpoints. text-nowrap prevents wrapping; text-truncate adds ellipsis for overflow (needs a max-width or constrained container). text-break breaks long words. lh-* controls line-height. These utilities handle text flow without custom CSS.

bootstrap
<!-- text alignment -->
<p class="text-start">Left aligned (default)</p>
<p class="text-center">Center aligned</p>
<p class="text-end">Right aligned</p>

<!-- responsive alignment -->
<p class="text-center text-md-start">
  Center on mobile, left on md+
</p>

<!-- text wrapping -->
<div class="text-wrap">This text wraps.</div>
<div class="text-nowrap">This text doesn't wrap.</div>

<!-- truncate with ellipsis -->
<div class="text-truncate" style="max-width: 200px;">
  This long text will be truncated with an ellipsis...
</div>

<!-- word break -->
<p class="text-break">verylongwordwithoutspaces</p>

<!-- line height -->
<p class="lh-1">Line height 1</p>
<p class="lh-sm">Line height small</p>
<p class="lh-base">Line height base (default)</p>
<p class="lh-lg">Line height large</p>
05

Tables

Basic Table

The .table class adds padding, borders, and horizontal dividers. Use <thead> for headers, <tbody> for data. scope='col' on <th> in the header and scope='row' on <th> in rows improve accessibility for screen readers. Tables are opt-in — without .table, you get unstyled HTML tables. Always use semantic table elements (thead, tbody, th, tr, td) for accessibility.

bootstrap
<!-- basic table with styling -->
<table class="table">
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">Name</th>
      <th scope="col">Email</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">1</th>
      <td>Alice</td>
      <td>[email protected]</td>
    </tr>
    <tr>
      <th scope="row">2</th>
      <td>Bob</td>
      <td>[email protected]</td>
    </tr>
  </tbody>
</table>

Table Variants & Striped

Table modifiers: table-striped (alternating row colors), table-bordered (all borders), table-borderless (no borders), table-hover (row highlight on hover), table-sm (condensed padding), table-dark (dark theme). These can be combined: table table-striped table-hover. The dark variant inverts colors for dark backgrounds. table-sm halves the cell padding for denser data display.

bootstrap
<!-- striped rows -->
<table class="table table-striped">
  <!-- alternating background colors -->
</table>

<!-- bordered table -->
<table class="table table-bordered">
  <!-- borders on all sides -->
</table>

<!-- borderless table -->
<table class="table table-borderless">
  <!-- no borders -->
</table>

<!-- hoverable rows -->
<table class="table table-hover">
  <!-- row highlights on hover -->
</table>

<!-- small table -->
<table class="table table-sm">
  <!-- condensed padding -->
</table>

<!-- dark table -->
<table class="table table-dark">
  <!-- dark background -->
</table>

Table Colors

Apply contextual classes to <tr> or <td>: table-primary, table-success, table-danger, table-warning, table-info, table-secondary, table-light, table-dark, table-active. These add subtle background tints for status indication. On dark tables, the same classes provide lighter variants. Use sparingly for meaningful status (success for completed, danger for errors) rather than decoration.

bootstrap
<!-- contextual row/cell colors -->
<table class="table">
  <thead>
    <tr class="table-primary"><th>Primary header</th></tr>
  </thead>
  <tbody>
    <tr class="table-success">
      <td>Success row (green)</td>
    </tr>
    <tr class="table-danger">
      <td>Danger row (red)</td>
    </tr>
    <tr class="table-warning">
      <td>Warning row (yellow)</td>
    </tr>
    <tr class="table-info">
      <td>Info row (cyan)</td>
    </tr>
    <!-- individual cells -->
    <tr>
      <td class="table-active">Active cell</td>
      <td>Normal cell</td>
    </tr>
  </tbody>
</table>

Responsive Tables

Wrap tables in .table-responsive to enable horizontal scrolling on small screens (prevents layout breakage). table-responsive-sm/md/lg/xl/xxl scroll only below that breakpoint — the table fits normally on larger screens. Without responsive wrapping, wide tables overflow their container. The wrapper div is necessary because overflow can't be applied directly to <table>. Always use this for tables with many columns.

bootstrap
<!-- responsive table (horizontal scroll on small screens) -->
<div class="table-responsive">
  <table class="table">
    <!-- scrolls horizontally on small screens -->
    <thead>...</thead>
    <tbody>...</tbody>
  </table>
</div>

<!-- responsive at specific breakpoints -->
<div class="table-responsive-sm">
  <!-- scrolls below sm -->
</div>
<div class="table-responsive-md">
  <!-- scrolls below md -->
</div>
<div class="table-responsive-lg">
  <!-- scrolls below lg -->
</div>

<!-- always scrollable -->
<div class="table-responsive">...</div>

Table Groups & Captions

caption-top moves the caption above the table (default is below). Captions improve accessibility for screen readers. Style thead/tbody/tfoot separately with table-light/table-dark. Vertical alignment: align-top, align-middle, align-bottom on cells or rows — useful when cells have different content heights. The tfoot element is styled like the thead by default but can be customized.

bootstrap
<!-- caption (accessibility, shown below table by default) -->
<table class="table caption-top">
  <caption>List of users</caption>
  <thead>
    <tr><th>#</th><th>Name</th></tr>
  </thead>
  <tbody>
    <tr><td>1</td><td>Alice</td></tr>
  </tbody>
</table>

<!-- table groups for styling -->
<table class="table">
  <thead class="table-light">
    <tr><th>Light header</th></tr>
  </thead>
  <tbody>
    <tr><td>Body</td></tr>
  </tbody>
  <tfoot class="table-dark">
    <tr><td>Dark footer</td></tr>
  </tfoot>
</table>

<!-- vertically aligned cells -->
<tr class="align-middle">
  <td class="align-top">Top</td>
  <td class="align-middle">Middle</td>
  <td class="align-bottom">Bottom</td>
</tr>
06

Forms

Form Controls

form-control styles text inputs, textareas, and file inputs. form-select styles dropdowns. Always pair inputs with <label> elements (with for/id matching) for accessibility. form-label adds appropriate spacing. disabled prevents interaction; readonly shows value without editing. mb-3 (margin-bottom) adds spacing between form groups. The .form-control class applies consistent height, padding, and focus styles.

bootstrap
<!-- text input -->
<div class="mb-3">
  <label for="email" class="form-label">Email</label>
  <input type="email" class="form-control" id="email" placeholder="Enter email">
</div>

<!-- textarea -->
<div class="mb-3">
  <label for="bio" class="form-label">Bio</label>
  <textarea class="form-control" id="bio" rows="3"></textarea>
</div>

<!-- select -->
<select class="form-select" aria-label="Default select">
  <option selected>Open this select menu</option>
  <option value="1">One</option>
  <option value="2">Two</option>
</select>

<!-- disabled -->
<input class="form-control" type="text" placeholder="Disabled" disabled>
<input class="form-control" type="text" value="Readonly" readonly>

Form Grid & Layout

Use the grid system (row/col) to lay out forms. For horizontal forms, pair col-form-label with col-* for label/input alignment. row-cols-lg-auto makes form elements size to their content (inline form) on large screens. align-items-center vertically aligns labels with inputs. The grid gives precise control over form layout at different breakpoints without custom CSS.

bootstrap
<!-- form using grid -->
<div class="row mb-3">
  <label for="name" class="col-sm-2 col-form-label">Name</label>
  <div class="col-sm-10">
    <input type="text" class="form-control" id="name">
  </div>
</div>

<!-- inline form -->
<form class="row row-cols-lg-auto g-3 align-items-center">
  <div class="col">
    <input class="form-control" placeholder="Email">
  </div>
  <div class="col">
    <input class="form-control" placeholder="Password">
  </div>
  <div class="col">
    <button type="submit" class="btn btn-primary">Sign in</button>
  </div>
</form>

<!-- horizontal form with column sizing -->
<form>
  <div class="row mb-3">
    <label class="col-2 col-form-label">Email</label>
    <div class="col-10">
      <input class="form-control" type="email">
    </div>
  </div>
</form>

Input Groups

Input groups prepend or append text/buttons to inputs using input-group-text. Common for @ usernames, $ currency, .00 decimals, or search buttons. The input-group wrapper makes the addon and input appear as one connected control. Multiple inputs can share one addon. Buttons inside input groups use btn-outline-* for a connected look. Don't mix input groups with form-floating.

bootstrap
<!-- prepend text -->
<div class="input-group mb-3">
  <span class="input-group-text">@</span>
  <input type="text" class="form-control" placeholder="Username">
</div>

<!-- append text -->
<div class="input-group mb-3">
  <input type="text" class="form-control" placeholder="Recipient's username">
  <span class="input-group-text">@example.com</span>
</div>

<!-- prepend + append -->
<div class="input-group mb-3">
  <span class="input-group-text">$</span>
  <input type="text" class="form-control">
  <span class="input-group-text">.00</span>
</div>

<!-- with button -->
<div class="input-group mb-3">
  <input type="text" class="form-control" placeholder="Search">
  <button class="btn btn-outline-secondary" type="button">Go</button>
</div>

<!-- multiple inputs -->
<div class="input-group">
  <span class="input-group-text">First and last name</span>
  <input type="text" class="form-control">
  <input type="text" class="form-control">
</div>

Floating Labels

Floating labels animate the label from inside the input to above it when the field is focused or has content. The label must come AFTER the input in HTML. A placeholder attribute is required (even if empty) for the floating behavior. This pattern saves space and looks modern. Don't combine with input-group. Floating labels work with inputs, textareas, and selects. They're a clean alternative to traditional labels for compact forms.

bootstrap
<!-- floating label (label floats up when focused/filled) -->
<div class="form-floating mb-3">
  <input type="email" class="form-control" id="floatingInput" placeholder="[email protected]">
  <label for="floatingInput">Email address</label>
</div>

<!-- floating textarea -->
<div class="form-floating">
  <textarea class="form-control" id="floatingTextarea" placeholder="Leave a comment"></textarea>
  <label for="floatingTextarea">Comments</label>
</div>

<!-- floating select -->
<div class="form-floating mb-3">
  <select class="form-select" id="floatingSelect">
    <option selected>Open this select menu</option>
    <option value="1">One</option>
  </select>
  <label for="floatingSelect">Works with selects</label>
</div>

<!-- note: placeholder is REQUIRED for floating labels -->
<!-- the placeholder="..." must be present -->

Validation

Bootstrap provides is-valid and is-invalid classes for validation styling, with valid-feedback and invalid-feedback for messages. The needs-validation class on a form, combined with novalidate (disables browser tooltips), lets you use Bootstrap's validation UI with custom JS. On submit, add was-validated to show all validation states. This works with HTML5 constraints (required, pattern, type) or custom server-side validation.

bootstrap
<!-- server-side validation classes -->
<div class="mb-3">
  <label for="username" class="form-label">Username</label>
  <input type="text" class="form-control is-valid" id="username" value="valid_user">
  <div class="valid-feedback">Looks good!</div>
</div>

<div class="mb-3">
  <label for="email" class="form-label">Email</label>
  <input type="email" class="form-control is-invalid" id="email">
  <div class="invalid-feedback">Please enter a valid email.</div>
</div>

<!-- no validate on form to disable browser validation -->
<form class="needs-validation" novalidate>
  <div class="mb-3">
    <input type="text" class="form-control" required>
    <div class="invalid-feedback">This field is required.</div>
  </div>
  <button class="btn btn-primary" type="submit">Submit</button>
</form>

<!-- JS: form.classList.add('was-validated') on submit -->
07

Buttons

Button Variants

btn is the base button class; btn-{color} sets the variant. Solid buttons have colored backgrounds with white text. Outline buttons (btn-outline-*) have transparent backgrounds with colored text/borders — good for secondary actions. btn-link makes a button look like a link. Choose colors meaningfully: success for confirm, danger for delete, warning for caution. Don't use color alone to convey meaning — include text.

bootstrap
<!-- solid buttons -->
<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-secondary">Secondary</button>
<button type="button" class="btn btn-success">Success</button>
<button type="button" class="btn btn-danger">Danger</button>
<button type="button" class="btn btn-warning">Warning</button>
<button type="button" class="btn btn-info">Info</button>
<button type="button" class="btn btn-light">Light</button>
<button type="button" class="btn btn-dark">Dark</button>

<!-- link-styled button -->
<button type="button" class="btn btn-link">Link</button>

<!-- outline buttons (transparent bg, colored border) -->
<button type="button" class="btn btn-outline-primary">Outline Primary</button>
<button type="button" class="btn btn-outline-danger">Outline Danger</button>

Button Sizes & States

Button sizes: btn-lg, default, btn-sm. disabled attribute (on <button>) or .disabled class (on <a> or when you can't use the attribute) greys out the button. .active adds a pressed appearance. For full-width buttons, use d-grid gap-2 (makes buttons stack and fill width). d-md-block reverts to inline at md breakpoint — useful for full-width on mobile, auto on desktop. The .disabled class also sets pointer-events: none.

bootstrap
<!-- sizes -->
<button class="btn btn-primary btn-lg">Large</button>
<button class="btn btn-primary">Default</button>
<button class="btn btn-primary btn-sm">Small</button>

<!-- disabled state -->
<button class="btn btn-primary" disabled>Disabled</button>
<!-- or -->
<button class="btn btn-primary disabled">Disabled (aria)</button>

<!-- active/pressed state -->
<button class="btn btn-primary active" aria-pressed="true">Active</button>

<!-- full-width block button -->
<div class="d-grid gap-2">
  <button class="btn btn-primary">Block button</button>
  <button class="btn btn-secondary">Another</button>
</div>

<!-- responsive block buttons -->
<div class="d-grid gap-2 d-md-block">
  <button class="btn btn-primary">Full on mobile, auto on md+</button>
</div>

Button Groups

btn-group groups buttons horizontally with connected edges. btn-toolbar combines multiple groups with spacing. btn-group-vertical stacks buttons vertically. Always add role='group' and aria-label for accessibility. Sizing classes (btn-group-lg/sm) apply to all buttons in the group. Button groups are the foundation for toolbars, segmented controls, and pagination-like UIs.

bootstrap
<!-- basic button group -->
<div class="btn-group" role="group" aria-label="Basic example">
  <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>

<!-- button toolbar (groups of groups) -->
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar">
  <div class="btn-group me-2" role="group">
    <button class="btn btn-outline-secondary">1</button>
    <button class="btn btn-outline-secondary">2</button>
  </div>
  <div class="btn-group" role="group">
    <button class="btn btn-outline-secondary">3</button>
    <button class="btn btn-outline-secondary">4</button>
  </div>
</div>

<!-- vertical button group -->
<div class="btn-group-vertical" role="group">
  <button class="btn btn-outline-primary">Top</button>
  <button class="btn btn-outline-primary">Middle</button>
  <button class="btn btn-outline-primary">Bottom</button>
</div>

<!-- sizing -->
<div class="btn-group btn-group-lg">...</div>
<div class="btn-group btn-group-sm">...</div>

Toggle & Checkbox Buttons

btn-check is a hidden checkbox/radio that pairs with a btn-label for toggle behavior. The input is visually hidden; the label (styled as a button) shows the state. For single toggles, use a checkbox. For mutually exclusive options, use radios with the same name. The button's appearance changes when checked. autocomplete='off' prevents browser state restoration. This is the accessible way to make button-style toggles.

bootstrap
<!-- toggle button (single) -->
<input type="checkbox" class="btn-check" id="btn-check" autocomplete="off">
<label class="btn btn-primary" for="btn-check">Toggle</label>

<!-- checked by default -->
<input type="checkbox" class="btn-check" id="btn-check-checked" checked autocomplete="off">
<label class="btn btn-primary" for="btn-check-checked">Checked</label>

<!-- radio button group -->
<div class="btn-group" role="group" aria-label="Radio toggle">
  <input type="radio" class="btn-check" name="options" id="opt1" autocomplete="off" checked>
  <label class="btn btn-outline-primary" for="opt1">Option 1</label>
  <input type="radio" class="btn-check" name="options" id="opt2" autocomplete="off">
  <label class="btn btn-outline-primary" for="opt2">Option 2</label>
</div>

<!-- outline toggle (changes when checked) -->
<input type="checkbox" class="btn-check" id="btn-check-out" autocomplete="off">
<label class="btn btn-outline-success" for="btn-check-out">Toggle</label>

Dropdown Buttons

Dropdowns need data-bs-toggle='dropdown' on the toggle button. The menu is a ul.dropdown-menu with dropdown-item links. dropdown-divider separates groups. Split buttons (dropdown-toggle-split) have a separate action button and toggle arrow — add visually-hidden text for accessibility. dropdown-item.active marks the current selection; .disabled greys it out. Dropdowns require the Bootstrap JS bundle (includes Popper for positioning).

bootstrap
<!-- button dropdown -->
<div class="dropdown">
  <button class="btn btn-secondary dropdown-toggle" type="button"
          data-bs-toggle="dropdown" aria-expanded="false">
    Dropdown button
  </button>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="#">Action</a></li>
    <li><a class="dropdown-item" href="#">Another action</a></li>
    <li><hr class="dropdown-divider"></li>
    <li><a class="dropdown-item" href="#">Separated link</a></li>
  </ul>
</div>

<!-- split dropdown -->
<div class="btn-group">
  <button type="button" class="btn btn-primary">Primary</button>
  <button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split"
          data-bs-toggle="dropdown" aria-expanded="false">
    <span class="visually-hidden">Toggle Dropdown</span>
  </button>
  <ul class="dropdown-menu">...</ul>
</div>

<!-- dropdown with active/disabled items -->
<li><a class="dropdown-item active" href="#">Active</a></li>
<li><a class="dropdown-item disabled">Disabled</a></li>
08

Cards

Basic Card

Cards are flexible containers with optional header, body, and footer. card-img-top places an image at the top. card-title, card-subtitle, card-text style content. The width is controlled by the parent grid or a style attribute — cards default to full width. card-header and card-footer are for structured content. Cards replaced Bootstrap 3's panels, wells, and thumbnails with one unified component.

bootstrap
<div class="card" style="width: 18rem;">
  <!-- optional image -->
  <img src="..." class="card-img-top" alt="...">

  <div class="card-body">
    <h5 class="card-title">Card title</h5>
    <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
    <p class="card-text">Some quick example text to build on the card title.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>

<!-- card sections -->
<div class="card">
  <div class="card-header">Header</div>
  <div class="card-body">Body content</div>
  <div class="card-footer text-muted">Footer</div>
</div>

Card Content Types

Cards can contain various content types: images, text, list groups (use list-group-flush to remove borders and match card edges), and links (card-link adds spacing). Mix and match sections — card-body, list-group, card-header, card-footer — to build the exact card you need. list-group-flush integrates lists seamlessly into cards without double borders. This flexibility makes cards Bootstrap's most versatile component.

bootstrap
<!-- list group inside card -->
<div class="card" style="width: 18rem;">
  <div class="card-header">Featured</div>
  <ul class="list-group list-group-flush">
    <li class="list-group-item">Item 1</li>
    <li class="list-group-item">Item 2</li>
    <li class="list-group-item">Item 3</li>
  </ul>
  <div class="card-body">
    <a href="#" class="card-link">Card link</a>
    <a href="#" class="card-link">Another link</a>
  </div>
</div>

<!-- kitchen sink (all content types) -->
<div class="card">
  <img src="..." class="card-img-top">
  <div class="card-body">
    <h5 class="card-title">Title</h5>
    <p class="card-text">Text</p>
  </div>
  <ul class="list-group list-group-flush">
    <li class="list-group-item">List item</li>
  </ul>
  <div class="card-body">
    <a href="#" class="card-link">Link</a>
  </div>
</div>

Card Grid & Groups

card-group connects cards with shared borders and equal heights. For responsive card grids, use the grid system: row row-cols-1 row-cols-md-3 g-4 gives 1 column on mobile, 3 on desktop, with gap-4 spacing. Each card goes in a .col. The grid approach is more flexible than card-group — cards can have independent heights, and you control wrapping. Use card-group only when you need connected, equal-height cards.

bootstrap
<!-- card group (cards share border, equal height) -->
<div class="card-group">
  <div class="card">
    <img src="..." class="card-img-top">
    <div class="card-body">
      <h5 class="card-title">Card 1</h5>
    </div>
  </div>
  <div class="card">
    <div class="card-body">
      <h5 class="card-title">Card 2</h5>
    </div>
  </div>
  <div class="card">
    <div class="card-body">
      <h5 class="card-title">Card 3</h5>
    </div>
  </div>
</div>
<!-- cards have equal height, connected borders -->

<!-- using grid for responsive card layout -->
<div class="row row-cols-1 row-cols-md-3 g-4">
  <div class="col">
    <div class="card">...</div>
  </div>
  <div class="col">
    <div class="card">...</div>
  </div>
</div>

Card Navigation

Add navigation to cards with nav-tabs or nav-pills in the card-header. card-header-tabs and card-header-pills integrate the nav flush with the header edges. Use nav-link active for the current tab, nav-link disabled for inactive. To make tabs functional (switch content), add data-bs-toggle='tab' and tab panes — Bootstrap's JS handles the switching. This pattern is common for dashboards and content cards with multiple views.

bootstrap
<!-- card with nav tabs -->
<div class="card text-center">
  <div class="card-header">
    <ul class="nav nav-tabs card-header-tabs">
      <li class="nav-item">
        <a class="nav-link active" href="#">Active</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Link</a>
      </li>
      <li class="nav-item">
        <a class="nav-link disabled">Disabled</a>
      </li>
    </ul>
  </div>
  <div class="card-body">
    <h5 class="card-title">Special title</h5>
    <p class="card-text">Content for active tab.</p>
    <a href="#" class="btn btn-primary">Go</a>
  </div>
</div>

<!-- card with pills -->
<div class="card-header">
  <ul class="nav nav-pills card-header-pills">
    <li class="nav-item"><a class="nav-link active" href="#">Active</a></li>
  </ul>
</div>

Card Variants & Overlay

Card color variants: bg-{color} with text-white/text-dark for solid backgrounds; border-{color} with text-{color} for colored borders. card-img-overlay places text over an image (card-img fills the card, overlay content sits on top) — use text-bg-dark for readable text on images. text-center/text-start/text-end control text alignment. For dark image overlays, ensure sufficient contrast between text and image.

bootstrap
<!-- colored card -->
<div class="card text-white bg-primary">
  <div class="card-header">Header</div>
  <div class="card-body">
    <h5 class="card-title">Primary card</h5>
    <p class="card-text">Text on colored background.</p>
  </div>
</div>

<!-- border colored card -->
<div class="card border-primary">
  <div class="card-header border-primary">Header</div>
  <div class="card-body text-primary">
    <h5 class="card-title">Primary border</h5>
  </div>
</div>

<!-- image overlay (text over image) -->
<div class="card text-bg-dark">
  <img src="..." class="card-img">
  <div class="card-img-overlay">
    <h5 class="card-title">Overlay title</h5>
    <p class="card-text">Text over the image.</p>
  </div>
</div>

<!-- text alignment -->
<div class="card text-center">Centered text</div>
<div class="card text-end">Right-aligned</div>
11

Alerts

Basic Alerts

Alerts display contextual feedback messages. alert-{variant} sets the color (primary, success, danger, warning, info, light, dark). role='alert' is for screen reader accessibility. alert-link styles links to match the alert color. Use alerts for: form validation feedback, action results (saved! deleted!), or important notices. Choose colors semantically: success for positive, danger for errors, warning for caution, info for neutral info.

bootstrap
<!-- alert variants -->
<div class="alert alert-primary" role="alert">Primary alert</div>
<div class="alert alert-secondary" role="alert">Secondary alert</div>
<div class="alert alert-success" role="alert">Success! Well done.</div>
<div class="alert alert-danger" role="alert">Danger! Something went wrong.</div>
<div class="alert alert-warning" role="alert">Warning! Check this.</div>
<div class="alert alert-info" role="alert">Info! Note this.</div>
<div class="alert alert-light" role="alert">Light alert</div>
<div class="alert alert-dark" role="alert">Dark alert</div>

<!-- with link -->
<div class="alert alert-primary" role="alert">
  This is an alert with <a href="#" class="alert-link">a link</a>.
</div>

Dismissible Alerts

alert-dismissible adds padding for the close button; btn-close with data-bs-dismiss='alert' closes it. fade show enables the fade-out transition. Once dismissed, the alert is removed from the DOM (not just hidden). Via JS, create an Alert instance and call close(). The closed.bs.alert event fires after removal. If you want to show it again, you must re-create the DOM element. For persistent dismissals (don't show again), store the state in localStorage.

bootstrap
<!-- dismissible alert -->
<div class="alert alert-warning alert-dismissible fade show" role="alert">
  <strong>Holy guacamole!</strong> You should check in on some of those fields.
  <button type="button" class="btn-close" data-bs-dismiss="alert"
          aria-label="Close"></button>
</div>

<!-- needs these classes:
     - alert-dismissible: adds padding for close button
     - fade show: enables transition

<!-- after dismissal, the alert is removed from DOM -->

<!-- via JavaScript -->
<script>
const alertEl = document.querySelector('.alert')
const alert = new bootstrap.Alert(alertEl)
alert.close()  // closes and removes
</script>

<!-- events -->
alertEl.addEventListener('closed.bs.alert', () => {
  console.log('alert was closed and removed')
})
</script>

Alert with Icons & Content

Alerts can contain rich content: headings (alert-heading), paragraphs, dividers (hr), icons, and action buttons. For icons, use Bootstrap Icons (bi class) or SVGs with flex alignment. d-flex align-items-center vertically centers icon and text. Include action buttons (like 'Extend' for session warnings) alongside the dismiss button. Alerts aren't just for text — they're flexible containers for important, dismissible feedback.

bootstrap
<!-- alert with icon -->
<div class="alert alert-success d-flex align-items-center" role="alert">
  <svg class="bi flex-shrink-0 me-2" width="24" height="24">
    <use xlink:href="#check-circle-fill"/>
  </svg>
  <div>
    Operation completed successfully!
  </div>
</div>

<!-- alert with more content -->
<div class="alert alert-danger" role="alert">
  <h4 class="alert-heading">Error!</h4>
  <p>There was a problem processing your request.</p>
  <hr>
  <p class="mb-0">Please try again or contact support.</p>
</div>

<!-- alert with action button -->
<div class="alert alert-warning alert-dismissible" role="alert">
  Your session expires in 5 minutes.
  <button type="button" class="btn btn-warning btn-sm ms-3">
    Extend
  </button>
  <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>

Toast Notifications

Toasts are lightweight notifications (like Android toasts). They auto-hide after a delay (default 5s). Wrap in toast-container for stacking. position-fixed with top-0 end-0 pins to top-right. aria-live='assertive' announces to screen readers. toast-header has icon, title, timestamp, close button; toast-body has the message. Unlike alerts, toasts don't block the UI — they're non-intrusive. Use for 'Saved!', 'New message', or background task completion.

bootstrap
<!-- toast container (top-right by default) -->
<div class="toast-container position-fixed top-0 end-0 p-3">
  <div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
    <div class="toast-header">
      <img src="..." class="rounded me-2" width="20">
      <strong class="me-auto">Bootstrap</strong>
      <small>11 mins ago</small>
      <button type="button" class="btn-close" data-bs-dismiss="toast"></button>
    </div>
    <div class="toast-body">
      Hello, world! This is a toast message.
    </div>
  </div>
</div>

<script>
// show via JS
const toastEl = document.querySelector('.toast')
const toast = new bootstrap.Toast(toastEl, {
  delay: 5000,       // auto-hide after 5s
  autohide: true
})
toast.show()

// events
toastEl.addEventListener('shown.bs.toast', () => {
  console.log('toast visible')
})
</script>

Live Alert Region

For dynamically updated alerts (status messages, progress), use aria-live='polite' — screen readers announce changes without interrupting. aria-live='assertive' announces immediately (for critical errors). aria-atomic='true' reads the entire content on change (not just the changed part). role='alert' is implicitly assertive. This accessibility pattern ensures screen reader users hear dynamic updates. Combine with alert classes for visual styling.

bootstrap
<!-- aria-live for dynamic alerts -->
<div class="alert alert-info" role="alert" aria-live="polite"
     aria-atomic="true" id="statusAlert">
  <!-- content updated dynamically -->
</div>

<script>
// update alert content
function showStatus(message, type = 'info') {
  const alert = document.getElementById('statusAlert')
  alert.className = 'alert alert-' + type
  alert.textContent = message
}

showStatus('Loading...', 'info')
// later:
showStatus('Data loaded!', 'success')

<!-- aria-live="polite" announces updates without interrupting -->
<!-- aria-live="assertive" interrupts immediately (urgent) -->

<!-- for form errors, use role="alert" (assertive by default) -->
<div role="alert" class="alert alert-danger" id="formError">
  Please fix the errors below.
</div>
</script>
12

Badges

Basic Badges

Badges are small count/label indicators. bg-{color} sets the background; add text-dark on light backgrounds (warning, info, light) for contrast. Badges scale relative to their parent element's font-size — an h1 badge is larger than a p badge. Use badges for: unread counts, status labels, 'New' tags, version numbers. They're inline elements that fit within text flow.

bootstrap
<!-- inline badges -->
<h1>Heading <span class="badge bg-secondary">New</span></h1>
<h2>Heading <span class="badge bg-secondary">New</span></h2>
<h3>Heading <span class="badge bg-secondary">New</span></h3>

<!-- colored badges -->
<span class="badge bg-primary">Primary</span>
<span class="badge bg-secondary">Secondary</span>
<span class="badge bg-success">Success</span>
<span class="badge bg-danger">Danger</span>
<span class="badge bg-warning text-dark">Warning</span>
<span class="badge bg-info text-dark">Info</span>
<span class="badge bg-light text-dark">Light</span>
<span class="badge bg-dark">Dark</span>

<!-- badge sizing scales with parent -->
<h1>Big <span class="badge bg-primary">Badge</span></h1>
<p>Small <span class="badge bg-primary">Badge</span></p>

Pill Badges & Rounded

rounded-pill makes fully rounded badges (pill shape). rounded-0 to rounded-3 control the border-radius scale. rounded-circle with padding creates circular badges — useful for notification counts. For perfect circles with single digits, set equal width/height and use d-flex align-items-center justify-content-center. Pill badges are common for tags and status indicators.

bootstrap
<!-- pill badges (more rounded) -->
<span class="badge rounded-pill bg-primary">Primary</span>
<span class="badge rounded-pill bg-success">Success</span>
<span class="badge rounded-pill bg-danger">Danger</span>

<!-- rounded variations -->
<span class="badge rounded-0 bg-primary">Not rounded</span>
<span class="badge rounded-1 bg-primary">Slightly rounded</span>
<span class="badge rounded-2 bg-primary">Default rounded</span>
<span class="badge rounded-3 bg-primary">More rounded</span>
<span class="badge rounded-pill bg-primary">Full pill</span>

<!-- circle badge (fixed size, centered text) -->
<span class="badge rounded-circle bg-primary p-2">5</span>
<!-- needs width/height for perfect circle -->

Badges in Buttons & Nav

Badges in buttons: position-relative on the button, position-absolute on the badge with top-0 start-100 translate-middle to place it at the top-right corner. visually-hidden text inside the badge provides context for screen readers. Badges in nav links and list items show counts — use justify-content-between to push them right. The '99+' pattern is common when counts exceed display limits.

bootstrap
<!-- badge inside button -->
<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">
    99+
  </span>
</button>

<!-- badge with position outside button -->
<button class="btn btn-primary position-relative">
  Messages
  <span class="position-absolute top-0 start-100 translate-middle
               badge rounded-pill bg-danger">
    9
    <span class="visually-hidden">unread messages</span>
  </span>
</button>

<!-- badge in nav link -->
<ul class="nav nav-pills">
  <li class="nav-item">
    <a class="nav-link" href="#">
      Profile
      <span class="badge bg-secondary">3</span>
    </a>
  </li>
</ul>

<!-- badge in list item -->
<li class="list-group-item d-flex justify-content-between align-items-center">
  Messages
  <span class="badge bg-primary rounded-pill">14</span>
</li>

Badge Indicators (Dot)

Dot badges (no text, just a colored circle) indicate status without a count — like 'unread' or 'online'. Use p-1 with rounded-circle for a small dot, border-light to separate from the background. Position with the same translate-middle technique. Online status dots (green = online, gray = offline) go at the bottom corner of avatars. Badges can also wrap icons (Bootstrap Icons: bi bi-star-fill) with text for ratings.

bootstrap
<!-- status dot badge -->
<button class="btn btn-primary position-relative">
  Notifications
  <span class="position-absolute top-0 start-100 translate-middle
               p-1 bg-danger border border-light rounded-circle">
    <span class="visually-hidden">New alerts</span>
  </span>
</button>

<!-- small dot (no text, just indicator) -->
<span class="position-absolute top-0 start-100 translate-middle
             p-1 bg-danger border border-light rounded-circle"></span>

<!-- online status indicator -->
<span class="position-relative d-inline-flex">
  <img src="avatar.jpg" class="rounded-circle" width="40">
  <span class="position-absolute bottom-0 end-0
               bg-success border border-light rounded-circle p-1">
  </span>
</span>
<!-- green dot = online -->

<!-- badge with icon -->
<span class="badge bg-primary">
  <i class="bi bi-star-fill"></i> 4.5
</span>

Spinner Border & Grow

Spinners indicate loading state. spinner-border is a rotating ring; spinner-grow is a pulsating circle. text-* colors them. spinner-border-sm/grow-sm are smaller. Custom sizes via width/height CSS. Always include role='status' with visually-hidden text for accessibility. In buttons, use spinner-border-sm with disabled to show a loading action. Spinners are CSS-only (no JS) — they animate via border and opacity transitions.

bootstrap
<!-- border spinner (rotating border) -->
<div class="spinner-border" role="status">
  <span class="visually-hidden">Loading...</span>
</div>

<!-- colored spinner -->
<div class="spinner-border text-primary"></div>
<div class="spinner-border text-success"></div>
<div class="spinner-border text-danger"></div>

<!-- grow spinner (pulsating circle) -->
<div class="spinner-grow" role="status">
  <span class="visually-hidden">Loading...</span>
</div>

<!-- sizes -->
<div class="spinner-border spinner-border-sm">Small</div>
<div class="spinner-grow spinner-grow-sm">Small grow</div>

<!-- custom size via CSS -->
<div class="spinner-border" style="width: 3rem; height: 3rem;"></div>

<!-- spinner in button -->
<button class="btn btn-primary" disabled>
  <span class="spinner-border spinner-border-sm"></span>
  Loading...
</button>

<!-- spinner with text -->
<div class="d-flex align-items-center">
  <strong>Loading...</strong>
  <div class="spinner-border ms-auto"></div>
</div>
13

Progress & Spinners

Basic Progress Bar

Progress bars show task completion. Set the width as a percentage via inline style. role='progressbar' with aria-valuenow/min/max is for accessibility. The height is set on the .progress container (default 1rem). bg-{color} on the bar changes its color. Label text inside the bar is optional — omit it for minimal bars. Always set width via style (not class) since values are dynamic.

bootstrap
<!-- basic progress bar -->
<div class="progress">
  <div class="progress-bar" role="progressbar" style="width: 50%"
       aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
    50%
  </div>
</div>

<!-- without label -->
<div class="progress">
  <div class="progress-bar" style="width: 75%" role="progressbar"
       aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"></div>
</div>

<!-- height (set on container) -->
<div class="progress" style="height: 1px;">
  <div class="progress-bar" style="width: 25%"></div>
</div>
<div class="progress" style="height: 20px;">
  <div class="progress-bar" style="width: 25%"></div>
</div>

<!-- background -->
<div class="progress">
  <div class="progress-bar bg-success" style="width: 25%">25%</div>
</div>

Striped & Animated Progress

progress-bar-striped adds diagonal stripes. progress-bar-animated makes the stripes move (for active/in-progress tasks). Only use animation for currently-running tasks — static bars for completed. Update progress via JS by changing style.width and aria-valuenow. The animation uses CSS — no JS needed for the movement. Combine bg-* with striped for colored striped bars.

bootstrap
<!-- striped progress bar -->
<div class="progress">
  <div class="progress-bar progress-bar-striped" style="width: 50%">
    50%
  </div>
</div>

<!-- animated stripes (for in-progress tasks) -->
<div class="progress">
  <div class="progress-bar progress-bar-striped progress-bar-animated"
       style="width: 75%" role="progressbar">
    75%
  </div>
</div>

<!-- colored striped -->
<div class="progress">
  <div class="progress-bar progress-bar-striped bg-success" style="width: 100%">
    Complete
  </div>
</div>

<!-- update via JS -->
<div class="progress">
  <div class="progress-bar" id="myBar" style="width: 0%"></div>
</div>
<script>
const bar = document.getElementById('myBar')
bar.style.width = '60%'  // update progress
bar.setAttribute('aria-valuenow', '60')
</script>

Multiple Progress Bars

Multiple progress-bar divs inside one .progress container stack side by side — each takes its percentage of the total width. This is useful for showing composition (e.g., storage: 25% photos, 30% videos, 20% apps). The bars share the same height and sit adjacent without gaps. Total should ideally sum to 100% (or less for incomplete). Each can have its own color and label.

bootstrap
<!-- stacked bars in one container -->
<div class="progress">
  <div class="progress-bar" style="width: 15%">15%</div>
  <div class="progress-bar bg-success" style="width: 30%">30%</div>
  <div class="progress-bar bg-info" style="width: 20%">20%</div>
</div>
<!-- total: 65% filled, 35% empty -->

<!-- stacked with different colors -->
<div class="progress" style="height: 30px;">
  <div class="progress-bar bg-danger" style="width: 25%">Errors</div>
  <div class="progress-bar bg-warning" style="width: 25%">Warnings</div>
  <div class="progress-bar bg-success" style="width: 50%">Success</div>
</div>

<!-- each bar takes its percentage of the full width -->
<!-- they sit side by side, not overlapping -->

Placeholder Loading

Placeholders (skeleton loading) show gray blocks where content will appear — better than spinners for content-heavy pages. placeholder-glow creates a pulsing shimmer; placeholder-wave creates a moving wave. col-* sets the width of each block. For skeleton cards, use placeholder inside card components with aria-hidden='true'. This gives users a preview of the layout before data loads, reducing perceived wait time.

bootstrap
<!-- placeholder (gray animated blocks) -->
<div class="placeholder-glow">
  <span class="placeholder col-12"></span>
  <span class="placeholder col-12"></span>
  <span class="placeholder col-8"></span>
</div>

<!-- wave animation -->
<div class="placeholder-wave">
  <span class="placeholder col-12"></span>
</div>

<!-- placeholder card (skeleton loading) -->
<div class="card" aria-hidden="true">
  <div class="card-body">
    <h5 class="card-title placeholder-glow">
      <span class="placeholder col-6"></span>
    </h5>
    <p class="card-text placeholder-glow">
      <span class="placeholder col-7"></span>
      <span class="placeholder col-4"></span>
      <span class="placeholder col-4"></span>
      <span class="placeholder col-6"></span>
    </p>
    <a class="btn btn-primary disabled placeholder col-6"></a>
  </div>
</div>

Range Slider

form-range styles the native range input (slider). Set min, max, and step attributes for the range and increment. The slider is full-width by default. For a value display, use oninput to update a label as the user drags. Disabled greys it out. Range inputs are good for volume, opacity, or any continuous value where exact precision isn't needed. Always pair with a label for accessibility.

bootstrap
<!-- range input -->
<label for="customRange1" class="form-label">Example range</label>
<input type="range" class="form-range" id="customRange1" min="0" max="5" step="0.5">

<!-- with value display -->
<div class="d-flex">
  <input type="range" class="form-range me-2" id="range"
         min="0" max="100" oninput="document.getElementById('val').textContent = this.value">
  <span id="val" class="badge bg-primary">50</span>
</div>

<!-- disabled -->
<input type="range" class="form-range" disabled>

<!-- min, max, step -->
<input type="range" class="form-range" min="-10" max="10" step="2" value="0">
14

Utility Classes

Spacing (Margin & Padding)

Spacing utilities follow {property}{sides}-{size}. m = margin, p = padding. Sides: t/b (top/bottom), s/e (start/end — left/right in LTR), x (left+right), y (top+bottom), blank (all). Sizes 0-5 map to the spacing scale (0, 0.25rem, 0.5rem, 1rem, 1.5rem, 3rem). mx-auto centers block elements. Responsive: add breakpoint (mt-md-3). Bootstrap 5 renamed left/right to start/end for RTL support.

bootstrap
<!-- format: {property}{sides}-{size} -->
<!-- property: m (margin), p (padding) -->
<!-- sides: t (top), b (bottom), s (start/left), e (end/right), x (both), y (both), blank (all) -->
<!-- size: 0, 1, 2, 3, 4, 5, auto -->

<div class="mt-3">margin-top: 1rem</div>
<div class="mb-4">margin-bottom: 1.5rem</div>
<div class="ms-2">margin-left: 0.5rem (start)</div>
<div class="me-2">margin-right: 0.5rem (end)</div>
<div class="mx-auto">margin: 0 auto (centered)</div>
<div class="my-5">margin top+bottom: 3rem</div>

<div class="p-3">padding: 1rem (all sides)</div>
<div class="px-4">padding left+right: 1.5rem</div>
<div class="pt-2">padding-top: 0.5rem</div>

<!-- responsive: {property}{sides}-{breakpoint}-{size} -->
<div class="mt-0 mt-md-3">No margin on mobile, 1rem on md+</div>
<div class="p-2 p-lg-4">Small padding, larger on lg+</div>

Colors & Backgrounds

text-{color} sets text color; bg-{color} sets background. text-bg-{color} (Bootstrap 5.2+) automatically sets contrasting text color for the background. bg-gradient adds a subtle gradient. Opacity: text-opacity-50 or bg-opacity-50 (10%, 25%, 50%, 75%, 100%). text-muted is gray (secondary text). Always ensure sufficient contrast between text and background — use text-white on dark backgrounds. Colors carry semantic meaning (success=green, danger=red).

bootstrap
<!-- text colors -->
<p class="text-primary">Primary text</p>
<p class="text-success">Success text</p>
<p class="text-danger">Danger text</p>
<p class="text-muted">Muted text</p>
<p class="text-white bg-dark">White on dark</p>

<!-- background colors -->
<div class="bg-primary text-white">Primary bg</div>
<div class="bg-success text-white">Success bg</div>
<div class="bg-light">Light bg</div>
<div class="bg-dark text-white">Dark bg</div>

<!-- gradient background -->
<div class="bg-primary bg-gradient text-white p-3">
  Gradient background
</div>

<!-- opacity -->
<div class="text-primary text-opacity-50">50% opacity text</div>
<div class="bg-primary bg-opacity-50">50% opacity bg</div>

<!-- text color with background theme -->
<div class="text-bg-primary">Primary (auto text color)</div>

Display & Flexbox

d-* sets display property (d-none hides elements). Responsive d-{breakpoint}-* changes display at breakpoints — d-none d-md-block hides on mobile, shows on desktop. d-flex enables flexbox; flex-direction, justify-content-*, align-items-*, and flex-wrap control flex layout. These utilities replace most custom CSS for layout. d-md-flex activates flex only at md+. Combine for complex responsive layouts without media queries.

bootstrap
<!-- display utilities -->
<div class="d-none">display: none</div>
<div class="d-inline">display: inline</div>
<div class="d-inline-block">display: inline-block</div>
<div class="d-block">display: block</div>
<div class="d-flex">display: flex</div>
<div class="d-inline-flex">display: inline-flex</div>

<!-- responsive display -->
<div class="d-none d-md-block">Hidden on mobile, block on md+</div>
<div class="d-block d-md-none">Visible only below md</div>

<!-- flex direction -->
<div class="d-flex flex-column">Column</div>
<div class="d-flex flex-row-reverse">Reversed row</div>

<!-- justify content -->
<div class="d-flex justify-content-center">Center</div>
<div class="d-flex justify-content-between">Space between</div>
<div class="d-flex justify-content-around">Space around</div>

<!-- align items -->
<div class="d-flex align-items-center">Vertically centered</div>
<div class="d-flex align-items-end">Bottom aligned</div>

<!-- flex wrap -->
<div class="d-flex flex-wrap">Wrap</div>
<div class="d-flex flex-nowrap">No wrap</div>

Position & Floats

position-* sets CSS position. position-sticky with top-0 creates a sticky header (sticks when scrolled). top/bottom/start/end (0 or 50%) set insets. translate-middle centers elements (combine top-50 start-50 for perfect centering). Floats (float-start/end) are legacy — use flexbox for layout. clearfix clears floated children. fixed-top/fixed-bottom pin elements to the viewport. Sticky is preferred over fixed for navbars as it doesn't overlap content initially.

bootstrap
<!-- position -->
<div class="position-static">Static (default)</div>
<div class="position-relative">Relative (offset parent)</div>
<div class="position-absolute">Absolute</div>
<div class="position-fixed">Fixed (viewport)</div>
<div class="position-sticky top-0">Sticky (sticks at top)</div>

<!-- positioning with insets -->
<div class="position-absolute top-0 start-0">Top-left</div>
<div class="position-absolute top-0 end-0">Top-right</div>
<div class="position-absolute bottom-0 start-50">Bottom-center</div>

<!-- center with translate -->
<div class="position-absolute top-50 start-50 translate-middle">
  Perfectly centered
</div>

<!-- floats (use flexbox instead when possible) -->
<div class="float-start">Float left</div>
<div class="float-end">Float right</div>
<div class="clearfix">Clear floats</div>

<!-- fixed top/bottom -->
<div class="fixed-top">Pinned to top</div>
<div class="fixed-bottom">Pinned to bottom</div>

Borders, Shadows & Radius

border adds/removes borders on specific sides. border-{color} colors them; border-{1-5} sets width. rounded-* controls border-radius: rounded-0 (none), rounded (default), rounded-3 (larger), rounded-pill (capsule), rounded-circle (50%). rounded-{side} rounds only specific corners. shadow-* adds box shadows: none, sm, default, lg. Shadows add depth — use shadow-sm for subtle elevation, shadow-lg for modals/floating elements. These utilities handle most visual styling without custom CSS.

bootstrap
<!-- borders -->
<div class="border">All borders</div>
<div class="border-top">Top only</div>
<div class="border border-0">No borders</div>
<div class="border-top-0">Remove top</div>

<!-- border color -->
<div class="border border-primary">Primary border</div>
<div class="border border-danger">Danger border</div>

<!-- border width -->
<div class="border border-2">2px border</div>
<div class="border border-4">4px border</div>

<!-- border radius -->
<div class="rounded">Default radius</div>
<div class="rounded-0">No radius</div>
<div class="rounded-3">Larger radius</div>
<div class="rounded-pill">Pill shape</div>
<div class="rounded-circle">Circle</div>
<div class="rounded-top">Top corners only</div>

<!-- shadows -->
<div class="shadow-none">No shadow</div>
<div class="shadow-sm">Small shadow</div>
<div class="shadow">Regular shadow</div>
<div class="shadow-lg">Large shadow</div>
15

Images & Figures

Responsive Images

img-fluid makes images responsive (max-width: 100%, height: auto) — they never overflow their container. w-100 forces full width. figure/figure-img/figure-caption provide semantic image-with-caption (better than div + p). float-start/end aligns images; mx-auto d-block centers. rounded adds rounded corners. Always include alt text for accessibility — describe the image for screen readers. For decorative images, use alt=''.

bootstrap
<!-- responsive image (scales with parent) -->
<img src="photo.jpg" class="img-fluid" alt="Responsive image">
<!-- max-width: 100%; height: auto; -->

<!-- full-width image -->
<img src="banner.jpg" class="img-fluid w-100" alt="Banner">

<!-- image with figure -->
<figure class="figure">
  <img src="photo.jpg" class="figure-img img-fluid rounded" alt="...">
  <figcaption class="figure-caption text-end">
    A caption for the image.
  </figcaption>
</figure>

<!-- image alignment -->
<img src="..." class="rounded float-start" alt="Left">
<img src="..." class="rounded float-end" alt="Right">
<img src="..." class="rounded mx-auto d-block" alt="Centered">

<!-- center with text-center on parent -->
<div class="text-center">
  <img src="..." class="rounded" alt="Centered">
</div>

Image Shapes & Thumbnails

Image shapes: rounded (rounded corners), rounded-circle (circular — works best on square images), rounded-pill (capsule). img-thumbnail adds a border, padding, and rounded corners — gives a photo-print look. Combine img-thumbnail with rounded-circle for a circular thumbnail. For avatars, set explicit width/height to reserve space and prevent layout shift. The shape classes work on any element, not just images — useful for video thumbnails or profile pictures.

bootstrap
<!-- rounded corners -->
<img src="..." class="rounded" alt="Rounded">

<!-- fully rounded (circle) -->
<img src="..." class="rounded-circle" alt="Circle">

<!-- thumbnail (border + padding) -->
<img src="..." class="img-thumbnail" alt="Thumbnail">
<!-- adds padding, border, rounded corners -->

<!-- pill shape -->
<img src="..." class="rounded-pill" alt="Pill">

<!-- combination: thumbnail + circle -->
<img src="..." class="img-thumbnail rounded-circle" alt="Circle thumb">

<!-- avatar sizes (set width/height) -->
<img src="avatar.jpg" class="rounded-circle" width="40" height="40" alt="Avatar">
<img src="avatar.jpg" class="rounded-circle" width="100" height="100" alt="Large avatar">

Figure & Captions

figure is the semantic HTML element for images with captions — better for accessibility than divs. figure-img styles the image (combine with img-fluid and rounded). figure-caption styles the caption text (muted by default). text-start/center/end aligns the caption. Wrap the image in an <a> for clickable enlargements. Figures are the correct semantic choice for editorial content, documentation, and galleries.

bootstrap
<!-- figure with caption -->
<figure class="figure">
  <img src="photo.jpg" class="figure-img img-fluid rounded" alt="Photo">
  <figcaption class="figure-caption">
    Caption describing the image.
  </figcaption>
</figure>

<!-- right-aligned caption -->
<figure class="figure">
  <img src="photo.jpg" class="figure-img img-fluid rounded" alt="Photo">
  <figcaption class="figure-caption text-end">
    Right-aligned caption.
  </figcaption>
</figure>

<!-- centered caption -->
<figcaption class="figure-caption text-center">Centered</figcaption>

<!-- figure with link -->
<figure class="figure">
  <a href="full-size.jpg">
    <img src="thumb.jpg" class="figure-img img-fluid rounded" alt="Click to enlarge">
  </a>
  <figcaption class="figure-caption">Click to view full size</figcaption>
</figure>

Background Images

Bootstrap doesn't have background-image utilities (images are content-dependent), so use inline styles. background-size: cover fills the container without distortion; background-position: center focuses the center. For text over images, add a gradient overlay (dark at the bottom) for readability. position-relative on a wrapper with position-absolute layers lets you stack image, overlay, and text. This is the pattern for hero sections and image cards.

bootstrap
<!-- background image via inline style -->
<div style="background-image: url('bg.jpg');
            background-size: cover;
            background-position: center;
            height: 400px;">
  <div class="d-flex align-items-center justify-content-center h-100">
    <h1 class="text-white">Hero text over image</h1>
  </div>
</div>

<!-- parallax-like fixed background -->
<div style="background-image: url('bg.jpg');
            background-attachment: fixed;
            background-size: cover;
            height: 300px;">
</div>

<!-- gradient overlay for text readability -->
<div class="position-relative">
  <img src="bg.jpg" class="img-fluid" alt="">
  <div class="position-absolute top-0 start-0 w-100 h-100"
       style="background: linear-gradient(to bottom, transparent, rgba(0,0,0,0.7));">
  </div>
  <div class="position-absolute bottom-0 start-0 p-4 text-white">
    <h2>Text over gradient</h2>
  </div>
</div>

Image Grid & Gallery

Image grids use the standard row/col system with g-* for gutters. For equal-height images regardless of aspect ratio, set a fixed height container and use object-fit: cover on the img with w-100 h-100. row-cols-* creates equal-width columns without specifying col-* on each item. For true masonry (variable heights, no gaps), CSS columns or JavaScript libraries are needed — Bootstrap's grid doesn't do masonry natively. img-fluid ensures images never overflow their grid cells.

bootstrap
<!-- responsive image grid -->
<div class="row g-2">
  <div class="col-6 col-md-4">
    <img src="1.jpg" class="img-fluid rounded" alt="">
  </div>
  <div class="col-6 col-md-4">
    <img src="2.jpg" class="img-fluid rounded" alt="">
  </div>
  <div class="col-6 col-md-4">
    <img src="3.jpg" class="img-fluid rounded" alt="">
  </div>
  <div class="col-6 col-md-4">
    <img src="4.jpg" class="img-fluid rounded" alt="">
  </div>
</div>

<!-- equal-height images with object-fit -->
<div class="row g-3">
  <div class="col-md-4">
    <div style="height: 200px;">
      <img src="tall.jpg" class="w-100 h-100"
           style="object-fit: cover;" alt="">
    </div>
  </div>
</div>

<!-- masonry-like with columns -->
<div class="row row-cols-1 row-cols-md-3 g-3">
  <div class="col"><img src="1.jpg" class="img-fluid rounded"></div>
  <div class="col"><img src="2.jpg" class="img-fluid rounded"></div>
</div>
17

Collapse & Accordion

Basic Collapse

Collapse toggles element visibility with a height animation. Trigger: data-bs-toggle='collapse' with data-bs-target (button) or href (anchor) pointing to the collapsible element's id. .collapse is hidden; .collapse.show is visible. aria-expanded on the trigger reflects state; aria-controls links trigger to target. Use buttons for actions, anchors for navigation. The animation smoothly transitions height from 0 to auto.

bootstrap
<!-- collapse trigger (button) -->
<p>
  <button class="btn btn-primary" type="button"
          data-bs-toggle="collapse" data-bs-target="#collapseExample"
          aria-expanded="false" aria-controls="collapseExample">
    Toggle collapse
  </button>
</p>
<div class="collapse" id="collapseExample">
  <div class="card card-body">
    This content is hidden by default and shown when the button is clicked.
  </div>
</div>

<!-- collapse via anchor -->
<a class="btn btn-primary" data-bs-toggle="collapse"
   href="#collapseExample2" role="button" aria-expanded="false"
   aria-controls="collapseExample2">
  Link trigger
</a>
<div class="collapse" id="collapseExample2">
  <div class="card card-body">Content</div>
</div>

<!-- shown by default -->
<div class="collapse show" id="alwaysShown">
  <div class="card card-body">Shown initially</div>
</div>

Accordion

The accordion is a group of collapses where data-bs-parent ensures only one is open at a time (opening one closes others). Structure: accordion > accordion-item > accordion-header (with accordion-button) + accordion-collapse (with accordion-body). The first item can have 'show' class to start expanded. 'collapsed' class on the button indicates the collapsed state (rotates the arrow). aria-expanded and aria-controls are essential for accessibility. The parent attribute creates the exclusive-open behavior.

bootstrap
<div class="accordion" id="accordionExample">
  <div class="accordion-item">
    <h2 class="accordion-header" id="headingOne">
      <button class="accordion-button" type="button"
              data-bs-toggle="collapse" data-bs-target="#collapseOne"
              aria-expanded="true" aria-controls="collapseOne">
        Accordion Item #1
      </button>
    </h2>
    <div id="collapseOne" class="accordion-collapse collapse show"
         aria-labelledby="headingOne" data-bs-parent="#accordionExample">
      <div class="accordion-body">
        <strong>First item content.</strong> Shown by default.
      </div>
    </div>
  </div>

  <div class="accordion-item">
    <h2 class="accordion-header" id="headingTwo">
      <button class="accordion-button collapsed" type="button"
              data-bs-toggle="collapse" data-bs-target="#collapseTwo"
              aria-expanded="false" aria-controls="collapseTwo">
        Accordion Item #2
      </button>
    </h2>
    <div id="collapseTwo" class="accordion-collapse collapse"
         aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
      <div class="accordion-body">
        <strong>Second item content.</strong>
      </div>
    </div>
  </div>
</div>

Accordion Flush & Variants

accordion-flush removes the outer borders and background — for integration into cards or containers without double borders. Without data-bs-parent on the collapse items, multiple sections can stay open simultaneously (always-open mode). With data-bs-parent, opening one closes the others (standard accordion). Choose based on UX: exclusive (one open) for FAQs where users read one at a time; multi-open for navigation or settings where users may compare sections.

bootstrap
<!-- flush (no borders, no background) -->
<div class="accordion accordion-flush" id="accordionFlush">
  <div class="accordion-item">
    <h2 class="accordion-header">
      <button class="accordion-button collapsed" data-bs-toggle="collapse"
              data-bs-target="#flush1">
        Item #1
      </button>
    </h2>
    <div id="flush1" class="accordion-collapse collapse">
      <div class="accordion-body">Flush content</div>
    </div>
  </div>
</div>
<!-- flush removes outer borders and background -->

<!-- always-open (no exclusive behavior) -->
<div class="accordion" id="accordionAlwaysOpen">
  <div class="accordion-item">
    <h2 class="accordion-header">
      <button class="accordion-button" data-bs-toggle="collapse"
              data-bs-target="#open1">
        Item #1
      </button>
    </h2>
    <div id="open1" class="accordion-collapse collapse show">
      <!-- no data-bs-parent = stays open when others open -->
      <div class="accordion-body">Content</div>
    </div>
  </div>
</div>

Collapse via JavaScript

Control collapse via JS: new bootstrap.Collapse(el, options). toggle: false prevents auto-toggling on init. parent: selector creates accordion behavior (closing siblings). Methods: show(), hide(), toggle(), dispose(). Events: show/shown/hide/hidden.bs.collapse — use shown/hidden (past tense) for post-animation actions like focusing an input or updating aria. This is useful for programmatic control, like expanding a section based on URL hash or form state.

bootstrap
<div class="collapse" id="jsCollapse">
  <div class="card card-body">Content</div>
</div>

<script>
const collapseEl = document.getElementById('jsCollapse')
const collapse = new bootstrap.Collapse(collapseEl, {
  toggle: false,      // don't toggle on init
  parent: null        // or selector for accordion behavior
})

// methods
collapse.show()
collapse.hide()
collapse.toggle()

// dispose (removes instance)
collapse.dispose()

// events
collapseEl.addEventListener('show.bs.collapse', () => {
  console.log('about to show')
})
collapseEl.addEventListener('shown.bs.collapse', () => {
  console.log('fully shown')
})
collapseEl.addEventListener('hide.bs.collapse', () => {
  console.log('about to hide')
})
collapseEl.addEventListener('hidden.bs.collapse', () => {
  console.log('fully hidden')
})
</script>

Multi-Target Collapse

data-bs-target accepts CSS selectors, so one trigger can toggle multiple elements — use a shared class (.multi-collapse) to target them all. Conversely, multiple triggers can control the same target (each has data-bs-target='#shared'). This is useful for 'Show all' / 'Hide all' buttons that control multiple sections, or for responsive layouts where different buttons control the same panel from different locations. The selector approach is very flexible.

bootstrap
<!-- one trigger toggles multiple targets -->
<p>
  <button class="btn btn-primary" data-bs-toggle="collapse"
          data-bs-target=".multi-collapse" aria-expanded="false">
    Toggle both
  </button>
</p>

<div class="row">
  <div class="col">
    <div class="collapse multi-collapse" id="first">
      <div class="card card-body">First block</div>
    </div>
  </div>
  <div class="col">
    <div class="collapse multi-collapse" id="second">
      <div class="card card-body">Second block</div>
    </div>
  </div>
</div>

<!-- multiple triggers control one target -->
<button class="btn btn-primary" data-bs-toggle="collapse"
        data-bs-target="#shared">Show A</button>
<button class="btn btn-secondary" data-bs-toggle="collapse"
        data-bs-target="#shared">Show B</button>
<div class="collapse" id="shared">
  <div class="card card-body">Shared content</div>
</div>
19

Icons & Accessibility

Bootstrap Icons

Bootstrap Icons is a separate icon library (not included in the CSS). Include the CSS, then use <i class='bi bi-{name}'></i>. Size icons with fs-* (font-size) utilities. Color them with text-* classes. Icons in buttons improve UX with visual cues. Icon-only buttons MUST have aria-label for screen readers — without it, the button has no accessible name. Browse icons at icons.getbootstrap.com. Icons are SVG fonts, so they scale crisply at any size.

bootstrap
<!-- Bootstrap Icons (separate from CSS framework) -->
<link rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">

<!-- usage: <i class="bi bi-{name}"></i> -->
<i class="bi bi-alarm"></i>
<i class="bi bi-arrow-right"></i>
<i class="bi bi-check-circle"></i>
<i class="bi bi-github"></i>

<!-- sizing with utility classes -->
<i class="bi bi-star fs-1"></i>  <!-- font-size 1 -->
<i class="bi bi-star fs-3"></i>
<i class="bi bi-star fs-6"></i>

<!-- colored icons -->
<i class="bi bi-exclamation-triangle text-warning fs-3"></i>
<i class="bi bi-check-circle text-success fs-3"></i>

<!-- icon in a button -->
<button class="btn btn-primary">
  <i class="bi bi-download"></i> Download
</button>

<!-- icon-only button (needs aria-label) -->
<button class="btn btn-link" aria-label="Search">
  <i class="bi bi-search"></i>
</button>

Accessibility: ARIA & Roles

Bootstrap components include ARIA attributes, but you must add them for custom controls. aria-label provides names for icon-only buttons. aria-labelledby references another element's id as a label. aria-describedby points to help text. aria-expanded reflects toggle state (true/false). aria-current='page' marks the current nav item. Use semantic HTML (nav, main, aside, header, footer) which have implicit ARIA roles — better than adding role attributes to divs.

bootstrap
<!-- landmark roles (use semantic HTML) -->
<nav role="navigation">...</nav>
<main role="main">...</main>
<aside role="complementary">...</aside>
<footer role="contentinfo">...</footer>

<!-- aria-label for unlabeled controls -->
<button aria-label="Close" class="btn-close"></button>
<button aria-label="Search" class="btn"><i class="bi bi-search"></i></button>

<!-- aria-labelledby (reference another element) -->
<div role="dialog" aria-labelledby="modalTitle">
  <h2 id="modalTitle">Modal Heading</h2>
</div>

<!-- aria-describedby (descriptive text) -->
<input aria-describedby="emailHelp" id="email">
<small id="emailHelp">We'll never share your email.</small>

<!-- aria-expanded for toggles -->
<button aria-expanded="false" data-bs-toggle="collapse">
  Toggle
</button>

<!-- aria-current for current page -->
<a aria-current="page" class="nav-link active">Home</a>

Visually Hidden & Screen Reader

visually-hidden hides content visually but keeps it for screen readers — use for labels on icon-only buttons or extra context. visually-hidden-focusable is hidden until keyboard focus (perfect for 'Skip to main content' links that appear when Tabbed to). aria-hidden='true' does the opposite: visible but not announced (for decorative elements). Always provide a skip link as the first focusable element for keyboard accessibility. These are essential for WCAG compliance.

bootstrap
<!-- visually hidden (hidden visually, read by screen readers) -->
<span class="visually-hidden">This text is read by screen readers</span>

<!-- for icon-only buttons -->
<button class="btn btn-primary" aria-label="Search">
  <i class="bi bi-search"></i>
  <span class="visually-hidden">Search</span>
</button>

<!-- skip link (keyboard users skip to content) -->
<a href="#main-content" class="visually-hidden-focusable">
  Skip to main content
</a>

<!-- visually-hidden-focusable: hidden until focused -->
<!-- useful for skip links that appear on Tab -->

<!-- main content target -->
<main id="main-content">
  <!-- skip link jumps here -->
</main>

<!-- hide from screen readers only (still visible) -->
<div aria-hidden="true">
  Decorative content (not announced)
</div>

Reduced Motion & Dark Mode

Dark mode: set data-bs-theme='dark' on <html> — Bootstrap swaps CSS variables. Toggle via JS by changing the attribute. Respect system preference with matchMedia('(prefers-color-scheme: dark)'). For reduced motion, Bootstrap automatically disables most animations when the user has prefers-reduced-motion: reduce set. You can add more rules to further reduce motion. Store the user's theme preference in localStorage. These features make your site accessible and comfortable for all users.

bootstrap
<!-- dark mode via data attribute -->
<html data-bs-theme="dark">
  <!-- entire page in dark mode -->
</html>

<!-- toggle dark mode via JS -->
<button id="themeToggle">Toggle theme</button>
<script>
const html = document.documentElement
const current = html.getAttribute('data-bs-theme')
html.setAttribute('data-bs-theme', current === 'dark' ? 'light' : 'dark')
</script>

<!-- respect system preference -->
<script>
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
  document.documentElement.setAttribute('data-bs-theme', 'dark')
}
</script>

<!-- reduced motion: Bootstrap respects this automatically -->
@media (prefers-reduced-motion: reduce) {
  /* Bootstrap disables transitions and animations */
  /* but you can add more */
  .carousel {
    transition: none;
  }
}

Focus Management & Keyboard

Never remove focus outlines (outline: none) without providing an alternative — keyboard users need them. Bootstrap's focus-visible shows the ring only for keyboard focus, not mouse. For modals, move focus into the modal on show and return to the trigger on hide. Custom widgets (tabs, menus) need arrow key navigation — implement keydown handlers for Arrow keys, Enter, Space, and Escape. Proper tabindex management (0 for focusable, -1 for programmatically focusable) ensures logical tab order. This is essential for keyboard-only users.

bootstrap
<!-- visible focus (don't remove outlines!) -->
<!-- Bootstrap provides focus-visible styling -->
<button class="btn btn-primary">Has visible focus ring</button>

<!-- skip link (first focusable element) -->
<body>
  <a href="#main" class="visually-hidden-focusable">Skip to content</a>
  <nav>...</nav>
  <main id="main">...</main>
</body>

<!-- focus management for modals -->
<script>
const modal = document.getElementById('myModal')
modal.addEventListener('shown.bs.modal', () => {
  // focus first input in modal
  modal.querySelector('input')?.focus()
})
modal.addEventListener('hidden.bs.modal', () => {
  // return focus to trigger
  document.getElementById('openModalBtn').focus()
})
</script>

<!-- keyboard navigation for custom widgets -->
<div role="tablist">
  <button role="tab" tabindex="0">Tab 1</button>
  <button role="tab" tabindex="-1">Tab 2</button>
</div>
<script>
// arrow key navigation
document.querySelectorAll('[role="tab"]').forEach((tab, i, tabs) => {
  tab.addEventListener('keydown', (e) => {
    if (e.key === 'ArrowRight') tabs[(i + 1) % tabs.length].focus()
    if (e.key === 'ArrowLeft') tabs[(i - 1 + tabs.length) % tabs.length].focus()
  })
})
</script>
20

List Group

Basic List Group

list-group is a flexible component for displaying a series of content. list-group-item styles each entry. active marks the current item; disabled greys it out (use aria-disabled='true' for accessibility). List groups are more semantic than styled divs for lists of related content like dashboards, settings, or inbox items. Use <ul>/<li> for navigation lists, or <div> for non-list content.

bootstrap
<!-- basic list group -->
<ul class="list-group">
  <li class="list-group-item">An item</li>
  <li class="list-group-item">A second item</li>
  <li class="list-group-item">A third item</li>
  <li class="list-group-item">A fourth item</li>
  <li class="list-group-item">A disabled item</li>
</ul>

<!-- with active item -->
<ul class="list-group">
  <li class="list-group-item active" aria-current="true">Active item</li>
  <li class="list-group-item">Regular item</li>
</ul>

<!-- disabled item -->
<ul class="list-group">
  <li class="list-group-item disabled" aria-disabled="true">Disabled</li>
</ul>

List Group with Links & Buttons

For actionable list items (clickable), use list-group-item-action on <a> or <button> elements. This adds hover and focus states. Use <a> for navigation (with href) and <button type='button'> for actions. Disable links with the .disabled class (and aria-disabled); disable buttons with the disabled attribute. Avoid using <li> for actionable items — use <div> as the list-group wrapper instead, since <a>/<button> inside <ul> is less semantic.

bootstrap
<!-- list group with links (actionable items) -->
<div class="list-group">
  <a href="#" class="list-group-item list-group-item-action active"
     aria-current="true">
    Active link item
  </a>
  <a href="#" class="list-group-item list-group-item-action">Link item</a>
  <a href="#" class="list-group-item list-group-item-action">Another link</a>
  <a class="list-group-item list-group-item-action disabled">Disabled link</a>
</div>

<!-- with buttons instead of links -->
<div class="list-group">
  <button type="button" class="list-group-item list-group-item-action active">
    Button item
  </button>
  <button type="button" class="list-group-item list-group-item-action">
    Another button
  </button>
</div>

<!-- note: use list-group-item-action for hover/focus states -->

List Group Flush & Numbered

list-group-flush removes borders and rounded corners — for embedding inside cards or containers without double borders. list-group-numbered (on <ol>) adds automatic numbering via CSS counters. list-group-horizontal makes items sit side by side; responsive variants (list-group-horizontal-md) switch to horizontal at a breakpoint. Horizontal lists are useful for small navigation or filter bars. Numbered lists are great for step-by-step instructions or rankings.

bootstrap
<!-- flush (no borders, no background, edge-to-edge) -->
<ul class="list-group list-group-flush">
  <li class="list-group-item">Flush item 1</li>
  <li class="list-group-item">Flush item 2</li>
  <li class="list-group-item">Flush item 3</li>
</ul>
<!-- removes outer borders and rounded corners -->

<!-- numbered list group (ordered) -->
<ol class="list-group list-group-numbered">
  <li class="list-group-item">First item</li>
  <li class="list-group-item">Second item</li>
  <li class="list-group-item">Third item</li>
</ol>
<!-- CSS counters add the numbers automatically -->

<!-- horizontal list group -->
<ul class="list-group list-group-horizontal">
  <li class="list-group-item">Item 1</li>
  <li class="list-group-item">Item 2</li>
  <li class="list-group-item">Item 3</li>
</ul>

<!-- responsive horizontal -->
<ul class="list-group list-group-horizontal-md">
  <!-- horizontal at md+, vertical below -->
</ul>

List Group Contextual Colors & Badges

Contextual classes (list-group-item-{color}) tint items for status indication — use meaningfully (success=done, danger=error). For badges inside list items, use d-flex justify-content-between align-items-center to push the badge to the right and vertically center it. rounded-pill badges look modern for counts. This pattern is the classic inbox/notification list: label on left, count on right. The flex utilities handle alignment without custom CSS.

bootstrap
<!-- contextual color items -->
<ul class="list-group">
  <li class="list-group-item list-group-item-primary">Primary item</li>
  <li class="list-group-item list-group-item-secondary">Secondary</li>
  <li class="list-group-item list-group-item-success">Success</li>
  <li class="list-group-item list-group-item-danger">Danger</li>
  <li class="list-group-item list-group-item-warning">Warning</li>
  <li class="list-group-item list-group-item-info">Info</li>
  <li class="list-group-item list-group-item-light">Light</li>
  <li class="list-group-item list-group-item-dark">Dark</li>
</ul>

<!-- with badges (counts aligned right) -->
<ul class="list-group">
  <li class="list-group-item d-flex justify-content-between align-items-center">
    Messages
    <span class="badge bg-primary rounded-pill">14</span>
  </li>
  <li class="list-group-item d-flex justify-content-between align-items-center">
    Notifications
    <span class="badge bg-danger rounded-pill">3</span>
  </li>
  <li class="list-group-item d-flex justify-content-between align-items-center">
    Spam
    <span class="badge bg-warning text-dark rounded-pill">1</span>
  </li>
</ul>

List Group Custom Content

List items can contain rich content: headings (fw-bold), descriptions (text-muted), metadata (small), and badges. Use d-flex justify-content-between align-items-start to lay out content and badge. me-auto pushes the badge to the right. For checkboxes/radios in list items, use form-check-input with a matching form-check-label — this creates selectable lists. Common use cases: email lists, settings panels, and search results where each item has a title, description, and status.

bootstrap
<!-- rich content list item -->
<ul class="list-group">
  <li class="list-group-item d-flex justify-content-between align-items-start">
    <div class="ms-2 me-auto">
      <div class="fw-bold">Subheading</div>
      <p class="mb-0 text-muted">Descriptive text for the list item.</p>
      <small>Additional metadata</small>
    </div>
    <span class="badge bg-primary rounded-pill">New</span>
  </li>
  <li class="list-group-item d-flex justify-content-between align-items-start">
    <div class="ms-2 me-auto">
      <div class="fw-bold">Another item</div>
      <p class="mb-0">More content here.</p>
    </div>
    <span class="badge bg-secondary rounded-pill">3</span>
  </li>
</ul>

<!-- list group with checkbox/radio -->
<ul class="list-group">
  <li class="list-group-item">
    <input class="form-check-input me-1" type="checkbox" id="check1">
    <label class="form-check-label" for="check1">Checkbox item</label>
  </li>
  <li class="list-group-item">
    <input class="form-check-input me-1" type="radio" name="radio" id="radio1">
    <label class="form-check-label" for="radio1">Radio item</label>
  </li>
</ul>

Was this helpful?

Learning path

Learn from scratch

Learn this language from the ground up with structured lessons.