perf: servir imagens de conteúdo em webp e descrever sizes corretamente (MAN-109)

Fecha a meta de LCP ≤ 2,5 s da SPEC §6.6 em todas as páginas, nos dois presets.
Continuação direta do commit anterior: com fontes e marca resolvidas, o elemento
de LCP de toda página no mobile passou a ser a imagem do hero, e o Load Delay de
1,88 s era contenção de banda pura.

## Variantes webp

`ResponsiveImage::generate()` escreve uma variante `.webp` ao lado de cada
variante no formato original, e `x-media.image` a oferece num
`<source type="image/webp">`. O `<img>` continua apontando para o formato
original: o `<source>` é preferência, não substituição, então nada quebra em
quem não decodifica webp, e mídia antiga sem irmãos webp renderiza `<img>` puro
como antes.

O `<picture>` recebe `display: contents` porque os chamadores estilizam o `<img>`
com classes como `h-full w-full object-cover` que resolvem contra o pai grid ou
flex — um wrapper inline quebraria isso.

O nome do arquivo acrescenta a extensão em vez de trocá-la
(`photo-720.jpg.webp`): uploads usam UUID, então colisão já era improvável, mas
`photo-720.webp` colidiria com a variante webp de um `photo.png`.

## sizes que descreve a realidade

Nenhuma imagem do site ocupa a viewport inteira — todas ficam dentro de
`container-amare`, que reserva 1,5rem de padding de cada lado. Declarar `100vw`
fazia uma viewport de 412 px em DPR 1,75 pedir 721 px e pular para a variante de
960 para desenhar uma caixa de 637 px. Errar por um pixel custava um terço a mais
de bytes em toda página.

Com `calc(100vw - 3rem)` a home passa a usar a variante de 720: 74 KiB, contra
143 KiB no início da investigação. Foi também por isso que 720 entrou em
`ResponsiveImage::WIDTHS` — sem ela o salto de 480 para 960 é grande demais para
a viewport mobile mais comum.

## Efeito medido

Mobile, mediana de 3 execuções: home 3,39 → 2,49 s; portfolio-detalhe
3,01 → 2,18 s; servicos 2,63 → 2,03 s; portfolio 1,58 s; sobre 1,58 s;
contato 1,50 s. Desktop no máximo 0,65 s. Peso total da home 798 → 347 KiB.

Contra o início da investigação (`2e43fde`): home 4,58 → 2,49 s com score de
performance 83 → 98.

A home passa **em cima da linha** — a pior das três execuções deu 2,57 s. Está
documentado como aprovada por margem, não com folga, e os levers restantes estão
listados em docs/evidence/lighthouse/README.md em ordem de custo.

Os 16 baselines visuais não mudaram: nenhum seeder gera variantes e
`media:generate-variants` não roda no runner visual, então naquele ambiente
`availableVariants()` volta vazio e o componente renderiza `<img>` puro. A
cobertura do caminho com variantes fica em MediaImageComponentTest e
ResponsiveImageTest, não nos baselines — anotado como lacuna conhecida.

Co-Authored-By: Claude noreply@anthropic.com
AI-Assisted: yes
AI-Tool: claude-code
This commit is contained in:
manoel.neto
2026-08-10 14:43:00 -03:00
parent 65919d4a6c
commit 9ab2beb3ea
13 changed files with 839 additions and 195 deletions

View File

@@ -12,8 +12,16 @@ use Throwable;
final class ResponsiveImage final class ResponsiveImage
{ {
/** @var list<int> */ /**
public const WIDTHS = [480, 960, 1440]; * 720 sits between the original 480 and 960 because that is where the common
* mobile viewport lands: 412 CSS px at a 1.75 device pixel ratio asks for
* ~721 px, so a full-width image used to jump straight to the 960 variant and
* pay for a third more pixels than it drew. Measured on the hero (MAN-109):
* 143 KiB at 960 in jpeg against 75 KiB at 720 in webp.
*
* @var list<int>
*/
public const WIDTHS = [480, 720, 960, 1440];
public static function generate(string $path, ?string $disk = null): void public static function generate(string $path, ?string $disk = null): void
{ {
@@ -47,6 +55,26 @@ final class ResponsiveImage
}; };
$filesystem->put($variantPath, (string) $encoded); $filesystem->put($variantPath, (string) $encoded);
// A webp sibling for every variant. The MAN-109 audit measured the
// hero as the LCP element on every mobile page, at 143 KiB for a
// 960 px jpeg — webp carries the same picture for roughly a third of
// that. `x-media.image` offers these through a <source> so a browser
// that cannot decode webp still gets the original format.
if ($extension === 'webp') {
continue;
}
$webp = $manager->read($contents);
if ($webp->width() > $width) {
$webp->scale(width: $width);
}
$filesystem->put(
self::webpVariantPath($path, $width),
(string) $webp->toWebp(quality: 80)
);
} }
} }
@@ -55,13 +83,13 @@ final class ResponsiveImage
$filesystem = self::filesystem($disk); $filesystem = self::filesystem($disk);
foreach (self::WIDTHS as $width) { foreach (self::WIDTHS as $width) {
$variantPath = self::variantPath($path, $width); foreach ([self::variantPath($path, $width), self::webpVariantPath($path, $width)] as $variantPath) {
if ($filesystem->exists($variantPath)) { if ($filesystem->exists($variantPath)) {
$filesystem->delete($variantPath); $filesystem->delete($variantPath);
} }
} }
} }
}
public static function delete(string $path, ?string $disk = null): void public static function delete(string $path, ?string $disk = null): void
{ {
@@ -93,6 +121,17 @@ final class ResponsiveImage
return $directory === '' ? $variantName : $directory.'/'.$variantName; return $directory === '' ? $variantName : $directory.'/'.$variantName;
} }
/**
* The webp sibling of a variant, named by appending rather than replacing the
* extension. Uploads are stored under a UUID so a collision is already
* unlikely, but `photo-480.jpg.webp` cannot collide with the webp variant of
* a `photo.png` the way `photo-480.webp` would.
*/
public static function webpVariantPath(string $path, int $width): string
{
return self::variantPath($path, $width).'.webp';
}
/** /**
* @return list<array{path: string, width: int}> * @return list<array{path: string, width: int}>
*/ */
@@ -115,6 +154,32 @@ final class ResponsiveImage
return $variants; return $variants;
} }
/**
* The webp variants that exist for a path. Empty when the media predates
* `media:generate-variants` running with webp support, which is why
* `x-media.image` treats the <source> as optional rather than assuming it.
*
* @return list<array{path: string, width: int}>
*/
public static function availableWebpVariants(string $path, ?string $disk = null): array
{
$filesystem = self::filesystem($disk);
$variants = [];
foreach (self::WIDTHS as $width) {
$variantPath = self::webpVariantPath($path, $width);
if ($filesystem->exists($variantPath)) {
$variants[] = [
'path' => $variantPath,
'width' => $width,
];
}
}
return $variants;
}
/** /**
* @return array{width: int, height: int}|null * @return array{width: int, height: int}|null
*/ */

View File

