Skip to contentHarish ReddySenior Product Designer
Back to projectsInternal tool / Built with Claude Code

Product thinking meets hands-on engineering

Ray

The system behind
the design system.

I designed Ray and built it from scratch using Claude Code, entirely through AI development. This AI-native approach turned my design-system workflow into a Figma plugin for foundations, re-theming, screens, code and documentation.

See how it works Explore the architecture
Photorealistic illustrative scene of a product designer reviewing a consistent software interface and reusable components.
My ownership
Product, UX & system architecture
Built for
My internal design workflow
Runtime
Figma Plugin API / TypeScript
Approach
AI-native / Claude Code

The product and the process

A Figma plugin.
Built with Claude Code.

Ray lives inside the design file. I built it through an AI-native development process.

Runs where the design happens.

Create and re-theme foundations, reuse components and build editable screens directly in Figma.

Ray at a glance

From theme to handoff.

One connected workflow for a new system or an existing one.

  1. Set the foundations

    Choose a theme, typography and tokens, or import DESIGN.md.

  2. Create or re-theme

    Build components or update an existing design system.

  3. Compose screens

    Reuse those components in starter flows and screens.

  4. Generate code

    Translate the Figma structure into React components.

  5. Export the handoff

    Package specifications, usage code and DESIGN.md.

Problem statement

Every new system.
The same manual setup.

For each new project or re-theme, I repeated foundation setup, component states, starter flows, code and handoff documentation. Manual work could take days or weeks, with the same design decisions needing to stay consistent across each output.

Solution

Carry one set of design decisions through every output.

I built Ray to define or import foundations once, create or re-theme components, reuse them in screens, and export code and documentation from the same workflow.

Without Ray

Rebuild the foundations → restyle the library → assemble screens → translate the handoff.

With Ray

Define the design intent → generate connected outputs → review and refine.

Business impact

Internal tool / Experience from my own use

Days of setup. A first build in minutes.

Ray replaces repeated foundation setup with a connected workflow for theming, components, starter screens, code and documentation. Its operational value is creating a reusable starting point sooner, leaving more time for product decisions and refinement.

Current evidence
In my internal use, an initial system that could take days or weeks manually can be generated in a couple of minutes. Timing varies by scope and machine; review and refinement still follow. This is not a controlled benchmark.
What to measure next
Measure time to an approved system, correction effort, re-theme coverage and the usefulness of generated code in a real application.

One connected workflow.

Each step reuses the system’s decisions. Explore what the designer controls and what Ray produces.

Ray Create interface with palette presets, typography and themes
Actual plugin UI, captured locally. Canvas operations run inside Figma.
01 / 05

Start with the rules.

Name the system, choose a palette, set typography and select a visual theme. Ray turns those decisions into reusable foundations instead of another disconnected UI kit.

The outputFigma variables, text styles and reusable components

A saved DESIGN.md carries the design intent into the rest of the workflow.

From design intent to editable screens.

Start with Markdown or theme choices. Follow how Ray structures the rules, selects an execution path and turns a screen definition into editable Figma objects.

Follow a request through Ray. Choose an input, then an operation to see which engine takes over.

1

Your decisions enter in two ways.

The designer supplies the intent. Ray parses or assembles the data.

DESIGN.md
---
name: Northstar
colors:
  primary: "#c3ff00"
typography:
  body:
    fontFamily: Inter
---
## Brand & Style
Clear, focused interfaces.

A Markdown document can contain structured frontmatter and human-readable guidelines.

Parse supported fieldsNo AI call
Structured design contract
{
  "name": "Northstar",
  "colors": { "primary": "#c3ff00" },
  "typography": {
    "body": { "fontFamily": "Inter" }
  },
  "spacing": { "md": 16 },
  "rounded": { "md": "8px" }
}

A simplified JSON representation of the DesignMd object. This stores design decisions; it is not a screen layout.

