Our current stack (2026)
The tools, services and workflows we actually use to build and ship. No affiliate links. No sponsored mentions. Just what we reach for and why.
1. The philosophy behind our choices
Every tool we use was chosen for one reason: it helps us get from idea to deployed website as fast as possible with as little friction as possible. We do not use tools because they are popular, because they are new, or because a blog post recommended them. We use them because they solve a specific problem in our workflow and do not create new ones.
Our stack has changed over the years. We have tried dozens of editors, frameworks, hosting platforms, domain registrars, and design tools. What we have now is the result of years of experimentation and a willingness to drop things that do not earn their place.
The core principle is: every tool in the stack must justify its presence. If we can do the same thing without a tool, we drop the tool. If a simpler tool does the same job, we switch. If a tool requires more configuration than the problem it solves, we find an alternative.
This is not minimalism for its own sake. It is pragmatism. Every tool has a cost: learning time, configuration time, maintenance time, context-switching time. A tool is only worth that cost if it saves more time than it consumes. Most tools fail this test.
The full stack at a glance
- Editor: VS Code with minimal extensions
- Languages: HTML, CSS, JavaScript (TypeScript for larger projects)
- Frameworks: None for most projects. Astro or Next.js for content-heavy sites.
- Icons: Lucide (CDN)
- Fonts: Inter (self-hosted WOFF)
- Version control: Git + GitHub
- Hosting: Cloudflare Pages
- DNS: Cloudflare
- Domains: Cloudflare Registrar, Porkbun
- Design: Browser DevTools (occasionally Figma for complex layouts)
- Notes: Obsidian
- Images: Squoosh for compression, WebP format
2. The editor: VS Code
We use Visual Studio Code. Not because it is the best editor in some abstract sense, but because it does everything we need without getting in the way. It opens fast, handles multiple projects, has a built-in terminal, and the extension ecosystem means we can add exactly the capabilities we need without the bloat of an IDE.
Extensions we actually use
We keep extensions to a minimum. Each one adds startup time and cognitive overhead. Here are the ones that survived the cut:
- Prettier. Auto-formats HTML, CSS, and JavaScript on save. This eliminates all formatting debates (with ourselves) and ensures every file is consistently styled. We use the default configuration with one override: single quotes instead of double.
- Live Server. Launches a local development server with auto-reload. For static HTML projects, this is the entire dev experience. Save a file, the browser refreshes. No build step. No configuration.
- Emmet. Built into VS Code. Expands abbreviations into HTML. Typing
ul>li*5and pressing Tab generates a five-item list. For hand-writing HTML, this is an enormous time saver. - Color Highlight. Shows a small color swatch next to hex and RGB values in CSS. A tiny quality-of-life improvement that prevents constant alt-tabbing to a color picker.
That is four extensions. We have tried many more over the years: bracket colorizers, path completion, snippet libraries, theme packs, AI copilots. All of them were removed because they either did not earn their place or actively interfered with the way we work.
Settings that matter
Our VS Code settings file is short. The important ones:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2,
"editor.wordWrap": "on",
"editor.minimap.enabled": false,
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"breadcrumbs.enabled": false,
"workbench.startupEditor": "none"
}
Format on save is the most important setting. It means we never think about formatting. We just write code and let the tool handle indentation, line breaks, and spacing. This saves a surprising amount of mental energy over a long session.
The minimap is disabled because it adds visual noise without being useful for files under 500 lines (which is all of our files). Breadcrumbs are disabled for the same reason. Every pixel of screen space is either useful or noise. We remove the noise.
3. Languages: the web trinity (and sometimes TypeScript)
Our primary languages are HTML, CSS, and JavaScript. For most Slop Brains projects, these three are all we need. No preprocessors. No transpilers. No compile step. The browser reads what we write.
HTML
We write semantic HTML. Not because we are purists, but because semantic elements do real work. A <button> is focusable, keyboard-accessible, and announced correctly by screen readers. A <div onclick> is none of those things without additional work. Choosing the right element saves time and produces a better result.
We use HTML5 elements liberally: <header>, <main>, <nav>, <article>, <section>, <aside>, <footer>, <dialog>, <details>, <summary>. Each one has built-in behavior and accessibility features that we would otherwise have to implement manually.
The native <dialog> element deserves special mention. Before it was widely supported, building an accessible modal required hundreds of lines of JavaScript for focus trapping, backdrop handling, Escape key binding, and scroll locking. Now we call dialog.showModal() and the browser handles most of it. We still add custom animations and a few event handlers, but the foundation is solid and free.
CSS
We write plain CSS with custom properties. No Sass. No PostCSS. No Tailwind. The reasoning is straightforward: for a single-page website with one stylesheet, plain CSS is the fastest to write, the fastest to debug, and produces the smallest output.
Modern CSS has eliminated most of the reasons people used preprocessors. Custom properties replace variables. calc() replaces math functions. Nesting is now supported natively. clamp() replaces complex responsive typography setups. Container queries replace many uses of media queries. The language has improved enough that the overhead of a preprocessor is no longer justified for our use case.
We use a handful of CSS patterns consistently across projects:
- Custom properties for the color system.
--bg,--surface,--line,--text,--muted,--blue. Six variables that define the entire palette. clamp()for fluid typography. Headings usefont-size: clamp(36px, 4.5vw, 58px)so they scale smoothly between mobile and desktop without breakpoints.- CSS Grid for page layouts. Grid is the right tool for two-dimensional layouts (headers, sidebars, card grids). Flexbox is for one-dimensional layouts (navigation, button rows, card internals).
prefers-reduced-motionfor accessibility. All animations and transitions are disabled when the user has requested reduced motion. This is a single media query that wraps a universal selector.
JavaScript
Vanilla JavaScript. No frameworks. No libraries (except Lucide for icons, loaded from a CDN). For the size and complexity of our projects, a framework would add weight without adding value.
Our JavaScript files are typically 200 to 400 lines. They handle: search and filtering, modal open/close, clipboard operations, form handling, local storage, and the occasional animation. These are well-understood patterns that do not benefit from React's component model or Vue's reactivity system.
We use modern JavaScript features freely: const/let, arrow functions, template literals, destructuring, optional chaining, nullish coalescing, async/await. We do not use modules for most projects because a single <script> tag is simpler than configuring module resolution for a small codebase.
TypeScript (when it earns its place)
For larger projects with structured data, we use TypeScript. protocols.page has typed interfaces for protocol definitions. slang.fyi has typed dictionaries. The type system catches errors that would otherwise surface as runtime bugs in production.
The rule is: if the project has data models (objects with specific shapes that are used in multiple places), use TypeScript. If the project is a single-page tool with a few event handlers, plain JavaScript is fine. The boundary is roughly 500 lines of JavaScript. Below that, types are overhead. Above that, types are insurance.
4. Hosting: Cloudflare Pages
Every Slop Brains project is hosted on Cloudflare Pages. We have tried Netlify, Vercel, GitHub Pages, Render, and raw S3 + CloudFront. Cloudflare Pages is the one we keep coming back to. Here is why.
What Cloudflare Pages does well
- Free tier is genuinely free. Unlimited sites, unlimited bandwidth, 500 builds per month. For static sites, this covers everything we need without ever thinking about costs.
- Global edge network. Files are served from data centers around the world. Our sites load fast from every continent because the content is physically close to the visitor.
- HTTPS on custom domains, automatically. Add a domain, Cloudflare provisions an SSL certificate. No configuration. No renewal management. It just works.
- Git integration. Push to GitHub, Cloudflare builds and deploys. The feedback loop from code change to live site is about 45 seconds.
- Preview deployments. Every branch gets its own URL. This is useful for reviewing changes before merging to production, though we rarely use branches for our small projects.
What Cloudflare Pages does not do well
Honesty matters in a stack post. Here are the rough edges:
- Build logs are minimal. When a build fails, the error messages are sometimes cryptic. Debugging build failures requires more guesswork than it should.
- The dashboard is sprawling. Cloudflare has a hundred products and the dashboard reflects that. Finding Pages settings sometimes requires navigating through menus that feel designed for enterprise customers, not indie builders.
- Function support is evolving. Cloudflare Workers and Pages Functions are powerful but the developer experience is not as polished as Vercel's serverless functions. We do not use server functions, so this does not affect us directly.
Why not Vercel or Netlify?
Both are excellent. We have used both. The reason we settled on Cloudflare Pages is that we already use Cloudflare for DNS on all our domains. Having hosting and DNS in the same place eliminates a class of configuration problems (CNAME vs. A records, propagation delays, certificate provisioning). The integration is seamless because there is nothing to integrate.
If we were starting fresh and did not already use Cloudflare DNS, Vercel would be a strong choice. The developer experience is slightly better, the dashboard is cleaner, and the documentation is excellent. But switching now would mean migrating DNS for 15+ domains, and the benefit does not justify the effort.
5. Domains: where we buy and how we choose
Domains are an unreasonably important part of our process. A good domain can make a project feel inevitable. A bad domain can make a great idea feel awkward. We think about domains more than is probably healthy.
Where we register
- Cloudflare Registrar for .com, .net, and .org domains. Cloudflare sells domains at wholesale cost with no markup. A .com is about $10/year. There is no cheaper option because there literally cannot be.
- Porkbun for newer TLDs (.fyi, .page, .zone, .day, .meme). Porkbun has excellent TLD coverage, fair pricing, and a surprisingly good interface for a registrar. They also include free WHOIS privacy, which some registrars charge extra for.
- Namecheap as a fallback when neither Cloudflare nor Porkbun carries the TLD we want. This is rare but happens occasionally with very new or country-code TLDs.
How we choose domain names
Our naming approach has evolved over time. Early domains were descriptive (qrbench.pages.dev). Current domains are short, memorable, and use the TLD as part of the name (slang.fyi, protocols.page, resign.fyi).
Good domain names share these qualities:
- They describe the content or action.
slang.fyiis a place to learn about slang.resign.fyiis a place to learn about resigning. The name sets expectations before the visitor arrives. - They are short enough to remember. Under 15 characters including the TLD. If you cannot type it from memory after seeing it once, it is too long.
- They are easy to say out loud. "Check out slang dot fyi" works in conversation. "Check out internet-slang-reference dot pages dot dev" does not.
- The TLD adds meaning. .fyi suggests informational content. .page suggests a single, focused resource. .day suggests something that updates daily. .zone suggests a community or observatory. We choose TLDs deliberately, not just based on availability.
The domain budget
We set a hard budget for domains: $500 per year total. This covers roughly 25 to 30 domains depending on TLD pricing. When we approach the budget, we let inactive domains expire. The budget forces prioritization. If a domain is not attached to a live project or a project we will build within the next six months, it is not worth renewing.
This discipline was hard-won. At the peak, we were spending over $800/year on domains, most of which pointed at nothing. The budget cut that in half and forced us to be honest about which ideas we were actually going to build.
6. Design tools: mostly the browser
Our primary design tool is Chrome DevTools. This is not a joke. We design in the browser because the browser is where the final product lives. Designing in Figma and then implementing in code creates a translation step where details get lost, compromises get made, and the implementation never quite matches the mockup.
By designing directly in the browser, we skip the translation. What we see is what ships. The design and the implementation are the same thing.
How we use DevTools for design
- Responsive mode for testing at different viewport widths. We check 375px (iPhone SE), 768px (iPad), and full desktop width. Not by picking device presets, but by dragging the viewport to find where layouts break.
- Element inspector for tweaking CSS values in real time. Adjusting padding, font sizes, colors, and border radii live is faster than editing the file, saving, and refreshing.
- Color picker for checking contrast ratios. DevTools shows the WCAG contrast ratio when you click on a color value. We check every text/background combination.
- Performance panel for identifying rendering issues. We do not obsess over Lighthouse scores, but we do check that first paint happens quickly and there are no layout shifts.
- Network panel for verifying file sizes and load order. We check that fonts are preloaded, images are compressed, and no unnecessary requests are made.
When we use Figma
We reach for Figma when a layout is complex enough that we need to see the composition before writing code. This happens maybe once every three or four projects. Examples:
- A page with a multi-column layout that changes at three breakpoints
- A card design with many elements that need to be balanced
- A hero section where the image, text, and buttons need precise alignment
Even in these cases, the Figma work is rough. No pixel-perfect mockups. No exported assets. Just a quick sketch of the layout to establish proportions before writing CSS. The total time in Figma per project is usually under 30 minutes.
7. Images: WebP and aggressive compression
Images are typically the largest files in any web project. For our sites, images account for 50 to 70 percent of total page weight. Keeping them small is the single most impactful performance optimization we can make.
Our image workflow
- Create or source the image. Screenshots, photos, or generated images. We avoid stock photos because they feel generic. If an image does not add information, we do not include it.
- Resize to the maximum display size. There is no point serving a 3000px image that will be displayed at 800px. We resize to 2x the maximum CSS display width (so a 400px-wide image is exported at 800px for retina screens).
- Convert to WebP. WebP produces files 25 to 35 percent smaller than JPEG at equivalent quality. Every modern browser supports it. There is no reason to use JPEG or PNG for photographic images in 2026.
- Compress with Squoosh. Google's Squoosh is a browser-based image compression tool. We typically use quality 75 to 80 for WebP. The visual difference between 80 and 100 is negligible. The file size difference is 40 to 60 percent.
- Add loading="lazy" to below-fold images. Native lazy loading means images below the viewport are not downloaded until the user scrolls near them. This improves initial page load without any JavaScript.
The result: most images on our sites are 15 to 40 KB. A full page with 10 images typically weighs under 250 KB total for all images combined. Compare that to a single unoptimized hero image on many commercial sites, which can exceed 2 MB.
8. The daily workflow
Our actual workflow on a typical building day looks like this:
- Open the project in VS Code. Read the "Next:" note from the previous session.
- Start Live Server. The browser opens with the current state of the project.
- Work on the one thing. Not three things. One thing. Fix the mobile layout. Add the search filter. Write the copy for the about section. One task per session keeps focus sharp.
- Test in the browser. Resize. Click everything. Tab through interactive elements. Check on a phone if the change is layout-related.
- Commit with a descriptive message. Not "update styles." Something like "fix card grid wrapping below 430px" or "add keyboard shortcut Cmd+K for search focus."
- Write the next note. One line describing what to do next. Close the laptop.
The whole session takes 45 minutes to two hours. Some sessions produce a lot of visible progress. Others are spent entirely on debugging one layout issue or testing edge cases. Both are equally productive. The visible work is not always the important work.
Git workflow
Our git workflow is simple because our team is one person. We work on main for most projects. No feature branches. No pull requests. No code review (there is nobody to review it). Just commit and push.
This sounds reckless, and for a team it would be. For a solo builder working on small static sites, it is efficient. The risk of breaking something is low because the projects are small and we test manually before every push. If something does break, reverting a commit in git is a 10-second operation.
Commit messages matter even when you are the only one reading them. Six months from now, git log is the only record of what changed and why. We write messages that our future self can understand without reading the diff.
9. Tools we tried and dropped
A stack post is incomplete without listing what did not work. These tools are good. They just did not fit our workflow.
Tailwind CSS
We tried Tailwind for two projects. The utility-first approach is powerful for teams that need consistent styling across many developers. For a single person writing CSS for small projects, it adds a build step and a learning curve without a corresponding benefit. We write less CSS than most people think, and plain CSS is faster for us than looking up utility class names.
React
We used React for the first version of protocols.page. It was over-engineered. A static site that renders protocol descriptions does not need a virtual DOM, component state, or a build pipeline. The rewrite in plain HTML was faster, smaller, and easier to maintain. React is excellent for complex interactive applications. Our projects are not complex interactive applications.
Notion (for project management)
We tried using Notion to manage our project ideas, tasks, and notes. The tool is powerful but the overhead of maintaining a structured workspace was higher than the value it provided. A plain text file in Obsidian accomplishes the same thing with zero friction. The best tool for capturing ideas is the one with the lowest barrier to use. For us, that is a text file.
Google Analytics
We used Google Analytics on early projects and hated every part of it. The dashboard is overwhelming. The data is noisy. The privacy implications are uncomfortable. We switched to Plausible (lightweight, privacy-friendly) and then removed analytics from most projects entirely. The number of page views does not change the quality of the project.
Figma (as the primary design tool)
We tried designing every project in Figma first, then implementing in code. The translation from mockup to code always introduced compromises. Interactive states, responsive behavior, and real content never matched the static mockup. Designing in the browser eliminated the translation step and the compromises that came with it.
The pattern
Every tool we dropped shared the same flaw: it added a step between us and the final product. The fewer steps between idea and deployed website, the faster we ship and the higher the quality. Every intermediate step is a place where friction accumulates and details get lost.