SuhuFramework v1.0 Lite

Build faster with
beautiful components

A complete UI framework with 50+ components and utility classes. One CSS file, one JS file. No build step required.

<link rel="stylesheet" href="suhu-framework.min.css">
<script src="suhu-framework.min.js" ></script>
50+
Components
200+
Utility classes
~18KB
Gzipped
0
Dependencies

Grid System

12-column responsive grid. .container — full width with wide horizontal inset (--sf-container-padding: 1rem → 3rem → 7rem). .container-fluid — tighter inset (--sf-gutter: 1rem → 1.5rem). Put .container inside full-bleed parents for background + inset content. .row has no default margin/padding; .col-* uses 0.5rem padding. .gap-* / .g-* on .row add gutters between columns.

col-12
col-6
col-6
col-6 + gap-2
col-6 + gap-2
col-4
col-4
col-4
col-3
col-3
col-6
col-12 col-md-4
col-12 col-md-4
col-12 col-md-4
View code
<!-- Full-bleed section + wide content inset (.container) -->
<section class="bg-gradient-primary">
  <div class="container">
    <div class="row">
      <div class="col-12 col-md-6">Col A</div>
      <div class="col-12 col-md-6">Col B</div>
    </div>
  </div>
</section>

<!-- Tighter edge padding (.container-fluid) — e.g. navbar -->
<header class="navbar">
  <div class="container-fluid">…</div>
</header>

<div class="container">
  <div class="row gap-2">
    <div class="col-6">col-6</div>
    <div class="col-6">col-6</div>
  </div>
</div>

Typography

Headings, display sizes, gradient text, and text utilities powered by Inter.

Display Gradient

H1 — The quick brown fox

H2 — The quick brown fox

H3 — The quick brown fox

H4 — The quick brown fox

H5 — The quick brown fox

Lead paragraph — larger text for introductions and hero subtitles.

Regular body text. Use heading classes for hierarchy, utility classes for emphasis.

Muted text using .text-muted

View code
<!-- Heading & display (matches preview) -->
<div class="display-1">Display <span class="text-gradient">Gradient</span></div>
<p class="h1">H1 — The quick brown fox</p>
<p class="h2">H2 — The quick brown fox</p>
<p class="h3">H3 — The quick brown fox</p>
<p class="lead">Lead paragraph — larger text for introductions.</p>
<p class="text-muted">Regular body text.</p>

<!-- Additional text utilities -->
<span class="text-gradient">Blue → Purple → Cyan</span>
<p class="fw-semibold">Semibold 600</p>
<span class="label-highlight">✨ New Feature</span>

Masthead

Page header banner with .bg-gradient-primary background, white title/lead text, and optional actions. After a fixed navbar, add .navbar-spacer then .masthead — vertical padding comes from framework tokens (--sf-masthead-padding-top/bottom), not inline styles. Use .masthead-compact for a shorter variant; .masthead-offset-nav only if you skip .navbar-spacer.

Build faster with SuhuFramework

A single CSS + JS file with familiar class names, theme modes, and mobile-first components.

View code
<!-- Fixed navbar + spacer, then masthead (recommended) -->
<nav class="navbar">…</nav>
<div class="navbar-spacer" aria-hidden="true"></div>
<header class="masthead bg-gradient-primary">
  <div class="masthead-content container">
    <h1 class="masthead-title">Page title</h1>
    <p class="masthead-lead">Short introduction text.</p>
    <div class="masthead-actions">
      <a href="#" class="btn btn-primary">Action</a>
    </div>
  </div>
</header>

Jumbrotton

Hero-style content block with .bg-gradient-primary (same as Masthead). Use .jumbrotton for a single centered column, or .jumbrotton.jumbrotton-2col for a two-column layout with media.

One column

Centered headline, lead copy, and call-to-action buttons in a single column.

Two columns

Text and actions on the left; image or illustration on the right (stacks on mobile).

Jumbrotton illustration
View code
<!-- 1 column -->
<div class="jumbrotton bg-gradient-primary rounded-3">
  <h2 class="jumbrotton-title">Title</h2>
  <p class="jumbrotton-lead">Lead text.</p>
  <div class="jumbrotton-actions">
    <button class="btn btn-primary">Action</button>
  </div>
</div>

<!-- 2 columns -->
<div class="jumbrotton jumbrotton-2col bg-gradient-primary rounded-3">
  <div>
    <h2 class="jumbrotton-title">Title</h2>
    <p class="jumbrotton-lead">Lead text.</p>
    <div class="jumbrotton-actions">…</div>
  </div>
  <div class="jumbrotton-media">
    <img src="…" alt="">
  </div>
</div>

Buttons

Use .btn with color, outline, gradient, size (.btn-sm, .btn-md default, .btn-lg, .btn-xl), and icon variants. All buttons include a ripple effect on click.

View code
<!-- Solid buttons: .btn + .btn-{color} -->
<button type="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>
<button class="btn btn-light">Light</button>
<button class="btn btn-dark">Dark</button>
View code
<!-- Outline buttons: .btn-outline-{color} -->
<button type="button" class="btn btn-outline-primary">Outline Primary</button>
<button class="btn btn-outline-secondary">Outline Secondary</button>
<button class="btn btn-outline-success">Outline Success</button>
<button class="btn btn-outline-danger">Outline Danger</button>
<button class="btn btn-outline-warning">Outline Warning</button>
<button class="btn btn-outline-info">Outline Info</button>

Button sizes — .btn-sm / .btn-md (default) / .btn-lg / .btn-xl

.btn-sm
.btn-md
.btn
.btn-lg
.btn-xl
View code
<!-- Button sizes: .btn-sm / .btn-md / .btn (default) / .btn-lg / .btn-xl -->
<button type="button" class="btn btn-primary btn-sm">Primary</button>
<button type="button" class="btn btn-outline-primary btn-sm">Outline</button>

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

<!-- Default medium — omit size class or use .btn-md -->
<button type="button" class="btn btn-primary">Primary</button>

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

<button type="button" class="btn btn-primary btn-xl">Primary</button>
<button type="button" class="btn btn-outline-primary btn-xl">Outline</button>
View code
<!-- Gradient, disabled & icon buttons (combine with size classes above) -->
<button type="button" class="btn btn-gradient">Gradient</button>
<button class="btn btn-gradient-purple">Purple</button>
<button class="btn btn-primary" disabled>Disabled</button>
<button class="btn btn-primary btn-icon" title="Icon button" data-tooltip="Icon only">★</button>

Offcanvas

Slide-in panel for mobile/tablet navigation (and any side drawer). Trigger with data-bs-toggle="offcanvas" and data-bs-target="#panelId". Backdrop and ESC close are automatic. Submenus: data-toggle on .offcanvas-nav-link / .offcanvas-submenu-link. JS: SuhuFramework.openOffcanvas('id') / closeOffcanvas('id').

View code
<!-- Trigger -->
<button type="button" class="btn btn-primary"
        data-bs-toggle="offcanvas"
        data-bs-target="#demoOffcanvasPanel">
  Open menu
</button>

