Courses

Zero to Design Engineer

For UX and product designers who want to write real TypeScript, ship React and Vue components, and contribute to a shared engineering codebase without breaking things. You'll build almost everything with Claude Code.

11 Modules
1 Capstone
~8h Per week
10–12 Weeks

You already know design. You understand users, hierarchy, interaction patterns, and what good looks like on screen. What you're adding is the ability to build it in code and land it in a shared codebase without breaking anything.

You don't need to memorize syntax. You need to understand concepts well enough to direct Claude Code, read what it produces, catch when it's wrong, and fit it into the team's codebase. That's the job.

Module 00

Orientation & Environment Setup

set up your workspace before anything else

Before any code, you need a working environment. Think of it like setting up your Figma workspace and shared libraries for the first time. It's tedious and you only do it once, but skipping it makes everything downstream harder.

VS Code

Your code editor. Free, standard everywhere. Install it first.

Claude Code

Your primary build tool. Install it and connect it to a project folder.

Node.js

The runtime that lets JavaScript/TypeScript run locally. Install the current LTS version.

Git

Version control — how teams share code. Install now, learn deeply in Module 6.

GitHub Account

Where shared repositories live. Free tier is fine.

Terminal

VS Code has one built in — use it. It's how you run commands.

The mental model shift: In design tools, your file is the artifact. In code, source files are instructions and a build process turns them into the running app. Edit text, compile, view in browser, repeat. That loop is everything.

Ask Claude Code to verify your setup: "Check that Node, npm, and git are installed and tell me the versions. If any are missing, tell me exactly how to install them on my OS." Get comfortable using it to diagnose your environment. You'll do this a lot.

  • Install VS Code, Node.js, Git, and Claude Code
  • In a terminal, run node --version, npm --version, and git --version — confirm each prints a version number
  • Create an empty folder, open it in VS Code, and open the built-in terminal inside it

You can open a project folder in VS Code, open a terminal inside it, and all three version commands return numbers without errors.

Module 01

The Web Platform: HTML, CSS & the Browser

the native language of layout and visual design

You already think in components, layout, spacing, and states. HTML and CSS are the native language of those ideas. Once you understand how the browser lays things out, CSS starts making sense instead of feeling arbitrary.

HTML is structure and meaning. Every element has a semantic purpose. Use the right element — a <button> not a <div> that acts like one — for accessibility and readability.

HTML
<article>
  <h2>Card title</h2>
  <p>Supporting copy.</p>
  <button>Take action</button>
</article>

Modern layout = Flexbox and Grid. Flexbox is one-directional (think Figma auto-layout). Grid is two-dimensional (think layout grids with defined columns).

CSS
.row {
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
}

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

DevTools are your Figma inspector. Right-click → Inspect on any page. See the HTML tree, the CSS on any element, and edit live. This is where you'll diagnose layout issues forever.

Claude Code writes HTML/CSS fluently. Your job is to evaluate it. Is the markup semantic? Is spacing using consistent tokens or magic numbers? Ask it to explain why it used grid instead of flexbox, and make sure the answer actually makes sense. That's how judgment builds.

  • Build a static "profile card" in plain HTML and CSS — avatar, name, role, a button. No frameworks.
  • Use Flexbox to lay out three cards in a responsive row that wraps on small screens
  • Open DevTools on a site you admire, find a button, and identify its padding, font-size, and border-radius

You can look at a rendered component, roughly predict the HTML structure and key CSS, and use DevTools to confirm or correct your guess.

Module 02

JavaScript Fundamentals

the essential 20% that covers 80% of what you'll read

JavaScript is where things do something: clicks, data, logic, state changes. You need to be able to read it, trace what it does, and catch when Claude Code's logic is wrong. This is the module where the real work starts.

Variables & types: const for values that don't change, let for ones that do.

JavaScript
const name = "Ada";            // string
const count = 3;              // number
const isActive = true;        // boolean
const tags = ["ux", "code"];   // array
const user = { id: 1, name: "Ada" }; // object

