ANTIGRAVITY LABJP
Articles/Agents & Manager
Agents & Manager/2026-03-29Advanced

to Design Systems × AI Development— Building a Seamless Workflow with UI Stack, AutoLayout, and Variables

Comprehensive system integrating UI Stack, AutoLayout, Variables, and VRT with AI development. Implementation guide from design system architecture to multi-agent automation pipeline and VRT integration.

Design SystemsUI StackAutoLayoutVariablesVRTAI Development7

Premium Article

The fusion of design systems and AI represents the cutting edge of modern development organizations. By systematically integrating UI Stack, AutoLayout, Variables, and VRT, you can realize a development culture where designers, engineers, and AI agents work in perfect harmony. This guide covers the complete implementation patterns and operational philosophy.

UI Stack: The Five-State Framework

UI Stack, proposed by Scott Hurff, is the design principle that every UI element must account for five distinct states:

  1. Ideal State: Data is sufficient and everything functions normally
  2. Loading State: Data is being fetched; skeleton screens or spinners display
  3. Empty State: No data exists or the system is in initial state
  4. Error State: API failures, validation errors, or network issues
  5. Partial State: Only partial data available or incremental loading in progress

Ideal State Implementation Pattern

// components/ProductCard.tsx
interface ProductCardProps {
  product: Product;
  isLoading?: false;
  isEmpty?: false;
  error?: undefined;
}
 
export const ProductCard: React.FC<ProductCardProps> = ({ product }) => {
  return (
    <div style={{ padding: '12px', borderRadius: '8px', border: '1px solid #E5E7EB' }}>
      <img src={product.image} alt={product.name} />
      <h3>{product.name}</h3>
      <p>{product.description}</p>
      <span style={{ color: '#2563EB', fontWeight: 'bold' }}>
        ${product.price.toFixed(2)}
      </span>
    </div>
  );
};

Loading State Implementation Pattern

interface ProductCardProps {
  product?: undefined;
  isLoading: true;
  isEmpty?: false;
  error?: undefined;
}
 
export const ProductCardSkeleton: React.FC = () => {
  return (
    <div style={{ padding: '12px', borderRadius: '8px', backgroundColor: '#F3F4F6' }}>
      <div style={{ width: '100%', height: '200px', backgroundColor: '#E5E7EB', borderRadius: '4px' }} />
      <div style={{ marginTop: '8px', height: '20px', backgroundColor: '#E5E7EB', borderRadius: '4px' }} />
      <div style={{ marginTop: '8px', height: '16px', backgroundColor: '#E5E7EB', width: '80%', borderRadius: '4px' }} />
    </div>
  );
};

Empty State Implementation Pattern

interface ProductCardProps {
  product?: undefined;
  isLoading?: false;
  isEmpty: true;
  error?: undefined;
}
 
export const ProductCardEmpty: React.FC = () => {
  return (
    <div style={{ textAlign: 'center', padding: '32px 12px' }}>
      <p style={{ color: '#6B7280', fontSize: '14px' }}>
        No products available yet.
      </p>
      <p style={{ color: '#9CA3AF', fontSize: '12px' }}>
        Please add a new product.
      </p>
    </div>
  );
};

Error State Implementation Pattern

interface ProductCardProps {
  product?: undefined;
  isLoading?: false;
  isEmpty?: false;
  error: Error;
}
 
export const ProductCardError: React.FC<{ error: Error }> = ({ error }) => {
  return (
    <div style={{ padding: '12px', borderRadius: '8px', backgroundColor: '#FEE2E2', borderLeft: '4px solid #EF4444' }}>
      <p style={{ color: '#991B1B', fontWeight: 'bold' }}>An error occurred</p>
      <p style={{ color: '#7F1D1D', fontSize: '14px' }}>
        {error.message}
      </p>
      <button onClick={() => window.location.reload()} style={{ marginTop: '8px', padding: '6px 12px', backgroundColor: '#EF4444', color: '#FFFFFF', borderRadius: '4px', border: 'none', cursor: 'pointer' }}>
        Retry
      </button>
    </div>
  );
};

Partial State Implementation Pattern

interface ProductCardProps {
  product: Partial<Product>;
  isLoading?: false;
  isEmpty?: false;
  error?: undefined;
}
 
export const ProductCardPartial: React.FC<{ product: Partial<Product> }> = ({ product }) => {
  return (
    <div style={{ padding: '12px', borderRadius: '8px', border: '1px solid #FCD34D' }}>
      {product.image && <img src={product.image} alt={product.name} />}
      <h3>{product.name || 'Loading...'}</h3>
      {product.description ? <p>{product.description}</p> : <p style={{ color: '#9CA3AF' }}>Loading description...</p>}
    </div>
  );
};

VRT (Visual Regression Testing): Leveraging UI Stack with Login States

VRT automatically validates that UI appearance matches intent and has no unintended changes. Combined with UI Stack's five states plus login status, language settings, and dark mode variations, test scenarios expand combinatorially.

VRT Test Scenario Design Example

ProductCard Component
├─ Ideal State
│  ├─ English × Light mode
│  ├─ English × Dark mode
│  ├─ Japanese × Light mode
│  └─ Japanese × Dark mode
├─ Loading State
│  ├─ Light mode
│  └─ Dark mode
├─ Empty State
│  ├─ Light mode
│  └─ Dark mode
├─ Error State
│  ├─ Light mode
│  └─ Dark mode
└─ Partial State
   ├─ Light mode
   └─ Dark mode

Total: 14 patterns

Playwright + Percy (VRT SaaS) Automation

// e2e/product-card.spec.ts
import { test } from '@playwright/test';
import percySnapshot from '@percy/playwright';
 
test.describe('ProductCard VRT Suite', () => {
  test('Ideal State - Light Mode EN', async ({ page }) => {
    await page.goto('/components/product-card?state=ideal&lang=en&theme=light');
    await percySnapshot(page, 'ProductCard-Ideal-Light-EN');
  });
 
  test('Loading State - Dark Mode', async ({ page }) => {
    await page.goto('/components/product-card?state=loading&theme=dark');
    await percySnapshot(page, 'ProductCard-Loading-Dark');
  });
 
  test('Error State - Light Mode', async ({ page }) => {
    await page.goto('/components/product-card?state=error&theme=light');
    await percySnapshot(page, 'ProductCard-Error-Light');
  });
});

Integrated into CI/CD pipelines, visual differences are automatically detected on every PR.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
UI Stack 5-state management: implementation patterns and comprehensive test scenario design
Design system × AI integration strategy: how agents understand design specifications
VRT automation pipeline construction: building an operational system that prioritizes visual quality
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

Agents & Manager2026-08-21
Why I Check Description Overlap Before Turning On inheritCustomizations
CLI 1.1.14 collapsed markdown agent inheritance into a single inheritCustomizations switch. Here is what happened when I actually inventoried the 47 skills in my workspace and decided the switch on description overlap rather than on context size.
Agents & Manager2026-08-17
When a Subagent Never Finishes, Look at the Parent's Unapproved Artifacts First
A subagent that runs all night and never returns is usually not the one that is stuck. Here is how I separate a blocked parent from a blocked child using a short log-scanning script, where the new always proceeds mode helps, and where I stop letting approvals happen automatically.
Agents & Manager2026-08-16
Android 17 Went Canary-Only, So I Gave My Agent the Silence Rules First
Android 17 dropped Developer Previews in favour of rolling Canary builds, which moved the verification deadline onto my side of the table. Here is how I handed the tracking job to an Antigravity agent by designing when it stays quiet, not when it reports.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →