Shared links now unfurl with an image in WhatsApp, Telegram and anything else that reads Open Graph, instead of showing the bare URL. A dedicated og/ rendition is generated per photo: 1200px longest side, quality laddered down until it fits under ~250 KB. WhatsApp drops the preview entirely once the image passes ~300 KB, and the medium rendition can exceed that, so it is not reused. Dimensions for og:image:width and og:image:height are read back from the encoded file with DecodeConfig, so they are emitted even when the rendition itself was skipped as up to date. The og:image and og:url values are absolute via BaseURL; both crawlers ignore relative image paths. Previews therefore only work against the deployed base URL, not http://localhost.
70 lines
3.1 KiB
HTML
70 lines
3.1 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>{{.Photo.Caption}}</title>
|
||
|
||
<!-- Open Graph + Twitter card: the preview shown when the link is shared to
|
||
WhatsApp, Telegram, etc. URLs are absolute because both crawlers ignore
|
||
relative image paths. Served from public/og/, kept under ~250 KB. -->
|
||
<meta property="og:type" content="article">
|
||
<meta property="og:site_name" content="{{.Author}}">
|
||
<meta property="og:title" content="{{.Photo.Caption}}">
|
||
<meta property="og:description" content="{{.Author}} — {{.Photo.Date.Format "January 2, 2006"}}">
|
||
<meta property="og:url" content="{{.BaseURL}}/photo/{{.Photo.Slug}}/">
|
||
<meta property="og:image" content="{{.BaseURL}}/{{.Photo.OG}}">
|
||
<meta property="og:image:type" content="image/jpeg">
|
||
{{if .Photo.OGWidth}}<meta property="og:image:width" content="{{.Photo.OGWidth}}">
|
||
<meta property="og:image:height" content="{{.Photo.OGHeight}}">{{end}}
|
||
<meta property="og:image:alt" content="{{.Photo.Caption}}">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<meta name="twitter:title" content="{{.Photo.Caption}}">
|
||
<meta name="twitter:description" content="{{.Author}} — {{.Photo.Date.Format "January 2, 2006"}}">
|
||
<meta name="twitter:image" content="{{.BaseURL}}/{{.Photo.OG}}">
|
||
|
||
<link rel="stylesheet" href="{{.FontsURL}}">
|
||
<link rel="stylesheet" href="/static/style.css">
|
||
<style>:root { --serif: '{{.SerifFamily}}', Georgia, serif; --mj: '{{.MlFamily}}', sans-serif; }</style>
|
||
<link rel="alternate" type="application/atom+xml" href="/feed.xml" title="Photo Feed">
|
||
{{if .Newer}}<link rel="prefetch" href="/photo/{{.Newer.Slug}}/">
|
||
<link rel="prefetch" as="image" href="/{{.Newer.Medium}}">{{end}}
|
||
{{if .Older}}<link rel="prefetch" href="/photo/{{.Older.Slug}}/">
|
||
<link rel="prefetch" as="image" href="/{{.Older.Medium}}">{{end}}
|
||
</head>
|
||
<body>
|
||
<div class="photo-page">
|
||
<nav class="photo-nav">
|
||
<a href="/" class="back-btn">← Back</a>
|
||
<span class="photo-nav-neighbours">
|
||
{{if .Newer}}<a href="/photo/{{.Newer.Slug}}/" data-nav="newer">← Newer</a>{{end}}
|
||
{{if and .Newer .Older}} / {{end}}
|
||
{{if .Older}}<a href="/photo/{{.Older.Slug}}/" data-nav="older">Older →</a>{{end}}
|
||
</span>
|
||
</nav>
|
||
<div class="dlayout">
|
||
<div class="dimg-wrap">
|
||
<a href="/{{.Photo.File}}" target="_blank" rel="noopener">
|
||
<img src="/{{.Photo.Medium}}" alt="{{.Photo.Caption}}">
|
||
</a>
|
||
</div>
|
||
<div class="dsidebar">
|
||
<div class="dauthor">
|
||
<div class="mini-avatar"><span>{{.Initial}}</span></div>
|
||
<span class="dauthor-name">{{.Author}}</span>
|
||
</div>
|
||
<p class="dcaption">{{.Photo.Caption}}</p>
|
||
<p class="ddate">{{.Photo.Date.Format "Jan 02, 2006"}}</p>
|
||
<p class="dfull"><a href="/{{.Photo.File}}" download>↓ Full size</a></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<footer class="site-footer">
|
||
© 2000–{{.Year}} {{.Author}}. All rights reserved.
|
||
</footer>
|
||
|
||
<script src="/static/photo-nav.js" defer></script>
|
||
</body>
|
||
</html>
|