{"id":"vusuzqhjmkb0u1i","title":"What It Actually Takes to Build a Website People Love","slug":"what-it-actually-takes-to-build-a-website-people-love","summary":"People love websites that respect their time. Clear structure, fast loading, useful copy, and small moments of personality beat a pile of trendy components every time.","imageUrl":"https://briancrabtree.me/images/journal-what-it-actually-takes-to-build-a-website-people-love.webp","category":"UX Theory","date":"11/26/2025","featured":false,"likes":52,"author":"Brian Crabtree","content":"<h2>Rethinking the Architectural Foundation</h2>\n\n<p>Most architecture meetings I sit in on these days devolve into arguments about which front-end framework has the best developer experience, completely ignoring the user experience. We are optimizing for the wrong people. A resilient architecture prioritizes the transmission of data over the convenience of the developer. This means we have to look closely at the relationship between the server and the client. For a few years, we swung the pendulum way too far toward client-side rendering, forcing the user's browser to download a massive bundle of JavaScript just to render a header tag. We are finally seeing a correction back toward Server-Side Rendering (SSR) and static generation, which is essentially just a fancy way of saying we are remembering how browsers were designed to work in the first place.</p>\n\n<p>However, SSR brings its own nightmare, specifically the concept of hydration. This is the uncanny valley of web performance where the site looks ready to use because the HTML is there, but you click a button and nothing happens because the JavaScript hasn't executed yet. It creates a rage-inducing user experience that metrics often miss. We need to be rigorously critical of what actually needs to be dynamic. If a component doesn't need to change state after the initial load, it has no business being part of a hydration bundle. We should be shipping HTML that functions primarily without JavaScript, treating interactivity as a progressive enhancement rather than a prerequisite for basic functionality.</p>\n\n<h2>Responsive Design is Not Just Squishy Layouts</h2>\n\n<p>I get tired of hearing people claim a site is \"responsive\" just because the columns stack on top of each other when you resize the window. That is the bare minimum, the table stakes from 2012. True device-agnostic design is an architectural commitment to performance and context, not just CSS media queries. It starts with a mindset shift that most designers and developers still struggle with, which is the rejection of \"pixel perfection.\" The web is a fluid medium. Pixels are a hallucination. If you are hard-coding values in pixels rather than using relative units like REMs, EMs, and percentages, you are building a brittle system that will shatter the moment a user adjusts their default browser font size for accessibility reasons.</p>\n\n<p>We have to move past the basics of Flexbox and Grid—which, by the way, if you are still using floats for layout, please stop—and look at resource delivery. The <code>picture</code> element is ugly code. It is verbose, repetitive, and annoying to write. It is also absolutely mandatory. Serving a 4K hero image to an iPhone SE is not just lazy; it is hostile to the user's data plan and battery life. We have to utilize the platform's native capabilities to let the browser decide what it needs. This extends to HTTP Client Hints. We can detect if a user has `Save-Data` enabled at the network level and choose to serve a low-res image or no image at all before the request is even fully processed. That is responsive design. It is responding to the user's constraints, not just their screen width.</p>\n\n<h2>The Navigation and Information Structure</h2>\n\n<p>There is a disturbing trend where we hide everything behind a hamburger menu even on desktop screens because it looks \"clean.\" Clean is often a synonym for empty. Navigation is the primary interface for the user's journey, and burying it effectively cripples the information architecture we spent weeks planning. A user should not have to hunt for the primary sections of a site. If you have the screen real estate, use it. Global navigation needs to be consistent, boring, and immediately available. It provides the anchor for the user's mental model of the application.</p>\n\n<p>Under the hood, this navigation must be built with rigorous semantic HTML. I see far too many clickable <code>div</code>s and <code>span</code>s souping up the DOM. A link is an anchor tag; a button is a button tag. This isn't pedantry; it is about how screen readers and search engines interpret structure. When you use the correct semantic element, you get keyboard accessibility and focus management for free. When you roll your own interactive elements using generic containers, you force yourself to write mounds of WAI-ARIA attributes to retrofit meaning that should have been there natively. It is a waste of code and a maintenance liability. If you find yourself writing a click handler for a <code>div</code>, take a walk and rethink your choices.</p>\n\n<h2>Performance as a Moral Obligation</h2>\n\n<p>We need to stop treating performance as a ticket in the backlog to be addressed \"post-launch.\" Performance is the feature. If the site is slow, it doesn't matter how good the content is because nobody is going to stick around to see it. Core Web Vitals have finally forced the industry to care about things like Cumulative Layout Shift (CLS), which has been the bane of my existence for years. There is nothing more infuriating than reading a paragraph and having the text jump down four hundred pixels because an ad or a hero image finally decided to load. This is solvable engineering. Reserve space for your assets. Define aspect ratios in your CSS. Don't let the layout thrash around like a fish out of water.</p>\n\n<p>The solution almost always comes down to sending less stuff. We are obsessed with third-party scripts. Marketing wants a tracker, sales wants a chatbot, analytics wants a heatmap, and suddenly the main thread is blocked for three seconds while the browser tries to execute a megabyte of unminified garbage from three different vendors. As engineers, we have to be the gatekeepers. We have to be the ones who say \"no.\" If a script doesn't add tangible value to the user, it gets cut. If it can be deferred, it gets deferred. If it can be proxied through a server-side container to save the client the processing cost, we do that. We are not just code monkeys; we are the stewards of the user's resources.</p>\n\n<p>Building for the web is about resilience. It is about acknowledging that the network is unreliable, devices are varied, and users are impatient. We don't need to reinvent the wheel every six months. We need to respect the medium. We need to write semantic HTML, efficient CSS, and performant JavaScript only when necessary. It is not glamorous work, and it won't win you any design awards from people who browse the web on $5,000 monitors connected to fiber optics. But it is the only way to build a digital presence that actually respects the people using it. For a related angle I keep coming back to, see <a href=\"/journal/performance-checks-before-handoff/\">Performance Checks Before Handoff</a>.</p>","tags":["Next.js App Router","React Server Components","TypeScript","Static Site Generation (SSG)","Server-Side Rendering (SSR)"],"views":110}