Session 1: Foundations & the Design-to-Code Mental Model

Session 1 of the "Designing & Shipping with shadcn/ui in Figma" series, written for Gordon, a designer building the Aster studio landing page. It closes the design-to-code gap by teaching the three layers: Tailwind supplies utility-first CSS, Radix supplies headless accessible behavior, and shadcn/ui supplies styled components that you copy into your repo and then own. The deck's key idea is the difference between copying components in and depending on a closed library. It includes a guided tour of the Figma shadcn kit - the Pages, the Assets panel, and the Variables panel - mapped one-to-one onto code, a "which layer owns this?" decision recipe, four checks, three traps, and a scaffolded your-turn build in which Gordon recreates an Aster profile card from the kit's Card, Avatar, Badge, and Button parts. The homework is to browse ui.shadcn.com and list five components the Aster site will lean on.

Subject: shadcn/ui + Figma · 97 slides · applied lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. Foundations & the Design-to-Code Mental Model

Title

Session 1 · shadcn/ui + Figma

Designers hand off pixels; developers rebuild in a system. This series closes that gap - starting with how Tailwind, Radix and shadcn actually relate.

2. What you will be able to do

Objectives

You already know Figma. By the end of this session you can speak the three words developers use all day - Tailwind, Radix, shadcn - and know exactly which one does what. Concretely, you can:

3. The design-to-code gap

Concept

Figure (svg): A design artboard on the left and a code file on the right with a jagged gap between them

The classic handoff: a designer exports a pixel-perfect frame, a developer eyeballs it and rebuilds it from scratch in their own system. Spacing drifts, colors get re-picked, and the two artifacts slowly diverge.

The fix isn't a better export - it's a shared system on both sides. When your Figma kit and the code are built from the same components and the same tokens, handoff becomes translation, not reconstruction.

This session gives you the vocabulary for that shared system. Everything after builds your Aster studio landing page on top of it.

4. Where we're headed: Aster

Concept

The running project across all six sessions is Aster - a landing page for your own freelance design studio. Brand: a violet primary oklch(0.55 0.24 285), base radius 0.625rem, project folder aster-site.

The page has five sections we'll return to constantly: navbar, hero, feature grid, pricing, footer. Today stays entirely in Figma - no terminal yet - but every example pushes Aster one step forward.

The three layers
Tailwind, Radix, shadcn - what each one owns.
Copy-in, own it
Why shadcn lives in YOUR repo, not npm.
The Figma kit
Pages, Assets, Variables - mirrored 1:1 in code.

5. The Three Layers

Section

Part 1

6. Layer 1 - Tailwind

Concept

Tailwind is a utility-first CSS system. Instead of writing a stylesheet with named classes like .card { padding: 16px }, you compose the style directly in the markup out of tiny single-purpose classes.

Tailwind — A utility-first CSS system: you style an element by composing many small, single-purpose classes (p-4, flex, gap-2, bg-primary) right on the element, instead of writing separate CSS rules.

Each class maps to one declaration. p-4 sets padding: 1rem, flex sets display: flex, gap-2 sets gap: 0.5rem. Nothing is prebuilt - Tailwind gives you the paint, not the furniture.

7. Break it if you can: Layer 1 - Tailwind

Counterexample

Discussion prompt

Each class maps to one declaration. p-4 sets padding: 1rem, flex sets display: flex, gap-2 sets gap: 0.5rem. Nothing is prebuilt - Tailwind gives you the paint, not the furniture.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

8. Tailwind: a class string, decoded

Worked example

Here's a small chunk of the Aster hero's call-to-action row. Every class is one CSS declaration - read it left to right.

<div class="flex items-center gap-3 p-4">
  <!-- flex row, vertically centered, 12px gap, 16px padding -->
</div>

The spacing scale is fixed and predictable: base unit is 0.25rem = 4px, so gap-3 = 12px and p-4 = 16px. No magic numbers - the scale keeps every gap on the grid.

classCSS it produces
flexdisplay: flex
items-centeralign-items: center
gap-3gap: 0.75rem (12px)
p-4padding: 1rem (16px)

9. Fill in: CSS it produces for Tailwind: a class string, decoded

Comparison

Comparison matrix

From Tailwind: a class string, decoded: refill the CSS it produces column from what you know. The rest of the table is as it appeared.

classCSS it produces
flexdisplay: flex
items-centeralign-items: center
gap-3gap: 0.75rem (12px)
p-4padding: 1rem (16px)

10. Layer 2 - Radix

Concept

Radix (Radix Primitives) is a set of headless behavior primitives. It ships the hard, invisible parts of an interactive component - focus management, keyboard navigation, ARIA roles - and no styling at all.

Radix — Unstyled ('headless') accessible UI primitives. Radix provides behavior and accessibility - focus trapping, keyboard nav, correct ARIA roles - but ships zero visual styling; you bring the skin.

Think of a dialog: Radix guarantees focus is trapped inside it, Esc closes it, focus returns to the trigger, and screen readers announce it correctly. What it looks like is entirely up to you.

11. By analogy: Layer 2 - Radix

Analogy

Discussion prompt

Explain Layer 2 - Radix by analogy to something with no shadcn/ui + Figma in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

Think of a dialog: Radix guarantees focus is trapped inside it, Esc closes it, focus returns to the trigger, and screen readers announce it correctly. What it looks like is entirely up to you.

12. Radix: behavior with no skin

Worked example

A raw Radix primitive renders correct structure and behavior but is visually unstyled - it looks like nothing until you add classes.

// Radix gives the behavior + ARIA, no styling:
<Dialog.Root>
  <Dialog.Trigger>Open</Dialog.Trigger>
  <Dialog.Content>...</Dialog.Content>
</Dialog.Root>

Out of the box this traps focus, wires aria-* attributes, and handles Esc and Tab. But it has no padding, no background, no rounded corners. Radix is the skeleton.

Getting accessibility right by hand is genuinely hard. Radix is why shadcn components are accessible without you having to think about ARIA on day one.

13. Layer 3 - shadcn/ui

Concept

shadcn/ui is the assembled layer: styled components built by putting a Tailwind skin on Radix behavior. A shadcn Button is Radix-grade behavior plus a class string that gives it Aster's look.

