Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CI/CD Integration

Catch design drift before it ships by integrating StyleGrab into your deployment pipeline.

Overview

Add StyleGrab checks to your CI/CD pipeline to:

  • Compare staging against production before deploying
  • Fail builds when unexpected design changes are detected
  • Generate design diff reports on pull requests

GitHub Actions

Basic Check

# .github/workflows/design-check.yml
name: Design Check

on:
  pull_request:
    branches: [main]

jobs:
  design-diff:
    runs-on: ubuntu-latest
    steps:
      - name: Extract from Preview
        id: preview
        run: |
          RESPONSE=$(curl -s -X POST https://api.stylegrab.dev/api/extractions \
            -H "Authorization: Bearer ${{ secrets.STYLEGRAB_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{"url": "${{ env.PREVIEW_URL }}"}')
          echo "extraction_id=$(echo $RESPONSE | jq -r '.id')" >> $GITHUB_OUTPUT
      
      - name: Wait for Extraction
        run: sleep 15
      
      - name: Compare with Production
        id: diff
        run: |
          # Get preview snapshot
          PREVIEW_SNAP=$(curl -s "https://api.stylegrab.dev/api/extractions/${{ steps.preview.outputs.extraction_id }}/snapshots" \
            -H "Authorization: Bearer ${{ secrets.STYLEGRAB_API_KEY }}" | jq -r '.data[0].id')
          
          # Compare against production baseline
          DIFF=$(curl -s "https://api.stylegrab.dev/api/extractions/${{ steps.preview.outputs.extraction_id }}/diff?from=${{ vars.PROD_BASELINE_SNAP }}&to=$PREVIEW_SNAP" \
            -H "Authorization: Bearer ${{ secrets.STYLEGRAB_API_KEY }}")
          
          TOTAL_CHANGES=$(echo $DIFF | jq '.summary.total_changes')
          echo "changes=$TOTAL_CHANGES" >> $GITHUB_OUTPUT
          echo "$DIFF" > diff.json
      
      - name: Comment on PR
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('fs');
            const diff = JSON.parse(fs.readFileSync('diff.json', 'utf8'));
            
            let body = '## 🎨 Design Token Changes\n\n';
            
            if (diff.summary.total_changes === 0) {
              body += '✅ No design token changes detected.';
            } else {
              body += `⚠️ **${diff.summary.total_changes} changes detected**\n\n`;
              body += `| Type | Changes |\n|------|--------|\n`;
              body += `| Colors | ${diff.summary.colors_changed} |\n`;
              body += `| Typography | ${diff.summary.typography_changed} |\n`;
              body += `| Spacing | ${diff.summary.spacing_changed} |\n`;
            }
            
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: body
            });
      
      - name: Fail on Unexpected Changes
        if: steps.diff.outputs.changes > 5
        run: |
          echo "Too many design changes detected (${{ steps.diff.outputs.changes }})"
          exit 1

Required Secrets

Add these to your repository settings:

  • STYLEGRAB_API_KEY — Your API key
  • PROD_BASELINE_SNAP — Snapshot ID of your production baseline

GitLab CI

# .gitlab-ci.yml
design-check:
  stage: test
  image: curlimages/curl:latest
  script:
    - |
      # Start extraction
      EXTRACTION=$(curl -s -X POST https://api.stylegrab.dev/api/extractions \
        -H "Authorization: Bearer $STYLEGRAB_API_KEY" \
        -d "{\"url\": \"$CI_ENVIRONMENT_URL\"}")
      EXTRACTION_ID=$(echo $EXTRACTION | jq -r '.id')
      
      # Wait and get snapshot
      sleep 15
      SNAPSHOT=$(curl -s "https://api.stylegrab.dev/api/extractions/$EXTRACTION_ID/snapshots" \
        -H "Authorization: Bearer $STYLEGRAB_API_KEY" | jq -r '.data[0].id')
      
      # Compare
      DIFF=$(curl -s "https://api.stylegrab.dev/api/extractions/$EXTRACTION_ID/diff?from=$PROD_BASELINE&to=$SNAPSHOT" \
        -H "Authorization: Bearer $STYLEGRAB_API_KEY")
      
      CHANGES=$(echo $DIFF | jq '.summary.total_changes')
      echo "Design changes detected: $CHANGES"
      
      if [ "$CHANGES" -gt 5 ]; then
        echo "Too many design changes. Failing pipeline."
        exit 1
      fi
  only:
    - merge_requests

