> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Catch Visual Regressions Before Every PR

export const UseCaseHero = ({title, description, prompt, category, features, devinUrl, agent, intent, playbookId, type}) => {
  const encodedPrompt = encodeURIComponent(prompt || '');
  const tag = 'docs-use-case-gallery';
  const utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=hero-cta';
  const agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  const devinHref = type === 'schedule' ? 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + (prompt ? '&prompt=' + encodedPrompt : '') : type === 'review' ? 'https://app.devin.ai/review?' + utm : agent === 'ada' ? 'https://app.devin.ai/search?' + utm + '&noSubmit=true' + (prompt ? '&prompt=' + encodedPrompt : '') : devinUrl ? devinUrl.includes('?') ? devinUrl + '&' + utm + agentParams : devinUrl + '?' + utm + agentParams : prompt ? 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encodedPrompt : 'https://app.devin.ai/?' + utm + agentParams;
  const buttonLabel = type === 'schedule' ? 'Schedule in Devin ↗' : type === 'review' ? 'Set Up Devin Review ↗' : agent === 'advanced' ? 'Try in Devin ↗' : agent === 'dana' ? 'Try in Dana ↗' : agent === 'ada' ? 'Try in Ask Devin ↗' : 'Try in Devin ↗';
  const featureList = features ? features.split(',').map(f => f.trim()) : [];
  return <div className="uc-hero">
      <div className="uc-hero-inner">
        <div className="uc-hero-left">
          <h1 className="uc-hero-title">{title}</h1>
          <p className="uc-hero-desc">{description}</p>
          <div>
            <a href={devinHref} target="_blank" rel="noopener noreferrer" className="try-in-devin-btn">
              {buttonLabel}
            </a>
          </div>
        </div>
        <div className="uc-hero-meta">
          <div className="uc-meta-item">
            <span className="uc-meta-label">Author</span>
            <span className="uc-meta-value">Cognition</span>
          </div>
          <div className="uc-meta-item">
            <span className="uc-meta-label">Category</span>
            <span className="uc-meta-value">{category}</span>
          </div>
          {featureList.length > 0 && <div className="uc-meta-item">
              <span className="uc-meta-label">Features</span>
              <span className="uc-meta-value">{featureList.join(', ')}</span>
            </div>}
        </div>
      </div>
    </div>;
};

export const PromptBlock = ({children, type, agent, intent, playbookId}) => {
  var utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=prompt-block';
  var tag = 'docs-use-case-gallery';
  var agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  var label = type === 'schedule' ? 'Schedule in Devin' : type === 'playbook' ? 'Create Playbook' : type === 'knowledge' ? 'Add to Knowledge' : agent === 'advanced' ? 'Try in Devin' : agent === 'dana' ? 'Try in Dana' : agent === 'ada' ? 'Try in Ask Devin' : 'Try in Devin';
  var buildUrl = function (text) {
    var encoded = encodeURIComponent(text);
    if (type === 'schedule') return 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + '&prompt=' + encoded;
    if (type === 'playbook') return 'https://app.devin.ai/settings/playbooks/create?' + utm + '&body=' + encoded;
    if (type === 'knowledge') return 'https://app.devin.ai/knowledge?' + utm + '&body=' + encoded;
    if (agent === 'ada') return 'https://app.devin.ai/search?' + utm + '&noSubmit=true&prompt=' + encoded;
    return 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encoded;
  };
  const ref = React.useRef(null);
  const [href, setHref] = React.useState('#');
  React.useEffect(() => {
    if (!ref.current) return;
    var codeEl = ref.current.querySelector('pre code');
    if (codeEl) {
      var text = codeEl.textContent.trim();
      if (text) setHref(buildUrl(text));
    }
    var header = ref.current.querySelector('[data-component-part="code-block-header"]');
    if (header && !header.querySelector('.prompt-block-devin-link')) {
      var link = document.createElement('a');
      link.href = href;
      link.target = '_blank';
      link.rel = 'noopener noreferrer';
      link.className = 'prompt-block-devin-link';
      link.style.cssText = 'display:inline-flex;align-items:center;gap:6px;text-decoration:none;color:#fff;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;background:#317CFF;transition:background 0.2s;margin-left:8px;';
      link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> ' + label;
      link.onmouseenter = function () {
        link.style.background = '#2968D9';
      };
      link.onmouseleave = function () {
        link.style.background = '#317CFF';
      };
      header.appendChild(link);
    }
    var existingLink = ref.current.querySelector('.prompt-block-devin-link');
    if (existingLink && href !== '#') existingLink.href = href;
  });
  return <div className="prompt-block" ref={ref}>{children}</div>;
};

<UseCaseHero title="Catch Visual Regressions Before Every PR" description="A repo skill that makes Devin screenshot every affected page and flag layout breakage before opening a PR." prompt="Create a repository skill at .agents/skills/visual-regression/visual-regression.md that tells Devin how to start our Next.js app, navigate every page touched by the current diff, screenshot at desktop and mobile widths, and flag any layout issues — before opening a PR." category="Feature Development" features="Skills" />

