Making my design portfolio mine again

Or: How I learned to stop worrying and love CSS

For years, I’ve been a huge Webflow advocate. I’ve built quite a few websites using Webflow, including freelance client websites, marketing websites for startups I’ve worked at, as well as my own portfolio website from 2020-2025 or so. I learned everything via Webflow University videos on YouTube, which were as entertaining as they were informative.

I saw Webflow as a way for designers to better understand how their designs adapt to the web, with the added bonus of creating something that is actually shippable and can be relied on indefinitely. Through my self-education on the platform, I became more aware of flexbox, CSS grid, design tokens and variables, and countless other concepts that helped me understand how websites are built at scale. For this, I thank Webflow. It introduced these concepts into my work, it made me a better designer, and I was even able to book some work because of it.

But something changed over the last few years. As I learned more, my clients’ requests (as well as my own) got more complex, and the tools I relied on needed to grow with them. The tool I happened to leave was Webflow. If you’re on Framer, Squarespace, or Wix, the concepts discussed here still hold up.

Why ditch Webflow?

Up front, I will say that Webflow has great features for many non-technical people to collaborate and edit a large website. Their business model seems to be changing into an enterprise-grade solution for large companies, while eschewing the individual designer/small businesses audience.

There are a few reasons that pushed me to move on. Over the years, the charm of the no-code interface started to feel bloated, clunky, and slow. Simple tasks take too much effort. Here’s an example: Replacing 20+ images with the same filenames on a single page. Seems simple enough, right?

  1. Open the Webflow Designer. Wait for the loading screen.
  2. Navigate to the page you’re changing.
  3. Scroll to find the image on the page.
  4. Click on the image.
  5. Click the Replace Image button.
  6. Navigate to where your image is uploaded in the Assets panel.
  7. Click the image you want (the panel closes automatically afterward).
  8. To keep your Assets panel free of duplicates, reopen it, otherwise the old image stays behind.
  9. Find the old image and click it.
  10. Click the overflow menu on the old image.
  11. Click Delete.
  12. Repeat for the other 20+ images.

That’s twelve steps, times twenty images, for one content update. None of it is particularly hard, but it’s incredibly tedious. And that tedium is the thing that makes you dread updating your site altogether.

But the image swap is a symptom, not the disease. The real reasons I left are harder to screenshot.

My design system lived in three places at once. The Variables panel, the global styles, a graveyard of one-off class overrides I made at 11pm to fix one specific thing, not to mention a bunch of random custom code patches just to work around a limitation. Changing what “blue” meant on my site was never a single edit. If you’ve ever inherited a mature Webflow project, you get what I mean.

My content wasn’t really mine: My writing and case studies lived inside a CMS UI. Exporting them gave me a CSV that I could file away, but this wasn’t ideal. For example, my case studies were essentially just a ton of HTML jammed inside a single cell of the CSV export. I wanted my content to be files. Things I could own, branch and review like everything else. My new setup has each case study as a simple markdown file that I can easily read, update and maintain.

Renting a tool I’ve outgrown: At some point I was paying a monthly subscription for a visual CMS to run a site that is, honestly, mostly static text and images. Not to mention that the price of Webflow has gone up considerably in the past few years; at one point they increased the price from $192/year to $276/year which is a massive 43% increase. (Note: While I was writing this post in late July 2026, Webflow just updated their pricing again to about $300/year, representing a 56% increase from when I started on the platform back in 2021). New features have not been enough to necessitate such a large increase in price, not to mention some of the new stuff they added (ahem, Logic) was introduced with great fanfare, we all used it on our clients’ sites, only to have it deprecated a short while later. I now host my website with Github + Vercel, the latter having a quite generous free plan.

Arbitrary limits: There are many limitations across Webflow which, over time, are hard to ignore, and result in a website that feels fragmented with a bunch of one-off overrides, random custom code implementations to fix simple things, and little “hacks” to get around the limits imposed upon the user by Webflow themselves.

  • At each tier, you’re limited to a set number of fields for your CMS items.
  • You can’t include a collection list (CMS) on a utility page, such as your 404.
  • You can’t nest collection lists in a symbol.

