Components
Avatar
An image with automatic fallback, wrapping Morphos's Avatar/AvatarImage/AvatarFallback.
Installation
npx kosmesis add avatarpnpm dlx kosmesis add avataryarn dlx kosmesis add avatarbunx kosmesis add avatarInstall the following dependencies:
npm install @morphos/feedbackpnpm add @morphos/feedbackyarn add @morphos/feedbackbun add @morphos/feedbackCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import { Avatar as MorphosAvatar, AvatarFallback as MorphosAvatarFallback, AvatarImage as MorphosAvatarImage, type AvatarFallbackProps as MorphosAvatarFallbackProps } from "@morphos/feedback";import { cn } from "@/lib/utils";/** * Extends (not wraps) Morphos's `Avatar` so `new Avatar()` still yields a real instance with * `.setImageStatus()`/`.imageLoaded`/`.imageError` — what `AvatarImage`/`AvatarFallback` need via * their `avatar` prop. */@Component()export class Avatar extends MorphosAvatar { render() { return ( <span id={this.id} class={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", this.class)} data-status={() => this._imageStatus}> {this.children} </span> ); }}export interface AvatarImageProps { avatar: Avatar; src: string; alt: string; class?: string;}@Component()export class AvatarImage extends StatelessComponent<AvatarImageProps> { render() { const { avatar, src, alt, class: cls } = this.props; return <MorphosAvatarImage avatar={avatar} src={src} alt={alt} class={cn("aspect-square size-full", cls)} />; }}@Component()export class AvatarFallback extends StatelessComponent<MorphosAvatarFallbackProps> { render() { const { avatar, class: cls, id, children } = this.props; return ( <MorphosAvatarFallback avatar={avatar} id={id} class={cn("flex size-full items-center justify-center rounded-full bg-muted text-sm font-medium", cls)} > {children} </MorphosAvatarFallback> ); }}Install the following dependencies:
npm install @morphos/feedback @praxisjs/csspnpm add @morphos/feedback @praxisjs/cssyarn add @morphos/feedback @praxisjs/cssbun add @morphos/feedback @praxisjs/cssCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import { Avatar as MorphosAvatar, AvatarFallback as MorphosAvatarFallback, AvatarImage as MorphosAvatarImage, type AvatarFallbackProps as MorphosAvatarFallbackProps } from "@morphos/feedback";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class AvatarStyles extends Stylesheet { $root = this.css({ position: "relative", display: "flex", width: "2rem", height: "2rem", flexShrink: 0, overflow: "hidden", borderRadius: "9999px", }); $image = this.css({ aspectRatio: "1 / 1", width: "100%", height: "100%" }); $fallback = this.css({ display: "flex", width: "100%", height: "100%", alignItems: "center", justifyContent: "center", borderRadius: "9999px", backgroundColor: t.muted, fontSize: "0.875rem", fontWeight: 500, });}/** * Extends (not wraps) Morphos's `Avatar` so `new Avatar()` still yields a real instance with * `.setImageStatus()`/`.imageLoaded`/`.imageError` — what `AvatarImage`/`AvatarFallback` need via * their `avatar` prop. */@Component()export class Avatar extends MorphosAvatar { @Styled(AvatarStyles) $s!: AvatarStyles; render() { return ( <span id={this.id} class={cx(this.$s.$root, this.class)} data-status={() => this._imageStatus}> {this.children} </span> ); }}export interface AvatarImageProps { avatar: Avatar; src: string; alt: string; class?: string;}@Component()export class AvatarImage extends StatelessComponent<AvatarImageProps> { @Styled(AvatarStyles) $s!: AvatarStyles; render() { const { avatar, src, alt, class: cls } = this.props; return <MorphosAvatarImage avatar={avatar} src={src} alt={alt} class={cx(this.$s.$image, cls)} />; }}@Component()export class AvatarFallback extends StatelessComponent<MorphosAvatarFallbackProps> { @Styled(AvatarStyles) $s!: AvatarStyles; render() { const { avatar, class: cls, id, children } = this.props; return ( <MorphosAvatarFallback avatar={avatar} id={id} class={cx(this.$s.$fallback, cls)}> {children} </MorphosAvatarFallback> ); }}Examples
About
Wraps Morphos's Avatar compound component. AvatarImage reports load/error status onto the
root's data-status attribute; AvatarFallback reads that status to hide itself once the image
has loaded.
Usage
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
@Component()
class MyPage extends StatefulComponent {
@State() avatar = new Avatar();
render() {
return (
<Avatar>
<AvatarImage avatar={this.avatar} src="/avatars/01.png" alt="Jane Doe" />
<AvatarFallback avatar={this.avatar}>JD</AvatarFallback>
</Avatar>
);
}
}Avatar renders the wrapping <span> itself, but AvatarImage/AvatarFallback still need the
same Avatar instance passed via their avatar prop to read/report load status — instantiate
it once (@State() avatar = new Avatar()), same as every other Morphos compound component.