Menu

Change language

GitHub repo

Category Showcase

blockcommerce

E-commerce category bento grid and featured collections showcase with interactive hover zoom effects, promo sale banner, and bento or uniform grid layouts.

Installation

$npx uipkge-ng@latest add category-showcase
Manifest:https://uipkge.dev/r/angular/category-showcase.jsonInstalls to:src/app/components/blocks/category-showcase/

Variants

Loading interactive previews…

npm dependencies

Files installed (2)

  • src/app/components/blocks/category-showcase/category-showcase.component.ts13.5 kB
    // E-commerce "Shop by Category" section. Edit the tiles inline -- each one is spelled out as a
    // sibling so its image, copy and badges can change independently.
    import { Component, Input, ChangeDetectionStrategy } from '@angular/core'
    import { ArrowRight, LucideAngularModule, Percent, ShoppingBag } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiButtonComponent } from '@/ui/button/button.component'
    import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'
    
    export type CategoryShowcaseVariant = 'bento' | 'grid'
    
    const tileBase =
      'group border-border bg-card relative flex flex-col justify-between overflow-hidden shadow-xs transition-colors duration-300 hover:shadow-md'
    
    /**
     * CategoryShowcase -- the React block, 1:1: a section header (badge, title, "View all
     * collections" link) over a bento (4x2, hero tile 2x2) or uniform grid of category tiles with
     * image scrims and hover zoom, closed by an archive sale promo tile.
     */
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-category-showcase, [ui-category-showcase]',
      standalone: true,
      // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.
      host: { '[attr.class]': '"contents"' },
      imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent],
      template: `
        <section data-slot="category-showcase" [class]="rootClass">
          <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
            <!-- Section Header -->
            <div class="mb-8 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-end lg:mb-12">
              <div class="space-y-3">
                <span ui-badge wrap variant="outline" class="gap-1.5 px-3 py-1 text-xs font-medium">
                  <lucide-icon [img]="ShoppingBag" class="text-primary size-3.5" />
                  Explore Collections
                </span>
                <h2 class="text-foreground text-3xl font-semibold tracking-tight sm:text-4xl">Shop by Category</h2>
                <p class="text-muted-foreground max-w-2xl text-sm sm:text-base">
                  Curated essentials designed for everyday performance.
                </p>
              </div>
              <a
                href="#"
                class="group/link text-primary hover:text-primary/80 inline-flex min-h-6 items-center gap-1.5 text-sm font-medium transition-colors"
              >
                View all collections
                <lucide-icon
                  [img]="ArrowRight"
                  [class]="'size-4 transition-transform duration-200 group-hover/link:translate-x-1'"
                />
              </a>
            </div>
    
            <!-- Bento / Grid Showcase -->
            <div [class]="gridClass">
              <!-- Tile 1: Featured Hero Tile (2x2 in bento) -->
              <div ui-card [class]="heroClass">
                <img
                  src="https://images.unsplash.com/photo-1544441893-675973e31985?w=1200&auto=format&fit=crop&q=80"
                  alt="Performance Outerwear collection"
                  class="absolute inset-0 size-full object-cover transition-transform duration-500 ease-out group-hover:scale-105"
                />
                <div class="absolute inset-0 bg-gradient-to-t from-black/90 via-black/45 to-black/15"></div>
    
                <div ui-card-content class="relative z-10 flex h-full flex-col justify-between p-6 sm:p-8">
                  <div class="flex flex-wrap items-center justify-between gap-3">
                    <span
                      ui-badge
                      wrap
                      variant="secondary"
                      class="bg-background/80 text-foreground border-white/15 text-xs font-medium shadow-xs backdrop-blur-md"
                    >
                      Featured Collection
                    </span>
                    <span
                      ui-badge
                      wrap
                      variant="outline"
                      class="border-white/20 bg-white/10 text-xs font-medium text-white backdrop-blur-xs"
                    >
                      Explore 32 Styles
                    </span>
                  </div>
    
                  <div class="space-y-4">
                    <div class="space-y-2">
                      <h3 class="text-2xl font-bold tracking-tight text-white sm:text-3xl lg:text-4xl">
                        Performance Outerwear
                      </h3>
                      <p class="line-clamp-2 max-w-md text-sm text-zinc-200">
                        Engineered weatherproof shells, insulated parkas, and technical layers designed for extreme
                        climates.
                      </p>
                    </div>
    
                    <button
                      ui-button
                      size="sm"
                      class="group/btn w-fit bg-white font-medium text-zinc-900 shadow-sm hover:bg-white/90 focus-visible:ring-white"
                    >
                      Shop Collection
                      <lucide-icon
                        [img]="ArrowRight"
                        [class]="'ml-1.5 size-4 transition-transform duration-200 group-hover/btn:translate-x-0.5'"
                      />
                    </button>
                  </div>
                </div>
              </div>
    
              <!-- Tile 2: Footwear & Running -->
              <div ui-card [class]="tileClass">
                <img
                  src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=800&auto=format&fit=crop&q=80"
                  alt="Footwear and running sneakers"
                  class="absolute inset-0 size-full object-cover transition-transform duration-500 ease-out group-hover:scale-105"
                />
                <div class="absolute inset-0 bg-gradient-to-t from-black/85 via-black/35 to-black/10"></div>
    
                <div ui-card-content class="relative z-10 flex h-full flex-col justify-between p-6">
                  <div class="flex items-center justify-between">
                    <span
                      ui-badge
                      wrap
                      variant="secondary"
                      class="bg-background/80 text-foreground border-white/15 text-xs font-medium shadow-xs backdrop-blur-md"
                    >
                      18 Items
                    </span>
                  </div>
    
                  <div class="space-y-1">
                    <h3 class="text-lg font-semibold tracking-tight text-white transition-colors group-hover:text-white/90">
                      Footwear &amp; Running
                    </h3>
                    <span class="inline-flex items-center gap-1 text-xs font-medium text-zinc-300">
                      View products
                      <lucide-icon
                        [img]="ArrowRight"
                        [class]="'size-3 transition-transform duration-200 group-hover:translate-x-0.5'"
                      />
                    </span>
                  </div>
                </div>
              </div>
    
              <!-- Tile 3: Accessories & Bags -->
              <div ui-card [class]="tileClass">
                <img
                  src="https://images.unsplash.com/photo-1553062407-98eeb64c6a62?w=800&auto=format&fit=crop&q=80"
                  alt="Accessories and bags collection"
                  class="absolute inset-0 size-full object-cover transition-transform duration-500 ease-out group-hover:scale-105"
                />
                <div class="absolute inset-0 bg-gradient-to-t from-black/85 via-black/35 to-black/10"></div>
    
                <div ui-card-content class="relative z-10 flex h-full flex-col justify-between p-6">
                  <div class="flex items-center gap-2">
                    <span
                      ui-badge
                      wrap
                      class="bg-primary text-primary-foreground border-transparent text-xs font-medium shadow-xs"
                    >
                      New Season
                    </span>
                    <span
                      ui-badge
                      wrap
                      variant="secondary"
                      class="bg-background/80 text-foreground border-white/15 text-xs font-medium shadow-xs backdrop-blur-md"
                    >
                      24 Items
                    </span>
                  </div>
    
                  <div class="space-y-1">
                    <h3 class="text-lg font-semibold tracking-tight text-white transition-colors group-hover:text-white/90">
                      Accessories &amp; Bags
                    </h3>
                    <span class="inline-flex items-center gap-1 text-xs font-medium text-zinc-300">
                      View products
                      <lucide-icon
                        [img]="ArrowRight"
                        [class]="'size-3 transition-transform duration-200 group-hover:translate-x-0.5'"
                      />
                    </span>
                  </div>
                </div>
              </div>
    
              <!-- Tile 4: Everyday Apparel -->
              <div ui-card [class]="wideTileClass">
                <img
                  src="https://images.unsplash.com/photo-1521572267360-ee0c2909d518?w=800&auto=format&fit=crop&q=80"
                  alt="Everyday apparel collection"
                  class="absolute inset-0 size-full object-cover transition-transform duration-500 ease-out group-hover:scale-105"
                />
                <div class="absolute inset-0 bg-gradient-to-t from-black/85 via-black/35 to-black/10"></div>
    
                <div ui-card-content class="relative z-10 flex h-full flex-col justify-between p-6">
                  <div class="flex items-center justify-between">
                    <span
                      ui-badge
                      wrap
                      variant="secondary"
                      class="bg-background/80 text-foreground border-white/15 text-xs font-medium shadow-xs backdrop-blur-md"
                    >
                      45 Items
                    </span>
                  </div>
    
                  <div class="space-y-1">
                    <h3 class="text-lg font-semibold tracking-tight text-white transition-colors group-hover:text-white/90">
                      Everyday Apparel
                    </h3>
                    <span class="inline-flex items-center gap-1 text-xs font-medium text-zinc-300">
                      View products
                      <lucide-icon
                        [img]="ArrowRight"
                        [class]="'size-3 transition-transform duration-200 group-hover:translate-x-0.5'"
                      />
                    </span>
                  </div>
                </div>
              </div>
    
              <!-- Tile 5: Sale Special Promo Tile -->
              <div ui-card [class]="promoClass">
                <div
                  class="bg-primary/15 group-hover:bg-primary/25 pointer-events-none absolute -right-8 -bottom-8 size-36 rounded-full blur-2xl transition-colors duration-500"
                ></div>
    
                <div ui-card-content class="relative z-10 flex h-full flex-col justify-between p-6">
                  <div class="flex items-center justify-between gap-2">
                    <div class="bg-primary/10 text-primary flex size-9 items-center justify-center rounded-lg">
                      <lucide-icon [img]="Percent" class="size-4" />
                    </div>
                    <span
                      ui-badge
                      wrap
                      variant="outline"
                      class="border-primary/30 bg-primary/10 text-primary font-mono text-xs uppercase"
                    >
                      ARCHIVE40
                    </span>
                  </div>
    
                  <div class="space-y-3">
                    <div class="space-y-1">
                      <p class="text-primary text-xs font-medium tracking-wider uppercase">Limited Time Promo</p>
                      <h3 class="text-foreground text-base font-semibold tracking-tight">Archive Sale · Up to 40% Off</h3>
                      <p class="text-muted-foreground text-xs leading-relaxed">
                        Seasonal essentials and core archive styles at exclusive discounted rates.
                      </p>
                    </div>
    
                    <button ui-button size="sm" class="group/btn w-full">
                      Shop Sale
                      <lucide-icon
                        [img]="ArrowRight"
                        [class]="'ml-1.5 size-4 transition-transform duration-200 group-hover/btn:translate-x-0.5'"
                      />
                    </button>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </section>
      `,
    })
    export class UiCategoryShowcaseComponent {
      /** `bento`: 4x2 grid with a 2x2 hero tile. `grid`: uniform 4-column grid. */
      @Input() variant: CategoryShowcaseVariant = 'bento'
      @Input('class') className?: string
    
      protected readonly ArrowRight = ArrowRight
      protected readonly Percent = Percent
      protected readonly ShoppingBag = ShoppingBag
    
      private get bento(): boolean {
        return this.variant === 'bento'
      }
    
      protected get rootClass(): string {
        return cn('bg-background w-full py-12 sm:py-16 lg:py-20', this.className)
      }
    
      protected get gridClass(): string {
        return cn('grid grid-cols-1 gap-4 sm:grid-cols-2', this.bento ? 'lg:grid-cols-4 lg:grid-rows-2' : 'lg:grid-cols-4')
      }
    
      protected get heroClass(): string {
        return cn(
          tileBase,
          this.bento
            ? 'min-h-[460px] sm:col-span-2 lg:col-span-2 lg:row-span-2 lg:min-h-[560px]'
            : 'min-h-[320px] sm:col-span-2 lg:col-span-2',
        )
      }
    
      protected get tileClass(): string {
        return cn(tileBase, 'col-span-1 min-h-[260px]')
      }
    
      protected get wideTileClass(): string {
        return cn(
          tileBase,
          this.bento ? 'col-span-1 min-h-[260px]' : 'col-span-1 min-h-[260px] sm:col-span-2 lg:col-span-2',
        )
      }
    
      protected get promoClass(): string {
        return cn(
          'group border-primary/20 bg-primary/5 hover:border-primary/40 dark:bg-primary/10 relative flex flex-col justify-between overflow-hidden shadow-xs transition-colors duration-300 hover:shadow-md',
          this.bento ? 'col-span-1 min-h-[260px]' : 'col-span-1 min-h-[260px] sm:col-span-2 lg:col-span-2',
        )
      }
    }
    
  • src/app/components/blocks/category-showcase/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/category-showcase.json