<!-- Panel: .offcanvas-start | -end | -top | -bottom -->
<div id="demoOffcanvasPanel" class="offcanvas offcanvas-start"
     role="dialog" aria-modal="true" aria-label="Menu" aria-hidden="true">
  <div class="offcanvas-header">
    <span class="navbar-brand">Title</span>
    <button type="button" class="offcanvas-close" aria-label="Close">✕</button>
  </div>
  <div class="offcanvas-body">
    <a href="#" class="offcanvas-nav-link">
      <span class="link-icon">🏠</span>
      <span class="link-text">Home</span>
    </a>
    <div class="offcanvas-nav-item">
      <button type="button" class="offcanvas-nav-link" data-toggle>
        <span class="link-text">More</span>
        <svg class="oc-arrow" …></svg>
      </button>
      <div class="offcanvas-submenu">
        <a href="#" class="offcanvas-submenu-link">Sub link</a>
      </div>
    </div>
  </div>
  <div class="offcanvas-footer">
    <a href="#" class="btn btn-primary">Action</a>
  </div>
</div>

<!-- With navbar: see Navbar section View code (full copy-paste) -->

Cards

Light, dark, gradient-border, and feature card variants.

Basic Card

Hover to see the lift shadow effect.

Learn more
Header

Card with header and footer.

Gradient Border

Hover to see the gradient border intensify.

View code
<!-- Cards: .card-group with multiple .card -->
<div class="card-group">
  <div class="card">
    <div class="card-body rounded-3">
      <h3 class="card-title">Basic Card</h3>
      <p class="card-text">Hover to see the lift shadow effect.</p>
      <a href="#" class="btn btn-primary mt-3">Learn more</a>
    </div>
  </div>
  <div class="card rounded-3">
    <div class="card-header">Header</div>
    <div class="card-body rounded-3"><p class="card-text">Card with header and footer.</p></div>
    <div class="card-footer d-flex justify-content-end gap-2">
      <button type="button" class="btn btn-ghost">Cancel</button>
      <button type="button" class="btn btn-primary">Save</button>
    </div>
  </div>
  <div class="card card-gradient-border">
    <div class="card-body rounded-3">
      <h3 class="card-title">Gradient Border</h3>
      <p class="card-text">Hover to see the gradient border intensify.</p>
    </div>
  </div>
</div>

Feature Card

Theme-aware card with gradient icon. Hover for glow border effect.

🎨

Custom Icon Color

Change the icon gradient with an inline style override.

View code
<!-- Feature card: .feature-card + .feature-icon (emoji or icon) -->
<div class="card-group">
  <div class="feature-card rounded-3">
    <div class="feature-icon">⚡</div>
    <h3 class="card-title">Feature Card</h3>
    <p class="card-text">Theme-aware card with gradient icon.</p>
  </div>
  <div class="feature-card rounded-3">
    <div class="feature-icon" style="background:linear-gradient(135deg,#a855f7,#ec4899);">🎨</div>
    <h3 class="card-title">Custom Icon Color</h3>
    <p class="card-text">Change the icon gradient with an inline style override.</p>
  </div>
</div>

Alerts

Contextual feedback messages. Add .alert-dismissible and .alert-dismiss button for a closable alert.

ℹ️
Information
A primary alert — for informational messages and tips.
Success
Your changes have been saved successfully.
🚨
Error
Something went wrong. Click ✕ to dismiss this alert.
⚠️
Warning
Please review your settings before proceeding.
View code
<!-- Alert: .alert-{color}; dismiss with .alert-dismissible + .alert-dismiss -->
<div class="alert alert-primary">
  <span class="alert-icon">ℹ️</span>
  <div class="alert-content">
    <div class="alert-title">Information</div>
    A primary alert — for informational messages and tips.
  </div>
</div>

<div class="alert alert-success">
  <span class="alert-icon">✅</span>
  <div class="alert-content">
    <div class="alert-title">Success</div>
    Your changes have been saved successfully.
  </div>
</div>

<div class="alert alert-danger alert-dismissible">
  <span class="alert-icon">🚨</span>
  <div class="alert-content">
    <div class="alert-title">Error</div>
    Something went wrong. Click ✕ to dismiss this alert.
  </div>
  <button class="alert-dismiss" aria-label="Dismiss">✕</button>
</div>

<div class="alert alert-warning">
  <span class="alert-icon">⚠️</span>
  <div class="alert-content">
    <div class="alert-title">Warning</div>
    Please review your settings before proceeding.
  </div>
</div>

Badges & Chips

Small status indicators and interactive chip tags.

Primary Success Danger Warning Info Dark Blue subtle Green subtle Red subtle Purple subtle Amber subtle
View code
<!-- Badge: .badge + .bg-{color} or .badge-{color} -->
<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>
<span class="badge rounded-pill bg-warning">Warning</span>
<span class="badge rounded-pill bg-info">Info</span>
<span class="badge rounded-pill bg-dark">Dark</span>
<span class="badge rounded-pill bg-blue-subtle">Blue subtle</span>
<span class="badge rounded-pill bg-green-subtle">Green subtle</span>
<span class="badge rounded-pill bg-red-subtle">Red subtle</span>
<span class="badge rounded-pill bg-purple-subtle">Purple subtle</span>
<span class="badge rounded-pill bg-amber-subtle">Amber subtle</span>
Default Primary ✓ Active ✗ Error React TypeScript WordPress PHP
View code
<!-- Chip: small tag; .chip-primary / .chip-success for color -->
<span class="chip rounded-pill">Default</span>
<span class="chip rounded-pill chip-primary">Primary</span>
<span class="chip rounded-pill chip-success">✓ Active</span>
<span class="chip rounded-pill chip-danger">✗ Error</span>
<span class="chip rounded-pill">React</span>
<span class="chip rounded-pill">TypeScript</span>
<span class="chip rounded-pill">WordPress</span>
<span class="chip rounded-pill chip-primary">PHP</span>

Forms

SuhuFramework form styling (ring-inset, rounded-md, shadow-sm, focus ring) — included in suhu-framework.min.css. Control sizes: .form-control-sm / default / .form-control-lg and .form-select-sm / default / .form-select-lg. Date field uses data-sf-date-picker (initialized by SuhuFramework.init()).

✓ Strong password
This username is already taken.
View code
<!-- Form: .form-label + .form-control / .form-select; feedback below field -->
<div class="sf-form-field">
  <label class="form-label" for="demo-email">Email address <span class="required">*</span></label>
  <input type="email" class="form-control rounded" id="demo-email" placeholder="you@example.com">
</div>

<div class="sf-form-field">
  <label class="form-label" for="demo-pass">Password</label>
  <input type="password" class="form-control rounded is-valid" id="demo-pass" value="securepwd123" autocomplete="off">
  <div class="valid-feedback">✓ Strong password</div>
</div>

<div class="sf-form-field">
  <label class="form-label" for="demo-user">Username</label>
  <input type="text" class="form-control rounded is-invalid" id="demo-user" value="taken_user">
  <div class="invalid-feedback">This username is already taken.</div>
</div>

<div class="sf-form-field">
  <label class="form-label" for="demo-fw">Framework</label>
  <select class="form-select rounded" id="demo-fw">
    <option>SuhuFramework</option>
    <option>Starter kit</option>
    <option>Pro bundle</option>
  </select>
</div>

<!-- Date picker: click month/year to pick (SuhuFramework.init / SFDatePicker) -->
<div class="sf-form-field">
  <label class="form-label" for="demo-date">Date</label>
  <input type="text"
         class="form-control rounded"
         id="demo-date"
         data-sf-date-picker
         data-sf-date-locale="id"
         data-sf-date-format="DD/MM/YYYY"
         placeholder="DD/MM/YYYY"
         readonly>
</div>

<!-- ISO value stored in data-sf-date-value; SFDatePicker.init() after dynamic DOM -->

