We vibe-coded a Nuxt 4 PWA. A concerned webdev agency's initial reactions.

As a web design agency in the business of coding web apps, we tried prompting out a flashcard app to probe the state of ai coding.

Screenshot of the app homeScreenshot of the numbers game
Final screens

The Nuxt 4 PWA we are building

While we do use Copilot in VS Code for coding assistance, we’ve never just recklessly barfed commands into an ai text field.

We were curious to see how it got on with Nuxt 4, Tailwind 4, and Vue, so we prompted our way into building the

Learn The Bloody Difficult Greek Alphabet Flashcard App

Cursor

Cursor seems to be the ai-app-du-jour so we signed up for a free trial, installed it, semi-successfully auto-imported from VS Code – it missed some themes, extensions and keyboard shortcuts but whatever.

Creating the Nuxt 4 app

A difficult start, as Nuxt 4 is only a few days old and is currently an opt-in setting in your config. The ai optimistically added the non-existant "nuxt": "^4.0.0" dependency. It did use the new /app directory structure however!

Not a big deal, we scaffolded it ourselves. Weirdly it would add pages and plugins into /src rather than Nuxt 4’s new /app directory despite making the app folder in the first place 🤷.

It also insisted on using Tailwind 3 rather than the new Tailwind 4, forgivable, and once both were manually installed it was plain sailing.

Greek flag banner

App Design

We knew what we wanted to build – a flashcard app which tests you on Greek letters but we’d designed nothing in advance.

We simply started with a Greek flag icon from the amazing Icones, added it as our favicon greek circular flag, and used that as our er, design system.

So far so manual.

Prompt-athon

We opened the chat window (we didn’t note the model, perhaps Claude 3.5? whatever the default was) and asked ai to

  • Grab all the modern Greek letters from wikipedia.org/wiki/Greek_alphabet
  • Create a json file based on these letters, and find an appropriate Greek word for each of the letters (it did a great job at this!)
{
  letter: 'β',
  name: 'Beta',
  equivalent: 'v',
  example: {
    greek: 'βιβλίο',
    english: 'book'
  }
},
{
  letter: 'Γ',
  name: 'Gamma',
  equivalent: 'g',
  example: {
    greek: 'Γεια σας',
    english: 'Hello'
  }
}
  • Install swiper.js and make a slide component with buttons using the english equivalent letter, randomly show a Greek letter as a title, when the user clicks the button with the correct Greek letter, advance to the next slide
  • Make a custom tailwind theme based on the blue colours in the favicon.svg
  • Make the background dark blue, the swiper a blue gradient, the foreground white, the buttons blue and round all the corners

This got us pretty far! A simple look and feel and basic functionality was in.

Robot CSS and HTML

The HTML generated was slightly semantic, using <main>, <nav>, and appropriate <H> titles, better than the tag soup we were dreading but nowhere near as good human HTML would think to use, no <sections>, <articles>, <headers>,<footers> etc.

The icon buttons were inaccessible, but a prompt fixed the aria-labelling.

Being designers, we couldn’t help but manually tweak the colours and paddings and typography, but we could ask the ai to lay out the buttons in a grid, change them to horizontal, adjust spacings ad infinitum.

Many basic jobs we asked it to do we should have done ourselves – we were surprised later how soon we burned through our free tokens – we wasted loads on easy tweaks but we were intrigued to see some ai design chops.

It struggled with – and we struggled with how to ask it – fixing CSS overflow/scroll issues, and implementing modern features like interpolate-size and allow-discrete and custom scrollbar colours, but it did a pretty decent job.

Screenshot of the app homeScreenshot of the numbers gameScreenshot of the app settings
Final screens

JavaScript-o-tron

Although we couldn’t keep our hands off the CSS, we decided to not touch the scripts at all, which led to a load of verbose but functional code.

It wasn’t aware of Nuxt’s auto importing so it was littered with redundant Vue and component imports. No biggy, but a tell.