Array methods — learn these three cold. They're 80% of real UI code:

JavaScript
// .map() — transform every item (data → components)
const names = users.map((u) => u.name);

// .filter() — keep items that match a condition
const admins = users.filter((u) => u.role === "admin");

// .find() — get the first match
const ada = users.find((u) => u.id === 1);

Async basics — fetching data takes time. async/await waits without freezing the UI:

JavaScript
const loadUser = async (id) => {
  const response = await fetch(`/api/users/${id}`);
  const data = await response.json();
  return data;
};

Use Claude Code as a tutor here, not just a generator. Paste a snippet and ask it to walk you through what it does line by line, as if you're a designer learning to code. Read every line. If you can't explain it, ask before you accept it.

  • Given an array of product objects, use .filter() and .map() to produce an array of names of products under $50
  • Write a function that takes a user object and returns a greeting string
  • Read three short JavaScript snippets Claude Code generates and explain each out loud before running them

You can read a 20-line piece of UI logic — loops, conditionals, function calls — and explain what it does and what it would render, without running it first.

Module 03

TypeScript

a design system with strict component props — for data

TypeScript adds types to JavaScript: labels that describe what shape your data is. A Button that only accepts variant: "primary" | "secondary" won't let you pass "purple" by accident. Types catch that class of mistake before the app runs. Every serious team codebase uses it.

Interfaces describe the shape of an object — like documenting the props of a Figma component:

TypeScript
interface User {
  id: number;
  name: string;
  role: "admin" | "member";  // union — only these values allowed
  avatarUrl?: string;           // ? = optional
}

const greet = (user: User): string => `Hi, ${user.name}`;

Reading type errors is the real skill. They almost always say "you said this was an X, but here it's a Y." Read the last line of the error first. Ask Claude Code: "explain this error in plain terms and show me the smallest fix."

When Claude Code generates an interface, read it like a spec. "So a Product always has a price as a number and an optional discount." When you hit a red squiggly, paste the error and ask Claude Code to explain it. Over time you'll start predicting the fixes before you ask.

  • Define an interface for a Product — id, name, price, optional tags array
  • Write a typed function that takes a Product[] and returns the total price
  • Intentionally pass the wrong type into a function, read the TypeScript error, and fix it

You can read an interface and describe in plain English what data is required vs. optional, and interpret a basic type error and fix it without panic.

Module 04

React: Thinking in Components

where your design brain and code brain finally merge

You already think in components. You build them in Figma every day. React is the same idea made interactive. A React component is a function that returns UI, and your design background gives you a real head start here.

Props are inputs — exactly like component properties/variants in Figma:

TSX — Component with Typed Props
interface CardProps {
  name: string;
  role: string;
}

function ProfileCard({ name, role }: CardProps) {
  return (
    <article className="card">
      <h2>{name}</h2>
      <p>{role}</p>
    </article>
  );
}

// Used like:
<ProfileCard name="Ada" role="Design Engineer" />

State is data that changes over time and re-renders the UI — think "interactive variants that respond to the user":

TSX — State with useState
import { useState } from "react";

function FavoriteButton() {
  const [favorited, setFavorited] = useState(false);
  return (
    <button onClick={() => setFavorited(!favorited)}>
      {favorited ? "★ Saved" : "☆ Save"}
    </button>
  );
}

Rendering lists.map() turns data into components. Always include a key prop:

TSX — List Rendering
{products.map((product) => (
  <ProductCard
    key={product.id}
    name={product.name}
    price={product.price}
  />
))}

Describe what you want the way you'd brief an engineer: "Build a ProductCard that takes name, price, and an optional badge. Use our spacing tokens. Include hover and loading states." Then review the output like a design critique. Are the props sensible? Is state in the right place?

  • Build a Button with a variant prop ("primary" | "secondary") that changes its styling
  • Build a ProductCard that takes typed props and renders them
  • Build a list view that maps an array of data into ProductCard components
  • Add a favorite toggle — a piece of state per card that switches between saved and unsaved