<div class="sf-form-field">
  <label class="form-label" for="demo-msg">Message</label>
  <textarea class="form-control rounded" id="demo-msg" rows="3" placeholder="Write your message..."></textarea>
</div>

<div class="form-check">
  <input class="form-check-input" type="checkbox" id="ch1" checked>
  <label class="form-check-label" for="ch1">Accept terms & conditions</label>
</div>

<div class="form-check">
  <input class="form-check-input" type="radio" name="plan" id="r1" checked>
  <label class="form-check-label" for="r1">Monthly plan</label>
</div>
<div class="form-check">
  <input class="form-check-input" type="radio" name="plan" id="r2">
  <label class="form-check-label" for="r2">Annual plan (save 20%)</label>
</div>

<div class="form-check form-switch">
  <input class="form-check-input" type="checkbox" id="sw1" checked>
  <label class="form-check-label" for="sw1">Enable notifications</label>
</div>

<label class="form-label">Volume: <span id="rangeVal">50</span>%</label>
<input type="range" class="form-range w-100" min="0" max="100" value="50"
       oninput="document.getElementById('rangeVal').textContent=this.value">

Form control sizes — .form-control-sm / default / .form-control-lg

View code
<!-- .form-control sizes -->
<input type="text" class="form-control form-control-sm rounded" placeholder="Small input">
<input type="text" class="form-control rounded" placeholder="Default input">
<input type="text" class="form-control form-control-lg rounded" placeholder="Large input">

<!-- Textarea also accepts size classes -->
<textarea class="form-control form-control-sm rounded" rows="2"></textarea>
<textarea class="form-control form-control-lg rounded" rows="3"></textarea>

Form select sizes — .form-select-sm / default / .form-select-lg

View code
<!-- .form-select sizes -->
<select class="form-select form-select-sm rounded">
  <option>Small select</option>
</select>

<select class="form-select rounded">
  <option>Default select</option>
</select>

<select class="form-select form-select-lg rounded">
  <option>Large select</option>
</select>

Modals

Accessible dialogs with backdrop blur and spring animation. By default, click outside the dialog or press ESC to close. Use data-modal-backdrop="static" (or class .modal-static-backdrop) to ignore outside clicks.

View code
<!-- Triggers (match preview) -->
<button class="btn btn-primary" data-modal-target="#modal-basic">Open Modal</button>
<button class="btn btn-outline-primary" data-modal-target="#modal-sm">Small</button>
<button class="btn btn-outline-primary" data-modal-target="#modal-lg">Large</button>
<button class="btn btn-gradient-purple" data-modal-target="#modal-form">Form Modal</button>
<button class="btn btn-outline-secondary" data-modal-target="#modal-static">Static backdrop</button>

<!-- Modal -->
<div class="modal-overlay" id="modal-basic">
  <div class="modal">
    <div class="modal-header">
      <h5 class="modal-title">Title</h5>
      <button class="modal-close rounded" data-modal-close>✕</button>
    </div>
    <div class="modal-body">Content here.</div>
    <div class="modal-footer">
      <button class="btn btn-ghost" data-modal-close>Cancel</button>
      <button class="btn btn-primary">Confirm</button>
    </div>
  </div>
</div>

<!-- Size variants -->
<div class="modal modal-sm">...</div>
<div class="modal modal-lg">...</div>
<div class="modal modal-xl">...</div>

<!-- Static backdrop: outside click does not close -->
<div class="modal-overlay" id="myStaticModal" data-modal-backdrop="static">
  <div class="modal">...</div>
</div>
<!-- Or: class="modal-overlay modal-static-backdrop" -->

<!-- Optional: disable ESC (data-modal-keyboard="false") -->

Accordion

Collapsible content panels with smooth max-height animation.

SuhuFramework ships ready-to-use components and utilities. Zero config — just link the CSS and JS files.
No build step required. Drop in the CSS and JS files and you're ready. Works with any backend or static site generator.
Yes. The entire demo page uses a dark theme. Components have explicit dark variants and the CSS variable system makes full dark mode easy.
View code
<!-- Accordion: click .accordion-button; one open unless data-multi-open -->
<div class="accordion">
  <div class="accordion-item">
    <div class="accordion-header">
      <button class="accordion-button active" aria-expanded="true">
        Title
        <svg class="accordion-icon">...chevron svg...</svg>
      </button>
    </div>
    <div class="accordion-collapse show">
      <div class="accordion-body">Content</div>
    </div>
  </div>

  <div class="accordion-item">
    <div class="accordion-header">
      <button class="accordion-button" aria-expanded="false">
        Another item
        <svg class="accordion-icon">...</svg>
      </button>
    </div>
    <div class="accordion-collapse">
      <div class="accordion-body">Hidden content</div>
    </div>
  </div>
</div>

<!-- Allow multiple panels open -->
<div class="accordion" data-multi-open>...</div>

Tabs

Underline (.nav-tabs) and pill (.nav-pills) variants. Link tabs to panes with data-target="#id". Align tab lists with .justify-content-* or stretch with .nav-fill / .nav-justified.

Tab alignment

.justify-content-start — Start (default)

.justify-content-center — Center

.justify-content-end — End

.nav-fill — Justify (equal width)

Pills + .justify-content-center

Pills + .nav-fill

View code
<!-- Tab alignment: .justify-content-* / .nav-fill on .nav-tabs or .nav-pills -->
<ul class="nav-tabs justify-content-start">
  <li><a class="nav-link active" href="#">Tab 1</a></li>
  <li><a class="nav-link" href="#">Tab 2</a></li>
</ul>

<!-- Center -->
<ul class="nav-tabs justify-content-center">...</ul>

<!-- End -->
<ul class="nav-tabs justify-content-end">...</ul>

<!-- Justify / equal width -->
<ul class="nav-tabs nav-fill">...</ul>
<!-- .nav-justified is an alias of .nav-fill -->

<!-- Same utilities on pill tabs -->
<div class="nav-pills justify-content-center">...</div>
<div class="nav-pills nav-fill">...</div>

Interactive examples

Overview

Switch between tabs to see the animated underline and fade transition.

  • ✅ Responsive grid system
  • ✅ SuhuFramework standard classes
  • ✅ Light & dark mode
// global: SuhuFramework (alias: Suhu)
SuhuFramework.init()

Showing all 48 items.

Showing 32 active items.

Showing 16 archived items.

View code
<!-- Interactive tabs: data-target must match .tab-pane id -->
<ul class="nav-tabs">
  <li><a class="nav-link active" href="#t1" data-target="#t1">Overview</a></li>
  <li><a class="nav-link" href="#t2" data-target="#t2">Features</a></li>
  <li><a class="nav-link" href="#t3" data-target="#t3">Code</a></li>
</ul>
<div class="tab-content">
  <div class="tab-pane active" id="t1">…Overview content…</div>
  <div class="tab-pane" id="t2">…Features content…</div>
  <div class="tab-pane" id="t3">…Code content…</div>
</div>

<!-- Pill tabs -->
<div class="nav-pills">
  <a class="nav-link active" href="#p1" data-target="#p1">All</a>
  <a class="nav-link" href="#p2" data-target="#p2">Active</a>
  <a class="nav-link" href="#p3" data-target="#p3">Archived</a>
</div>
<div class="tab-content">
  <div class="tab-pane active" id="p1">Showing all 48 items.</div>
  <div class="tab-pane" id="p2">Showing 32 active items.</div>
  <div class="tab-pane" id="p3">Showing 16 archived items.</div>
</div>

Toast

