Communitygithub.com

brainzcode/premium-landing

Ship link previews and favicons that actually work - Open Graph and Twitter card tags, a correctly framed 1200x630 share image, and a full favicon set (SVG, ICO, apple-touch, maskable manifest icons). Use when asked for social sharing, OG image, open graph, twitter card, link preview, share image, favicon, app icon, apple touch icon, or web manifest. Also use when a shared link previews blank, shows the wrong image, or a favicon will not appear. Includes the absolute-URL trap that silently blanks every preview and the font trap that breaks SVG favicons on other machines.

O que é premium-landing?

premium-landing is a Claude Code agent skill that ship link previews and favicons that actually work - Open Graph and Twitter card tags, a correctly framed 1200x630 share image, and a full favicon set (SVG, ICO, apple-touch, maskable manifest icons). Use when asked for social sharing, OG image, open graph, twitter card, link preview, share image, favicon, app icon, apple touch icon, or web manifest. Also use when a shared link previews blank, shows the wrong image, or a favicon will not appear. Includes the absolute-URL trap that silently blanks every preview and the font trap that breaks SVG favicons on other machines.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/brainzcode/premium-landing/tree/HEAD/plugins/premium-landing/skills/social-share-favicons

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Social sharing and favicons

The one thing that matters

og:image and og:url must be absolute URLs on a publicly reachable host.

The crawler fetches them from its own servers. It has no page context, no cookies, no referer, and no idea what your relative path is relative to. A path like /assets/og.jpg or assets/og.jpg resolves to nothing and the card comes back blank.

<!-- dead: renders an empty card everywhere -->
<meta property="og:image" content="assets/images/og-image.jpg">

<!-- works -->
<meta property="og:image" content="https://example.com/assets/images/og-image.jpg">

This is the single most common reason a preview does not work. Before debugging anything else, confirm the URL is absolute and loads in a private window.

The corollary: previews cannot work on localhost or file://. Nothing you do locally will produce a real preview. Deploy first, then validate.

The tag set

<link rel="canonical" href="https://example.com/">

<meta property="og:type"        content="website">
<meta property="og:site_name"   content="Brand">
<meta property="og:locale"      content="en_US">
<meta property="og:url"         content="https://example.com/">
<meta property="og:title"       content="Brand - what it is">
<meta property="og:description" content="One sentence. Around 110 characters.">
<meta property="og:image"       content="https://example.com/og-image.jpg">
<meta property="og:image:type"  content="image/jpeg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt"   content="What is in the picture.">

<meta name="twitter:card"        content="summary_large_image">
<meta name="twitter:title"       content="Brand - what it is">
<meta name="twitter:description" content="One sentence. Around 110 characters.">
<meta name="twitter:image"       content="https://example.com/og-image.jpg">
<meta name="twitter:image:alt"   content="What is in the picture.">

Three details that are easy to get wrong:

og: uses property=, twitter: uses name=. Mixing them up is silently ignored, so the tags look present in the source and do nothing. Some parsers tolerate name= on og:, but do not rely on it.

twitter:card must be summary_large_image. The default is a small square card with the image as a thumbnail beside the text, which wastes a 1200x630 image.

Declaring og:image:width and og:image:height makes the card render immediately. Without them the platform has to download the image before it knows the aspect, so the first person to share the link often sees a text-only card. They must match the real file. Measure it, do not assume.

Copy length: titles are cut around 60 characters, descriptions around 110 to 200 depending on platform. Front-load the meaning.

The share image

1200x630 (1.905:1). JPEG unless the design needs alpha, which it does not, since every platform composites onto white. Keep it under about 1 MB; the hard caps are 8 MB (Facebook) and 5 MB (X), but large files time out during scraping.

Render it at the target ratio, do not crop a screenshot

Taking an arbitrary screenshot and cropping to 1.905:1 throws away the composition the designer chose. Render the source at the ratio instead:

# 1440x756 is exactly 1.905:1, so it downscales to 1200x630 with no crop
chrome --headless --disable-gpu --hide-scrollbars --virtual-time-budget=8000 \
  --window-size=1440,756 --screenshot=og-src.png "file://$PWD/index.html"

The short-viewport trap

If the page has a full-height hero (100vh / 100svh), rendering directly at 1.905:1 gives a viewport far shorter than a real desktop screen. Any object-fit: cover media crops much harder than it does in real use, and elements that normally clear each other collide - typically a header logo landing on the subject's head.

Render at a taller viewport so the media crops the way it does in real use, then cut the 1.905:1 window out of it:

chrome --headless ... --window-size=1440,1000 --screenshot=og-src.png "file://$PWD/index.html"
ffmpeg -i og-src.png -vf "crop=1440:756:0:0" -q:v 2 og-crop.png     # top-anchored

Compare a top-anchored crop against a centred one before choosing. Top-anchored usually keeps the brand furniture (utility bar, nav, wordmark); centred usually keeps the subject and the call to action. You cannot have both, because a full-height hero is taller than 1.905:1 allows.

Framing

Cards render around 500 px wide in a feed, and each platform crops differently (LinkedIn and Slack are tighter than Facebook). Keep anything that must survive inside the central 80%, and set any type large enough to read at half size. Fine UI detail disappears; a big wordmark and a strong image do not.

Favicons

The font trap

Do not put <text> in an SVG favicon.

<!-- breaks: renders in whatever fallback font the viewer happens to have -->
<svg ...><text x="50%" y="50%" font-family="Geist">G</text></svg>

The SVG renders on the viewer's machine, where your webfont does not exist. Extract the glyph as a path so it is font-independent and resolution-independent. See the script below.

The transparency trap

A dark glyph on a transparent background vanishes against dark browser chrome, and a light one vanishes on light. Draw the icon on a filled tile. It also gives the mark a consistent silhouette across tab bars, bookmarks and home screens.

The file set

FileSizeWhy
/favicon.ico16, 32, 48browsers request this path implicitly even when you declare others; keep it at the web root
favicon.svgscalablewhat modern browsers prefer, and it stays sharp on any display
apple-touch-icon.png180iOS home screen
icon-192.png192manifest
icon-512.png512manifest, splash screens
icon-maskable-512.png512Android adaptive masks
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/assets/icons/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/assets/icons/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">

The maskable safe zone

Android clips icons to arbitrary shapes (circle, squircle, teardrop). A maskable icon must keep everything meaningful inside the central 80% circle, which means roughly 40% of the tile edge is disposable padding. Reuse the normal icon and it will get its edges shaved off.

In practice: if the glyph covers 60% of the tile in the standard icon, drop it to about 40% for the maskable one. Ship both, with "purpose": "any" and "purpose": "maskable" as separate entries. Never put both purposes on one file.

Web manifest

{
  "name": "Brand",
  "short_name": "Brand",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "background_color": "#f7f6f4",
  "theme_color": "#0a0a0a",
  "icons": [
    { "src": "/assets/icons/favicon.svg", "type": "image/svg+xml", "sizes": "any" },
    { "src": "/assets/icons/icon-192.png", "type": "image/png", "sizes": "192x192", "purpose": "any" },
    { "src": "/assets/icons/icon-512.png", "type": "image/png", "sizes": "512x512", "purpose": "any" },
    { "src": "/assets/icons/icon-maskable-512.png", "type": "image/png", "sizes": "512x512", "purpose": "maskable" }
  ]
}

theme_color tints mobile browser chrome, so it should match whatever sits at the very top of the page, not the brand's main colour. If the page opens on a black utility bar, use that black. A mismatch reads as a rendering bug. Mirror it in <meta name="theme-color">.

background_color is the splash screen behind the icon before first paint, so it should match the page background.

Generation script

Needs pillow and fonttools. Produces the whole icon set plus the share image.

import os
from PIL import Image, ImageDraw, ImageFont
from fontTools.ttLib import TTFont
from fontTools.pens.svgPathPen import SVGPathPen
from fontTools.pens.boundsPen import BoundsPen

