]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
New avatar component (#41997)
authorMark Otto <markd.otto@gmail.com>
Thu, 8 Jan 2026 01:21:07 +0000 (17:21 -0800)
committerMark Otto <markdotto@gmail.com>
Fri, 9 Jan 2026 04:14:09 +0000 (20:14 -0800)
* New avatar component

* bump

.bundlewatch.config.json
scss/_avatar.scss [new file with mode: 0644]
scss/bootstrap.scss
site/data/sidebar.yml
site/src/content/docs/components/avatar.mdx [new file with mode: 0644]

index 965cb974b8d2f0c09424ed4f6e0d240c8e08259c..53a01dd33397fd16bf0dc53ac64c9cbd29c7fae4 100644 (file)
     },
     {
       "path": "./dist/css/bootstrap.css",
-      "maxSize": "37.5 kB"
+      "maxSize": "38.0 kB"
     },
     {
       "path": "./dist/css/bootstrap.min.css",
-      "maxSize": "36.25 kB"
+      "maxSize": "36.75 kB"
     },
     {
       "path": "./dist/js/bootstrap.bundle.js",
-      "maxSize": "67.75 kB"
+      "maxSize": "67.5 kB"
     },
     {
       "path": "./dist/js/bootstrap.bundle.min.js",
@@ -42,7 +42,7 @@
     },
     {
       "path": "./dist/js/bootstrap.esm.js",
-      "maxSize": "39.0 kB"
+      "maxSize": "38.75 kB"
     },
     {
       "path": "./dist/js/bootstrap.esm.min.js",
@@ -50,7 +50,7 @@
     },
     {
       "path": "./dist/js/bootstrap.js",
-      "maxSize": "39.75 kB"
+      "maxSize": "39.5 kB"
     },
     {
       "path": "./dist/js/bootstrap.min.js",
diff --git a/scss/_avatar.scss b/scss/_avatar.scss
new file mode 100644 (file)
index 0000000..51502dc
--- /dev/null
@@ -0,0 +1,137 @@
+@use "variables" as *;
+@use "theme" as *;
+@use "mixins/border-radius" as *;
+
+// scss-docs-start avatar-variables
+$avatar-size:                 2.5rem !default;
+$avatar-size-xs:              1.5rem !default;
+$avatar-size-sm:              2rem !default;
+$avatar-size-lg:              3rem !default;
+$avatar-size-xl:              4rem !default;
+$avatar-border-radius:        50% !default;
+$avatar-border-width:         2px !default;
+$avatar-border-color:         var(--bg-body) !default;
+$avatar-bg:                   var(--bg-2) !default;
+$avatar-color:                var(--color-body) !default;
+
+$avatar-status-size:          .75rem !default;
+$avatar-status-border-width:  2px !default;
+$avatar-status-border-color:  var(--bg-body) !default;
+
+$avatar-stack-spacing:        -.3 !default; // Percentage of avatar size (negative for overlap)
+// scss-docs-end avatar-variables
+
+@layer components {
+  .avatar {
+    // scss-docs-start avatar-css-vars
+    --avatar-border-radius: #{$avatar-border-radius};
+    --avatar-border-width: #{$avatar-border-width};
+    --avatar-border-color: #{$avatar-border-color};
+    --avatar-bg: #{$avatar-bg};
+    --avatar-color: #{$avatar-color};
+    --avatar-status-size: #{$avatar-status-size};
+    --avatar-status-border-width: #{$avatar-status-border-width};
+    --avatar-status-border-color: #{$avatar-status-border-color};
+    // scss-docs-end avatar-css-vars
+
+    position: relative;
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    width: var(--avatar-size, #{$avatar-size});
+    height: var(--avatar-size, #{$avatar-size});
+    font-size: calc(var(--avatar-size) * .4);
+    font-weight: $font-weight-medium;
+    line-height: 1;
+    color: var(--theme-contrast, var(--avatar-color));
+    text-transform: uppercase;
+    vertical-align: middle;
+    background-color: var(--theme-bg, var(--avatar-bg));
+    @include border-radius(var(--avatar-border-radius));
+  }
+
+  .avatar-subtle {
+    color: var(--theme-text, var(--avatar-color));
+    background-color: var(--theme-bg-subtle, var(--avatar-bg));
+  }
+
+  .avatar-img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+    @include border-radius(inherit);
+  }
+
+  .avatar-status {
+    position: absolute;
+    right: calc(var(--avatar-status-border-width) * -1);
+    bottom: calc(var(--avatar-status-border-width) * -1);
+    width: var(--avatar-status-size);
+    height: var(--avatar-status-size);
+    background-color: var(--gray-400);
+    border: var(--avatar-status-border-width) solid var(--avatar-status-border-color);
+    @include border-radius(50%);
+
+    &.status-online {
+      background-color: var(--green-500);
+    }
+
+    &.status-offline {
+      background-color: var(--gray-400);
+      @include border-radius(20%);
+    }
+
+    &.status-busy {
+      background-color: var(--red-500);
+      @include border-radius(20%);
+    }
+
+    &.status-away {
+      background-color: var(--yellow-500);
+    }
+  }
+
+  .avatar-stack {
+    display: inline-flex;
+    flex-direction: row-reverse;
+
+    .avatar {
+      // Stack spacing is calculated as a percentage of avatar size
+      margin-left: calc(var(--avatar-size, #{$avatar-size}) * #{$avatar-stack-spacing});
+      border: var(--avatar-border-width) solid var(--avatar-border-color);
+      mask-image: none;
+
+      &:last-child {
+        margin-left: 0;
+      }
+
+      &:hover {
+        z-index: 1;
+        transform: translateY(-2px);
+      }
+    }
+  }
+
+  .avatar-xs,
+  .avatar-stack-xs {
+    --avatar-size: #{$avatar-size-xs};
+    --avatar-status-size: .625rem;
+  }
+
+  .avatar-sm,
+  .avatar-stack-sm {
+    --avatar-size: #{$avatar-size-sm};
+  }
+
+  .avatar-lg,
+  .avatar-stack-lg {
+    --avatar-size: #{$avatar-size-lg};
+    --avatar-status-size: 1rem;
+  }
+
+  .avatar-xl,
+  .avatar-stack-xl {
+    --avatar-size: #{$avatar-size-xl};
+    --avatar-status-size: 1.25rem;
+  }
+}
index 09f9662639ef8d4b0efe2cda3d3a3c678ef9acc8..764d9f3149a84be97efc4e6178fc709accffd141 100644 (file)
@@ -13,6 +13,7 @@
 // Components
 @forward "accordion";
 @forward "alert";
+@forward "avatar";
 @forward "badge";
 @forward "breadcrumb";
 @forward "card";
index 50df81cf06d84b733faa49c6e62c4f49dde55f59..ca6b349be9a68a916852b6b2b1b447c7e9220287 100644 (file)
@@ -85,6 +85,7 @@
   pages:
     - title: Accordion
     - title: Alert
+    - title: Avatar
     - title: Badge
     - title: Breadcrumb
     - title: Buttons
diff --git a/site/src/content/docs/components/avatar.mdx b/site/src/content/docs/components/avatar.mdx
new file mode 100644 (file)
index 0000000..b532410
--- /dev/null
@@ -0,0 +1,229 @@
+---
+title: Avatar
+description: Documentation and examples for avatars, including image avatars, initials, status indicators, and avatar stacks.
+toc: true
+---
+
+## Examples
+
+Avatars are used to represent users or entities. They can display an image or initials as a fallback.
+
+### Image
+
+Use `.avatar` with an `.avatar-img` for image-based avatars. The parent `.avatar` element provides an easy wrapper for additional avatar features like status indicators and stacks. You're welcome to use the `.avatar-img` class on its own if you only need a single HTML element.
+
+<Example code={`<span class="avatar">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+  </span>`} />
+
+### Initials
+
+Use text content inside `.avatar` for initials-based avatars.
+
+<Example code={`<span class="avatar">AB</span>
+<span class="avatar theme-primary">CD</span>
+<span class="avatar theme-accent">EF</span>
+<span class="avatar theme-success">GG</span>
+<span class="avatar theme-danger">GH</span>
+<span class="avatar theme-warning">IJ</span>
+<span class="avatar theme-info">KL</span>
+<span class="avatar theme-inverse">MN</span>
+<span class="avatar theme-secondary">OP</span>`} />
+
+Use `.avatar-subtle` to create a subtle avatar.
+
+<Example code={`<span class="avatar">AB</span>
+<span class="avatar avatar-subtle theme-primary">CD</span>
+<span class="avatar avatar-subtle theme-accent">EF</span>
+<span class="avatar avatar-subtle theme-success">GG</span>
+<span class="avatar avatar-subtle theme-danger">GH</span>
+<span class="avatar avatar-subtle theme-warning">IJ</span>
+<span class="avatar avatar-subtle theme-info">KL</span>
+<span class="avatar avatar-subtle theme-inverse">MN</span>
+<span class="avatar avatar-subtle theme-secondary">OP</span>`} />
+
+## Sizes
+
+Avatars come in multiple sizes: extra small, small, default, large, and extra large.
+
+<Example code={`<span class="avatar avatar-xs">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+  </span>
+  <span class="avatar avatar-sm">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+  </span>
+  <span class="avatar">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+  </span>
+  <span class="avatar avatar-lg">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+  </span>
+  <span class="avatar avatar-xl">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+  </span>`} />
+
+## Status indicator
+
+Add a `.avatar-status` element inside the avatar to show a status indicator. Each status has a distinct shape and color:
+
+- `.status-online` — green circle
+- `.status-offline` — gray rounded square
+- `.status-busy` — red rounded square
+- `.status-away` — yellow circle
+
+<Example code={`<span class="avatar">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-online"></span>
+  </span>
+  <span class="avatar">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-offline"></span>
+  </span>
+  <span class="avatar">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-busy"></span>
+  </span>
+  <span class="avatar">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-away"></span>
+  </span>`} />
+
+### Status with sizes
+
+The status indicator scales with the avatar size.
+
+<Example code={`<span class="avatar avatar-xs">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-online"></span>
+  </span>
+  <span class="avatar avatar-sm">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-online"></span>
+  </span>
+  <span class="avatar">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-online"></span>
+  </span>
+  <span class="avatar avatar-lg">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-online"></span>
+  </span>
+  <span class="avatar avatar-xl">
+    <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    <span class="avatar-status status-online"></span>
+  </span>`} />
+
+## Avatar stack
+
+Use `.avatar-stack` to group multiple avatars together with overlapping effect. Avatars are rendered in reverse order so the first avatar appears on top. Stacks use a percentage of the avatar size to determine how much to overlap stacked avatars.
+
+<Example code={`<div class="avatar-stack">
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+  </div>`} />
+
+### Stack with sizes
+
+As a shorthand, size classes are available for `.avatar-stack` and `.avatar`.
+
+<Example class="vstack align-items-start gap-3" code={`<div class="avatar-stack avatar-stack-xs">
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+  </div>
+
+  <div class="avatar-stack avatar-stack-sm">
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+  </div>
+
+  <div class="avatar-stack">
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+  </div>
+
+  <div class="avatar-stack avatar-stack-lg">
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+  </div>
+
+  <div class="avatar-stack avatar-stack-xl">
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+  </div>`} />
+
+### Stack with count
+
+Combine with initials to show a count of additional users.
+
+<Example code={`<div class="avatar-stack">
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://github.com/mdo.png" alt="mdo">
+    </span>
+    <span class="avatar">
+      <img class="avatar-img" src="https://i.pravatar.cc/150?img=34" alt="User avatar">
+    </span>
+    <span class="avatar theme-secondary">+5</span>
+  </div>`} />
+
+## CSS
+
+### Variables
+
+<CSSVariables component="Avatar" className="avatar" />
+
+<ScssDocs name="avatar-css-vars" file="scss/_avatar.scss" />
+
+### Sass variables
+
+<ScssDocs name="avatar-variables" file="scss/_avatar.scss" />