Lightweight notification toasts. Trigger programmatically with SuhuFramework.showToast() or via data-toast-* attributes. Auto-dismiss with a progress bar.

Saved just now
Profile updated successfully.
View code
<!-- Toast from button: data-toast-title, data-toast-message, data-toast-type -->
<button type="button" class="btn btn-primary btn-sm"
        data-toast-title="Information"
        data-toast-message="Your changes have been saved successfully."
        data-toast-type="primary">Show Info</button>

<button class="btn btn-success btn-sm"
        data-toast-title="Success!"
        data-toast-message="The item was created."
        data-toast-type="success">Show Success</button>

<button class="btn btn-danger btn-sm"
        data-toast-title="Error"
        data-toast-message="Something went wrong. Please try again."
        data-toast-type="danger">Show Error</button>

<button class="btn btn-warning btn-sm"
        data-toast-title="Warning"
        data-toast-message="Your session will expire in 5 minutes."
        data-toast-type="warning">Show Warning</button>

<!-- Static toast markup (preview; add position styles in layout if needed) -->
<div class="toast toast-success show" style="position:relative;transform:none;opacity:1;">
  <div class="toast-header">
    <span class="toast-icon">✓</span>
    <span class="toast-title">Saved</span>
    <span class="toast-time">just now</span>
    <button class="toast-close rounded">✕</button>
  </div>
  <div class="toast-body">Profile updated successfully.</div>
  <div class="toast-progress"></div>
</div>

Tooltip

CSS-only tooltips via data-tooltip. Position with data-tooltip-pos="top|bottom|left|right". On native <button>, JS wraps a .sf-tooltip-host so the arrow is not clipped inside the control.

Badge .tooltip
View code
<!-- Tooltip CSS: data-tooltip + data-tooltip-pos (top default) -->
<button type="button" class="btn btn-secondary" data-tooltip="Default — top position">Hover me (top)</button>
<button class="btn btn-secondary" data-tooltip="Slides in from below" data-tooltip-pos="bottom">Bottom</button>
<button class="btn btn-secondary" data-tooltip="Left side tooltip" data-tooltip-pos="left">Left</button>
<button class="btn btn-secondary" data-tooltip="Right side tooltip" data-tooltip-pos="right">Right</button>

<span class="badge rounded-pill badge-primary" data-tooltip="Tooltips work on any element">Badge</span>
<code data-tooltip="Code element tooltip" data-tooltip-pos="bottom">.tooltip</code>

Popovers

Click-triggered popovers with title and body. Use data-bs-toggle="popover", data-bs-title, data-bs-content, and data-bs-placement (top, bottom, left, right). Click outside or press Escape to dismiss.

View code
<!-- Popover: click to open; click outside / Escape to close -->
<button type="button" class="btn btn-primary"
        data-bs-toggle="popover"
        data-bs-title="SuhuFramework"
        data-bs-content="Lightweight UI kit — one CSS file, one JS file."
        data-bs-placement="top">Top</button>

<button type="button" class="btn btn-secondary"
        data-bs-toggle="popover"
        data-bs-title="Documentation"
        data-bs-content="See guide.md for the full component reference."
        data-bs-placement="bottom">Bottom</button>

<button type="button" class="btn btn-success"
        data-bs-toggle="popover"
        data-bs-title="RTL ready"
        data-bs-content="Try the Direction section — LTR / RTL toggles inside .sf-preview only."
        data-bs-placement="left">Left</button>

<button type="button" class="btn btn-info"
        data-bs-toggle="popover"
        data-bs-title="Theme modes"
        data-bs-content="Light, Dark, or Auto — matches your system preference."
        data-bs-placement="right">Right</button>

TabBar

Floating bottom navigation for mobile (<992px). On real pages use position: fixed at the viewport bottom; add .tab-bar-spacer so content is not covered. Preview below uses .tab-bar-frame to simulate a phone screen on any viewport.

Page content — scrollable area above the TabBar.

View code
<!-- Production: fixed to viewport bottom (mobile < 992px) -->
<nav class="tab-bar" aria-label="Bottom navigation">
  <a href="#" class="tab-bar-item active">
    <span class="tab-bar-icon">🏠</span>
    <span class="tab-bar-label">Home</span>
  </a>
  <a href="#" class="tab-bar-item">
    <span class="tab-bar-icon">🔍</span>
    <span class="tab-bar-label">Search</span>
  </a>
  <button type="button" class="tab-bar-item">
    <span class="tab-bar-icon">⚙️</span>
    <span class="tab-bar-label">Settings</span>
  </button>
</nav>
<div class="tab-bar-spacer" aria-hidden="true"></div>

<!-- Docs preview: phone frame (visible on desktop too) -->
<div class="tab-bar-frame">
  <div class="tab-bar-frame__content">…</div>
  <nav class="tab-bar" aria-label="Bottom navigation">…</nav>
</div>

Mobile Animations

Touch-friendly enter/exit animations for mobile views and page transitions. Apply classes directly or toggle them in JS.

In — enter / show

Out — exit / hide

Animated panel
View code
<!-- In (enter) -->
<div class="mobile-fade-left-in">…</div>
<div class="mobile-fade-right-in">…</div>
<div class="mobile-fade-up-in">…</div>
<div class="mobile-fade-down-in">…</div>
<div class="mobile-scale-in">…</div>

<!-- Out (exit) -->
<div class="mobile-fade-left-out">…</div>
<div class="mobile-fade-right-out">…</div>
<div class="mobile-fade-up-out">…</div>
<div class="mobile-fade-down-out">…</div>
<div class="mobile-scale-out">…</div>

Theme

Three color schemes: Light, Dark, and Auto (follows OS prefers-color-scheme). Preference is stored in localStorage under sf-theme-mode. Resolved theme is applied via data-theme on <html>.

View code
<!-- Theme: Light / Dark / Auto (stored in localStorage sf-theme-mode) -->
<div class="sf-theme-picker" role="group" aria-label="Color scheme">
  <button type="button" class="sf-theme-picker__btn" data-theme-mode-option="light" title="Light mode">☀ Light</button>
  <button type="button" class="sf-theme-picker__btn" data-theme-mode-option="dark" title="Dark mode">☾ Dark</button>
  <button type="button" class="sf-theme-picker__btn" data-theme-mode-option="auto" title="System preference">◐ Auto</button>
</div>

<!-- Optional: single button cycles light → dark → auto -->
<button type="button" class="btn btn-outline-secondary btn-sm" data-theme-toggle>
  <span data-theme-icon>◐</span>
  <span data-theme-label>Auto</span>
</button>

<script>
  SuhuFramework.setThemeMode('auto'); // 'light' | 'dark' | 'auto'
</script>

Direction (LTR / RTL)

Preview only inside .sf-preview — the rest of this page stays LTR. Set dir on a scoped container with data-sf-dir-scope. Utility classes .ms-*, .me-*, .text-start / .text-end use logical properties and mirror under RTL.

Sample layout (scoped preview)

Start ms-auto

Text aligned with .text-end

View code
<!-- RTL scoped to this area: data-sf-dir-scope (demo page stays LTR) -->
<div class="sf-preview" data-sf-dir-scope data-sf-dir="ltr">
  <div class="sf-dir-picker" role="group" aria-label="Text direction preview">
    <button type="button" class="sf-dir-picker__btn" data-dir-option="ltr" title="Left to right">LTR</button>
    <button type="button" class="sf-dir-picker__btn" data-dir-option="rtl" title="Right to left">RTL</button>
  </div>
  <div class="d-flex align-items-center gap-2 flex-wrap">
    <span class="badge rounded-pill bg-primary">Start</span>
    <span class="ms-auto badge rounded-pill bg-secondary">ms-auto</span>
  </div>
  <p class="text-end">Text aligned with .text-end</p>
