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.
<!-- 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 JSContainers
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.
<!-- 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 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.
<!-- 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.
: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>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.
<!-- .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.
<!-- 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.
<!-- 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.
<!-- 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'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>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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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 -->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.
<!-- 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.
<!-- 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.
<!-- 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 (< for <). Blockquotes can be aligned with text-center/text-end. The kbd styling gives keys a keyboard-like appearance.
<!-- 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><section></code> tag.</p>
<!-- code block -->
<pre><code><div class="container">
Hello
</div>
</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.
<!-- 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>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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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>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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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 -->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.
<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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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>Modals
Basic Modal
Modals are triggered by data-bs-toggle='modal' with data-bs-target matching the modal's id. Structure: modal > modal-dialog > modal-content > modal-header/body/footer. btn-close is the X button (data-bs-dismiss='modal'). fade adds a transition. tabindex='-1' and aria-hidden are for accessibility. The modal is hidden by default and positioned with fixed positioning. Bootstrap JS handles showing, hiding, backdrop, and focus management.
<!-- Button to trigger -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal"
data-bs-target="#exampleModal">
Launch demo modal
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1"
aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body">
Modal body text goes here.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>Modal Sizing & Scrolling
Modal sizes: modal-sm, default, modal-lg, modal-xl, modal-fullscreen. modal-fullscreen-{breakpoint}-down is fullscreen only below that breakpoint. modal-dialog-scrollable makes the body scroll (header/footer stay fixed) — essential for long content. modal-dialog-centered vertically centers the modal. These classes go on modal-dialog. Combine them: modal-dialog modal-dialog-centered modal-dialog-scrollable for a centered, scrollable modal.
<!-- sizes -->
<div class="modal-dialog modal-sm">Small</div>
<div class="modal-dialog">Default</div>
<div class="modal-dialog modal-lg">Large</div>
<div class="modal-dialog modal-xl">Extra large</div>
<div class="modal-dialog modal-fullscreen">Fullscreen</div>
<!-- responsive fullscreen -->
<div class="modal-dialog modal-fullscreen-sm-down">
<!-- fullscreen below sm, normal above -->
</div>
<!-- scrollable long content -->
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-body">
<!-- long content scrolls within modal -->
</div>
</div>
</div>
<!-- vertically centered -->
<div class="modal-dialog modal-dialog-centered">
<!-- centered vertically in viewport -->
</div>Modal via JavaScript
Control modals via JS: create an instance with new bootstrap.Modal(el), then show()/hide()/toggle(). backdrop: 'static' prevents closing on backdrop click; keyboard: false disables Esc. Events: show.bs.modal, shown.bs.modal, hide.bs.modal, hidden.bs.modal (show = starting, shown = finished). Use shown.bs.modal to focus an input or load dynamic content. The static backdrop is useful for forms where accidental closure loses data.
<!-- HTML modal (hidden) -->
<div class="modal fade" id="myModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">Dynamic content here</div>
</div>
</div>
</div>
<script>
// create modal instance
const modalEl = document.getElementById('myModal')
const modal = new bootstrap.Modal(modalEl)
// show
modal.show()
// hide
modal.hide()
// toggle
modal.toggle()
// handle events
modalEl.addEventListener('shown.bs.modal', () => {
console.log('modal fully shown')
})
modalEl.addEventListener('hidden.bs.modal', () => {
console.log('modal fully hidden')
})
// pass options
const modal = new bootstrap.Modal(modalEl, {
backdrop: 'static', // don't close on backdrop click
keyboard: false // don't close on Esc
})
</script>Tooltips & Popovers
Tooltips show on hover/focus; popovers show on click. Both need JS initialization (Bootstrap doesn't auto-init for performance). data-bs-placement sets position (top/bottom/left/right). Popovers have title + content; data-bs-html='true' allows HTML. data-bs-trigger='focus' makes popovers dismissible (click outside). Tooltips and popovers use Popper.js (included in the bundle) for smart positioning. They're ideal for hints, definitions, and contextual info.
<!-- tooltip (hover/focus) -->
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip"
data-bs-placement="top" title="Tooltip on top">
Hover me
</button>
<!-- positions: top, bottom, left, right -->
<button data-bs-toggle="tooltip" data-bs-placement="right"
title="Right tooltip">Right</button>
<!-- popover (click) -->
<button type="button" class="btn btn-lg btn-danger" data-bs-toggle="popover"
title="Popover title" data-bs-content="Content here">
Click to toggle popover
</button>
<!-- popover with HTML content -->
<button data-bs-toggle="popover" data-bs-html="true"
data-bs-content="<b>Bold</b> content">HTML popover</button>
<!-- dismissible popover -->
<button data-bs-toggle="popover" data-bs-trigger="focus"
title="Dismissible">Click outside to dismiss</button>
<script>
// MUST initialize tooltips and popovers
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
tooltipTriggerList.forEach(t => new bootstrap.Tooltip(t))
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
popoverTriggerList.forEach(p => new bootstrap.Popover(p))
</script>Offcanvas (Drawer)
Offcanvas is a slide-in drawer (like a modal but from the edge). offcanvas-start/end/top/bottom sets the slide direction. Structure mirrors modal: offcanvas-header/body with btn-close. data-bs-backdrop='static' prevents closing on backdrop click. text-bg-dark for dark variant. Offcanvas is ideal for mobile navigation, filters, or side panels. Unlike modals, it doesn't cover the full screen — it's an overlay drawer. Replace navbar-collapse with offcanvas for modern mobile menus.
<!-- trigger button -->
<button class="btn btn-primary" data-bs-toggle="offcanvas"
data-bs-target="#demoOffcanvas">
Open offcanvas
</button>
<!-- offcanvas drawer -->
<div class="offcanvas offcanvas-start" tabindex="-1" id="demoOffcanvas">
<div class="offcanvas-header">
<h5 class="offcanvas-title">Title</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<p>Content for the offcanvas.</p>
</div>
</div>
<!-- positions: start (left), end (right), top, bottom -->
<div class="offcanvas offcanvas-end" id="rightDrawer">Right side</div>
<div class="offcanvas offcanvas-top" id="topDrawer">Top</div>
<!-- dark variant -->
<div class="offcanvas offcanvas-start text-bg-dark" id="darkDrawer">
Dark offcanvas
</div>
<!-- static backdrop (no close on click outside) -->
<div class="offcanvas" data-bs-backdrop="static">...</div>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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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>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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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>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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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">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.
<!-- 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).
<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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>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=''.