fix: performance
This commit is contained in:
@@ -57,7 +57,7 @@ function getProfile(relativePath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (normalizedRelativePath.startsWith('portrait/')) {
|
if (normalizedRelativePath.startsWith('portrait/')) {
|
||||||
return { maxWidth: 900, quality: 82, alphaQuality: 90, variants: [128, 320, 480, 640, 900] };
|
return { maxWidth: 640, quality: 82, alphaQuality: 90, variants: [128, 320, 480] };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { maxWidth: 1600, quality: 82, alphaQuality: 92 };
|
return { maxWidth: 1600, quality: 82, alphaQuality: 92 };
|
||||||
|
|||||||
@@ -66,11 +66,14 @@
|
|||||||
<div class="portrait-media" [class.portrait-media-switching]="isPortraitSwitching">
|
<div class="portrait-media" [class.portrait-media-switching]="isPortraitSwitching">
|
||||||
<img
|
<img
|
||||||
class="hero-feature-image"
|
class="hero-feature-image"
|
||||||
[src]="currentPortraitHighlight.image"
|
[ngSrc]="currentPortraitHighlight.image"
|
||||||
[attr.srcset]="currentPortraitHighlight.imageSrcset"
|
[attr.srcset]="currentPortraitHighlight.imageSrcset"
|
||||||
[attr.sizes]="currentPortraitHighlight.imageSizes"
|
[attr.sizes]="currentPortraitHighlight.imageSizes"
|
||||||
width="560"
|
[width]="currentPortraitHighlight.imageWidth"
|
||||||
height="420"
|
[height]="currentPortraitHighlight.imageHeight"
|
||||||
|
disableOptimizedSrcset
|
||||||
|
priority
|
||||||
|
decoding="async"
|
||||||
[alt]="currentPortraitHighlight.text + ' Image'"
|
[alt]="currentPortraitHighlight.text + ' Image'"
|
||||||
/>
|
/>
|
||||||
@if (currentPortraitHighlightIndex === 0) {
|
@if (currentPortraitHighlightIndex === 0) {
|
||||||
@@ -144,9 +147,12 @@
|
|||||||
<img
|
<img
|
||||||
class="project-icon"
|
class="project-icon"
|
||||||
[class.project-icon-circle]="project.CircleIcon !== false"
|
[class.project-icon-circle]="project.CircleIcon !== false"
|
||||||
[src]="project.icon"
|
[ngSrc]="project.icon"
|
||||||
[attr.srcset]="project.iconSrcset ?? null"
|
[attr.srcset]="project.iconSrcset ?? null"
|
||||||
[attr.sizes]="project.iconSizes ?? null"
|
[attr.sizes]="project.iconSizes ?? null"
|
||||||
|
width="216"
|
||||||
|
height="216"
|
||||||
|
disableOptimizedSrcset
|
||||||
[alt]="project.title + ' icon'"
|
[alt]="project.title + ' icon'"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { AfterViewInit, OnDestroy, OnInit } from '@angular/core';
|
import type { AfterViewInit, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { Component, inject, NgZone } from '@angular/core';
|
import { Component, inject, NgZone } from '@angular/core';
|
||||||
|
import { NgOptimizedImage } from '@angular/common';
|
||||||
import { faArrowRight, faArrowDown, faEnvelope, faPhone } from '@fortawesome/free-solid-svg-icons';
|
import { faArrowRight, faArrowDown, faEnvelope, faPhone } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faDiscord, faGithub, faLinkedin } from '@fortawesome/free-brands-svg-icons';
|
import { faDiscord, faGithub, faLinkedin } from '@fortawesome/free-brands-svg-icons';
|
||||||
import type { IconDefinition } from '@fortawesome/angular-fontawesome';
|
import type { IconDefinition } from '@fortawesome/angular-fontawesome';
|
||||||
@@ -16,17 +17,13 @@ import { FooterComponent } from '../footer/footer.component';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [FooterComponent, FaIconComponent],
|
imports: [FooterComponent, FaIconComponent, NgOptimizedImage],
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
})
|
})
|
||||||
export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
private readonly sanitizer = inject(DomSanitizer);
|
private readonly sanitizer = inject(DomSanitizer);
|
||||||
private readonly zone = inject(NgZone);
|
private readonly zone = inject(NgZone);
|
||||||
private preloadedImages: HTMLImageElement[] = [];
|
|
||||||
private tallestPortraitAspectRatio: number | null = null;
|
|
||||||
private lockedHeroSectionMinHeight: number = 0;
|
|
||||||
private scrollAnimationFrameId: number | null = null;
|
private scrollAnimationFrameId: number | null = null;
|
||||||
private heroHeightAnimationFrameId: number | null = null;
|
|
||||||
private projectEntryAnimationFrameId: number | null = null;
|
private projectEntryAnimationFrameId: number | null = null;
|
||||||
private aboutSectionAnimationFrameId: number | null = null;
|
private aboutSectionAnimationFrameId: number | null = null;
|
||||||
private readonly scheduleNameGradientUpdate: () => void = (): void => {
|
private readonly scheduleNameGradientUpdate: () => void = (): void => {
|
||||||
@@ -39,17 +36,6 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.updateNameGradientProgress();
|
this.updateNameGradientProgress();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
private readonly scheduleHeroSectionHeightUpdate: () => void = (): void => {
|
|
||||||
if (this.heroHeightAnimationFrameId !== null || typeof window === 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.heroHeightAnimationFrameId = window.requestAnimationFrame((): void => {
|
|
||||||
this.heroHeightAnimationFrameId = null;
|
|
||||||
this.lockedHeroSectionMinHeight = 0;
|
|
||||||
this.updateHeroSectionMinHeight();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
private readonly scheduleProjectEntryRevealUpdate: () => void = (): void => {
|
private readonly scheduleProjectEntryRevealUpdate: () => void = (): void => {
|
||||||
if (this.projectEntryAnimationFrameId !== null || typeof window === 'undefined') {
|
if (this.projectEntryAnimationFrameId !== null || typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
@@ -100,7 +86,6 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.preloadImageAssets();
|
|
||||||
this.initNameGradientScrollAnimation();
|
this.initNameGradientScrollAnimation();
|
||||||
|
|
||||||
this.zone.runOutsideAngular((): void => {
|
this.zone.runOutsideAngular((): void => {
|
||||||
@@ -199,91 +184,6 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.scrollToElementById('contact-links', this.getResponsiveScrollOffset(0.05));
|
this.scrollToElementById('contact-links', this.getResponsiveScrollOffset(0.05));
|
||||||
}
|
}
|
||||||
|
|
||||||
private preloadImageAssets(): void {
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const projectIcons: string[] = this.projects
|
|
||||||
.map((project: PortfolioProject): string | undefined => project.icon)
|
|
||||||
.filter((icon: string | undefined): icon is string => Boolean(icon));
|
|
||||||
|
|
||||||
const uniqueImageUrls: string[] = Array.from(
|
|
||||||
new Set<string>([
|
|
||||||
...this.portraitHighlights.map((highlight: PortraitHighlight): string => highlight.image),
|
|
||||||
...projectIcons,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.preloadedImages = uniqueImageUrls.map((url: string): HTMLImageElement => {
|
|
||||||
const image = new Image();
|
|
||||||
image.decoding = 'async';
|
|
||||||
image.loading = 'eager';
|
|
||||||
image.src = url;
|
|
||||||
return image;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.syncHeroHeightToLargestPortraitImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
private syncHeroHeightToLargestPortraitImage(): void {
|
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const portraitImageUrls: Set<string> = new Set<string>(
|
|
||||||
this.portraitHighlights.map(
|
|
||||||
(highlight: PortraitHighlight): string =>
|
|
||||||
new URL(highlight.image, window.location.href).href,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const portraitImages: HTMLImageElement[] = this.preloadedImages.filter(
|
|
||||||
(image: HTMLImageElement): boolean => portraitImageUrls.has(image.src),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (portraitImages.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const finalizeAspectRatio = (): void => {
|
|
||||||
const minAspectRatio: number = portraitImages.reduce(
|
|
||||||
(minRatio: number, image: HTMLImageElement): number => {
|
|
||||||
if (image.naturalWidth === 0 || image.naturalHeight === 0) {
|
|
||||||
return minRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentRatio: number = image.naturalWidth / image.naturalHeight;
|
|
||||||
return minRatio === 0 ? currentRatio : Math.min(minRatio, currentRatio);
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (minAspectRatio > 0) {
|
|
||||||
this.tallestPortraitAspectRatio = minAspectRatio;
|
|
||||||
this.scheduleHeroSectionHeightUpdate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let pendingLoads: number = portraitImages.length;
|
|
||||||
const onImageSettled = (): void => {
|
|
||||||
pendingLoads -= 1;
|
|
||||||
if (pendingLoads === 0) {
|
|
||||||
finalizeAspectRatio();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const image of portraitImages) {
|
|
||||||
if (image.complete) {
|
|
||||||
onImageSettled();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
image.addEventListener('load', onImageSettled, { once: true });
|
|
||||||
image.addEventListener('error', onImageSettled, { once: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private initNameGradientScrollAnimation(): void {
|
private initNameGradientScrollAnimation(): void {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
@@ -291,9 +191,7 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
window.addEventListener('scroll', this.scheduleNameGradientUpdate, { passive: true });
|
window.addEventListener('scroll', this.scheduleNameGradientUpdate, { passive: true });
|
||||||
window.addEventListener('resize', this.scheduleNameGradientUpdate, { passive: true });
|
window.addEventListener('resize', this.scheduleNameGradientUpdate, { passive: true });
|
||||||
window.addEventListener('resize', this.scheduleHeroSectionHeightUpdate, { passive: true });
|
|
||||||
this.scheduleNameGradientUpdate();
|
this.scheduleNameGradientUpdate();
|
||||||
this.scheduleHeroSectionHeightUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initProjectEntryRevealObserver(): void {
|
private initProjectEntryRevealObserver(): void {
|
||||||
@@ -363,20 +261,13 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
window.removeEventListener('scroll', this.scheduleNameGradientUpdate);
|
window.removeEventListener('scroll', this.scheduleNameGradientUpdate);
|
||||||
window.removeEventListener('resize', this.scheduleNameGradientUpdate);
|
window.removeEventListener('resize', this.scheduleNameGradientUpdate);
|
||||||
window.removeEventListener('resize', this.scheduleHeroSectionHeightUpdate);
|
|
||||||
|
|
||||||
if (this.scrollAnimationFrameId !== null) {
|
if (this.scrollAnimationFrameId !== null) {
|
||||||
window.cancelAnimationFrame(this.scrollAnimationFrameId);
|
window.cancelAnimationFrame(this.scrollAnimationFrameId);
|
||||||
this.scrollAnimationFrameId = null;
|
this.scrollAnimationFrameId = null;
|
||||||
}
|
}
|
||||||
if (this.heroHeightAnimationFrameId !== null) {
|
|
||||||
window.cancelAnimationFrame(this.heroHeightAnimationFrameId);
|
|
||||||
this.heroHeightAnimationFrameId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.documentElement.style.removeProperty('--name-pride-progress');
|
document.documentElement.style.removeProperty('--name-pride-progress');
|
||||||
document.documentElement.style.removeProperty('--hero-section-min-height');
|
|
||||||
this.lockedHeroSectionMinHeight = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateNameGradientProgress(): void {
|
private updateNameGradientProgress(): void {
|
||||||
@@ -405,57 +296,6 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
root.style.setProperty('--name-pride-progress', progress.toFixed(4));
|
root.style.setProperty('--name-pride-progress', progress.toFixed(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateHeroSectionMinHeight(): void {
|
|
||||||
if (typeof window === 'undefined' || this.tallestPortraitAspectRatio === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const heroSection: HTMLElement | null = document.querySelector('.hero-section');
|
|
||||||
const heroCopy: HTMLElement | null = document.querySelector('.hero-copy');
|
|
||||||
const portraitHighlight: HTMLElement | null = document.querySelector('.portrait-highlight');
|
|
||||||
const portraitButton: HTMLElement | null = document.querySelector('.portrait-highlight-button');
|
|
||||||
|
|
||||||
if (
|
|
||||||
heroSection === null ||
|
|
||||||
heroCopy === null ||
|
|
||||||
portraitHighlight === null ||
|
|
||||||
portraitButton === null
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttonWidth: number = portraitButton.getBoundingClientRect().width;
|
|
||||||
if (buttonWidth <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxMediaHeight: number = buttonWidth / this.tallestPortraitAspectRatio;
|
|
||||||
const currentButtonHeight: number = portraitButton.getBoundingClientRect().height;
|
|
||||||
const currentHighlightHeight: number = portraitHighlight.getBoundingClientRect().height;
|
|
||||||
const staticHighlightOverhead: number = Math.max(
|
|
||||||
currentHighlightHeight - currentButtonHeight,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const requiredHighlightHeight: number = maxMediaHeight + staticHighlightOverhead;
|
|
||||||
const requiredContentHeight: number = Math.max(
|
|
||||||
heroCopy.getBoundingClientRect().height,
|
|
||||||
requiredHighlightHeight,
|
|
||||||
);
|
|
||||||
const heroSectionStyles: CSSStyleDeclaration = window.getComputedStyle(heroSection);
|
|
||||||
const paddingTop: number = Number.parseFloat(heroSectionStyles.paddingTop) || 0;
|
|
||||||
const paddingBottom: number = Number.parseFloat(heroSectionStyles.paddingBottom) || 0;
|
|
||||||
const requiredSectionMinHeight: number = requiredContentHeight + paddingTop + paddingBottom;
|
|
||||||
this.lockedHeroSectionMinHeight = Math.max(
|
|
||||||
this.lockedHeroSectionMinHeight,
|
|
||||||
requiredSectionMinHeight,
|
|
||||||
);
|
|
||||||
|
|
||||||
document.documentElement.style.setProperty(
|
|
||||||
'--hero-section-min-height',
|
|
||||||
`${Math.ceil(this.lockedHeroSectionMinHeight)}px`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateAboutSectionScrollProgress(): void {
|
private updateAboutSectionScrollProgress(): void {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+20
-6
@@ -13,6 +13,8 @@ export interface PortraitHighlight {
|
|||||||
image: string;
|
image: string;
|
||||||
imageSrcset: string;
|
imageSrcset: string;
|
||||||
imageSizes: string;
|
imageSizes: string;
|
||||||
|
imageWidth: number;
|
||||||
|
imageHeight: number;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,49 +82,61 @@ export const global: Global = {
|
|||||||
{
|
{
|
||||||
image: 'assets/optimized/portrait/me.webp',
|
image: 'assets/optimized/portrait/me.webp',
|
||||||
imageSrcset:
|
imageSrcset:
|
||||||
'assets/optimized/portrait/me-320.webp 320w, assets/optimized/portrait/me-480.webp 480w, assets/optimized/portrait/me-640.webp 640w, assets/optimized/portrait/me.webp 900w',
|
'assets/optimized/portrait/me-320.webp 320w, assets/optimized/portrait/me-480.webp 480w, assets/optimized/portrait/me.webp 640w',
|
||||||
imageSizes:
|
imageSizes:
|
||||||
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
||||||
|
imageWidth: 640,
|
||||||
|
imageHeight: 769,
|
||||||
text: 'ME',
|
text: 'ME',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: 'assets/optimized/portrait/love.webp',
|
image: 'assets/optimized/portrait/love.webp',
|
||||||
imageSrcset:
|
imageSrcset:
|
||||||
'assets/optimized/portrait/love-320.webp 320w, assets/optimized/portrait/love-480.webp 480w, assets/optimized/portrait/love-640.webp 640w, assets/optimized/portrait/love.webp 900w',
|
'assets/optimized/portrait/love-320.webp 320w, assets/optimized/portrait/love-480.webp 480w, assets/optimized/portrait/love.webp 640w',
|
||||||
imageSizes:
|
imageSizes:
|
||||||
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
||||||
|
imageWidth: 640,
|
||||||
|
imageHeight: 710,
|
||||||
text: 'My Love',
|
text: 'My Love',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: 'assets/optimized/portrait/scuba.webp',
|
image: 'assets/optimized/portrait/scuba.webp',
|
||||||
imageSrcset:
|
imageSrcset:
|
||||||
'assets/optimized/portrait/scuba-320.webp 320w, assets/optimized/portrait/scuba-480.webp 480w, assets/optimized/portrait/scuba-640.webp 640w, assets/optimized/portrait/scuba.webp 900w',
|
'assets/optimized/portrait/scuba-320.webp 320w, assets/optimized/portrait/scuba-480.webp 480w, assets/optimized/portrait/scuba.webp 640w',
|
||||||
imageSizes:
|
imageSizes:
|
||||||
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
||||||
|
imageWidth: 640,
|
||||||
|
imageHeight: 480,
|
||||||
text: 'Scuba Diving',
|
text: 'Scuba Diving',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: 'assets/optimized/portrait/trains.webp',
|
image: 'assets/optimized/portrait/trains.webp',
|
||||||
imageSrcset:
|
imageSrcset:
|
||||||
'assets/optimized/portrait/trains-320.webp 320w, assets/optimized/portrait/trains-480.webp 480w, assets/optimized/portrait/trains-640.webp 640w, assets/optimized/portrait/trains.webp 900w',
|
'assets/optimized/portrait/trains-320.webp 320w, assets/optimized/portrait/trains-480.webp 480w, assets/optimized/portrait/trains.webp 640w',
|
||||||
imageSizes:
|
imageSizes:
|
||||||
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
||||||
|
imageWidth: 640,
|
||||||
|
imageHeight: 850,
|
||||||
text: 'Trains',
|
text: 'Trains',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: 'assets/optimized/portrait/traveling.webp',
|
image: 'assets/optimized/portrait/traveling.webp',
|
||||||
imageSrcset:
|
imageSrcset:
|
||||||
'assets/optimized/portrait/traveling-320.webp 320w, assets/optimized/portrait/traveling-480.webp 480w, assets/optimized/portrait/traveling-640.webp 640w, assets/optimized/portrait/traveling.webp 900w',
|
'assets/optimized/portrait/traveling-320.webp 320w, assets/optimized/portrait/traveling-480.webp 480w, assets/optimized/portrait/traveling.webp 640w',
|
||||||
imageSizes:
|
imageSizes:
|
||||||
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
||||||
|
imageWidth: 640,
|
||||||
|
imageHeight: 480,
|
||||||
text: 'Traveling',
|
text: 'Traveling',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: 'assets/optimized/portrait/culture.webp',
|
image: 'assets/optimized/portrait/culture.webp',
|
||||||
imageSrcset:
|
imageSrcset:
|
||||||
'assets/optimized/portrait/culture-320.webp 320w, assets/optimized/portrait/culture-480.webp 480w, assets/optimized/portrait/culture-640.webp 640w, assets/optimized/portrait/culture.webp 900w',
|
'assets/optimized/portrait/culture-320.webp 320w, assets/optimized/portrait/culture-480.webp 480w, assets/optimized/portrait/culture.webp 640w',
|
||||||
imageSizes:
|
imageSizes:
|
||||||
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
'(max-width: 700px) calc(100vw - 2rem), (max-width: 900px) min(100vw - 6rem, 560px), 560px',
|
||||||
|
imageWidth: 640,
|
||||||
|
imageHeight: 482,
|
||||||
text: 'Culture',
|
text: 'Culture',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -119,7 +119,6 @@ hr {
|
|||||||
grid-template-columns: minmax(0, 1.15fr) minmax(280px, 0.85fr);
|
grid-template-columns: minmax(0, 1.15fr) minmax(280px, 0.85fr);
|
||||||
gap: var(--space-6);
|
gap: var(--space-6);
|
||||||
align-items: start;
|
align-items: start;
|
||||||
min-height: var(--hero-section-min-height, auto);
|
|
||||||
padding: clamp(2rem, 4vw, 4rem);
|
padding: clamp(2rem, 4vw, 4rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user