Screenshots and GIFs in a GitHub README: size, dark mode and autoplay

A README section you can copy, what GitHub keeps when it renders README images, how to swap a screenshot in dark mode, and why a GIF might not play.

Chris Raroque

·

The uv README on github.com in GitHub’s dark theme, in a dark browser window over a soft blue sky: the README tab, the uv title, its badges, a one-line tagline and a purple bar chart of install times with light gray labels

To add a screenshot or any other image to a GitHub README, commit it to the repository, in a folder such as docs/images, and point to it from README.md with a relative path. GitHub’s guide to basic writing and formatting says to use relative links, not absolute ones, for an image that’s in your repository. In Markdown, an image is an exclamation mark, then the alt text in square brackets, then the path in parentheses, with no spaces between the three parts: ![The Today screen](docs/images/today-light.png). The HTML form does the same job and also takes a width:

<img src="docs/images/today-light.png" width="300" alt="Ellie's Today screen">

If you’d rather not commit the file, drag it into the editor while you edit README.md on github.com: GitHub’s guide says images can be uploaded by dragging, choosing a file or pasting, in issues, pull requests, comments and .md files. An uploaded file gets an anonymized URL, and on a public repository anyone can open it without signing in.

Below: a README section you can copy, then size, dark mode, GIFs, GitHub’s limits and what to check when an image doesn’t show. What GitHub keeps from HTML was tested by rendering a README through GitHub’s Markdown API on September 18, 2026; the limits and settings were rechecked on GitHub’s docs on September 19. The images on this page come from Moonjar, our product; nothing below depends on it.

A README section you can copy

This puts a screenshot that follows the reader’s light or dark theme above a GIF of one action, with text that still makes sense to someone who can’t see the image or whose GIF doesn’t play:

Copyable text
## Screenshots

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="docs/images/today-dark.png">
    <source media="(prefers-color-scheme: light)" srcset="docs/images/today-light.png">
    <img src="docs/images/today-light.png" width="300" alt="Today screen with three tasks due this afternoon">
  </picture>
</p>

Drag a task from Brain Dump onto the calendar to give it a time:

<p>
  <img src="docs/images/timebox-drag.gif" width="600" alt="A task dragged from Brain Dump lands at 2 PM">
</p>

Why it’s built that way:

  • The alt text says what the image shows, not “screenshot”. It’s what a screen reader reads and what shows if the file fails to load.

  • The sentence before the GIF carries the action in words. A reader with reduced motion turned on may see the GIF without it playing (see “Add a GIF that plays” below), so the text has to stand on its own.

  • One action per GIF, and a width on every image, so a phone screenshot doesn’t run past the bottom of a laptop screen and a GIF stays small enough to load.

  • Two files for the screenshot, one for each theme, taken from the same screen in the same state.

Swap in your own file names and descriptions. This exact section hasn’t been viewed on github.com, because the Markdown API test can’t resolve paths inside a repository. So after you commit it, view the README in both the light and the dark theme, and in each one open the screenshot in a new tab to confirm the right file loads: today-dark.png in the dark theme. The sections below explain each part.

Set the size of a README screenshot

Markdown image syntax has no width. An image shows at its own pixel width until it reaches the edge of the README column, because GitHub’s stylesheet caps README images at the column (max-width:100% on .markdown-body img in the CSS github.com served on September 18, 2026). A wide desktop screenshot shrinks to fit. A phone screenshot fills the column and runs past the bottom of a laptop screen: an iPhone 17 Pro Simulator screenshot is 1206 by 2622 pixels, more than twice as tall as it is wide.

The img tag sets the size with its width attribute. What the test render kept and dropped:

  • Kept: width, and height, but GitHub added height: auto so the image keeps its proportions. Set the width alone.

  • Kept: align="center" on a wrapping <p>, so <p align="center"> around the img tag centers it.

  • Dropped: a style attribute (GitHub put its own max-width: 100% in its place) and a class. A width set in CSS does nothing; use the attribute.

  • Added: a link around each image that isn’t already inside one. On github.com it opens the image’s own page in the repository, where the reader sees the full-size file. An image you wrap in your own link keeps your link.

