All skills
wshaddix avatar

/bootstrap5-ui

@5724d98

Production-grade Bootstrap 5.3 patterns for building modern, responsive web UIs in HTML and ASP.NET Core Razor Pages/Views. Use when creating or styling web pages, layouts, navigation, forms, cards, modals, tables, or any UI component with Bootstrap 5. Covers the grid system, responsive breakpoints, utility classes, color modes (dark/light), accessibility, and integration with ASP.NET Core tag helpers and Razor syntax. Trigger on any task involving Bootstrap CSS classes, responsive HTML layouts, Razor Page UI design, or front-end styling for .NET web applications.

Use this Skill: https://skilld.dev/gh/wshaddix/dotnet-skills/bootstrap5-ui

This session only. Nothing lands on disk.

referencescomponents.md

≈5.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Bootstrap 5 Component Patterns

Table of Contents

Modals

Standard Modal

<!-- Trigger -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
    Open 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">
                <p>Modal body content here.</p>
            </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 Sizes

  • .modal-sm — 300px max-width
  • Default — 500px max-width
  • .modal-lg — 800px max-width
  • .modal-xl — 1140px max-width
  • .modal-fullscreen — full viewport
<div class="modal-dialog modal-lg">...</div>
<div class="modal-dialog modal-fullscreen-md-down">...</div> <!-- Fullscreen below md -->

Scrollable Modal with Static Backdrop

<div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
    <div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
        <div class="modal-content">...</div>
    </div>
</div>

Modal via JavaScript

const modal = new bootstrap.Modal(document.getElementById('myModal'));
modal.show();
// modal.hide();
// modal.toggle();

// Listen for events
document.getElementById('myModal').addEventListener('hidden.bs.modal', () => {
    // Cleanup after close
});

Alerts and Toasts

Dismissible Alert

<div class="alert alert-success alert-dismissible fade show" role="alert">
    <strong>Success!</strong> Your changes have been saved.
    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>

<!-- Alert variants: alert-primary, alert-secondary, alert-success, alert-danger,
     alert-warning, alert-info, alert-light, alert-dark -->

<!-- Alert with icon and additional content -->
<div class="alert alert-warning d-flex align-items-center" role="alert">
    <svg class="bi flex-shrink-0 me-2" width="24" height="24"><use xlink:href="#exclamation-triangle-fill"/></svg>
    <div>
        <h6 class="alert-heading mb-1">Warning!</h6>
        An example alert with an icon and heading.
    </div>
</div>

Toast Notifications

<!-- Toast container — position fixed in corner -->
<div class="toast-container position-fixed bottom-0 end-0 p-3" style="z-index: 11">
    <div id="successToast" class="toast align-items-center text-bg-success border-0" role="alert"
         aria-live="assertive" aria-atomic="true">
        <div class="d-flex">
            <div class="toast-body">Record saved successfully.</div>
            <button type="button" class="btn-close btn-close-white me-2 m-auto"
                    data-bs-dismiss="toast" aria-label="Close"></button>
        </div>
    </div>
</div>

Show toast via JS:

const toast = new bootstrap.Toast(document.getElementById('successToast'));
toast.show();

Toast with Header

<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
    <div class="toast-header">
        <strong class="me-auto">Notification</strong>
        <small class="text-body-secondary">just now</small>
        <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
    </div>
    <div class="toast-body">Your order has been placed.</div>
</div>

Accordion

<div class="accordion" id="faqAccordion">
    <div class="accordion-item">
        <h2 class="accordion-header">
            <button class="accordion-button" type="button"
                    data-bs-toggle="collapse" data-bs-target="#collapseOne"
                    aria-expanded="true" aria-controls="collapseOne">
                Question #1
            </button>
        </h2>
        <div id="collapseOne" class="accordion-collapse collapse show"
             data-bs-parent="#faqAccordion">
            <div class="accordion-body">Answer to question #1.</div>
        </div>
    </div>
    <div class="accordion-item">
        <h2 class="accordion-header">
            <button class="accordion-button collapsed" type="button"
                    data-bs-toggle="collapse" data-bs-target="#collapseTwo"
                    aria-expanded="false" aria-controls="collapseTwo">
                Question #2
            </button>
        </h2>
        <div id="collapseTwo" class="accordion-collapse collapse"
             data-bs-parent="#faqAccordion">
            <div class="accordion-body">Answer to question #2.</div>
        </div>
    </div>