shadcn/ui — A collection of styled UI components built on Radix primitives and Tailwind. You copy the component source into your own repo (components/ui/) and own it - it is not an installed, locked dependency.

Crucially, shadcn is not a package you install and hide behind. You copy each component's source into your project and it becomes your code - the single most important idea in this deck, and the whole of Part 2.

14. Take the definitions apart: Radix vs shadcn/ui

Definition probe

Sort into buckets

Every line below is part of the definition of Radix or of shadcn/ui — one or the other, never both. Put each where it belongs.

Radix
Unstyled ('headless') accessible UI primitives.; Radix provides behavior and accessibility - focus trapping, keyboard nav, correct ARIA roles - but ships zero visual styling
shadcn/ui
A collection of styled UI components built on Radix primitives and Tailwind.; You copy the component source into your own repo (components/ui/) and own it - it is not an installed, locked dependency.
b1
Unstyled ('headless') accessible UI primitives. Radix provides behavior and accessibility - focus trapping, keyboard nav, correct ARIA roles - but ships zero visual styling; you bring the skin.
b2
A collection of styled UI components built on Radix primitives and Tailwind. You copy the component source into your own repo (components/ui/) and own it - it is not an installed, locked dependency.

15. Three stacked layers

Intuition

Figure (svg): Three stacked layers: Radix skeleton at the bottom, Tailwind paint in the middle, shadcn assembled component on top

Read the stack bottom-up. Radix is the skeleton: it moves correctly. Tailwind is the paint: it composes the look. shadcn/ui is the assembled, finished component that combines both - and it lives in your repo.

Each layer has one job. When something's wrong, this stack tells you where to look: broken keyboard focus is a Radix concern; a wrong color is a Tailwind/token concern; a component's whole shape is shadcn.

16. Something is wrong here: 'Radix gives you the pretty styling'

Anomaly

Predict first

A student writes this, and it looks reasonable:

Assuming Radix is what makes a shadcn Button look good.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: There's nothing to change - Radix ships zero styling.

Radix owns behavior; Tailwind and shadcn own the skin.

Why: There's nothing to change - Radix ships zero styling. You'd find no colors, no padding, no radius in it.

17. Trap: 'Radix gives you the pretty styling'

Trap

The trap

Assuming Radix is what makes a shadcn Button look good.

Reach for Radix to change the button's color and radius

Why: There's nothing to change - Radix ships zero styling. You'd find no colors, no padding, no radius in it.

Conclude 'Radix must be broken - it has no styles'

Why: It's working as designed. Headless means behavior only; the look was never Radix's job.

The fix

Radix owns behavior; Tailwind and shadcn own the skin.

Let Radix handle focus, keyboard, and ARIA

Why: That's the invisible, hard-to-get-right part - exactly what a headless primitive is for.

Change the color and radius via Tailwind classes / tokens in the shadcn component

Why: The look lives in the class string and the CSS variables - the layer you actually edit.

18. Something is wrong here: 'Tailwind is a component framework like Bootstrap'

Anomaly

Predict first

A student writes this, and it looks reasonable:

Expecting Tailwind to hand you prebuilt, styled components.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Those don't exist. Bootstrap ships components; Tailwind ships only utilities.

Tailwind gives utilities; the component comes from shadcn (or you).

Why: Those don't exist. Bootstrap ships components; Tailwind ships only utilities.

19. Trap: 'Tailwind is a component framework like Bootstrap'

Trap

The trap

Expecting Tailwind to hand you prebuilt, styled components.

Search Tailwind for a ready-made .card or .btn-primary

Why: Those don't exist. Bootstrap ships components; Tailwind ships only utilities.

Assume class="card" will render a styled card

Why: card isn't a Tailwind class. You get an unstyled div - nothing prebuilt appears.

The fix

Tailwind gives utilities; the component comes from shadcn (or you).

Compose the look from utilities: rounded-lg border bg-card p-6

Why: You build the card by stacking single-purpose classes - Tailwind is paint, not furniture.

Or drop in the shadcn Card component

Why: The prebuilt, styled component is shadcn's job - built on top of Tailwind utilities.

20. Break it on purpose: 'Tailwind is a component framework like…

Break the constraint

Discussion prompt

The rule this trap just fixed:

You build the card by stacking single-purpose classes - Tailwind is paint, not furniture.

Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?

Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.

Answer:

Those don't exist. Bootstrap ships components; Tailwind ships only utilities.

21. How the three fit together

Concept

One sentence to keep: shadcn = Radix (behavior) + Tailwind (styling), copied into your repo. Two dependencies do the heavy lifting; the visible component is yours.

LayerOwnsAster example
Tailwindutility-first styling (paint)gap-3 p-4 bg-primary on the hero CTA
Radixbehavior + accessibility (skeleton)focus trap on the pricing FAQ accordion
shadcn/uiassembled styled component you ownthe <Button> in the navbar

22. What each one costs: How the three fit together

Trade off

Comparison matrix

From How the three fit together: every row here is a choice with a cost. Fill the Owns column, then say which row you would actually pick and what you give up for it.

LayerOwnsAster example
Tailwindutility-first styling (paint)gap-3 p-4 bg-primary on the hero CTA
Radixbehavior + accessibility (skeleton)focus trap on the pricing FAQ accordion
shadcn/uiassembled styled component you ownthe <Button> in the navbar

23. Tailwind's spacing scale is a grid

Intuition

Figure (svg): A ruler showing Tailwind spacing steps 1 through 8 mapping to 4, 8, 12, 16, 24, 32 pixels

Tailwind's spacing isn't arbitrary. One base unit is 0.25rem = 4px, and every step is a multiple of it. So the number in the class is the multiplier: p-4 = 4 units = 16px.

This is why designs stay on a grid. If your Figma Auto-Layout gaps are 8 and 16, they land exactly on gap-2 and gap-4 - no odd 13px values to reverse-engineer at handoff.

24. Teach it back: Tailwind's spacing scale is a grid

Explain it

Discussion prompt

Explain Tailwind's spacing scale is a grid to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Tailwind's spacing isn't arbitrary. One base unit is 0.25rem = 4px, and every step is a multiple of it. So the number in the class is the multiplier: p-4 = 4 units = 16px.