For a sharp picture on high-density screens, export about twice the width you set: a phone screenshot shown at width="300" from a file 600 pixels wide or more. Cropping to the part of the screen the README talks about does more for legibility than any width. For a web app, capture the page at 2x and frame it before it goes in the README; for a phone app, the Simulator screenshot guide covers a clean status bar.

Show a different screenshot in dark mode

GitHub shows a README in light or dark to match each reader’s theme, and a screenshot of a white app can glare on the dark page. The <picture> element picks an image by theme; GitHub’s formatting guide lists it as supported, and its quickstart gives this pattern for a profile README, which works the same in a repository README:

JSX
<picture>
  <source media="(prefers-color-scheme: dark)"
    srcset="docs/images/today-dark.png">
  <source media="(prefers-color-scheme: light)"
    srcset="docs/images/today-light.png">
  <img alt="Ellie's Today screen"
    src="docs/images/today-light.png" width="300">
</picture>

The img tag inside is the fallback, and it carries the alt text and the width. In the test render, GitHub kept the picture, both sources and the width, and wrapped the whole thing in an element of its own called themed-picture.

The uv README on GitHub twice, light theme above dark: the title, badges, tagline and a purple bar chart of install times whose labels are dark gray on the white page and light gray on the dark page

The same README in GitHub’s light and dark themes. The benchmark chart is two images in a picture element, one with dark gray labels and one with light gray. Screenshot of github.com/astral-sh/uv/blob/main/README.md, captured September 18, 2026 with Moonjar.