</div>

Flush variant: Add .accordion-flush to remove borders and rounded corners.

Always-open: Remove data-bs-parent to allow multiple items open simultaneously.

Offcanvas

<!-- Trigger -->
<button class="btn btn-primary" type="button" data-bs-toggle="offcanvas"
        data-bs-target="#sidePanel" aria-controls="sidePanel">
    Open Filters
</button>

<!-- Offcanvas -->
<div class="offcanvas offcanvas-start" tabindex="-1" id="sidePanel"
     aria-labelledby="sidePanelLabel">
    <div class="offcanvas-header">
        <h5 class="offcanvas-title" id="sidePanelLabel">Filters</h5>
        <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
    </div>
    <div class="offcanvas-body">
        <p>Filter content here.</p>
    </div>
</div>

Placement: offcanvas-start (left), offcanvas-end (right), offcanvas-top, offcanvas-bottom

Responsive: offcanvas-lg — offcanvas below lg breakpoint, visible inline above lg.

Buttons and Button Groups

Button Variants

<!-- Solid -->
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-success">Success</button>
<button class="btn btn-danger">Danger</button>
<button class="btn btn-warning">Warning</button>
<button class="btn btn-info">Info</button>

<!-- Outline -->
<button class="btn btn-outline-primary">Outline Primary</button>

<!-- Sizes -->
<button class="btn btn-primary btn-lg">Large</button>
<button class="btn btn-primary btn-sm">Small</button>

<!-- Loading state -->
<button class="btn btn-primary" type="button" disabled>
    <span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
    <span role="status">Loading...</span>
</button>

Button Group

<div class="btn-group" role="group" aria-label="Actions">
    <button type="button" class="btn btn-outline-primary">View</button>
    <button type="button" class="btn btn-outline-primary">Edit</button>
    <button type="button" class="btn btn-outline-danger">Delete</button>
</div>

Button Toolbar

<div class="btn-toolbar justify-content-between" role="toolbar" aria-label="Toolbar with button groups">
    <div class="btn-group" role="group">
        <button type="button" class="btn btn-outline-secondary">Bold</button>
        <button type="button" class="btn btn-outline-secondary">Italic</button>
    </div>
    <div class="input-group">
        <div class="input-group-text">@</div>
        <input type="text" class="form-control" placeholder="Username">
    </div>
</div>

Badges

<!-- Inline badge -->
<h4>Messages <span class="badge text-bg-primary">4</span></h4>

<!-- Pill badge -->
<span class="badge rounded-pill text-bg-success">Active</span>
<span class="badge rounded-pill text-bg-danger">Expired</span>
<span class="badge rounded-pill text-bg-warning">Pending</span>

<!-- Badge in 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 class="visually-hidden">unread messages</span>
    </span>
</button>

Progress and Spinners

Progress Bars

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

<!-- Striped and animated -->
<div class="progress">
    <div class="progress-bar progress-bar-striped progress-bar-animated bg-success" style="width: 50%"></div>
</div>

<!-- Stacked -->
<div class="progress-stacked">
    <div class="progress" role="progressbar" style="width: 15%">
        <div class="progress-bar">15%</div>
    </div>
    <div class="progress" role="progressbar" style="width: 30%">
        <div class="progress-bar bg-success">30%</div>
    </div>
    <div class="progress" role="progressbar" style="width: 20%">
        <div class="progress-bar bg-info">20%</div>
    </div>
</div>

Spinners

<!-- Border spinner -->
<div class="spinner-border text-primary" role="status">
    <span class="visually-hidden">Loading...</span>
</div>

<!-- Growing spinner -->
<div class="spinner-grow text-success" role="status">
    <span class="visually-hidden">Loading...</span>
</div>