25. Anatomy of a shadcn Button

Concept

A shadcn component isn't a black box - it exposes a small, deliberate set of knobs. The Button, which Aster's navbar and hero both use, has two axes you'll set constantly.

Variants swap the whole look: default, secondary, destructive, outline, ghost, link. Sizes change the footprint: default, sm, lg, icon.

propvaluesAster use
variantdefault / secondary / outline / ghost / linkdefault for the hero CTA, ghost in the navbar
sizedefault / sm / lg / iconlg for the hero, icon for the menu toggle

26. What rests on this: Anatomy of a shadcn Button

Socratic

Discussion prompt

A shadcn component isn't a black box - it exposes a small, deliberate set of knobs. The Button, which Aster's navbar and hero both use, has two axes you'll set constantly.

Suppose that were not true. What is the first thing in Session 1: Foundations & the Design-to-Code Mental Model that would stop working?

Hint: Follow it one step downstream. The answer is whatever was quietly relying on it.

27. Reading the Button in code and Figma

Worked example

In code, you set variant and size as props. In Figma, the exact same choices are the Button instance's variant properties - the twin relationship you'll rely on all series.

// Aster hero CTA
<Button variant="default" size="lg">Start a project</Button>

// Aster navbar link-style button
<Button variant="ghost" size="sm">Work</Button>

Setting variant="ghost" in Figma's properties panel picks the same style set that variant="ghost" picks in code. You are choosing among the component's built-in options, not restyling by hand.

Figma choiceCode prop
Variant = default, Size = lgvariant="default" size="lg"
Variant = ghost, Size = smvariant="ghost" size="sm"
Variant = outline, Size = defaultvariant="outline"

28. The states that matter

Concept

A real component has more than one look. Design each meaningful interactive state, because the code will render all of them - and Radix drives when each applies.

The focus-visible ring is the one designers most often forget. It's an accessibility feature Radix and Tailwind wire up; design it so keyboard users can see where they are on the Aster page.

29. All three layers in one Button

Worked example

Here is a single Aster CTA with each layer's contribution labeled. Read it as: Radix behavior, Tailwind classes, all wrapped in a shadcn component you own.

// shadcn <Button> (your file) = Radix behavior + Tailwind skin
<Button
  className="gap-2 px-4 rounded-lg focus-visible:ring-2"
>
  Start a project
</Button>
PieceLayerWhat it does
<Button> componentshadcn/uithe assembled component in components/ui/
gap-2 px-4 rounded-lgTailwindspacing and radius (radius via --radius token)
focus-visible ring, click/keyboardRadixaccessible focus + interaction behavior

Change the look? Edit the classes. Change the behavior? That's Radix underneath. Need a whole new variant? Edit the component file - it's yours. The three-layer map tells you where to go every time.

30. Copy-In, Own the Code

Section

Part 2 · The key idea

31. Two models of a component library

Concept

There are two ways a component library can reach your project, and the difference decides how much control you have.

Closed / installed (MUI, Ant Design): you npm install a package. The component source lives in node_modules/ - code you don't own and shouldn't edit. You style it only from the outside, through the props and theme options the authors exposed.

Copy-in / owned (shadcn/ui): the CLI copies the component's actual source into your repo, at components/ui/. It's now your file. You read it, edit any line, and it updates only when you choose to re-pull it.

32. node_modules vs your repo

Intuition

Figure (svg): Left: a locked box labeled node_modules holding a closed library. Right: an open editable file labeled components ui button tsx inside your repo

Left: a closed library is a locked box in node_modules/. You can pass it props, but you can't open it and change how it's built.

Right: a shadcn component is an open file in your own source tree. Same as any other file you wrote - fully readable, fully editable, versioned in your git history.

33. What rests on this: node_modules vs your repo

Socratic

Discussion prompt

Left: a closed library is a locked box in node_modules/. You can pass it props, but you can't open it and change how it's built.

Suppose that were not true. What is the first thing in Session 1: Foundations & the Design-to-Code Mental Model that would stop working?

Hint: Follow it one step downstream. The answer is whatever was quietly relying on it.

Answer:

Right: a shadcn component is an open file in your own source tree. Same as any other file you wrote - fully readable, fully editable, versioned in your git history.

34. Owning it: adding an Aster brand variant

Worked example

Say Aster needs a special 'brand' button that isn't one of the built-in variants. With shadcn, button.tsx is your file, so you just add a line to the variant map.

// components/ui/button.tsx - your own file
const buttonVariants = cva("...", {
  variants: { variant: {
    default: "bg-primary text-primary-foreground",
    brand: "bg-violet-600 text-white shadow-lg", // added
  } },
})

Now <Button variant="brand"> works everywhere. You didn't fork a package or fight the library - you edited a file you own.

With a closed library, a variant the authors didn't ship simply isn't available - you'd be stuck overriding styles from the outside.

35. Something is wrong here: 'shadcn is an npm library you install and can't edit'

Anomaly

Predict first

A student writes this, and it looks reasonable:

Treating shadcn like MUI: install it, then wrestle its internals from outside.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: You're fighting a library from the outside because you assume you can't touch its source.

The component's source is already in your repo - just open and edit it.

Why: You're fighting a library from the outside because you assume you can't touch its source.

36. Trap: 'shadcn is an npm library you install and can't edit'

Trap

The trap

Treating shadcn like MUI: install it, then wrestle its internals from outside.

Try to override a component's internal styles with !important in your CSS

Why: You're fighting a library from the outside because you assume you can't touch its source.

Hit a wall: the internal markup you need to change lives in node_modules and won't budge

Why: For a truly closed library that's genuinely impossible - but shadcn was never closed.

The fix

The component's source is already in your repo - just open and edit it.

Open components/ui/button.tsx and change the actual line

Why: It's your file. No override war, no !important - you edit the source directly.

Commit the change like any other code

Why: You own it and it's in your git history; updates happen only when you choose to re-pull.

37. Why copy-in wins for Aster

Concept

For a bespoke brand like Aster, ownership is the whole point: no lock-in, full control, and updates on your terms.

38. The closed-library override tax

Worked example

