ScreenshotNeo

BlogHow-to

How to Design an Instagram-Style HTML Page

Build a responsive Instagram-style profile with semantic HTML and CSS Grid, or embed public posts when you need real Instagram content.

By the ScreenshotNeo team1 October 20269 min read

To design an Instagram-style page with HTML and CSS, build a semantic profile header and a gallery of square image links, then use CSS Grid and aspect-ratio: 1 to keep the tiles square as the layout changes width. This creates a visual mockup using your own content. If you need real Instagram posts, use an available embed workflow instead; embedding and recreating the interface are different tasks.

1. Decide whether you need a mockup or real posts

Approach Content Layout control Use it for
HTML/CSS mockup Your own or otherwise authorized images and text You control the page layout and styling A portfolio, prototype, or design exercise
Post embed A real Instagram post through an embed workflow Presentation and behavior partly depend on the provider and embedding platform Displaying an eligible real post

WordPress documents an Instagram embed block that accepts post URLs and supports public posts. Its block also has a resize setting to preserve the aspect ratio on smaller devices. That describes the WordPress workflow; it does not guarantee every custom HTML setup or every post will embed. Check current official Instagram documentation before relying on a different embed or API method.

2. Create the page files

Make a directory with index.html and styles.css. The HTML below includes a profile header, a short bio, and six linked gallery tiles. Replace the example paths and descriptions with images you have permission to use. Each gallery link points to a larger image file; you can instead link to a detail page or remove the links if the tiles are not interactive.

<!-- index.html -->
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="A photo profile and gallery.">
    <title>Maya Chen — Photo Journal</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main class="profile-page">
      <header class="profile">
        <img class="profile__avatar" src="images/avatar.jpg" alt="Maya Chen">
        <div class="profile__details">
          <div class="profile__title-row">
            <h1>Maya Chen</h1>
            <a class="profile__link" href="https://example.com/">Website</a>
          </div>
          <p class="profile__stats">
            <span><strong>36</strong> posts</span>
            <span><strong>1,240</strong> followers</span>
            <span><strong>410</strong> following</span>
          </p>
          <p class="profile__bio">Photographer and maker. Notes from the coast and the city.</p>
        </div>
      </header>

      <section class="gallery-section" aria-labelledby="gallery-heading">
        <h2 id="gallery-heading">Photo gallery</h2>
        <div class="gallery">
          <a class="gallery__tile" href="images/coast-large.jpg">
            <img src="images/coast.jpg" alt="Waves breaking against a rocky coast" loading="lazy">
          </a>
          <a class="gallery__tile" href="images/market-large.jpg">
            <img src="images/market.jpg" alt="A flower stall at a morning market" loading="lazy">
          </a>
          <a class="gallery__tile" href="images/architecture-large.jpg">
            <img src="images/architecture.jpg" alt="Sunlight across a brick building" loading="lazy">
          </a>
          <a class="gallery__tile" href="images/coffee-large.jpg">
            <img src="images/coffee.jpg" alt="Coffee and a notebook on a cafe table" loading="lazy">
          </a>
          <a class="gallery__tile" href="images/trail-large.jpg">
            <img src="images/trail.jpg" alt="A path through a green forest" loading="lazy">
          </a>
          <a class="gallery__tile" href="images/studio-large.jpg">
            <img src="images/studio.jpg" alt="A camera on a studio desk" loading="lazy">
          </a>
        </div>
      </section>
    </main>
  </body>
</html>

The stylesheet is connected with a <link rel="stylesheet" href="…"> element in the document head, as described in MDN’s link element reference.

/* styles.css */
:root {
  color-scheme: light;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  color: #202124;
  background: #fff;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
}

.profile-page {
  width: min(100% - 32px, 960px);
  margin: 40px auto;
}

.profile {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: 48px;
  align-items: center;
  padding: 8px 24px 40px;
}

.profile__avatar {
  width: 144px;
  aspect-ratio: 1;
  border-radius: 50%;
  object-fit: cover;
}

.profile__details {
  min-width: 0;
}

.profile__title-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
}

.profile h1 {
  margin: 0;
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  font-weight: 500;
}

.profile__link {
  color: #2457a7;
}

.profile__stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  padding: 0;
  margin: 24px 0 16px;
}

.profile__stats span {
  white-space: nowrap;
}

.profile__bio {
  max-width: 52ch;
  margin: 0;
  line-height: 1.5;
}

.gallery-section h2 {
  margin: 0 0 12px;
  font-size: 1rem;
  font-weight: 600;
}

.gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 3px;
}

.gallery__tile {
  display: block;
  min-width: 0;
  aspect-ratio: 1;
  overflow: hidden;
  background: #eee;
}

.gallery__tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.gallery__tile:focus-visible,
.profile__link:focus-visible {
  outline: 3px solid #2457a7;
  outline-offset: 3px;
}

@media (max-width: 600px) {
  .profile-page {
    width: 100%;
    margin: 20px auto;
  }

  .profile {
    grid-template-columns: 72px minmax(0, 1fr);
    gap: 18px;
    padding: 8px 16px 24px;
  }

  .profile__avatar {
    width: 72px;
  }

  .profile__stats {
    gap: 8px 14px;
    margin: 16px 0 12px;
    font-size: 0.9rem;
  }

  .gallery {
    gap: 1px;
  }
}

The gallery uses three equal columns, a small gap, and square tiles. minmax(0, 1fr) lets tracks shrink without their contents forcing overflow; aspect-ratio: 1 preserves square tile proportions as widths change. The images fill each tile and crop as needed with object-fit: cover. MDN documents this square-grid pattern and responsive embedded media techniques in its guide to CSS aspect ratios.