@@ -1,29 +1,29 @@
# Lighthouse — local-pos # Lighthouse — local-sizes
- Origem: `http://127.0.0.1:8000` - Origem: `http://127.0.0.1:8000`
- Lighthouse: 12.8.2 - Lighthouse: 12.8.2
- Navegador: `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/151.0.0.0 Safari/537.36` - Navegador: `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/151.0.0.0 Safari/537.36`
- Coletado em: 2026-08-10T16:20:48.372Z - Coletado em: 2026-08-10T17:38:33.741Z
- Execuções por página/preset: 3 (reportada a de LCP mediano) - Execuções por página/preset: 3 (reportada a de LCP mediano)
- Commit: `2e43fde` - Commit: `65919d4`
- Seeder: `ContentSeeder` - Seeder: `ContentSeeder`
Metas SPEC §6.6: LCP ≤ 2,5 s · CLS ≤ 0,1 · INP ≤ 200 ms · zero erro de console. Metas SPEC §6.6: LCP ≤ 2,5 s · CLS ≤ 0,1 · INP ≤ 200 ms · zero erro de console.
| página | preset | perf | a11y | BP | SEO | LCP | FCP | CLS | TBT | TTFB servidor | LCP minmax | | página | preset | perf | a11y | BP | SEO | LCP | FCP | CLS | TBT | TTFB servidor | LCP minmax |
|---|---|---|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|---|---|---|
| contato | desktop | 100 | 100 | 100 | 100 | 0.36 s | 0.25 s | 0.000 | 0 ms | 20 ms | 0.36 s 0.37 s | | contato | desktop | 100 | 100 | 100 | 100 | 0.36 s | 0.25 s | 0.000 | 0 ms | 91 ms | 0.36 s 0.37 s |
| contato | mobile | 100 | 100 | 100 | 100 | 1.51 s | 0.92 s | 0.000 | 0 ms | 46 ms | 1.50 s 1.51 s | | contato | mobile | 100 | 100 | 100 | 100 | 1.50 s | 0.92 s | 0.000 | 0 ms | 66 ms | 1.50 s 1.51 s |
| home | desktop | 100 | 100 | 100 | 100 | 0.67 s | 0.25 s | 0.000 | 0 ms | 87 ms | 0.67 s 0.67 s | | home | desktop | 100 | 100 | 100 | 100 | 0.53 s | 0.25 s | 0.000 | 0 ms | 110 ms | 0.53 s 0.54 s |
| home | mobile | 92 | 100 | 100 | 100 | 3.39 s | 0.92 s | 0.000 | 0 ms | 88 ms | 3.38 s 3.46 s | | home | mobile | 98 | 100 | 100 | 100 | 2.49 s | 0.92 s | 0.000 | 0 ms | 77 ms | 2.48 s 2.57 s |
| portfolio-detalhe | desktop | 100 | 100 | 100 | 100 | 0.79 s | 0.25 s | 0.000 | 0 ms | 74 ms | 0.79 s 0.79 s | | portfolio-detalhe | desktop | 100 | 100 | 100 | 100 | 0.65 s | 0.25 s | 0.000 | 0 ms | 81 ms | 0.65 s 0.66 s |
| portfolio-detalhe | mobile | 95 | 100 | 100 | 100 | 3.01 s | 0.91 s | 0.000 | 0 ms | 89 ms | 2.93 s 3.01 s | | portfolio-detalhe | mobile | 99 | 100 | 100 | 100 | 2.18 s | 0.90 s | 0.000 | 0 ms | 77 ms | 2.18 s 2.18 s |
| portfolio | desktop | 100 | 100 | 100 | 100 | 0.65 s | 0.25 s | 0.000 | 0 ms | 73 ms | 0.37 s 0.65 s | | portfolio | desktop | 100 | 100 | 100 | 100 | 0.36 s | 0.25 s | 0.000 | 0 ms | 72 ms | 0.36 s 0.49 s |
| portfolio | mobile | 100 | 100 | 100 | 100 | 1.58 s | 0.91 s | 0.000 | 0 ms | 69 ms | 1.58 s 3.24 s | | portfolio | mobile | 100 | 100 | 100 | 100 | 1.58 s | 0.91 s | 0.000 | 0 ms | 71 ms | 1.58 s 1.58 s |
| servicos | desktop | 100 | 100 | 100 | 100 | 0.36 s | 0.25 s | 0.000 | 0 ms | 121 ms | 0.36 s 0.59 s | | servicos | desktop | 100 | 100 | 100 | 100 | 0.36 s | 0.24 s | 0.000 | 0 ms | 59 ms | 0.36 s 0.38 s |
| servicos | mobile | 97 | 100 | 100 | 100 | 2.63 s | 0.90 s | 0.000 | 0 ms | 73 ms | 1.58 s 2.63 s | | servicos | mobile | 99 | 100 | 100 | 100 | 2.03 s | 0.90 s | 0.000 | 0 ms | 62 ms | 1.58 s 2.03 s |
| sobre | desktop | 100 | 100 | 100 | 100 | 0.37 s | 0.25 s | 0.000 | 0 ms | 97 ms | 0.36 s 0.45 s | | sobre | desktop | 100 | 100 | 100 | 100 | 0.36 s | 0.25 s | 0.000 | 0 ms | 51 ms | 0.36 s 0.37 s |
| sobre | mobile | 100 | 100 | 100 | 100 | 1.58 s | 0.91 s | 0.000 | 0 ms | 48 ms | 1.58 s 1.96 s | | sobre | mobile | 100 | 100 | 100 | 100 | 1.58 s | 0.91 s | 0.000 | 0 ms | 93 ms | 1.58 s 1.58 s |
## Decomposição do LCP ## Decomposição do LCP
@@ -42,13 +42,13 @@ Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.03 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.03 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.03 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.03 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.10 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.03 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.10 s |
| `/contato` | Document | 5 KiB | 0.02 s | | `/contato` | Document | 5 KiB | 0.09 s |
| `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.03 s | | `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.10 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
@@ -72,13 +72,13 @@ Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.06 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.05 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.07 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.05 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.07 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.06 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.06 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.08 s |
| `/contato` | Document | 5 KiB | 0.05 s | | `/contato` | Document | 5 KiB | 0.07 s |
| `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.06 s | | `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.08 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
@@ -89,63 +89,63 @@ Requests concluídas até o LCP, mais pesadas primeiro:
### home — desktop ### home — desktop
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/og/og-default-960.jpg" srcset="/storage/content/og/og-default-480.jpg 480w, /storage/content/og/og-defaul…" size` Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/og/og-default-720.jpg.webp" srcset="/storage/content/og/og-default-480.jpg 480w, /storage/content/og/og-defaul…"`
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.12 s | | TTFB | 0.12 s |
| Load Delay | 0.33 s | | Load Delay | 0.25 s |
| Load Time | 0.03 s | | Load Time | 0.03 s |
| Render Delay | 0.19 s | | Render Delay | 0.12 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/og/og-default-960.jpg` | Image | 143 KiB | 0.10 s | | `/storage/content/og/og-default-720.jpg.webp` | Image | 74 KiB | 0.13 s |
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.11 s | | `/storage/content/portfolio/casamento-ana-lucas-cover-720.jpg.webp` | Image | 67 KiB | 0.14 s |
| `/storage/content/portfolio/lancamento-verano-cover-960.jpg` | Image | 106 KiB | 0.11 s | | `/storage/content/portfolio/lancamento-verano-cover-720.jpg.webp` | Image | 45 KiB | 0.14 s |
| `/storage/content/portfolio/mini-wedding-marina-cover-960.jpg` | Image | 100 KiB | 0.11 s | | `/storage/content/portfolio/mini-wedding-marina-cover-720.jpg.webp` | Image | 43 KiB | 0.14 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.10 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.12 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.09 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.12 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.12 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.10 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.12 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
| Improve image delivery | 0.15 s | — | | Improve image delivery | 0.10 s | — |
| Use efficient cache lifetimes | 0.00 s | — | | Use efficient cache lifetimes | 0.00 s | — |
| Network dependency tree | 0.00 s | — | | Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — | | Render blocking requests | 0.00 s | — |
### home — mobile ### home — mobile
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/og/og-default-960.jpg" srcset="/storage/content/og/og-default-480.jpg 480w, /storage/content/og/og-defaul…" size` Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/og/og-default-720.jpg.webp" srcset="/storage/content/og/og-default-480.jpg 480w, /storage/content/og/og-defaul…"`
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.45 s | | TTFB | 0.45 s |
| Load Delay | 1.88 s | | Load Delay | 1.24 s |
| Load Time | 0.17 s | | Load Time | 0.10 s |
| Render Delay | 0.88 s | | Render Delay | 0.69 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/og/og-default-960.jpg` | Image | 143 KiB | 0.10 s | | `/storage/content/og/og-default-720.jpg.webp` | Image | 74 KiB | 0.09 s |
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.12 s | | `/storage/content/portfolio/casamento-ana-lucas-cover-720.jpg.webp` | Image | 67 KiB | 0.10 s |
| `/storage/content/portfolio/lancamento-verano-cover-960.jpg` | Image | 106 KiB | 0.12 s | | `/storage/content/portfolio/lancamento-verano-cover-720.jpg.webp` | Image | 45 KiB | 0.10 s |
| `/storage/content/portfolio/mini-wedding-marina-cover-960.jpg` | Image | 100 KiB | 0.12 s | | `/storage/content/portfolio/mini-wedding-marina-cover-720.jpg.webp` | Image | 43 KiB | 0.10 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.10 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.09 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.10 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.09 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.09 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.10 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.09 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
| Improve image delivery | 0.90 s | — | | Improve image delivery | 0.45 s | — |
| Use efficient cache lifetimes | 0.15 s | — | | Use efficient cache lifetimes | 0.00 s | — |
| Network dependency tree | 0.00 s | — | | Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — | | Render blocking requests | 0.00 s | — |
@@ -156,22 +156,22 @@ Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/portfolio/casa
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.12 s | | TTFB | 0.12 s |
| Load Delay | 0.43 s | | Load Delay | 0.28 s |
| Load Time | 0.03 s | | Load Time | 0.03 s |
| Render Delay | 0.21 s | | Render Delay | 0.22 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/portfolio/casamento-ana-lucas-cover-1440.jpg` | Image | 287 KiB | 0.08 s | | `/storage/content/portfolio/casamento-ana-lucas-cover-1440.jpg.webp` | Image | 210 KiB | 0.09 s |
| `/storage/content/portfolio/casamento-ana-lucas-gallery-1-960.jpg` | Image | 209 KiB | 0.09 s | | `/storage/content/portfolio/casamento-ana-lucas-gallery-1-720.jpg.webp` | Image | 105 KiB | 0.10 s |
| `/storage/content/portfolio/casamento-ana-lucas-gallery-2-960.jpg` | Image | 172 KiB | 0.09 s | | `/storage/content/portfolio/casamento-ana-lucas-gallery-2-720.jpg.webp` | Image | 88 KiB | 0.10 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.09 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.09 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.09 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.09 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.08 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.09 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
@@ -188,27 +188,27 @@ Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/portfolio/casa
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.45 s | | TTFB | 0.45 s |
| Load Delay | 1.63 s | | Load Delay | 1.11 s |
| Load Time | 0.06 s | | Load Time | 0.04 s |
| Render Delay | 0.87 s | | Render Delay | 0.57 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/portfolio/casamento-ana-lucas-gallery-1-960.jpg` | Image | 209 KiB | 0.11 s | | `/storage/content/portfolio/casamento-ana-lucas-gallery-1-720.jpg.webp` | Image | 105 KiB | 0.09 s |
| `/storage/content/portfolio/casamento-ana-lucas-gallery-2-960.jpg` | Image | 172 KiB | 0.11 s | | `/storage/content/portfolio/casamento-ana-lucas-gallery-2-720.jpg.webp` | Image | 88 KiB | 0.09 s |
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.10 s | | `/storage/content/portfolio/casamento-ana-lucas-cover-720.jpg.webp` | Image | 67 KiB | 0.09 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.10 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.10 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.10 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.09 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.10 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.09 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
| Improve image delivery | 0.90 s | — | | Improve image delivery | 0.45 s | — |
| Use efficient cache lifetimes | 0.15 s | — | | Use efficient cache lifetimes | 0.00 s | — |
| LCP request discovery | 0.00 s | — | | LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — | | Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — | | Render blocking requests | 0.00 s | — |
@@ -220,27 +220,27 @@ Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/portfolio/casa
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.12 s | | TTFB | 0.12 s |
| Load Delay | 0.28 s | | Load Delay | 0.14 s |
| Load Time | 0.05 s | | Load Time | 0.01 s |
| Render Delay | 0.19 s | | Render Delay | 0.09 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.10 s | | `/storage/content/portfolio/casamento-ana-lucas-cover-720.jpg.webp` | Image | 67 KiB | 0.09 s |
| `/storage/content/portfolio/lancamento-verano-cover-960.jpg` | Image | 106 KiB | 0.10 s | | `/storage/content/portfolio/lancamento-verano-cover-720.jpg.webp` | Image | 45 KiB | 0.09 s |
| `/storage/content/portfolio/mini-wedding-marina-cover-960.jpg` | Image | 100 KiB | 0.10 s | | `/storage/content/portfolio/mini-wedding-marina-cover-720.jpg.webp` | Image | 43 KiB | 0.09 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.08 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.08 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
| Improve image delivery | 0.20 s | — |
| Use efficient cache lifetimes | 0.00 s | — | | Use efficient cache lifetimes | 0.00 s | — |
| Improve image delivery | 0.00 s | — |
| LCP request discovery | 0.00 s | — | | LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — | | Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — | | Render blocking requests | 0.00 s | — |
@@ -252,22 +252,22 @@ Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/portfolio/casa
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.45 s | | TTFB | 0.45 s |
| Load Delay | 0.64 s | | Load Delay | 0.66 s |
| Load Time | 0.03 s | | Load Time | 0.04 s |
| Render Delay | 0.46 s | | Render Delay | 0.42 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.09 s | | `/storage/content/portfolio/casamento-ana-lucas-cover-720.jpg.webp` | Image | 67 KiB | 0.09 s |
| `/storage/content/portfolio/lancamento-verano-cover-960.jpg` | Image | 106 KiB | 0.09 s | | `/storage/content/portfolio/lancamento-verano-cover-720.jpg.webp` | Image | 45 KiB | 0.09 s |
| `/storage/content/portfolio/mini-wedding-marina-cover-960.jpg` | Image | 100 KiB | 0.09 s | | `/storage/content/portfolio/mini-wedding-marina-cover-720.jpg.webp` | Image | 43 KiB | 0.09 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.08 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.08 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
@@ -279,27 +279,27 @@ Requests concluídas até o LCP, mais pesadas primeiro:
### servicos — desktop ### servicos — desktop
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/services/casamentos-960.jpg" srcset="/storage/content/services/casamentos-480.jpg 480w, /storage/content/servic…` Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/services/casamentos-720.jpg.webp" srcset="/storage/content/services/casamentos-480.jpg 480w, /storage/content/se`
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.12 s | | TTFB | 0.12 s |
| Load Delay | 0.18 s | | Load Delay | 0.14 s |
| Load Time | 0.01 s | | Load Time | 0.01 s |
| Render Delay | 0.06 s | | Render Delay | 0.10 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/services/casamentos-960.jpg` | Image | 107 KiB | 0.14 s | | `/storage/content/services/casamentos-720.jpg.webp` | Image | 49 KiB | 0.08 s |
| `/storage/content/services/eventos-corporativos-960.jpg` | Image | 100 KiB | 0.14 s | | `/storage/content/services/eventos-corporativos-720.jpg.webp` | Image | 39 KiB | 0.08 s |
| `/storage/content/services/celebracoes-intimistas-960.jpg` | Image | 83 KiB | 0.14 s | | `/storage/content/services/celebracoes-intimistas-720.jpg.webp` | Image | 28 KiB | 0.08 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.13 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.07 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.13 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.06 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.13 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.06 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.13 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.07 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.13 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.07 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
@@ -311,59 +311,59 @@ Requests concluídas até o LCP, mais pesadas primeiro:
### servicos — mobile ### servicos — mobile
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/services/casamentos-960.jpg" srcset="/storage/content/services/casamentos-480.jpg 480w, /storage/content/servic…` Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/services/casamentos-720.jpg.webp" srcset="/storage/content/services/casamentos-480.jpg 480w, /storage/content/se`
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.45 s | | TTFB | 0.45 s |
| Load Delay | 1.50 s | | Load Delay | 1.08 s |
| Load Time | 0.11 s | | Load Time | 0.07 s |
| Render Delay | 0.56 s | | Render Delay | 0.43 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/services/casamentos-960.jpg` | Image | 107 KiB | 0.09 s | | `/storage/content/services/casamentos-720.jpg.webp` | Image | 49 KiB | 0.08 s |
| `/storage/content/services/eventos-corporativos-960.jpg` | Image | 100 KiB | 0.09 s | | `/storage/content/services/eventos-corporativos-720.jpg.webp` | Image | 39 KiB | 0.08 s |
| `/storage/content/services/celebracoes-intimistas-960.jpg` | Image | 83 KiB | 0.09 s | | `/storage/content/services/celebracoes-intimistas-720.jpg.webp` | Image | 28 KiB | 0.08 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.07 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.07 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.07 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.07 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.08 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.07 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
| Improve image delivery | 0.60 s | — | | Improve image delivery | 0.45 s | — |
| Use efficient cache lifetimes | 0.00 s | — | | Use efficient cache lifetimes | 0.15 s | — |
| LCP request discovery | 0.00 s | — | | LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — | | Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — | | Render blocking requests | 0.00 s | — |
### sobre — desktop ### sobre — desktop
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/about/about-image-960.jpg" srcset="/storage/content/about/about-image-480.jpg 480w, /storage/content/about/ab…" ` Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/about/about-image-720.jpg.webp" srcset="/storage/content/about/about-image-480.jpg 480w, /storage/content/about/`
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.13 s | | TTFB | 0.12 s |
| Load Delay | 0.15 s | | Load Delay | 0.11 s |
| Load Time | 0.00 s | | Load Time | 0.01 s |
| Render Delay | 0.09 s | | Render Delay | 0.12 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/about/about-image-960.jpg` | Image | 60 KiB | 0.12 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.06 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.11 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.06 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.10 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.06 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s | | `/storage/content/about/about-image-720.jpg.webp` | Image | 22 KiB | 0.07 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.11 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.06 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.11 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.06 s |
| `/sobre` | Document | 4 KiB | 0.10 s | | `/sobre` | Document | 5 KiB | 0.05 s |
| `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.11 s | | `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.06 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|
@@ -375,27 +375,27 @@ Requests concluídas até o LCP, mais pesadas primeiro:
### sobre — mobile ### sobre — mobile
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/about/about-image-960.jpg" srcset="/storage/content/about/about-image-480.jpg 480w, /storage/content/about/ab…" ` Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/about/about-image-720.jpg.webp" srcset="/storage/content/about/about-image-480.jpg 480w, /storage/content/about/`
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0.45 s | | TTFB | 0.45 s |
| Load Delay | 0.57 s | | Load Delay | 0.77 s |
| Load Time | 0.10 s | | Load Time | 0.02 s |
| Render Delay | 0.46 s | | Render Delay | 0.33 s |
Requests concluídas até o LCP, mais pesadas primeiro: Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim | | recurso | tipo | transferido | fim |
|---|---|---|---| |---|---|---|---|
| `/storage/content/about/about-image-960.jpg` | Image | 60 KiB | 0.07 s | | `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.06 s | | `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.06 s | | `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.06 s | | `/storage/content/about/about-image-720.jpg.webp` | Image | 22 KiB | 0.11 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.06 s | | `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.10 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.06 s | | `/build/assets/app-8wBXH8kj.css` | Stylesheet | 10 KiB | 0.10 s |
| `/sobre` | Document | 4 KiB | 0.05 s | | `/sobre` | Document | 5 KiB | 0.09 s |
| `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.06 s | | `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.10 s |
| insight com falha | ganho estimado de LCP | bytes | | insight com falha | ganho estimado de LCP | bytes |
|---|---|---| |---|---|---|

View File

@@ -0,0 +1,407 @@
# Lighthouse — local-pos
- Origem: `http://127.0.0.1:8000`
- Lighthouse: 12.8.2
- Navegador: `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/151.0.0.0 Safari/537.36`
- Coletado em: 2026-08-10T16:20:48.372Z
- Execuções por página/preset: 3 (reportada a de LCP mediano)
- Commit: `2e43fde`
- Seeder: `ContentSeeder`
Metas SPEC §6.6: LCP ≤ 2,5 s · CLS ≤ 0,1 · INP ≤ 200 ms · zero erro de console.
| página | preset | perf | a11y | BP | SEO | LCP | FCP | CLS | TBT | TTFB servidor | LCP minmax |
|---|---|---|---|---|---|---|---|---|---|---|---|
| contato | desktop | 100 | 100 | 100 | 100 | 0.36 s | 0.25 s | 0.000 | 0 ms | 20 ms | 0.36 s 0.37 s |
| contato | mobile | 100 | 100 | 100 | 100 | 1.51 s | 0.92 s | 0.000 | 0 ms | 46 ms | 1.50 s 1.51 s |
| home | desktop | 100 | 100 | 100 | 100 | 0.67 s | 0.25 s | 0.000 | 0 ms | 87 ms | 0.67 s 0.67 s |
| home | mobile | 92 | 100 | 100 | 100 | 3.39 s | 0.92 s | 0.000 | 0 ms | 88 ms | 3.38 s 3.46 s |
| portfolio-detalhe | desktop | 100 | 100 | 100 | 100 | 0.79 s | 0.25 s | 0.000 | 0 ms | 74 ms | 0.79 s 0.79 s |
| portfolio-detalhe | mobile | 95 | 100 | 100 | 100 | 3.01 s | 0.91 s | 0.000 | 0 ms | 89 ms | 2.93 s 3.01 s |
| portfolio | desktop | 100 | 100 | 100 | 100 | 0.65 s | 0.25 s | 0.000 | 0 ms | 73 ms | 0.37 s 0.65 s |
| portfolio | mobile | 100 | 100 | 100 | 100 | 1.58 s | 0.91 s | 0.000 | 0 ms | 69 ms | 1.58 s 3.24 s |
| servicos | desktop | 100 | 100 | 100 | 100 | 0.36 s | 0.25 s | 0.000 | 0 ms | 121 ms | 0.36 s 0.59 s |
| servicos | mobile | 97 | 100 | 100 | 100 | 2.63 s | 0.90 s | 0.000 | 0 ms | 73 ms | 1.58 s 2.63 s |
| sobre | desktop | 100 | 100 | 100 | 100 | 0.37 s | 0.25 s | 0.000 | 0 ms | 97 ms | 0.36 s 0.45 s |
| sobre | mobile | 100 | 100 | 100 | 100 | 1.58 s | 0.91 s | 0.000 | 0 ms | 48 ms | 1.58 s 1.96 s |
## Decomposição do LCP
### contato — desktop
Elemento de LCP: `<h1 class="text-headline font-medium tracking-tight text-amare-text">`
| fase | tempo |
|---|---|
| TTFB | 0.12 s |
| Load Delay | 0.00 s |
| Load Time | 0.00 s |
| Render Delay | 0.24 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.03 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.03 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.03 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.03 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.03 s |
| `/contato` | Document | 5 KiB | 0.02 s |
| `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.03 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Use efficient cache lifetimes | 0.00 s | — |
| Improve image delivery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### contato — mobile
Elemento de LCP: `<h1 class="text-headline font-medium tracking-tight text-amare-text">`
| fase | tempo |
|---|---|
| TTFB | 0.45 s |
| Load Delay | 0.00 s |
| Load Time | 0.00 s |
| Render Delay | 1.05 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.06 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.05 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.05 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.06 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.06 s |
| `/contato` | Document | 5 KiB | 0.05 s |
| `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.06 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Use efficient cache lifetimes | 0.00 s | — |
| Improve image delivery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### home — desktop
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/og/og-default-960.jpg" srcset="/storage/content/og/og-default-480.jpg 480w, /storage/content/og/og-defaul…" size`
| fase | tempo |
|---|---|
| TTFB | 0.12 s |
| Load Delay | 0.33 s |
| Load Time | 0.03 s |
| Render Delay | 0.19 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/og/og-default-960.jpg` | Image | 143 KiB | 0.10 s |
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.11 s |
| `/storage/content/portfolio/lancamento-verano-cover-960.jpg` | Image | 106 KiB | 0.11 s |
| `/storage/content/portfolio/mini-wedding-marina-cover-960.jpg` | Image | 100 KiB | 0.11 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.09 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.10 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Improve image delivery | 0.15 s | — |
| Use efficient cache lifetimes | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### home — mobile
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/og/og-default-960.jpg" srcset="/storage/content/og/og-default-480.jpg 480w, /storage/content/og/og-defaul…" size`
| fase | tempo |
|---|---|
| TTFB | 0.45 s |
| Load Delay | 1.88 s |
| Load Time | 0.17 s |
| Render Delay | 0.88 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/og/og-default-960.jpg` | Image | 143 KiB | 0.10 s |
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.12 s |
| `/storage/content/portfolio/lancamento-verano-cover-960.jpg` | Image | 106 KiB | 0.12 s |
| `/storage/content/portfolio/mini-wedding-marina-cover-960.jpg` | Image | 100 KiB | 0.12 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.10 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Improve image delivery | 0.90 s | — |
| Use efficient cache lifetimes | 0.15 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### portfolio-detalhe — desktop
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/portfolio/casamento-ana-lucas-cover-…" srcset="/storage/content/portfolio/casamento-ana-lucas-cover-480.jpg 480w`
| fase | tempo |
|---|---|
| TTFB | 0.12 s |
| Load Delay | 0.43 s |
| Load Time | 0.03 s |
| Render Delay | 0.21 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/portfolio/casamento-ana-lucas-cover-1440.jpg` | Image | 287 KiB | 0.08 s |
| `/storage/content/portfolio/casamento-ana-lucas-gallery-1-960.jpg` | Image | 209 KiB | 0.09 s |
| `/storage/content/portfolio/casamento-ana-lucas-gallery-2-960.jpg` | Image | 172 KiB | 0.09 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.08 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Improve image delivery | 0.10 s | — |
| Use efficient cache lifetimes | 0.00 s | — |
| LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### portfolio-detalhe — mobile
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/portfolio/casamento-ana-lucas-cover-…" srcset="/storage/content/portfolio/casamento-ana-lucas-cover-480.jpg 480w`
| fase | tempo |
|---|---|
| TTFB | 0.45 s |
| Load Delay | 1.63 s |
| Load Time | 0.06 s |
| Render Delay | 0.87 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/portfolio/casamento-ana-lucas-gallery-1-960.jpg` | Image | 209 KiB | 0.11 s |
| `/storage/content/portfolio/casamento-ana-lucas-gallery-2-960.jpg` | Image | 172 KiB | 0.11 s |
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.10 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.10 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.10 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Improve image delivery | 0.90 s | — |
| Use efficient cache lifetimes | 0.15 s | — |
| LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### portfolio — desktop
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/portfolio/casamento-ana-lucas-cover-…" srcset="/storage/content/portfolio/casamento-ana-lucas-cover-480.jpg 480w`
| fase | tempo |
|---|---|
| TTFB | 0.12 s |
| Load Delay | 0.28 s |
| Load Time | 0.05 s |
| Render Delay | 0.19 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.10 s |
| `/storage/content/portfolio/lancamento-verano-cover-960.jpg` | Image | 106 KiB | 0.10 s |
| `/storage/content/portfolio/mini-wedding-marina-cover-960.jpg` | Image | 100 KiB | 0.10 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.08 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Improve image delivery | 0.20 s | — |
| Use efficient cache lifetimes | 0.00 s | — |
| LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### portfolio — mobile
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/portfolio/casamento-ana-lucas-cover-…" srcset="/storage/content/portfolio/casamento-ana-lucas-cover-480.jpg 480w`
| fase | tempo |
|---|---|
| TTFB | 0.45 s |
| Load Delay | 0.64 s |
| Load Time | 0.03 s |
| Render Delay | 0.46 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/portfolio/casamento-ana-lucas-cover-960.jpg` | Image | 142 KiB | 0.09 s |
| `/storage/content/portfolio/lancamento-verano-cover-960.jpg` | Image | 106 KiB | 0.09 s |
| `/storage/content/portfolio/mini-wedding-marina-cover-960.jpg` | Image | 100 KiB | 0.09 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.08 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Use efficient cache lifetimes | 0.00 s | — |
| Improve image delivery | 0.00 s | — |
| LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### servicos — desktop
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/services/casamentos-960.jpg" srcset="/storage/content/services/casamentos-480.jpg 480w, /storage/content/servic…`
| fase | tempo |
|---|---|
| TTFB | 0.12 s |
| Load Delay | 0.18 s |
| Load Time | 0.01 s |
| Render Delay | 0.06 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/services/casamentos-960.jpg` | Image | 107 KiB | 0.14 s |
| `/storage/content/services/eventos-corporativos-960.jpg` | Image | 100 KiB | 0.14 s |
| `/storage/content/services/celebracoes-intimistas-960.jpg` | Image | 83 KiB | 0.14 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.13 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.13 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.13 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.13 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.13 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Use efficient cache lifetimes | 0.00 s | — |
| Improve image delivery | 0.00 s | — |
| LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### servicos — mobile
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/services/casamentos-960.jpg" srcset="/storage/content/services/casamentos-480.jpg 480w, /storage/content/servic…`
| fase | tempo |
|---|---|
| TTFB | 0.45 s |
| Load Delay | 1.50 s |
| Load Time | 0.11 s |
| Render Delay | 0.56 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/services/casamentos-960.jpg` | Image | 107 KiB | 0.09 s |
| `/storage/content/services/eventos-corporativos-960.jpg` | Image | 100 KiB | 0.09 s |
| `/storage/content/services/celebracoes-intimistas-960.jpg` | Image | 83 KiB | 0.09 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.08 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.08 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.08 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.08 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Improve image delivery | 0.60 s | — |
| Use efficient cache lifetimes | 0.00 s | — |
| LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### sobre — desktop
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/about/about-image-960.jpg" srcset="/storage/content/about/about-image-480.jpg 480w, /storage/content/about/ab…" `
| fase | tempo |
|---|---|
| TTFB | 0.13 s |
| Load Delay | 0.15 s |
| Load Time | 0.00 s |
| Render Delay | 0.09 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/about/about-image-960.jpg` | Image | 60 KiB | 0.12 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.11 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.10 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.10 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.11 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.11 s |
| `/sobre` | Document | 4 KiB | 0.10 s |
| `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.11 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Use efficient cache lifetimes | 0.00 s | — |
| Improve image delivery | 0.00 s | — |
| LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |
### sobre — mobile
Elemento de LCP: `<img src="http://127.0.0.1:8000/storage/content/about/about-image-960.jpg" srcset="/storage/content/about/about-image-480.jpg 480w, /storage/content/about/ab…" `
| fase | tempo |
|---|---|
| TTFB | 0.45 s |
| Load Delay | 0.57 s |
| Load Time | 0.10 s |
| Render Delay | 0.46 s |
Requests concluídas até o LCP, mais pesadas primeiro:
| recurso | tipo | transferido | fim |
|---|---|---|---|
| `/storage/content/about/about-image-960.jpg` | Image | 60 KiB | 0.07 s |
| `/build/assets/eb-garamond-600-normal-DHwxsLHv.woff2` | Font | 25 KiB | 0.06 s |
| `/build/assets/eb-garamond-500-normal-DehAIUv0.woff2` | Font | 25 KiB | 0.06 s |
| `/build/assets/eb-garamond-400-normal-BCNrxLz_.woff2` | Font | 24 KiB | 0.06 s |
| `/brand/lockup-on-light.webp` | Image | 14 KiB | 0.06 s |
| `/build/assets/app-Cswasu1n.css` | Stylesheet | 10 KiB | 0.06 s |
| `/sobre` | Document | 4 KiB | 0.05 s |
| `/build/assets/app-Cr9w0NCu.js` | Script | 2 KiB | 0.06 s |
| insight com falha | ganho estimado de LCP | bytes |
|---|---|---|
| Use efficient cache lifetimes | 0.00 s | — |
| Improve image delivery | 0.00 s | — |
| LCP request discovery | 0.00 s | — |
| Network dependency tree | 0.00 s | — |
| Render blocking requests | 0.00 s | — |