<!-- Small spinner in button -->
<button class="btn btn-primary" type="button" disabled>
    <span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
    Saving...
</button>

<!-- Centered full-page loader -->
<div class="d-flex justify-content-center align-items-center" style="min-height: 300px;">
    <div class="spinner-border" role="status">
        <span class="visually-hidden">Loading...</span>
    </div>
</div>

List Groups

<!-- Basic -->
<ul class="list-group">
    <li class="list-group-item active" aria-current="true">Active item</li>
    <li class="list-group-item">Second item</li>
    <li class="list-group-item">Third item</li>
</ul>

<!-- Actionable with badges -->
<div class="list-group">
    <a href="#" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
        Messages
        <span class="badge text-bg-primary rounded-pill">14</span>
    </a>
    <a href="#" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
        Orders
        <span class="badge text-bg-primary rounded-pill">2</span>
    </a>
</div>

<!-- Custom content -->
<div class="list-group">
    <a href="#" class="list-group-item list-group-item-action">
        <div class="d-flex w-100 justify-content-between">
            <h6 class="mb-1">List group item heading</h6>
            <small class="text-body-secondary">3 days ago</small>
        </div>
        <p class="mb-1">Some placeholder content.</p>
        <small class="text-body-secondary">Additional metadata.</small>
    </a>
</div>

Tabs and Pills

Tabs with Content Panels

<ul class="nav nav-tabs" id="myTab" role="tablist">
    <li class="nav-item" role="presentation">
        <button class="nav-link active" id="home-tab" data-bs-toggle="tab"
                data-bs-target="#home-panel" type="button" role="tab"
                aria-controls="home-panel" aria-selected="true">Home</button>
    </li>
    <li class="nav-item" role="presentation">
        <button class="nav-link" id="profile-tab" data-bs-toggle="tab"
                data-bs-target="#profile-panel" type="button" role="tab"
                aria-controls="profile-panel" aria-selected="false">Profile</button>
    </li>
</ul>
<div class="tab-content" id="myTabContent">
    <div class="tab-pane fade show active" id="home-panel" role="tabpanel"
         aria-labelledby="home-tab" tabindex="0">
        <div class="p-3">Home content here.</div>
    </div>
    <div class="tab-pane fade" id="profile-panel" role="tabpanel"
         aria-labelledby="profile-tab" tabindex="0">
        <div class="p-3">Profile content here.</div>
    </div>
</div>

Pills Navigation

<ul class="nav nav-pills mb-3">
    <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>
</ul>

Vertical Pills

<div class="d-flex align-items-start">
    <div class="nav flex-column nav-pills me-3" role="tablist" aria-orientation="vertical">
        <button class="nav-link active" data-bs-toggle="pill" data-bs-target="#v-tab1"
                type="button" role="tab">Tab 1</button>
        <button class="nav-link" data-bs-toggle="pill" data-bs-target="#v-tab2"
                type="button" role="tab">Tab 2</button>
    </div>
    <div class="tab-content">
        <div class="tab-pane fade show active" id="v-tab1" role="tabpanel">Content 1</div>
        <div class="tab-pane fade" id="v-tab2" role="tabpanel">Content 2</div>
    </div>
</div>

Tooltips and Popovers

Tooltips

Require initialization:

document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
    new bootstrap.Tooltip(el);
});
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip"
        data-bs-placement="top" title="Tooltip text">
    Hover me
</button>

Popovers

Require initialization:

document.querySelectorAll('[data-bs-toggle="popover"]').forEach(el => {
    new bootstrap.Popover(el);
});
<button type="button" class="btn btn-lg btn-danger" data-bs-toggle="popover"
        data-bs-title="Popover title" data-bs-content="Popover body content."
        data-bs-trigger="focus">
    Click me
</button>

Carousel