Where it shone was rapid prototyping – we added and removed and added again SwiperJS as we experimented with interaction patterns – which would have been way more time consuming than simply commanding ai to put swiper back.

southpark WoW cartoon nerd reclining at his messy desk

And then, we thought why not add a Greek number game…

And it added a whole new dimension to the app in seconds. It created an ugly backspace button and a hideous nav bar that we had to replace ourselves, but cool that it added them without being told to.

Play-testing and modifications

We added the app to our phones and tested it out, came up with a list of changes and improvements for the next day down the prompt mines…

  • Add a hint, ideally read out the example word
  • Add gamification, a high-score perhaps
  • Make it an offline PWA, we soon discovered it didn’t work on the metro…
  • Mix the fonts between serif and sans-serif for variation

Adding audio was a fun task, we got the ai to call an elevenlabs api to record the example words into mp3’s (script), but according to some real Greeks, the pronunciation and accent were terrible.

We got it to add localstorage scorekeeping, show your current streak, and show mistakes in a new settings page, which worked well! Ugly, and more overflow issues, but a sensible layout.

Also fun was asking to add canvas-confetti, fire it on scores of *5, and use the colours from our svg logo which it did exactly as commanded on the first attempt.

It not-unreasonably installed the unmaintained pwa.nuxtjs.org to make it work offline, instead of the modern vite-pwa-nuxt. Once we corrected that, both the ai and us struggled to get it working offline, but some back and forth brainstorming and error-pasting got the service workers working.

Interestingly it choked on a simple task – randomly apply sans-serif or serif to the main letter. It took an amount of begging persuasion to come up with a solution, and left the component littered with unused code, but got there in the end.

Redoing the audio

We had a brainwave (should we have asked ai for some ideas and saved our precious waves?) – MacOS comes with great text-to-speech built in with the say command.

Using the mellifluous Greek Melina voice, and with a little iteration, we had ai come up with a ffmpeg shell script (GitHub) that looped through all the Greek words, added some padding, spoke them into an aiff file and converted to mp3, which worked brilliantly.

We have been reliably informed “this voice is perfect”.

If you want to hear your mac read out some lovely Greek words, pop this in your terminal:

say -v Melina "Βιβλίο, Γεια σας, Δρόμος, Ηλιος, Θάλασσα, Λόγος, Ξένος, Ρόδο"

In review; The Good

📱 An eye-opening exercise! In around 12 hours we have an app on our phone that is actually teaching us the Greek alphabet and numbers, is fun, and looks good doing it.

🌀 It was pretty good at Vue 3! Some questionable choices but solid. It had less of a grasp of Nuxt, but soldiered through it with few errors.

The Bad

💸 We burned through all our free credits in that time, which was informative!

🐛 There is a random on-device crash with a generic error that we can’t work out a) what the cause is and b) how to fix it without reviewing all the code line by line. Bread and butter debugging for us developers, but if you were a layman prompt-coder you might find yourself struggling at this stage.

🧑‍🎨 Its design and layout sensibilities were often good! But leaning average or poor, although we were using it for code rather than for example, one of the fancy new figma to app design agents so we’ll forgive it on that front.

The Ugly

🍝 Would we build a client project like this? Ab-so-lute-ly-hell-no. OMG, no. It used old and insecure packages, redundant code, brittle layouts, piles of spaghetti and non-A11Y HTML.

The Good again

🤖 But! Many aspects are great – grabbing data from wikipedia, generating translations, writing boilerplate functions, generating audio generators, extracting colours from images, writing font-import CSS (so tedious) and so on, many different bits of JavaScript up to our standard and beyond.

Conclusion

We think we’ll stick to us writing the code and using the ai for the mundane stuff and debugging assist, but we’d not hesitate to vibe-out again with Cursor on an internal tool, app or script.

Still, when you take a step back and think about it, not only will it only ever get better, it’s already mind-blowingly capable, and will soon be producing production quality code.

For now, though, it needs experienced developers to steer, advise and review. For now…

Our species is doomed. Good luck all!