View File

@@ -31,31 +31,46 @@ Preset mobile padrão do Lighthouse 12.8.2: throttling simulado de 150 ms de RTT
~1,6 Mbps, CPU 4× mais lenta. Chrome estável do sistema (não o Chromium do ~1,6 Mbps, CPU 4× mais lenta. Chrome estável do sistema (não o Chromium do
Playwright), imagem de produção local, seeder `ContentSeeder`. Playwright), imagem de produção local, seeder `ContentSeeder`.
Antes: commit `2e43fde`. Depois: as duas correções descritas abaixo. Antes: commit `2e43fde`. As colunas intermediárias mostram cada correção
isoladamente, medida com uma passada completa antes da seguinte entrar.
| página | preset | LCP antes | LCP depois | Δ | perf antes | perf depois | FCP antes | FCP depois | bytes antes | bytes depois | | página | preset | antes | + fontes e marca | + webp e 720w | + sizes correto | perf antes depois | bytes antes depois |
|---|---|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|
| contato | desktop | 0.53 s | 0.36 s | -0.16 s | 100 | 100 | 0.37 s | 0.25 s | 262 KiB | 106 KiB | | contato | desktop | 0.53 s | 0.36 s | 0.37 s | **0.36 s** | 100 → 100 | 262 106 KiB |
| contato | mobile | 2.55 s | 1.51 s | -1.05 s | 97 | 100 | 1.50 s | 0.92 s | 262 KiB | 106 KiB | | contato | mobile | 2.55 s | 1.51 s | 1.51 s | **1.50 s** | 97 → 100 | 262 106 KiB |
| home | desktop | 0.87 s | 0.67 s | -0.20 s | 99 | 100 | 0.37 s | 0.25 s | 798 KiB | 609 KiB | | home | desktop | 0.87 s | 0.67 s | 0.53 s | **0.53 s** | 99 → 100 | 798 → 347 KiB |
| home | mobile | 4.58 s | 3.39 s | -1.20 s | 83 | 92 | 1.51 s | 0.92 s | 798 KiB | 609 KiB | | home | mobile | 4.58 s | 3.39 s | 2.87 s | **2.49 s** | 83 → 98 | 798 → 347 KiB |
| portfolio | desktop | 0.83 s | 0.65 s | -0.18 s | 99 | 100 | 0.37 s | 0.25 s | 609 KiB | 452 KiB | | portfolio | desktop | 0.83 s | 0.65 s | 0.37 s | **0.36 s** | 99 → 100 | 609 → 259 KiB |
| portfolio | mobile | 3.98 s | 1.58 s | -2.40 s | 87 | 100 | 1.51 s | 0.91 s | 609 KiB | 452 KiB | | portfolio | mobile | 3.98 s | 1.58 s | 2.64 s | **1.58 s** | 87 → 100 | 609 → 259 KiB |
| portfolio-detalhe | desktop | 0.97 s | 0.79 s | -0.18 s | 99 | 100 | 0.37 s | 0.25 s | 930 KiB | 773 KiB | | portfolio-detalhe | desktop | 0.97 s | 0.79 s | 0.66 s | **0.65 s** | 99 → 100 | 930 → 508 KiB |
| portfolio-detalhe | mobile | 3.98 s | 3.01 s | -0.98 s | 87 | 95 | 1.51 s | 0.91 s | 785 KiB | 629 KiB | | portfolio-detalhe | mobile | 3.98 s | 3.01 s | 2.71 s | **2.18 s** | 87 → 99 | 785 → 365 KiB |
| servicos | desktop | 0.77 s | 0.36 s | -0.40 s | 100 | 100 | 0.37 s | 0.25 s | 552 KiB | 395 KiB | | servicos | desktop | 0.77 s | 0.36 s | 0.37 s | **0.36 s** | 100 → 100 | 552 → 222 KiB |
| servicos | mobile | 3.68 s | 2.63 s | -1.05 s | 89 | 97 | 1.51 s | 0.90 s | 552 KiB | 395 KiB | | servicos | mobile | 3.68 s | 2.63 s | 1.58 s | **2.03 s** | 89 → 99 | 552 → 222 KiB |
| sobre | desktop | 0.61 s | 0.37 s | -0.23 s | 100 | 100 | 0.37 s | 0.25 s | 322 KiB | 165 KiB | | sobre | desktop | 0.61 s | 0.37 s | 0.37 s | **0.36 s** | 100 → 100 | 322 → 127 KiB |
| sobre | mobile | 3.01 s | 1.58 s | -1.43 s | 94 | 100 | 1.51 s | 0.91 s | 322 KiB | 165 KiB | | sobre | mobile | 3.01 s | 1.58 s | 1.60 s | **1.58 s** | 94 → 100 | 322 → 127 KiB |
Acessibilidade, boas práticas e SEO marcam 100 em todas as páginas nos dois FCP cai de 1,51 s para 0,91 s em todas as páginas no mobile. Acessibilidade,
presets, antes e depois. CLS é 0,000 e TBT é 0 ms em todas — as metas de §6.6 boas práticas e SEO marcam 100 em todas as páginas nos dois presets, antes e
para essas três métricas já passavam e continuam passando. depois. CLS é 0,000 e TBT é 0 ms em todas — as metas de §6.6 para essas três
métricas já passavam e continuam passando.
**Contra a meta de LCP ≤ 2,5 s da §6.6:** desktop passa com folga em todas as **Contra a meta de LCP ≤ 2,5 s da §6.6: todas as páginas passam nos dois
páginas (máximo 0,79 s). No mobile, `contato`, `portfolio` e `sobre` entraram na presets.** Desktop com folga (máximo 0,65 s). No mobile o pior caso é a home a
meta; `servicos` (2,63 s), `portfolio-detalhe` (3,01 s) e `home` (3,39 s) 2,49 s, ou seja **em cima da linha** — a pior das três execuções dela deu 2,57 s.
continuam fora. Tratar a home como aprovada por margem, não com folga.
Duas colunas intermediárias merecem leitura cuidadosa em vez de conclusão:
- `portfolio` mobile aparece pior na coluna do webp (2,64 s) do que na anterior
(1,58 s). É variância, não regressão: as três execuções daquela passada foram
1,59 / 2,64 / 2,78 s. A página é a mais instável do conjunto e a mediana pulou
de ponta. Na passada final as três deram 1,58 s.
- `servicos` mobile sobe de 1,58 s para 2,03 s da terceira para a quarta coluna,
pelo mesmo motivo (1,58 / 1,58 / 2,03).
É exatamente por isso que o script roda três vezes e reporta a mediana; ainda
assim, diferenças abaixo de meio segundo entre passadas não devem ser lidas como
efeito de uma correção.
Ressalva: a medição é local, então latência de origem e TLS não entram, e o Ressalva: a medição é local, então latência de origem e TLS não entram, e o
throttling de rede é simulado. Trate o LCP como piso, não como valor de campo. throttling de rede é simulado. Trate o LCP como piso, não como valor de campo.
@@ -90,27 +105,61 @@ Isto absorve MAN-122: os 16 baselines visuais foram regenerados no runner Linux
(`scripts/test/visual-update-ci.sh`), e o diff é imperceptível a 2× de zoom — (`scripts/test/visual-update-ci.sh`), e o diff é imperceptível a 2× de zoom —
mesma forma, mesma cor, só menos bytes. mesma forma, mesma cor, só menos bytes.
## O que ainda separa a home da meta Os arquivos versionados aqui são: `2026-08-10-local-antes.md` (commit `2e43fde`),
`2026-08-10-local-etapa-fontes-e-marca.md` (passada intermediária) e
`2026-08-10-local-depois.md` (estado final).
`Improve image delivery` estima 0,90 s de ganho de LCP restante e aponta para as **Variantes WebP nas imagens de conteúdo.** Com fontes e marca resolvidas, o
imagens de conteúdo, não para mais nada do layout. Depois das duas correções, a elemento de LCP de toda página no mobile era a imagem do hero, e o Load Delay de
decomposição do LCP da home no mobile é: 1,88 s era contenção de banda pura: 143 KiB de JPEG q82 a 960 px, com as três
capas do portfólio somando outros 348 KiB. `ResponsiveImage::generate()` passa a
escrever uma variante `.webp` ao lado de cada variante no formato original, e
`x-media.image` a oferece num `<source type="image/webp">`. O `<img>` continua
apontando para o formato original, então nada quebra em quem não decodifica webp,
e mídia antiga sem irmãos webp renderiza `<img>` puro como antes.
**`sizes` que descreve a realidade.** Nenhuma imagem do site ocupa a viewport
inteira: todas ficam dentro de `container-amare`, que reserva 1,5rem de padding
de cada lado. Declarar `100vw` fazia uma viewport de 412 px em DPR 1,75 pedir
721 px e pular para a variante de 960 para desenhar uma caixa de 637 px — errar
por um pixel custava um terço a mais de bytes. Com `calc(100vw - 3rem)` a home
passa a usar a variante de 720 (**74 KiB**, contra 143 KiB no início).
Foi também por isso que 720 entrou em `ResponsiveImage::WIDTHS`: sem ela o salto
de 480 para 960 é grande demais para a viewport mobile mais comum.
Decomposição final do LCP da home no mobile:
| fase | tempo | | fase | tempo |
|---|---| |---|---|
| TTFB | 0,45 s | | TTFB | 0,45 s |
| Load Delay | 1,88 s | | Load Delay | 1,24 s |
| Load Time | 0,17 s | | Load Time | 0,10 s |
| Render Delay | 0,88 s | | Render Delay | 0,69 s |
O elemento de LCP é a imagem do hero (`og-default-960.jpg`, **143 KiB** em JPEG ## Se a home precisar de mais folga
q82). O Load Delay de 1,88 s é contenção de banda: as três capas do portfólio
somam 348 KiB e, embora sejam `loading="lazy"` e prioridade `Low`, o total da
página ainda é 609 KiB.
O próximo lever, portanto, é o formato das imagens de conteúdo — variantes WebP O Load Delay de 1,24 s ainda é contenção: as três capas do portfólio somam
em `App\Support\ResponsiveImage` com `<picture>` em `x-media.image`. Não entrou 155 KiB e, embora sejam `loading="lazy"` e prioridade `Low`, o navegador as busca
nesta entrega por escopo: mexe no pipeline de mídia que o CMS usa para upload. porque entram no limiar de lazy loading da viewport emulada. Os levers restantes,
em ordem de custo:
1. Baixar a qualidade webp de 80 para 75 (medido: 109 → 92 KiB na imagem do
hero a 960 px). Barato, mas mexe na qualidade de imagem de uma marca cujo
posicionamento é acabamento editorial — decisão de produto, não de engenharia.
2. Reduzir o Render Delay de 0,69 s, que agora é a segunda maior fatia e é
trabalho de main thread, não de rede.
3. FrankenPHP worker mode para o TTFB de 0,45 s. Tem gatilho objetivo em
SPEC §22 e não deve ser puxado antes dele.
## Cobertura que este trabalho não tem
Os testes de regressão visual nunca exercitam `srcset` nem `<picture>`: nem
`ContentSeeder` nem `VisualContentSeeder` geram variantes, e `media:generate-variants`
não roda no runner visual. Naquele ambiente `availableVariants()` volta vazio e o
componente renderiza `<img>` puro — foi por isso que os 16 baselines não mudaram
com a introdução do `<picture>`. A cobertura do caminho com variantes fica nos
testes de feature (`MediaImageComponentTest`), não nos baselines.
## Staging ## Staging