</div>

<script>
  SuhuFramework.setDirection('rtl'); // entire document — optional
</script>

Progress

Animated gradient progress bars. data-width triggers on scroll into view.

HTML/CSS92%
JavaScript78%
Performance95%
Errors18%
View code
<!-- Progress: fill .progress-bar; optional striped/animated -->
<div class="progress rounded-pill">
  <div class="progress-bar" data-width="75%"></div>
</div>

<!-- Color variants -->
<div class="progress-bar progress-bar-success" data-width="90%"></div>
<div class="progress-bar progress-bar-danger"  data-width="30%"></div>
<div class="progress-bar progress-bar-warning" data-width="60%"></div>
<div class="progress-bar progress-bar-info"    data-width="45%"></div>

<!-- Size variants -->
<div class="progress rounded-pill progress-sm">...</div>
<div class="progress rounded-pill progress-lg">...</div>

Spinners

Border and grow spinners in multiple sizes and color variants.

View code
<!-- Spinner: border or grow; color via .spinner-{color} -->
<div class="spinner-border spinner-primary"></div>
<div class="spinner-border spinner-success"></div>
<div class="spinner-border spinner-danger"></div>
<div class="spinner-border spinner-warning"></div>
<div class="spinner-border spinner-sm spinner-primary"></div>
<div class="spinner-border spinner-lg spinner-primary"></div>
<div class="spinner-grow spinner-primary"></div>
<div class="spinner-grow spinner-success"></div>

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

Tables

Mobile-first layout: datatable toolbar stacks search and actions on small screens; table scrolls inside .table-responsive. Enable features via .table-datatable data attributes (SuhuFramework.init()).

Basic table

#NameRoleStatusAction
1
AB
Ahmad Budi
DeveloperActive
2
CS
Citra Sari
DesignerActive
3
DW
Dian Wati
ManagerInactive
View code
<!-- Basic table: wrap in .table-responsive for mobile scroll -->
<div class="table-responsive">
  <table class="table rounded table-striped table-hover">
    <thead>
      <tr><th scope="col">#</th><th scope="col">Name</th><th scope="col">Role</th><th scope="col">Status</th><th scope="col">Action</th></tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td><div style="display:flex;align-items:center;gap:.5rem;"><div class="avatar avatar-sm">AB</div>Ahmad Budi</div></td>
        <td>Developer</td>
        <td><span class="badge rounded-pill bg-green-subtle">Active</span></td>
        <td><button type="button" class="btn btn-outline-primary btn-sm">Edit</button></td>
      </tr>
      <tr><td>2</td><td>…</td><td>Designer</td><td>…</td><td>…</td></tr>
      <tr><td>3</td><td>…</td><td>Manager</td><td>…</td><td>…</td></tr>
    </tbody>
  </table>
</div>

Enhanced — mobile-first toolbar (search full-width, action buttons in a grid). Desktop: search left, actions right.

#NameRoleStatusAction
1
AB
Ahmad Budi
DeveloperActive
2
CS
Citra Sari
DesignerActive
3
DW
Dian Wati
ManagerInactive
4
EP
Eko Prasetyo
DeveloperActive
5
FR
Fira Rahma
QA EngineerActive
6
GS
Gita Surya
Product OwnerPending
7
HL
Hadi Lestari
DevOpsActive
8
IN
Indra Nugroho
AnalystInactive
View code
<!-- Datatable: enable via data-table-* attributes (requires SuhuFramework.init()) -->
<div class="table-datatable rounded"
     data-table-search
     data-table-export
     data-table-refresh
     data-table-pagination="4"
     data-table-count
     data-table-search-placeholder="Search name, role, status…"
     data-table-export-label="Ekspor .xlsx"
     data-table-refresh-label="Refresh"
     data-table-export-filename="daftar-pengguna.xlsx"
     data-table-count-template="Showing {start}–{end} of {filtered} entries"
     data-table-empty="No matching records found.">
  <div class="table-responsive">
    <table class="table rounded table-striped table-hover">
      <thead><tr><th scope="col">#</th><th scope="col">Name</th><th scope="col">Role</th><th scope="col">Status</th><th scope="col">Action</th></tr></thead>
      <tbody>
        <!-- 8 data rows like preview -->
        <tr><td>1</td><td>…</td><td>Developer</td><td>…</td><td>…</td></tr>
      </tbody>
    </table>
  </div>
</div>

<!-- Table modifiers -->
<table class="table rounded table-bordered"></table>
<table class="table rounded table-dark"></table>

Pagination

Page navigation with active, disabled, and size variants.

View code
<!-- Pagination: .page-item + .active / .disabled -->
<nav aria-label="Pagination">
  <ul class="pagination">
    <li class="page-item disabled"><span class="page-link rounded">‹</span></li>
    <li class="page-item"><a class="page-link rounded" href="#">1</a></li>
    <li class="page-item active"><a class="page-link rounded" href="#" aria-current="page">2</a></li>
    <li class="page-item"><a class="page-link rounded" href="#">›</a></li>
  </ul>
</nav>

<!-- Sizes -->
<ul class="pagination pagination-sm">...</ul>
<ul class="pagination pagination-lg">...</ul>

Color Palette

Full SuhuFramework color system exposed as CSS variables and utility classes.

Slate

Sky / Blue

Indigo / Purple / Pink

Green / Emerald / Cyan / Amber / Red

View code
/* Override in your stylesheet */
:root {
  --primary:     #3b82f6;
  --success:     #22c55e;
  --danger:      #ef4444;
  --warning:     #f59e0b;
  --info:        #06b6d4;
}