Normalize the vocabulary.

Imported color names are translated into canonical slots. For example, Material-style names can map to the Shadcn-style vocabulary used downstream. Typography, spacing and radius data travel with the contract.

surfacebackgroundon-surfaceforeground
Unmapped color keys are reported. Recognized aliases and heuristics reduce the need for AI interpretation.
2

The request selects the engine.

The same active contract supplies the rules. The selected operation decides which engine runs.

Zero AI tokens for the saved-pattern render

Load the layout. Resolve the theme. Render.

Industry and flow choices locate a registered pattern. Its saved JSON contains the screen structure, so Ray can render the flow directly. The system supplies the current colors, styles and components from the existing or re-themed library.

  1. Find the pattern

    SaaS → onboarding → screen definitions

  2. Load saved JSON

    AIDocument trees from the pattern store

  3. Render with the theme

    Resolve roles, expand recipes, create nodes

If a registered pattern cannot be fetched and no usable copy is available, Ray reports the failure. It does not silently replace that path with a paid AI call.

Where this lives in the implementationflows/ → asset-fetcher.ts → renderAIDocument()
3

A screen tree becomes an editable canvas.

Saved-pattern JSON and parsed AI responses meet at the same renderer. It resolves the theme and creates editable Figma objects.

Saved pattern JSON
or
Parsed AI response
Screen layout tree
{
  "name": "Welcome",
  "root": {
    "type": "frame",
    "direction": "vertical",
    "fill": "@role:bg",
    "children": [
      { "type": "text",
        "content": "Your workspace",
        "fill": "@role:fg" }
    ]
  }
}

Illustrative AIDocument shape. This defines what to draw and how to arrange it.

The shared renderer

  • Expand named layout recipes
  • Resolve roles to file styles or contract colors
  • Find reusable component instances
  • Create frames, text and auto layout
renderAIDocument()
Native Figma output
  • Editable layers
  • Theme-aware styling
  • Reusable components where available

The design contract and the screen JSON do different jobs. The contract says which visual rules to use. The layout tree says which elements to create. The renderer brings them together; a Markdown file alone does not specify an entire screen.

The editable file becomes a developer handoff.

Ray carries the design decisions into two complementary outputs.

Component code

React components for Shadcn, Radix or Tailwind, generated through a shared intermediate representation.

System documentation

A Word-compatible document and DESIGN.md, with specifications, component descriptions and usage examples.

Where I designed out unnecessary AI work.

Use code for repeatable rules. Ask the model when the request needs interpretation.

Translate names with rules

A synonym dictionary and color-role heuristics normalize imported color names. This translation does not need an LLM call. Unmapped keys are recorded instead of claiming every vocabulary is understood.

design-md-translate.ts

Reuse small layout recipes

Named patterns are functions that expand into frame, text and instance trees. AI can refer to a reusable recipe instead of spelling out the same low-level layout repeatedly. Token savings vary with the request.

ui-patterns.ts → expandPattern()

Render saved flows directly

Registered flows already contain screen JSON. Their render path bypasses the model entirely: no new prompt and no new AI response are needed to reconstruct the same screen structure.

pattern-registry.ts → renderAIDocument()

Reuse downloaded assets

Memory and Figma clientStorage cache pattern assets, with network checks for changes. This reduces repeated asset loading; it is asset caching, not a claim of model prompt caching.

asset-fetcher.ts

These diagrams explain the architecture I built. They illustrate how Ray works; token savings have not been benchmarked.

Inspect the six implementation layers
Optional AI interpretation4 providers / structured layout → execution engine

Choose a layer to inspect the decision behind it. Arrows show the primary path; code and documentation are separate outputs.

Inside the execution engine

Use repeatable construction for repeatable work.

The message router dispatches system builders, re-theme operations and registered flow definitions. For an open-ended request, the AI path produces a structured layout description; a renderer turns that description into Figma nodes.

