MediaObject

The <smart-media-object> component provides a flexible media-object layout wrapper with an InjectionToken-based extension mechanism. It pairs an image with adjacent body content (the classic media object pattern) and renders a default MediaObjectStandardComponent which can be replaced via MEDIA_OBJECT_STANDARD_COMPONENT_TOKEN.


Usage

<smart-media-object
  [mediaUrl]="avatarUrl"
  mediaAlt="Portrait of Lindsay Walton"
  [options]="options"
>
  <h4>Lindsay Walton</h4>
  <p>Joined the design systems team in March.</p>
</smart-media-object>

Components

MediaObjectComponent (<smart-media-object>)

Main wrapper component. Renders MediaObjectStandardComponent by default. When MEDIA_OBJECT_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet. Projects body content into the standard via <ng-content />.

MediaObjectStandardComponent (<smart-media-object-standard>)

Default concrete implementation. Renders an <img> and a .smart-media-object-body slot inside a wrapper <div>. The wrapper exposes data-position and data-alignment attributes derived from options for CSS styling.

MediaObjectBaseComponent (abstract)

Abstract base directive for extending custom media-object implementations. Declares mediaUrl (required), mediaAlt (required), options, and cssClass.

API

Inputs

InputTypeDefaultDescription
mediaUrlInputSignal<string>requiredImage URL
mediaAltInputSignal<string>requiredImage alt text
optionsInputSignal<IMediaObjectOptions | undefined>undefinedLayout configuration
classInputSignal<string>''External CSS classes (alias for cssClass)

Content Projection

The wrapper projects body content via <ng-content /> into the .smart-media-object-body slot of the standard component.

IMediaObjectOptions

The standard renders:

  • data-position attribute: defaults to 'left' when not provided
  • data-alignment attribute: only set when options.alignment is provided

MEDIA_OBJECT_STANDARD_COMPONENT_TOKEN

InjectionToken that allows replacing the default MediaObjectStandardComponent with a custom implementation. Provide a Type<MediaObjectBaseComponent> to override.

Extending the base class

import {
  ChangeDetectionStrategy,
  Component,
  computed,
  input,
  ViewEncapsulation,
} from '@angular/core';

import {
  IMediaObjectOptions,
  MEDIA_OBJECT_STANDARD_COMPONENT_TOKEN,
  MediaObjectBaseComponent,
  MediaObjectComponent,
} from '@smartsoft001/angular';

/**
 * A custom media object built on `MediaObjectBaseComponent`.
 *
 * The base contributes the `mediaUrl`, `mediaAlt`, `options` and `class`
 * inputs; the implementation owns the markup around them.
 */
@Component({
  selector: 'docs-custom-media-object',
  template: `
    <article
      [class]="containerClasses()"
      [attr.data-alignment]="options()?.alignment ?? 'top'"
      [attr.data-position]="options()?.position ?? 'left'"
    >
      <img
        class="docs-media-object__media"
        [src]="mediaUrl()"
        [alt]="mediaAlt()"
        width="64"
        height="64"
      />

      <!--
        smart-media-object renders a custom implementation through
        NgComponentOutlet, which does not forward projected content. Only
        mediaUrl, mediaAlt, options and cssClass arrive here, so a custom media
        object renders its own body instead of relying on ng-content.
      -->
      <div class="docs-media-object__body">
        <h3 class="docs-media-object__title">Lindsay Walton</h3>
        <p class="docs-media-object__text">
          Front-end developer, joined the design systems team in March.
        </p>
      </div>
    </article>
  `,
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomMediaObjectComponent extends MediaObjectBaseComponent {
  // NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
  override cssClass = input<string>('');

  containerClasses = computed(() =>
    [
      'docs-media-object',
      this.options()?.wide ? 'docs-media-object--wide' : '',
      this.options()?.nested ? 'docs-media-object--nested' : '',
      this.cssClass(),
    ]
      .filter(Boolean)
      .join(' '),
  );
}

/**
 * Registering the implementation against
 * `MEDIA_OBJECT_STANDARD_COMPONENT_TOKEN` makes every `<smart-media-object>`
 * in this injector render it instead of the standard variation.
 */
@Component({
  selector: 'docs-media-object-custom-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [MediaObjectComponent],
  providers: [
    {
      provide: MEDIA_OBJECT_STANDARD_COMPONENT_TOKEN,
      useValue: CustomMediaObjectComponent,
    },
  ],
  template: `
    <smart-media-object
      [mediaUrl]="mediaUrl"
      mediaAlt="Portrait of Lindsay Walton"
      [options]="options"
      class="docs-media-object--demo"
    />
  `,
})
export class MediaObjectCustomExampleComponent {
  mediaUrl =
    'https://images.unsplash.com/photo-1568602471122-7832951cc4c5?auto=format&fit=facearea&facepad=2&w=300&h=300&q=80';

  options: IMediaObjectOptions = {
    alignment: 'center',
    position: 'right',
    responsive: true,
  };
}

Preset

MediaObjectPresetComponent (selector smart-media-object-preset) is a styled, drop-in replacement for the standard component. It extends MediaObjectStandardComponent and reuses the same inputs (mediaUrl, mediaAlt, options, class), so registering it through the token restyles every <smart-media-object>.

Look: a flex row (smart:flex smart:gap-4) with a rounded, cover-fitted thumbnail (smart:size-16 smart:rounded-lg smart:object-cover smart:shrink-0) beside small, muted body text (smart:text-sm smart:text-gray-700 smart:dark:text-gray-300). All utilities are smart:-prefixed and dark-mode aware.

IMediaObjectOptions drives every variant (no new fields):

  • position: 'right' reverses the row (smart:flex-row-reverse).
  • alignment: 'top' | 'center' | 'bottom' maps to smart:items-start | items-center | items-end; 'stretched' instead makes the media fill the row height (smart:self-stretch smart:h-auto).
  • responsive: true stacks into a column on mobile then rows out from sm (smart:flex-col smart:sm:flex-row, reversed when position: 'right').
  • nested: true tightens the gap to smart:gap-3 and indents with smart:mt-4.
  • wide: true widens the thumbnail to smart:w-32 (keeping a smart:h-16 height).

data-role hooks (root, media, body) and the data-position / data-alignment attributes are exposed for testing and targeting; the smart-media-object-body marker class is preserved for content projection parity with the standard component.

Register it as the standard replacement:

Class recipes live in media-object/preset/preset-classes.util.ts (getMediaObjectRootClasses, getMediaObjectMediaClasses, getMediaObjectBodyClasses).

Gaps: the media is always an <img> (no icon/video slot), and wide uses a fixed w-32/h-16 footprint rather than an intrinsic aspect ratio.

Source

The component lives in packages/shared/angular/src/lib/components/media-object and is documented for Claude Code by the angular-components-media-object skill.