More screenshots

    Works with the AI tools you already use

    Claude CodeClaude CodeCursorCursorCodex CLICodex CLIGitHub CopilotGitHub CopilotGemini CLIGemini CLI+20 more

    Awwwards Angular Site Generator

    4

    Turns a short brief into an art-directed, production-ready Angular 22 site with GSAP, Lenis and SSR-safe motion.

    $15

    Secure checkout via Stripe

    30-day refund guarantee

    Converts to your local currency at checkout

    0 installsSecurity scanned
    Awwwards Angular Site Generator

    Awwwards Angular Site Generator

    Example session with this skill installed

    Generate an immersive landing page for "Vora", a high-end minimalist watch brand. Vibe is Swiss luxury, primary colour #1A1A1A, balanced motion intensity, English copy. Angular 22 and Tailwind v4.

    • Read your context and instructions
    • Compiled the awwwards angular site
    • Generated the app files

    Brief analysis

    Vora, high-end minimalist watches. Brand showcase with one flagship product. Audience: design-literate buyers, 30–55, international. Vibe: Swiss luxury, quiet, precise. Primary colour #1A1A1A supplied; secondary left blank, so a warm paper white becomes the light surface. Motion intensity: balanced. Language: English. Defaults resolved: seven sections, editorial grotesque display type, no video.

    Creative direction

    Concept: "Silence, measured." The page behaves like a watch dial: mostly empty, every mark placed with intent. Type does the work an image would usually do. Photography is sparse, large and slow. Motion is limited to reveals, one parallax and one horizontal scrub, all with long expo eases.

    Principles: black is a surface, not a background · one idea per viewport · type on the grid, images break it · nothing decorates, everything reveals · whitespace is the luxury signal · the product appears late and once.

    Visual system

    TokenValueRole
    --color-ink#1A1A1Aprimary surface and text on light
    --color-graphite#2B2B2Belevated dark surface
    --color-paper#F4F1EClight surface and text on dark
    --color-ash#8E8B86muted text, hairlines
    --color-brass#B79B6Csingle accent, seconds hand
    Display'Neue Haas Grotesk Display', fallback 'Helvetica Neue', Arialclamp(2.5rem, 7vw, 8rem), tracking -0.04em
    Body'Inter', fallback system sansclamp(1rem, 1.05vw, 1.125rem), line-height 1.6
    EyebrowInter, uppercase0.6875rem, tracking 0.22em
    Section rhythm--space-sectionclamp(6rem, 14vw, 13rem)
    Grid12 columns, 1.5rem gutter, max 1440pxasymmetric 4/8 and 7/5 splits

    Contrast checked: paper on ink 14.9:1, ash on ink 4.9:1, brass on ink 6.1:1. All pass WCAG AA.

    Page architecture

    #SectionPurposePrimary motion
    1NavigationWordmark, three anchors, "Reserve" linkHairline draws on load, hides on scroll-down
    2Hero"Time, without noise."Word-mask reveal, dial image scales 1.06 → 1.0
    3StatementOne paragraph on restraintLine stagger on enter
    4Craft sequenceCase, movement, strap in three pinned beatsPinned image, scrubbed text panels (desktop only)
    5SpecificationsAsymmetric spec grid, 7/5Reveal stagger, hairline draw
    6Voices + NumbersTwo quotes, three figuresCount-up on reveal
    7Reserve + FooterReservation form, structured footerSurface shift from ink to paper

    No two sections share a layout pattern.

    Scaffold

    ng new vora --ssr --style=css --zoneless --skip-git
    cd vora
    npm install tailwindcss @tailwindcss/postcss postcss gsap lenis
    

    Zoneless bootstrap confirmed. zone.js absent from polyfills. .postcssrc.json configured with @tailwindcss/postcss.

    Implementation

    Fourteen files written to disk. Five shown in full below; the remaining nine (SEO service, section shell, masked image, reveal directive, four section components, routes) follow the same contract.

    src/styles.css

    @import "tailwindcss";
    
    @theme {
      --color-ink: #1A1A1A;
      --color-graphite: #2B2B2B;
      --color-paper: #F4F1EC;
      --color-ash: #8E8B86;
      --color-brass: #B79B6C;
    
      --font-display: "Neue Haas Grotesk Display", "Helvetica Neue", Arial, sans-serif;
      --font-body: "Inter", system-ui, sans-serif;
    
      --spacing-section: clamp(6rem, 14vw, 13rem);
    }
    
    html { background: var(--color-ink); color: var(--color-paper); }
    body { font-family: var(--font-body); -webkit-font-smoothing: antialiased; }
    
    .line { display: block; will-change: transform, opacity; }
    

    src/app/app.config.ts

    import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
    import { provideRouter } from '@angular/router';
    import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
    import { routes } from './app.routes';
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideZonelessChangeDetection(),
        provideRouter(routes),
        provideClientHydration(withEventReplay()),
      ],
    };
    

    src/app/core/services/motion.service.ts

    import { Injectable, PLATFORM_ID, inject, signal } from '@angular/core';
    import { isPlatformBrowser } from '@angular/common';
    import gsap from 'gsap';
    import { ScrollTrigger } from 'gsap/ScrollTrigger';
    
    @Injectable({ providedIn: 'root' })
    export class MotionService {
      private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
    
      readonly reducedMotion = signal(false);
      readonly isTouch = signal(false);
      readonly isDesktop = signal(false);
    
      constructor() {
        if (!this.isBrowser) return;
        gsap.registerPlugin(ScrollTrigger);
        this.reducedMotion.set(window.matchMedia('(prefers-reduced-motion: reduce)').matches);
        this.isTouch.set(window.matchMedia('(pointer: coarse)').matches);
        this.isDesktop.set(window.matchMedia('(min-width: 1024px)').matches);
      }
    
      /** Reveals each target line from below. Caller's gsap.context owns the tween. */
      revealLines(targets: gsap.TweenTarget, opts: { trigger?: Element; delay?: number } = {}): gsap.core.Tween {
        return gsap.from(targets, {
          yPercent: 110,
          opacity: 0,
          duration: 1.4,
          ease: 'expo.out',
          stagger: 0.08,
          delay: opts.delay ?? 0,
          scrollTrigger: opts.trigger ? { trigger: opts.trigger, start: 'top 80%', once: true } : undefined,
        });
      }
    
      parallax(target: gsap.TweenTarget, amount: number, trigger: Element): gsap.core.Tween {
        return gsap.to(target, {
          yPercent: amount,
          ease: 'none',
          scrollTrigger: { trigger, start: 'top bottom', end: 'bottom top', scrub: true },
        });
      }
    
      matchMedia(): gsap.MatchMedia {
        return gsap.matchMedia();
      }
    }
    

    src/app/core/services/smooth-scroll.service.ts

    import { Injectable, OnDestroy, inject } from '@angular/core';
    import gsap from 'gsap';
    import { ScrollTrigger } from 'gsap/ScrollTrigger';
    import Lenis from 'lenis';
    import { MotionService } from './motion.service';
    
    @Injectable({ providedIn: 'root' })
    export class SmoothScrollService implements OnDestroy {
      private readonly motion = inject(MotionService);
      private lenis?: Lenis;
      private refreshHandle?: number;
      private readonly tick = (time: number): void => this.lenis?.raf(time * 1000);
    
      /** Call once from the root shell inside afterNextRender. */
      init(): void {
        if (this.lenis || this.motion.reducedMotion()) return;
        this.lenis = new Lenis({ lerp: 0.08, smoothWheel: true });
        this.lenis.on('scroll', ScrollTrigger.update);
        gsap.ticker.add(this.tick);
        gsap.ticker.lagSmoothing(0);
      }
    
      scrollTo(target: string | number | HTMLElement, options?: { offset?: number }): void {
        this.lenis?.scrollTo(target, { offset: options?.offset ?? 0 });
      }
    
      refresh(): void {
        clearTimeout(this.refreshHandle);
        this.refreshHandle = window.setTimeout(() => ScrollTrigger.refresh(), 120);
      }
    
      ngOnDestroy(): void {
        gsap.ticker.remove(this.tick);
        this.lenis?.destroy();
        ScrollTrigger.getAll().forEach((t) => t.kill());
      }
    }
    

    src/app/features/home/sections/hero-section.ts

    Written to disk with an inline template. Structure and motion, in order:

    ElementClasses and bindingsMotion
    section, min-h-svh, 12-column grid, aria-labelledby="hero-heading"host of the component gsap.contextnone
    Eyebrow p.eyebrowtext-ash, uppercase, tracking-[0.22em]fades in at 0.9s
    h1#hero-heading, font-display, text-[clamp(2.5rem,7vw,8rem)]@for (word of hero.titleWords; track $index), each word wrapped in an overflow-hidden span with a .line childword-mask reveal via MotionService.revealLines, 0.2s delay
    figure#dial, aspect-[4/5], columns 8 to 12 on desktopimg with ngSrc, width, height, priority, sizes="(min-width: 1024px) 40vw, 100vw"scale 1.06 to 1.0 over 2.2s, expo.out; parallax of -8% on desktop only

    Class body: hero = SITE.hero, host = inject(ElementRef), motion = inject(MotionService), dial = viewChild.required('dial', { read: ElementRef }), one ctx?: gsap.Context created in afterNextRender and reverted in ngOnDestroy. Breakpoints and reduced motion handled with gsap.matchMedia(); no tween runs when prefers-reduced-motion is set.

    src/app/content/site.content.ts (excerpt, file is complete on disk)

    export const SITE = {
      brand: 'Vora',
      lang: 'en',
      hero: {
        eyebrow: 'Swiss made · Est. 2019',
        titleWords: ['Time,', 'without', 'noise.'],
        image: {
          src: 'https://picsum.photos/seed/vora-dial/1200/1500',
          alt: 'Vora Meridian dial in matte graphite, brass seconds hand',
          width: 1200,
          height: 1500,
        },
      },
      statement: {
        lines: [
          'A watch should say one thing',
          'and say it for forty years.',
          'Everything else is noise.',
        ],
      },
    } as const;
    

    Verify

    npx ng build 2>&1 | tail -6
    Application bundle generation complete. [4.812 seconds]
    Initial chunk files | Names   | Raw size | Estimated transfer size
    main-Q7K2L9.js      | main    | 214.3 kB | 58.1 kB
    styles-P3HZ1D.css   | styles  |  11.8 kB |  2.9 kB
    

    Dev server started, curl -s http://localhost:4200 | head -c 2000 returned server-rendered markup containing the hero heading with id hero-heading. Server stopped.

    Quality audit

    CheckStatusNote
    Zoneless bootstrap, no zone.js polyfillprovideZonelessChangeDetection() in config
    Standalone, signals, @if/@for with track, inject() onlygrep for @Input(, *ngIf, constructor(private returned nothing
    GSAP and Lenis never run on the serverall DOM work inside afterNextRender, MotionService guarded
    One Lenis instance, driven by gsap.ticker, lagSmoothing(0)no requestAnimationFrame anywhere in src/
    One gsap.context per animated component, revert() in ngOnDestroy6 contexts, 6 reverts
    Reduced motion respectedLenis disabled, tweens skipped via matchMedia
    Craft sequence pindesktop only, stacked reveals on tablet and mobile
    One h1, logical heading order, lang="en"
    Palette contrast WCAG AAsee visual system
    NgOptimizedImage with dimensions, priority on hero onlybelow-the-fold images lazy
    Fonts⚠️Neue Haas Grotesk is a licensed face; falls back to Helvetica Neue until you self-host
    SEO canonical and OG image⚠️placeholder https://vora.example in seo.service.ts

    Implementation validation

    1. Replace the picsum.photos seeds vora-dial, vora-case, vora-movement, vora-strap with product photography at the documented sizes in site.content.ts.
    2. Self-host the display font or swap to an owned face; update the two preload tags in index.html.
    3. Point the reservation form to your endpoint. It currently posts to /api/reserve.
    4. Set the canonical URL and OG image in seo.service.ts.
    5. If your installed Angular minor differs, verify the provideZonelessChangeDetection import path.

    Next steps

    Run npm start and open http://localhost:4200. For refinements, ask in plain language: "slow the hero reveal", "swap Voices for a Heritage timeline", "audit cleanup", or "translate copy to Italian". Only the affected files are rewritten.

    awwwards-angular-site-generator-app.zip

    ZIP · project files

    Generated

    Example file from a real run - the skill writes it into your workspace.

    Connects securely to your tools. The creator never sees your data.

    What you get

    Generate a high-end product showcase with smooth scrolling and parallax.Architect a zoneless Angular site using Signals and Tailwind v4.Integrate GSAP and ScrollTrigger safely into an Angular SSR environment.Create an editorial landing page with fluid typography and custom grids.

    About this skill

    The problem

    Standard Angular generators produce generic, bootstrap-style layouts that lack the high-end motion and art direction required for premium brands. Developers often struggle to integrate GSAP, Lenis, and SSR-safe animations without introducing memory leaks or layout shifts.

    What it does

    • Generates production-grade Angular 22 standalone applications using zoneless change detection and Signals.
    • Configures a complete motion system with GSAP, ScrollTrigger, and Lenis smooth scrolling tailored for SSR.
    • Architects art-directed visual systems including Tailwind v4 @theme tokens, fluid typography, and 12-column grids.
    • Builds 6-8 distinct, non-repetitive page sections with real copy and optimized image handling via NgOptimizedImage.
    • Automates cleanup patterns using gsap.context and revert() within lifecycle hooks to prevent performance degradation.

    Demo

    https://rubra-demo.netlify.app

    Frameworks & tools

    Angular 22, GSAP (ScrollTrigger), Lenis, Tailwind CSS v4, PostCSS, TypeScript, and SSR (Server-Side Rendering).

    Why this beats prompting it yourself

    General-purpose LLMs frequently fail at the intersection of Angular SSR and heavy animation, often producing code that breaks on the server or leaks memory. This skill enforces a strict technical contract for zoneless Signals and GSAP cleanup, ensuring your "Awwwards-style" site is actually stable and performant.

    Use cases

    • Launching a premium product landing page with complex scroll-driven storytelling.
    • Building a high-end creative agency portfolio with seamless page transitions.
    • Creating editorial-style marketing sites that require fluid, responsive typography.
    • Migrating legacy animation code to a modern, zoneless Angular architecture.

    Known limitations

    Not intended for CRUD applications, admin dashboards, or complex data-heavy interfaces. Requires a modern Node.js environment for scaffolding.

    How to install

    Works the same in every agent - Claude, Cursor, Codex, Copilot and 20+ more.

    ~30 seconds
    1. 1

      Download the ZIP

      Free skills download straight away. Paid skills unlock right after purchase.

    2. 2

      Unzip into your skills folder

      Every agent reads skills from one folder on your machine. Drop the unzipped folder in there.

    3. 3

      Ask your agent to use it

      Restart the agent if it was already running. It picks the skill up automatically - no config needed.

    Skills folder by agent

    Click the path to copy it. Create the folder if it does not exist yet.

    Reviews

    No reviews yet

    Be one of the first to try it. Every listed skill passes our trust checks below.

    Security scanned

    Passed our 8-point scan before listing

    Fresh listing

    Recently published to Agensi

    30-day refund

    Not a fit? Get your money back

    Trust & safety

    Security scanned

    Verified clean 3 days ago

    • Passed all security checks, Safe to install

    Listed3 days ago

    Frequently Asked Questions