These all sound like individual edge cases but they add up to an experience that feels like you’re constantly fighting against the tool instead of being empowered by it. I want to enjoy building my website, not feel like I am “hacking” it just to get it to do what I want.

I want my portfolio to feel like a product I fully own, end to end. A design system I control, with a thin layer of content on top, not something I am renting in perpetuity, awaiting the inevitable price increase or platform update that might change features I rely on.

You can own this yourself

I’m a designer, not a developer. Three years ago, “just build it in code” would have meant months of actually learning how to code from scratch, which is probably a good skill to learn, but the time commitment is too drastic especially between life, work, leisure and everything else I have going on.

Now I open Cursor, describe what I want, and it writes the code. My job isn’t necessarily to write code from scratch, but rather to understand the scaffolding that supports my content. My job is to know what looks good, to know what I want, and to be willing to open a file and read it.

You don’t need to become an engineer, but it helps to learn a bit. To do this, you need to get comfortable with two things: editing CSS when you want to change how something looks, and using Git to efficiently manage your code and ship updates to the live site. The AI agents do the rest of the heavy lifting.

At first, Git was super confusing and I spent a lot of time learning the basics, and wrapping my head around conceptually how git works. Think of it as version history, or checkpoints in a video game. If you mess it up, you can always roll it back. Cursor has its own checkpoints, but they’re temporary and they miss anything you edit by hand. Git is the permanent record. Learn Git. Everything product designers design is now built in code, so we are getting closer to the final product. It’s a good thing.

Why Astro?

After some research, I chose Astro as the backend/framework for building my site. This was done for a few simple reasons:

  • It’s free and open source (MIT license).
  • Astro is a static site builder and it only renders the necessary code for each page, meaning the site is super fast when browsing.
  • Content is stored in markdown files. I like writing in Obsidian so it’s great to be able to work on a case study or blog post, and simply drop in my content.
  • It has Islands and Components, which simplify the building process. As a non-technical person learning code as I go, this helps me isolate problems and ensure code itself doesn’t become too unwieldy.
  • AI agents like Cursor work with Astro very well, meaning I can move very fast and spin up a website in just a few hours, refine it as I go, then ship super fast.
  • It’s UI agnostic, meaning if you want to add more complexity/interactivity to your site (React, svelte, web components, and so on) it plays nicely and you can just drop it in.

As someone who used to “code up” Myspace layouts back in the day, Astro felt like a fancy version of HTML + CSS that felt like a natural progression of my self-education in web development and building websites.

Design tokens: your Figma variables, in one file

After setting up a new Astro project, the first thing I do is start putting together a simple CSS-based design system. If you use Variables in Figma, you already understand the most important idea in this setup. I have one file, tokens.css, and it holds everything: colors, spacing, radius, type sizes, or you can create a standalone css file for each individual aspect. I just find it helpful for smaller projects to keep it as contained as possible. My tokens are then divided into two layers:

Primitives are the raw scale, the stuff I rarely touch. A Radix-style color ramp from --blue-1 to --blue-12, a spacing scale, type sizes. You can create a 12-step color ramp based on your favorite color with this handy generator by Radix. For type scales, you could use something like Utopia or Typescale.

Semantic tokens are what my components actually use and I try to keep them human-readable and fairly simple. Some examples: --color-accent, --color-text, --color-link, --color-border.

It’s fairly simple in practice: Components and page layouts only ever consume the semantic layer so my primitives can easily be changed/updated in the future, if need be. So my button doesn’t know it’s blue. It knows it’s --color-accent, and --color-accent happens to point at --blue-9. Change that one line, and my accent color, my links, and my focus rings all move together. So let’s say in a year or two I want to redesign my personal brand to use orange instead of blue, I can simply drop in new values for my Accent and the changes required should be fairly minimal.

This is the cascade, and it’s the single most satisfying thing to feel working. It’s also why, in the workshop I’m building, I have people set up their tokens before they build a single page. When you hard-code a color and then have to change it in fifteen places later, you feel the pain. When you change one token and watch the whole page shift, you feel the point.