You can describe any UI as a tree of components with props and state, decide what belongs where, and read a React component someone else wrote and explain what renders and when.

Module 05

Organizing Code Like a Team Does

information architecture applied to files

Organizing a codebase is design-systems thinking applied to files. Messy, inconsistent code gets rejected in review the same way an inconsistent design does. Clean file structure is one of the first things engineers look for in a PR from someone new.

Project Structure
src/
  components/     # reusable UI (Button, Card, Modal)
  features/       # components grouped by product area
  hooks/          # reusable logic (useAuth, useProducts)
  lib/ or utils/  # helper functions
  types/          # shared TypeScript interfaces
  styles/         # tokens, global styles

Design tokens in code — your design system's values become CSS variables. Often literally your job as a design engineer:

CSS — Design Tokens
:root {
  --color-primary: #C4633F;
  --space-2:  8px;
  --space-4:  16px;
  --space-8:  32px;
  --radius-md: 4px;
}

The golden rule: Follow the team's existing pattern, not your personal preference. One component per file, PascalCase names: ProductCard.tsx exports ProductCard. Consistency is worth more than your favorite approach.

Ask it to split a bloated component: "This component is doing too much. Move the data-fetching into a hook and keep the component presentational." But you own the structure decisions. You're the one defending them in code review.

  • Take your Module 4 components and organize them into the proper folder structure
  • Extract colors and spacing into a CSS variables tokens file — no more hard-coded values
  • Pull repeated logic out into a utils function or custom hook

You can look at a feature and decide where each file should live, name things consistently, and explain why code belongs in a component vs. a hook vs. a utility.

Module 06

Git & Version Control

the gateway skill — without this you can't contribute

Git is version history, branching, and multiplayer for code. Without it you cannot contribute to a shared repo. You'll use about eight commands 95% of the time, and Claude Code can run and explain every one of them.

Terminal — Daily Git Workflow
# 1. Get the latest team code before starting
git pull

# 2. Create a branch for your work
git checkout -b feature/product-card-hover

# 3. Do your work and save files...

# 4. Stage your changes
git add .

# 5. Commit with a clear, present-tense message
git commit -m "Add hover state to ProductCard"

# 6. Send your branch to GitHub
git push

# 7. Open a Pull Request on GitHub ↗

main / trunk

The shared, must-always-work version. Never commit directly to it on a team.

branch

An isolated copy to work on. Changes stay isolated until merged.

commit message

Like a good layer name — short, clear, present-tense. "Fix spacing on mobile nav" not "stuff."

merge conflict

Two people changed the same lines. Git asks which to keep. Alarming but routine.

Claude Code can run git commands and narrate what each one does. Ask it: "Show me exactly what changed, write a clear commit message, and explain each step." When something goes wrong, paste the error and ask for the safe fix. Repeat the loop until it's automatic.

  • Initialize a Git repo for one of your projects and make your first commit
  • Create a branch, make a change, commit it, and merge it back to main
  • Deliberately create a tiny merge conflict and resolve it with Claude Code's help
  • Push a branch to GitHub

You can run the full branch → commit → push loop without looking it up, write a clear commit message, and stay calm when you hit a conflict because you know it's just a question to answer.

Module 07

Working in a Shared Team Repository

the actual job — shipping code engineers trust

The earlier modules were practice. This one is the actual job: getting your code into a codebase others depend on, through the team's process, without breaking anything. Doing this well is how engineers start trusting your work.

Pull Requests (PRs) — how you propose changes. A good PR is:

Small & focused

One logical change — like a tight design crit, not a 200-screen dump

Clear description

What changed and why. Engineers should never wonder what you did.

Before/after screenshots

For any UI change. Your design instincts make you great at this — own it.

CI passing ✓

Automated checks (build, lint, tests) must be green before requesting review.

