ngxtm

foundry-nextgen-frontend

0
0
# Install this skill:
npx skills add ngxtm/devkit --skill "foundry-nextgen-frontend"

Install specific skill from multi-skill repository

# Description

Build elegant frontend UIs following Microsoft Foundry's NextGen Design System using Vite + React + pnpm + Framer Motion. Use when creating dashboards, agent builders, data grids, entity management interfaces, or any application matching Foundry's refined dark-themed aesthetic.

# SKILL.md


name: foundry-nextgen-frontend
description: Build elegant frontend UIs following Microsoft Foundry's NextGen Design System using Vite + React + pnpm + Framer Motion. Use when creating dashboards, agent builders, data grids, entity management interfaces, or any application matching Foundry's refined dark-themed aesthetic.


Microsoft Foundry NextGen Frontend Skill

Build elegant, production-ready interfaces following Microsoft Foundry's NextGen Design System - a refined neutral dark-themed design language with minimal purple accents and subtle animations.

⚠️ CRITICAL: Color Usage Rules

Purple (#8251EE / #A37EF5) is ONLY for:
- Primary action buttons (filled background)
- Active tab indicators (2px underline)
- Row selection indicators (left border bar)
- Active sidebar navigation icons
- Links and interactive text
- Progress indicators and sliders

Everything else uses NEUTRAL DARK GREYS:
- Backgrounds: Near-black (#0A0A0A, #0D0D0D)
- Cards/Surfaces: Dark grey (#141414, #1C1C1C) with NO visible borders or very subtle ones
- Text: White (#FFFFFF), grey (#A1A1A1), muted (#6B6B6B)
- Borders: Mostly invisible or very subtle (#1F1F1F)

Preferred Stack

pnpm create vite@latest my-foundry-app --template react-ts
cd my-foundry-app
pnpm install
pnpm add framer-motion lucide-react

Required packages:
- framer-motion - for subtle, elegant animations
- lucide-react - for icons

⚠️ CRITICAL: Spacing & Padding Rules

Consistent spacing is non-negotiable. Use the spacing scale:

:root {
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
}

Standard padding patterns:
| Element | Padding |
|---------|---------|
| Page content | 32px (--space-8) |
| Card | 20px (--space-5) |
| Card header | 16px 20px |
| Button | 8px 16px |
| Input | 10px 12px |
| Table cell | 12px 16px |
| Modal body | 24px |
| Badge/Tag | 4px 10px |

Grid gaps:
| Layout | Gap |
|--------|-----|
| Card grid | 16px (--space-4) |
| Form fields | 20px (--space-5) |
| Button group | 12px (--space-3) |
| Tag group | 8px (--space-2) |

Core Design Tokens

:root {
  /* BACKGROUNDS - Neutral Darks */
  --bg-page: #0A0A0A;
  --bg-sidebar: #0D0D0D;
  --bg-card: #141414;
  --bg-surface: #1C1C1C;
  --bg-elevated: #242424;
  --bg-hover: rgba(255, 255, 255, 0.05);
  --bg-active: rgba(255, 255, 255, 0.08);

  /* TEXT */
  --text-primary: #FFFFFF;
  --text-secondary: #A1A1A1;
  --text-muted: #6B6B6B;
  --text-disabled: #4A4A4A;
  --text-link: #A37EF5;

  /* BRAND - Use Sparingly! */
  --brand-primary: #8251EE;
  --brand-hover: #9366F5;
  --brand-light: #A37EF5;

  /* BORDERS - Keep Subtle! */
  --border-subtle: rgba(255, 255, 255, 0.06);
  --border-default: rgba(255, 255, 255, 0.08);
  --border-strong: rgba(255, 255, 255, 0.12);

  /* STATUS */
  --success: #10B981;
  --success-bg: rgba(16, 185, 129, 0.12);
  --warning: #F59E0B;
  --warning-bg: rgba(245, 158, 11, 0.12);
  --error: #EF4444;
  --error-bg: rgba(239, 68, 68, 0.12);
  --info: #3B82F6;
  --info-bg: rgba(59, 130, 246, 0.12);

  /* RADIUS */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;
}

⚠️ CRITICAL: Animation with Framer Motion

Always add subtle animations. Never skip animations.

import { motion } from 'framer-motion';

// Page/container fade in
const pageVariants = {
  hidden: { opacity: 0 },
  visible: { 
    opacity: 1,
    transition: { duration: 0.3, ease: 'easeOut' }
  }
};

// Stagger children (for lists, grids)
const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.05,
      delayChildren: 0.1
    }
  }
};

// Individual item animation
const itemVariants = {
  hidden: { opacity: 0, y: 8 },
  visible: { 
    opacity: 1, 
    y: 0,
    transition: { duration: 0.3, ease: 'easeOut' }
  }
};

// Hover scale for cards/buttons
const hoverScale = {
  whileHover: { scale: 1.01 },
  whileTap: { scale: 0.99 },
  transition: { duration: 0.15 }
};

Standard animation patterns:

// Page wrapper - always animate page entry
<motion.div
  initial="hidden"
  animate="visible"
  variants={pageVariants}
  className="page"
>
  {children}
</motion.div>

// Card grid with stagger
<motion.div
  variants={containerVariants}
  initial="hidden"
  animate="visible"
  className="card-grid"
>
  {items.map(item => (
    <motion.div key={item.id} variants={itemVariants}>
      <Card {...item} />
    </motion.div>
  ))}
</motion.div>

// Interactive card with hover
<motion.div
  className="card"
  whileHover={{ scale: 1.01, backgroundColor: 'rgba(255,255,255,0.02)' }}
  whileTap={{ scale: 0.99 }}
  transition={{ duration: 0.15 }}
>
  {content}
</motion.div>

// Button with press feedback
<motion.button
  className="btn btn-primary"
  whileHover={{ scale: 1.02 }}
  whileTap={{ scale: 0.98 }}
  transition={{ duration: 0.1 }}
>
  Create
</motion.button>

// Modal with backdrop
<motion.div
  className="modal-overlay"
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  exit={{ opacity: 0 }}
>
  <motion.div
    className="modal"
    initial={{ opacity: 0, scale: 0.95, y: 10 }}
    animate={{ opacity: 1, scale: 1, y: 0 }}
    exit={{ opacity: 0, scale: 0.95, y: 10 }}
    transition={{ duration: 0.2, ease: 'easeOut' }}
  >
    {content}
  </motion.div>
</motion.div>

Card Component (Correct Implementation)

Cards should have NO visible borders or very subtle ones.

import { motion } from 'framer-motion';

function Card({ title, description, status, tags, meta }) {
  return (
    <motion.div 
      className="card"
      whileHover={{ 
        scale: 1.01,
        backgroundColor: 'rgba(255, 255, 255, 0.02)'
      }}
      transition={{ duration: 0.15 }}
    >
      <div className="card-header">
        <h3 className="card-title">{title}</h3>
        {status && <StatusBadge status={status} />}
      </div>

      <p className="card-description">{description}</p>

      {tags && (
        <div className="card-tags">
          {tags.map(tag => (
            <span key={tag} className="tag">{tag}</span>
          ))}
        </div>
      )}

      {meta && (
        <div className="card-meta">
          {meta}
        </div>
      )}
    </motion.div>
  );
}
.card {
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  padding: var(--space-5);  /* 20px */
  /* NO border or very subtle */
  border: 1px solid transparent;
  /* Use subtle shadow instead of border */
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

.card:hover {
  /* Subtle border on hover only */
  border-color: var(--border-subtle);
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: var(--space-3);  /* 12px */
}

.card-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

.card-description {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0 0 var(--space-4);  /* 16px bottom */
  line-height: 1.5;
}

.card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);  /* 8px */
  margin-bottom: var(--space-4);
}

.card-meta {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-size: 12px;
  color: var(--text-muted);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-subtle);
}

