
Figma
- 12 installs
- 638 repo stars
- Updated March 7, 2026
- sundial-org/awesome-openclaw-skills
Helps with design & ui/ux tasks during AI-assisted development.
About
figma is a Claude Code skill for design & ui/ux. It helps solo builders move faster with AI-assisted coding.
- figma
- Design & UI/UX
- AI-coding skill
Figma by the numbers
- 12 all-time installs (skills.sh)
- Ranked #1,432 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sundial-org/awesome-openclaw-skills --skill figmaAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 12 |
|---|---|
| repo stars | ★ 638 |
| Last updated | March 7, 2026 |
| Repository | sundial-org/awesome-openclaw-skills ↗ |
What it does
Helps with design & ui/ux tasks during AI-assisted development.
Files
Figma Design Analysis & Export
Professional-grade Figma integration for design system analysis, asset export, and comprehensive design auditing.
Core Capabilities
1. File Operations & Analysis
- File inspection: Get complete JSON representation of any Figma file
- Component extraction: List all components, styles, and design tokens
- Asset export: Batch export frames, components, or specific nodes as PNG/SVG/PDF
- Version management: Access specific file versions and branch information
Example usage:
- "Export all components from this design system file"
- "Get the JSON data for these specific frames"
- "Show me all the colors and typography used in this file"
2. Design System Management
- Style auditing: Analyze color usage, typography consistency, spacing patterns
- Component analysis: Identify unused components, measure usage patterns
- Brand compliance: Check adherence to brand guidelines across files
- Design token extraction: Generate CSS/JSON design tokens from Figma styles
Example usage:
- "Audit this design system for accessibility issues"
- "Generate CSS custom properties from these Figma styles"
- "Find all inconsistencies in our component library"
3. Bulk Asset Export
- Multi-format exports: Export assets as PNG, SVG, PDF, or WEBP
- Platform-specific sizing: Generate @1x, @2x, @3x assets for iOS/Android
- Organized output: Automatic folder organization by format or platform
- Client packages: Complete deliverable packages with documentation
Example usage:
- "Export all components in PNG and SVG formats"
- "Generate complete asset package for mobile app development"
- "Create client deliverable with all marketing assets"
4. Accessibility & Quality Analysis
- Contrast checking: Verify WCAG color contrast requirements
- Font size analysis: Ensure readable typography scales
- Interactive element sizing: Check touch target requirements
- Focus state validation: Verify keyboard navigation patterns
Example usage:
- "Check this design for WCAG AA compliance"
- "Analyze touch targets for mobile usability"
- "Generate an accessibility report for this app design"
Quick Start
Authentication Setup
# Set your Figma access token
export FIGMA_ACCESS_TOKEN="your-token-here"
# Or store in .env file
echo "FIGMA_ACCESS_TOKEN=your-token" >> .envBasic Operations
# Get file information and structure
python scripts/figma_client.py get-file "your-file-key"
# Export frames as images
python scripts/export_manager.py export-frames "file-key" --formats png,svg
# Analyze design system consistency
python scripts/style_auditor.py audit-file "file-key" --generate-html
# Check accessibility compliance
python scripts/accessibility_checker.py "file-key" --level AA --format htmlWorkflow Patterns
Design System Audit Workflow
1. Extract file data → Get components, styles, and structure 2. Analyze consistency → Check for style variations and unused elements 3. Generate report → Create detailed findings and recommendations 4. Manual implementation → Use findings to guide design improvements
Asset Export Workflow
1. Identify export targets → Specify frames, components, or nodes 2. Configure export settings → Set formats, sizes, and naming conventions 3. Batch process → Export multiple assets simultaneously 4. Organize output → Structure files for handoff or implementation
Analysis & Documentation Workflow
1. Extract design data → Pull components, styles, and design tokens 2. Audit compliance → Check accessibility and brand consistency 3. Generate documentation → Create style guides and component specs 4. Export deliverables → Package assets for development or client handoff
Resources
scripts/
figma_client.py- Complete Figma API wrapper with all REST endpointsexport_manager.py- Professional asset export with multiple formats and scalesstyle_auditor.py- Design system analysis and brand consistency checkingaccessibility_checker.py- Comprehensive WCAG compliance validation and reporting
references/
figma-api-reference.md- Complete API documentation and examplesdesign-patterns.md- UI patterns and component best practicesaccessibility-guidelines.md- WCAG compliance requirementsexport-formats.md- Asset export options and specifications
assets/
templates/design-system/- Pre-built component library templatestemplates/brand-kits/- Standard brand guideline structurestemplates/wireframes/- Common layout patterns and flows
Integration Examples
With Development Workflows
# Generate design tokens for CSS
python scripts/export_manager.py export-tokens "file-key" --format css
# Create component documentation
python scripts/figma_client.py document-components "file-key" --output docs/With Brand Management
# Audit brand compliance in designs
python scripts/style_auditor.py audit-file "file-key" --brand-colors "#FF0000,#00FF00,#0000FF"
# Extract current brand colors for analysis
python scripts/figma_client.py extract-colors "file-key" --output brand-colors.jsonWith Client Deliverables
# Generate client presentation assets
python scripts/export_manager.py client-package "file-key" --template presentation
# Create development handoff assets
python scripts/export_manager.py dev-handoff "file-key" --include-specsLimitations & Scope
Read-Only Operations
This skill provides read-only access to Figma files through the REST API. It can:
- ✅ Extract data, components, and styles
- ✅ Export assets in multiple formats
- ✅ Analyze and audit design files
- ✅ Generate comprehensive reports
What It Cannot Do
- ❌ Modify existing files (colors, text, components)
- ❌ Create new designs or components
- ❌ Batch update multiple files
- ❌ Real-time collaboration features
For file modifications, you would need to develop a Figma plugin using the Plugin API.
Technical Features
API Rate Limiting
Built-in rate limiting and retry logic to handle Figma's API constraints gracefully.
Error Handling
Comprehensive error handling with detailed logging and recovery suggestions.
Multi-Format Support
Export assets in PNG, SVG, PDF, and WEBP with platform-specific sizing.
{
"brand_name": "Your Brand",
"colors": {
"primary": "#007AFF",
"secondary": "#5856D6",
"success": "#34C759",
"warning": "#FF9500",
"error": "#FF3B30",
"neutral_100": "#FFFFFF",
"neutral_200": "#F2F2F7",
"neutral_300": "#C7C7CC",
"neutral_800": "#1C1C1E",
"neutral_900": "#000000"
},
"typography": {
"heading_primary": {
"font_family": "SF Pro Display",
"font_weight": "700",
"font_size": "32px",
"line_height": "1.2"
},
"heading_secondary": {
"font_family": "SF Pro Display",
"font_weight": "600",
"font_size": "24px",
"line_height": "1.25"
},
"body_large": {
"font_family": "SF Pro Text",
"font_weight": "400",
"font_size": "17px",
"line_height": "1.4"
},
"body_regular": {
"font_family": "SF Pro Text",
"font_weight": "400",
"font_size": "15px",
"line_height": "1.4"
},
"caption": {
"font_family": "SF Pro Text",
"font_weight": "400",
"font_size": "12px",
"line_height": "1.3"
}
},
"spacing": {
"xs": "4px",
"sm": "8px",
"md": "16px",
"lg": "24px",
"xl": "32px",
"2xl": "48px",
"3xl": "64px"
},
"border_radius": {
"none": "0px",
"sm": "4px",
"md": "8px",
"lg": "12px",
"xl": "16px",
"full": "9999px"
},
"shadows": {
"sm": "0 1px 2px rgba(0, 0, 0, 0.05)",
"md": "0 4px 6px rgba(0, 0, 0, 0.1)",
"lg": "0 10px 15px rgba(0, 0, 0, 0.1)",
"xl": "0 20px 25px rgba(0, 0, 0, 0.1)"
},
"accessibility": {
"min_contrast_ratio": 4.5,
"min_touch_target": "44px",
"focus_indicator_color": "#007AFF",
"focus_indicator_width": "3px"
}
}Accessibility Guidelines for Figma Design
Comprehensive WCAG compliance guide and accessibility best practices for inclusive design.
WCAG 2.1 Compliance Levels
Level A (Minimum)
Basic web accessibility features that should be present in all designs.
Level AA (Standard)
Recommended level for most websites and applications. Removes major barriers to accessing content.
Level AAA (Enhanced)
Highest level, required for specialized contexts but not recommended as general policy.
Focus on AA compliance - this covers the vast majority of accessibility needs without excessive constraints.
Color and Visual Accessibility
Color Contrast Requirements
WCAG AA Standards
- Normal text: 4.5:1 contrast ratio minimum
- Large text (18pt+ or 14pt+ bold): 3:1 contrast ratio minimum
- UI components: 3:1 contrast ratio for borders, icons, form controls
- Graphics: 3:1 contrast ratio for meaningful graphics
WCAG AAA Standards (Enhanced)
- Normal text: 7:1 contrast ratio
- Large text: 4.5:1 contrast ratio
Testing Tools in Figma
- Stark plugin: Real-time contrast checking
- Color Oracle: Color blindness simulation
- WebAIM contrast checker: External validation
Color Usage Guidelines
Don't Rely on Color Alone
❌ Bad: "Click the green button to continue"
✅ Good: "Click the 'Continue' button (green) to proceed"- Use icons, text labels, or patterns alongside color
- Ensure information is conveyed through multiple visual cues
- Test designs in grayscale to verify information accessibility
Color Blindness Considerations
- Red-green color blindness affects ~8% of men, ~0.5% of women
- Blue-yellow color blindness is less common but still significant
- Use tools like Colorblinding or Stark to test your designs
- Consider using shapes, patterns, or positions as additional indicators
Typography Accessibility
Font Size Guidelines
- Minimum body text: 16px (12pt) for web
- Minimum mobile text: 16px (prevents zoom on iOS)
- Large text threshold: 18pt (24px) regular, 14pt (18.7px) bold
- Line height: 1.5x font size minimum for body text
- Paragraph spacing: At least 1.5x line height
Font Choice
- Sans-serif fonts generally more readable on screens
- Avoid decorative fonts for body text
- System fonts ensure consistency and performance
- Web-safe fonts for broader compatibility
Text Layout
- Line length: 45-75 characters for optimal readability
- Left alignment for left-to-right languages
- Adequate spacing between letters, words, lines, paragraphs
- Avoid justified text which can create awkward spacing
Interactive Element Accessibility
Touch and Click Targets
Size Requirements
- Minimum size: 44x44px (iOS/Material Design standard)
- Recommended size: 48x48px for better usability
- Spacing: At least 8px between adjacent targets
- Mobile considerations: Thumb-friendly zones, easy reach
Visual Feedback
- Hover states: Clear indication of interactive elements
- Active states: Immediate feedback on interaction
- Disabled states: Clearly distinguish non-functional elements
- Loading states: Show progress for time-consuming actions
Focus Management
Focus Indicators
- Visible focus: Clear outline or background change
- High contrast: Focus indicator must have 3:1 contrast ratio
- Consistent style: Same focus treatment across the interface
- Never remove focus indicators without providing alternative
Focus Order
- Logical sequence: Follow visual layout and reading order
- Tab navigation: All interactive elements reachable via keyboard
- Skip links: Allow bypassing repetitive navigation
- Focus traps: Keep focus within modals/dialogs when open
Form Accessibility
Label Requirements
- All inputs must have labels: Use explicit labels, not just placeholders
- Required field indicators: Clear marking of mandatory fields
- Group related fields: Use fieldsets and legends for grouped inputs
- Help text: Provide guidance when needed
Error Handling
❌ Bad: Red border with no explanation
✅ Good: "Email address is required" with clear visual indicator- Specific error messages: Explain what's wrong and how to fix it
- Error summaries: List all errors at top of form for screen readers
- Inline validation: Real-time feedback where helpful
- Success confirmation: Confirm successful form submissions
Form Layout
- Single column layouts: Easier to navigate and complete
- Logical grouping: Related fields grouped together
- Progress indicators: Show steps in multi-step forms
- Clear submission: Make it obvious how to submit the form
Content Structure and Navigation
Heading Hierarchy
Proper Heading Structure
H1 - Page title (one per page)
├── H2 - Main sections
│ ├── H3 - Subsections
│ │ └── H4 - Sub-subsections
│ └── H3 - Another subsection
└── H2 - Another main section- Don't skip levels: H1 → H2 → H3, never H1 → H3
- Use headings for structure: Not just for visual styling
- One H1 per page: Primary page title only
Link Accessibility
Link Text Guidelines
❌ Bad: "Click here for more information"
✅ Good: "Read our complete accessibility guide"- Descriptive link text: Explains where the link leads
- Context independence: Should make sense when read alone
- Unique link text: Different destinations need different text
- External link indicators: Show when links lead off-site
Navigation Patterns
Skip Links
- Skip to main content: Bypass repetitive navigation
- Skip to search: Quick access to search functionality
- Keyboard users: Essential for efficient navigation
- Hidden until focused: Don't clutter visual design
Breadcrumbs
- Show location: Help users understand where they are
- Provide navigation: Easy way to move up the hierarchy
- Current page: Don't make current page a link
- Separator clarity: Use > or / with proper ARIA labels
Images and Media
Image Accessibility
Alt Text Guidelines
- Decorative images: Use empty alt attribute (alt="")
- Informative images: Describe the information conveyed
- Functional images: Describe the action/function
- Complex images: Provide detailed description nearby
Alt Text Examples
❌ Bad: alt="image"
❌ Bad: alt="photo.jpg"
✅ Good: alt="Bar chart showing 40% increase in sales"
✅ Good: alt="Submit form" (for submit button image)
✅ Good: alt="" (for purely decorative images)Video and Audio
Video Accessibility
- Captions: For all spoken content
- Audio descriptions: For visual content not described in audio
- Transcript: Full text version of audio content
- Player controls: Accessible play/pause/volume controls
Audio Accessibility
- Transcripts: For all audio content
- Auto-play restrictions: Avoid auto-playing audio
- Volume controls: User control over audio levels
- Visual indicators: Show when audio is playing
Mobile Accessibility
Touch Interface Guidelines
Gesture Support
- Single-tap primary: Main interaction method
- Alternative access: Provide alternatives to complex gestures
- Gesture hints: Teach users about available gestures
- Gesture conflicts: Avoid conflicts with system gestures
Mobile-Specific Considerations
- Orientation support: Work in both portrait and landscape
- Zoom support: Allow pinch-to-zoom for text content
- Motion sensitivity: Respect reduced motion preferences
- One-handed use: Design for thumb navigation
Screen Reader Support
iOS VoiceOver
- Element labeling: Provide clear, descriptive labels
- Navigation order: Logical focus sequence
- Custom actions: Define available actions for elements
- Notifications: Use announcements for dynamic changes
Android TalkBack
- Content descriptions: Equivalent to alt text for UI elements
- Clickable indicators: Mark interactive elements properly
- Live regions: Announce dynamic content changes
- Semantic markup: Use proper HTML/accessibility semantics
Testing and Validation
Automated Testing Tools
Figma Plugins
- Stark: Comprehensive accessibility checker
- Color Blind Web Page Filter: Color blindness simulation
- Able: Color contrast and font size checker
- A11y - Color Contrast Checker: Quick contrast validation
External Tools
- WebAIM WAVE: Web accessibility evaluation
- axe DevTools: Automated accessibility testing
- Lighthouse: Google's accessibility auditing
- Pa11y: Command-line accessibility testing
Manual Testing Methods
Keyboard Testing
1. Tab navigation: Can you reach all interactive elements? 2. Enter/Space activation: Do buttons and links work? 3. Arrow key navigation: Works in menus and lists? 4. Escape key: Closes modals and menus?
Screen Reader Testing
1. VoiceOver (Mac): System Preferences → Accessibility → VoiceOver 2. NVDA (Windows): Free screen reader for testing 3. JAWS (Windows): Professional screen reader 4. TalkBack (Android): Built-in Android screen reader
Visual Testing
1. Zoom to 200%: Content should remain usable 2. Grayscale mode: Information still accessible? 3. High contrast mode: Text and UI still visible? 4. Color blindness simulation: Information still clear?
User Testing
Include Users with Disabilities
- Recruit diverse participants: Different disabilities and assistive technologies
- Test with real users: Automated tools can't catch everything
- Observe natural usage: Don't guide too much during testing
- Iterate based on feedback: Accessibility is an ongoing process
Testing Scenarios
- First-time usage: Can new users complete key tasks?
- Error recovery: What happens when things go wrong?
- Complex workflows: Multi-step processes accessible?
- Different contexts: Various devices, environments, capabilities
Implementation Guidelines
Designer Handoff
Accessibility Annotations
- Alt text specifications: Document all image alt text
- Focus order notes: Specify tab sequence where non-obvious
- Heading levels: Mark proper heading hierarchy
- Color contrast values: Include specific contrast ratios
- Interactive states: Document all hover/focus/active states
Component Documentation
- Accessibility features: Built-in accessibility considerations
- Usage guidelines: When and how to use accessibly
- ARIA patterns: Required ARIA attributes and roles
- Keyboard interactions: Expected keyboard behavior
Design System Integration
Accessible Components
- Design once, use everywhere: Build accessibility into components
- Default accessibility: Make accessible the easy choice
- Clear documentation: Accessibility requirements in design system
- Regular audits: Review and update component accessibility
Style Guidelines
- Color palettes: Pre-tested for contrast ratios
- Typography scales: Meet minimum size requirements
- Spacing systems: Ensure adequate touch targets
- Icon libraries: Include alt text recommendations
Legal and Compliance
Relevant Laws and Standards
United States
- ADA (Americans with Disabilities Act): Civil rights law
- Section 508: Federal agency accessibility requirements
- WCAG 2.1: Technical standard referenced by many laws
International
- EN 301 549 (European Union): European accessibility standard
- AODA (Ontario): Accessibility for Ontarians with Disabilities Act
- DDA (Australia): Disability Discrimination Act
Risk Mitigation
- Legal compliance: Following WCAG AA reduces legal risk
- Documentation: Keep records of accessibility efforts
- Regular audits: Ongoing compliance checking
- User feedback: Channels for reporting accessibility issues
Resources and Tools
Essential Resources
- WCAG 2.1 Guidelines: Official W3C accessibility standard
- WebAIM: Practical accessibility guidance and tools
- A11y Project: Community-driven accessibility resources
- Inclusive Design Principles: Microsoft's inclusive design guide
Figma-Specific Resources
- Figma Accessibility Guide: Official Figma accessibility documentation
- Accessible Design Systems: Examples of accessible component libraries
- Plugin Directory: Accessibility-focused Figma plugins
- Community Resources: Accessibility templates and examples
Design Patterns & Component Best Practices
Comprehensive guide to UI patterns, component design, and design system best practices for Figma.
Component Architecture
Atomic Design Principles
Atoms (Basic Elements)
- Buttons: Primary, secondary, ghost, icon buttons
- Form inputs: Text fields, selectors, checkboxes, radio buttons
- Typography: Headings, body text, captions, labels
- Icons: Consistent icon library with standardized sizing
- Avatars: User profile images with fallback states
Best Practices:
- Use auto-layout for flexible resizing
- Create consistent hover/focus/disabled states
- Establish clear naming conventions
- Include component documentation
Molecules (Simple Combinations)
- Form groups: Label + input + validation message
- Navigation items: Icon + text + badge
- Card headers: Title + subtitle + actions
- Search bars: Input + search icon + clear button
Best Practices:
- Combine atoms logically and purposefully
- Maintain single responsibility principle
- Use component properties for variations
- Test across different content lengths
Organisms (Complex Combinations)
- Navigation bars: Logo + menu + user profile + search
- Data tables: Headers + rows + pagination + actions
- Product cards: Image + title + price + actions
- Forms: Multiple form groups + buttons + validation
Best Practices:
- Design for responsive behavior
- Consider loading and error states
- Plan for empty states and edge cases
- Optimize for accessibility
Component Naming Conventions
Hierarchical Structure
Component Name / Variant / State
Examples:
- Button / Primary / Default
- Button / Primary / Hover
- Button / Secondary / Disabled
- Input / Text / Error
- Card / Product / LoadingDescriptive Naming
- Use descriptive, action-oriented names
- Avoid technical jargon in user-facing names
- Be consistent across similar components
- Include size/type indicators when helpful
Layout Patterns
Grid Systems
Standard Grid Configurations
- 12-column grid: Most versatile, works for web and mobile
- 8-column grid: Good for tablet layouts
- 4-column grid: Mobile-friendly, simple layouts
- Custom grids: Match specific brand requirements
Grid Properties:
- Consistent gutters (16px, 20px, 24px common)
- Responsive breakpoints (320px, 768px, 1024px, 1440px)
- Maximum content width (1200px-1440px typical)
Auto-Layout Best Practices
- Use auto-layout for all flexible components
- Set appropriate resizing constraints
- Consider padding vs margin usage
- Test with varying content lengths
Common Layout Patterns
Header Patterns
1. Simple header: Logo + navigation + CTA 2. Mega menu: Logo + dropdown navigation + search + account 3. Mobile header: Hamburger + logo + account/cart 4. Dashboard header: Breadcrumbs + title + actions
Content Layouts
1. Single column: Simple, focused content flow 2. Two column: Main content + sidebar 3. Three column: Sidebar + main + secondary sidebar 4. Card grid: Responsive card layouts 5. Masonry: Pinterest-style irregular grid
Footer Patterns
1. Simple footer: Copyright + key links 2. Rich footer: Multiple link columns + social + newsletter 3. Sticky footer: Always at bottom of viewport 4. Fat footer: Extensive links + contact info + sitemap
Interface Patterns
Navigation Patterns
Primary Navigation
- Horizontal nav: Works well for 3-7 main sections
- Vertical sidebar: Good for 8+ items or complex hierarchies
- Tab navigation: For equal-importance sections
- Breadcrumbs: Show hierarchy and allow backtracking
Secondary Navigation
- Dropdown menus: Organize related sub-items
- Contextual sidebars: Show relevant options for current content
- Floating action buttons: Promote primary actions
- Bottom navigation: Mobile-friendly for core functions
Form Patterns
Form Layout
- Single column: Easier to scan and complete
- Label placement: Above fields for better readability
- Required indicators: Use asterisks or "(required)" text
- Help text: Provide when needed, but don't overdo
Input Patterns
- Progressive disclosure: Show additional fields as needed
- Smart defaults: Pre-fill when possible
- Inline validation: Real-time feedback on field completion
- Clear error states: Specific, actionable error messages
Form Actions
- Primary/secondary buttons: Clear visual hierarchy
- Save states: Show progress and confirmation
- Cancel behavior: Ask about unsaved changes
- Multi-step forms: Show progress and allow navigation
Data Display Patterns
Tables
- Sortable headers: Allow data organization
- Pagination: Handle large datasets
- Row actions: Edit, delete, view details
- Selection: Bulk operations capability
- Responsive behavior: Stack or hide columns on mobile
Cards
- Consistent structure: Image + title + metadata + actions
- Hover states: Show additional information or actions
- Loading states: Skeleton screens or progress indicators
- Empty states: Helpful guidance when no content exists
Lists
- Simple lists: Basic text with optional icons
- Rich lists: Multiple lines of information
- Interactive lists: Drag-and-drop, selection
- Infinite scroll: Load more content seamlessly
Responsive Design Patterns
Breakpoint Strategy
Common Breakpoints
- Mobile: 320px - 767px
- Tablet: 768px - 1023px
- Desktop: 1024px - 1439px
- Large desktop: 1440px+
Content Strategy
- Mobile first: Design for constraints, enhance for larger screens
- Progressive enhancement: Add features as screen size allows
- Content parity: Ensure feature availability across devices
- Touch targets: Minimum 44px for mobile interactions
Adaptive Techniques
Navigation Adaptation
- Collapsible menu: Hamburger pattern for mobile
- Priority navigation: Show most important items first
- Overflow menus: "More" option for secondary items
- Tab bar: Bottom navigation for mobile apps
Content Adaptation
- Stacking: Single column on mobile, multiple on desktop
- Content reduction: Progressive disclosure on smaller screens
- Image scaling: Responsive images with appropriate crops
- Typography scaling: Larger text on mobile for readability
Accessibility Patterns
Color and Contrast
- 4.5:1 contrast ratio: Minimum for normal text (WCAG AA)
- 3:1 contrast ratio: Minimum for large text and UI components
- Don't rely on color alone: Use icons, text, or patterns too
- Color blind considerations: Test with color vision simulators
Interaction Patterns
- Focus indicators: Clear visual focus for keyboard navigation
- Touch targets: Minimum 44x44px for touch interfaces
- Click/tap areas: Generous padding around interactive elements
- Hover states: Clear feedback for interactive elements
Content Patterns
- Alt text: Descriptive text for images and icons
- Heading hierarchy: Proper H1-H6 structure
- Link text: Descriptive, avoid "click here"
- Form labels: Clear, descriptive labels for all inputs
Animation and Microinteractions
Animation Principles
- Purposeful motion: Animation should serve a function
- Consistent timing: Use consistent easing and duration
- Respect preferences: Honor reduced motion preferences
- Performance: Smooth 60fps animations
Common Microinteractions
- Button feedback: Subtle scale or color change on press
- Loading indicators: Skeleton screens or spinners
- Success confirmations: Checkmarks or brief messaging
- Error handling: Gentle shake or color change for errors
- Page transitions: Smooth movement between states
Transition Patterns
- Slide transitions: Natural for sequential content
- Fade transitions: Good for overlays and modals
- Scale transitions: Effective for showing/hiding elements
- Morphing transitions: Transform one element into another
Design System Organization
File Structure
Design System/
├── Foundation/
│ ├── Colors
│ ├── Typography
│ ├── Spacing
│ ├── Grid
│ └── Iconography
├── Components/
│ ├── Atoms/
│ ├── Molecules/
│ └── Organisms/
├── Patterns/
│ ├── Navigation
│ ├── Forms
│ ├── Data Display
│ └── Feedback
└── Templates/
├── Landing Pages
├── Dashboard
└── Content PagesDocumentation Standards
- Component purpose: What problem does it solve?
- Usage guidelines: When and how to use
- Do's and don'ts: Clear examples of proper usage
- Accessibility notes: ARIA patterns, keyboard behavior
- Implementation notes: Technical considerations
Maintenance Practices
- Regular audits: Review and update components quarterly
- Usage tracking: Monitor which components are actually used
- Feedback loops: Collect input from designers and developers
- Version control: Clear versioning and change logs
- Testing: Validate components across different contexts
Mobile-Specific Patterns
Touch Interactions
- Tap: Primary interaction method
- Long press: Secondary actions, context menus
- Swipe: Navigation, dismissal actions
- Pinch: Zoom functionality
- Pull to refresh: Common mobile pattern
Mobile Navigation
- Tab bar: 3-5 primary sections
- Hamburger menu: Secondary navigation
- Segmented control: Filter or view switching
- Bottom sheet: Contextual actions and options
Mobile Content
- Card-based layouts: Easy to scan and interact with
- Thumb-friendly zones: Important actions in easy reach
- Generous whitespace: Improve readability and touch accuracy
- Clear hierarchy: Bold typography and visual separation
Export Formats and Specifications
Complete guide to Figma export options, formats, and optimization strategies for different use cases.
Supported Export Formats
Raster Formats
PNG (Portable Network Graphics)
Best for: UI elements, icons with transparency, screenshots
Characteristics:
- Lossless compression
- Supports transparency
- Larger file sizes than JPG
- Perfect for designs with sharp edges
Use cases:
- App icons and UI elements
- Logos with transparency
- Screenshots and mockups
- Print materials requiring transparency
Export settings:
- Scale: 1x, 2x, 3x, 4x
- Recommended: 2x for web, 3x for mobile apps
- Transparent backgrounds supported
JPG (Joint Photographic Experts Group)
Best for: Photographs, complex images, web optimization
Characteristics:
- Lossy compression
- Smaller file sizes
- No transparency support
- Good for photographic content
Use cases:
- Hero images and photography
- Marketing materials
- Email templates
- Web banners where file size matters
Export settings:
- Quality levels available in some tools
- Automatic white background fill
- Scale options: 1x, 2x, 4x
WEBP
Best for: Web optimization, modern browsers
Characteristics:
- Superior compression to PNG/JPG
- Supports transparency and animation
- Smaller file sizes
- Not supported in all browsers
Use cases:
- Web assets for modern browsers
- Progressive web apps
- Performance-critical applications
Vector Formats
SVG (Scalable Vector Graphics)
Best for: Icons, simple illustrations, scalable graphics
Characteristics:
- Infinitely scalable
- Small file sizes for simple graphics
- Editable code
- Supports interactive elements
Use cases:
- Icon libraries
- Simple illustrations
- Logos for web use
- Scalable graphics
Export options:
svg_include_id: Include node IDs for manipulationsvg_simplify_stroke: Optimize stroke paths- Text handling: Convert to paths vs keep as text
PDF (Portable Document Format)
Best for: Print materials, presentations, documentation
Characteristics:
- Vector-based when possible
- High quality for print
- Preserves text and formatting
- Universal compatibility
Use cases:
- Print marketing materials
- Presentations
- Documentation handoff
- High-quality mockups
Export settings:
- Vector elements preserved when possible
- Raster elements included at appropriate resolution
- Text can remain selectable
Export Scales and Resolutions
Device Pixel Ratios
1x (Standard Resolution)
- Use for: Web designs, standard monitors
- Pixel density: 96 DPI
- File size: Smallest
- Quality: Standard
2x (High-DPI)
- Use for: Retina displays, high-DPI web
- Pixel density: 192 DPI
- File size: 4x larger than 1x
- Quality: Sharp on high-DPI screens
3x (Mobile High-DPI)
- Use for: iPhone Plus, Android high-end devices
- Pixel density: 288 DPI
- File size: 9x larger than 1x
- Quality: Extremely sharp mobile displays
4x (Maximum Resolution)
- Use for: Future-proofing, print materials
- Pixel density: 384 DPI
- File size: 16x larger than 1x
- Quality: Maximum detail
Platform-Specific Recommendations
iOS Apps
- 1x: iPhone 3GS and older (rarely needed)
- 2x: iPhone 4-8, iPad non-Retina
- 3x: iPhone 6 Plus and newer large iPhones
- Required: All three scales for App Store submission
Android Apps
- ldpi (0.75x): Low-density screens (rarely used)
- mdpi (1x): Medium-density baseline
- hdpi (1.5x): High-density screens
- xhdpi (2x): Extra high-density
- xxhdpi (3x): Extra extra high-density
- xxxhdpi (4x): Highest density screens
Web Development
- 1x: Base resolution for all browsers
- 2x: For
@2xmedia queries and Retina displays - Consider WEBP: For modern browsers with fallback
Asset Organization Strategies
Folder Structure
By Platform
assets/
├── web/
│ ├── 1x/
│ ├── 2x/
│ └── icons/
├── ios/
│ ├── 1x/
│ ├── 2x/
│ └── 3x/
└── android/
├── ldpi/
├── mdpi/
├── hdpi/
├── xhdpi/
├── xxhdpi/
└── xxxhdpi/By Component Type
assets/
├── icons/
│ ├── navigation/
│ ├── actions/
│ └── status/
├── images/
│ ├── heroes/
│ ├── thumbnails/
│ └── placeholders/
└── logos/
├── full-color/
├── monochrome/
└── reversed/Naming Conventions
Descriptive Naming
✅ Good:
- icon-search-24px.svg
- button-primary-large@2x.png
- hero-homepage-1200w.jpg
❌ Bad:
- icon1.svg
- button.png
- image.jpgPlatform Conventions
iOS:
icon-name.png (1x)
icon-name@2x.png (2x)
icon-name@3x.png (3x)Android:
ic_name.png (mdpi)
ic_name_hdpi.png (hdpi)
ic_name_xhdpi.png (xhdpi)
ic_name_xxhdpi.png (xxhdpi)Web:
icon-name.svg (vector)
icon-name.png (1x fallback)
icon-name@2x.png (2x for Retina)Optimization Techniques
File Size Optimization
PNG Optimization
- Reduce colors: Use 8-bit when possible instead of 24-bit
- Remove metadata: Strip EXIF data and comments
- Optimize palettes: Use indexed color for simple graphics
- Tools: TinyPNG, ImageOptim, OptiPNG
JPG Optimization
- Quality settings: 80-90% for most use cases
- Progressive JPEG: Better perceived loading
- Appropriate dimensions: Don't export larger than needed
- Tools: JPEGmini, ImageOptim, MozJPEG
SVG Optimization
- Simplify paths: Remove unnecessary points
- Group similar elements: Reduce code duplication
- Remove unused definitions: Clean up gradients, styles
- Tools: SVGO, SVGOMG, Figma's built-in optimization
Performance Considerations
Image Dimensions
- Web images: No larger than container size
- 2x images: Exactly 2x the display size
- Responsive images: Multiple sizes for different breakpoints
- Lazy loading: Consider loading strategies
Format Selection Decision Tree
Is it a photograph or complex image?
├── Yes → JPG (or WEBP for modern browsers)
└── No → Does it need transparency?
├── Yes → PNG (or SVG if simple)
└── No → JPG for web, PNG for UI elementsDesign Handoff Specifications
Developer Handoff Assets
Complete Asset Package
1. All required scales: Platform-specific requirements 2. Multiple formats: SVG + PNG fallbacks for icons 3. Organized structure: Clear folder organization 4. Naming documentation: Explain naming conventions 5. Usage guidelines: When to use each asset
Asset Specifications Document
Asset Name: primary-button-large
Formats Available: PNG (1x, 2x, 3x), SVG
Dimensions:
- 1x: 120x44px
- 2x: 240x88px
- 3x: 360x132px
Usage: Primary call-to-action buttons
States: Default, Hover, Active, DisabledDesign System Documentation
Component Assets
- Multiple states: Default, hover, active, disabled, loading
- Size variations: Small, medium, large
- Theme variations: Light mode, dark mode
- Context usage: When and where to use each variation
Icon Libraries
- Consistent sizing: 16px, 24px, 32px standard sizes
- Stroke weights: Consistent line thickness across set
- Style coherence: Same visual style for entire set
- Semantic grouping: Organize by function or category
Batch Export Strategies
Figma Export Tips
Selection-Based Export
1. Select multiple frames/components 2. Use export panel for batch settings 3. Apply same settings to all selected items 4. Export to organized folder structure
Component-Based Workflow
1. Create export-ready components 2. Use consistent naming for automatic organization 3. Set up export settings as part of component definition 4. Use plugins for advanced batch operations
Automation Opportunities
Script-Based Export
- Figma API: Programmatic export control
- Custom tools: Build specific export workflows
- Batch processing: Handle hundreds of assets efficiently
- Quality assurance: Automated optimization and validation
CI/CD Integration
- Automated exports: Trigger on design updates
- Asset deployment: Push directly to CDN or asset pipeline
- Version control: Track asset changes alongside code
- Optimization pipeline: Automatic image optimization
Special Use Cases
App Store Assets
iOS App Store
- App icons: 1024x1024px for store, various sizes for app
- Screenshots: Device-specific dimensions
- Requirements: No transparency, specific format requirements
- Validation: App Store Connect validation rules
Google Play Store
- Feature graphic: 1024x500px
- Screenshots: Various device categories
- App icons: 512x512px high-res icon
- Requirements: Specific aspect ratios and content guidelines
Print Materials
Print Specifications
- Resolution: 300 DPI minimum for professional printing
- Color mode: CMYK for print, RGB for digital
- Bleed areas: Extra space beyond trim line
- Safe areas: Keep important content away from edges
Export Settings
- PDF format: Preferred for print handoff
- High resolution: Use 4x scale or higher
- Color profiles: Include ICC profiles when possible
- Vector preservation: Maintain vector elements where possible
Email Templates
Email Constraints
- Image blocking: Many clients block images by default
- File size limits: Keep images under 100KB when possible
- Fallback text: ALT text for accessibility
- Dimensions: Consider mobile email clients
Optimization Strategy
- JPG for photos: Smaller file sizes
- PNG for UI elements: Crisp edges and transparency
- Inline critical images: Small logos and icons
- CDN hosting: Fast loading from reliable servers
Quality Assurance
Export Validation
Visual Inspection
- Compare to original: Side-by-side comparison
- Different scales: Verify all export scales look correct
- Multiple devices: Test on target devices/browsers
- Print proofs: Physical proofs for print materials
Technical Validation
- File sizes: Reasonable for intended use
- Dimensions: Correct pixel dimensions
- Format compatibility: Works in target environments
- Color accuracy: Colors match design intent
Testing Workflows
Cross-Platform Testing
- Multiple browsers: Chrome, Firefox, Safari, Edge
- Different devices: iOS, Android, various screen sizes
- Operating systems: macOS, Windows, Linux
- Assistive technology: Screen readers, high contrast modes
Performance Testing
- Load times: Measure actual loading performance
- Bandwidth testing: Test on slow connections
- Caching behavior: Verify proper caching headers
- CDN performance: Test global delivery speeds
Figma API Reference
Complete reference for Figma REST API endpoints and Plugin API capabilities.
Authentication
Access Token Setup
1. Generate personal access token: Figma → Settings → Account → Personal Access Tokens 2. For team/organization usage: Create OAuth app for broader access 3. Set environment variable: FIGMA_ACCESS_TOKEN=your_token_here
API Headers
X-Figma-Token: your_access_token_here
Content-Type: application/jsonREST API Endpoints
File Operations
GET /v1/files/:key
Get complete file data including document tree, components, and styles.
Parameters:
version(optional): Specific version IDids(optional): Comma-separated node IDs to limit scopedepth(optional): How deep to traverse document treegeometry(optional): Set to "paths" for vector dataplugin_data(optional): Plugin IDs to include plugin data
Response includes:
- Document tree with all nodes
- Components map with metadata
- Styles map with style definitions
- Version and file metadata
GET /v1/files/:key/nodes
Get specific nodes from a file.
Parameters:
ids(required): Comma-separated node IDsversion,depth,geometry,plugin_data(same as above)
GET /v1/images/:key
Export nodes as images.
Parameters:
ids(required): Node IDs to exportscale(optional): 1, 2, or 4 (default: 1)format(optional): jpg, png, svg, or pdf (default: png)svg_include_id(optional): Include node IDs in SVGsvg_simplify_stroke(optional): Simplify strokes in SVGuse_absolute_bounds(optional): Use absolute coordinatesversion(optional): Specific version to export
Returns: Map of node IDs to image URLs (URLs expire after 30 days)
GET /v1/files/:key/images
Get image fill metadata from a file.
Component Operations
GET /v1/files/:key/components
Get all components in a file.
GET /v1/components/:key
Get component metadata by component key.
GET /v1/teams/:team_id/components
Get team component library.
Parameters:
page_size(optional): Results per page (max 1000)after(optional): Pagination cursor
Style Operations
GET /v1/files/:key/styles
Get all styles in a file.
GET /v1/styles/:key
Get style metadata by style key.
GET /v1/teams/:team_id/styles
Get team style library.
Project Operations
GET /v1/teams/:team_id/projects
Get projects for a team.
GET /v1/projects/:project_id/files
Get files in a project.
User Operations
GET /v1/me
Get current user information.
Rate Limits
- 1000 requests per minute per access token
- Image exports: 100 requests per minute
- Use exponential backoff for 429 responses
- Monitor
X-RateLimit-*headers
Error Handling
Common HTTP Status Codes
400 Bad Request: Invalid parameters401 Unauthorized: Invalid or missing access token403 Forbidden: Insufficient permissions404 Not Found: File or resource doesn't exist429 Too Many Requests: Rate limit exceeded500 Internal Server Error: Figma server error
Error Response Format
{
"status": 400,
"err": "Bad request: Invalid file key"
}Node Types
Document Structure
DOCUMENT- Root document nodeCANVAS- Page/canvas nodeFRAME- Frame containerGROUP- Group containerSECTION- Section container
Shape Nodes
RECTANGLE- Rectangle shapeLINE- Line shapeELLIPSE- Ellipse shapePOLYGON- Polygon shapeSTAR- Star shapeVECTOR- Vector shape
Text and Components
TEXT- Text nodeCOMPONENT- Master componentCOMPONENT_SET- Component set (variants)INSTANCE- Component instance
Special Nodes
BOOLEAN_OPERATION- Boolean operation resultSLICE- Export sliceSTICKY- Sticky note (FigJam)CONNECTOR- Connector line (FigJam)
Plugin API Overview
The Plugin API allows creating, modifying, and analyzing design files through plugins.
Key Capabilities
- Create nodes: Generate frames, shapes, text, components
- Modify properties: Update fills, strokes, effects, layout
- Component management: Create/update components and instances
- Style operations: Create and apply text/fill/effect styles
- File operations: Navigate pages, selection, document structure
Plugin API Limitations
- Runs in browser sandbox environment
- Cannot directly access external APIs (use UI for HTTP requests)
- Limited file system access
- Must be installed/authorized by users
Common Plugin Patterns
Creating Basic Shapes
// Create rectangle
const rect = figma.createRectangle();
rect.resize(100, 100);
rect.fills = [{type: 'SOLID', color: {r: 1, g: 0, b: 0}}];
// Create text
const text = figma.createText();
await figma.loadFontAsync(text.fontName);
text.characters = "Hello World";Working with Components
// Create component
const component = figma.createComponent();
component.name = "Button";
// Create instance
const instance = component.createInstance();Traversing Document Tree
function traverse(node) {
console.log(node.name, node.type);
if ("children" in node) {
for (const child of node.children) {
traverse(child);
}
}
}
traverse(figma.root);Best Practices
API Usage
1. Batch operations: Group multiple API calls when possible 2. Cache results: Store file data to minimize repeat requests 3. Use specific node IDs: Limit data transfer with ids parameter 4. Handle rate limits: Implement exponential backoff 5. Version awareness: Use version parameter for consistency
Image Exports
1. Choose appropriate format: PNG for complex images, SVG for icons 2. Optimize scale: Use scale=1 unless high-DPI needed 3. Batch exports: Export multiple nodes in single request 4. Cache URLs: Store image URLs but remember 30-day expiration
Plugin Development
1. Minimize processing: Keep operations fast to avoid timeouts 2. Progress feedback: Show progress for long operations 3. Error handling: Gracefully handle missing fonts, permissions 4. Memory management: Clean up large data structures 5. User consent: Request permissions appropriately
Security
1. Token protection: Never expose access tokens in client-side code 2. Scope principle: Use minimal required permissions 3. Input validation: Validate all user inputs and API responses 4. Audit logs: Track API usage for compliance
Common Use Cases
Design System Automation
- Extract design tokens (colors, typography, spacing)
- Generate code from components
- Sync design systems across files
- Audit design consistency
Asset Generation
- Export marketing assets in multiple formats
- Generate app icons and favicons
- Create social media templates
- Produce print-ready assets
Workflow Integration
- Connect designs to development tools
- Automate handoff documentation
- Version control for design files
- Collaborative review processes
Quality Assurance
- Accessibility compliance checking
- Brand guideline validation
- Consistency auditing across projects
- Performance optimization recommendations
requests>=2.31.0
aiohttp>=3.9.0
pathlib#!/usr/bin/env python3
"""
Figma Accessibility Checker - WCAG compliance validation
Specialized accessibility audit with detailed WCAG compliance checking.
"""
import os
import sys
import json
import math
import time
from typing import Dict, List, Optional, Union, Any, Tuple
import argparse
try:
from figma_client import FigmaClient
except ImportError:
# Handle case where script is run directly
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).parent))
from figma_client import FigmaClient
class AccessibilityChecker:
"""WCAG-focused accessibility checker for Figma designs"""
def __init__(self, figma_client: FigmaClient):
self.client = figma_client
def check_wcag_compliance(self, file_key: str, level: str = 'AA') -> Dict[str, Any]:
"""Comprehensive WCAG compliance check"""
print(f"Checking WCAG {level} compliance for file: {file_key}")
file_data = self.client.get_file(file_key)
results = {
'file_key': file_key,
'file_name': file_data.get('name', 'Unknown'),
'wcag_level': level,
'timestamp': time.time(),
'compliance_score': 0,
'issues': [],
'summary': {}
}
# Run all WCAG checks
self._check_color_contrast(file_data, results, level)
self._check_touch_targets(file_data, results)
self._check_text_sizing(file_data, results)
self._check_focus_indicators(file_data, results)
# Calculate compliance score
results['compliance_score'] = self._calculate_compliance_score(results)
results['summary'] = self._generate_summary(results)
return results
def _check_color_contrast(self, file_data: Dict[str, Any], results: Dict[str, Any], level: str):
"""Check color contrast ratios against WCAG standards"""
contrast_requirements = {
'AA': {'normal_text': 4.5, 'large_text': 3.0, 'ui_components': 3.0},
'AAA': {'normal_text': 7.0, 'large_text': 4.5, 'ui_components': 4.5}
}
requirements = contrast_requirements[level]
def check_node_contrast(node):
if node.get('type') == 'TEXT':
# Get text color
fills = node.get('fills', [])
if not fills:
return
text_color = fills[0].get('color', {})
if not text_color:
return
# Estimate background color (simplified - would need parent analysis)
bg_color = {'r': 1, 'g': 1, 'b': 1} # Assume white background
contrast_ratio = self._calculate_contrast_ratio(text_color, bg_color)
# Determine if text is large
style = node.get('style', {})
font_size = style.get('fontSize', 16)
font_weight = style.get('fontWeight', 400)
is_large_text = font_size >= 18 or (font_size >= 14 and font_weight >= 700)
required_ratio = requirements['large_text'] if is_large_text else requirements['normal_text']
if contrast_ratio < required_ratio:
results['issues'].append({
'type': 'color_contrast',
'severity': 'error' if level == 'AA' else 'warning',
'message': f'Insufficient contrast: {contrast_ratio:.1f}:1 (required: {required_ratio}:1)',
'node_id': node.get('id'),
'node_name': node.get('name', ''),
'wcag_criterion': '1.4.3' if level == 'AA' else '1.4.6',
'details': {
'contrast_ratio': contrast_ratio,
'required_ratio': required_ratio,
'text_color': self._rgb_to_hex(text_color),
'is_large_text': is_large_text
}
})
# Check children
for child in node.get('children', []):
check_node_contrast(child)
if 'document' in file_data:
check_node_contrast(file_data['document'])
def _check_touch_targets(self, file_data: Dict[str, Any], results: Dict[str, Any]):
"""Check minimum touch target sizes (WCAG 2.5.5)"""
min_size = 44 # iOS/WCAG standard
def check_node_size(node):
# Look for interactive elements
node_name = node.get('name', '').lower()
node_type = node.get('type', '')
is_interactive = (
'button' in node_name or
'link' in node_name or
node_type in ['COMPONENT', 'INSTANCE'] and
any(keyword in node_name for keyword in ['btn', 'tap', 'click', 'interactive'])
)
if is_interactive:
bounds = node.get('absoluteBoundingBox', {})
width = bounds.get('width', 0)
height = bounds.get('height', 0)
if width < min_size or height < min_size:
results['issues'].append({
'type': 'touch_target',
'severity': 'warning',
'message': f'Touch target too small: {width:.0f}×{height:.0f}px (minimum: {min_size}×{min_size}px)',
'node_id': node.get('id'),
'node_name': node.get('name', ''),
'wcag_criterion': '2.5.5',
'details': {
'width': width,
'height': height,
'min_size': min_size
}
})
# Check children
for child in node.get('children', []):
check_node_size(child)
if 'document' in file_data:
check_node_size(file_data['document'])
def _check_text_sizing(self, file_data: Dict[str, Any], results: Dict[str, Any]):
"""Check minimum text sizes for readability"""
min_size = 12 # Minimum readable size
recommended_size = 16 # Recommended for body text
def check_text_size(node):
if node.get('type') == 'TEXT':
style = node.get('style', {})
font_size = style.get('fontSize', 16)
if font_size < min_size:
results['issues'].append({
'type': 'text_size',
'severity': 'error',
'message': f'Text too small: {font_size}px (minimum: {min_size}px)',
'node_id': node.get('id'),
'node_name': node.get('name', ''),
'wcag_criterion': '1.4.4',
'details': {
'font_size': font_size,
'min_size': min_size,
'characters': node.get('characters', '')[:50]
}
})
elif font_size < recommended_size:
results['issues'].append({
'type': 'text_size',
'severity': 'info',
'message': f'Text smaller than recommended: {font_size}px (recommended: {recommended_size}px)',
'node_id': node.get('id'),
'node_name': node.get('name', ''),
'wcag_criterion': '1.4.4',
'details': {
'font_size': font_size,
'recommended_size': recommended_size
}
})
# Check children
for child in node.get('children', []):
check_text_size(child)
if 'document' in file_data:
check_text_size(file_data['document'])
def _check_focus_indicators(self, file_data: Dict[str, Any], results: Dict[str, Any]):
"""Check for focus indicators on interactive elements"""
def check_focus_states(node):
node_name = node.get('name', '').lower()
node_type = node.get('type', '')
is_interactive = (
'button' in node_name or
'link' in node_name or
'input' in node_name or
node_type in ['COMPONENT', 'INSTANCE']
)
if is_interactive:
# Check for focus-related effects or states
effects = node.get('effects', [])
has_focus_indicator = any(
'focus' in str(effect).lower() or
effect.get('type') == 'DROP_SHADOW'
for effect in effects
)
if not has_focus_indicator:
results['issues'].append({
'type': 'focus_indicator',
'severity': 'info',
'message': 'Interactive element may need focus indicator',
'node_id': node.get('id'),
'node_name': node.get('name', ''),
'wcag_criterion': '2.4.7',
'details': {
'suggestion': 'Add visible focus state for keyboard navigation'
}
})
# Check children
for child in node.get('children', []):
check_focus_states(child)
if 'document' in file_data:
check_focus_states(file_data['document'])
def _calculate_contrast_ratio(self, color1: Dict[str, float], color2: Dict[str, float]) -> float:
"""Calculate WCAG contrast ratio between two colors"""
def get_luminance(color):
def linearize(val):
if val <= 0.03928:
return val / 12.92
else:
return pow((val + 0.055) / 1.055, 2.4)
r = linearize(color.get('r', 0))
g = linearize(color.get('g', 0))
b = linearize(color.get('b', 0))
return 0.2126 * r + 0.7152 * g + 0.0722 * b
lum1 = get_luminance(color1)
lum2 = get_luminance(color2)
lighter = max(lum1, lum2)
darker = min(lum1, lum2)
return (lighter + 0.05) / (darker + 0.05)
def _rgb_to_hex(self, color: Dict[str, float]) -> str:
"""Convert RGB color to hex string"""
r = int(color.get('r', 0) * 255)
g = int(color.get('g', 0) * 255)
b = int(color.get('b', 0) * 255)
return f"#{r:02x}{g:02x}{b:02x}"
def _calculate_compliance_score(self, results: Dict[str, Any]) -> int:
"""Calculate overall compliance score (0-100)"""
error_count = len([i for i in results['issues'] if i['severity'] == 'error'])
warning_count = len([i for i in results['issues'] if i['severity'] == 'warning'])
info_count = len([i for i in results['issues'] if i['severity'] == 'info'])
# Scoring: errors are -10 points, warnings -3 points, info -1 point
penalty = error_count * 10 + warning_count * 3 + info_count * 1
score = max(0, 100 - penalty)
return score
def _generate_summary(self, results: Dict[str, Any]) -> Dict[str, Any]:
"""Generate summary of accessibility results"""
issues_by_type = {}
issues_by_severity = {'error': 0, 'warning': 0, 'info': 0}
for issue in results['issues']:
issue_type = issue['type']
severity = issue['severity']
issues_by_type[issue_type] = issues_by_type.get(issue_type, 0) + 1
issues_by_severity[severity] += 1
compliance_level = 'FAIL'
if issues_by_severity['error'] == 0:
if issues_by_severity['warning'] == 0:
compliance_level = 'AAA'
elif issues_by_severity['warning'] <= 2:
compliance_level = 'AA'
else:
compliance_level = 'A'
return {
'total_issues': len(results['issues']),
'issues_by_type': issues_by_type,
'issues_by_severity': issues_by_severity,
'compliance_level': compliance_level,
'score': results['compliance_score']
}
def generate_accessibility_report(self, results: Dict[str, Any], output_path: str = None) -> str:
"""Generate detailed accessibility report"""
if not output_path:
output_path = f"accessibility-report-{int(time.time())}.html"
html_report = self._create_accessibility_html_report(results)
with open(output_path, 'w') as f:
f.write(html_report)
print(f"Accessibility report generated: {output_path}")
return output_path
def _create_accessibility_html_report(self, results: Dict[str, Any]) -> str:
"""Create comprehensive HTML accessibility report"""
# Color coding for compliance levels
level_colors = {
'AAA': '#28a745',
'AA': '#17a2b8',
'A': '#ffc107',
'FAIL': '#dc3545'
}
level_color = level_colors.get(results['summary']['compliance_level'], '#6c757d')
html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accessibility Report - {results['file_name']}</title>
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
margin: 40px;
line-height: 1.6;
color: #333;
}}
.header {{
border-bottom: 3px solid {level_color};
padding-bottom: 20px;
margin-bottom: 30px;
}}
.compliance-badge {{
display: inline-block;
background: {level_color};
color: white;
padding: 10px 20px;
border-radius: 25px;
font-weight: bold;
font-size: 18px;
margin: 10px 0;
}}
.score {{
font-size: 48px;
font-weight: bold;
color: {level_color};
}}
.summary {{
background: #f8f9fa;
padding: 25px;
border-radius: 8px;
margin-bottom: 30px;
border-left: 5px solid {level_color};
}}
.issue {{
margin-bottom: 25px;
padding: 20px;
border-left: 4px solid #ddd;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}}
.error {{ border-left-color: #dc3545; background: #f8d7da; }}
.warning {{ border-left-color: #ffc107; background: #fff3cd; }}
.info {{ border-left-color: #17a2b8; background: #d1ecf1; }}
.wcag-criterion {{
background: #e9ecef;
padding: 4px 8px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
}}
.node-info {{
color: #6c757d;
font-size: 14px;
margin-top: 5px;
}}
.stats {{
display: flex;
gap: 20px;
margin: 20px 0;
}}
.stat {{
background: white;
padding: 15px;
border-radius: 8px;
text-align: center;
border: 1px solid #dee2e6;
}}
.stat-number {{
font-size: 24px;
font-weight: bold;
color: {level_color};
}}
.recommendations {{
background: #e7f3ff;
border: 1px solid #b8daff;
border-radius: 8px;
padding: 20px;
margin: 30px 0;
}}
</style>
</head>
<body>
<div class="header">
<h1>🔍 Accessibility Report</h1>
<p><strong>File:</strong> {results['file_name']}</p>
<p><strong>WCAG Level:</strong> {results['wcag_level']}</p>
<p><strong>Generated:</strong> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(results['timestamp']))}</p>
<div class="compliance-badge">
WCAG {results['summary']['compliance_level']} Compliance
</div>
</div>
<div class="summary">
<h2>📊 Summary</h2>
<div class="stats">
<div class="stat">
<div class="stat-number">{results['summary']['score']}</div>
<div>Score</div>
</div>
<div class="stat">
<div class="stat-number">{results['summary']['total_issues']}</div>
<div>Total Issues</div>
</div>
<div class="stat">
<div class="stat-number">{results['summary']['issues_by_severity']['error']}</div>
<div>Errors</div>
</div>
<div class="stat">
<div class="stat-number">{results['summary']['issues_by_severity']['warning']}</div>
<div>Warnings</div>
</div>
</div>
</div>
"""
if results['issues']:
html += "<h2>🐛 Issues Found</h2>\n"
for issue in results['issues']:
severity_class = issue['severity']
html += f"""
<div class="issue {severity_class}">
<h3>{issue['type'].replace('_', ' ').title()}: {issue['message']}</h3>
<span class="wcag-criterion">WCAG {issue['wcag_criterion']}</span>
<div class="node-info">
<strong>Element:</strong> {issue.get('node_name', 'N/A')}
(ID: {issue.get('node_id', 'N/A')})
</div>
"""
if 'details' in issue and issue['details']:
html += "<div style='margin-top: 10px;'><strong>Details:</strong><ul>"
for key, value in issue['details'].items():
html += f"<li><strong>{key.replace('_', ' ').title()}:</strong> {value}</li>"
html += "</ul></div>"
html += "</div>\n"
else:
html += """
<div class="recommendations">
<h2>🎉 Excellent Work!</h2>
<p>No accessibility issues found in this design. This indicates strong adherence to WCAG guidelines.</p>
</div>
"""
html += """
<div class="recommendations">
<h2>💡 Recommendations</h2>
<ul>
<li><strong>Manual Testing:</strong> Automated checks catch many issues, but manual testing with assistive technologies is still essential.</li>
<li><strong>User Testing:</strong> Include users with disabilities in your testing process.</li>
<li><strong>Regular Audits:</strong> Run accessibility checks throughout the design process, not just at the end.</li>
<li><strong>Design System:</strong> Build accessibility into your component library to prevent issues.</li>
</ul>
</div>
<div style="margin-top: 40px; padding-top: 20px; border-top: 1px solid #dee2e6; color: #6c757d; font-size: 14px;">
<p>Generated by Figma Accessibility Checker | Learn more about WCAG at <a href="https://www.w3.org/WAI/WCAG21/quickref/">WCAG Quick Reference</a></p>
</div>
</body>
</html>"""
return html
def main():
"""CLI interface for accessibility checking"""
parser = argparse.ArgumentParser(description='Figma Accessibility Checker')
parser.add_argument('file_key', help='Figma file key or URL')
parser.add_argument('--level', choices=['AA', 'AAA'], default='AA', help='WCAG compliance level')
parser.add_argument('--output', help='Output file for accessibility report')
parser.add_argument('--format', choices=['json', 'html'], default='json', help='Output format')
args = parser.parse_args()
try:
client = FigmaClient()
checker = AccessibilityChecker(client)
file_key = client.parse_file_url(args.file_key)
results = checker.check_wcag_compliance(file_key, args.level)
if args.format == 'html':
output_path = args.output or f"accessibility-report-{file_key}.html"
checker.generate_accessibility_report(results, output_path)
else:
output_content = json.dumps(results, indent=2)
if args.output:
with open(args.output, 'w') as f:
f.write(output_content)
print(f"Accessibility results saved to {args.output}")
else:
print(output_content)
# Print summary
print(f"\n🔍 Accessibility Summary:")
print(f" Score: {results['summary']['score']}/100")
print(f" Compliance Level: WCAG {results['summary']['compliance_level']}")
print(f" Total Issues: {results['summary']['total_issues']}")
print(f" Errors: {results['summary']['issues_by_severity']['error']}")
print(f" Warnings: {results['summary']['issues_by_severity']['warning']}")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()#!/usr/bin/env python3
"""
Figma Export Manager - Batch asset export with intelligent organization
Handles multiple formats, naming conventions, and export workflows.
"""
import os
import sys
import json
import asyncio
import aiohttp
from pathlib import Path
from typing import Dict, List, Optional, Union, Any
from dataclasses import dataclass, field
from concurrent.futures import ThreadPoolExecutor
import argparse
import time
try:
from figma_client import FigmaClient
except ImportError:
# Handle case where script is run directly
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).parent))
from figma_client import FigmaClient
@dataclass
class ExportConfig:
"""Configuration for export operations"""
formats: List[str] = field(default_factory=lambda: ['png'])
scales: List[float] = field(default_factory=lambda: [1.0])
output_dir: str = './figma-exports'
naming_pattern: str = '{name}_{id}.{format}'
create_manifest: bool = True
skip_existing: bool = False
max_concurrent: int = 5
organize_by_format: bool = True
class ExportManager:
"""Professional-grade Figma asset export manager"""
def __init__(self, figma_client: FigmaClient, config: ExportConfig = None):
self.client = figma_client
self.config = config or ExportConfig()
# Create output directory
Path(self.config.output_dir).mkdir(parents=True, exist_ok=True)
def export_frames(self, file_key: str, frame_ids: List[str] = None,
frame_names: List[str] = None) -> Dict[str, Any]:
"""Export all frames or specific frames from a file"""
# Get file data to identify frames
file_data = self.client.get_file(file_key)
if not frame_ids and not frame_names:
# Export all frames
frame_nodes = self._find_frames(file_data)
else:
# Filter specific frames
all_frames = self._find_frames(file_data)
frame_nodes = []
for frame in all_frames:
if frame_ids and frame['id'] in frame_ids:
frame_nodes.append(frame)
elif frame_names and frame['name'] in frame_names:
frame_nodes.append(frame)
if not frame_nodes:
print("No frames found to export")
return {'exported': 0, 'files': []}
print(f"Found {len(frame_nodes)} frames to export")
return self._export_nodes(file_key, frame_nodes)
def export_components(self, file_key: str, component_names: List[str] = None) -> Dict[str, Any]:
"""Export all components or specific components from a file"""
file_data = self.client.get_file(file_key)
component_nodes = self._find_components(file_data)
if component_names:
component_nodes = [c for c in component_nodes if c['name'] in component_names]
if not component_nodes:
print("No components found to export")
return {'exported': 0, 'files': []}
print(f"Found {len(component_nodes)} components to export")
return self._export_nodes(file_key, component_nodes)
def export_pages(self, file_key: str, page_names: List[str] = None) -> Dict[str, Any]:
"""Export all pages or specific pages as complete images"""
file_data = self.client.get_file(file_key)
pages = []
for child in file_data.get('document', {}).get('children', []):
if child.get('type') == 'CANVAS':
if not page_names or child.get('name') in page_names:
pages.append(child)
if not pages:
print("No pages found to export")
return {'exported': 0, 'files': []}
print(f"Found {len(pages)} pages to export")
return self._export_nodes(file_key, pages)
def export_custom_selection(self, file_key: str, node_ids: List[str]) -> Dict[str, Any]:
"""Export specific nodes by ID"""
# Get node information
nodes_data = self.client.get_file_nodes(file_key, node_ids)
if 'nodes' not in nodes_data:
print("No nodes found with provided IDs")
return {'exported': 0, 'files': []}
nodes = []
for node_id, node_info in nodes_data['nodes'].items():
if 'document' in node_info:
node = node_info['document']
node['id'] = node_id # Ensure ID is present
nodes.append(node)
print(f"Found {len(nodes)} nodes to export")
return self._export_nodes(file_key, nodes)
def export_design_tokens(self, file_key: str, output_format: str = 'json') -> str:
"""Export design tokens (colors, typography, effects) in various formats"""
file_data = self.client.get_file(file_key)
# Extract design tokens
tokens = {
'colors': self._extract_color_tokens(file_data),
'typography': self._extract_typography_tokens(file_data),
'effects': self._extract_effect_tokens(file_data),
'spacing': self._extract_spacing_tokens(file_data)
}
# Format output
if output_format == 'css':
output_content = self._tokens_to_css(tokens)
output_file = Path(self.config.output_dir) / 'design-tokens.css'
elif output_format == 'scss':
output_content = self._tokens_to_scss(tokens)
output_file = Path(self.config.output_dir) / 'design-tokens.scss'
elif output_format == 'js':
output_content = self._tokens_to_js(tokens)
output_file = Path(self.config.output_dir) / 'design-tokens.js'
else: # json
output_content = json.dumps(tokens, indent=2)
output_file = Path(self.config.output_dir) / 'design-tokens.json'
# Write output file
with open(output_file, 'w') as f:
f.write(output_content)
print(f"Design tokens exported to {output_file}")
return str(output_file)
def create_client_package(self, file_key: str, package_name: str = None) -> str:
"""Create a complete client delivery package with all assets"""
if not package_name:
file_data = self.client.get_file(file_key)
package_name = file_data.get('name', 'figma-package').replace(' ', '-').lower()
package_dir = Path(self.config.output_dir) / package_name
package_dir.mkdir(parents=True, exist_ok=True)
# Export all asset types
results = {}
# 1. Export all frames
self.config.output_dir = str(package_dir / 'frames')
Path(self.config.output_dir).mkdir(exist_ok=True)
results['frames'] = self.export_frames(file_key)
# 2. Export all components
self.config.output_dir = str(package_dir / 'components')
Path(self.config.output_dir).mkdir(exist_ok=True)
results['components'] = self.export_components(file_key)
# 3. Export design tokens
self.config.output_dir = str(package_dir)
results['tokens'] = {
'json': self.export_design_tokens(file_key, 'json'),
'css': self.export_design_tokens(file_key, 'css'),
'scss': self.export_design_tokens(file_key, 'scss')
}
# 4. Create documentation
doc_file = package_dir / 'README.md'
self._create_package_documentation(file_key, doc_file, results)
print(f"Client package created at {package_dir}")
return str(package_dir)
def _export_nodes(self, file_key: str, nodes: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Export nodes with all configured formats and scales"""
exported_files = []
total_exports = len(nodes) * len(self.config.formats) * len(self.config.scales)
current_export = 0
for node in nodes:
node_id = node['id']
node_name = self._sanitize_filename(node.get('name', 'untitled'))
for format in self.config.formats:
for scale in self.config.scales:
current_export += 1
print(f"Exporting {current_export}/{total_exports}: {node_name} ({format} @ {scale}x)")
# Get export URLs from Figma
try:
export_data = self.client.export_images(
file_key, [node_id],
format=format, scale=scale
)
if 'images' in export_data and node_id in export_data['images']:
image_url = export_data['images'][node_id]
if image_url:
# Generate filename
filename = self.config.naming_pattern.format(
name=node_name,
id=node_id,
format=format,
scale=f'{scale}x' if scale != 1.0 else ''
)
# Organize by format if configured
if self.config.organize_by_format:
format_dir = Path(self.config.output_dir) / format
format_dir.mkdir(exist_ok=True)
output_path = format_dir / filename
else:
output_path = Path(self.config.output_dir) / filename
# Skip if file exists and skip_existing is True
if self.config.skip_existing and output_path.exists():
print(f" Skipping existing file: {output_path}")
continue
# Download the image
self.client.download_image(str(image_url), str(output_path))
exported_files.append({
'path': str(output_path),
'node_id': node_id,
'node_name': node_name,
'format': format,
'scale': scale,
'url': image_url
})
print(f" Saved: {output_path}")
else:
print(f" Warning: No image URL returned for {node_name}")
except Exception as e:
print(f" Error exporting {node_name}: {e}")
continue
# Rate limiting
time.sleep(0.5)
# Create manifest file
if self.config.create_manifest:
manifest_path = Path(self.config.output_dir) / 'export-manifest.json'
manifest_data = {
'exported_at': time.strftime('%Y-%m-%d %H:%M:%S'),
'file_key': file_key,
'total_files': len(exported_files),
'config': {
'formats': self.config.formats,
'scales': self.config.scales,
'naming_pattern': self.config.naming_pattern
},
'files': exported_files
}
with open(manifest_path, 'w') as f:
json.dump(manifest_data, f, indent=2)
print(f"Manifest created: {manifest_path}")
return {
'exported': len(exported_files),
'files': exported_files
}
def _find_frames(self, file_data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Find all frames in the file"""
frames = []
def traverse_node(node):
if node.get('type') == 'FRAME':
frames.append(node)
for child in node.get('children', []):
traverse_node(child)
if 'document' in file_data:
traverse_node(file_data['document'])
return frames
def _find_components(self, file_data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Find all components in the file"""
components = []
def traverse_node(node):
if node.get('type') == 'COMPONENT':
components.append(node)
for child in node.get('children', []):
traverse_node(child)
if 'document' in file_data:
traverse_node(file_data['document'])
return components
def _extract_color_tokens(self, file_data: Dict[str, Any]) -> Dict[str, str]:
"""Extract color design tokens from file styles"""
colors = {}
for style_id, style in file_data.get('styles', {}).items():
if style.get('styleType') == 'FILL':
name = style.get('name', '').replace('/', '-').lower()
# This would need to be enhanced with actual color values
# from the style definition
colors[name] = f"#{style_id[:6]}" # Placeholder
return colors
def _extract_typography_tokens(self, file_data: Dict[str, Any]) -> Dict[str, Dict]:
"""Extract typography design tokens"""
typography = {}
for style_id, style in file_data.get('styles', {}).items():
if style.get('styleType') == 'TEXT':
name = style.get('name', '').replace('/', '-').lower()
typography[name] = {
'fontSize': '16px', # Placeholder - would need actual values
'fontWeight': '400',
'lineHeight': '1.5',
'fontFamily': 'Inter'
}
return typography
def _extract_effect_tokens(self, file_data: Dict[str, Any]) -> Dict[str, str]:
"""Extract effect design tokens (shadows, etc.)"""
effects = {}
for style_id, style in file_data.get('styles', {}).items():
if style.get('styleType') == 'EFFECT':
name = style.get('name', '').replace('/', '-').lower()
effects[name] = "0 2px 4px rgba(0,0,0,0.1)" # Placeholder
return effects
def _extract_spacing_tokens(self, file_data: Dict[str, Any]) -> Dict[str, str]:
"""Extract spacing tokens from layout patterns"""
# This would analyze common spacing patterns in the design
return {
'xs': '4px',
'sm': '8px',
'md': '16px',
'lg': '24px',
'xl': '32px'
}
def _tokens_to_css(self, tokens: Dict[str, Any]) -> str:
"""Convert tokens to CSS custom properties"""
css_content = ":root {\n"
# Colors
for name, value in tokens['colors'].items():
css_content += f" --color-{name}: {value};\n"
# Typography
for name, values in tokens['typography'].items():
for prop, value in values.items():
css_content += f" --{name}-{prop.lower()}: {value};\n"
# Effects
for name, value in tokens['effects'].items():
css_content += f" --effect-{name}: {value};\n"
# Spacing
for name, value in tokens['spacing'].items():
css_content += f" --spacing-{name}: {value};\n"
css_content += "}\n"
return css_content
def _tokens_to_scss(self, tokens: Dict[str, Any]) -> str:
"""Convert tokens to SCSS variables"""
scss_content = "// Design Tokens\n\n"
# Colors
scss_content += "// Colors\n"
for name, value in tokens['colors'].items():
scss_content += f"${name.replace('-', '_')}: {value};\n"
scss_content += "\n// Typography\n"
for name, values in tokens['typography'].items():
for prop, value in values.items():
scss_content += f"${name.replace('-', '_')}_{prop.lower()}: {value};\n"
return scss_content
def _tokens_to_js(self, tokens: Dict[str, Any]) -> str:
"""Convert tokens to JavaScript/JSON module"""
return f"export const designTokens = {json.dumps(tokens, indent=2)};\n\nexport default designTokens;\n"
def _sanitize_filename(self, name: str) -> str:
"""Convert name to safe filename"""
# Remove/replace invalid characters
safe_name = "".join(c for c in name if c.isalnum() or c in (' ', '-', '_'))
safe_name = safe_name.strip().replace(' ', '-').lower()
return safe_name or 'untitled'
def _create_package_documentation(self, file_key: str, doc_path: Path, results: Dict[str, Any]):
"""Create documentation for the exported package"""
file_data = self.client.get_file(file_key)
doc_content = f"""# {file_data.get('name', 'Figma Export')}
Exported from Figma on {time.strftime('%Y-%m-%d %H:%M:%S')}
## File Information
- **File Key**: {file_key}
- **Last Modified**: {file_data.get('lastModified', 'Unknown')}
- **Version**: {file_data.get('version', 'Unknown')}
## Package Contents
### Frames ({results.get('frames', {}).get('exported', 0)} files)
All page frames exported in configured formats
Location: `./frames/`
### Components ({results.get('components', {}).get('exported', 0)} files)
All reusable components exported for development handoff
Location: `./components/`
### Design Tokens
Design system tokens in multiple formats:
- `design-tokens.json` - Raw token data
- `design-tokens.css` - CSS custom properties
- `design-tokens.scss` - SCSS variables
## Usage
### Web Development
```css
/* Import CSS tokens */
@import './design-tokens.css';
.my-component {{
color: var(--color-primary);
font-size: var(--typography-body-fontsize);
}}
```
### React/JavaScript
```javascript
import tokens from './design-tokens.js';
const MyComponent = () => (
<div style={{{{color: tokens.colors.primary}}}}>
Content
</div>
);
```
## Support
For questions about this export or design implementation, contact your design team.
"""
with open(doc_path, 'w') as f:
f.write(doc_content)
def main():
"""CLI interface for export operations"""
parser = argparse.ArgumentParser(description='Figma Export Manager')
parser.add_argument('command', choices=[
'export-frames', 'export-components', 'export-pages', 'export-nodes',
'export-tokens', 'client-package'
])
parser.add_argument('file_key', help='Figma file key or URL')
parser.add_argument('node_ids', nargs='?', help='Comma-separated node IDs for export-nodes')
parser.add_argument('--formats', default='png', help='Export formats (comma-separated)')
parser.add_argument('--scales', default='1.0', help='Export scales (comma-separated)')
parser.add_argument('--output-dir', default='./figma-exports', help='Output directory')
parser.add_argument('--token-format', default='json', choices=['json', 'css', 'scss', 'js'])
parser.add_argument('--package-name', help='Name for client package')
parser.add_argument('--frame-names', help='Specific frame names to export (comma-separated)')
parser.add_argument('--component-names', help='Specific component names to export (comma-separated)')
args = parser.parse_args()
try:
client = FigmaClient()
file_key = client.parse_file_url(args.file_key)
# Configure export settings
config = ExportConfig(
formats=args.formats.split(','),
scales=[float(s) for s in args.scales.split(',')],
output_dir=args.output_dir
)
manager = ExportManager(client, config)
if args.command == 'export-frames':
frame_names = args.frame_names.split(',') if args.frame_names else None
result = manager.export_frames(file_key, frame_names=frame_names)
elif args.command == 'export-components':
component_names = args.component_names.split(',') if args.component_names else None
result = manager.export_components(file_key, component_names=component_names)
elif args.command == 'export-pages':
result = manager.export_pages(file_key)
elif args.command == 'export-nodes':
if not args.node_ids:
parser.error('node_ids required for export-nodes command')
result = manager.export_custom_selection(file_key, args.node_ids.split(','))
elif args.command == 'export-tokens':
result = manager.export_design_tokens(file_key, args.token_format)
print(f"Design tokens exported: {result}")
return
elif args.command == 'client-package':
result = manager.create_client_package(file_key, args.package_name)
print(f"Client package created: {result}")
return
print(f"Export completed: {result['exported']} files exported")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()#!/usr/bin/env python3
"""
Figma API Client - Complete wrapper for Figma REST API
Handles authentication, rate limiting, and all major endpoints.
"""
import os
import sys
import json
import time
import requests
from typing import Dict, List, Optional, Union, Any
from urllib.parse import urlencode
import argparse
from dataclasses import dataclass
@dataclass
class FigmaConfig:
"""Configuration for Figma API client"""
access_token: str
base_url: str = "https://api.figma.com/v1"
rate_limit_delay: float = 0.5
max_retries: int = 3
class FigmaClient:
"""Professional-grade Figma API client with rate limiting and error handling"""
def __init__(self, access_token: str = None):
self.config = FigmaConfig(
access_token=access_token or os.getenv('FIGMA_ACCESS_TOKEN')
)
if not self.config.access_token:
raise ValueError("Figma access token required. Set FIGMA_ACCESS_TOKEN env var or pass token.")
self.session = requests.Session()
self.session.headers.update({
'X-Figma-Token': self.config.access_token,
'Content-Type': 'application/json'
})
def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
"""Make authenticated request with rate limiting and retry logic"""
url = f"{self.config.base_url}/{endpoint.lstrip('/')}"
for attempt in range(self.config.max_retries):
try:
# Rate limiting
time.sleep(self.config.rate_limit_delay)
response = self.session.request(method, url, **kwargs)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if response.status_code == 429: # Rate limited
wait_time = 2 ** attempt
print(f"Rate limited. Waiting {wait_time}s before retry {attempt + 1}/{self.config.max_retries}")
time.sleep(wait_time)
continue
elif response.status_code == 403:
raise ValueError("Access denied. Check your Figma token permissions.")
elif response.status_code == 404:
raise ValueError(f"File or resource not found: {url}")
else:
raise
except requests.exceptions.RequestException as e:
if attempt == self.config.max_retries - 1:
raise
print(f"Request failed, retrying {attempt + 1}/{self.config.max_retries}: {e}")
time.sleep(2 ** attempt)
# ========== FILE OPERATIONS ==========
def get_file(self, file_key: str, **params) -> Dict[str, Any]:
"""Get complete file data including components and styles"""
return self._request('GET', f'/files/{file_key}', params=params)
def get_file_nodes(self, file_key: str, node_ids: Union[str, List[str]], **params) -> Dict[str, Any]:
"""Get specific nodes from a file"""
if isinstance(node_ids, list):
node_ids = ','.join(node_ids)
params['ids'] = node_ids
return self._request('GET', f'/files/{file_key}/nodes', params=params)
def get_file_versions(self, file_key: str) -> Dict[str, Any]:
"""Get version history for a file"""
return self._request('GET', f'/files/{file_key}/versions')
def get_file_components(self, file_key: str) -> Dict[str, Any]:
"""Get all components in a file"""
return self._request('GET', f'/files/{file_key}/components')
def get_file_styles(self, file_key: str) -> Dict[str, Any]:
"""Get all styles in a file"""
return self._request('GET', f'/files/{file_key}/styles')
# ========== IMAGE EXPORTS ==========
def export_images(self, file_key: str, node_ids: Union[str, List[str]],
format: str = 'png', scale: float = 1.0, **params) -> Dict[str, Any]:
"""Export nodes as images"""
if isinstance(node_ids, list):
node_ids = ','.join(node_ids)
params.update({
'ids': node_ids,
'format': format,
'scale': scale
})
return self._request('GET', f'/images/{file_key}', params=params)
def get_image_fills(self, file_key: str) -> Dict[str, Any]:
"""Get image fill metadata from a file"""
return self._request('GET', f'/files/{file_key}/images')
# ========== TEAM & PROJECT OPERATIONS ==========
def get_team_projects(self, team_id: str) -> Dict[str, Any]:
"""Get projects for a team"""
return self._request('GET', f'/teams/{team_id}/projects')
def get_project_files(self, project_id: str) -> Dict[str, Any]:
"""Get files in a project"""
return self._request('GET', f'/projects/{project_id}/files')
# ========== COMPONENT & STYLE OPERATIONS ==========
def get_team_components(self, team_id: str, **params) -> Dict[str, Any]:
"""Get team component library"""
return self._request('GET', f'/teams/{team_id}/components', params=params)
def get_component(self, component_key: str) -> Dict[str, Any]:
"""Get individual component metadata"""
return self._request('GET', f'/components/{component_key}')
def get_team_styles(self, team_id: str, **params) -> Dict[str, Any]:
"""Get team style library"""
return self._request('GET', f'/teams/{team_id}/styles', params=params)
def get_style(self, style_key: str) -> Dict[str, Any]:
"""Get individual style metadata"""
return self._request('GET', f'/styles/{style_key}')
# ========== UTILITY METHODS ==========
def parse_file_url(self, url: str) -> str:
"""Extract file key from Figma URL"""
# https://www.figma.com/file/ABC123/File-Name
if '/file/' in url:
return url.split('/file/')[1].split('/')[0]
return url # Assume it's already a file key
def get_user_info(self) -> Dict[str, Any]:
"""Get current user information"""
return self._request('GET', '/me')
def download_image(self, image_url: str, output_path: str) -> str:
"""Download image from Figma CDN"""
response = requests.get(image_url)
response.raise_for_status()
with open(output_path, 'wb') as f:
f.write(response.content)
return output_path
# ========== ANALYSIS HELPERS ==========
def extract_colors(self, file_data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Extract all colors used in a file"""
colors = []
def traverse_node(node):
if 'fills' in node:
for fill in node.get('fills', []):
if fill.get('type') == 'SOLID':
color = fill.get('color', {})
if color:
colors.append({
'r': color.get('r', 0),
'g': color.get('g', 0),
'b': color.get('b', 0),
'a': color.get('a', 1),
'node_id': node.get('id'),
'node_name': node.get('name', '')
})
for child in node.get('children', []):
traverse_node(child)
if 'document' in file_data:
traverse_node(file_data['document'])
return colors
def extract_text_styles(self, file_data: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Extract all text styles used in a file"""
text_styles = []
def traverse_node(node):
if node.get('type') == 'TEXT':
style = node.get('style', {})
if style:
text_styles.append({
'font_family': style.get('fontFamily', ''),
'font_size': style.get('fontSize', 0),
'font_weight': style.get('fontWeight', 400),
'line_height': style.get('lineHeightPx', 0),
'letter_spacing': style.get('letterSpacing', 0),
'node_id': node.get('id'),
'node_name': node.get('name', ''),
'text': node.get('characters', '')
})
for child in node.get('children', []):
traverse_node(child)
if 'document' in file_data:
traverse_node(file_data['document'])
return text_styles
def main():
"""CLI interface for Figma operations"""
parser = argparse.ArgumentParser(description='Figma API Client')
parser.add_argument('command', choices=[
'get-file', 'export-images', 'get-components', 'get-styles',
'extract-colors', 'extract-typography', 'user-info'
])
parser.add_argument('file_key', nargs='?', help='Figma file key or URL')
parser.add_argument('--node-ids', help='Comma-separated node IDs')
parser.add_argument('--format', default='png', choices=['png', 'svg', 'pdf'])
parser.add_argument('--scale', type=float, default=1.0)
parser.add_argument('--output', help='Output file path')
parser.add_argument('--token', help='Figma access token (overrides env var)')
args = parser.parse_args()
try:
client = FigmaClient(access_token=args.token)
if args.command == 'get-file':
if not args.file_key:
parser.error('file_key required for get-file command')
file_key = client.parse_file_url(args.file_key)
result = client.get_file(file_key)
elif args.command == 'export-images':
if not args.file_key or not args.node_ids:
parser.error('file_key and --node-ids required for export-images command')
file_key = client.parse_file_url(args.file_key)
result = client.export_images(
file_key,
args.node_ids.split(','),
format=args.format,
scale=args.scale
)
elif args.command == 'get-components':
if not args.file_key:
parser.error('file_key required for get-components command')
file_key = client.parse_file_url(args.file_key)
result = client.get_file_components(file_key)
elif args.command == 'get-styles':
if not args.file_key:
parser.error('file_key required for get-styles command')
file_key = client.parse_file_url(args.file_key)
result = client.get_file_styles(file_key)
elif args.command == 'extract-colors':
if not args.file_key:
parser.error('file_key required for extract-colors command')
file_key = client.parse_file_url(args.file_key)
file_data = client.get_file(file_key)
result = client.extract_colors(file_data)
elif args.command == 'extract-typography':
if not args.file_key:
parser.error('file_key required for extract-typography command')
file_key = client.parse_file_url(args.file_key)
file_data = client.get_file(file_key)
result = client.extract_text_styles(file_data)
elif args.command == 'user-info':
result = client.get_user_info()
# Output result
output = json.dumps(result, indent=2)
if args.output:
with open(args.output, 'w') as f:
f.write(output)
print(f"Output saved to {args.output}")
else:
print(output)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()