Aspect Ratio
Displays content within a chosen width-to-height ratio.
- v1.0.0
- SSR
- Zoneless
- 0.4 kB gzip
import { Component } from '@angular/core';
import { AspectRatio } from '@/ui/aspect-ratio/aspect-ratio';
/** An illustration kept at 16:9 whatever the width. */
@Component({
selector: 'example-aspect-ratio-demo',
imports: [AspectRatio],
template: `
<div
ndAspectRatio
[ratio]="16 / 9"
class="w-full max-w-md overflow-hidden rounded-lg bg-muted"
>
<svg
viewBox="0 0 160 90"
role="img"
aria-label="Hills under a setting sun"
class="size-full"
>
<circle cx="112" cy="34" r="14" class="fill-primary/30" />
<path d="M0 70 Q40 40 80 62 T160 52 V90 H0Z" class="fill-primary/20" />
<path d="M0 80 Q50 58 100 76 T160 70 V90 H0Z" class="fill-primary/40" />
</svg>
</div>
`,
})
export class AspectRatioDemoExample {}
Aspect Ratio keeps its content at a width-to-height ratio as the width
changes, for images, video and maps. It sets the CSS aspect-ratio
property from ratio, such as 16 / 9. Size the media inside to fill
it.
Installation
npx @ndop/cli add aspect-ratiopnpm dlx @ndop/cli add aspect-ratioyarn dlx @ndop/cli add aspect-ratiobunx @ndop/cli add aspect-ratioInstall the dependencies
npm install @ndop/corepnpm add @ndop/coreyarn add @ndop/corebun add @ndop/coreCopy the code into your project
At the paths your
ndop.jsonaliases point to; these use the defaults.src/app/ui/aspect-ratio/aspect-ratio.tsimport { computed, Directive, input, numberAttribute } from '@angular/core'; import { cn } from '@/lib/utils'; /** * Keeps its content at a width-to-height ratio, with the CSS * `aspect-ratio` property. Put an image or media inside, sized to fill it. * * @example * ```html * <div ndAspectRatio [ratio]="16 / 9"> * <img src="/cover.jpg" alt="…" class="size-full object-cover" /> * </div> * ``` */ @Directive({ selector: '[ndAspectRatio]', host: { 'data-slot': 'aspect-ratio', '[style.aspect-ratio]': 'ratio()', '[class]': 'classes()', }, }) export class AspectRatio { /** Width divided by height, such as `16 / 9`. */ readonly ratio = input(1, { transform: numberAttribute }); /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn('relative w-full', this.class()), ); }src/app/lib/utils.ts/** * Class helpers used by every styled item. They come from `@ndop/core`; * this file is yours, so it is the place to customize them, for example by * wrapping `cn` with an extended tailwind-merge configuration for your own * utilities. * * @packageDocumentation */ export { cn, variants, type ClassValue, type VariantOf } from '@ndop/core';Update the import paths to match your project
The files import each other through the
@/uiand@/libaliases.
Usage
import { AspectRatio } from '@/ui/aspect-ratio/aspect-ratio';<div
ndAspectRatio
[ratio]="16 / 9"
class="w-full max-w-md overflow-hidden rounded-lg bg-muted"
>
<svg
viewBox="0 0 160 90"
role="img"
aria-label="Hills under a setting sun"
class="size-full"
>
<circle cx="112" cy="34" r="14" class="fill-primary/30" />
<path d="M0 70 Q40 40 80 62 T160 52 V90 H0Z" class="fill-primary/20" />
<path d="M0 80 Q50 58 100 76 T160 70 V90 H0Z" class="fill-primary/40" />
</svg>
</div>Examples
Aspect Ratio square
Square tiles in a grid.
- Jan
- Feb
- Mar
- Apr
- May
- Jun
import { Component } from '@angular/core';
import { AspectRatio } from '@/ui/aspect-ratio/aspect-ratio';
/** Square tiles in a grid that stay square as the grid resizes. */
@Component({
selector: 'example-aspect-ratio-square',
imports: [AspectRatio],
template: `
<ul class="grid w-full max-w-md grid-cols-3 gap-2">
@for (tile of tiles; track tile) {
<li
ndAspectRatio
class="grid place-items-center rounded-md bg-muted text-sm font-medium"
>
{{ tile }}
</li>
}
</ul>
`,
})
export class AspectRatioSquareExample {
protected readonly tiles = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
}
Accessibility
Aspect Ratio has no keyboard interaction or focus handling of its own; it keeps the semantics of the element it's on.
API reference
AspectRatio
[ndAspectRatio]Keeps its content at a width-to-height ratio, with the CSS
aspect-ratio property. Put an image or media inside, sized to fill it.
| AspectRatio binding | Type | Default |
|---|---|---|
[ratio]Width divided by height, such as 16 / 9. | number | 1 |
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | aspect-ratio |
Agent notes
Do
- Put an
imgorvideoinside withclass="size-full object-cover". - Give images a meaningful
alt, oralt=""when they're decorative. - Use it to stop the page shifting while media loads.
Don't
- Don't put text that can grow inside it; it will overflow.
- Don't use it for things with a natural size, such as icons.