View File

@@ -60,7 +60,7 @@
:alt="$settings->default_og_image_alt ?: $settings->brand_name" :alt="$settings->default_og_image_alt ?: $settings->brand_name"
loading="eager" loading="eager"
fetchpriority="high" fetchpriority="high"
sizes="(max-width: 768px) 100vw, 40vw" sizes="(max-width: 768px) calc(100vw - 3rem), 40vw"
class="img-editorial h-full w-full object-cover" class="img-editorial h-full w-full object-cover"
/> />
</div> </div>

View File

@@ -20,7 +20,7 @@
<x-media.image <x-media.image
:path="$case->cover_image_path" :path="$case->cover_image_path"
:alt="$case->cover_image_alt ?: $case->title" :alt="$case->cover_image_alt ?: $case->title"
sizes="(max-width: 768px) 100vw, 50vw" sizes="(max-width: 768px) calc(100vw - 3rem), 50vw"
class="img-editorial aspect-[4/3] w-full object-cover" class="img-editorial aspect-[4/3] w-full object-cover"
/> />
@endif @endif

View File

@@ -1,7 +1,11 @@
@props([ @props([
'path', 'path',
'alt', 'alt',
'sizes' => '(max-width: 768px) 100vw, 960px', // No image on the site spans the full viewport: every one of them sits inside
// `container-amare`, which reserves 1.5rem of padding on each side. Claiming
// 100vw made a 412 px viewport at DPR 1.75 ask for 721 px and jump to the
// 960 variant for a box it draws at 637 px.
'sizes' => '(max-width: 768px) calc(100vw - 3rem), 960px',
'loading' => 'lazy', 'loading' => 'lazy',
'fetchpriority' => null, 'fetchpriority' => null,
'width' => null, 'width' => null,
@@ -18,15 +22,25 @@
$diskName = $disk ?? PublicImageUploadRules::disk(); $diskName = $disk ?? PublicImageUploadRules::disk();
$filesystem = Storage::disk($diskName); $filesystem = Storage::disk($diskName);
$src = $filesystem->url($path); $src = $filesystem->url($path);
$variants = ResponsiveImage::availableVariants($path, $diskName);
$srcset = collect($variants) $toSrcset = fn (array $variants): string => collect($variants)
->map(fn (array $variant): string => $filesystem->url($variant['path']).' '.$variant['width'].'w') ->map(fn (array $variant): string => $filesystem->url($variant['path']).' '.$variant['width'].'w')
->implode(', '); ->implode(', ');
$variants = ResponsiveImage::availableVariants($path, $diskName);
$srcset = $toSrcset($variants);
if ($srcset === '' && $filesystem->exists($path)) { if ($srcset === '' && $filesystem->exists($path)) {
$srcset = null; $srcset = null;
} }
// Offered ahead of the original format because webp carries the same picture
// for roughly a third of the bytes, and the MAN-109 audit found the hero
// image to be the LCP element on every page at mobile widths. Media uploaded
// before `media:generate-variants` learned to emit webp has no siblings, so
// the <source> is skipped rather than pointed at nothing.
$webpSrcset = $toSrcset(ResponsiveImage::availableWebpVariants($path, $diskName));
$dimensions = ($width === null || $height === null) $dimensions = ($width === null || $height === null)
? ResponsiveImage::dimensions($path, $diskName) ? ResponsiveImage::dimensions($path, $diskName)
: null; : null;
@@ -36,6 +50,13 @@
$loadingValue = $loading; $loadingValue = $loading;
@endphp @endphp
{{-- `display: contents` keeps <picture> out of the layout: the callers style the
<img> with classes like `h-full w-full object-cover` that resolve against the
grid or flex parent, and an inline wrapper would break that. --}}
@if ($webpSrcset !== '')
<picture class="contents">
<source type="image/webp" srcset="{{ $webpSrcset }}" sizes="{{ $sizes }}">
@endif
<img <img
src="{{ $src }}" src="{{ $src }}"
@if ($srcset) srcset="{{ $srcset }}" sizes="{{ $sizes }}" @endif @if ($srcset) srcset="{{ $srcset }}" sizes="{{ $sizes }}" @endif
@@ -47,3 +68,6 @@
@if ($class) class="{{ $class }}" @endif @if ($class) class="{{ $class }}" @endif
{{ $attributes->except(['path', 'alt', 'sizes', 'loading', 'width', 'height', 'disk', 'class']) }} {{ $attributes->except(['path', 'alt', 'sizes', 'loading', 'width', 'height', 'disk', 'class']) }}
> >
@if ($webpSrcset !== '')
</picture>
@endif

View File

@@ -25,7 +25,7 @@
:path="$siteSettings->about_image_path" :path="$siteSettings->about_image_path"
:alt="$siteSettings->about_image_alt ?: $siteSettings->brand_name" :alt="$siteSettings->about_image_alt ?: $siteSettings->brand_name"
loading="lazy" loading="lazy"
sizes="(max-width: 768px) 100vw, 50vw" sizes="(max-width: 768px) calc(100vw - 3rem), 50vw"
class="img-editorial aspect-[4/3] w-full object-cover" class="img-editorial aspect-[4/3] w-full object-cover"
/> />
</div> </div>

View File

@@ -20,7 +20,7 @@
<x-media.image <x-media.image
:path="$case->cover_image_path" :path="$case->cover_image_path"
:alt="$case->cover_image_alt ?: $case->title" :alt="$case->cover_image_alt ?: $case->title"
sizes="(max-width: 768px) 100vw, 50vw" sizes="(max-width: 768px) calc(100vw - 3rem), 50vw"
class="img-editorial aspect-[4/3] w-full object-cover transition-transform duration-(--amare-duration-slow) ease-(--amare-ease-standard) motion-safe:hover:scale-[1.02]" class="img-editorial aspect-[4/3] w-full object-cover transition-transform duration-(--amare-duration-slow) ease-(--amare-ease-standard) motion-safe:hover:scale-[1.02]"
/> />
</a> </a>

View File

@@ -22,7 +22,7 @@
:path="$case->cover_image_path" :path="$case->cover_image_path"
:alt="$case->cover_image_alt ?: $case->title" :alt="$case->cover_image_alt ?: $case->title"
loading="eager" loading="eager"
sizes="(max-width: 1024px) 100vw, 1120px" sizes="(max-width: 1024px) calc(100vw - 3rem), 1120px"
class="img-editorial aspect-[16/9] w-full object-cover" class="img-editorial aspect-[16/9] w-full object-cover"
data-motion-beat="media" data-motion-beat="media"
/> />
@@ -59,7 +59,7 @@
<x-media.image <x-media.image
:path="$image->path" :path="$image->path"
:alt="$image->alt_text" :alt="$image->alt_text"
sizes="(max-width: 768px) 100vw, 50vw" sizes="(max-width: 768px) calc(100vw - 3rem), 50vw"
class="img-editorial aspect-[4/3] w-full object-cover" class="img-editorial aspect-[4/3] w-full object-cover"
/> />
@if (filled($image->caption)) @if (filled($image->caption))

View File

@@ -25,7 +25,7 @@
<x-media.image <x-media.image
:path="$service->cover_image_path" :path="$service->cover_image_path"
:alt="$service->cover_image_alt ?: $service->title" :alt="$service->cover_image_alt ?: $service->title"
sizes="(max-width: 768px) 100vw, 40vw" sizes="(max-width: 768px) calc(100vw - 3rem), 40vw"
class="img-editorial aspect-[16/10] w-full object-cover" class="img-editorial aspect-[16/10] w-full object-cover"
/> />
@endif @endif

View File

@@ -60,5 +60,55 @@ class MediaImageComponentTest extends TestCase
$this->assertStringContainsString('alt="Legado"', $html); $this->assertStringContainsString('alt="Legado"', $html);
$this->assertStringContainsString('loading="lazy"', $html); $this->assertStringContainsString('loading="lazy"', $html);
$this->assertStringNotContainsString('srcset=', $html); $this->assertStringNotContainsString('srcset=', $html);
$this->assertStringNotContainsString('<picture', $html);
}
public function test_webp_variants_are_offered_ahead_of_the_original_format(): void
{
Storage::fake('public');
$manager = new ImageManager(new Driver);
Storage::disk('public')->put('content/hero.jpg', (string) $manager->create(1600, 900)->toJpeg());
ResponsiveImage::generate('content/hero.jpg', 'public');
$html = Blade::render(
'<x-media.image path="content/hero.jpg" alt="Hero" loading="eager" sizes="100vw" class="h-full w-full" />'
);
$this->assertStringContainsString('<source type="image/webp"', $html);
$this->assertStringContainsString('content/hero-720.jpg.webp 720w', $html);
// `display: contents` keeps the wrapper out of the layout — callers style
// the img against the grid or flex parent, and an inline <picture> would
// break `h-full`.
$this->assertStringContainsString('<picture class="contents">', $html);
// The source is a preference, not a replacement: the img keeps the
// original format for anything that cannot decode webp.
$this->assertStringContainsString('src="/storage/content/hero.jpg"', $html);
$this->assertStringContainsString('content/hero-720.jpg 720w', $html);
$this->assertStringContainsString('sizes="100vw"', $html);
}
public function test_media_without_webp_siblings_renders_a_bare_image(): void
{
Storage::fake('public');
$manager = new ImageManager(new Driver);
Storage::disk('public')->put('content/partial.jpg', (string) $manager->create(1600, 900)->toJpeg());
// Variants from before webp support: the original format only.
foreach (ResponsiveImage::WIDTHS as $width) {
Storage::disk('public')->put(
ResponsiveImage::variantPath('content/partial.jpg', $width),
(string) $manager->create(1600, 900)->toJpeg()
);
}
$html = Blade::render('<x-media.image path="content/partial.jpg" alt="Antigo" />');
$this->assertStringContainsString('srcset=', $html);
$this->assertStringNotContainsString('<picture', $html);
$this->assertStringNotContainsString('image/webp', $html);
} }
} }

