feat: add @sda/flair-elements-ui library

Angular 19+ standalone library with 61 components, 15 directives,
and 8 services for decorative visual effects, animations, and
micro-interactions.

Components include particle systems, flow fields, aurora effects,
tilt/holographic/spotlight cards, text effects, dividers, scroll
animations, generative art, celebrations, image treatments,
morphing shapes, terminal output, and pattern textures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Giuliano Silvestro
2026-03-09 10:24:52 +10:00
commit daf6182e94
230 changed files with 10642 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
:host {
display: block;
width: 100%;
height: 100%;
position: relative;
}
.fl-card-stack__container {
position: relative;
width: 100%;
height: 100%;
> * {
cursor: pointer;
}
}
:host.fl-card-stack--hover-expand {
.fl-card-stack__container > *:hover {
transform: translate(-50%, -50%) rotate(0deg) scale(1.05) !important;
z-index: 1000 !important;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
}
:host.fl-card-stack--reduced-motion {
.fl-card-stack__container > * {
transition: none !important;
}
}

View File

@@ -0,0 +1,62 @@
import {
Component, ChangeDetectionStrategy, input, inject,
DestroyRef, ElementRef, afterNextRender,
} from '@angular/core';
import { ReducedMotionService } from '../../services/reduced-motion.service';
@Component({
selector: 'fl-card-stack',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="fl-card-stack__container">
<ng-content />
</div>
`,
styleUrl: './fl-card-stack.component.scss',
host: {
'class': 'fl-card-stack',
'[class.fl-card-stack--reduced-motion]': 'reducedMotion.reduced()',
'[class.fl-card-stack--hover-expand]': 'hoverExpand()',
},
})
export class FlCardStackComponent {
protected reducedMotion = inject(ReducedMotionService);
private destroyRef = inject(DestroyRef);
private el = inject(ElementRef);
// Inputs
readonly spread = input(20);
readonly rotation = input(3);
readonly hoverExpand = input(true);
constructor() {
afterNextRender(() => {
this.applyCardStyles();
});
}
private applyCardStyles(): void {
const container = this.el.nativeElement.querySelector('.fl-card-stack__container') as HTMLElement;
if (!container) return;
const children = Array.from(container.children) as HTMLElement[];
const total = children.length;
const spreadPx = this.spread();
const rotationDeg = this.rotation();
children.forEach((child, index) => {
const offset = (index - (total - 1) / 2);
const translateY = offset * spreadPx;
const rotate = offset * rotationDeg;
const zIndex = total - Math.abs(index - Math.floor(total / 2));
child.style.position = 'absolute';
child.style.top = '50%';
child.style.left = '50%';
child.style.transform = `translate(-50%, calc(-50% + ${translateY}px)) rotate(${rotate}deg)`;
child.style.zIndex = `${zIndex}`;
child.style.transition = 'transform 0.3s ease';
});
}
}

View File

@@ -0,0 +1 @@
export * from './fl-card-stack.component';