The uv README uses this for its benchmark chart. Its two images are SVG files that differ only in the color of their text (#333333 in the light one, #C9D1D9 in the dark one) and have no background of their own, so the page shows through either way. For app screenshots, take both images from the same screen in the same state, so the theme is the only difference:

  • iOS Simulator: xcrun simctl ui booted appearance dark switches the booted simulator to dark, and light switches it back, according to simctl’s help text.

  • Mac: System Settings, then Appearance, then Light or Dark.

  • Web page: in Chrome DevTools, the Rendering tab can emulate prefers-color-scheme: dark.

Add a GIF that plays

A GIF goes in the same way as a screenshot, as a Markdown image or an img tag with a width. Two things decide whether a reader sees it move.

  1. The reader’s setting. GitHub’s accessibility settings follow the operating system’s reduced motion preference for animated images by default, and a signed-in reader can set “Autoplay animated images” to Sync with system, Enabled or Disabled (Settings, then Accessibility, then Motion; rechecked September 19, 2026). A reader with reduced motion turned on may not see your GIF play, so say what it shows in the alt text and in the sentence next to it, as the copyable section above does.

  2. The loop count in the file. A GIF stores how many times it plays. ffmpeg’s GIF muxer documentation lists looping forever as the default and -loop -1 for no loop. A GIF that stops after one pass was made that way; export it again with the default.

This ffmpeg command makes a small, sharp GIF from a screen recording by building one color palette for the clip:

Shell
ffmpeg -i "Screen Recording.mov" \
  -vf "fps=15,scale=800:-1:flags=lanczos,split[a][b];\
[a]palettegen=stats_mode=diff[p];\
[b][p]paletteuse=dither=bayer:bayer_scale=3:diff_mode=rectangle" \
  out.gif

On a 19.9-second recording of a Mac app window, it made an 800-pixel-wide GIF of 858 KB, converted with ffmpeg 6.0 on September 18, 2026. Reading that file the same day, its loop field is 0, which means forever. For trimming first, other widths and frame rates, and when an MP4 is the better file, see turn a Mac screen recording into a small GIF.

Animated GIF of the Ellie for Mac planner: the Plan the spring break budget task is dragged from the Brain Dump list onto the Timebox, where it lands as a green block at 2 PM, then Update emergency contacts lands as an orange block at 3 PM

The command’s output: 800 x 444 pixels, 15 frames per second, 19.9 seconds, 858 KB, set to loop forever. This copy doubles every pixel (1600 x 888, 2.2 MB); the frames are otherwise identical. Converted with ffmpeg 6.0 on September 18, 2026 from a Moonjar recording of Ellie for Mac on demo data.

Keep a README GIF to one action. For a longer demo, upload an MP4 instead: GitHub’s basic writing guide lists .md files among the places you can upload to, and its attaching page lists MP4, MOV and WebM and recommends H.264. GitHub hosts the file, and GitHub doesn’t support videos hosted anywhere else. A public README read on github.com on September 18, 2026 puts an uploaded MP4’s URL in a <video> tag, and the page GitHub served has a video player for it, with controls, muted and in GitHub’s own styling; the tag’s width and poster were not kept. The Markdown API test removed a <video> tag that pointed at a file in the repository, but the API is a weak guide for video: it also leaves an uploaded video’s URL as a plain link. For one short action that loops by itself, a GIF is still the simpler choice.

GitHub’s size limits for README images and GIFs

Checked September 19, 2026, on GitHub’s page on attaching files (uploads by dragging, pasting or choosing a file) and its page on large files (files committed to the repository):

Where the file goes

Limit

Uploaded image or GIF

10 MB

Uploaded video, free plan

10 MB

Uploaded video, paid plan

100 MB

Committed in the browser

25 MiB

Pushed with Git

Warns over 50 MiB, blocks over 100 MiB

The video limits depend on the plan of the user or organization that owns the repository. For a video over 10 MB in a repository on a paid plan, the uploader must be an organization member or outside collaborator, or be on a paid plan. GitHub accepts PNG, GIF, JPEG and SVG images.

A committed GIF can be far larger than 10 MB, but every reader downloads all of it. The command’s 858 KB output above runs nearly 20 seconds.

When a README image doesn’t show

  • The path is wrong. A relative path starts from the file that contains it, and one that starts with / starts at the repository root. So a README inside docs/ points to docs/images/shot.png as images/shot.png. Check the folder and the file name letter for letter, including capitals.

  • The image lives in another private repository. Cross-repository image links into a private repository work only for viewers with at least read access to it.

  • The image is on another server. GitHub passes images through a proxy called Camo. GitHub’s page on anonymized URLs says an image that shows in your browser but not on GitHub may have a content type Camo doesn’t support, that a changed image can stay cached, and that an image on a private network or behind a login can’t be shown; it gives a curl -I check of the image’s headers for the first two.

  • The GIF is still. Check the loop count, and remember the reader’s autoplay setting (see above).

  • The upload fails. An image or GIF over 10 MB won’t upload through the editor. Make it smaller with the command above, or trim it first.

  • The HTML did nothing. GitHub drops style and class, so use width and align for images. For video, upload the file to GitHub rather than pointing a <video> tag at the repository or another server.

Making README screenshots and GIFs with Moonjar

Moonjar is our product: a Mac app in early access that an agent such as Claude Code or Codex drives to capture screens and recordings. For a README:

  • It captures a web page by URL in light or dark (color_scheme), at a viewport you set, and can take the whole scroll height (full_page). The two uv captures above were made that way.

  • It can switch the iOS Simulator between light and dark before a capture, so both images come from the same screen.

  • It renders a cut recording straight to a GIF: 960 pixels wide, 800 past 10 seconds and 640 past 20, at up to 15 frames per second. Its render notes suggest an MP4 or WebM, or a shorter range, when a GIF passes about 10 MB.

Nothing on this page needs it. If you want to try it, request early access. For what to put in the screenshot in the first place, see how to make a good product screenshot.

How this page was made

The light and dark captures of the uv README were made with Moonjar, our product, from github.com on September 18, 2026. The GIF is an ffmpeg conversion of a Moonjar recording of Ellie for Mac, the planner I build, on demo data. The cover is a Moonjar capture of the uv README on github.com in GitHub’s dark theme, made September 23, 2026, over a photo by X F on Unsplash. The test render used GitHub’s public Markdown API on September 18; GitHub’s limits and accessibility settings were rechecked on its docs on September 19. The copyable README section was written for this page and hasn’t been rendered on github.com. This page was drafted with AI assistance.

MOONJAR © 2026

SCREENSHOTS AND DEMOS MADE WITH MOONJAR

MOONJAR © 2026

SCREENSHOTS AND DEMOS MADE WITH MOONJAR