Code review is design critique with a different medium. Respond graciously, make changes, re-request review. Don't take it personally. It's how quality stays high. Follow the team's pattern, not your personal preference.

Use it to onboard fast. Ask: "Give me a map of this codebase. Where do components live, how is styling done, what conventions do they follow?" Before writing anything, ask it to find an existing component similar to yours so you can match the pattern.

  • Clone a real open-source repo. Have Claude Code map it, then verify by exploring yourself.
  • Find an existing component and describe the team's patterns: file structure, styling, naming
  • Make a tiny safe change, open a PR with a proper description and screenshot, and walk it through CI

You can land in an unfamiliar repo, identify how the team does things, make a focused change matching their patterns, open a clean PR, get it through CI and review, and merge it.

Module 08

Vue Translation

if your team uses Vue — everything maps directly over

Vue and React solve the same problems with slightly different syntax. If your team uses Vue, everything from Modules 1–7 still applies. Only the component syntax changes.

Vue 3 — Single-File Component
<script setup lang="ts">
import { ref } from "vue";
interface Props { name: string; role: string; }
defineProps<Props>();
const favorited = ref(false);
</script>

<template>
  <article class="card">
    <h2>{{ name }}</h2>
    <p>{{ role }}</p>
    <button @click="favorited = !favorited">
      {{ favorited ? '★' : '☆' }}
    </button>
  </article>
</template>

<style scoped>
.card { padding: var(--space-4); }
</style>

The concept map — React → Vue:

ReactVue 3What it does
.tsx component.vue SFCThe component file format
Props interfacedefineProps<Props>()Typed component inputs
useState(false)ref(false)Reactive state
useEffectwatch / onMountedSide effects and lifecycle
.map() in JSXv-for in templateRendering lists
&& / ternaryv-if / v-showConditional rendering
onClick={fn}@click="fn"Event handling
classNameclassCSS class attribute

TypeScript, code organization, git, PRs, and testing are identical between React and Vue. The framework is the smallest part of the job.

Tell Claude Code your team uses Vue 3 with the Composition API and <script setup> and it will match that style. Ask it to "show this component in both React and Vue side by side" to cement the mapping.

You can read a Vue SFC and map each part to the equivalent React concept, and build a basic Vue component with typed props and reactive state.

Module 09

Testing & Smoke Testing

verify your work before someone else has to

Smoke testing means checking that the basics work before you ship. For a design engineer, that means verifying your component handles the obvious cases well enough that you're not the one who broke something in the next release.

1. Build passes

App builds with no errors in the terminal

2. Happy path works

The main use case renders and functions correctly

3. Edge states

Empty, loading, and error states all render

4. Responsive

Works at mobile and desktop widths

5. No console errors

Zero new red errors in the browser console

6. Keyboard nav

Tab through it. Focus states visible.

7. Edge case text

Very long text, empty strings — layout holds?

8. Lint + types

Run lint and typecheck locally before pushing

Automated tests verify behavior without clicking. Vitest + Testing Library is the common tool:

TSX — Component Test
import { render, screen } from "@testing-library/react";
import { ProductCard } from "./ProductCard";

test("shows the product name", () => {
  render(<ProductCard name="Notebook" price={12} />);
  expect(screen.getByText("Notebook")).toBeInTheDocument();
});

test("shows empty state when no products", () => {
  render(<ProductList products={[]} />);
  expect(screen.getByText(/no products/i)).toBeInTheDocument();
});

Ask it to write smoke tests covering the happy path, empty state, and error state. Then read them to confirm they actually test the right things. A test that always passes tells you nothing.

  • Run your ProductCard through the smoke-test checklist manually
  • Have Claude Code write three component tests; read each and confirm what it's verifying
  • Deliberately break a component and watch a test fail. Fix it and watch it pass.
  • Run lint, typecheck, and tests locally and get them all green

Before opening any PR, you instinctively run through the smoke-test checklist. You can read a test and say what it verifies. You catch your own broken states before a teammate does.

Module 10

Building Effectively with Claude Code