Tags & Badges

/* Service/integration tags - subtle, not colorful */
.tag {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text-secondary);
}

/* Status badges */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  border-radius: var(--radius-sm);
}

.badge-success {
  background: var(--success-bg);
  color: var(--success);
}

.badge-info {
  background: var(--info-bg);
  color: var(--info);
}

.badge-warning {
  background: var(--warning-bg);
  color: var(--warning);
}

Card Grid Layout

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--space-4);  /* 16px - consistent gap */
  padding: var(--space-8);  /* 32px page padding */
}

/* For fixed 4-column layout */
.card-grid-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
}

/* For 3-column layout */
.card-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

Page Layout with Sidebar

function AppLayout({ children }) {
  return (
    <div className="app-layout">
      <Sidebar />
      <main className="main-content">
        <TopBar />
        <motion.div 
          className="page-content"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ duration: 0.3 }}
        >
          {children}
        </motion.div>
      </main>
    </div>
  );
}
.app-layout {
  display: flex;
  min-height: 100vh;
  background: var(--bg-page);
}

.sidebar {
  width: 56px;
  background: var(--bg-sidebar);
  border-right: 1px solid var(--border-subtle);
  flex-shrink: 0;
}

.main-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.topbar {
  height: 48px;
  background: var(--bg-sidebar);
  border-bottom: 1px solid var(--border-subtle);
  display: flex;
  align-items: center;
  padding: 0 var(--space-6);
}

.page-content {
  flex: 1;
  padding: var(--space-8);  /* 32px consistent padding */
  overflow-y: auto;
}

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-6);  /* 24px before content */
}

.page-title {
  font-size: 20px;
  font-weight: 600;
  color: var(--text-primary);
}

Buttons

<motion.button
  className="btn btn-primary"
  whileHover={{ scale: 1.02 }}
  whileTap={{ scale: 0.98 }}
>
  Create
</motion.button>

<motion.button
  className="btn btn-secondary"
  whileHover={{ scale: 1.02 }}
  whileTap={{ scale: 0.98 }}
>
  Cancel
</motion.button>
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 500;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background 0.15s ease;
}

.btn-primary {
  background: var(--brand-primary);
  color: white;
  border: none;
}

.btn-primary:hover {
  background: var(--brand-hover);
}

.btn-secondary {
  background: transparent;
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
}

.btn-secondary:hover {
  background: var(--bg-hover);
}

Critical Don'ts ❌

  • Don't use visible card borders - cards blend into background
  • Don't use inconsistent padding - follow the spacing scale
  • Don't forget animations - every list, modal, page needs motion
  • Don't use purple for cards/backgrounds - neutral greys only
  • Don't skip hover states - everything interactive needs feedback
  • Don't use hard-coded pixel values - use CSS variables

Critical Do's βœ…

  • Use Framer Motion for all animations
  • Use consistent spacing scale (4, 8, 12, 16, 20, 24, 32px)
  • Use 32px padding for page content areas
  • Use 16px gap for card grids
  • Use transparent or very subtle borders
  • Add hover animations to interactive elements
  • Stagger list/grid item animations

File References

  • Design Tokens: See references/design-tokens.md
  • Components: See references/components.md
  • Patterns: See references/patterns.md

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.