FONT   = "brand.ttf"      # the real brand face, at the weight the wordmark uses
CHAR   = "G"
INK    = "#0a0a0a"        # tile
PAPER  = "#f2f1ed"        # glyph
COVER  = 0.60             # glyph height as a share of the tile
OUT    = "assets/icons"
os.makedirs(OUT, exist_ok=True)

# ── SVG: glyph as a real path, so it does not depend on the viewer's fonts ──
f  = TTFont(FONT)
gs = f.getGlyphSet()
name = f.getBestCmap()[ord(CHAR)]

bp = BoundsPen(gs); gs[name].draw(bp)
x0, y0, x1, y1 = bp.bounds
pen = SVGPathPen(gs); gs[name].draw(pen)
d = pen.getCommands()

VB = 64
s  = (VB * COVER) / (y1 - y0)
tx = (VB - (x1 - x0) * s) / 2 - x0 * s
ty = (VB + (y1 - y0) * s) / 2 + y0 * s      # y flips: fonts are y-up, SVG is y-down

open(f"{OUT}/favicon.svg", "w").write(
    f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {VB} {VB}">'
    f'<rect width="{VB}" height="{VB}" fill="{INK}"/>'
    f'<path transform="translate({tx:.3f} {ty:.3f}) scale({s:.5f} -{s:.5f})" '
    f'd="{d}" fill="{PAPER}"/></svg>')

# ── Raster tiles ────────────────────────────────────────────────────────────
def tile(px, cover=COVER):
    img = Image.new("RGB", (px, px), INK)
    dr  = ImageDraw.Draw(img)
    fnt = ImageFont.truetype(FONT, px * 2)
    bb  = dr.textbbox((0, 0), CHAR, font=fnt)
    fnt = ImageFont.truetype(FONT, int(px * 2 * (px * cover) / (bb[3] - bb[1])))
    bb  = dr.textbbox((0, 0), CHAR, font=fnt)
    dr.text(((px - (bb[2] - bb[0])) / 2 - bb[0],
             (px - (bb[3] - bb[1])) / 2 - bb[1]), CHAR, font=fnt, fill=PAPER)
    return img

tile(180).save(f"{OUT}/apple-touch-icon.png", optimize=True)
tile(192).save(f"{OUT}/icon-192.png", optimize=True)
tile(512).save(f"{OUT}/icon-512.png", optimize=True)
tile(512, cover=0.42).save(f"{OUT}/icon-maskable-512.png", optimize=True)  # 40% safe zone
tile(256).save("favicon.ico", sizes=[(16, 16), (32, 32), (48, 48)])

# ── Share image: crop a taller render to ratio, then downscale ──────────────
src = Image.open("og-src.png").convert("RGB")          # e.g. 1440x1000
src.crop((0, 0, 1440, 756)).resize((1200, 630), Image.LANCZOS).save(
    "assets/images/og-image.jpg", quality=88, optimize=True, progressive=True)

No brand font available? Any geometric grotesque works for a monogram. Rasterising an existing logo PNG onto the tile is fine too; just centre it inside the same COVER fraction and keep the maskable variant smaller.

Verify before shipping

import re, json, os
from PIL import Image
s = open("index.html").read()
tags = dict(re.findall(r'(?:property|name)="((?:og|twitter):[^"]+)"\s+content="([^"]+)"', s))

for t in ("og:url", "og:image", "twitter:image"):
    assert tags[t].startswith("http"), f"{t} is not absolute: {tags[t]}"

w, h = Image.open("assets/images/og-image.jpg").size
assert (str(w), str(h)) == (tags["og:image:width"], tags["og:image:height"]), "declared size is wrong"
assert tags["twitter:card"] == "summary_large_image"

m = json.load(open("site.webmanifest"))
for i in m["icons"]:
    assert os.path.isfile(i["src"].lstrip("/")), f"missing icon {i['src']}"
print("ok")

Checklist:

  • og:image and og:url absolute, on the real host, loading in a private window
  • declared og:image:width / height match the actual file
  • twitter:card is summary_large_image
  • og: tags use property=, twitter: tags use name=
  • every placeholder domain replaced (grep for it; there are usually four)
  • favicon.ico at the web root, not only in an assets folder
  • SVG favicon contains a <path>, no <text>
  • icon tile is filled, so it survives dark and light browser chrome
  • maskable icon padded to the central 80%
  • theme_color matches the top edge of the page