CircleCI

# .circleci/config.yml
version: 2.1

jobs:
  design-check:
    docker:
      - image: cimg/base:stable
    steps:
      - run:
          name: Check Design Tokens
          command: |
            RESPONSE=$(curl -s -X POST https://api.stylegrab.dev/api/extractions \
              -H "Authorization: Bearer $STYLEGRAB_API_KEY" \
              -d '{"url": "'$PREVIEW_URL'"}')
            
            EXTRACTION_ID=$(echo $RESPONSE | jq -r '.id')
            sleep 15
            
            SNAPSHOT=$(curl -s "https://api.stylegrab.dev/api/extractions/$EXTRACTION_ID/snapshots" \
              -H "Authorization: Bearer $STYLEGRAB_API_KEY" | jq -r '.data[0].id')
            
            CHANGES=$(curl -s "https://api.stylegrab.dev/api/extractions/$EXTRACTION_ID/diff?from=$PROD_BASELINE&to=$SNAPSHOT" \
              -H "Authorization: Bearer $STYLEGRAB_API_KEY" | jq '.summary.total_changes')
            
            echo "Changes: $CHANGES"
            [ "$CHANGES" -le 5 ] || exit 1

workflows:
  version: 2
  test:
    jobs:
      - design-check

Best Practices

1. Use Preview Environments

Always compare against a preview/staging URL, not localhost:

env:
  PREVIEW_URL: https://preview-${{ github.event.pull_request.number }}.yoursite.dev

2. Set Reasonable Thresholds

Start permissive and tighten over time:

  • Warning: > 3 changes
  • Failure: > 10 changes

3. Allow Intentional Changes

Add labels to bypass checks for intentional design updates:

- name: Check for Skip Label
  if: contains(github.event.pull_request.labels.*.name, 'design-update')
  run: echo "Skipping design check - intentional update"

4. Update Baseline After Deploys

# Post-deploy job
update-baseline:
  needs: deploy
  steps:
    - run: |
        EXTRACTION=$(curl -s -X POST https://api.stylegrab.dev/api/extractions \
          -H "Authorization: Bearer $STYLEGRAB_API_KEY" \
          -d '{"url": "https://yoursite.com"}')
        
        sleep 15
        
        SNAPSHOT=$(curl -s "https://api.stylegrab.dev/api/extractions/$(echo $EXTRACTION | jq -r '.id')/snapshots" \
          -H "Authorization: Bearer $STYLEGRAB_API_KEY" | jq -r '.data[0].id')
        
        # Store as new baseline
        gh variable set PROD_BASELINE_SNAP --body "$SNAPSHOT"

Troubleshooting

Extraction timing out

Increase the wait time or use webhooks instead of polling:

- name: Wait for Webhook
  uses: lewagon/wait-on-check-action@v1.3.4
  with:
    ref: ${{ github.ref }}
    check-name: 'stylegrab-extraction'
    wait-interval: 10

Flaky diffs

Some sites have dynamic content. Use selectors to target stable areas:

curl -X POST https://api.stylegrab.dev/api/extractions \
  -d '{
    "url": "https://yoursite.com",
    "options": {
      "selectors": ["#main-content", ".design-system-components"],
      "exclude_selectors": [".ad-banner", ".dynamic-promo"]
    }
  }'

Next Steps