Showcase

Animations from past Functn projects, adapted for TaylorUI. Copy the components into your project and wire them up to your own data.


This Product carousel is copied from the functndev/wsk-bank-relaunch (opens in new tab) project and adapted to TaylorUI. See it live in the Unsere Produkte section on wsk-bank.at (opens in new tab).

The carousel combines TaylorUI carousel primitives with embla-carousel-fade so slides crossfade in place. A fixed text region above animates title and description on slide change (fadeUp from @/animations). The thumbnail rail below expands the active item and accepts clicks to jump between slides.

To reuse in your project:

  1. Copy src/ui/components/carousel/product-carousel.tsx into your app.
  2. Ensure TaylorUI carousel primitives are available (see file list below).
  3. Install embla-carousel-fade alongside embla-carousel-react.
  4. Replace placeholderProductList in @/assets with your own slide data — each slide needs a title, description, optional links, and an image.

Files to copy (skip any your project already has):

  • src/ui/components/carousel/product-carousel.tsx
  • src/ui/components/carousel/carousel.tsx
  • src/lib/cn.ts
  • src/animations/framer/variants.ts (for fadeUp)

Dependencies: embla-carousel-react, embla-carousel-fade, framer-motion, next, react

import { ProductCarouselShowcase } from '@/ui/components/carousel/product-carousel';
 
// Renders with placeholder slides from @/assets.
export function ProductsSection() {
  return (
    <section>
      <ProductCarouselShowcase />
    </section>
  );
}

To customise slide content, map your data to the ProductSlide type from @/assets:

import type { ProductSlide } from '@/assets';
 
const products: ProductSlide[] = items.map((item) => ({
  productTitle: item.title,
  productText: item.description,
  productLinks: item.links,
  image: item.image,
}));

Animated Buttons

Uses Framer Motion's layoutId inside a LayoutGroup to smoothly morph between two buttons with different positions and sizes as you scroll. The button starts small at the top-left and grows into a large variant at the bottom-right once the container reaches the viewport center — scrolling back reverses the animation.

Wrap both button positions in LayoutGroup, give each motion.div the same layoutId, and use useScroll to toggle between them. AnimatePresence keeps the exiting button alive so the morph is seamless.

import {
  AnimatePresence,
  LayoutGroup,
  motion,
  useMotionValueEvent,
  useScroll,
} from 'framer-motion';
 
function AnimatedButton() {
  const ref = useRef(null);
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ['start end', 'center center'],
  });
  const [showFirst, setShowFirst] = useState(true);
  useMotionValueEvent(scrollYProgress, 'change', (v) => setShowFirst(v < 0.8));
 
  return (
    <LayoutGroup>
      <div ref={ref} className="relative h-[50vh]">
        <AnimatePresence initial={false}>
          {showFirst ? (
            <motion.div
              key="first"
              layoutId="my-button"
              className="absolute top-8 left-8"
            >
              <Button size="small">Small</Button>
            </motion.div>
          ) : (
            <motion.div
              key="second"
              layoutId="my-button"
              className="absolute right-8 bottom-8"
            >
              <Button size="large">Large</Button>
            </motion.div>
          )}
        </AnimatePresence>
      </div>
    </LayoutGroup>
  );
}

Scroll-Animation

This Scroll-Animation is copied from the functndev/remus-webshop (opens in new tab) project and adapted to TaylorUI. See it live on the development test page at remus-exhausts.com (opens in new tab).

A content panel sits alongside a two-column image gallery. As you scroll, Framer Motion's useScroll and useTransform drive opposing parallax on the left and right columns — the left column moves up while the right moves down, creating depth as the section passes through the viewport.

To reuse in your project:

  1. Copy src/animations/components/scroll-animation-gallery.tsx into your app.
  2. Ensure ButtonLink, cn, and PageWidth are available (see file list below).
  3. Edit PLACEHOLDER_IMAGES and PLACEHOLDER_CONTENT at the top of the copied file.
  4. Adjust parallaxOffset inside the component to control how far the columns travel.