Why I made this choiceSaved patterns do not need a fresh model response on every render. AI is reserved for the work that benefits from interpretation.

Implementation modulescode.ts · components*.ts · flows/ · ai-generation.ts

Supporting services stay outside the canvas.

Supabase supports authentication, pattern storage and optional AI proxying. Assets use a memory → local storage → network cache. These integrations are separated from the document-building code. Ray is presented here as my internal tool, not a publicly launched service.

Existing systems, new possibilities

Re-theme the foundation.
Keep the components.

Ray can work with an existing design system, including one built outside Ray. It updates supported foundation tokens and styles, then uses the existing or re-themed components to create screens.

The change happens at the foundation: color variables, typography, spacing and radii. Bound component instances follow those values. Preserve mode keeps variable identity and file structure intact; compatibility depends on the file’s token and style mappings.

Try a foundation update

1. Choose colors. 2. Click Re-theme. 3. Reuse the components in a screen.

Choose foundation colors, then apply the re-theme.

Interactive illustration of Ray’s token propagation. This does not connect to or modify a live Figma file.

Applied foundation valuesIn sync
brand / primary #c3ff00surface / base #10191d
Foundation variableSemantic tokenComponent instance
Existing component
Token-bound

Workspace setup

A reusable card with a primary button, status and surface.

Continue

Button fill → primary
Card fill → surface

Existing screen instance
Northstar

Welcome back.

Token-boundWorkspace setup

Same component. Same token bindings.

Continue

The component is not rebuilt for each color change. In Figma, Ray updates supported variable values and styles. Bound components inherit the update; screen generation can then reuse those components. Unbound, hard-coded values need separate handling.

Built for more than the happy path.

The system needs predictable boundaries, especially when AI, existing files and several export formats meet.

Re-theme values, preserve relationships.

Renaming or rebuilding variables can break an existing file. Preserve mode changes supported color values while retaining variable identity and aliases. Unrecognized palettes need review rather than a promise of universal conversion.

Keep AI behind a clear boundary.

Provider adapters normalize four AI services behind one interface. The agent has an eight-turn limit. A missing saved pattern produces an actionable error instead of silently falling back to paid AI generation.

Share the translation layer.

Token resolution and variant extraction happen before the code target is chosen. A shared IR carries those decisions into three emitters, making additional targets easier to reason about without duplicating the whole pipeline.

Where I deliberately set limits

Pattern unavailable
Surface the fetch error. Do not silently substitute an AI-generated flow.
Large code selections
Truncate very large trees to stay within plugin-data limits; review the result.
Generated screens & code
Treat them as editable starting points. Validate responsiveness, accessibility and product logic.

A working product.
A repeatable foundation.

My internal use showed that the approach worked for the setup problem. These counts describe the current implementation, not adoption or customer impact.

5
Registered flows

SaaS onboarding and invites, creator, AI / dev tools, and health onboarding.

30
Catalog screen definitions

Across the five registered flows, with theme-aware rendering.

3
React code targets

Shadcn, Radix and plain Tailwind, using a shared translation pipeline.

What this says about my work

I took a recurring design problem, defined the product, designed the workflow and developed Ray entirely with Claude Code. The critical work was deciding how the parts should connect: what to generate, what to preserve, where AI belongs and how a designer continues after generation.

What I would measure next

Time to an approved system, correction effort after generation, re-theme coverage on existing files, and code usability in a real application. A faster first pass matters only if the output stays useful.

Built through an AI-native process.

Claude Code handled the implementation. I directed the product, experience and system architecture. This describes the development approach; inside Ray, deterministic builders and optional runtime AI still have separate responsibilities.

Design and code workspaces brought together in a cinematic product studio

From solving my own workflow to building a tool for it.

I didn’t stop
at the design.

Arrivals / Contact

Every great journey starts
with a conversation.

Open to senior product design roles and thoughtful product collaborations.