<div className="uc-detail-wrapper">
  <Tip>Don't want to set this up manually? Paste a link to this page into a Devin session and ask it to set everything up for you.</Tip>

  <Steps>
    <Step title="The problem: CSS bugs slip past unit tests">
      Your test suite passes, CI is green, and the PR gets merged — but the settings page now has overlapping text on mobile, and the checkout button disappeared behind a footer. Unit tests and linters don't catch visual breakage, so it ships to production and a customer reports it.

      A repository skill solves this. Skills are markdown files you commit to `.agents/skills/<your-skill>/` in any of your repos. Devin sees all skills across all connected repositories — you can trigger them manually or Devin can choose to trigger them automatically when it detects a relevant situation. This skill tells Devin exactly how to boot your app, navigate the pages affected by the diff, and screenshot them at multiple viewport widths — every time, before every PR.
    </Step>

    <Step title="Add the visual-regression skill to your repo">
      Commit a file at `.agents/skills/visual-regression/visual-regression.md` that encodes your team's manual QA checklist as steps Devin can follow:

      ```markdown theme={null}
      # Visual Regression Check

      ## Description
      Before opening any PR, start the app locally and screenshot every
      page affected by the current diff at desktop and mobile widths.
      Flag any layout issues found.

      ## Prerequisites
      - Docker running (for the database)
      - Port 3000 available

      ## Setup
      1. Install dependencies: `npm install`
      2. Start services: `docker-compose up -d postgres redis`
      3. Run migrations: `npx prisma migrate dev`
      4. Seed test data: `npm run db:seed`
      5. Start the dev server: `npm run dev`
      6. Wait for "Ready on http://localhost:3000" in the terminal

      ## Visual Checks
      1. Read the current git diff to identify which pages are affected
      2. For each affected page:
         a. Open the page in the browser at http://localhost:3000/{route}
         b. Screenshot at 1280px width (desktop)
         c. Resize the browser to 375px width (mobile)
         d. Screenshot at mobile width
         e. Check for: overlapping text, elements hidden behind other
            elements, horizontal scroll, buttons or links that are
            unreachable, missing images or icons, console errors
      3. If any issue is found, list it with the page URL, viewport
         width, and a description of the problem

      ## Before Opening the PR
      1. Run `npm run lint` and fix any issues
      2. Run `npm test` and confirm all tests pass
      3. Include all screenshots in the PR description
      4. If issues were found, list them at the top of the PR body
      ```

      This file ships with your code. Once committed, Devin sees it as an available skill — whenever a session touches UI files in this repo, Devin can trigger the visual regression checks automatically, or you can invoke them manually at any point.
    </Step>

    <Step title="Make the checks specific to your pages">
      Generic instructions like "check the page looks right" produce vague results. The highest-value part of this skill is telling Devin what correct looks like for each area of your app.

      Add route-specific sections to your skill:

      ```markdown theme={null}
      ## Route-Specific Checks

      ### Dashboard (`/dashboard`)
      - The metric cards should display in a 3-column grid on desktop
      - Cards should stack to a single column on mobile
      - The chart should be fully visible without horizontal scroll

      ### Checkout (`/checkout`)
      - The "Place Order" button must be visible without scrolling
        on both desktop and mobile
      - The order summary sidebar should collapse into an accordion on mobile
      - The price breakdown should show subtotal, tax, and total on separate lines

      ### Settings (`/settings`)
      - All form labels should be left-aligned with their inputs
      - The "Save" button must be reachable at the bottom of the form
      - Tab navigation between sections should update the URL hash
      ```

      Devin reads the diff, identifies which routes were touched, and follows the matching section — so checks are focused rather than generic.
    </Step>

    <Step title="See it catch a real regression">
      Suppose an engineer changes the CSS grid on the dashboard from `grid-template-columns: repeat(3, 1fr)` to `repeat(auto-fit, minmax(200px, 1fr))` to make the layout responsive. The unit tests pass — no logic changed. But on mobile, the cards now overflow the viewport.

      When Devin finishes the code change, it follows the skill automatically:

      1. **Boots the app** — installs dependencies, starts Docker, runs migrations, seeds data, starts the dev server
      2. **Reads the diff** — sees that `src/components/Dashboard.tsx` changed, maps it to the `/dashboard` route
      3. **Screenshots at 1280px** — the 3-column grid renders correctly
      4. **Screenshots at 375px** — spots horizontal overflow: the cards don't fit in the viewport
      5. **Flags the issue** — reports "Horizontal scroll detected on /dashboard at 375px width — metric cards overflow the viewport"
      6. **Fixes the CSS** — adds `overflow-x: hidden` and adjusts the `minmax` minimum to `150px`
      7. **Re-screenshots** — confirms the fix at both widths
      8. **Opens the PR** — includes before/after screenshots and the fix in the description

      You can watch this happen in real time via the browser tab in your [session workspace](/work-with-devin/devin-session-tools).
    </Step>

    <Step title="Extend for your stack">
      Adapt the skill for additional tools and verification steps specific to your project:

      <PromptBlock>
        ```txt Add Storybook component checks theme={null}
        Update the visual regression skill to also run
        `npm run storybook`, open http://localhost:6006, and screenshot
        every story for components modified in the current diff. Flag any
        stories that show a rendering error or blank canvas.
        ```
      </PromptBlock>

      <PromptBlock>
        ```txt Add dark mode verification theme={null}
        Update the visual regression skill to also toggle the app into dark mode
        (click the theme toggle at the top right) and re-screenshot every
        affected page. Flag any text that becomes unreadable against the
        dark background or any elements that lose their border.
        ```
      </PromptBlock>
    </Step>
  </Steps>
</div>