I also keep a simple Design System page that renders the real tokens. If a token changes, the page changes with it. It’s the closest thing to opening your Webflow style guide, except it’s built from the exact same CSS as the rest of the site. I’d encourage designers to keep their own style guide/design system page as a reference when refining their own design system.

Components, and the thing that actually sold me

Designing in Figma (or Webflow for that matter) gets you used to the concept of designing in reusable symbols/components and Astro is no different, though it’s infinitely more powerful. Astro encourages you to build Component-based architecture straight into your websites and I find this to be the most efficient way of creating a maintainable system that you can use across my portfolio.

I have a bunch of simple components: Header, Section, ProjectCard, Tag, nothing too complex. Each of these can be used as as-is directly on the page, or linked up with a content collection which is like using Webflow CMS (collection lists) template pages so rendering things like projects, case studies, blog posts, etc. is fairly trivial. Just design a template page 1 time and it works for 100 blog posts.

Every case study on my site is a single markdown (.md) file, transformed into an .mdx file once you start adding components. Markdown for the writing, and when I need something structured, I import a component right into the middle of the prose.

It feels much closer to composing with Figma components than filling in CMS fields in Webflow, which felt incredibly limiting when you want to do more complex things in a blog post or case study. Conditional visibility helps but again, these feel like workarounds.

I’ll share one of my favorite components I’ve built for my site, because it replaced an entire production step: MediaFrame which I use on case study pages to render my mockups without requiring a lot of manual effort (I build once and use it dozens of times).

I can even add an optional caption here :)

Here was my old way to make a polished case-study visual:

  1. Record your screen or take a screenshot
  2. Pull the file into After Effects, or drop a screenshot into Figma
  3. Crop it, composite a fake browser/device around it
  4. Export, compress, upload to my website

Every revision meant running that whole loop again. Once your product UI changes (or they release another iPhone), your mockup is stale.

Now the browser frame is code. I hand MediaFrame a raw screen recording (mp4 compressed with FFMPEG) and the component does the rest. It draws a minimal macOS browser window in CSS, traffic-light dots and all. The video autoplays, loops, and stays muted, with a fallback so Safari behaves. The radius, the shadow, and the caption styling all come from my tokens, so every framed visual across a dozen case studies matches automatically.

Here’s what my MediaFrame component looks like when added to my MDX (markdown) file:

<MediaFrame 
  browserChrome
  src="UI-walkthrough.mp4" 
  alt="Alt text" 
  caption="Optional caption"
/>

If I want to restyle every mockup on the site, that’s one CSS edit, not re-exporting a dozen videos. I see this as a huge timesaver and even allows some components to be designed responsively.

For one client website, we display horizontal aspect ratio (16:9) images on desktop (to better suit the larger screen ratio) and square (1:1) images on mobile to better suit the aspect ratio of the phone. In this instance, we simply added in a media query to the component itself to render a different image/video asset based on the viewer’s screen size, and all my client needs to do is upload 2 assets (desktop & mobile) which are both linked in the MediaFrame component instance.

When you go about trying this on your own site, don’t start by building a massive component library. Build one real page first, get it looking right, and then pull the repeated parts out into components. Components should be discovered, not designed up front. You’ll make far better ones once you’ve seen where they actually repeat. Of course, simple atomic components (inputs, buttons, dropdowns) can easily be imported and styled using a UI system like Radix, which simplifies the process and ensures everything is properly accessible and correctly built.

Translating Webflow CMS into Astro

Here’s the rough translation of webflow things to what you’ll do in Astro:

WebflowAstro site
CMS CollectionContent collection
Collection fieldsA schema
CMS itemA .md (or .mdx) with frontmatter (docs)
Collection templateA route that loops over the files (docs)

I have three main collections on my portfolio site: projects for case studies, writing for posts, and now for my /now page entries. Same mental model as Webflow, however we’re now using files instead of database rows.

Each collection has a schema that declares what fields are allowed. If a project is missing its year, or I typo a field name, the build fails before it ever goes live. I suppose this would be similar to Webflow’s “Required field” types and so you are unable to publish something that’s broken. It felt strict at first but this catches dumb mistakes before pushing to prod.

The boring parts

A few unglamorous things came up on my migration process:

