Skip to main content
Glama
1yhy
by 1yhy
layout-detection-research.md31.8 kB
# Design-to-Code Layout Detection Research ## Executive Summary This document provides a comprehensive analysis of layout detection algorithms used in design-to-code (D2C) conversion tools. The research focuses on how various implementations distinguish between flow (flex) layouts, stacked (absolute) layouts, and grid layouts. ## Table of Contents 1. [Problem Definition](#problem-definition) 2. [Industry Implementations](#industry-implementations) 3. [Academic Research](#academic-research) 4. [Algorithm Comparison](#algorithm-comparison) 5. [Detection Criteria & Thresholds](#detection-criteria--thresholds) 6. [Overlap Detection](#overlap-detection) 7. [Implementation Recommendations](#implementation-recommendations) --- ## Problem Definition ### The Core Challenge Design tools like Figma store element positions as absolute coordinates (x, y, width, height). Converting these to semantic CSS layouts requires inferring the designer's intent: | Layout Type | CSS Property | Use Case | | --------------------- | --------------------------------------- | -------------------------------------------- | | **Flow (Horizontal)** | `display: flex; flex-direction: row` | Elements in a row with consistent spacing | | **Flow (Vertical)** | `display: flex; flex-direction: column` | Elements stacked vertically | | **Grid** | `display: grid` | 2D matrix of aligned elements | | **Stacked/Absolute** | `position: absolute` | Overlapping elements, decorative positioning | ### Key Questions to Answer 1. How do we detect if elements form a **row** vs a **column**? 2. When should we use **Grid** instead of **nested Flex**? 3. How do we identify **overlapping/stacked** elements that need absolute positioning? 4. What **tolerance thresholds** work best for real-world designs? --- ## Industry Implementations ### 1. imgcook (Alibaba D2C Platform) **Architecture**: Deterministic-first pipeline with ML fallback ``` Design JSON → Flatten → Row/Column Grouping → Layout Inference → Semantic Labels → Code ``` **Flow vs Stacked Detection Algorithm**: ```typescript // Core concept: Axis overlap determines layout type function detectLayoutDirection(elements: Element[]): 'row' | 'column' | 'stacked' { // Step 1: Check Y-axis overlap (same horizontal row) const yOverlapGroups = groupByAxisOverlap(elements, 'y', tolerance: 2); // Step 2: If only one Y-group, check X-axis overlap (vertical column) if (yOverlapGroups.length === 1) { const xOverlapGroups = groupByAxisOverlap(elements, 'x', tolerance: 2); if (xOverlapGroups.length > 1) return 'column'; } // Step 3: Check for overlapping elements const hasOverlap = elements.some((a, i) => elements.slice(i + 1).some(b => calculateIoU(a, b) > 0.1) ); return hasOverlap ? 'stacked' : 'row'; } ``` **Grouping Algorithm**: ```typescript function groupByAxisOverlap(elements: Element[], axis: "x" | "y", tolerance: number): Element[][] { // Sort by axis position const sorted = elements.sort((a, b) => a[axis] - b[axis]); const groups: Element[][] = [[sorted[0]]]; for (let i = 1; i < sorted.length; i++) { const current = sorted[i]; const lastGroup = groups[groups.length - 1]; const lastElement = lastGroup[lastGroup.length - 1]; // Check if current overlaps with last element on opposite axis const overlaps = axis === "y" ? rangesOverlap(lastElement.y, lastElement.bottom, current.y, current.bottom, tolerance) : rangesOverlap(lastElement.x, lastElement.right, current.x, current.right, tolerance); if (overlaps) { lastGroup.push(current); } else { groups.push([current]); } } return groups; } ``` **Key Thresholds**: | Parameter | Value | Purpose | | --------------------- | -------- | ----------------------------- | | Y-axis tolerance | 2px | Row grouping | | X-axis tolerance | 2px | Column grouping | | Gap variance | 20% | Detect consistent spacing | | Alignment tolerance | 2px | Detect aligned edges | | IoU overlap threshold | 0.1 | Mark for absolute positioning | | Max recursion depth | 5 | Prevent infinite nesting | | Gap rounding | 4px grid | Snap to common design values | **Confidence Scoring**: ```typescript interface LayoutConfidence { patternCoverage: number; // How many elements fit the pattern gapConsistency: number; // Standard deviation of gaps alignmentAccuracy: number; // How well elements align } function calculateConfidence(analysis: LayoutAnalysis): number { let score = 0; score += analysis.patternCoverage * 0.4; // 40% weight score += analysis.gapConsistency * 0.35; // 35% weight score += analysis.alignmentAccuracy * 0.25; // 25% weight return score; } // Only accept layout if confidence >= 0.3 const MIN_CONFIDENCE = 0.3; ``` --- ### 2. FigmaToCode (Open Source) **Approach**: Metadata-first (trusts Figma Auto Layout) ```typescript // FigmaToCode does NOT infer layout from coordinates // Instead, it reads Figma's native Auto Layout metadata function convertLayout(node: FigmaNode): CSSLayout { if (node.layoutMode === "HORIZONTAL") { return { display: "flex", flexDirection: "row" }; } if (node.layoutMode === "VERTICAL") { return { display: "flex", flexDirection: "column" }; } // No Auto Layout = fall back to absolute positioning return { position: "absolute" }; } ``` **Limitations**: - Requires designers to use Figma Auto Layout feature - Legacy designs without Auto Layout get absolute positioning - No coordinate-based layout inference **Key Innovation**: AltNodes intermediate representation - Converts Figma nodes to simplified virtual nodes - Handles mixed positioning (absolute + auto-layout children) - Intelligent decisions about code structure --- ### 3. Locofy **Approach**: Auto Layout + Absolute positioning hybrid Locofy uses LocoAI to analyze designs and apply appropriate CSS properties: ```typescript // Locofy's approach to layout detection function analyzeLayout(elements: Element[]): LayoutDecision { // LocoAI groups elements for better structure const groups = locoAI.groupElements(elements); // Apply relevant CSS property (flex) for responsiveness for (const group of groups) { if (group.hasAutoLayout) { // Auto layout corresponds to Flexbox in CSS applyFlexLayout(group); } else if (group.isFloating) { // Floating elements use absolute positioning applyAbsolutePosition(group); } } } ``` **Key Features**: - Change absolute position status and re-run algorithm for regrouping - Floating elements use absolute property in auto layout setting - Parent of absolutely positioned element determines positioning context --- ### 4. Anima Auto-Flexbox **Approach**: Computer Vision algorithms for automatic Flexbox ```typescript // Anima's Auto-Flexbox algorithm // Reverse-engineered from developer thought process function applyAutoFlexbox(design: Design): Layout { // Without Auto-Flexbox: generates absolute layout // With Auto-Flexbox: generates relative positioning (Flexbox) // Computer Vision algorithms from CV world const flexboxLayout = cvAlgorithm.analyzeAndApply(design); // Relative positioning allows layers to push each other return flexboxLayout; } ``` **Key Insight**: "Absolute layout is great for design phase, but less so for end product. Flexbox layout means relative positioning." --- ### 5. Gridaco / Grida **Approach**: Rules + ML hybrid (similar to imgcook) ```typescript // High-availability layout direction via heuristics // ML reserved for component/loop recognition function detectLayout(elements: Element[]): LayoutType { // Rule-based direction detection first const direction = detectDirectionByRules(elements); // ML for semantic understanding const semantics = mlModel.predictSemantics(elements); return combineResults(direction, semantics); } ``` --- ### 6. Phoenix Codie Position Detection System **Key Innovation**: Explicit "position: absolute abuse" avoidance ```typescript interface PositionDecision { element: Element; shouldBeAbsolute: boolean; reason: "overlap" | "decorative" | "anchor" | "flow"; } function analyzePosition(element: Element, siblings: Element[]): PositionDecision { // Only use absolute for: // 1. Overlapping elements // 2. Decorative elements (badges, icons positioned freely) // 3. Anchor elements (tooltips, modals) const overlaps = siblings.some((s) => hasSignificantOverlap(element, s)); const isDecorative = element.type === "DECORATIVE"; const isAnchor = element.hasAnchorConstraint; return { element, shouldBeAbsolute: overlaps || isDecorative || isAnchor, reason: overlaps ? "overlap" : isDecorative ? "decorative" : isAnchor ? "anchor" : "flow", }; } ``` --- ## Academic Research ### 1. Allen's Interval Algebra (Foundational) **Source**: "A Layout Inference Algorithm for GUIs" (ScienceDirect) Allen's 13 interval relations describe 1D spatial relationships: ``` | Relation | Visual | Description | |----------|--------|-------------| | before | A---B | A completely before B | | meets | A--B | A ends where B starts | | overlaps | A-B- | A partially overlaps B | | starts | AB-- | A starts at same position as B | | during | -A-B | A completely inside B | | equals | A=B | Same position and size | ``` **Application to Layout Detection**: ```typescript function getAllenRelation(a: Interval, b: Interval): AllenRelation { if (a.end < b.start) return "before"; if (a.end === b.start) return "meets"; if (a.start < b.start && a.end > b.start && a.end < b.end) return "overlaps"; if (a.start === b.start && a.end < b.end) return "starts"; if (a.start > b.start && a.end < b.end) return "during"; if (a.start === b.start && a.end === b.end) return "equals"; // ... inverse relations } // For 2D layouts, apply Allen relations to both X and Y axes function get2DRelation(a: Rect, b: Rect): [AllenRelation, AllenRelation] { return [ getAllenRelation({ start: a.x, end: a.right }, { start: b.x, end: b.right }), getAllenRelation({ start: a.y, end: a.bottom }, { start: b.y, end: b.bottom }), ]; } ``` **Two-Phase Algorithm**: 1. **Phase 1**: Convert absolute coordinates to relative positioning using directed graphs 2. **Phase 2**: Apply pattern matching and graph rewriting for layout composition **Results**: 97% layout faithfulness, 84% proportional retention on resize --- ### 2. GRIDS - MILP-Based Layout Inference (Aalto University) **Approach**: Mathematical optimization for grid generation ```python # Formulate grid inference as Mixed Integer Linear Programming def solve_grid_layout(elements): model = gp.Model("grid_layout") # Variables: track sizes, element placements track_widths = model.addVars(max_columns, lb=0, name="col_width") track_heights = model.addVars(max_rows, lb=0, name="row_height") placements = model.addVars(len(elements), max_rows, max_columns, vtype=GRB.BINARY) # Constraints: elements must fit in tracks, no overlap for i, elem in enumerate(elements): # Element width must equal sum of spanned track widths model.addConstr(sum(track_widths[c] * placements[i,r,c] for r in range(max_rows) for c in range(max_columns)) >= elem.width) # Objective: minimize deviation from original positions model.setObjective(sum_position_errors, GRB.MINIMIZE) model.optimize() return extract_grid_template(model) ``` **Advantages**: - Mathematically optimal solutions - Handles complex grid configurations - Precise track size calculation **Disadvantages**: - Computationally expensive - Requires Gurobi optimizer - Overkill for simple layouts --- ### 3. UI Semantic Group Detection (arXiv 2024) **Key Innovation**: Gestalt-based pre-clustering ```typescript // Apply Gestalt principles before layout detection interface GestaltAnalysis { proximityGroups: Element[][]; // Close elements grouped similarityGroups: Element[][]; // Similar-looking elements grouped continuityChains: Element[][]; // Elements forming visual lines closureGroups: Element[][]; // Elements forming enclosed shapes } function applyGestaltPrinciples(elements: Element[]): GestaltAnalysis { return { proximityGroups: clusterByProximity(elements, distanceThreshold: 20), similarityGroups: clusterBySimilarity(elements, sizeVariance: 0.2), continuityChains: detectVisualLines(elements), closureGroups: detectEnclosures(elements) }; } // Use Gestalt groups to improve grid detection function detectGridWithGestalt(elements: Element[]): GridResult { const gestalt = applyGestaltPrinciples(elements); // Only consider similarity groups for grid detection for (const group of gestalt.similarityGroups) { if (group.length >= 4) { const gridResult = detectGridLayout(group); if (gridResult.confidence >= 0.6) { return gridResult; } } } return { isGrid: false }; } ``` --- ### 4. Screen Parsing (CMU UIST 2021) **Approach**: ML-based container type prediction - Trained on 210K mobile screens (130K iOS + 80K Android) - Predicts 7 container types including grids - Uses visual features and spatial relationships ```typescript interface ContainerPrediction { type: "list" | "grid" | "carousel" | "tabs" | "form" | "navigation" | "content"; confidence: number; children: Element[]; } // ML model input features interface ScreenFeatures { elementBoundingBoxes: Rect[]; elementTypes: string[]; visualSimilarities: number[][]; // Pairwise visual similarity spatialRelations: AllenRelation[][]; // Pairwise spatial relations } ``` --- ## Algorithm Comparison ### Detection Approaches Summary | Tool/Research | Approach | Flow Detection | Grid Detection | Overlap Handling | | --------------------- | ----------------- | ----------------------- | -------------------------- | --------------------------- | | **imgcook** | Rule-based + ML | Y-axis overlap grouping | Row/column alignment check | IoU > 0.1 → absolute | | **FigmaToCode** | Metadata-first | Read Figma Auto Layout | None (no inference) | N/A | | **Grida** | Rules + ML | Similar to imgcook | Similar to imgcook | Similar to imgcook | | **Phoenix Codie** | Rule-based | Position analysis | N/A | Explicit overlap check | | **Allen's Algorithm** | Graph-based | Interval relations | Two-phase inference | Contains/overlaps relations | | **GRIDS** | MILP optimization | N/A | Mathematical optimization | Constraint-based | | **Screen Parsing** | ML-based | ML prediction | ML prediction | ML prediction | ### Strengths and Weaknesses | Approach | Strengths | Weaknesses | | ------------------ | ------------------------------ | ---------------------------------- | | **Rule-based** | Predictable, explainable, fast | Limited flexibility, manual tuning | | **Metadata-first** | Accurate when metadata exists | Fails without designer cooperation | | **ML-based** | Handles complex patterns | Requires training data, black box | | **MILP** | Mathematically optimal | Computationally expensive | | **Hybrid** | Best of both worlds | Complex implementation | --- ## Detection Criteria & Thresholds ### Universal Thresholds (Industry Consensus) ```typescript const LAYOUT_THRESHOLDS = { // Axis overlap tolerances ROW_GROUPING_TOLERANCE: 2, // px - Y-axis overlap for same row COLUMN_GROUPING_TOLERANCE: 2, // px - X-axis overlap for same column // Gap analysis GAP_VARIANCE_THRESHOLD: 0.2, // 20% - Max coefficient of variation GAP_ROUNDING_GRID: 4, // px - Snap gaps to multiples // Alignment ALIGNMENT_TOLERANCE: 2, // px - Edge alignment detection // Size homogeneity SIZE_CV_THRESHOLD: 0.2, // 20% - Max size variance for grid // Overlap detection IOU_OVERLAP_THRESHOLD: 0.1, // 10% - Mark as overlapping IOU_SIGNIFICANT_OVERLAP: 0.5, // 50% - Definitely overlapping // Confidence thresholds MIN_FLOW_CONFIDENCE: 0.3, // Minimum to accept flow layout MIN_GRID_CONFIDENCE: 0.6, // Minimum to accept grid layout // Element counts MIN_GRID_ELEMENTS: 4, // Minimum for grid detection MIN_GRID_ROWS: 2, // Minimum rows for grid MIN_GRID_COLUMNS: 2, // Minimum columns for grid }; ``` ### Gap Rounding Values ```typescript // Common design system spacing values const COMMON_GAP_VALUES = [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64]; function roundGapToCommon(gap: number): number { return COMMON_GAP_VALUES.reduce((prev, curr) => Math.abs(curr - gap) < Math.abs(prev - gap) ? curr : prev, ); } ``` --- ## Overlap Detection ### IoU (Intersection over Union) Calculation ```typescript interface Rect { x: number; y: number; width: number; height: number; } function calculateIoU(a: Rect, b: Rect): number { // Calculate intersection const xOverlap = Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)); const yOverlap = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y)); const intersection = xOverlap * yOverlap; // Calculate union const areaA = a.width * a.height; const areaB = b.width * b.height; const union = areaA + areaB - intersection; return union > 0 ? intersection / union : 0; } ``` ### Overlap Classification ```typescript type OverlapType = "none" | "adjacent" | "partial" | "significant" | "contained"; function classifyOverlap(a: Rect, b: Rect): OverlapType { const iou = calculateIoU(a, b); if (iou === 0) { // Check if adjacent (touching but not overlapping) const gap = calculateGap(a, b); return gap <= 2 ? "adjacent" : "none"; } if (iou < 0.1) return "partial"; if (iou < 0.5) return "significant"; return "contained"; } function shouldUseAbsolutePosition(element: Rect, siblings: Rect[]): boolean { for (const sibling of siblings) { const overlap = classifyOverlap(element, sibling); if (overlap === "partial" || overlap === "significant" || overlap === "contained") { return true; } } return false; } ``` ### Z-Index Inference ```typescript // When elements overlap, infer z-order from: // 1. Figma layer order (later = on top) // 2. Size (smaller elements usually on top) // 3. Type (text/icons usually on top of backgrounds) function inferZIndex(elements: Element[]): Map<Element, number> { const zIndexMap = new Map<Element, number>(); // Sort by layer order (Figma provides this) const sorted = [...elements].sort((a, b) => a.layerOrder - b.layerOrder); sorted.forEach((el, index) => { zIndexMap.set(el, index); }); return zIndexMap; } ``` --- ## Implementation Recommendations ### For This Project (Figma-Context-MCP) Based on the research, here's the recommended algorithm: #### 1. Pre-filtering: Homogeneity Check (IMPLEMENTED) ```typescript // Already implemented in detector.ts export function filterHomogeneousForGrid( rects: ElementRect[], nodeTypes?: string[], ): ElementRect[] { const homogeneity = analyzeHomogeneity(rects, nodeTypes, 0.2); return homogeneity.homogeneousElements; } ``` #### 2. Overlap Detection (TO IMPLEMENT) ```typescript function detectOverlappingElements(nodes: SimplifiedNode[]): { flowElements: SimplifiedNode[]; stackedElements: SimplifiedNode[]; } { const rects = nodes.map(nodeToRect); const flowElements: SimplifiedNode[] = []; const stackedElements: SimplifiedNode[] = []; for (let i = 0; i < nodes.length; i++) { const hasOverlap = rects.some((other, j) => i !== j && calculateIoU(rects[i], other) > 0.1); if (hasOverlap) { stackedElements.push(nodes[i]); } else { flowElements.push(nodes[i]); } } return { flowElements, stackedElements }; } ``` #### 3. Child Element Style Cleanup (TO IMPLEMENT) ```typescript function cleanChildStylesForFlexParent(child: SimplifiedNode): void { if (child.cssStyles) { // Remove position: absolute when parent is flex/grid if (child.cssStyles.position === "absolute") { delete child.cssStyles.position; } // Remove left/top (now handled by flex/grid) delete child.cssStyles.left; delete child.cssStyles.top; // Keep width/height for flex items (used by flex-basis) } } function cleanChildStylesForGridParent(child: SimplifiedNode): void { if (child.cssStyles) { delete child.cssStyles.position; delete child.cssStyles.left; delete child.cssStyles.top; // Width/height may be redundant if grid tracks define sizes // But keep them for explicit sizing } } ``` #### 4. Default Value Removal (TO IMPLEMENT) ```typescript const CSS_DEFAULT_VALUES: Record<string, string[]> = { fontWeight: ["400", "normal"], textAlign: ["left", "start"], flexDirection: ["row"], position: ["static"], opacity: ["1"], backgroundColor: ["transparent", "rgba(0,0,0,0)"], borderWidth: ["0", "0px"], }; function removeDefaultValues(cssStyles: CSSStyle): CSSStyle { const cleaned: CSSStyle = {}; for (const [key, value] of Object.entries(cssStyles)) { const defaults = CSS_DEFAULT_VALUES[key]; if (defaults && defaults.includes(String(value))) { continue; // Skip default value } cleaned[key] = value; } return cleaned; } ``` ### Complete Detection Pipeline ``` ┌─────────────────────────────────────────────────────────────────┐ │ Layout Detection Pipeline │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. INPUT: Array of child elements with absolute coordinates │ │ ↓ │ │ 2. OVERLAP CHECK: Separate overlapping elements │ │ - IoU > 0.1 → stackedElements (keep absolute) │ │ - IoU ≤ 0.1 → flowElements (continue analysis) │ │ ↓ │ │ 3. HOMOGENEITY CHECK: Filter similar elements │ │ - Size CV < 20% AND same types → homogeneous │ │ - Mixed sizes/types → heterogeneous │ │ ↓ │ │ 4. ROW GROUPING: Y-axis overlap (2px tolerance) │ │ - 1 row → horizontal flex │ │ - Multiple rows → continue │ │ ↓ │ │ 5. GRID CHECK (homogeneous only): │ │ - rows ≥ 2 AND columns ≥ 2 │ │ - Column alignment ≥ 80% │ │ - Confidence ≥ 0.6 │ │ → YES: display: grid │ │ → NO: continue │ │ ↓ │ │ 6. FLEX DIRECTION: │ │ - Dominant direction by element count │ │ - Row if most elements horizontal │ │ - Column if most elements vertical │ │ ↓ │ │ 7. CHILD CLEANUP: │ │ - Remove position: absolute │ │ - Remove left/top │ │ - Remove default CSS values │ │ ↓ │ │ 8. OUTPUT: LayoutInfo with clean child styles │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## References ### Academic Papers 1. **"A Layout Inference Algorithm for Graphical User Interfaces"** (2015) - [ScienceDirect](https://www.sciencedirect.com/science/article/abs/pii/S0950584915001718) - [ResearchGate PDF](https://www.researchgate.net/publication/283526120_A_layout_inference_algorithm_for_Graphical_User_Interfaces) - Key: Allen's Interval Algebra, 97% layout faithfulness 2. **"GRIDS: Interactive Layout Design with Integer Programming"** (CHI 2020) - [Project Page](https://userinterfaces.aalto.fi/grids/) - [GitHub](https://github.com/aalto-ui/GRIDS) - [Paper PDF](https://acris.aalto.fi/ws/portalfiles/portal/40720569/CHI2020_Dayama_GRIDS.pdf) - Authors: Niraj Dayama, Kashyap Todi, Taru Saarelainen, Antti Oulasvirta (Aalto University) 3. **"Screen Parsing: Towards Reverse Engineering of UI Models from Screenshots"** (UIST 2021) - [CMU ML Blog](https://blog.ml.cmu.edu/2021/12/10/understanding-user-interfaces-with-screen-parsing/) - [Paper PDF](https://www.cs.cmu.edu/~jbigham/pubs/pdfs/2021/screen-parsing.pdf) - [ACM Digital Library](https://dl.acm.org/doi/fullHtml/10.1145/3472749.3474763) - Authors: Jason Wu, Xiaoyi Zhang, Jeff Nichols, Jeffrey P. Bigham 4. **"UI Semantic Group Detection"** (arXiv 2024) - arXiv:2403.04984v1 5. **"UIHASH: Grid-Based UI Similarity"** - Jun Zeng et al. ### Open Source Projects 1. [FigmaToCode](https://github.com/bernaferrari/FigmaToCode) - Generate responsive pages on HTML, Tailwind, Flutter, SwiftUI 2. [GRIDS](https://github.com/aalto-ui/GRIDS) - MILP-based grid layout generation (Python + Gurobi) 3. [Grida](https://github.com/gridaco/grida) - Design-to-code platform 4. [Yoga](https://github.com/facebook/yoga) - Facebook's cross-platform Flexbox layout engine ### Industry Resources 1. **imgcook (Alibaba)** - [Layout Algorithm Blog](https://www.alibabacloud.com/blog/imgcook-3-0-series-layout-algorithm-design-based-code-generation_597856) - [How imgcook Works](https://medium.com/imgcook/imgcook-how-are-codes-generated-intelligently-from-design-files-in-alibaba-98ba8e55246d) - [100% Accuracy Rate](https://www.alibabacloud.com/blog/imgcook-intelligent-code-generation-from-design-drafts-with-a-100%25-accuracy-rate_598093) 2. **Locofy** - [Design Optimiser Docs](https://www.locofy.ai/docs/lightning/design-optimiser/) - [Auto Layout to Responsive Code](https://www.locofy.ai/docs/classic/design-structure/responsiveness/auto-layout/) 3. **Anima** - [Auto-Flexbox Introduction](https://www.animaapp.com/blog/design-to-code/introducing-auto-flexbox/) - [Flexbox from Constraints](https://www.animaapp.com/blog/product-updates/producing-flexbox-responsive-code-based-on-figma-adobe-xd-and-sketch-constraints/) 4. **CSS Standards** - [CSS Grid Layout Module Level 1](https://www.w3.org/TR/css-grid-1/) - W3C - [CSS Flexible Box Layout](https://www.w3.org/TR/css-flexbox-1/) - W3C - [Understanding Layout Algorithms](https://www.joshwcomeau.com/css/understanding-layout-algorithms/) - Josh W. Comeau 5. **Foundational** - [Allen's Interval Algebra](https://en.wikipedia.org/wiki/Allen's_interval_algebra) - Wikipedia - [Figma Grid Auto-Layout](https://help.figma.com/hc/en-us/articles/31289469907863) - Figma Help - [IoU Explained](https://www.v7labs.com/blog/intersection-over-union-guide) - V7 Labs --- ## Appendix: Code Examples ### A. Complete Flow Detection ```typescript export function detectFlowLayout(elements: Element[]): LayoutInfo { // Step 1: Check for overlaps const { flowElements, stackedElements } = detectOverlappingElements(elements); if (flowElements.length < 2) { return { type: "absolute", elements: stackedElements }; } // Step 2: Group into rows const rows = groupIntoRows(flowElements, THRESHOLDS.ROW_GROUPING_TOLERANCE); // Step 3: Determine direction if (rows.length === 1) { // Single row = horizontal flex const gapAnalysis = analyzeGaps(rows[0], "horizontal"); return { type: "flex", direction: "row", gap: gapAnalysis.averageGap, alignment: detectAlignment(rows[0], "vertical"), justifyContent: detectJustifyContent(rows[0], "horizontal"), }; } // Step 4: Check for grid (multiple rows) const gridResult = detectGridLayout(flowElements); if (gridResult.isGrid && gridResult.confidence >= 0.6) { return { type: "grid", columns: gridResult.columnCount, rows: gridResult.rowCount, columnGap: gridResult.columnGap, rowGap: gridResult.rowGap, trackWidths: gridResult.trackWidths, }; } // Step 5: Fallback to column flex const rowGapAnalysis = analyzeGaps( rows.map((r) => r[0]), "vertical", ); return { type: "flex", direction: "column", gap: rowGapAnalysis.averageGap, alignment: detectAlignment(rows.flat(), "horizontal"), }; } ``` ### B. Complete Grid Detection ```typescript export function detectGridLayout(elements: ElementRect[]): GridAnalysisResult { // Step 1: Group into rows const rows = groupIntoRows(elements, 2); if (rows.length < 2) { return { isGrid: false, confidence: 0 }; } // Step 2: Check column count consistency const columnCounts = rows.map((r) => r.length); const countVariance = coefficientOfVariation(columnCounts); if (countVariance > 0.2) { return { isGrid: false, confidence: 0 }; } // Step 3: Extract column positions const columnPositions = extractColumnPositions(rows); const alignmentResult = checkColumnAlignment(columnPositions, 4); // Step 4: Calculate gaps const columnGaps = calculateColumnGaps(rows); const rowGaps = calculateRowGaps(rows); // Step 5: Calculate confidence let confidence = 0; if (rows.length >= 2) confidence += 0.2; if (rows.length >= 3) confidence += 0.1; if (countVariance < 0.1) confidence += 0.2; if (alignmentResult.isAligned) confidence += 0.25; if (coefficientOfVariation(columnGaps) < 0.2) confidence += 0.1; if (coefficientOfVariation(rowGaps) < 0.2) confidence += 0.1; // Step 6: Calculate track sizes const trackWidths = calculateTrackWidths(rows, alignmentResult.alignedPositions); const trackHeights = rows.map((row) => Math.max(...row.map((el) => el.height))); return { isGrid: confidence >= 0.6, confidence, rowCount: rows.length, columnCount: Math.max(...columnCounts), rowGap: roundGapToCommon(average(rowGaps)), columnGap: roundGapToCommon(average(columnGaps)), trackWidths, trackHeights, }; } ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/1yhy/Figma-Context-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server