direction, review, and judgment — not just generation

This is your primary build tool, so using it well matters. A designer who generates code and ships it without understanding it is not a design engineer. The job is judgment: knowing what to ask for, how to review what comes back, and when to verify before accepting.

Brief like an engineer

Include: the stack, conventions, the file it lives in, props, states, edge cases. Vague in, vague out.

Work in small steps

One component at a time. Review, then continue — same as iterating on a design.

Review like a critique

Understand every part. Does it match team patterns? What edge cases did it miss?

Use it to learn

Ask "explain this," "why this approach," "what would an engineer flag?" Every feature is a lesson.

Verify, don't assume

Run it. Smoke-test it. Check the console. AI-generated code can be confidently wrong on edge cases.

Own the architecture

Let Claude Code write the code. You decide the structure, component boundaries, and where state lives.

The full end-to-end workflow:

  1. Understand the task and existing code — map relevant files before writing anything
  2. Brief clearly, referencing existing patterns and conventions in the repo
  3. Generate in small steps, reviewing and understanding each piece before continuing
  4. Integrate into the right files with the right structure and naming
  5. Smoke test against your Module 9 checklist — every state, every edge case
  6. Run lint, typecheck, and tests locally — get to green before pushing
  7. Commit with clear messages, push, open a focused PR with description and screenshots
  8. Respond to review, understand each change before pushing it, then merge

You can take a feature from "design is ready" to merged in the team repo, directing Claude Code throughout, and you understand and can defend every line that ships.

★ Capstone

Build & Ship: Saved Items Feature

everything connects — design to code to merged PR

Feature: Saved Items

Build a product list app with a "Saved Items" feature from scratch. A list view that fetches and renders typed ProductCard components, a save/favorite toggle per card, a "Saved" filtered view, empty/loading/error states handled, responsive and keyboard-accessible, organized into a proper file structure using design tokens.

The process:

  1. Scaffold a fresh React + TypeScript + Vite project (have Claude Code do it)
  2. Build in small, reviewed steps — own all structure and architecture decisions yourself
  3. Type everything: props, state, data fetching, utility functions
  4. Write a smoke-test checklist and run through it. Write component tests for key behaviors.
  5. Run lint, typecheck, and tests until all are green
  6. Initialize a Git repo, commit in logical chunks with clear messages
  7. Open a PR against a main branch with a proper description and screenshots
  8. Write a short reflection: what did you understand vs. just accept?

You're a design engineer when you can do all of that, explain every decision, and honestly say you understand and stand behind the code. That's where this course is taking you.

Ref

Engineer Vocabulary Cheat Sheet

speak the same language in standups and reviews
TermWhat it means
repo / repositoryThe project's codebase, version-controlled with Git
branchAn isolated copy of the code to work on — changes stay here until merged
commitA saved snapshot of changes with a descriptive message
PR / pull requestA proposal to merge your branch into the main code, reviewed by teammates
mergeCombining a branch into another — usually feature branch → main
main / trunkThe primary, always-working version. Never commit directly to it on a team.
CI / CI checksAutomated build/lint/test that runs on every PR. Green ✓ = clear to merge.
lintAutomated code-style and error checking — must pass before merging
type errorTypeScript saying your data shape doesn't match the declared type
propsInputs passed into a component from its parent
stateData that changes over time and causes the UI to re-render
hookReusable React logic — a function starting with "use" (useState, useEffect…)
refactorRestructure code without changing its behavior — for clarity or reuse
buildThe process of turning source code into the runnable app (often via Vite)
consoleThe browser's log/error panel in DevTools. Always check for errors here.
smoke testA quick check that the basics work before shipping
regressionA change that accidentally breaks something that previously worked
tokenA named design value (color, spacing) used consistently in code
ship it / land itGet the change merged and deployed to production
LGTM"Looks good to me" — how engineers signal PR approval
nitA minor review comment, non-blocking — fix it, don't sweat it
blockingA review comment that must be resolved before the PR can merge