Caching

Every platform caches aggressively and will keep serving the old card after you fix it. Re-scrape:

LinkedIn caches hardest and effectively ignores changes for around 7 days unless you run the inspector. If a URL has already been shared widely with a broken card, adding a query string (?v=2) forces a fresh scrape.

Favicons cache hard in the browser too. Test in a private window; a normal reload will usually keep serving the old one.

Placeholder domains

When the real domain is not known yet, use a subdomain of example.com (https://brand.example.com). RFC 2606 reserves it, so it is unmistakably a placeholder and can never accidentally point at a domain somebody else owns. Never leave a plausible but unowned domain in shipped metadata.

Put the replacement instruction in a comment directly above the block and in the project README, and say how many occurrences there are. It is normally four: canonical, og:url, og:image, twitter:image.

Individual skills in this repo

This repo contains 7 individual skills — each has its own dedicated page.

brainzcode/premium-landing

Add a studio or agency build credit to a website footer — "Site by <Studio>" linking to the studio's site. Use when finishing or shipping a site, adding a footer, or when asked for an agency credit, build credit, designed-by line, or attribution. Covers wording, placement opposite the client's copyright, markup for each stack, styling from the host site's tokens, and the sitewide-footer-link SEO footprint when the same credit runs across many client sites.

brainzcode/premium-landing

Build a complete premium landing page, either replicated from a reference screenshot/URL or designed from a written brief, in HTML, React, Next.js or Astro. Use when asked to build, design, replicate or clone a landing page, marketing page, or one-page site; when handed a design screenshot or Figma/Pinterest reference to turn into code; or when a landing page needs a full premium pass. Covers design tokens, section patterns, the layout traps that silently break grids and marquees, and screenshot-based responsive verification from 320px to 1920px.

brainzcode/premium-landing

Add Lenis smooth scrolling to a site correctly — setup, anchor offsets for sticky headers, scroll locking for modals and menus, reduced-motion handling. Use when asked for smooth scroll, Lenis, momentum/eased scrolling, or when smooth scroll broke a sticky header, a modal lock, or anchor links. Includes the sticky-position trap that only appears at non-zero scroll.

brainzcode/premium-landing

Build true Apple-style liquid glass in the browser — real refraction via SVG displacement maps, chromatic dispersion, specular edges. Use when asked for liquid glass, glassmorphism, frosted/glass cards, glass navbars, or when a blur-based "glass" effect looks flat. Includes the performance trap that makes backdrop-filter stutter on scroll and the contrast trap that makes text unreadable on glass.

brainzcode/premium-landing

Build a premium mobile nav — a hamburger button that morphs into a close X, a full-screen overlay panel with staggered link reveal, scroll lock, focus trap, and reduced-motion handling. Use when asked for a mobile menu, hamburger/burger button, nav drawer, off-canvas menu, or when a menu loses scroll position, still scrolls behind on iOS, leaks Tab focus to the page underneath, or the close button stops responding. Includes the z-index trap that swallows the close button and the iOS rubber-band trap.

brainzcode/premium-landing

Rebuild a design accurately from a supplied reference — a screenshot, PDF page, mockup or live URL — by measuring it numerically instead of eyeballing it. Use when asked to replicate, clone, match or rebuild a design "exactly", "1 to 1" or "with no mistakes", or when a build drifts from its reference. Covers extracting the true content box from a padded screenshot, deriving element positions as percentages, solving object-fit framing, and knowing when a reference cannot be matched.

brainzcode/premium-landing

Source free, licence-clear photography for a site from Unsplash — searching by mood rather than noun, sizing and cropping for the slot, honouring the API's hotlink and download-tracking requirements, and writing real alt text. Use when a page needs a hero image, section photography, avatars or card art and none was supplied; when placeholder greys need replacing with real imagery; or when asked to find, pick or swap photos for a design.

Habilidades Relacionadas