<div id="heroCarousel" class="carousel slide" data-bs-ride="carousel">
    <div class="carousel-indicators">
        <button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="0"
                class="active" aria-current="true" aria-label="Slide 1"></button>
        <button type="button" data-bs-target="#heroCarousel" data-bs-slide-to="1"
                aria-label="Slide 2"></button>
    </div>
    <div class="carousel-inner">
        <div class="carousel-item active">
            <img src="..." class="d-block w-100" alt="...">
            <div class="carousel-caption d-none d-md-block">
                <h5>First slide</h5>
                <p>Description text.</p>
            </div>
        </div>
        <div class="carousel-item">
            <img src="..." class="d-block w-100" alt="...">
        </div>
    </div>
    <button class="carousel-control-prev" type="button" data-bs-target="#heroCarousel" data-bs-slide="prev">
        <span class="carousel-control-prev-icon" aria-hidden="true"></span>
        <span class="visually-hidden">Previous</span>
    </button>
    <button class="carousel-control-next" type="button" data-bs-target="#heroCarousel" data-bs-slide="next">
        <span class="carousel-control-next-icon" aria-hidden="true"></span>
        <span class="visually-hidden">Next</span>
    </button>
</div>

Dropdowns

<!-- Standard dropdown -->
<div class="dropdown">
    <button class="btn btn-secondary dropdown-toggle" type="button"
            data-bs-toggle="dropdown" aria-expanded="false">
        Options
    </button>
    <ul class="dropdown-menu">
        <li><h6 class="dropdown-header">Section</h6></li>
        <li><a class="dropdown-item" href="#">Action</a></li>
        <li><a class="dropdown-item active" href="#">Active item</a></li>
        <li><a class="dropdown-item disabled" aria-disabled="true">Disabled</a></li>
        <li><hr class="dropdown-divider"></li>
        <li><a class="dropdown-item text-danger" href="#">Delete</a></li>
    </ul>
</div>

<!-- Dropdown directions: dropup, dropend, dropstart -->
<div class="dropup">
    <button class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown">Dropup</button>
    <ul class="dropdown-menu">...</ul>
</div>

<!-- Auto-close behavior -->
<div class="dropdown">
    <button class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown"
            data-bs-auto-close="outside">Click outside to close</button>
    <ul class="dropdown-menu">...</ul>
</div>

Typography Patterns

<!-- Display headings -->
<h1 class="display-1">Display 1</h1>
<h1 class="display-4">Display 4</h1>

<!-- Lead paragraph -->
<p class="lead">This is a lead paragraph with larger font.</p>

<!-- Text utilities -->
<p class="text-body-secondary">Muted secondary text</p>
<p class="text-body-emphasis">Emphasized text</p>
<p class="fw-bold">Bold text</p>
<p class="fst-italic">Italic text</p>
<p class="text-decoration-underline">Underlined</p>
<p class="text-truncate" style="max-width: 150px;">Long text that truncates</p>

<!-- Blockquote -->
<figure>
    <blockquote class="blockquote">
        <p>A well-known quote.</p>
    </blockquote>
    <figcaption class="blockquote-footer">
        Author in <cite title="Source">Source Title</cite>
    </figcaption>
</figure>

Image Patterns

<!-- Responsive image -->
<img src="..." class="img-fluid" alt="Responsive image">

<!-- Thumbnail -->
<img src="..." class="img-thumbnail" alt="Thumbnail">

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

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

Common Page Layouts

Dashboard Layout

<div class="container-fluid">
    <div class="row">
        <!-- Sidebar -->
        <nav class="col-md-3 col-lg-2 d-md-block bg-body-tertiary sidebar collapse" id="sidebarMenu">
            <div class="position-sticky pt-3">
                <ul class="nav flex-column">
                    <li class="nav-item">
                        <a class="nav-link active" aria-current="page" href="#">Dashboard</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">Orders</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">Products</a>
                    </li>
                </ul>
            </div>
        </nav>

        <!-- Main content -->
        <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
            <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                <h1 class="h2">Dashboard</h1>
                <div class="btn-toolbar mb-2 mb-md-0">
                    <div class="btn-group me-2">
                        <button type="button" class="btn btn-sm btn-outline-secondary">Share</button>
                        <button type="button" class="btn btn-sm btn-outline-secondary">Export</button>
                    </div>
                </div>
            </div>

            <!-- Stats cards -->
            <div class="row row-cols-1 row-cols-sm-2 row-cols-xl-4 g-3 mb-4">
                <div class="col">
                    <div class="card">
                        <div class="card-body">
                            <h6 class="card-subtitle mb-2 text-body-secondary">Total Revenue</h6>
                            <h3 class="card-title mb-0">$45,231</h3>
                        </div>
                    </div>
                </div>
                <!-- repeat for other stats -->
            </div>

            <!-- Content area -->
            @RenderBody()
        </main>
    </div>
