How I created this blog
This (first!) blog post details how I leveraged LLMs to build this blog. I’ve always wanted to make a blog. Partially within the guise of ‘being marketable’, but also because it’s just cool. As such, I’ve made it an effort to innovate this blog application itself as much as its documents.
The Goal
Coding a blog can be done in an afternoon with a simple framework like Astro, or hell even just HTMX. But I want to make this robust and extensible. I want an interactive journal as much as a ‘blog’.
Svelte’s a good choice for the framework. It’s the only Typescript framework that doesn’t make me crazy just by looking at the docs.
I’ve dodged Frontend throughout my developer career, but now it’s time.
I’ll start first at the design.
Stealing like an artist LLM: Deducing a design
I’ve never been good at using mere imagination to devise the creation of any artistic product. I need to first see and judge, then iterate. So then how do I start? Usually just with brute force.
Luckily it’s 2025 and I don’t have to do this manually. Advancements in reasoning models - particularly the Agentic Sonnet 3.7 Thinking released this month - enable one-shot creations of full websites.
We’ll use these hollow sites as a visual guide and for general frontend scaffolding.
Prototyping a blog-site
As previously stated, Agentic LLMs are crazy good. There’s been some booming new Saas’ that have sprung up as a result. In the category of UI website design; Loveable, bolt, v0, Polymet, all give you a (non-functioning) website with just one prompt.
Thankfully, I’m an unemployed developer with some free time. I should be able to deconstruct & extract the pretty pieces for use in my own implementation.
If we spin up enough websites, we can visualize and deduce the design and application of the blog.
Brute forcing a design
We’ll prompt those ‘AI website creation’ tools by describing the vibe/aesthetic of the blog (basically just lofty-language stuffing it). Further, we can even toss our prompt/description through an LLM to flesh it out a bit more.
We can even get multiple samples of a single prompt/idea:
- say 3 unique generations for a dark-themed blog-site, and 2 for a brutalist-themed one (i just saw the movie, sry).
Each prompt will yield a different result from the previous, especially of course if from a different service.
- Here’s some 7 generations (I’m sorry environment. I’ll be taking shorter hot showers this week.)
Some were prompted with specific color scheme specifications I picked out from the former gens I liked
I’m sure as you can imagine, it’s clear what direction we’re headed.
Setting up the generated blogs
If I’m going to utilize these generations for building up my site, I don’t want merely the image. I want the code - I want the structure.
We’ll download the entire source code of these generated sites so we can run them in a local developer environment.
Downloading
Despite not wanting to list the tools cause it’s not of topic, I feel compelled to mention I primarily used loveable.dev. Figures if I should batch-download off their free tier, I should rightfully hail them the best.
Hoping I don’t have to pull out Selenium - just opening the network inspector tab reveals a detailed GET REQUEST
for a /source_code
endpoint that yields some JSON with all the file contents, structure and all.
Pipe the copied curl request to a file, throw it into an LLM to create a bash script, and we have a script that compiles it (if you want to use that word) into a local React project.
One more LLM generated bash script later and we can npm run dev
each site in the cwd
. Now we have a nice working development environment for each of these sites.
I should mention now that I don’t really know Frontend web development. I’ve played around with APIs, Databases, Scraping, WebApp pentesting, but Frontend… Frontend is hard.
With any luck, some LLMs, and some good brutalist application architecture, we should be able to combine the best parts of all that we fancied into a single Blog-site.
But before synthesizing, we have to make sure they ‘speak the same design language’
Problems & Refactoring: Tailwind and Svelte
Before even thinking about letting the LLM generated React code (shivers) into my Svelte blog, it needs to first be re-engineered to work with not only Svelte & TypeScript, but bloody Taildwind 4 - My precious blog isn’t going to be built on deprecated software.
Tailwind v4 updated some things that are not compatible with v3 out of the box. Thankfully their upgrade tool can do most of the work.
The identical Tailwind configurations will essentially grant us homogenity for the theming. The code in my Svelte project should produce identical elements to the React project’s black-magic - A button with the same HTML code should agnostically look exactly the same on both sites.
Curiously enough, I wonder if loveable.dev enforces React and its UI component libraries to improve quality? v0, bolt, all adhered to my “Use Svelte” specification. Not loveable. Perhaps that’s partly why loveable’s came out the best? Because it relied on a wider trained sample of code/designs available? (React is much older than Svelte.)
Moving on.
Implementation
We need to now turn the pretty generated website into something coherent and functional.
I’ll have to implement each page. Component by Component.
Luckily with both React and Svelte already sharing HTML
and Tailwind
, we’re already 2/3 on overlap. We only have to implement Svelte[Kit]’s syntax and philosophy differences
(Page routing, layouts, state control, etc.)
We may also have to deal with components that don’t use the same color schemes. We can either assert ours, or integrate its styling into our own Tailwind config.
And by I’ll have to implement & integrate; I mean I’ll prompt the translation between the two.
(illustrations for this grouped to next section)
Prospective Planning
(because it sounds cooler than premature optimization)
Better componentization. Unified branding and theme. SSG for the posts. Basic CMS workflow. SSR. Widgets. Skeletons. Brutalism.
“No matter what the others try and sell you, it is the destination, not the journey” - The Brutalist
Further implementation
Like I said, we’re doing this primarily with LLMs (I need training wheels for frontend). That means, some strategic laziness is afforded. I think it’s due time now that I show some actual LLM use.
I’m wary of showing Cursor-specific screencaps as I don’t want to conflate LLM pair programming (I’m not gonna use the v word.) with any one tool. Aider ♥ was used extensively with Gemini 2.5 Pro for larger architectual questions. Claude Code would’ve been used if I was Mr. Moneybags. Etc. etc.
This site was basically built off a template blueprint. Let’s architect away the inaccuracies.
Message Logs to Cursor (JSON):
{@html `{
"commandType": 4,
"text": "Convert blog/+page.svelte to use our project's Svelte 5 and Sveltekit. The current code is taken from a React project. Use @+page.svelte as a reference for the implementation. "
},
{
"commandType": 4,
"text": "How should I make this actually fetch articles? I'm aware of the implementation, but beyond that - what stack should I use? I'll most likely have a content folder in the svelte project directory that contains .md files. Should I store the metadata (date, title, tags, etc) inline in the markdown files themselves? Use a database? What are best practices here?"
},
{
"commandType": 4,
"text": "Convert about/+page.svelte to use our project's Svelte 5 and Sveltekit. The current code is taken from a React project. Use @+page.svelte as a reference for the implementation. "
},
{
"commandType": 1,
"text": "`User` is lucia Icon. "
},
{
"commandType": 4,
"text": "Convert about/+page.svelte to use our project's Svelte 5 and Sveltekit. The current code is taken from a React project. Use @+page.svelte as a reference for the implementation. Use @lucide/svelte."
},
{
"commandType": 4,
"text": "Convert contact/+page.svelte to use our project's Svelte 5 and Sveltekit. The current code is taken from a React project. Use @+page.svelte as a reference for the implementation. Use @lucide/svelte."
},
...
{
"commandType": 4,
"text": "How should I actually implement blog posts? Right now I just have placeholders. It should be maximally runtime efficient. "
},
{
"commandType": 4,
"text": "Implement loading blog posts from content/posts rather than using placeholder blog posts. "
},
{
"commandType": 4,
"text": "Implement loading blog posts from content/posts rather than using placeholder blog posts. We'll use SSR so we may need to create +page.server.ts files. Ideally, we'd only do this once (I make this distinction because we'll have featured blog posts on the index of the site). This implementation should be as optimized as possible. We need not use markdown-related libraries other than the currently installed mdsvex for now. "
},
{
"commandType": 4,
"text": "Let's use the official gray-matter npm package for parsing the YAML"
},
{
"commandType": 4,
"text": "Use gray-matter to read the YAML"
},
{
"commandType": 4,
"text": "If there aren't enough blog posts to satisfy the 6 featured posts amount, create skeleton BlogPost containers. "
},
{
"commandType": 4,
"text": "If there aren't 6 posts to show in the featured posts section, make the rest skeleton BlogPost containers so the styling is consistent. "
},
...
{
"commandType": 4,
"text": "This Stats component will instead be a Stats component specific to the website. So change to developer-specific stats for building a web application like this one. ie. git commits, changes, days since last git commit to this website. \n\n(We need not implement the numbers yet - no implementation, only with placeholders) "
},
{
"commandType": 4,
"text": "Nice. Now add a label, header, or some identifier that this is indeed specific to this website. That the data shown here in stats is for the website's application code. "
},
{
"commandType": 4,
"text": "Optimize the sizing "
},
{
"commandType": 4,
"text": "Nice. Now add a label, header, or some identifier that this is indeed specific to this website. That the data shown here in stats is for the website's application code. Keep it consistent with the site's styling."
},
{
"commandType": 4,
"text": "I've added new UI elements that are inline with the theming of our application. Extract design and structural elements at your discresion for adaptation in our website stats. The website stats idea should be clearly represented within the component. "
},
...
{
"commandType": 4,
"text": "note \"****\" accross the code base. We want to instead use text from this brand.js file as the branding and emails used globally in this codebase. What's the best way to do it? Import brand from this file everywhere it's used? Is there a more efficient method?"
},
{
"commandType": 4,
"text": "Can I import in layout and will it inherit?"
},
{
"commandType": 4,
"text": "Might there be extra overhead? I thought this may have been used to keep track of transient variables, not consts."
},
{
"commandType": 1,
"text": "brand.name"
},
{
"commandType": 1,
"text": "brand.github"
},
...
{
"commandType": 4,
"text": "Remake this component into a 'suggest a topic' component for my blog. Users can \"submit a topic they're curious about\"."
},
{
"commandType": 4,
"text": "Make the \"Submit a topic you're curious about for future posts.\" horizontally next to \"Suggest a topic\". This will make the Your topic suggestion textbook span the entire width. This will allow for more complex suggestions"
},
{
"commandType": 4,
"text": "Hmm, the spacing is a bit akward. Put the subtext under the main header once again. We'll keep the length of the textbook though. "
},
{
"commandType": 4,
"text": "Let's enhance the layout. \n\nWe're showing 3 of these components. Let's make the first component shown (this one - `Suggest.svelte`), horizontally longer than the call to action while still being smaller than the repository stats component. This will enhance feng shui."
},
`}
You get the point.
WIP
This concludes part 1. for now. Still need to deploy and setup CI/CD!