Here's what ownership saves you from. With a closed library you can't reach the internal element, so you stack ever-more-specific selectors and !important to force a change from outside.

/* fighting a closed library from outside */
.MuiButton-root .MuiButton-label {
  color: var(--primary) !important; /* please win */
}

It's brittle: a library update can rename that inner class and silently break your override. You never touched the real markup - you just shouted louder than it.

// shadcn: edit the one line in YOUR file instead
// components/ui/button.tsx
"text-primary" // done - no !important, no selector war

39. Updates on your terms

Concept

'You own it' cuts both ways: nothing auto-updates behind your back. When shadcn improves a component, you choose whether and when to pull the new version in.

Because both your version and the new one are just files, you diff them - see exactly what changed, keep your Aster edits, and take the upstream improvements you want. No surprise breakage on a routine npm update.

40. A 'Which Layer Owns This?' Recipe

Section

Part 3 · Pattern

41. Rebuild the recipe: Which layer owns this decision?

Ranking

Put in order

These are the steps of Which layer owns this decision?, scrambled. Put them back in order before the next slide shows you.

  1. Is it about behavior - focus, keyboard, ARIA, open/close logic? That's Radix.
  2. Is it about utility styling - spacing, color, radius, layout? That's Tailwind (usually via a token).
  3. Is it the whole assembled component you place and can edit - its structure and variants? That's shadcn/ui (your file).

Why: This is the order the recipe itself gives. Recalling the sequence without the slide in front of you is the difference between recognising the method and being able to run it — most of what goes wrong in practice is a step done out of turn.

42. Which layer owns this decision?

Pattern

When you hit any UI decision, route it to the layer that owns it. Ask these in order:

  1. Is it about behavior - focus, keyboard, ARIA, open/close logic? That's Radix.
  2. Is it about utility styling - spacing, color, radius, layout? That's Tailwind (usually via a token).
  3. Is it the whole assembled component you place and can edit - its structure and variants? That's shadcn/ui (your file).

One nuance: colors and radius flow through design tokens (CSS variables). So 'change the primary color' is a token change that Tailwind classes then read - not a hand-edit on every component.

43. Where does it stop working: Which layer owns this decision?

Edge cases

Discussion prompt

Which layer owns this decision? works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".

Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.

Answer:

When you hit any UI decision, route it to the layer that owns it. Ask these in order:

44. Running the recipe on Aster

Worked example

Four real decisions from the Aster build. For each, ask the recipe's questions and route it to the owning layer before you read the answer.

DecisionQuestion it answersOwner
Make the hero CTA violetutility styling, via a tokenTailwind (--primary token)
Pricing FAQ closes on Escbehavior + accessibilityRadix
Add an Aster-only 'brand' button stylethe assembled component you ownshadcn (edit button.tsx)
Bump gap between feature cards to 24pxutility styling (spacing scale)Tailwind (gap-6)

Notice the first and last are both Tailwind, but the color one goes through a token so it stays consistent everywhere. The recipe scales to every choice you'll make this series.

45. Where does each piece belong: Session 1: Foundations & the Design-to-Code…

Sorting

Sort into buckets

These are the pieces of Session 1: Foundations & the Design-to-Code Mental Model, out of order. Put each one back under the part of the lesson it belongs to.

The Three Layers
Layer 1 - Tailwind; Tailwind: a class string, decoded; Layer 2 - Radix
Copy-In, Own the Code
Two models of a component library; node_modules vs your repo; Owning it: adding an Aster brand variant
A 'Which Layer Owns This?' Recipe
Which layer owns this decision?; Running the recipe on Aster
s1
The Three Layers is where Session 1: Foundations & the Design-to-Code Mental Model puts Layer 1 - Tailwind, Tailwind: a class string, decoded, Layer 2 - Radix. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s2
Copy-In, Own the Code is where Session 1: Foundations & the Design-to-Code Mental Model puts Two models of a component library, node_modules vs your repo, Owning it: adding an Aster brand variant. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s3
A 'Which Layer Owns This?' Recipe is where Session 1: Foundations & the Design-to-Code Mental Model puts Which layer owns this decision?, Running the recipe on Aster. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.

46. Rule out three: Check: place the decision

Elimination

Eliminate the wrong options

Keyboard/Esc handling and focus management for the Aster FAQ accordion - which layer owns it?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. Radix - it's behavior and accessibility
  • B. Tailwind - it's a utility class you add
  • C. shadcn - you write the keyboard code yourself in the component
  • D. A design token in the Variables panel

Survives elimination: A

Why: Focus management, keyboard navigation, and Esc-to-close are behavior and accessibility concerns - exactly what Radix primitives provide. shadcn's Accordion is built on the Radix Accordion, so you get this for free.

47. Check: place the decision

Check

Aster's pricing section has an accordion of FAQs. Pressing Esc should close an open item, and focus should stay managed correctly. Which layer owns that?

Check your understanding

Keyboard/Esc handling and focus management for the Aster FAQ accordion - which layer owns it?

  • A. Radix - it's behavior and accessibility (correct)
  • B. Tailwind - it's a utility class you add
  • C. shadcn - you write the keyboard code yourself in the component
  • D. A design token in the Variables panel

Answer: A

Why: Focus management, keyboard navigation, and Esc-to-close are behavior and accessibility concerns - exactly what Radix primitives provide. shadcn's Accordion is built on the Radix Accordion, so you get this for free.

Why B tempts people
Tailwind only produces CSS declarations (spacing, color, layout). It has no mechanism for keyboard handling or focus - that's not what utility classes do.
Why C tempts people
You could hand-write it, but the whole reason shadcn builds on Radix is so you don't. The behavior comes from the Radix primitive underneath the component.
Why D tempts people
Design tokens are CSS variables for values like color and radius. Keyboard behavior isn't a value a token can hold.

48. Answer it before you see the options: Check: closed vs copy-in

Prediction

Predict first

Why can you change a shadcn Button's internal markup but not a closed library's?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: shadcn's source is copied into your repo (components/ui/), so it's your editable file; the closed library's source lives in node_modules and is off-limits

Why: The core distinction: shadcn copies the actual component source into your project so you own and edit it directly, while an installed closed library keeps its source in node_modules where you can only configure it from the outside via props/theme.

