Design Engineer. I build with React, Next.js, TypeScript. See my work at https://damirkotoric.com
- Next.js 16 (App Router)
- TypeScript
- Tailwind CSS 4 (inline
@themeconfig inglobals.css, notailwind.configfile) - shadcn/ui
- Motion (Framer Motion)
- pnpm (pinned to 10.18.1 via corepack —
npmandyarnwon't work)
pnpm devpredev and prebuild hooks auto-regenerate image dimensions before every dev and build.
All portfolio images live in public/images/portfolio/ as AVIF.
ffmpeg -i input.png -c:v libaom-av1 -crf 18 -still-picture 1 output.avifCRF 18 (visually lossless) is the compression standard for this project. Lower CRF = higher quality/larger file, higher CRF = more compression/smaller file.
src/lib/image-dimensions.ts is auto-generated by scripts/generate-image-dimensions.ts. Don't edit it manually. It runs automatically on dev and build, or manually via pnpm gen:images.
Without correct dimensions, grid images fall back to 600px width and render blurry.
Portfolio images use quality={100} on the Next.js <Image> component since CRF 18 already handles compression. Apply this to any new portfolio image usage.
src/lib/blur-placeholders.ts contains base64 blur data for portfolio showcase images. These are manually maintained (no generation script).
PixelatedCanvas (src/components/ui/pixelated-canvas.tsx) can't reliably read AVIF naturalWidth/naturalHeight from the browser. Pass explicit naturalWidth and naturalHeight props when using AVIF sources with this component.
Portfolio projects are defined in src/lib/portfolio-data.tsx.
full- Full-width imagegrid-2- Two-column gridgrid-3- Three-column grid
Animated cover backgrounds are mapped in src/components/portfolio-cover.tsx:
- Create component (e.g.,
src/components/project-bg.tsx) - Import and add to
interactiveCoversmap
@/* maps to ./src/* (configured in tsconfig.json). Use @/components, @/lib, etc. for all imports.