diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json index 965cb974b8d2..53a01dd33397 100644 --- a/.bundlewatch.config.json +++ b/.bundlewatch.config.json @@ -26,15 +26,15 @@ }, { "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 index 000000000000..51502dce4791 --- /dev/null +++ b/scss/_avatar.scss @@ -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; + } +} diff --git a/scss/bootstrap.scss b/scss/bootstrap.scss index 09f9662639ef..764d9f3149a8 100644 --- a/scss/bootstrap.scss +++ b/scss/bootstrap.scss @@ -13,6 +13,7 @@ // Components @forward "accordion"; @forward "alert"; +@forward "avatar"; @forward "badge"; @forward "breadcrumb"; @forward "card"; diff --git a/site/data/sidebar.yml b/site/data/sidebar.yml index 50df81cf06d8..ca6b349be9a6 100644 --- a/site/data/sidebar.yml +++ b/site/data/sidebar.yml @@ -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 index 000000000000..b5324104f33d --- /dev/null +++ b/site/src/content/docs/components/avatar.mdx @@ -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. + + + mdo + `} /> + +### Initials + +Use text content inside `.avatar` for initials-based avatars. + +AB +CD +EF +GG +GH +IJ +KL +MN +OP`} /> + +Use `.avatar-subtle` to create a subtle avatar. + +AB +CD +EF +GG +GH +IJ +KL +MN +OP`} /> + +## Sizes + +Avatars come in multiple sizes: extra small, small, default, large, and extra large. + + + mdo + + + mdo + + + mdo + + + mdo + + + mdo + `} /> + +## 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 + + + mdo + + + + mdo + + + + mdo + + + + mdo + + `} /> + +### Status with sizes + +The status indicator scales with the avatar size. + + + mdo + + + + mdo + + + + mdo + + + + mdo + + + + mdo + + `} /> + +## 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. + + + + mdo + + + mdo + + + mdo + + + mdo + + + mdo + + `} /> + +### Stack with sizes + +As a shorthand, size classes are available for `.avatar-stack` and `.avatar`. + + + + mdo + + + mdo + + + mdo + + + +
+ + mdo + + + mdo + + + mdo + +
+ +
+ + mdo + + + mdo + + + mdo + +
+ +
+ + mdo + + + mdo + + + mdo + +
+ +
+ + mdo + + + mdo + + + mdo + +
`} /> + +### Stack with count + +Combine with initials to show a count of additional users. + + + + mdo + + + mdo + + + User avatar + + +5 + `} /> + +## CSS + +### Variables + + + + + +### Sass variables + +