49. Check: closed vs copy-in

Check

You need to change the internal markup of a Button - not just its props - so it renders an extra wrapper element. Compare a closed library (MUI) with shadcn.

Check your understanding

Why can you change a shadcn Button's internal markup but not a closed library's?

  • A. shadcn Buttons expose more props, so no source editing is ever needed
  • B. Closed libraries are always slower, so editing is disabled for performance
  • C. shadcn's source is copied into your repo (components/ui/), so it's your editable file; the closed library's source lives in node_modules and is off-limits (correct)
  • D. You install shadcn from npm too, but it ships with an 'unlock' flag

Answer: C

Why: The core distinction: shadcn copies the actual component source into your project so you own and edit it directly, while an installed closed library keeps its source in node_modules where you can only configure it from the outside via props/theme.

Why A tempts people
Props can only do what the authors exposed; they can't restructure internal markup. Ownership of the source - not prop count - is what lets you change the internals.
Why B tempts people
Editability has nothing to do with speed, and closed libraries aren't inherently slow. The block is that you don't own node_modules code.
Why D tempts people
shadcn is not an installed package you unlock - the model is that the CLI copies real source files into your repo in the first place.

50. Touring the Figma shadcn Kit

Section

Part 4

51. The kit mirrors the code 1:1

Concept

The Figma shadcn kit isn't decoration - it's the design-side twin of the codebase. Three panels do the work, and each maps to something concrete in code.

Pages
Organize the kit into sections (primitives, components, examples).
Assets panel
The component library - drag instances onto the canvas.
Variables panel
Design tokens = the CSS variables in globals.css.

52. Which is which: The kit mirrors the code 1:1

Matching

Match the pairs

From The kit mirrors the code 1:1 — match each one to what it actually does. The descriptions have been shuffled.

  • c1. Pages
  • c2. Assets panel
  • c3. Variables panel
  • b1. Organize the kit into sections (primitives, components, examples).
  • b2. The component library - drag instances onto the canvas.
  • b3. Design tokens = the CSS variables in globals.css.

Why: Pages, Assets panel, Variables panel are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.

53. Pages - organizing the kit

Concept

Pages (top-left of the Figma file) split a large kit into navigable sections - for example a cover page, a components page, and an examples page. They're organization only; they don't change how anything renders.

Loosely, a page is like a folder in the project - a way to keep the button gallery separate from the assembled example screens. You'll live mostly on the components and examples pages.

54. Auto Layout is flexbox

Concept

One mapping unlocks most of Figma-to-Tailwind: Auto Layout is flexbox. The controls you already use in Figma have direct Tailwind twins - so the way you lay out Aster's navbar is the way it's coded.

Auto Layout settingTailwind class
Vertical / Horizontalflex-col / flex-row
Gap between itemsgap-* (on the scale)
Paddingp-* / px-* / py-*
Space betweenjustify-between
Resizing: Hug / Fill / Fixedw-fit / flex-1 (or w-full) / a set width

55. The Aster navbar, both ways

Intuition

Figure (svg): A horizontal navbar with a logo on the left and links plus a button pushed to the right by space-between

Aster's navbar is a horizontal Auto-Layout frame with space between the logo and the links. In code that's exactly flex flex-row items-center justify-between - the logo hugs left, the links and Hire button sit right.

You didn't guess the CSS. You read it off the Auto-Layout panel: direction, alignment, and the space-between setting each name their own class.

56. What rests on this: The Aster navbar, both ways

Socratic

Discussion prompt

You didn't guess the CSS. You read it off the Auto-Layout panel: direction, alignment, and the space-between setting each name their own class.

Suppose that were not true. What is the first thing in Session 1: Foundations & the Design-to-Code Mental Model that would stop working?

Hint: Follow it one step downstream. The answer is whatever was quietly relying on it.

57. Composition: components to blocks to pages

Concept

Small parts compose upward. A component (Button) sits inside a block (a whole navbar or pricing section - shadcn calls these 'blocks'), and blocks compose into a page (the full Aster landing page).

Figma mirrors this: instances nest inside a section frame, sections stack into a screen. Same shape on both sides - which is why the kit's example screens read like real page code.

58. The Assets panel - the component library

Concept

Figure (svg): The Assets panel listing components Button, Card, Avatar, Badge, with an arrow dragging a Button instance onto a canvas

The Assets panel is the kit's component library. Every main component - Button, Card, Avatar, Badge - appears here. You drag one onto the canvas to place an instance of it.

Assets panel — The Figma panel that lists all published/available components in a file. Dragging one onto the canvas creates an instance - a live copy that stays linked to the main component.

An instance is the design-side twin of an imported component in code. Placing a Button instance in Figma is the same act as writing <Button /> in a file.

59. What rests on this: The Assets panel - the component library

Socratic

Discussion prompt

The Assets panel is the kit's component library. Every main component - Button, Card, Avatar, Badge - appears here. You drag one onto the canvas to place an instance of it.

Suppose that were not true. What is the first thing in Session 1: Foundations & the Design-to-Code Mental Model that would stop working?

Hint: Follow it one step downstream. The answer is whatever was quietly relying on it.

Answer:

An instance is the design-side twin of an imported component in code. Placing a Button instance in Figma is the same act as writing <Button /> in a file.

60. The Variables panel - design tokens

Concept

The Variables panel holds the kit's design tokens: named values like a primary color or a base radius. Bind a layer's fill to the primary variable and it now references that token instead of a raw hex.

design token — A named, reusable design value (a color, spacing, or radius) stored once and referenced everywhere. In Figma it's a Variable; in code it's a CSS variable like --primary. Change it once, everything bound to it updates.

A bound Figma variable is the exact twin of a CSS variable in globals.css. Set Aster's primary to oklch(0.55 0.24 285) once, and every instance bound to it retints - same as editing --primary in code.

61. Something is wrong here: 'a Figma variable is just a saved color swatch'

Anomaly

Predict first

A student writes this, and it looks reasonable:

Treating a bound variable as a one-time color pick, like grabbing a saved swatch.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: A saved swatch copies a value in and forgets it - no living link remains.

A bound variable is a living reference - a token, the twin of a CSS variable.

