Just make cool sh** stuff.

Current Stack – Back to basics. Going “build–step free” for a handcrafted experience.

Feb 23 2024   @joshua

  1. Yearning for a simpler time – the artisanal web.

    Websites are too BIG, too complicated, and too hard. Reactive programing is conceptually easy to grasp – but the distance between the end result vs. frameworks & tooling is too far apart.

    I get it, the web is a complicated place. We can create pretty much anything online we put our minds to, and I think that is awesome. There is a lot of rubber that needs to hit the road before even a simple website is considered “ready” to go online. We’ve built amazing tools to help us do more, “faster, & better”, but I feel like somewhere along the line I lost touch with crafting the web. Now it’s 80% setting up or debugging my toolchain, 20% creating. So, I’m throwing it all in the garbage and going back to type -> save -> refresh.

  2. Here is what I am using.

    TLDR; – Alpine JS, ES6 JS, Pico CSS, Remix Icons, Processwire & PHP, MariaDB, MacOS & Linux, Nova, Indigo. APPRIM

    Alpine JS

    The reactive part. I have a lot of love for Vue & Svelte, even React/Preact (not my personal flavor of choice, it does the job). Buuuuut… I wanted a no–build–step solution to writing reactive frontend JS. Why Alpine?…

    I've tried a bunch of alternatives like Petite Vue, ArrowJS, HTMX, Litedom, or all the options on this list: Unsuckjs.com. There are some good alternatives, but I keep coming back to Alpine. Don’t let the simplicity fool you. I often hear people talking about how it’s a cute little replacement for jQuery (it’s in the tagline), but you can do (almost) 99% of anything larger frameworks offer, without all the hassle. Of course React & Vue have huge ecosystems, but one of the joys of using Alpine is realizing that I don’t really want/need it.

    Alpine JS is feature complete.

    Reactivity, reactive primitives, watchers, reusable “components”, 2–way data binding, computed properties, refs, store, CSP, extendable, and it’s relatively performant. Nested x-for loops can get you down, but it’s easy to get around using .map().

    There are no “props” (HTML attribute properties), but, you can easily just use attribute binding :my-property="variableName". Every Alpine JS “component” inherits from it's parent component, so no need to do “prop drilling”. Inter–component communication is easy with $data, and you can always use custom events (recommended) with $dispatch.

    There is no built–in router, but there are a few Alpine specific choices that are more than enough; ex. Alpinejs-router & Pinecone. You can always add your favorite 3rd party router as well, in less time than it takes you to worry it’s not included in the box.

    Components.

    Alpine doesn’t “technically” have components, in the sense of <my-component></my-component>. But you wont be missing out. Anything with an x-data attribute is a component in Alpine–land, and that takes you pretty far. If you are looking for (HTML/syntax) encapsulation, you have a lot of options.

    The quick and dirty way:

    
    	// reusable alpine "component"
    	Alpine.data('my-component', ()=>({
    		mc:{
    			['x-html'](){
    				return `<a @click="doSomething">Click me</a>`;
    			}
    		}
    	}));
    	
    	<div x-data="my-component" x-bind="mc"></div>	
    

    You could go a little further and create a <template></template> with your markup in it and query for the template to populate your components.

    Alpine JS + Web components. It’s pretty straight forward to wire the two together. There are a lot of options if you want to streamline using Web components too, such as: Ponies.

    Use as much or as little as you want.

    “Every bit of reactivity that takes place in Alpine, happens because of two very important reactive functions in Alpine’s core: Alpine.reactive(), and Alpine.effect()”… using these 2 methods, you can do whatever you want, and leave the rest behind. Alpine uses VueJS's reactivity engine under the hood to provide these functions.

    SSR & Hydration.

    SSR is the [New/Old] rage. Alpine JS roots are as a companion for adding reactivity to server rendered HTML. This is a great way to use Alpine + PHP without much hangups. Send your HTML over the wire with Alpine JS sprinkled in. Boom, server–side reactive “components”. Commonly used with Blade/Livewire. Even simpler: HTMX.

    Of course, that is not how most of us are writing our reactive frontends. Instead, we are receiving JSON/JSONP and creating our HTML on the frontend with our framework of choice. Whatever method you choose, Alpine JS can work.

    Single File Components.

    A simple (conceptual) method for single file components with Alpine JS. Save your files as my-component.html/js, then bootstrap them into your page with Fetch. Here is a simple example file. Mimicking Vue 3’s single file components.


    my-component.html
    
    	// my-component.html
    	<style>
    		// scoped CSS!
    		.my-component{
    			.my-style{color:green;}
    		}
    	</style>
    	
    	<script type="module">
    		Alpine.data('my-component', ()=>({
    			... your code here
    		});
    	</script>
    	
    	<template id="my-component">
    		<div class="my-component">
    			... your HTML here.
    		</div>
    	</template>
    

    my-component.js (alternative)
    
    	// my-component.js
    	Alpine.data(('my-component')=>({	
    		style: ()=>{
    			return `
    			<style>
    				// scoped CSS!
    				.my-component{
    					.my-style{color:green;}
    				}
    			</style>`;
    		}(),
    		template: ()=>{
    			return `
    			<div class="my-component">
    				... your HTML here.
    			</div>`;
    		}(),
    		
    		.. the rest of your component code here.	
    	});
    	
    

  3. Shout out to jQuery… & goodbye. 🖖🏼

    Without jQuery, I wouldn’t have been building the web back in the day ♥️. I always loved jQuery’s concise API and short–methods for a lot of things… I know it off by heart. I used jQuery for many years. But, the majority of my “jQuery needs” have been part of most browsers/env for a while now and I don’t find myself querying the DOM very often.

    The first time I was exposed to the delight of data binding, nothing was ever the same. The tendency to use the DOM as state in jQuery–soup projects is the short path to insanity. And I am more than glad to leave it behind.

  4. Javascript ES6

    I love some of the new language features that are part of ES6! I seriously love ${Template Literals}, Arrow functions, All the object and array methods. The spread syntax..., default parameter values, object property shorthand, destructuring assignment, classes, Map & Set  and more. Javascript is a joy to work with these days.

    That being said, I still prefer to use _lodash for some things. Mainly for a lot of the methods that require the use of Array.reduce(). There are many examples where _lodash methods are more readable, concise, and faster. Take a look at You don’t need _lodash/_underscore. Honestly, it’s days are numbered, but instead of recreating _lodash with vanilla JS, I just use it. You can cherry–pick your favorite methods, and leave the rest to vanilla JS (ex. Array.filter()). It’s smaller than a single image, and worth it IMO.

    No Typescript? 🤯

    Nope. It requires a build step. I like typed languages. I use types in PHP & Swift, but I don't have a huge need for Typescript in my projects. Maybe if I was collaberating with larger teams. Most of the time TS and I bonk heads.

  5. Remix Icons

    There are a lot of great options for icons these days – too many to choose from. I love using Material Symbols, available in three styles and four adjustable variable font styles (fill, weight, grade, and optical size). They fit pretty much any need. The downside is that hot–loading them into your website/app is costly at over 2 MB, it’s just too large.

    Instead, I found that a single style from Remix Icons is enough for most needs. They are exceptionally well made, reasonable file size, & look fantastic at any size.

  6. Pico CSS

    “Minimal CSS Framework for Semantic HTML” – straight off their website. That says it all. Again, no SASS (optional) or build–steps here. Just a CSS @import and we are good to go. Automatic light/dark Mode and some good basics. I can get away with this because it’s my website and I want to keep it lean.

    My goto in the past used to be UIKIT. It’s a great general use “frontend (CSS) framework”, if not a little bit old–school. It gives me the same vibes as the recent version(s) of Bootstrap and just felt like too much “stuff”.

    Why not Tailwind?

    The easy answer is, Tailwind requires a build step & I am trying to avoid that. There are some real advantages to Tailwind: Fast to itterate, no CSS selectors, easy to share, great for CSS in JS. A Utility–first approach to CSS is awesome, and it’s a lot of fun to use, but I am going to stick to vanilla CSS and see how far I can get with custom properties and creative naming. I actually like writing CSS.

  7. Processwire & PHP 8.2

    At first glance, it looks like a small PHP CMS, like so many before it. That couldn’t be farther from the truth. It’s a powerful & robust CMF with an incredibly powerful & elegant API. (Check out the awesome documentation). So much so, that it rivals some of the larger frameworks, and clearly passes most CMSs in functionality and usability.

    Processwire includes everything out of the box – auth, users, permissions, roles, scaffolding, file management, query Api (ORM–like), fluid interfaces, and more. It has everything you need for a solid backend. Highly performant, even with millions of records & a mirad of requests. It’s a joy to work with, stays out of your way, and has one of the best UX/DX of any software I have ever used.

    Everyone’s favorite PHP framework is Laravel, & with good reason, it’s a fantastic framework. It’s what I used before finding Processwire, but there is no looking back. After Processwire, anything else feels like doing work for works sake.

  8. MariaDB InnoDB

    There are some interesting option in the DB space. I am particularly interested in offline–first + CRDTs. However, I am using MariaDB because that is the default for Processwire, has transactions, it’s relational, and does more than I need without having to reinvent anything. Processwire has a fluid–like schema, great indexing, fantastic cache, and that covers most of the attraction to alternative javascript based DBs. However, I would still like to find a solution to realtime replication, offline support, and subscription events.

    Honorable mention: PocketBase, PouchDB/CouchDB, & RxDB.

  9. MacOS & Apps

    Ubuntu (Linux) on the server, MacOS/iOS for work/life.

    MacOS isn’t perfect, but I can’t think of any other OS I would rather use as my daily driver. I like the idea of open source OSes and I’ve tried various Linux/BSD alternatives, but in my experience none of them make the cut. I love having a *nix machine that I can do anything with, while getting the polish of MacOS integration, security, ecosystem, and world–class applications. Apple has a culture of creating software (& platform) that continually delights, eliminates needless complexity, and values the user. It’s feels like I am doing what I am focused on instead of USING SOFTWARE.

    Apps that never close: My text editor (Panic Nova), Safari & Dev Tools, Notes.app, Terminal.app, Sketch (yeah, yeah, Figma…), Pixelmator Pro, FaceTime/Slack, and of course Music.app. (Bonus: check out Secret Agent on SomaFM).

  10. Panic Nova (formerly Coda)

    I am a stickler for well made, artisan software designed to be the best with a single purpose. My choices are not popular, but they are so satisfying. By far the best native Mac code editor. Hands down. It has Transmit build in, robust GIT support, extensions, workflows, syncs to the cloud, and is a pleasure to use. My favorite editor of all time.

  11. Indigo

    “The native macOS app which will revolutionize the way you configure and run local web servers on your Mac”. Indigo just makes my life easier. I don’t have to have 50 Docker containers running and I can make as many environments as I want. Turn them instantly on/off, and they don’t use up a million resources. If I need a virtual machine, I can always spin one up and connect to one of my environments 🍰.

Wrapping up

Phew, that was a lot of stuff.

Attempting to make a simpler stack still requires a lot of resources and know–how. Did I accomplish my goal of making it easier to build stuff on the web? I think so, but at what cost? My guess is that I can get pretty far working on this by myself, but could potentially be a huge point of contention if I were to work on it with other people. Time will tell. 🖖🏼