The 600-pixel breakpoint is an example, not a required Instagram measurement. Adjust it to suit your content and inspect the page at narrow phone, wider phone, tablet, and desktop viewport sizes. The gallery can remain three columns on phones for a compact tile grid; if you prefer larger images, use two columns at a narrow breakpoint and keep the same square-tile rule.

4. Add real Instagram content when needed

For an actual post, use an embed option supported by the platform or publishing system you use. In WordPress, the documented Instagram block takes a post URL; the documentation says public posts can be embedded and describes a resize control for smaller screens. Confirm that the post is public and check the rendered block on a phone-sized viewport. A static HTML/CSS mockup has different content and eligibility assumptions.

If the workflow gives you an iframe, include a concise title that identifies the embedded post, such as title="Instagram post: coastal sunset photo". People using assistive technology can use that label to identify the frame. See MDN’s iframe reference for iframe labeling and sizing notes.

Portrait video often uses a 9:16 ratio. MDN’s aspect-ratio guide shows responsive embed patterns; make sure the parent container can shrink to the available width. An iframe’s content sizing is not generally exposed to its parent page by default, so responsive behavior can depend on support from the embedded document.

5. Accessibility and content details

  • Give informative gallery images concise alternative text that conveys what is visible, rather than repeating a caption or filename.
  • Use empty alternative text, alt="", for an image that is purely decorative and adds no information.
  • Keep a visible keyboard focus style on linked tiles and other controls.
  • Use a logical heading order: the profile name is the page’s h1, and the gallery heading is an h2.
  • Provide useful link destinations. A tile should open the larger image, a detail page, or another clearly related destination.

6. Optional profile structured data

If the page represents a real person and you want to describe that profile to search engines, Google Search Central documents ProfilePage structured data with a Person as mainEntity. This metadata does not create the visible page and is unnecessary for a mock profile. Only add accurate values that match the page.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "ProfilePage",
  "mainEntity": {
    "@type": "Person",
    "name": "Maya Chen",
    "alternateName": "mayachen",
    "description": "Photographer and maker.",
    "image": "https://example.com/images/avatar.jpg",
    "sameAs": ["https://example.com/"]
  }
}
</script>

Replace every example value and URL. Do not label a fictional mockup as a real person’s profile. Google’s ProfilePage structured data guide provides the relevant field context.

7. Troubleshooting

Problem Likely cause Fix
Styles do not appear The stylesheet path or filename does not match the href. Check that styles.css is beside index.html, or update the relative path. Check browser developer tools for a failed stylesheet request.
Tiles are not square The tile does not have aspect-ratio, or another rule overrides its sizing. Set aspect-ratio: 1 on the tile and inspect computed styles. Keep the image at width: 100%; height: 100%.
Images look stretched The image is being resized to both dimensions without cropping. Use object-fit: cover on the image. Choose object-fit: contain instead if showing the entire image matters more than filling the square.
Gallery causes horizontal scrolling A grid child has an intrinsic minimum width, or the page has fixed-width content. Use minmax(0, 1fr) for tracks, set min-width: 0 on grid children, and remove fixed widths wider than the viewport.
Some image boxes are empty The local path is incorrect, the file is missing, or the host blocks access. Verify each src in the browser’s network panel and use a working, authorized image URL or local file.
Embedded post is unavailable The post may not be public or the chosen embed workflow may not support it. Check the post’s visibility and the publishing platform’s current embed requirements. The documented WordPress block specifies public posts.
Embed is cut off on a phone The embed container or iframe has a fixed width or height. Let the container use the available width, preserve its intended aspect ratio, and use the embed provider’s responsive option where available. Test at real viewport sizes.

8. Performance, reliability, and cost

For a small static gallery, the main practical costs are the image files and the time needed to maintain their paths and descriptions. Use appropriately sized thumbnails for the grid and link to larger files only when needed. The example marks gallery images as lazy-loaded, which can defer offscreen image requests; ensure that the initial viewport still has its key profile image available promptly.

A local mockup does not depend on an embed provider at render time. An embedded post relies on the chosen provider and embed workflow, so check availability, responsive behavior, and the current eligibility rules before making it a required part of a page. The sources for this guide do not establish embed quotas, platform-wide availability guarantees, or performance figures.

If you need a screenshot of the finished page for a review, documentation, or preview, ScreenshotNeo is a website screenshot API and MCP server. Its API can return a screenshot or PDF, and its options include full-page capture, viewport and device presets, custom CSS, wait conditions, and element capture. See the ScreenshotNeo API documentation for request options.

Or skip the browser setup

Use ScreenshotNeo when you want a rendered page image without setting up a browser capture script. This GET request saves a WebP screenshot:

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com/profile \
  -o profile.webp

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up free for 1,000 screenshots a month, no card required.

FAQ

Yes. Add a narrow-screen media query that changes grid-template-columns to two equal tracks. Keep aspect-ratio: 1 on each tile so it stays square.

Does this example reproduce Instagram’s actual interface?

No. It is an original profile-style layout made from standard HTML and CSS, with example content. Adjust the design and use only images and text you are allowed to publish.

Do I need structured data for a visual mockup?

No. The optional profile metadata is for a page representing a real person and does not affect the visible layout.

Where can I find a longer HTML and CSS learning resource?

DigitalOcean’s HTML and CSS website tutorial is one available learning resource.