Why: A saved swatch copies a value in and forgets it - no living link remains.

62. Trap: 'a Figma variable is just a saved color swatch'

Trap

The trap

Treating a bound variable as a one-time color pick, like grabbing a saved swatch.

Think 'the variable just set this fill to violet once'

Why: A saved swatch copies a value in and forgets it - no living link remains.

Expect nothing to change elsewhere when you edit the variable

Why: If it were only a swatch, editing it wouldn't ripple - but that's not what a variable is.

The fix

A bound variable is a living reference - a token, the twin of a CSS variable.

Bind the fill to the primary variable

Why: The layer now points at the token; it doesn't copy the value, it references it.

Edit primary once

Why: Every layer bound to it retints at once - exactly like changing --primary in globals.css.

63. Which of these survive contact with Session 1: Foundations & the Design-to-Code…?

Two truths and a lie

Sort into buckets

Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.

Holds up
One sentence to keep: shadcn = Radix (behavior) + Tailwind (styling), copied into your repo. Two dependencies do the heavy lifting; the visible component is yours.; A shadcn component isn't a black box - it exposes a small, deliberate set of knobs. The Button, which Aster's navbar and hero both use, has two axes you'll set constantly.; A real component has more than one look. Design each meaningful interactive state, because the code will render all of them - and Radix drives when each applies.
Breaks
Assuming Radix is what makes a shadcn Button look good.; Expecting Tailwind to hand you prebuilt, styled components.
sound
These are stated as this lesson states them — each one survives the edge cases Session 1: Foundations & the Design-to-Code Mental Model puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

64. Variant vs boolean properties

Concept

Component properties in Figma come in kinds, and two matter today. A variant property swaps a whole style set; a boolean property toggles one feature on or off.

Use them the way code does. variant="outline" is a variant (many exclusive options). showIcon is a boolean (a single on/off). Booleans keep the variant set lean - you don't create a separate variant for 'with icon' and 'without icon'.

Figma propertyKindCode twin
variant: default/outline/ghostvariant (pick one)variant="outline"
showIcon: on/offboolean (toggle)showIcon or the icon child

65. Fill in: Code twin for Variant vs boolean properties

Comparison

Comparison matrix

From Variant vs boolean properties: refill the Code twin column from what you know. The rest of the table is as it appeared.

Figma propertyKindCode twin
variant: default/outline/ghostvariant (pick one)variant="outline"
showIcon: on/offboolean (toggle)showIcon or the icon child

66. The Figma-to-code map

Concept

Here is the whole mental model in one table. Each Figma concept has exactly one code twin - that's what makes handoff a translation.

FigmaCode twinAster example
Assets instanceimported component<Button /> in the navbar
Bound variableCSS variable (token)--primary in globals.css
Component propertypropvariant="outline", size="lg"
Pageloosely, a folder/sectionthe 'examples' page

67. What has to happen first: Live in Figma: inspect a Button

Ranking

Put in order

Put the moves of Live in Figma: inspect a Button into the order they have to happen.

  1. Drag Button from the Assets panel onto the canvas
  2. Open the variant dropdown in the properties panel
  3. Check the boolean property (e.g. showIcon)
  4. Inspect the fill - it's bound to the primary variable

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. This creates an instance - the twin of writing <Button /> in code.

68. Live in Figma: inspect a Button

Worked example

Let's read a Button the way its code twin reads. Drop a Button instance on the canvas and open its properties on the right.

Drag Button from the Assets panel onto the canvas

Why: This creates an instance - the twin of writing <Button /> in code.

Open the variant dropdown in the properties panel

Why: You'll see the real shadcn variants: default, secondary, destructive, outline, ghost, link - each maps to a prop value.

Check the boolean property (e.g. showIcon)

Why: A boolean prop toggles one feature on/off, keeping the variant set lean instead of exploding it.

Inspect the fill - it's bound to the primary variable

Why: The bound token is the twin of bg-primary reading --primary; the button follows Aster's brand automatically.

69. Work backwards from the answer: Live in Figma: inspect a Button

Reverse engineer

Discussion prompt

Work backwards. The example finished here:

Inspect the fill - it's bound to the primary variable

What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.

Hint: Every quantity in the result had to enter somewhere. Account for each one.

Answer:

Let's read a Button the way its code twin reads. Drop a Button instance on the canvas and open its properties on the right.

70. Answer it before you see the options: Check: what a bound variable does

Prediction

Predict first

You edit the single primary variable from violet to teal in Figma's Variables panel. What happens?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: Every layer bound to primary retints to teal at once - like changing --primary in globals.css

Why: A bound variable is a living reference, not a copied value. Editing the token updates every layer that references it simultaneously - the exact twin of changing the --primary CSS variable, where every bg-primary re-tints at once.

71. Check: what a bound variable does

Check

Every Aster button, badge, and link in the kit has its fill bound to the primary variable. You change primary from violet to teal, once, in the Variables panel.

Check your understanding

You edit the single primary variable from violet to teal in Figma's Variables panel. What happens?

  • A. Every layer bound to primary retints to teal at once - like changing --primary in globals.css (correct)
  • B. Only the one layer you last selected changes; the rest keep the old violet
  • C. Nothing changes until you re-drag each component from the Assets panel
  • D. It creates a new saved swatch but leaves all existing fills untouched

Answer: A

Why: A bound variable is a living reference, not a copied value. Editing the token updates every layer that references it simultaneously - the exact twin of changing the --primary CSS variable, where every bg-primary re-tints at once.

Why B tempts people
That would be true if each layer held its own copied color. Binding means the layers point at the token, so all of them follow the edit - not just one.
Why C tempts people
Re-dragging isn't needed; the binding is live. The instances already reference the variable, so they update the moment its value changes.
Why D tempts people
That describes a saved swatch, which forgets its link. A variable keeps the reference, so existing bound fills do change.

72. Rule out three: Check: the code twin

Elimination

Eliminate the wrong options

Dragging a Card instance from the Assets panel onto the canvas is the design-side twin of what in code?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. Defining a brand-new CSS variable in globals.css
  • B. Placing an instance of an imported component, like writing <Card /> in a file
  • C. Running npx shadcn@latest add card in the terminal
  • D. Editing the --radius token so corners change

