feat(site): blocos 3D da marca, novas ofertas e hero mais abrangente
Hero e movimento - cena 3D em tempo real com os blocos da marca (kit feito no Blender, 3d/blocos.py): marca, tela, integrações e dados; segue a rolagem, tem vida em repouso, reage ao cursor e, parada no topo, passa sozinha pelos estados com legenda - três níveis de aparelho (full, lite, static); sem WebGL fica o pôster - título novo: "Sites e sistemas sob medida para o jeito que a sua empresa trabalha." - faixa de clientes com o ramo, linha do primeiro passo, hero alinhado às seções - rótulos digitados, entrada dos blocos, cortina de blocos entre páginas e capa compartilhada entre a lista de cases e o case Ofertas - atendimento no WhatsApp com IA e nota fiscal para a reforma tributária (páginas de serviço, cartões da home, formulário e aviso com data de validade) - página /ia atualizada, com fontes - peças 3D em loop nos cartões e topos de página (3d/pecas.py) Design - hover por papel (bloco nos botões, barra nos links), rótulos sem caixa alta - um destino por rótulo: "Falar sobre meu projeto" leva ao formulário, "Chamar no WhatsApp" abre o WhatsApp - logo montado em blocos, 404 redesenhado, grades sem célula vazia - ícones do PWA e manifesto novos Specs em specs/, contexto de design em DESIGN.md e PRODUCT.md.
This commit is contained in:
1 parent
4671837c44
commit
c7d09d63d4
165 files changed
+7940
-3790
No files matched your search
+44
-93
@@ -1,16 +1,8 @@
|
||||
import Image from "next/image";
|
||||
import { getShowcaseAssets } from "@/lib/project-showcase";
|
||||
import HoverPrefetchLink from "./HoverPrefetchLink";
|
||||
import { BrowserFrame } from "./showcase/BrowserFrame";
|
||||
import { PhoneFrame } from "./showcase/PhoneFrame";
|
||||
|
||||
/** Endereço mostrado na barra do navegador (mesmo formato de displayUrl em showcase/BrowserFrame). */
|
||||
export type HeroWorkUrl = { text: string; secure: boolean } | null;
|
||||
|
||||
const DESKTOP_PROJECT = "lavacao-047";
|
||||
const DESKTOP_PROJECT_TITLE = "Lavação 047";
|
||||
const MOBILE_PROJECT = "as-servicos";
|
||||
/** No celular o cartão mostra o site da SEV e leva ao case; o navegador da tela larga, ao lado do título, mostra o da Lavação 047. */
|
||||
/** O cartão mostra o site da SEV e leva ao case. */
|
||||
const SMALL_SCREEN_PROJECT = "sev-modaintima";
|
||||
const SMALL_SCREEN_TITLE = "SEV Moda Íntima";
|
||||
const SMALL_SCREEN_HREF = `/cases/${SMALL_SCREEN_PROJECT}`;
|
||||
@@ -28,95 +20,54 @@ const SMALL_CARD_IMAGE_HEIGHT = 72;
|
||||
const SMALL_CARD_IMAGE_POSITION = "object-[50%_57%]";
|
||||
|
||||
/**
|
||||
* Trabalho real do hero, em dois formatos. Em telas de 1024 px ou mais, ao lado do título: o pôster do site da
|
||||
* Lavação 047 no navegador (a barra de endereço só mostra domínio próprio; o da Lavação é de hospedagem, então ela
|
||||
* fica com "pré-visualização") e o do aplicativo da AS Serviços no celular, numa coluna de cerca de 40% da largura. Abaixo de 1024 px, um cartão largo e clicável
|
||||
* (a largura da coluna, cerca de 350 px num celular) com o pôster do site da SEV, abaixo dos botões, que leva ao
|
||||
* case da SEV; antes era uma moldura de celular de 65 por 160 px, pequena demais para ver o que era.
|
||||
* Os pôsteres vão pelo next/image com `sizes` do tamanho em que aparecem (um arquivo de 1440 px para 491 px de tela
|
||||
* era banda desperdiçada), em `loading="lazy"` e prioridade baixa: no celular o título segue como o maior elemento da
|
||||
* dobra (LCP; ver SMALL_CARD_IMAGE_HEIGHT), e a imagem que fica escondida pelo breakpoint (display: none) nem é
|
||||
* baixada. Em tela larga o pôster do navegador é maior que o título e, numa rede lenta, vira o LCP quando chega.
|
||||
* Trabalho real do hero, só abaixo de 1024 px: um cartão largo e clicável (a largura da coluna, cerca de 350 px num
|
||||
* celular) com o pôster do site da SEV, abaixo dos botões, que leva ao case da SEV; antes era uma moldura de celular
|
||||
* de 65 por 160 px, pequena demais para ver o que era. Em telas largas a coluna da direita é da peça 3D, e o trabalho
|
||||
* real começa na seção Projetos.
|
||||
* O pôster vai pelo next/image com `sizes` do tamanho em que aparece, em `loading="lazy"` e prioridade baixa: o título
|
||||
* segue como o maior elemento da dobra (LCP; ver SMALL_CARD_IMAGE_HEIGHT), e em tela larga, onde o cartão fica
|
||||
* escondido pelo breakpoint (display: none), a imagem nem é baixada.
|
||||
*/
|
||||
export default function HeroWork({ url = null }: { url?: HeroWorkUrl }) {
|
||||
const desktop = getShowcaseAssets(DESKTOP_PROJECT, "desktop");
|
||||
const phone = getShowcaseAssets(MOBILE_PROJECT, "mobile");
|
||||
export default function HeroWork() {
|
||||
const smallCard = getShowcaseAssets(SMALL_SCREEN_PROJECT, "mobile");
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
data-testid="hero-work"
|
||||
className="hero-in pointer-events-none absolute top-1/2 right-sp-12 hidden w-[40%] -translate-y-[46%] [--d:.8s] lg:block"
|
||||
<HoverPrefetchLink
|
||||
data-testid="hero-work-mobile"
|
||||
href={SMALL_SCREEN_HREF}
|
||||
className="hero-in group mt-sp-5 flex w-full flex-col overflow-hidden border border-white/20 bg-white/[0.05] font-display no-underline transition-colors hover:border-white/45 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--slash)] motion-reduce:transition-none [--d:.8s] lg:hidden"
|
||||
>
|
||||
<span
|
||||
className="relative block w-full overflow-hidden bg-[var(--ink)]"
|
||||
style={{ height: SMALL_CARD_IMAGE_HEIGHT }}
|
||||
>
|
||||
<BrowserFrame url={url} className="w-[88%]">
|
||||
{/* a moldura mostra ~491 px em 1440 px de tela: 36vw até 1400 px, e fixo dali em diante */}
|
||||
<Image
|
||||
src={desktop.poster}
|
||||
alt={`Site da ${DESKTOP_PROJECT_TITLE} aberto no computador`}
|
||||
width={desktop.width}
|
||||
height={desktop.height}
|
||||
sizes="(min-width: 1400px) 500px, 36vw"
|
||||
loading="lazy"
|
||||
fetchPriority="low"
|
||||
className="block h-auto w-full"
|
||||
/>
|
||||
</BrowserFrame>
|
||||
{/* position inline: .ds-phone (showcase.css, sem camada) define position: relative e vence a utilidade "absolute" */}
|
||||
<PhoneFrame
|
||||
className="right-0 bottom-[-12%] w-[26%]"
|
||||
style={{ position: "absolute" }}
|
||||
>
|
||||
<Image
|
||||
src={phone.poster}
|
||||
alt="Site da AS Serviços aberto no celular"
|
||||
width={phone.width}
|
||||
height={phone.height}
|
||||
sizes="(min-width: 1400px) 136px, 10vw"
|
||||
loading="lazy"
|
||||
fetchPriority="low"
|
||||
className="absolute inset-0 block h-full w-full object-cover object-top"
|
||||
/>
|
||||
</PhoneFrame>
|
||||
</div>
|
||||
|
||||
<HoverPrefetchLink
|
||||
data-testid="hero-work-mobile"
|
||||
href={SMALL_SCREEN_HREF}
|
||||
className="hero-in group mt-sp-5 flex w-full flex-col overflow-hidden border border-white/20 bg-white/[0.05] font-display no-underline transition-colors hover:border-white/45 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--slash)] motion-reduce:transition-none [--d:.8s] lg:hidden"
|
||||
>
|
||||
<span
|
||||
className="relative block w-full overflow-hidden bg-[var(--ink)]"
|
||||
style={{ height: SMALL_CARD_IMAGE_HEIGHT }}
|
||||
>
|
||||
<Image
|
||||
src={smallCard.poster}
|
||||
alt=""
|
||||
width={smallCard.width}
|
||||
height={smallCard.height}
|
||||
sizes="(min-width: 1024px) 1px, calc(100vw - 48px)"
|
||||
loading="lazy"
|
||||
fetchPriority="low"
|
||||
className={`absolute inset-0 block h-full w-full object-cover ${SMALL_CARD_IMAGE_POSITION}`}
|
||||
/>
|
||||
<Image
|
||||
src={smallCard.poster}
|
||||
alt=""
|
||||
width={smallCard.width}
|
||||
height={smallCard.height}
|
||||
sizes="(min-width: 1024px) 1px, calc(100vw - 48px)"
|
||||
loading="lazy"
|
||||
fetchPriority="low"
|
||||
className={`absolute inset-0 block h-full w-full object-cover ${SMALL_CARD_IMAGE_POSITION}`}
|
||||
/>
|
||||
</span>
|
||||
<span className="flex min-h-12 items-center justify-between gap-sp-4 px-sp-4 py-sp-2 text-[15px] leading-6 text-[#f4f2ed]">
|
||||
<span className="flex flex-col">
|
||||
<span className="font-semibold">Site da {SMALL_SCREEN_TITLE}</span>{" "}
|
||||
<span className="text-[rgba(244,242,237,.72)]">Ver case</span>
|
||||
</span>
|
||||
<span className="flex min-h-12 items-center justify-between gap-sp-4 px-sp-4 py-sp-2 text-[15px] leading-6 text-[#f4f2ed]">
|
||||
<span className="flex flex-col">
|
||||
<span className="font-semibold">Site da {SMALL_SCREEN_TITLE}</span>{" "}
|
||||
<span className="text-[rgba(244,242,237,.72)]">Ver case</span>
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 16 16"
|
||||
className="h-4 w-4 shrink-0 transition-transform group-hover:translate-x-1 motion-reduce:transition-none"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M2 8h11M9 3.5 13.5 8 9 12.5" />
|
||||
</svg>
|
||||
</span>
|
||||
</HoverPrefetchLink>
|
||||
</>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 16 16"
|
||||
className="h-4 w-4 shrink-0 transition-transform group-hover:translate-x-1 motion-reduce:transition-none"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M2 8h11M9 3.5 13.5 8 9 12.5" />
|
||||
</svg>
|
||||
</span>
|
||||
</HoverPrefetchLink>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user