npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@layout-projection/angular

v1.0.0-alpha.0

Published

Layout Projection and declarative layout animation for Angular

Downloads

11

Readme

Layout Projection / Angular

Layout Projection and declarative layout animation for Angular

npm i @layout-projection/{core,animation,angular}

Usage

The Angular adapter depends on several fundamental services to function. Use provideLayoutProjectionBuiltinSetup to provide the pre-built implementations of these services:

import { provideLayoutProjectionBuiltinSetup } from '@layout-projection/angular/setup';

export const APP_CONFIG: ApplicationConfig = {
  providers: [provideLayoutProjectionBuiltinSetup()],
};

Animating Layout Changes

The Angular adapter introduces two handy directives, LayoutNode and LayoutNodeAnimator, that allows you to animate layout updates simply by adding an attribute:

import { LayoutNode, LayoutNodeAnimator } from '@layout-projection/angular';

@Component({
  imports: [LayoutNode, LayoutNodeAnimator]
})
<div layout></div>

This layout attribute animates any layout changes, including previously unavailable CSS values such as switching justify-content between flex-start and flex-end:

<div
  layout
  [style.justify-content]="active ? 'flex-start' : 'flex-end'"
></div>

It is not required to change the layout via inline styles. You can change the layout by whatever means you like, like switching the presence of a CSS class or updating a custom attribute.

example-switcher

The layout change should happen immediately - no CSS transition or animation should be applied. The directives will take care of the animation and smoothly transition the layout from the old state to the new state.

A layout change can be anything:

  • updating width or height
  • changing number of grid columns
  • adding or removing items from a list
  • reordering the existing items in a list

example-grid-shuffle

Shared Element Transitions

Sometimes one visual element might be represented by different DOM elements in different states. For example, an underline in a tabs component might be different DOM elements when switching between tabs.

Such transitions can be implemented by assigning the same layout ID to the elements that represent the same visual element, which will result in a shared element transition.

The layout ID can be assigned by providing a value to the layout attribute:

<!-- prettier-ignore -->
```html
@for (tab of tabs; track $index) {
<div class="tab" (click)="selectedTab = tab">
  <div class="tab-content">{{ tab.label }}</div>
  @if (tab === selectedTab) {
  <div class="tab-underline" layout="tab-underline"></div>
  }
</div>
}

example-grid-tabs

Formally, in order to initiate a shared element transition:

  • the old element must be removed before the new element is inserted, and
  • the new element must be inserted immediately after the old element is removed (within one event loop), and
  • the old and new elements must have the same layout ID

Dynamic Layout ID

The layout ID can be dynamically determined by providing a binding expression to the layout attribute:

<div class="tab" [layout]="tab.id"></div>

The expression bound to the layout attribute should be stable. If the expression yields multiple values, only the first value will be used as the layout ID.

Reusing HTML ID

If an element is assigned an HTML id, the HTML id will be used as the layout ID.

The following two snippets are equivalent:

<div class="tab-underline" layout="tab-underline"></div>
<div id="tab-underline" layout></div>

If both the id attribute and the layout attribute are assigned a value, the layout attribute will take precedence.

<div id="tab-underline" layout="underline"></div>

In the above example, the layout ID is underline.

Distortion Cancellation

The layout animations are driven by CSS transform styles, in order to take advantage of GPU acceleration and achieve high performance.

However, the transform styles can sometimes visually distort children. This is especially noticeable when the layout change involves size updates, where scaling needs to be applied to animate the size change.

<div class="parent" [class.open]="open" layout (click)="open = !open">
  <div class="child"></div>
</div>

example-distortion

To fix this, you can apply the directives on the direct children to cancel out the distortion:

<div class="parent" [class.open]="open" layout (click)="open = !open">
  <div class="child" layout></div>
</div>

example-distortion-cancelled

Transforms can also distort box-shadow and border-radius styles. The pre-built setup has already included the distortion cancellation for these two styles.

Skipping Size/Position

Some elements change their visual appearance between different aspect ratios, such as images and text. In such cases, you might want to skip the size animation and only animate the position changes.

The pre-built setup considers two metadata attributes during animations:

  • SKIP_SIZE skips the size animation
  • SKIP_POSITION skips the position animation

These metadata can be defined via the corresponding directives.

In order to skip the size animation for a specific element:

import { LayoutNode, LayoutNodeAnimator, SkipSize } from '@layout-projection/angular';

@Component({
  imports: [LayoutNode, LayoutNodeAnimator, SkipSize]
})
<p layout skipSize>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>

Customizing Setup

A setup refers to a collection of fundamental services provided at the root injector that is used by the directives to perform animations.

While the pre-built setup should be sufficient for most use cases, you can customize the setup by providing your own implementations of the services.

All the Layout Projection packages are designed following the SOLID principles and are well documented with JSDoc, making it relatively easy to hook into any procedure with your customized code. See the source code for more information.