Survives elimination: B

Why: An Assets-panel instance maps 1:1 to an imported component being used. Dragging a Card onto the canvas is the design equivalent of writing <Card /> - you're placing a linked instance of a defined component.

73. Check: the code twin

Check

You drag a Card from the Assets panel onto the Aster canvas. In code, what did you just do?

Check your understanding

Dragging a Card instance from the Assets panel onto the canvas is the design-side twin of what in code?

  • A. Defining a brand-new CSS variable in globals.css
  • B. Placing an instance of an imported component, like writing <Card /> in a file (correct)
  • C. Running npx shadcn@latest add card in the terminal
  • D. Editing the --radius token so corners change

Answer: B

Why: An Assets-panel instance maps 1:1 to an imported component being used. Dragging a Card onto the canvas is the design equivalent of writing <Card /> - you're placing a linked instance of a defined component.

Why A tempts people
Defining a CSS variable is the twin of creating a Variable in the Variables panel, not dragging a component instance from Assets.
Why C tempts people
Running the add command copies the component's source into your repo - it's the setup step. Dragging an instance is how you use the component, which happens after it exists.
Why D tempts people
Editing --radius changes a design token's value; that's a Variables-panel edit, not the act of placing a component instance.

74. From Kit to Real Code

Section

Part 5 · The bridge

75. Responsive: same scale, more breakpoints

Concept

Aster's feature grid is one column on a phone and three on a desktop. In Tailwind that's grid-cols-1 md:grid-cols-3 - the md: prefix applies a class only at that breakpoint and up.

prefixkicks in atAster use
(none)all sizesgrid-cols-1 - stacked on mobile
md:768px+md:grid-cols-3 - three feature cards
lg:1024px+lg:px-8 - roomier page gutters

In Figma you plan this with breakpoints and constraints (pin / center / scale). The key discipline: keep spacing and type on the same scale across breakpoints, so the grid stays coherent.

76. Teach it back: Responsive: same scale, more breakpoints

Explain it

Discussion prompt

Explain Responsive: same scale, more breakpoints to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Aster's feature grid is one column on a phone and three on a desktop. In Tailwind that's grid-cols-1 md:grid-cols-3 - the md: prefix applies a class only at that breakpoint and up.

77. tweakcn - theme the kit visually

Concept

You won't hand-write OKLCH values from memory. tweakcn.com is a free visual theme editor for shadcn/ui: tune colors and radius, preview live, and export both a globals.css and a Figma-compatible theme.

That's the round trip you'll use for Aster next session: pick the violet and 0.625rem radius in tweakcn, export the tokens, and the same values drop into :root/.dark in code and into the Figma Variables panel. One source, both sides.

78. The scaffold you'll run later

Concept

No terminal today - but here's where this all lands. Next sessions you'll scaffold aster-site and pull components in; each command copies real source into components/ui/.

npx shadcn@latest init          # sets up config + globals.css
npx shadcn@latest add button card avatar badge input

Every part you place from the Figma kit today has a twin you'll add then own. The design work you're about to do IS the plan for that code.

79. By analogy: The scaffold you'll run later

Analogy

Discussion prompt

Explain The scaffold you'll run later by analogy to something with no shadcn/ui + Figma in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

No terminal today - but here's where this all lands. Next sessions you'll scaffold aster-site and pull components in; each command copies real source into components/ui/.

80. Your Turn: The Aster Profile Card

Section

Part 6 · Hands-on

81. The build: an Aster profile card

Concept

Time to build. You'll recreate a small Aster profile card entirely from kit parts - no terminal this session. The card shows a team member's avatar, name, an 'Available' badge, and a 'View work' button.

Four kit parts, assembled from the Assets panel. Do each milestone in Figma, then read the self-check to confirm you see the right thing before moving on.

#kit partrole in the card
1Cardthe container frame
2Avatarthe circular profile image
3Badgethe 'Available' status pill
4Buttonthe 'View work' action

82. Break it if you can: The build: an Aster profile card

Counterexample

Discussion prompt

Four kit parts, assembled from the Assets panel. Do each milestone in Figma, then read the self-check to confirm you see the right thing before moving on.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

83. Guess the shape of the answer: Milestone 1 - drop a Card frame

Estimation

Predict first

Your turn: place a Card from the kit as the container for everything else. Give it a moment - where in Figma do the kit's components live?

Commit before you compute: what does Milestone 1 - drop a Card frame come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: Leave its inner Auto Layout as-is for now

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. The Card's vertical Auto Layout is the twin of a flex-col container; the other parts will stack inside it.

84. Milestone 1 - drop a Card frame

Worked example

Your turn: place a Card from the kit as the container for everything else. Give it a moment - where in Figma do the kit's components live?

Hint: open the Assets panel (the component library) and find Card. You drag from there onto the canvas - you don't draw a rectangle by hand.

Open the Assets panel, drag Card onto the canvas

Why: This places a Card instance - the twin of <Card /> - already styled with Aster's border, radius, and padding tokens.

Leave its inner Auto Layout as-is for now

Why: The Card's vertical Auto Layout is the twin of a flex-col container; the other parts will stack inside it.

you should seemeaning
A rounded, bordered framethe Card's --radius and --border tokens are applied
'Card' listed as an instanceit's linked to the main component, not a loose shape

85. Work backwards from the answer: Milestone 1 - drop a Card frame

Reverse engineer

Discussion prompt

Work backwards. The example finished here:

Leave its inner Auto Layout as-is for now

What was it asked to do, and what must it have been given? Reconstruct the problem from its answer.

Hint: Every quantity in the result had to enter somewhere. Account for each one.

Answer:

Your turn: place a Card from the kit as the container for everything else. Give it a moment - where in Figma do the kit's components live?

86. Guess the shape of the answer: Milestone 2 - add the Avatar

Estimation

Predict first

Your turn: put an Avatar at the top of the card for the profile image. Which panel, again, holds the components?

Commit before you compute: what does Milestone 2 - add the Avatar come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: Confirm it renders as a circle

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. The Avatar component ships its own rounded-full styling; you don't set the radius yourself.

87. Milestone 2 - add the Avatar

