Copy environment

Landing Hero

<div class="landing-hero  ">
    <div class="landing-hero__inner">
        <h1 class="landing-hero__title-container">
            <span class="landing-hero__title h1" data-text="Letters"><span class="landing-hero__title-text">Letters</span>Letters</span>
            <span class="landing-hero__title h1 landing-hero__title--second" data-text="from"><span class="landing-hero__title-text">from</span>from</span>
            <span class="landing-hero__title h1 landing-hero__title--third" data-text="Tartu"><span class="landing-hero__title-text">Tartu</span>Tartu</span>
        </h1>
        <div class="landing-hero__info-container">
            <div class="landing-hero__info text-large">
                Creative initiative showcasing typefaces designed in Tartu and historical letters found around the town.
            </div>

            <a href="#" class="button button--icon-text landing-hero__button ">

                <span class="button__inner" data-text="Read more">
                    <span class="button__text">
                        Read more
                        <svg class="icon  button__icon">
                            <use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#arrow-right"></use>
                        </svg>

                    </span>
                </span>
            </a>
        </div>
    </div>
</div>
<div class="landing-hero {{ modifier }} {{ class }}">
    <div class="landing-hero__inner">
        <h1 class="landing-hero__title-container">
            <span class="landing-hero__title h1" data-text="Letters"><span class="landing-hero__title-text">Letters</span>Letters</span>
            <span class="landing-hero__title h1 landing-hero__title--second" data-text="from"><span class="landing-hero__title-text">from</span>from</span>
            <span class="landing-hero__title h1 landing-hero__title--third" data-text="Tartu"><span class="landing-hero__title-text">Tartu</span>Tartu</span>
        </h1>
        <div class="landing-hero__info-container">
            {% if data.info %}
                <div class="landing-hero__info text-large">
                     {{ data.info }}
                </div>
            {% endif %}
            {% if data.button %}
                {% include '@button' with { data: data.button|merge({icon: 'arrow-right'}), modifier: 'button--icon-text', class: 'landing-hero__button'}%}
            {% endif %}
        </div>
    </div>
</div>
{
  "language": "en-US",
  "data": {
    "info": "Creative initiative showcasing typefaces designed in Tartu and historical letters found around the town.",
    "button": {
      "text": "Read more",
      "icon": "arrow-right",
      "link": "#"
    }
  }
}
  • Content:
    @keyframes appear-horizontal {
        from {
            width: 0;
        }
    
        to {
            width: 100%;
        }
    }
    
    @keyframes appear-vertical {
        from {
            height: 0;
        }
    
        to {
            height: 100%;
        }
    }
    
    @keyframes appear-title {
        from {
            font-size: 425px;
            line-height: 450px;
            transform: translate(100vw, 100vh);
        }
    
        to {
            position: absolute;
            font-size: $font-size-h1-lg;
            line-height: $font-line-height-h1-lg;
            transform: translate(0, 0);
        }
    }
    
    .landing-hero {
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
        height: 101vh;
        min-height: 700px;
        padding-top: 80px;
    
        @include bp(sm-min) {
            padding-top: 120px;
            min-height: 800px;
        }
    
        @include bp(md-min) {
            padding-top: 0;
            justify-content: center;
        }
    }
    
    .landing-hero__inner {
        display: flex;
        position: relative;
        flex-direction: column;
    
        @include bp(md-min) {
            flex-direction: row;
        }
    
        &:before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 0;
            bottom: 0;
            border-top: 1px solid var(--color-brand);
            transition: $transition-duration $transition-easing;
            transition-property: border-top;
        }
    
        .landing-hero.is-animating &:before {
            animation: .8s $transition-easing 0s appear-horizontal forwards;
        }
    
        &:after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 0;
            bottom: 0;
            border-bottom: 1px solid var(--color-brand);
            pointer-events: none;
            transition: $transition-duration $transition-easing;
            transition-property: border-bottom;
        }
    
        .landing-hero.is-animating &:after {
            animation: .8s $transition-easing 1s appear-horizontal forwards;
    
            @include bp(md-min) {
                animation: .8s $transition-easing .75s appear-horizontal forwards;
            }
        }
    }
    
    .landing-hero__title-container {
        display: flex;
        flex: 1;
        flex-direction: column;
        position: relative;
        border-right: none;
    
        &:before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 0;
    
            @include bp(md-min) {
                border-right: 1px solid var(--color-brand);
                transition: $transition-duration $transition-easing;
                transition-property: border-right;
            }
        }
    
        .landing-hero.is-animating &:before {
            animation: .8s $transition-easing 0s appear-vertical forwards;
        }
    }
    
    .landing-hero__title {
        color: transparent;
        display: block;
        border-right: none;
        position: relative;
        padding-left: 16px;
        flex-grow: 1;
    
        @include bp(md-min) {
            padding-left: 92px;
        }
    
        & + &:before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 0;
            bottom: 0;
            border-top: 1px solid var(--color-brand);
            transition: $transition-duration $transition-easing;
            transition-property: border-top;
        }
    }
    
    .landing-hero__title--second {
        .landing-hero.is-animating &:before {
            animation: .8s $transition-easing .25s appear-horizontal forwards;
        }
    }
    
    .landing-hero__title--third {
        &:after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 0;
            bottom: 0;
            border-bottom: 1px solid var(--color-brand);
            transition: $transition-duration $transition-easing;
            transition-property: border-bottom;
        }
    
        .landing-hero.is-animating &:before {
            animation: .8s $transition-easing .5s appear-horizontal forwards;
        }
    
        .landing-hero.is-animating &:after {
            animation: .8s $transition-easing .75s appear-horizontal forwards;
    
            @include bp(md-min) {
                visibility: hidden;
            }
        }
    }
    
    .landing-hero__title-text {
        position: absolute;
        top: 0;
        color: var(--color-text);
        transition: $transition-duration $transition-easing;
        transition-property: color;
    }
    
    .landing-hero__info-container {
        flex: 1;
    }
    
    .landing-hero__info {
        padding: 80px 32px 50px 71px;
        transition: $transition-duration $transition-easing $transition-duration * 3;
        transition-property: opacity, transform;
        opacity: 0;
        transform: translateY(-50px);
    
        .landing-hero.is-animating & {
            opacity: 1;
            transform: translateY(0);
        }
    }
    
    .landing-hero__button {
        margin: 0 32px 50px 71px;
        transition: $transition-duration $transition-easing $transition-duration * 4;
        transition-property: opacity, transform;
        opacity: 0;
        transform: translateY(-50px);
    
        .landing-hero.is-animating & {
            opacity: 1;
            transform: translateY(0);
        }
    }
    
  • URL: /components/raw/landing-hero/landing-hero.scss
  • Filesystem Path: src/patterns/components/landing-hero/landing-hero.scss
  • Size: 4.8 KB
  • Content:
    import './landing-hero.scss';
    import Component from '@component';
    import helpers from '@helpers';
    
    interface ICoords {
        x: number;
        y: number;
    }
    
    export default class LandingHero extends Component {
        static initSelector: string = '.landing-hero';
    
        widthFactor: number = 1.1;
        heightFactor: number = 0.8;
        h1Size: number = 80;
        h1SizeLg: number = 120;
        titleSize: number = 105;
        titleSizeLg: number = 425;
        h1Height: number = 85;
        h1HeightLg: number = 140;
        titleHeight: number = 125;
        titleHeightLg: number = 450;
        timingLength: number = .8;
    
        animationDelay: number = 1000;
        animationPercent: number = 1;
        isAnimating: boolean = false;
        titles: JQuery;
        titleCoords: ICoords[] = [];
        animationRequest: number;
    
        constructor(target: HTMLElement) {
            super(target);
    
            this.titles = this.element.find('.landing-hero__title-text');
    
            this.titles.each((index: number, domElement: HTMLElement) => {
                this.titleCoords.push({x: domElement.getBoundingClientRect().x, y: domElement.getBoundingClientRect().y});
            });
    
            this.init();
        }
    
        init(): void {
            if ($(window).scrollTop() === 0) {
                helpers.disableScroll();
    
                if (helpers.isMobileDevice) {
                    window.addEventListener('touchstart', this.startAnimation.bind(this));
                } else {
                    window.addEventListener('wheel', this.startAnimation.bind(this));
                }
    
                window.addEventListener('resize', () => this.animate(1));
    
                this.animationRequest = requestAnimationFrame(() => this.animate(2));
    
                requestAnimationFrame(() => $('body').removeClass('header-is-visible'));
            } else {
                this.element.addClass('is-animating');
                this.cleanUp();
            }
        }
    
        smoothstep(x : number): number {
            const clampedValue: number = Math.min(1, Math.max(x, 0));
    
            return clampedValue * clampedValue * (3 - 2 * clampedValue);
        }
    
        viewportWidthToPixels(vw: number): number {
            return vw * (document.documentElement.clientWidth / 100);
        }
    
        setTitlePosition(index: number, percent: number): void {
            const title: HTMLElement = this.titles.get()[index];
            const titleRect: DOMRect = title.getBoundingClientRect();
    
            const maxX: number = window.innerWidth - title.parentElement.getBoundingClientRect().x - titleRect.width * this.widthFactor;
            const maxY: number = window.innerHeight - title.parentElement.getBoundingClientRect().y - titleRect.height * this.heightFactor + titleRect.height * index * 2;
    
            const translateX: number = maxX * percent;
            const translateY: number = maxY * percent;
    
            let h1Size: number = this.h1Size;
            const titleSize: number = this.viewportWidthToPixels(28);
    
            let h1Height: number = this.h1Height;
            const titleHeight: number = this.viewportWidthToPixels(28);
    
            if (window.matchMedia('only screen and (min-width: 768px)').matches) {
                h1Size = this.h1SizeLg;
    
                h1Height = this.h1HeightLg;
            }
    
            const fontSize: number = h1Size + (titleSize - h1Size) * percent;
            const lineHeight: number = h1Height + (titleHeight - h1Height) * percent;
    
            title.style.transform = 'translate(' + translateX + 'px, ' + translateY + 'px' + ')';
            title.style.fontSize = fontSize + 'px';
            title.style.lineHeight = lineHeight + 'px';
        }
    
        startAnimation(): void {
            if (!this.isAnimating) {
                this.element.addClass('is-animating');
    
                this.isAnimating = true;
    
                window.removeEventListener('resize', this.animate.bind(this));
                this.animationRequest = requestAnimationFrame(this.animate.bind(this));
            }
        }
    
        adjustAnimationTiming(percent: number, length: number, delay: number): number {
            const lower: number = Math.max((1 - percent) - delay, 0);
    
            return 1 - Math.min(lower / length, 1);
        }
    
        animate(times?: number): void {
            if (this.isAnimating) {
                this.animationPercent = Math.max(0, this.animationPercent - 0.03);
            }
    
            if (this.animationPercent > 0) {
                this.titles.each((index: number) => {
                    this.setTitlePosition(index, this.smoothstep(this.adjustAnimationTiming(this.animationPercent, this.timingLength, (1- this.timingLength) / 3 * index)));
                });
    
                if (times !== undefined) {
                    if (times > 0) {
                        this.animationRequest = requestAnimationFrame(() => this.animate(times - 1));
                    }
                } else {
                    this.animationRequest = requestAnimationFrame(this.animate.bind(this));
                }
            } else {
                this.cleanUp();
            }
        }
    
        cleanUp(): void {
            helpers.enableScroll();
            requestAnimationFrame(() => $('body').addClass('header-is-visible'));
            this.titles.get().forEach((title: HTMLElement) => {
                title.style.transform = '';
                title.style.fontSize = '';
                title.style.lineHeight = '';
            });
        }
    }
    
  • URL: /components/raw/landing-hero/landing-hero.ts
  • Filesystem Path: src/patterns/components/landing-hero/landing-hero.ts
  • Size: 5.2 KB
  • Handle: @landing-hero--default
  • Filesystem Path: src/patterns/components/landing-hero/landing-hero.twig
  • References (1): @button
  • Referenced by (1): @view-landing