The share card I never looked at
When you paste a link into Slack, the thing your colleague sees first is not your writing. It is a picture, a bold line of text the platform renders, and a domain. You wrote one of those three.
I generate that picture for every post here. I built the generator weeks ago, looked at the output at 1200×630 in a preview window, thought nice, and shipped it. This week I resized one to 360px and looked again, which is a thing I should have done on day one.
This is what I found on my own site. It is not a law about link previews — it is one blog, inspected properly for the first time.
The card at a size somebody might actually see it
Every post gets a card rendered at build time: satori lays out a subset of CSS into SVG, resvg rasterises it, and the route drops the PNG at /og/<slug>.png. Here is the one for my previous post, at 360px wide:
360 divided by 1200 is 0.3, so every type size on that card arrives at 30% of what I designed:
| Element | In the 1200px card | At 360px |
|---|---|---|
| Headline | 66px | 19.8px |
| Brand wordmark | 38px | 11.4px |
| Date and tags | 22px | 6.6px |
I cannot read the date line. Neither can you — it is in the image above. At the width I chose, that element is not small, it is absent.
And the headline is less safe than 19.8px suggests, because the generator picks its size from the title’s character count:
function titleSize(title: string) {
if (title.length <= 34) return 66;
if (title.length <= 58) return 56;
if (title.length <= 86) return 46;
return 39;
}
Four rungs, so a headline lands at 19.8, 16.8, 13.8 or 11.7px at this test width. My longest live title — the cafe survey, 79 characters — sits on the 46px rung and renders around 13.8px. Still legible to me on a laptop screen. The bottom rung is not, and nothing in the generator warns me when a title falls onto it.
Character count is also a poor stand-in for rendered width, since it does not know an i from a W. This post’s own card proves it: the title is 32 characters, which buys it the largest rung at 66px, and it still wraps onto a second line with the word “at” alone on it. Go look at the card for the page you are reading. I could fix that by measuring the string instead of counting it, which is the actual bug, or by writing shorter headlines, which is cheaper. I have done neither yet.
So the finding is narrow and specific: on this card, at this width, one element is unreadable, one is marginal, and the headline carries it. I have not tested it inside a real Slack, Discord or feed layout, at multiple device pixel ratios, or with anyone else’s eyes, so I am not going to hand you a threshold in pixels. Resize your own and look.
Here is the part I want to be honest about rather than tidy. Having found that the date line is decoration, I am keeping it. Not because I measured an audience that reads it at full size — I have not, and I doubt it is large. I keep it because I like how the card looks with a bit of structure under the headline, and because deleting it does not make the headline any bigger. That is a preference, not a conclusion, and I would rather label it than dress it up as a design principle.
I cannot find X’s card spec anymore
While checking what dimensions I should target, I went looking for X’s card documentation. I could not find a current first-party version of it. Here is exactly where I looked, so you can falsify this in about a minute:
$ curl -sIL -o /dev/null -w "%{url_effective} -> %{http_code}\n" \
https://developer.x.com/en/docs/twitter-for-websites/cards/overview/markup
https://docs.x.com/overview -> 200
Both the old developer.x.com and developer.twitter.com card URLs land on the API overview page. Then the current docs site’s own indexes:
$ curl -s https://docs.x.com/llms.txt | grep -ic card
0
$ curl -s https://docs.x.com/sitemap.xml > x.xml
$ grep -oc "<loc>" x.xml
4782
$ tr '<' '\n' < x.xml | grep -ic card
0
$ tr '<' '\n' < x.xml | grep -ic post # sanity check that grep works
670
4,782 URLs in X’s documentation sitemap, zero of them with “card” anywhere in the path. The old validator at cards-dev.twitter.com/validator bounces to a login and returns 403.
That is not proof that no X card documentation exists — a page could be titled something else, or live in a help centre I did not search, and twitter:card markup demonstrably still does something in the wild. One current first-party page would kill this claim. But it does mean the specific documents that the entire “X card image size 2026” genre is downstream of no longer resolve, and those posts do not agree with each other either: 1200×628, 1200×675, 1200×600, at 1.91:1 or 2:1, max 5MB, all stated with equal confidence. They are citing each other.
Meta, by contrast, still publishes numbers in its own docs:
Use images that are at least 1200 x 630 pixels for the best display on high resolution devices.
The minimum allowed image dimension is 200 x 200 pixels.
Try to keep your images as close to 1.91:1 aspect ratio as possible to display the full image in Feed without any cropping.
The size of the image file must not exceed 8 MB.
LinkedIn’s own developer documentation covers its share API in detail and says nothing about og:image dimensions, so I am not repeating the 1200×627 everyone quotes. Slack does document its behaviour, and the sentence is better than any spec:
Slack crawls the URL, looks for common OpenGraph and X (formerly known as Twitter) Card metadata, and renders some micro-approximation of the content.
Micro-approximation. That is what you are designing for.
What I take from this is narrower than “build to Meta’s spec.” Several platforms read the same tags, which tells you nothing about how each one crops, scales or caches the result. Meta’s numbers are the only documented ones I could find, so I use 1200×630 as a conservative baseline for this site, and I would not assume the 8MB ceiling transfers anywhere else. Mine is 44KB, which keeps that question hypothetical.
What my server hands back to a crawler user-agent
I sent six requests to a live post, changing only the User-Agent string:
$ for ua in Twitterbot facebookexternalhit Slackbot-LinkExpanding \
Discordbot WhatsApp LinkedInBot; do
curl -s -o /dev/null -w "%{http_code} %{size_download}b\n" -A "$ua" \
https://vik.blog/signup-form-is-a-skill-now/
done
200 20532b
200 20532b
200 20532b
200 20532b
200 20532b
200 20532b
Be clear about what that is and is not. I did not observe Twitter, Meta, Slack, Discord, WhatsApp or LinkedIn. I observed my own server answering six strings, from my laptop, once. It proves my origin does not vary its response by those user agents and does not block them — nothing about what the real crawlers request, parse, or render.
As a smoke test it is still worth two minutes, because a status code alone does not tell you the tags survived. Extract them:
$ curl -s -A "Twitterbot/1.0" https://vik.blog/signup-form-is-a-skill-now/ \
| grep -oE '<meta (property|name)="(og|twitter):[^>]*>'
That catches the failures that actually happen to people: a bot filter serving a challenge page, a CDN returning a cached variant, a head assembled by JavaScript that a crawler never runs, or an og:image that is a relative path. Then fetch the image URL it printed and check it is a 200 with an image content type. My card is 44362 bytes of image/png, which I know because I asked for it rather than because a preview window looked fine.
The two properties I was missing
Running that made me read my own <head> properly, and it had no og:image:width or og:image:height. Meta documents what those are for:
The crawler has to see an image at least once before it can be rendered. This means that the first person who shares a piece of content won’t see a rendered image.
Using these tags will specify the image dimensions to the crawler so that it can render the image immediately without having to asynchronously download and process it.
I have not gone back and confirmed that any specific share of mine came out blank, so I am not going to tell you it did. What I can say is that omitting the properties leaves one documented cause of a missing image on a cold URL, that removing it costs two lines, and that it is most relevant to me because I am usually the person posting the link first. Those tags ship with this post.
The other gap I have left. This site sends twitter:card with no twitter:image, relying on a fallback to og:image that appears to work and that I can no longer find documented anywhere at X. Both of those are true and I would rather write them down than pick one.
The number I am not going to use
There is an obvious sentence available here about how you have eight seconds, less than a goldfish, so your card had better work fast.
That statistic is fabricated, and it is worth knowing how thoroughly. Simon Maybin traced it for BBC World Service’s More or Less in March 2017. The claim — “the average attention span is down from 12 seconds in the year 2000 to eight seconds now. That is less than the nine-second attention span of your average goldfish” — leads back to a 2015 Microsoft Canada consumer insights report, where it appears cited to a third party called Statistic Brain rather than to Microsoft’s own research. Maybin went to Statistic Brain’s listed sources:
And when I contact the listed sources — the National Center for Biotechnology Information at the US National Library of Medicine, and the Associated Press — neither can find any record of research that backs up the stats.
Statistic Brain never replied to him. And the researchers who study attention think the framing is wrong to begin with. Dr Gemma Briggs of the Open University, quoted in the same piece, calls the idea of an “average attention span” pretty meaningless:
It’s very much task-dependent. How much attention we apply to a task will vary depending on what the task demand is.
That is where the research stops, and everything after this paragraph is my own heuristic rather than anything Briggs said.
Mine is: my links get dropped into work chats far more often than they get scrolled past in a feed, so my card’s job is mostly to confirm what a link is to someone who already has a reason to care — closer to a label than a poster. That is a guess about my own distribution, not a finding, and it happens to justify the plain card I already had, which is exactly the kind of coincidence I should be suspicious of.
What I would check on your own site
All of this took an afternoon and two command-line tools:
- Resize your card and look at it.
sips -Z 360 card.png. If you cannot read an element, decide deliberately whether it stays. - Test your longest title, not your favourite one. If your generator scales type to fit, the worst case is the one to inspect.
- Curl your own URL with a crawler
User-Agentand extract the tags. Confirms your origin does not serve crawlers something different, and that the tags are present with absolute URLs. Then fetch the image URL and check the status. - Ship
og:image:widthandog:image:height. Two lines, one documented failure mode removed. - Use 1200×630 as a baseline if you have no reason to do otherwise, and treat any cross-platform limit you did not read in that platform’s own docs as folklore.
What I am not claiming: that any of this moves a click, a read or a subscriber. I have no test, no control and a sample of one blog. The bounded result is that I shipped a card without inspecting it at any size a person would meet it, and when I finally did, one element was invisible, one was marginal, and my head tags were incomplete.
Go resize your own.
Checked on 4 Aug 2026: Meta’s sharing image docs, Slack’s link unfurling docs, and Simon Maybin, “Busting the attention span myth”, BBC News, 10 March 2017. The X redirects, the llms.txt and sitemap.xml counts, the six user-agent responses and the card’s byte size were measured with the commands shown, against this site and those hosts, on the same day. LinkedIn dimensions are omitted because I could not find them in LinkedIn’s own documentation. This post was given a hostile read before publishing and it removed most of the general claims in the first draft; what is left is deliberately smaller.