/* Background utility classes */
.bg-primary      { background: var(--primary); color: #fff; }
.bg-blue-subtle  { background: #dbeafe; color: #1e40af; }
.bg-green-subtle { background: #dcfce7; color: #166534; }

/* Text color utilities */
.text-primary  { color: #2563eb; }
.text-success  { color: #16a34a; }
.text-danger   { color: #ef4444; }
.text-muted    { color: #94a3b8; }

Animations

CSS animation utilities, scroll-triggered reveal effects, and mobile animations (.mobile-fade-left-in, etc.).

🚀
float
pulse
spin
bounce
scale-in
View code
<!-- Preview demos -->
<div class="animate-float">…</div>
<button class="btn btn-gradient btn-sm animate-pulse">Pulse</button>
<div class="spinner-border spinner-primary"></div>
<div class="animate-bounce">…</div>
<div class="animate-scale-in">…</div>

<!-- All animation utilities -->
<div class="animate-float"></div>
<div class="animate-bounce"></div>
<div class="animate-pulse"></div>
<div class="animate-spin"></div>
<div class="animate-fade-in-up"></div>
<div class="animate-scale-in"></div>

<!-- Scroll reveal -->
<div class="reveal"></div>
<div class="reveal-left"></div>
<div class="reveal-right delay-200"></div>

<!-- Mobile animations (see Mobile Animations section) -->
<div class="mobile-fade-left-in"></div>
<div class="mobile-fade-right-out"></div>
<div class="mobile-scale-in"></div>

<!-- Counter (hero stats) -->
<span data-count="50" data-suffix="+">0</span>

Background

Background color, gradient, and transparent utilities compatible with SuhuFramework utility naming.

bg-primary bg-secondary bg-success bg-danger bg-warning bg-info bg-dark bg-white bg-gradient-primary bg-gradient-purple bg-gradient-success bg-gradient-danger bg-blue-subtle bg-green-subtle bg-red-subtle bg-amber-subtle bg-transparent
View code
<div class="bg-primary">Primary</div>
<div class="bg-secondary">Secondary</div>
<div class="bg-success">Success</div>
<div class="bg-danger">Danger</div>
<div class="bg-warning">Warning</div>
<div class="bg-info">Info</div>
<div class="bg-dark">Dark</div>
<div class="bg-white">White</div>
<div class="bg-transparent">Transparent</div>

<!-- Gradient backgrounds -->
<div class="bg-gradient-primary">Gradient Primary</div>
<div class="bg-gradient-purple">Gradient Purple</div>
<div class="bg-gradient-success">Gradient Success</div>
<div class="bg-gradient-danger">Gradient Danger</div>

<!-- Subtle backgrounds -->
<div class="bg-blue-subtle">Blue Subtle</div>
<div class="bg-green-subtle">Green Subtle</div>
<div class="bg-red-subtle">Red Subtle</div>
<div class="bg-amber-subtle">Amber Subtle</div>
<div class="bg-transparent">Transparent</div>

Borders

Border, border-color, and border-radius utilities for quickly styling elements.

Sides
.border
.border-top
.border-bottom
.border-start
.border-end
Colors
.border-primary
.border-success
.border-danger
.border-warning
Radius
.rounded-0
.rounded-1
.rounded
.rounded-3
.rounded-pill
View code
<!-- Border sides -->
<div class="border">All sides</div>
<div class="border-0">No border</div>
<div class="border-top">Top only</div>
<div class="border-bottom">Bottom only</div>
<div class="border-start">Left only</div>
<div class="border-end">Right only</div>

<!-- Border colors -->
<div class="border border-primary">Primary</div>
<div class="border border-success">Success</div>
<div class="border border-danger">Danger</div>

<!-- Border radius -->
<div class="rounded-0">No radius</div>
<div class="rounded">Default</div>
<div class="rounded-pill">Pill</div>
<div class="rounded-circle">Circle</div>

Colors

Text color utilities using text-* classes. Covers semantic colors and gradients.

.text-primary .text-secondary .text-success .text-danger .text-warning .text-info .text-white .text-light .text-muted
.text-gradient .text-gradient-blue .text-gradient-purple .text-gradient-cyan
View code
<span class="text-primary">Primary</span>
<span class="text-secondary">Secondary</span>
<span class="text-success">Success</span>
<span class="text-danger">Danger</span>
<span class="text-warning">Warning</span>
<span class="text-info">Info</span>
<span class="text-white">White</span>
<span class="text-dark">Dark</span>
<span class="text-muted">Muted</span>
<span class="text-body">Body</span>
<span class="text-reset">Reset (inherit)</span>

<!-- Gradient text -->
<span class="text-gradient">Default gradient</span>
<span class="text-gradient-blue">Blue gradient</span>
<span class="text-gradient-purple">Purple gradient</span>
<span class="text-gradient-cyan">Cyan gradient</span>

Display

Control element display with d-* classes. Responsive variants available: d-sm-*, d-md-*, d-lg-*, d-xl-*.

.d-block .d-inline .d-inline-block .d-flex .d-inline-flex .d-grid .d-table .d-none .d-md-none .d-lg-block
View code
<div class="d-block">Block</div>
<span class="d-inline">Inline</span>
<div class="d-inline-block">Inline block</div>
<div class="d-flex">Flex</div>
<div class="d-grid">Grid</div>
<div class="d-none">Hidden</div>

<!-- Responsive display -->
<div class="d-none d-md-block">Visible on md+</div>
<div class="d-md-none">Hidden on md+</div>
<div class="d-none d-lg-flex">Flex on lg+</div>

Flex

Comprehensive flexbox utilities for direction, wrapping, justification, alignment, and gaps.

justify-content-*
startcenterend
ABC
align-items-*
top middle bottom
flex-wrap / gap
Item 1Item 2Item 3Item 4Item 5
View code
<!-- Direction -->
<div class="d-flex flex-row">...</div>
<div class="d-flex flex-column">...</div>

<!-- Justify -->
<div class="d-flex justify-content-start">...</div>
<div class="d-flex justify-content-center">...</div>
<div class="d-flex justify-content-end">...</div>
<div class="d-flex justify-content-between">...</div>
<div class="d-flex justify-content-around">...</div>
<div class="d-flex justify-content-evenly">...</div>

<!-- Align -->
<div class="d-flex align-items-start">...</div>
<div class="d-flex align-items-center">...</div>
<div class="d-flex align-items-end">...</div>
<div class="d-flex align-items-stretch">...</div>

<!-- Wrap & grow -->
<div class="d-flex flex-wrap gap-2">...</div>
<div class="d-flex flex-nowrap">...</div>
<div class="d-flex"><div class="flex-fill">Fill</div></div>
<div class="d-flex"><div class="flex-grow-1">Grow</div></div>

<!-- Self -->
<div class="align-self-center">Centered self</div>

Float

Float an element left or right. Always use .clearfix on the parent to contain floated children.

float-start
float-end
View code
<div class="clearfix">
  <div class="float-start">Float left</div>
  <div class="float-end">Float right</div>
</div>

<!-- Remove float -->
<div class="float-none">No float</div>

Interactions

Control user interactions: user-select and pointer-events utilities.

This text cannot be selected (.user-select-none)

Click once to select all (.user-select-all)

View code
<!-- user-select -->
<p class="user-select-none">Cannot be selected</p>
<p class="user-select-all">All selected on click</p>
<p class="user-select-auto">Default selection</p>

<!-- pointer-events -->
<a href="#" class="pe-none">Link disabled</a>
<a href="#" class="pe-auto">Link active</a>

Object fit

Control how an image or video is resized to fit its container with object-fit-* classes.

cover
.object-fit-cover
contain
.object-fit-contain
fill
.object-fit-fill
scale-down
.object-fit-scale
View code
<img class="object-fit-cover w-100 h-100" src="..." alt="">
<img class="object-fit-contain w-100 h-100" src="..." alt="">
<img class="object-fit-fill w-100 h-100" src="..." alt="">
<img class="object-fit-scale w-100 h-100" src="..." alt="">
<img class="object-fit-none w-100 h-100" src="..." alt="">

Overflow

Control content overflow on both axes with overflow-*, overflow-x-*, and overflow-y-*.

.overflow-hidden — text below is clipped with no scroll. Line 2: Long content that does not fit in the box stays hidden. Line 3: You cannot scroll to read the rest of this text. Line 4: Only the portion visible in the 4rem area is shown.
overflow-hidden
.overflow-auto — vertical scroll appears when content exceeds the box height. Line 2: Add paragraphs or lists so the height exceeds 4rem. Line 3: Scrollbar appears only when needed (auto). Line 4: Good for sidebars, log panels, or notification lists. Line 5: Try scrolling down to read this line. Line 6: More text below — overflow-auto is active. Line 7: Last line to make scrolling obvious.
overflow-auto
.overflow-x-auto — horizontal scroll only  ·  SuhuFramework utilities  ·  overflow-x-auto  ·  one very long line of text so a horizontal scrollbar appears in this box
overflow-x-auto
View code
<div class="overflow-auto">Scrollable</div>
<div class="overflow-hidden">Clipped</div>
<div class="overflow-scroll">Always scroll</div>
<div class="overflow-visible">Visible</div>
<div class="overflow-x-auto">Scroll X only</div>
<div class="overflow-y-auto">Scroll Y only</div>
<div class="overflow-x-hidden">Hide X overflow</div>
<div class="overflow-y-hidden">Hide Y overflow</div>

Opacity

Set element opacity with opacity-0, opacity-25, opacity-50, opacity-75, opacity-100.

opacity-100
opacity-75
opacity-50
opacity-25
opacity-0
View code
<div class="opacity-0">Invisible (opacity 0)</div>
<div class="opacity-25">25% opacity</div>
<div class="opacity-50">50% opacity</div>
<div class="opacity-75">75% opacity</div>
<div class="opacity-100">Full opacity</div>

Position

Position utilities: position-*, top-*, start-*, end-*, bottom-*, and centering helpers.

top-0 start-0 top-0 end-0 bottom-0 start-0 bottom-0 end-0 center
.position-static .position-relative .position-absolute .position-fixed .position-sticky
View code
<div class="position-relative">
  <span class="position-absolute top-0 start-0">Top left</span>
  <span class="position-absolute top-0 end-0">Top right</span>
  <span class="position-absolute bottom-0 start-0">Bottom left</span>
  <span class="position-absolute bottom-0 end-0">Bottom right</span>
  <span class="position-absolute top-50 start-50 translate-middle">Center</span>
</div>

<!-- Position types -->
<div class="position-static">Static</div>
<div class="position-relative">Relative</div>
<div class="position-sticky top-0">Sticky top</div>

Shadows

Box shadow scale from shadow-none to shadow-xl.

shadow-none
shadow-sm
shadow
shadow-md
shadow-lg
shadow-xl
View code
<div class="shadow-none">No shadow</div>
<div class="shadow-sm">Small shadow</div>
<div class="shadow">Default shadow</div>
<div class="shadow-md">Medium shadow</div>
<div class="shadow-lg">Large shadow</div>
<div class="shadow-xl">Extra large shadow</div>

Sizing

Width and height utilities: w-*, h-*, mw-100, mh-100, vw-100, vh-100.

w-100
w-75
w-50
w-25
View code
<!-- Width -->
<div class="w-25">25%</div>
<div class="w-50">50%</div>
<div class="w-75">75%</div>
<div class="w-100">100%</div>
<div class="w-auto">Auto</div>

<!-- Height -->
<div class="h-25">25%</div>
<div class="h-50">50%</div>
<div class="h-100">100%</div>
<div class="h-auto">Auto</div>

<!-- Max -->
<img class="mw-100" src="..." alt="">
<div class="mh-100">Max height 100%</div>

<!-- Viewport -->
<div class="vw-100">Full viewport width</div>
<div class="vh-100">Full viewport height</div>
<div class="min-vh-100">Min full height</div>

Spacing

Margin (m-*, mt-*, mb-*, ms-*, me-*, mx-*, my-*) and Padding (p-*, etc.) utilities. Scale: 0–5.

Margin scale
m-0
m-1
m-2
m-3
m-4
m-5
Padding scale
p-0
p-1
p-2
p-3
p-4
p-5
View code
<!-- Margin (0=0, 1=.25rem, 2=.5rem, 3=1rem, 4=1.5rem, 5=3rem) -->
<div class="m-3">margin all 1rem</div>
<div class="mt-3">margin-top</div>
<div class="mb-3">margin-bottom</div>
<div class="ms-3">margin-left</div>
<div class="me-3">margin-right</div>
<div class="mx-3">margin horizontal</div>
<div class="my-3">margin vertical</div>
<div class="mx-auto">center block</div>

<!-- Padding -->
<div class="p-3">padding all 1rem</div>
<div class="pt-3">padding-top</div>
<div class="px-4 py-2">pill padding</div>

<!-- Gap (for flex/grid) -->
<div class="d-flex gap-2">gap .5rem</div>
<div class="d-flex gap-4">gap 1.5rem</div>

Text

Alignment, wrapping, transform, weight, style, size, and decoration utilities for typography.

.text-start — left aligned

.text-center — center aligned

.text-end — right aligned

fw-light fw-normal fw-medium fw-semibold fw-bold fw-extrabold fst-italic
text-uppercase TEXT-LOWERCASE text-capitalize
text-decoration-none text-decoration-underline line-through
fs-1 fs-3 fs-5 fs-6 fs-sm fs-xs

.text-truncate — this text is very long and will be truncated with an ellipsis at the end.

.text-nowrap — will not wrap to a second line even if it is long

View code
<!-- Alignment -->
<p class="text-start">Left</p>
<p class="text-center">Center</p>
<p class="text-end">Right</p>

<!-- Weight -->
<span class="fw-light">Light (300)</span>
<span class="fw-normal">Normal (400)</span>
<span class="fw-semibold">Semibold (600)</span>
<span class="fw-bold">Bold (700)</span>

<!-- Style -->
<span class="fst-italic">Italic</span>
<span class="fst-normal">Normal style</span>

<!-- Transform -->
<span class="text-uppercase">uppercase</span>
<span class="text-lowercase">lowercase</span>
<span class="text-capitalize">capitalize</span>

<!-- Decoration -->
<a class="text-decoration-none">No underline</a>
<a class="text-decoration-underline">Underline</a>
<span class="text-decoration-line-through">Strikethrough</span>

<!-- Wrap / Truncate -->
<p class="text-nowrap">No wrap</p>
<p class="text-wrap">Normal wrap</p>
<p class="text-truncate">Truncate long text...</p>
<p class="text-break">Force break-word</p>

<!-- Font sizes -->
<span class="fs-1">fs-1 (2.5rem)</span>
<span class="fs-3">fs-3 (1.75rem)</span>
<span class="fs-6">fs-6 (1rem)</span>
<span class="fs-sm">fs-sm (.875rem)</span>
<span class="fs-xs">fs-xs (.75rem)</span>

Vertical align

Vertical alignment utilities for inline and table-cell elements.

Text baseline top middle bottom text-top text-bottom
View code
<span class="align-baseline">baseline</span>
<span class="align-top">top</span>
<span class="align-middle">middle</span>
<span class="align-bottom">bottom</span>
<span class="align-text-top">text-top</span>
<span class="align-text-bottom">text-bottom</span>

Visibility

.visible and .invisible toggle visibility without affecting layout (unlike d-none).

.visible
.invisible (space preserved)
View code
<!-- Still takes up space, just not visible -->
<div class="visible">Visible</div>
<div class="invisible">Hidden but layout preserved</div>

<!-- To remove from layout completely, use d-none -->
<div class="d-none">Removed from layout</div>

Z-index

Z-index scale utilities: z-n1, z-0, z-1, z-2, z-3.

z-1
z-2
z-3
View code
<div class="position-relative">
  <div class="position-absolute z-n1">Behind (-1)</div>
  <div class="position-absolute z-0">Layer 0</div>
  <div class="position-absolute z-1">Layer 1</div>
  <div class="position-absolute z-2">Layer 2</div>
  <div class="position-absolute z-3">Top layer (3)</div>
</div>

Collapse

Toggle visibility of content with data-bs-toggle="collapse" and data-bs-target="#id". Use data-bs-parent for accordion behavior.

This content is now visible! Click the button again to hide it.

Accordion (one open at a time)

Content for section 1.
Content for section 2.
View code
<!-- Collapse: data-bs-toggle + data-bs-target to #id panel -->
<button type="button" class="btn btn-primary"
        data-bs-toggle="collapse"
        data-bs-target="#collapse-demo-1"
        aria-expanded="false">
  Toggle content
</button>
<div id="collapse-demo-1" class="collapse">
  This content is now visible! Click the button again to hide it.
</div>

<!-- Accordion: data-bs-parent so only one panel stays open -->
<div class="accordion" id="collapse-parent">
  <div class="accordion-item">
    <div class="accordion-header">
      <button type="button" class="accordion-button" aria-expanded="false">
        Section 1
        <svg class="accordion-icon" …></svg>
      </button>
    </div>
    <div id="collapse-acc-1" class="accordion-collapse collapse" data-bs-parent="#collapse-parent">
      <div class="accordion-body">Content for section 1.</div>
    </div>
  </div>
  <div class="accordion-item">
    <div class="accordion-header">
      <button type="button" class="accordion-button" aria-expanded="false">Section 2 …</button>
    </div>
    <div id="collapse-acc-2" class="accordion-collapse collapse" data-bs-parent="#collapse-parent">
      <div class="accordion-body">Content for section 2.</div>
    </div>
  </div>
</div>

List group

Flexible component for displaying lists of content with optional active states, colours, and action variants.

Default

  • Active item
  • Normal item
  • Another item
  • Fourth item
  • Disabled

Contextual

  • Primary
  • Success
  • Danger
  • Warning
  • Secondary
View code
<!-- List group default -->
<ul class="list-group">
  <li class="list-group-item active">Active item</li>
  <li class="list-group-item">Normal item</li>
  <li class="list-group-item">Another item</li>
  <li class="list-group-item">Fourth item</li>
  <li class="list-group-item disabled">Disabled</li>
</ul>

<!-- Clickable link -->
<div class="list-group">
  <a href="#" class="list-group-item list-group-item-action">Dashboard</a>
  <a href="#" class="list-group-item list-group-item-action active">Components</a>
  <a href="#" class="list-group-item list-group-item-action">Settings</a>
  <a href="#" class="list-group-item list-group-item-action">Profile</a>
  <a href="#" class="list-group-item list-group-item-action">Help</a>
</div>

<!-- Contextual colors -->
<ul class="list-group">
  <li class="list-group-item list-group-item-primary">Primary</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-secondary">Secondary</li>
</ul>

Scrollspy

Automatically update navigation links based on scroll position. Set data-bs-spy="scroll" on the scrollable container and data-bs-target to your nav's selector.

Section 1

Scroll inside this panel — the section links above update as each heading passes the top edge.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.

Section 2

The active nav link uses the .active class. Set data-bs-offset to match padding or a sticky header inside the scroll area.

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget.

Tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.

Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi.

Section 3

Keep scrolling until this block — Section 3 should stay highlighted at the bottom of the panel.

Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis.

Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat.

Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus. Phasellus ultrices nulla quis nibh. Quisque a odio. Morbi non lectus.

View code
<!-- Scrollspy: data-bs-spy on scroll panel + data-bs-target to nav -->
<div class="sf-scrollspy-demo">
  <nav id="spy-nav-demo" class="sf-scrollspy-nav">
    <p class="sf-scrollspy-nav-label">On this page</p>
    <ul class="sf-scrollspy-nav-list">
      <li><a href="#spy-sec-1" class="sf-sidebar-link">Section 1</a></li>
      <li><a href="#spy-sec-2" class="sf-sidebar-link">Section 2</a></li>
      <li><a href="#spy-sec-3" class="sf-sidebar-link">Section 3</a></li>
    </ul>
  </nav>

  <div data-bs-spy="scroll"
       data-bs-target="#spy-nav-demo"
       data-bs-offset="0"
       class="sf-scrollspy-panel">
  <h3 id="spy-sec-1" class="sf-scrollspy-heading">Section 1</h3>
  <p class="sf-scrollspy-text">...</p>
  <h3 id="spy-sec-2" class="sf-scrollspy-heading">Section 2</h3>
  <p class="sf-scrollspy-text">...</p>
  <h3 id="spy-sec-3" class="sf-scrollspy-heading">Section 3</h3>
  <p class="sf-scrollspy-text">...</p>
  </div>
</div>

<!-- Mobile: nav pills on top, full-width panel (see .sf-scrollspy-demo CSS) -->

Input group

Extend form controls with prepended or appended text, icons, and buttons using .input-group and .input-group-text.

@
.00
$ USD
View code
<!-- Prepend text -->
<div class="input-group">
  <span class="input-group-text rounded">@</span>
  <input type="text" class="form-control rounded" placeholder="username">
</div>

<!-- Append text -->
<div class="input-group">
  <input type="text" class="form-control rounded" placeholder="Amount">
  <span class="input-group-text rounded">.00</span>
</div>

<!-- Both sides -->
<div class="input-group">
  <span class="input-group-text rounded">$</span>
  <input type="number" class="form-control rounded" placeholder="0.00">
  <span class="input-group-text rounded">USD</span>
</div>

<!-- With button -->
<div class="input-group">
  <input type="text" class="form-control rounded" placeholder="Search...">
  <button class="btn btn-primary">Search</button>
</div>

<!-- File upload -->
<div class="input-group input-group-file">
  <label class="btn btn-outline-secondary" for="my-file">Browse</label>
  <input type="file" id="my-file" class="visually-hidden" tabindex="-1">
  <input type="text" class="form-control rounded" readonly placeholder="Choose file..." data-file-display>
</div>

Floating labels

Label starts inside the field like a placeholder; on focus or when filled it animates up to the top border. For <select>, use an empty hidden first <option> so the label is the only placeholder text.

View code
<!-- Text input — placeholder=" " required for float detection -->
<div class="form-floating">
  <input type="email" class="form-control rounded"
         id="emailInput" placeholder=" ">
  <label for="emailInput">Email address</label>
</div>

<!-- Password -->
<div class="form-floating">
  <input type="password" class="form-control rounded"
         id="passInput" placeholder=" ">
  <label for="passInput">Password</label>
</div>

<!-- Select — empty hidden first option; label is the placeholder -->
<div class="form-floating">
  <select class="form-select rounded" id="mySelect">
    <option value="" selected disabled hidden></option>
    <option value="1">One</option>
  </select>
  <label for="mySelect">Select option</label>
</div>

Validation

Show valid/invalid states using .is-valid / .is-invalid classes, or add .was-validated to the form to use native HTML5 constraint API.

Looks good!
Password is required (min. 8 characters).
Please select a valid city.
View code
<!-- Manual validation: .is-valid / .is-invalid + .valid-feedback / .invalid-feedback -->
<form novalidate>
  <div>
    <label class="form-label" for="val-email">Email address</label>
    <input type="email" class="form-control rounded is-valid" id="val-email" value="hello@suhu.dev">
    <div class="valid-feedback">Looks good!</div>
  </div>
  <div>
    <label class="form-label" for="val-pass">Password</label>
    <input type="password" class="form-control rounded is-invalid" id="val-pass" value="">
    <div class="invalid-feedback">Password is required (min. 8 characters).</div>
  </div>
  <div>
    <label class="form-label" for="val-city">City</label>
    <select class="form-select rounded is-invalid" id="val-city">
      <option value="">Choose a city...</option>
      <option>Jakarta</option>
      <option>Bandung</option>
    </select>
    <div class="invalid-feedback">Please select a valid city.</div>
  </div>
  <button type="submit" class="btn btn-primary">Submit form</button>
</form>

<!-- Or HTML5 validation: add .was-validated to <form> -->