View File

@@ -58,4 +58,53 @@ class ResponsiveImageTest extends TestCase
Storage::disk('public')->assertExists('content/new-960.jpg'); Storage::disk('public')->assertExists('content/new-960.jpg');
Storage::disk('public')->assertExists('content/new-1440.jpg'); Storage::disk('public')->assertExists('content/new-1440.jpg');
} }
public function test_generates_a_webp_sibling_for_every_variant(): void
{
Storage::fake('public');
$manager = new ImageManager(new Driver);
$path = 'content/photo.jpg';
Storage::disk('public')->put($path, (string) $manager->create(2000, 1200)->toJpeg(quality: 90));
ResponsiveImage::generate($path, 'public');
foreach (ResponsiveImage::WIDTHS as $width) {
Storage::disk('public')->assertExists("content/photo-{$width}.jpg.webp");
}
$this->assertSame(
ResponsiveImage::WIDTHS,
array_column(ResponsiveImage::availableWebpVariants($path, 'public'), 'width'),
);
// The original format stays available so a browser that cannot decode
// webp still has something to fall back to through the <img>.
foreach (ResponsiveImage::WIDTHS as $width) {
Storage::disk('public')->assertExists("content/photo-{$width}.jpg");
}
ResponsiveImage::delete($path, 'public');
foreach (ResponsiveImage::WIDTHS as $width) {
Storage::disk('public')->assertMissing("content/photo-{$width}.jpg.webp");
}
}
public function test_webp_originals_do_not_get_a_duplicate_webp_sibling(): void
{
Storage::fake('public');
$manager = new ImageManager(new Driver);
$path = 'content/already.webp';
Storage::disk('public')->put($path, (string) $manager->create(1600, 900)->toWebp());
ResponsiveImage::generate($path, 'public');
Storage::disk('public')->assertExists('content/already-480.webp');
Storage::disk('public')->assertMissing('content/already-480.webp.webp');
$this->assertSame([], ResponsiveImage::availableWebpVariants($path, 'public'));
}
} }