Files
portifolio-evandrodevbr/app/cases/_components/CaseCover.tsx
T
Evandro Fonseca Junior de6af74a0c feat(cases): prova certa por serviço, páginas internas com trabalho no topo
Diagnóstico sem case que não cabe; desktop com o GCC Installer como projeto próprio; IoT com o Twins Lab e a nota da Apolo. Mosaico de pôsteres em /servicos e /cases, faixa baixa em uma coluna no currículo, privacidade e laboratório; Laboratório como lista compacta; SEV começando por catálogo, pedido no WhatsApp e painel; Joinville e SC nos títulos; imagens do LCP com fetchPriority e sizes reais; pré-carregamento só no hover; 404 com Open Graph próprio.
2026-10-14 17:01:35 -03:00

69 lines
2.5 KiB
TypeScript

import Image from "next/image";
import {
BrowserFrame,
displayUrl,
} from "@/app/components/showcase/BrowserFrame";
import { getCaseMedia } from "./case-media";
/**
* Largura real da capa em cada lugar da página, para o navegador baixar a imagem do tamanho certo
* (com 100vw, um celular de 390 px pedia a versão de 750 px para uma capa de 300 px). As contas seguem o
* padding das seções (20 px até 768 px; depois 6vw, no máximo 64 px), a coluna central de 1240 px e os cartões
* (borda e 20 a 28 px de padding).
*/
export const coverSizes = {
/** Topo do case e do serviço: coluna da direita a partir de 1024 px, e a largura da página abaixo disso. */
hero: "(min-width: 1440px) 560px, (min-width: 1024px) 40vw, (min-width: 769px) 88vw, calc(100vw - 40px)",
/** Capa dentro de um cartão (`ds-card`) da grade de duas colunas, que vira uma só até 1024 px. */
card: "(min-width: 1440px) 560px, (min-width: 1025px) calc(50vw - 134px), (min-width: 769px) calc(88vw - 56px), calc(100vw - 88px)",
/** Capa solta numa grade de duas colunas, sem cartão em volta (a partir de 768 px). */
column:
"(min-width: 1440px) 604px, (min-width: 1068px) calc(50vw - 80px), (min-width: 768px) calc(44vw - 16px), calc(100vw - 40px)",
} as const;
/**
* Props da imagem que abre a página (a maior acima da dobra): carrega na hora e com prioridade de rede alta.
* No Next 16 a prop `priority` do `Image` só gera o `<link rel="preload">`; a prioridade de rede vem do `fetchPriority`.
*/
export const priorityImageProps = {
loading: "eager",
fetchPriority: "high",
} as const;
/**
* Capa do case: o pôster do site dentro de uma moldura de navegador. Sem captura gravada, não mostra nada.
* `priority` é para a imagem que abre a página: aplica `priorityImageProps`.
*/
export function CaseCover({
id,
title,
demoUrl,
sizes = coverSizes.card,
priority = false,
}: {
id: string;
title: string;
demoUrl?: string | null;
sizes?: string;
priority?: boolean;
}) {
const media = getCaseMedia(id, "desktop");
if (!media) return null;
const priorityProps = priority ? priorityImageProps : {};
return (
<BrowserFrame url={displayUrl({ demoUrl, title })}>
<div className="relative aspect-[16/10] bg-[#0a0a0a]">
<Image
src={media.poster}
alt={`Captura de tela do site ${title}`}
fill
sizes={sizes}
{...priorityProps}
className="object-cover object-top"
/>
</div>
</BrowserFrame>
);
}