Worked example

Your turn: put an Avatar at the top of the card for the profile image. Which panel, again, holds the components?

Hint: back to the Assets panel - find Avatar and drag it inside the Card so it joins the card's Auto Layout.

Drag Avatar from Assets into the Card

Why: Dropping it inside makes it a child of the Card's Auto Layout - the twin of nesting <Avatar /> inside <Card />.

Confirm it renders as a circle

Why: The Avatar component ships its own rounded-full styling; you don't set the radius yourself.

you should seemeaning
A circular avatar at the topthe Avatar instance is placed and styled
It sits inside the card, spaced by the gapit joined the Card's Auto Layout (flex-col + gap)

88. What each one costs: Milestone 2 - add the Avatar

Trade off

Comparison matrix

From Milestone 2 - add the Avatar: every row here is a choice with a cost. Fill the meaning column, then say which row you would actually pick and what you give up for it.

you should seemeaning
A circular avatar at the topthe Avatar instance is placed and styled
It sits inside the card, spaced by the gapit joined the Card's Auto Layout (flex-col + gap)

89. Guess the shape of the answer: Milestone 3 - add the Badge

Estimation

Predict first

Your turn: add a Badge that reads 'Available' as a small status pill under the name. Same source as before.

Commit before you compute: what does Milestone 3 - add the Badge come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: Choose a variant (e.g. secondary) from its properties

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. The variant property maps 1:1 to the variant prop - swapping a whole style set, not restyling by hand.

90. Milestone 3 - add the Badge

Worked example

Your turn: add a Badge that reads 'Available' as a small status pill under the name. Same source as before.

Hint: drag Badge from the Assets panel, then use its variant property to pick the style, and edit its text to 'Available'.

Drag Badge into the card and set its text to 'Available'

Why: The Badge instance is the twin of <Badge>Available</Badge>; the text is editable like a prop/child.

Choose a variant (e.g. secondary) from its properties

Why: The variant property maps 1:1 to the variant prop - swapping a whole style set, not restyling by hand.

you should seemeaning
A small pill reading 'Available'the Badge instance is placed with your text
Its color follows the chosen variantthe variant property picked a token-driven style

91. Milestone 4 - add the Button

Worked example

Your turn: finish with a 'View work' Button at the bottom of the card. You know where to get it.

Hint: drag Button from the Assets panel; set its text to 'View work' and confirm its fill is bound to the primary variable so it wears Aster's violet.

Drag Button into the card, set label to 'View work'

Why: The Button instance is the twin of <Button>View work</Button> at the bottom of the card.

Check the fill is bound to primary

Why: The bound token means it already matches Aster's brand - the twin of bg-primary reading --primary.

you should seemeaning
A violet 'View work' buttonthe fill is bound to the primary token
Card, Avatar, Badge, Button all stackedfour instances composed in one Auto Layout

92. Put it together

Worked example

Step back and look at the whole card. Four kit instances, stacked in the Card's Auto Layout, every color and radius coming from Aster's tokens.

Figure (svg): An assembled profile card: a circular avatar at top, a name, an Available badge, and a violet View work button, inside a rounded bordered card

If yours looks like this - a bordered card holding a circular avatar, a name, an 'Available' badge, and a violet button - you assembled a real UI from kit parts, and each part is the twin of a coded component.

93. Show it off - name the code twins

Worked example

Your turn: without touching code, explain how each kit part maps to a real coded component. Say it out loud, part by part - this is the fluency the whole series is building.

Hint: each Assets instance is an imported component; each variant/text you set is a prop; each bound fill is a token. Walk the card top to bottom.

Kit partIts coded twin
Card instance<Card> - the container component
Avatar instance<Avatar> - imported, renders the circle
Badge + variant<Badge variant="secondary">Available</Badge>
Button + primary fill<Button> reading bg-primary from --primary

That mapping - instance to component, property to prop, bound variable to token - is the design-to-code model in miniature. Everything in the coming sessions extends it.

94. Fill in: Its coded twin for Show it off - name the code twins

Comparison

Comparison matrix

From Show it off - name the code twins: refill the Its coded twin column from what you know. The rest of the table is as it appeared.

Kit partIts coded twin
Card instance<Card> - the container component
Avatar instance<Avatar> - imported, renders the circle
Badge + variant<Badge variant="secondary">Available</Badge>
Button + primary fill<Button> reading bg-primary from --primary

95. Homework - the Aster component shortlist

Concept

Homework: browse the component gallery at ui.shadcn.com and get a feel for what's available - Button, Card, Input, Badge, Navigation Menu, and more.

Then write down the five components the Aster site will lean on most across its navbar, hero, feature grid, pricing, and footer. Think about what each section needs - for example a nav/navigation menu, a button, a card, an input, and a badge.

Bring that list to Session 2. It's the shopping list we'll use when we start theming and, eventually, scaffolding the real aster-site project.

96. Connect it up: Session 1: Foundations & the Design-to-Code Mental Model

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — The Three Layers · Copy-In, Own the Code · A 'Which Layer Owns This?' Recipe · Touring the Figma shadcn Kit · From Kit to Real Code · Your Turn: The Aster Profile Card. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

97. What you can do now

Recap

FigmaCode twin
Assets instanceimported component (<Button />)
Bound variableCSS variable / token (--primary)
Component propertyprop (variant, size)
Pagea folder/section, loosely

Next time (Session 2): design tokens in depth - primitive to semantic to mode, OKLCH, and wiring Aster's violet through :root, .dark, and @theme inline so one variable retints the whole site.

Sources

  1. shadcn/ui - Introduction & Theming docs (copy-in components, globals.css tokens)
  2. Tailwind CSS - Utility-First Fundamentals & spacing/type scale
  3. Radix Primitives - unstyled, accessible behavior primitives
  4. Figma Help Center - Pages, Assets panel, and Variables (design tokens)
  5. Facts checked against ui.shadcn.com/docs, tailwindcss.com/docs, radix-ui.com, help.figma.com, tweakcn.com. — Author verification pass, 2026-07-03 (shadcn/ui + Figma series, Session 1).

Want this taught 1-on-1? Alexander tutors shadcn/ui + Figma — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108