Files to copy (skip any your project already has):

  • src/animations/components/scroll-animation-gallery.tsx
  • src/ui/structure/page-width/page-width.tsx
  • src/ui/components/button/button-link.tsx
  • src/lib/cn.ts
  • src/assets/index.ts (or provide your own ImageMedia images)

Dependencies: framer-motion, next, react, @remixicon/react

import { ScrollAnimationShowcase } from '@/animations/components/scroll-animation-gallery';
import { PageWidth } from '@/ui/structure/page-width/page-width';
 
// Renders with placeholder images and copy baked into the component.
export function CtaGallerySection() {
  return (
    <PageWidth width="wide">
      <ScrollAnimationShowcase />
    </PageWidth>
  );
}

To customise content, edit the constants at the top of scroll-animation-gallery.tsx:

import { type ImageMedia, placeholderImageList } from '@/assets';
 
const PLACEHOLDER_IMAGES = placeholderImageList.slice(0, 8);
 
const PLACEHOLDER_CONTENT = {
  title: 'Your title',
  subtitle: 'Your subtitle',
  supportingText: 'Your supporting text.',
  ctaLabel: 'Call to action',
  ctaHref: '/your-link',
};

Each image should match the ImageMedia type from @/assets (a next/image -compatible source with optional alt text). Use 8 images for the full two-column parallax effect — 4 per column on desktop.

Discover our gallery

Scroll to explore

Two image columns move in opposite directions as you scroll, creating a parallax effect alongside the content panel.

Learn more

This Team carousel is copied from the functndev/wsk-bank-relaunch (opens in new tab) project and adapted to TaylorUI. See it live in the Unser Team section on wsk-bank.at (opens in new tab).

Each card shows a team member photo over the contact details. On hover (or when the info button is toggled), the photo slides down to reveal email and phone links. Location tabs filter the carousel, and TaylorUI carousel primitives handle scroll navigation.

To reuse in your project:

  1. Copy src/animations/components/team-carousel-showcase.tsx and src/animations/components/team-member-card.tsx into your app.
  2. Ensure TaylorUI carousel primitives are available (see file list below).
  3. Replace PLACEHOLDER_TEAM_MEMBERS with your own team data.
  4. Adjust card height (h-[28rem]) and image offset (bottom-[-28rem]) together if you change the card layout.

Files to copy (skip any your project already has):

  • src/animations/components/team-carousel-showcase.tsx
  • src/animations/components/team-member-card.tsx
  • src/ui/components/carousel/carousel.tsx
  • src/lib/cn.ts
  • src/assets/index.ts (or provide your own member images)

Dependencies: embla-carousel-react, react-aria-components, next, react

import { TeamCarouselShowcase } from '@/animations/components/team-carousel-showcase';
 
export function TeamSection() {
  return <TeamCarouselShowcase />;
}

Our team

Hover a card to reveal contact details, or use the info button to keep them visible while navigating the carousel.

6 team members at Gentzgasse


A project-style carousel with animated slide metadata (title, year, description) and a progress bar.

Uses TaylorUI carousel primitives with Framer Motion crossfades for slide metadata. A content sizer keeps the text area height stable as descriptions change length.

To reuse in your project:

  1. Copy src/ui/components/carousel/project-carousel.tsx into your app.
  2. Ensure TaylorUI carousel primitives and MotionReveal are available.
  3. Map your project data to the ProjectCarouselSlide type.

Files to copy (skip any your project already has):

  • src/ui/components/carousel/project-carousel.tsx
  • src/ui/components/carousel/carousel.tsx
  • src/animations/components/motion-reveal.tsx
  • src/lib/cn.ts

Dependencies: embla-carousel-react, framer-motion, next, react

import { ProjectCarouselShowcase } from '@/app/animations/showcase/showcase';
 
export function SelectedWorksSection() {
  return <ProjectCarouselShowcase />;
}

Project Collection / Selected works