</div>

Hero Section

<div class="px-4 py-5 my-5 text-center">
    <h1 class="display-5 fw-bold text-body-emphasis">Welcome to MyApp</h1>
    <div class="col-lg-6 mx-auto">
        <p class="lead mb-4">Quickly design and customize responsive mobile-first sites.</p>
        <div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
            <a href="#" class="btn btn-primary btn-lg px-4 gap-3">Get started</a>
            <a href="#" class="btn btn-outline-secondary btn-lg px-4">Learn more</a>
        </div>
    </div>
</div>

Pricing Cards

<div class="row row-cols-1 row-cols-md-3 mb-3 text-center g-4">
    <div class="col">
        <div class="card mb-4 rounded-3 shadow-sm">
            <div class="card-header py-3">
                <h4 class="my-0 fw-normal">Free</h4>
            </div>
            <div class="card-body">
                <h1 class="card-title">$0<small class="text-body-secondary fw-light">/mo</small></h1>
                <ul class="list-unstyled mt-3 mb-4">
                    <li>10 users included</li>
                    <li>2 GB of storage</li>
                    <li>Email support</li>
                </ul>
                <button type="button" class="w-100 btn btn-lg btn-outline-primary">Sign up for free</button>
            </div>
        </div>
    </div>
    <div class="col">
        <div class="card mb-4 rounded-3 shadow-sm border-primary">
            <div class="card-header py-3 text-bg-primary border-primary">
                <h4 class="my-0 fw-normal">Pro</h4>
            </div>
            <div class="card-body">
                <h1 class="card-title">$15<small class="text-body-secondary fw-light">/mo</small></h1>
                <ul class="list-unstyled mt-3 mb-4">
                    <li>20 users included</li>
                    <li>10 GB of storage</li>
                    <li>Priority email support</li>
                </ul>
                <button type="button" class="w-100 btn btn-lg btn-primary">Get started</button>
            </div>
        </div>
    </div>
</div>

Login Page

<div class="d-flex align-items-center py-4 bg-body-tertiary min-vh-100">
    <main class="form-signin w-100 m-auto" style="max-width: 330px;">
        <form method="post">
            <h1 class="h3 mb-3 fw-normal text-center">Please sign in</h1>

            <div class="form-floating mb-2">
                <input type="email" class="form-control" id="email" placeholder="name@example.com">
                <label for="email">Email address</label>
            </div>
            <div class="form-floating mb-3">
                <input type="password" class="form-control" id="password" placeholder="Password">
                <label for="password">Password</label>
            </div>

            <div class="form-check text-start mb-3">
                <input class="form-check-input" type="checkbox" value="remember-me" id="rememberMe">
                <label class="form-check-label" for="rememberMe">Remember me</label>
            </div>
            <button class="btn btn-primary w-100 py-2" type="submit">Sign in</button>
        </form>
    </main>
</div>

Source: SKILL.md on GitHub

No alerts14d4 checks · Risk SAFE
  • Gen Agent Trust Hub14d

    The skill provides comprehensive UI patterns for Bootstrap 5 and ASP.NET Core integration. No malicious patterns, obfuscation, or security risks were detected. It follows industry best practices by including subresource integrity (SRI) hashes for external assets and provides explicit security guidance for mitigating cross-site scripting (XSS) and cross-site request forgery (CSRF).

  • Socket14d

    No alerts

  • Snyk14d

    Risk: LOW · No issues

  • Runlayer6mo

    1/3 files flagged

Signed by skilld at 5724d98. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Dormantupdated 8 months ago

README badge

README badge for wshaddix/dotnet-skills/bootstrap5-ui