Plot twist: The backend engineer who hates CSS is building a fullstack app with the simplest possible stack. ExpressJS + HTML + TailwindCSS + HTMX + AlpineJS. No React, no complex build tools, no 500MB node_modules. Just pure, beautiful simplicity. Here's why I'm doing this to myself and loving every minute of it.
What's This Crazy Experiment About?
- The "Why Am I Doing This?" Story
- My Minimalist Stack Choices
- The Reality Check
- Why I'm Excited (And Slightly Terrified)
- The CSS Confession
- What I'm Building
- The Challenges I'm Expecting
- Early Discoveries
- Why This Feels Like Coming Alive
- Updates from the Trenches
The "Why Am I Doing This?" Story
Okay, so picture this: I'm a backend engineer, right? I live and breathe APIs, databases, server architecture. My comfort zone is Node.js, PostgreSQL, Docker containers – the whole backend paradise.
But lately, I've been looking at modern web development and thinking... are we making this way too complicated?
I mean, we've got React apps that take 30 seconds to start up in development. Build processes that need a PhD to understand. Package.json files with 200+ dependencies. Frontend frameworks that change every six months.
And then I stumbled upon this whole "HTML over the wire" movement. HTMX. Alpine.js. People building real applications with just HTML, CSS, and a sprinkle of JavaScript.
My first thought: "That can't possibly work for real apps."
My second thought: "...but what if it does?"
So I decided to find out. I'm building my next personal project with the most minimal stack I can possibly get away with.
My Minimalist Stack Choices
Here's what I'm working with:
Backend: ExpressJS
Why: It's simple, it's fast, I know it well. No fancy frameworks, no magic – just good old Express doing what it does best.
// This is basically my entire backend philosophy right now
const express = require('express');
const app = express();
app.use(express.static('public'));
app.use(express.json());
// Real routes, real logic, no abstraction layers
app.get('/users', (req, res) => {
// Just do the thing
});
Frontend: HTML + TailwindCSS + HTMX + AlpineJS
The stack that sounds crazy but might be genius:
- HTML - The foundation that never goes out of style
- TailwindCSS - My CSS salvation (more on my CSS hatred later)
- HTMX - Making HTML dynamic without losing my sanity
- AlpineJS - Just enough JavaScript to make things interactive
Total JavaScript frameworks: Zero
Build step complexity: Minimal
Bundle size: Tiny
My sanity level: Surprisingly intact
The Reality Check
Let me be honest with you – choosing this stack means I'm signing up for some challenges:
I'll need to build almost everything myself. No component library that gives me a beautiful dashboard in 5 minutes. No pre-built authentication flows. No magic state management.
I'll probably reinvent some wheels. That fancy date picker everyone uses? I might need to build my own. That smooth infinite scroll? Yeah, that's on me too.
I need to figure out best practices from scratch. The React community has established patterns. The minimalist community? We're still figuring it out as we go.
But here's the thing – I'm actually excited about this.
Why I'm Excited (And Slightly Terrified)
The Challenge Factor
You know that feeling when you're playing a video game and you switch to hard mode? That little adrenaline rush of "this is going to be interesting"?
That's exactly how I feel about this project.
I've been comfortable in my backend bubble for years. Building APIs, optimizing databases, setting up infrastructure. I'm good at it. Maybe too good. Maybe too comfortable.
This stack is my hard mode. And honestly? I've missed feeling challenged like this.
The Learning Explosion
Every day I'm learning something new:
- How HTMX actually works under the hood
- Why Alpine.js is surprisingly powerful for such a tiny library
- How to structure HTML when it's carrying most of your application logic
- CSS patterns that don't make me want to quit programming
The best part: I'm understanding the web platform at a deeper level. No abstractions hiding what's really happening.
The Performance Promise
Here's what I'm betting on: this stack will be fast. Really fast.
No massive JavaScript bundles to download. No virtual DOM to reconcile. No hydration delays. Just the browser doing what browsers are optimized to do – render HTML and handle user interactions.
My prediction: Sub-second page loads even on slow connections. We'll see if I'm right!
The CSS Confession
Alright, time for a confession that might get me kicked out of the backend engineers club:
I used to absolutely hate CSS. Like, viscerally hate it.
You know the feeling – you want to center a div, and somehow you end up questioning your life choices and considering a career in farming.
/* This used to be my nightmare */
.center-me {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* WHY IS THIS SO HARD?! */
}
But then TailwindCSS happened in my life:
<!-- This... this I can understand -->
<div class="flex items-center justify-center h-screen">
<div>I'm centered!</div>
</div>
TailwindCSS didn't make me love CSS. Let's be clear about that. I still think CSS is weird and sometimes feels like black magic.
But Tailwind made CSS... tolerable. Maybe even occasionally enjoyable.
It's like having a translator between my backend brain and the mystical world of visual design. I can think in terms of "flex items-center" instead of trying to remember which combination of CSS properties will actually center something.
Do I still hate CSS? Yeah, kind of. But now it's more like a friendly rivalry than mortal enemies.
What I'm Building
I can't spill all the details yet (the projects are still very much in progress), but here's what I'm working on:
Project #1: A Simple Task Manager
The goal: Build something actually useful with this stack
The challenge: Make it feel as smooth as a modern SPA
The learning: How far can HTMX + Alpine take me?
Project #2: A Personal Dashboard
The goal: Replace some of the SaaS tools I use daily
The challenge: Complex interactions without a frontend framework
The learning: Can I build something I'd actually want to use every day?
Current status: Both are in that exciting/terrifying "it's starting to work" phase.
I'm planning to share detailed write-ups once I have something worth showing. Complete with performance metrics, development experience notes, and honest assessments of where this approach shines and where it struggles.
The Challenges I'm Expecting
Let me be realistic about what I'm walking into:
1. State Management Without a Framework
<!-- How do I manage complex application state with just Alpine? -->
<div x-data="{ user: null, loading: false, error: null }">
<!-- This works for simple stuff, but what about complex workflows? -->
</div>
The question: Can Alpine.js handle the kind of state complexity I'm used to managing with proper backend patterns?
2. Component Reusability
In React, I'd build a component once and use it everywhere:
<UserCard user={user} onEdit={handleEdit} />
With HTML templates, I need to figure out patterns for reusability without getting too clever for my own good.
3. Testing Strategy
How do you test an HTMX application? The usual frontend testing tools are built for JavaScript frameworks. I'm going to need to figure out new patterns.
4. Development Workflow
No hot reload. No component dev tools. I'm back to refresh-driven development. It's going to be... interesting.
Early Discoveries
Even though the projects aren't finished, I've already learned some fascinating things:
HTMX is Genuinely Magical
<!-- This blew my mind -->
<button hx-post="/api/like"
hx-target="#like-count"
hx-swap="innerHTML">
Like
</button>
<span id="like-count">42</span>
That's it. No state management. No useEffect hooks. No complex data flow. The server sends back new HTML, HTMX updates the DOM. It just works.
Alpine.js Does More Than I Expected
<div x-data="{
items: [],
async fetchItems() {
this.items = await fetch('/api/items').then(r => r.json())
}
}"
x-init="fetchItems()">
<template x-for="item in items">
<div x-text="item.name"></div>
</template>
</div>
This is handling API calls, reactivity, and templating. In HTML. With no build step. My backend brain is confused but impressed.
TailwindCSS Makes Me Feel Like a Designer
I'm not saying I'm good at design. I'm saying Tailwind makes my terrible design sense look... less terrible.
<!-- I made this look decent! Me! The CSS hater! -->
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
<h2 class="text-xl font-semibold text-gray-800 mb-2">Card Title</h2>
<p class="text-gray-600">Some content that doesn't look horrible</p>
</div>
Why This Feels Like Coming Alive
Here's the thing that surprised me most about this experiment:
I feel more creative than I have in years.
When you're working with React/Vue/Angular, there's often a "right way" to do things. Established patterns. Community conventions. You follow the path.
With this minimalist stack, I'm making it up as I go. Every problem requires creative thinking. Every feature is a puzzle to solve.
Yesterday I spent 3 hours figuring out how to build a smooth dropdown menu with just HTMX and Alpine. In React, I'd have installed a library and been done in 10 minutes.
But those 3 hours? They felt alive. I was problem-solving. I was learning. I was building something from first principles.
The Joy of Constraints
Having fewer tools means I have to be more creative with the tools I have. It's like being a jazz musician with just a piano instead of a full orchestra.
Constraints breed creativity. And this stack has just the right amount of constraints to keep things interesting.
Understanding Everything
In a typical React app, there's a lot of magic happening. Webpack is doing things. Babel is transforming code. The framework is managing state and lifecycle and effects.
With this stack, I understand every piece. When something breaks, I know where to look. When something works, I know why.
There's something deeply satisfying about that level of understanding.
The "Tackling the Hardest Thing" Philosophy
You mentioned something in your message that really resonated with me:
"I believe, tackling something that you think is the most hard things to do in the world would bring so much satisfaction when I finally beat them, right?"
This is exactly it.
For me, the hardest thing wasn't learning another JavaScript framework. I could pick up React or Vue in a weekend. The hard thing was stepping away from the familiar tools and building something real with constraints I'd never worked with before.
The hard thing was admitting that maybe I've been overcomplicating things for years.
The hard thing was being willing to reinvent wheels instead of just importing them.
The hard thing was building frontend interfaces when I've spent my career hiding behind API endpoints.
And you know what? Every small victory feels amazing.
When I got my first HTMX form submission working smoothly, I literally did a little victory dance in my kitchen. When I built a responsive layout that didn't look terrible, I sent screenshots to my friends.
These tiny wins feel bigger than shipping complex features with familiar tools ever did.
Updates from the Trenches
I'm planning to document this journey as I go. Here's what you can expect:
Performance Metrics
I want to measure everything:
- Page load times
- Time to interactive
- Bundle sizes
- Memory usage
- Network requests
The hypothesis: This stack will crush traditional SPAs on performance metrics.
Development Experience Notes
The good, the bad, and the "why did I do this to myself" moments.
Code Patterns That Work
As I figure out best practices, I'll share them. We're all making this up together.
Honest Failure Stories
When this approach doesn't work, I'll talk about that too. No point in pretending everything is perfect.
Follow along if you want! I'll be sharing updates as the projects progress.
The Bottom Line
I don't know if this experiment will prove that "less is more" in web development. I don't know if I'll end up loving this stack or running back to familiar tools.
But I know I'm having more fun building things than I have in a long time.
I'm learning. I'm struggling. I'm solving problems creatively. I'm building things I understand completely.
That feels pretty alive to me.
Whether you're a backend engineer like me who's curious about frontend, or a frontend developer who's tired of the complexity treadmill, maybe there's something interesting in this minimalist approach.
We're all just figuring it out as we go. Might as well have some fun while we're at it.
About This Journey
I'm just a backend engineer who got curious about doing things differently. No grand claims about revolutionizing web development – just sharing the adventure of trying something new and seeing what happens.
Want to follow along? I'll be posting updates as these projects progress. And if you're trying similar experiments, I'd love to hear about your experiences!
Got questions or thoughts? Always happy to chat about minimalist development approaches, even if we're all still figuring out what works.
Written while procrastinating on CSS debugging – some things never change!
Top comments (0)