DEV Community

shashank ms
shashank ms

Posted on

Designing with LLM: Best Practices and Tips

Today we are building a Design System Agent that turns a rough product brief into a structured JSON design system. It helps frontend engineers and designers bootstrap consistent color palettes, typography scales, and component rules without starting from scratch. We will run it on Oxlo.ai using the OpenAI-compatible SDK so we get flat per-request pricing and no cold starts.

What you'll need

Step 1: Configure the Oxlo.ai client

First we import the SDK and point it at Oxlo.ai. I keep my key in an environment variable so it does not leak into git.

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key=os.getenv("OXLO_API_KEY")
)

# Quick sanity check
print("Client ready:", client.base_url)

Step 2: Define the system prompt

A good design agent needs tight constraints. I use a system prompt that sets the role, output schema, and accessibility rules. This is the single most important lever for quality.

SYSTEM_PROMPT = """You are a senior design systems engineer.
Given a product brief, emit a JSON object that follows this exact schema:
- palette: primary, secondary, neutral, semantic colors (hex values)
- typography: font family, heading sizes in rem, body size, line heights
- spacing: base unit in rem, scale steps
- components: button, input, card styles with border radius and shadow tokens
- accessibility: minimum contrast ratios and focus ring specs

Rules:
1. All color contrast ratios must be at least 4.5:1 for normal text.
2. Use rem units for everything scalable.
3. Do not explain the JSON. Output only valid JSON."""

Step 3: Build the generator function

I call Llama 3.3 70B through Oxlo.ai with JSON mode enabled. Request-based pricing means I do not worry about a long system prompt inflating my bill, which matters when you iterate on prompts.

import json

def generate_design_system(brief: str) -> dict:
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": brief},
        ],
        response_format={"type": "json_object"},
        temperature=0.2,
    )
    raw = response.choices[0].message.content
    return json.loads(raw)

Step 4: Add a self-critique pass

Design systems need consistency. I run a second call that critiques the generated JSON against WCAG contrast rules and spacing math, then returns a corrected version. This agentic loop catches errors a single pass often misses.

CRITIC_PROMPT = """You are an accessibility-focused design auditor.
Review the provided design system JSON.
Fix any contrast ratio violations, inconsistent spacing math, or missing focus states.
Return only the corrected JSON. Do not add commentary."""

def refine_system(design: dict, brief: str) -> dict:
    payload = json.dumps(design, indent=2)
    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[
            {"role": "system", "content": CRITIC_PROMPT},
            {"role": "user", "content": f"Brief: {brief}\n\nDesign system:\n{payload}"},
        ],
        response_format={"type": "json_object"},
        temperature=0.1,
    )
    raw = response.choices[0].message.content
    return json.loads(raw)

Step 5: Render the final spec

Humans read markdown faster than raw JSON. I convert the validated dict into a short spec sheet.

def render_spec(system: dict) -> str:
    lines = [
        "# Generated Design System",
        "",
        "## Palette",
    ]
    for name, value in system.get("palette", {}).items():
        lines.append(f"- **{name}**: `{value}`")
    
    lines.extend(["", "## Typography"])
    for name, value in system.get("typography", {}).items():
        lines.append(f"- **{name}**: {value}")
    
    lines.extend(["", "## Spacing"])
    for name, value in system.get("spacing", {}).items():
        lines.append(f"- **{name}**: {value}")
    
    lines.extend(["", "## Accessibility Notes"])
    lines.append(system.get("accessibility", "No notes provided."))
    
    return "\n".join(lines)

Run it

Here is the full script entry point. I pass a brief for a fintech dashboard and print the markdown.

if __name__ == "__main__":
    brief = (
        "A fintech dashboard for retail investors. "
        "The tone should be trustworthy, calm, and data-dense. "
        "Dark mode preferred with high contrast charts."
    )
    
    draft = generate_design_system(brief)
    final = refine_system(draft, brief)
    markdown = render_spec(final)
    
    print(markdown)

Example output:

# Generated Design System

## Palette
- **primary**: `#0A84FF`
- **secondary**: `#5E5CE6`
- **neutral-100**: `#F5F5F7`
- **neutral-900**: `#1C1C1E`
- **success**: `#30D158`
- **danger**: `#FF453A`

## Typography
- **fontFamily**: `Inter, system-ui, sans-serif`
- **h1**: `2rem`
- **h2**: `1.5rem`
- **body**: `1rem`
- **lineHeight**: `1.5`

## Spacing
- **baseUnit**: `0.25rem`
- **scale**: `[1, 2, 4, 6, 8, 12, 16, 24]`

## Accessibility Notes
All normal text meets a 4.5:1 contrast ratio against neutral-900. Focus rings are 2px solid primary with 2px offset.

Wrap-up

Two concrete next steps. First, wire this agent into a Figma plugin using the Figma REST API so tokens sync automatically. Second, add an Oxlo.ai image generation step with Flux.1 to produce a mood board from the same brief, keeping visual and structural design in sync.

Top comments (0)