Redirect and consolidate old URLs Early on in my new website setup, I mapped every old URL to its new home and then set up URL redirects within Astro. I had a bunch of outdated/extra pages, renamed case studies, a few old pages that were returning 404’s. Stuff like /about-me being renamed to /about and so on. At the very least, I would ensure you plan to create a new page for everything on your previous sitemap.xml and ensure SEO is not disrupted in the migration process. No dead ends and no dead links!

Keep the old data around I exported my whole Webflow CMS as CSV and kept it in the repo alongside the new site as I was building. As I rebuilt my CMS into Astro Collections, this was handy to have this data as a reference. I can point my Cursor agent at a CSV to pull out content from a CSV row, then drop it into a mdx page and move on.

Preview deploys are a design-review tool Every branch I push gets its own live URL on Vercel. I share a link, get feedback, and merge when it’s right. No more staging-site anxiety, no more publishing too early by accident. Git history is my revision history. Branches are my staging.

Marketing/SEO improvements I include JSON LD on top-level page templates to make my site readable by machines, and perhaps have better presence in LLMs. For pages with FAQs, these question/answer pairs get included as JSON LD as well. Don’t forget to update your favicon and opengraph images. There are guides out there to generate dynamic opengraph images for blog content/case studies, etc. or you can just have a single opengraph image for your whole site.

What got harder

I’m not going to pretend this is easy for everyone. Some people are not technical and have no interest in writing in markdown or looking at CSS. I get that. Maintaining a custom code-based website requires mental energy and labor on your behalf; sure, some of it may be outsourced to AI agents and LLMs but it’s still important for you (the maintainer) to understand conceptually what’s going on and how to fix/change something when you want to.

I gave up the visual editor. Changing my homepage hero now means opening a file, not dragging stuff around on a canvas. For me that’s fine, because my system changes more often than my layout does. If you redesign your visuals constantly and never want to see a code file, you might miss the canvas. Occasionally, I’ll go back and sketch something in Figma and then figure out how I can replicate it in code.

I maintain the design system/tokens/components myself. That’s kind of the point, but it’s still work.

I made a mess I haven’t fully cleaned up. Some project metadata lives in frontmatter, and some lives in a separate data file for the cards and ordering. They can drift out of sync. It’s not the tidiest solution but I understand my own system and that’s what matters. This is where documentation helps; you can have an AI agent write documentation based on complex file structures or code you don’t fully understand as a way of moving towards educating yourself and learning how the code actually works.

All of my edits and updates are now done in a terminal, using git in Cursor. Again, it’s just a different way of thinking but to some, this might feel “harder”. It removes the tedium of simple updates and I don’t dread updating my portfolio anymore. At the same time, I feel empowered to learn more about design engineering and web development which is a direction I can see my career going in the future.

Who this is for

If you run client sites, company landing pages, or anything where lots of non-technical marketing people need to log in and edit copy/publish blog posts, you’ll probably want to stay on a platform like Webflow or Framer. No-code platforms like this are great at enabling lots of different people to collaborate on the same site. Of course, services like Sanity CMS can be easily implemented with an Astro website and offers a similar functionality: a visual CMS whereby non-technical people can log in and make edits. Your mileage may vary.

This path is for designers who are comfortable in Cursor or VS Code, or who are willing to learn. And the willing-to-learn part is smaller than it sounds. You need design sense, a little CSS, Git, and the ability to articulate exactly what you want. The AI agent writes the rest of the code to fill in the gaps.

This is why I’m putting together a one-day, in-person workshop that I’m calling DIY Portfolio. Building a real, coded portfolio from scratch, tokens first, page by page, until you have a site you own end to end, not from a template. The first cohorts are small (5 seats) and I’m collecting honest feedback from attendees, so if that sounds like you, get in touch.

I didn’t leave Webflow because I stopped designing visually. I left so my visual system, my components, and my content could live in one place that I fully control. I’m finally having fun with my own website again, and I have a bit of a digital garden to make things for myself again. If this speaks to you, I’d encourage you to build your own site.

If you’re thinking about doing the same, or you already have, I’d love to see it.


Additional reading: