Files
Lorenz Hilpert 38de927c4e Merged PR 2047: feat(carousel): convert to transformX with touch support and card animations
feat(carousel): convert to transformX with touch support and card animations

- Convert carousel from scroll-based to translate3d() transform positioning
- Add touch/swipe support with direct DOM manipulation for smooth 60fps performance
- Add mouse drag support for desktop navigation
- Implement hardware-accelerated transforms with will-change optimization
- Add disabled input to prevent navigation when needed
- Fix bounds calculation to use parent viewport width
- Add card stacking animation with translateY and rotation effects
- Remove shadow for cards beyond 3rd position in stacked mode
- Update tests with 4 new disabled state test cases (17 tests total)

Refs #5499

Related work items: #5499
2025-11-24 13:37:34 +00:00
..
2025-10-28 10:34:57 +00:00
2025-10-28 10:34:57 +00:00

@isa/ui/carousel

A horizontal scroll container component with left/right navigation arrows, responsive behavior, keyboard support, and auto-hide functionality.

Features

  • Smooth horizontal scrolling with viewport-width scroll amount
  • Navigation arrows with auto-hide on hover (configurable)
  • Keyboard navigation (Arrow Left/Right when focused)
  • Responsive design with native touch gesture support
  • Disabled states when at scroll boundaries
  • Touch gesture support (native browser behavior)
  • Content projection for flexible item rendering
  • Angular signals for reactive state management

Installation

The carousel library is part of the ISA-Frontend monorepo. Import it using the path alias:

import { CarouselComponent } from '@isa/ui/carousel';

Basic Usage

import { Component } from '@angular/core';
import { CarouselComponent } from '@isa/ui/carousel';

@Component({
  selector: 'app-example',
  template: `
    <ui-carousel>
      @for (item of items; track item.id) {
        <div class="card">{{ item.name }}</div>
      }
    </ui-carousel>
  `,
  imports: [CarouselComponent],
})
export class ExampleComponent {
  items = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' },
  ];
}

API Reference

Component Selector

ui-carousel

Input Properties

Property Type Default Description
gap string '1rem' CSS gap value for spacing between carousel items (e.g., '1rem', '2rem', '0.5rem').
arrowAutoHide boolean true Whether to auto-hide arrows until the carousel is hovered or focused. When true, arrows fade in on hover/focus.

Methods

Method Parameters Description
scrollToPrevious() event?: Event Scrolls to the previous set of items (left). Scrolls by viewport width.
scrollToNext() event?: Event Scrolls to the next set of items (right). Scrolls by viewport width.

Keyboard Navigation

Key Action
Arrow Left Scroll to previous items
Arrow Right Scroll to next items

Note: The carousel must be focused (click or tab to it) for keyboard navigation to work.

Usage Examples

import { Component } from '@angular/core';
import { CarouselComponent } from '@isa/ui/carousel';

@Component({
  template: `
    <ui-carousel>
      <div class="card">Card 1</div>
      <div class="card">Card 2</div>
      <div class="card">Card 3</div>
    </ui-carousel>
  `,
  imports: [CarouselComponent],
})
export class BasicCarouselExample {}

Example 2: Always Show Arrows

import { Component } from '@angular/core';
import { CarouselComponent } from '@isa/ui/carousel';

@Component({
  template: `
    <ui-carousel [arrowAutoHide]="false">
      @for (product of products; track product.id) {
        <product-card [product]="product"></product-card>
      }
    </ui-carousel>
  `,
  imports: [CarouselComponent],
})
export class AlwaysShowArrowsExample {
  products = [/* ... */];
}

Example 3: Custom Gap

import { Component } from '@angular/core';
import { CarouselComponent } from '@isa/ui/carousel';

@Component({
  template: `
    <ui-carousel [gap]="'2rem'">
      @for (card of cards; track card.id) {
        <div class="wide-card">{{ card.content }}</div>
      }
    </ui-carousel>
  `,
  imports: [CarouselComponent],
})
export class CustomGapExample {
  cards = [/* ... */];
}

Styling

CSS Classes

The carousel component uses the following CSS classes:

Class Element Description
.ui-carousel Host element Main container with focus styles
.ui-carousel--auto-hide Host element Applied when arrowAutoHide is true
.ui-carousel__wrapper Wrapper Contains buttons and scroll container
.ui-carousel__container Scroll container Horizontal scroll area with hidden scrollbar
.ui-carousel__button Navigation button Left/right arrow buttons
.ui-carousel__button--left Left button Positioned on the left side
.ui-carousel__button--right Right button Positioned on the right side

Custom Styling

You can override the default styles using CSS:

// Custom arrow button colors
.ui-carousel__button {
  background-color: #your-color;
  border-color: #your-border-color;
  color: #your-icon-color;

  &:hover {
    background-color: #your-hover-color;
  }
}

// Custom focus outline
.ui-carousel:focus {
  outline-color: #your-focus-color;
}

// Custom scroll container
.ui-carousel__container {
  // Add custom styles
}

Accessibility

Keyboard Navigation

  • The carousel is focusable with tabindex="0"
  • Arrow Left/Right keys scroll the carousel when focused
  • Focus outline visible for keyboard users (:focus-visible)

ARIA Attributes

  • Navigation buttons have aria-label attributes:
    • Left button: "Previous"
    • Right button: "Next"
  • Buttons are properly marked with type="button" to prevent form submission

E2E Testing Attributes

All interactive elements include proper data attributes for automated testing:

<button data-what="button" data-which="carousel-previous">...</button>
<button data-what="button" data-which="carousel-next">...</button>

Behavior

Scroll Logic

  • Scroll Amount: Scrolls by the viewport width of the container
  • Smooth Scrolling: Uses CSS scroll-behavior: smooth for animated transitions
  • Boundary Detection: Automatically disables arrows when at the start/end of content
  • Responsive: Arrows always show when content is scrollable, on all screen sizes

Auto-Hide Arrows

When arrowAutoHide is true:

  • Arrows are invisible by default (opacity: 0)
  • Arrows fade in on carousel hover or focus
  • Smooth CSS transitions for fade effects

Touch Gestures

The carousel supports native touch gestures on mobile/tablet devices:

  • Swipe left/right to scroll
  • No custom JavaScript needed (browser default behavior)

Browser Compatibility

  • Modern browsers with CSS Grid and Flexbox support
  • Native smooth scrolling support
  • ResizeObserver API for content change detection

Performance

  • OnPush Change Detection: Optimized for minimal re-renders
  • Angular Signals: Fine-grained reactivity with automatic dependency tracking
  • Native Scroll: Leverages browser's native scroll performance
  • Cleanup: Automatic event listener and observer cleanup on destroy

Testing

Run unit tests:

npx nx test ui-carousel --skip-nx-cache

The carousel component includes comprehensive tests for:

  • Component creation and default values
  • Input property changes
  • CSS class application
  • Content projection
  • Keyboard navigation
  • Navigation button rendering
  • E2E data attributes
  • @isa/ui/buttons - Button components used for navigation arrows
  • @isa/icons - Icon library for arrow icons

Contributing

When contributing to this component:

  1. Follow the ISA-Frontend coding conventions
  2. Use Angular signals for reactive state
  3. Include E2E data attributes (data-what, data-which)
  4. Write comprehensive Vitest tests
  5. Update this README for API changes

License

Internal ISA-Frontend library.