| name | latcoder-layout-aware-code-generation |
| title | LaTCoder - Layout-as-Thought for Webpage Design-to-Code |
| version | 0.0.2 |
| engine | skillxiv-v0.0.2-claude-opus-4.6 |
| license | MIT |
| url | https://arxiv.org/abs/2508.03560 |
| keywords | ["code-generation","layout","visual-reasoning","multimodal"] |
| description | Convert webpage designs to code via Layout-as-Thought reasoning, detecting layout structure and generating HTML/CSS for spatial blocks. |
LaTCoder: Layout-as-Thought for Design-to-Code
LaTCoder addresses layout preservation in webpage design-to-code generation through Layout-as-Thought (LaT): decompose visual designs into geometric blocks, reason about layout first, then generate code for each block. This divide-and-conquer approach avoids MLLM weaknesses in spatial reasoning and numerical understanding, achieving >60% preference over baselines.
Core Concept
Converting webpage designs to code requires understanding spatial layout—where elements are positioned and sized. MLLMs struggle with: (1) "factual interpretation" of visual coordinates, and (2) "numerical reasoning" for dimensions. LaTCoder sidesteps this by anchoring code generation to explicit spatial coordinates: detect layout blocks in the design, treat each block as an independent reasoning step, and assemble via layout-aware assembly strategies.
Architecture Overview
- Layout-Aware Division: Detect horizontal/vertical dividing lines, extract distinct layout blocks
- Block-Wise Code Synthesis: Generate HTML/CSS per block via Chain-of-Thought
- Layout-Preserved Assembly: Two strategies (absolute positioning or MLLM-based assembly)
- Verifier: MAE + CLIP similarity for selecting best assembly strategy
Implementation Steps
Step 1: Detect Layout Structure
import cv2
import numpy as np
from PIL import Image
from typing import List, Tuple, Dict
class LayoutDetector:
"""Detect rectangular layout blocks in webpage designs."""
def __init__(self):
self.dividing_line_threshold = 200
def detect_dividing_lines(self, image: Image.Image) -> Tuple[List[int], List[int]]:
"""
Detect horizontal and vertical dividing lines.
Lines are solid-colored regions where layout blocks meet.
"""
img_array = np.array(image)
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
horizontal_lines = []
for row in range(1, gray.shape[0] - 1):
row_color = gray[row, :]
if np.std(row_color) < 30:
horizontal_lines.append(row)
vertical_lines = []
for col in range(, gray.shape[] - ):
col_color = gray[:, col]
np.std(col_color) < :
vertical_lines.append(col)
horizontal = ._consolidate_lines(horizontal_lines, threshold=)
vertical = ._consolidate_lines(vertical_lines, threshold=)
horizontal, vertical
() -> []:
lines:
[]
consolidated = [lines[]]
line lines[:]:
line - consolidated[-] > threshold:
consolidated.append(line)
consolidated
() -> []:
h_dividers = [] + (h_dividers) + [image.height]
v_dividers = [] + (v_dividers) + [image.width]
blocks = []
i ((h_dividers) - ):
j ((v_dividers) - ):
top = h_dividers[i]
bottom = h_dividers[i + ]
left = v_dividers[j]
right = v_dividers[j + ]
(bottom - top) < (right - left) < :
block_image = image.crop((left, top, right, bottom))
block = {
: (blocks),
: (left, top, right, bottom),
: right - left,
: bottom - top,
: block_image,
}
blocks.append(block)
blocks
Step 2: Implement Chain-of-Thought Code Generation per Block
class BlockCodeGenerator:
"""Generate HTML/CSS for individual layout blocks."""
def __init__(self, mlm_model):
self.mlm = mlm_model
def generate_block_code(self, block: Dict, block_position: int,
previous_blocks_context: str = "") -> str:
"""
Generate code for single block with Layout-as-Thought.
CoT prompts emphasize layout fidelity before content.
"""
block_image = block['image']
width = block['width']
height = block['height']
bbox = block['bbox']
cot_prompt = f"""Webpage Design Block #{block_position}:
Layout dimensions: {width}px × {height}px
Position in page: top-left ({bbox[0]}, {bbox[1]})
Let's think step by step about the layout:
1. What is the spatial structure of this block? (grid, flex, absolute positioning)
2. What are the key layout dimensions? (widths, heights, gaps)
3. What visual content should be in this block? (text, images, buttons)
4. How should padding/margins be set for the layout?
Generate HTML and CSS for this block that preserves the visual layout.
Include all necessary styling for positioning and spacing.
Code:
```html
[HTML/CSS code for this block]
```"""
code = self.mlm.generate(
prompt=cot_prompt,
image=block_image,
max_tokens=500
)
return code.strip()
def () -> [, ]:
block_codes = {}
i, block (blocks):
previous_context = .join(
j ((, i - ), i)
)
code = .generate_block_code(block, i, previous_context)
block_codes[i] = code
block_codes
Step 3: Implement Layout-Preserved Assembly
class AssemblyStrategy:
"""Combine blocks into full webpage while preserving layout."""
def absolute_positioning_assembly(self, blocks: List[Dict],
block_codes: Dict[int, str]) -> str:
"""
Assembly via absolute positioning: use bounding boxes for placement.
Each block positioned using its extracted coordinates.
"""
html_parts = ['<!DOCTYPE html>\n<html>\n<head>\n<style>\n']
html_parts.append('body { position: relative; }\n')
for block in blocks:
left, top, right, bottom = block['bbox']
width = right - left
height = bottom - top
block_css = f"""
.block_{block['index']} {{
position: absolute;
left: {left}px;
top: {top}px;
width: {width}px;
height: {height}px;
}}
"""
html_parts.append(block_css)
html_parts.append('</style>\n</head>\n<body>\n')
for block in blocks:
block_code = block_codes[block['index']]
inner_html = self._extract_inner_html(block_code)
html_parts.append(f'<div class="block_{block["index"]}">\n')
html_parts.append(inner_html)
html_parts.append('</div>\n')
html_parts.append()
.join(html_parts)
() -> :
assembly_prompt =
i, block (blocks):
assembly_prompt +=
assembly_prompt +=
assembly_prompt +=
html = mlm_model.generate(assembly_prompt, max_tokens=)
html
() -> :
lines = block_code.split()
inner_lines = [l l lines l.strip().startswith()
l.strip().startswith()
l.strip().startswith()]
.join(inner_lines)
Step 4: Implement Verifier
class AssemblyVerifier:
"""Select best assembly strategy using MAE and CLIP similarity."""
def __init__(self):
self.clip_model = load_clip_model()
self.mae_model = load_mae_model()
def compute_mae_loss(self, original_design: Image.Image,
generated_html: str) -> float:
"""
Render generated HTML, compare pixel-level with original.
MAE = Mean Absolute Error between designs.
"""
rendered_image = render_html_to_image(generated_html)
original_array = np.array(original_design)
rendered_array = np.array(rendered_image.resize(original_design.size))
mae = np.mean(np.abs(original_array.astype(float) - rendered_array.astype(float)))
return mae
def compute_clip_similarity(self, original_design: Image.Image,
generated_html: str) -> float:
"""
Semantic similarity via CLIP: encode both visually.
High similarity = good content preservation.
"""
rendered_image = render_html_to_image(generated_html)
original_features = self.clip_model.encode_image(original_design)
rendered_features = self.clip_model.encode_image(rendered_image)
similarity = torch.nn.functional.cosine_similarity(
original_features.unsqueeze(0),
rendered_features.unsqueeze(0)
).item()
(similarity + ) /
() -> [, ]:
scores = {}
strategy_name, html assemblies.items():
mae = .compute_mae_loss(original_design, html)
mae_quality = / ( + mae)
clip_sim = .compute_clip_similarity(original_design, html)
score = * mae_quality + * clip_sim
scores[strategy_name] = score
best_strategy = (scores, key=scores.get)
assemblies[best_strategy], scores[best_strategy]
Step 5: End-to-End Pipeline
def latcoder_design_to_code(design_image: Image.Image,
mlm_model) -> str:
"""
Complete pipeline: detect layout → generate blocks → assemble.
"""
detector = LayoutDetector()
h_dividers, v_dividers = detector.detect_dividing_lines(design_image)
blocks = detector.extract_layout_blocks(design_image, h_dividers, v_dividers)
print(f"Detected {len(blocks)} layout blocks")
code_gen = BlockCodeGenerator(mlm_model)
block_codes = code_gen.generate_blocks_with_context(blocks)
assembler = AssemblyStrategy()
absolute_html = assembler.absolute_positioning_assembly(blocks, block_codes)
mllm_html = assembler.mllm_assembly(blocks, block_codes, mlm_model)
verifier = AssemblyVerifier()
final_html, quality_score = verifier.select_best_assembly(
design_image,
{'absolute_positioning': absolute_html, 'mllm': mllm_html}
)
print(f"Selected assembly strategy with quality: {quality_score:.3f}")
return final_html
Practical Guidance
When to Use:
- Webpage design to code conversion
- Scenarios where spatial layout is critical
- Visual design systems with consistent structure
- Conversion of mockups/Figma designs to HTML/CSS
When NOT to Use:
- Complex interactive designs (animations, transitions)
- Designs with overlapping elements
- Real-time rendering requirements
- Scenarios where exact pixel-perfect layout is unnecessary
Hyperparameters:
| Parameter | Default | Impact |
|---|
dividing_line_threshold | 200 | Pixel threshold for detecting solid lines; lower = more sensitive |
min_block_size | 20 | Minimum block dimensions (px) to avoid artifacts |
mae_weight | 0.6 | Weight of pixel-level accuracy in assembly selection |
clip_weight | 0.4 | Weight of semantic similarity in assembly selection |
Reference
Paper: LaTCoder: Converting Webpage Design to Code with Layout-as-Thought (2508.03560)
-
60% human preference over baselines
- Layout-as-Thought anchors reasoning to spatial coordinates
- Two assembly strategies: absolute positioning vs. MLLM reasoning
- Verifier uses MAE and CLIP for quality assessment