fix: style; feat: AGENTS.md
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import type { ApplicationConfig } from '@angular/core';
|
||||
import { provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter, withInMemoryScrolling } from '@angular/router';
|
||||
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
@@ -15,6 +14,5 @@ export const appConfig: ApplicationConfig = {
|
||||
scrollPositionRestoration: 'top',
|
||||
}),
|
||||
),
|
||||
provideClientHydration(withEventReplay()),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -57,13 +57,12 @@
|
||||
<div class="portrait-media" [class.portrait-media-switching]="isPortraitSwitching">
|
||||
<img
|
||||
class="hero-feature-image"
|
||||
[ngSrc]="currentPortraitHighlight.image"
|
||||
[src]="currentPortraitHighlight.image"
|
||||
[attr.srcset]="currentPortraitHighlight.imageSrcset"
|
||||
[attr.sizes]="currentPortraitHighlight.imageSizes"
|
||||
[width]="currentPortraitHighlight.imageWidth"
|
||||
[height]="currentPortraitHighlight.imageHeight"
|
||||
disableOptimizedSrcset
|
||||
priority
|
||||
fetchpriority="high"
|
||||
decoding="async"
|
||||
[alt]="currentPortraitHighlight.text + ' ' + content().home.portraitAltSuffix"
|
||||
/>
|
||||
|
||||
@@ -33,6 +33,7 @@ describe('HomeComponent', (): void => {
|
||||
scheduleNameGradientUpdate(): void;
|
||||
scheduleProjectEntryRevealUpdate(): void;
|
||||
scheduleAboutSectionScrollAnimationUpdate(): void;
|
||||
shouldEnableScrollEffects(): boolean;
|
||||
toggleBio(): void;
|
||||
showNextPortraitHighlight(): void;
|
||||
scrollToAboutSection(): void;
|
||||
@@ -428,6 +429,29 @@ describe('HomeComponent', (): void => {
|
||||
// ── RAF debouncing (performance) ───────────────────────────────────────────
|
||||
|
||||
describe('Performance', (): void => {
|
||||
it('should keep scroll effects enabled on small viewports when motion is allowed', (): void => {
|
||||
spyOnProperty(window, 'innerWidth').and.returnValue(390);
|
||||
spyOn(window, 'matchMedia').and.callFake(
|
||||
(query: string): MediaQueryList =>
|
||||
({
|
||||
matches: query === '(prefers-reduced-motion: reduce)' ? false : true,
|
||||
}) as MediaQueryList,
|
||||
);
|
||||
|
||||
expect(comp.shouldEnableScrollEffects()).toBeTrue();
|
||||
});
|
||||
|
||||
it('should disable scroll effects when reduced motion is requested', (): void => {
|
||||
spyOn(window, 'matchMedia').and.callFake(
|
||||
(query: string): MediaQueryList =>
|
||||
({
|
||||
matches: query === '(prefers-reduced-motion: reduce)',
|
||||
}) as MediaQueryList,
|
||||
);
|
||||
|
||||
expect(comp.shouldEnableScrollEffects()).toBeFalse();
|
||||
});
|
||||
|
||||
it('should create and run initial change detection within 200 ms', (): void => {
|
||||
const start = performance.now();
|
||||
const f = TestBed.createComponent(HomeComponent);
|
||||
|
||||
@@ -220,13 +220,11 @@ export class HomeComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private shouldEnableScrollEffects(): boolean {
|
||||
if (typeof window === 'undefined') return false;
|
||||
|
||||
const isSmallViewport: boolean = window.innerWidth <= 700;
|
||||
const prefersReducedMotion: boolean = window.matchMedia(
|
||||
'(prefers-reduced-motion: reduce)',
|
||||
).matches;
|
||||
const isCoarsePointer: boolean = window.matchMedia('(pointer: coarse)').matches;
|
||||
|
||||
return !isSmallViewport && !prefersReducedMotion && !isCoarsePointer;
|
||||
return !prefersReducedMotion;
|
||||
}
|
||||
|
||||
private initNameGradientScrollAnimation(): void {
|
||||
|
||||
+10
-22
@@ -1010,14 +1010,6 @@ hr {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(217, 162, 140, 0.18), transparent 28%),
|
||||
radial-gradient(circle at bottom right, rgba(127, 78, 63, 0.24), transparent 32%),
|
||||
linear-gradient(180deg, rgba(9, 9, 12, 0.72), rgba(8, 8, 11, 0.96)),
|
||||
url('assets/optimized/background.webp') center top / cover no-repeat;
|
||||
}
|
||||
|
||||
.page-shell,
|
||||
.footer-inner {
|
||||
width: min(calc(100% - 2rem), var(--page-max-width));
|
||||
@@ -1162,20 +1154,6 @@ hr {
|
||||
backdrop-filter: none;
|
||||
}
|
||||
|
||||
.project-entry,
|
||||
.about-scroll-anim-enabled .story-copy,
|
||||
.about-scroll-anim-enabled .story-detail {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
will-change: auto;
|
||||
}
|
||||
|
||||
.project-entry {
|
||||
transition:
|
||||
border-color var(--transition-fast),
|
||||
background var(--transition-fast);
|
||||
}
|
||||
|
||||
.projects-section,
|
||||
.story-section,
|
||||
.legal-section {
|
||||
@@ -1184,6 +1162,16 @@ hr {
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) and (hover: none) and (pointer: coarse) {
|
||||
body {
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(217, 162, 140, 0.18), transparent 28%),
|
||||
radial-gradient(circle at bottom right, rgba(127, 78, 63, 0.24), transparent 32%),
|
||||
linear-gradient(180deg, rgba(9, 9, 12, 0.72), rgba(8, 8, 11, 0.96)),
|
||||
url('assets/optimized/background.webp') center / cover fixed no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
.action-link:hover,
|
||||
.detail-toggle:hover,
|
||||
|
||||
Reference in New Issue
Block a user