which I think makes it a little more clear how weird this could get if you wanted to add more styles. You just keep growing the params passed to the ClassList add method, in a string. I would personally find
to be much more readable, but the author seems to imply this is complicated due to their approach of "Locality of Behaviour".
--
> Where libraries are necessary, use libraries that leverage html attributes over libraries built around javascript or custom syntax
I totally agree! But why is your example of a library not built around javascript or a custom syntax feature:
<input type="text" _="on input put me into #output">
<div id="output"></div>
That "on input put me into #output" I think is more jarring that the library shown as the bad example. Even better, this could just be some JS, without a framework at all.
--
> Prefer "naked" HTML to obfuscation layers that compile down to HTML
Their example is to not use Rails ERB tag helpers, in your templates. I don't think this is that big of an issue, these helpers can actually handle a lot of things that will look messy if you use the minimal amount of templating to write them. The example leaves off unique dom ids, turbo tags (very HTML/no JS focused!) and iteration.
>> Where possible, default to defining style and behaviour with inline HTML attributes
This was my critiquing point as well. Style configurations should be separate from actions, with only references linking the two. It's the only way that makes sense for anything meaningful.
Yes, I could technically write a python app where all SQL database connections have hardcoded SQL as one one long file, but that is poor practice. This suggested principle seems the same to me.
> Where libraries are necessary, use libraries that leverage html attributes over libraries built around javascript or custom syntax
And then they demo using _hyperscript [0] as encouraged. However, that's a library built around a custom syntax. It's only using an HTML attribute to encode a script that's in a new language you need to learn. Is this serious?
I'm not saying hyperscript isn't serious (I have no opinion on it at all actually). I'm saying the claim "avoid DSLs" followed by an example using a DSL is a sign of unseriousness on the part of the author of HTML First.
Six principles and one of them is presented with an example that blatantly violates that same principle. I could see a mistake like this slipping through if there were many more principles and examples, but this is a relatively short piece for such an error to slip in.
I get the idea - using the build-in capabilities of html is nice, clean, and simple. But that wasn't viable ten years ago, and it isn't today - and I don't particularly feel that htmx etc. is a better solution than something heavier like react.
My go-to questions with anything like this are: how do things look if I want a dropdown? Multiselect? Datepicker? If we use <input type="date" /> do we get a datepicker across browsers? (Looks like yes.) Is the look/feel/controls consistent across browsers? (No.) Can we style them to get there? (Also no.)
Multiselects are similar - shift/control-clicking to get multiple things is a flat no-go from a UX perspective - but at my last check, this is still how the default elements work, and it can't be changed. Similarly, the look/feel of multiselects (and even selects!) is terrible and largely cannot be changed.
There's a reason third-party components for this kind of thing get built for any new framework. The built-in stuff just doesn't get it done. It's the same reason 90% of my projects still have lodash as a dependency, even though the list of built-in stuff on MDN's Array page grows year by year. It's better than it was 10 years ago for sure - but its still not there.
Quick thought regarding date pickers, specifically:
> Is the look/feel/controls consistent across browsers? (No.) Can we style them to get there? (Also no.)
Assuming you design this website for users. Each users may use a different browser, but they probably use this same browser for all websites they visit. Hence IMO its more important that date pickers are consistent across all websites on 1 browser, then across 1 website on all browsers. (Its of course a different story if you need custom functionality.)
I find I'm in the opposite position - I would rather the date picker is not consistent, because different date pickers have different purposes. The date picker I want to use to put in my date of birth is different to the one I want to use to add an appointment to my calendar, and that's different to the one I want to use to browse prices for different days, and that's different to the one I want to use to be able to select a range of dates, and even that's often different to the one I want to use to select the two dates of a return journey.
Of the classic form controls, choosing a date is probably the one that has the most application-specific needs, and therefore the one that I would most expect to vary between applications.
The optimal date picker depends heavily on the platform too. Don't want your crusty custom date picker over my platform specific date picker that I know well.
The default datepickers in browsers are not feature-rich at all though. They're fine for extremely basic "pick a date" type of usecases, but as soon as you want to do anything slightly complex like picking a range, a date & specific time in one or having it be interactive in some other way like Google Flights showing you a range of prices alongside the date etc., you have to create your own datepicker component (or use an already built one).
This applies beyond date pickers too. To me, usability trumps consistency when your users access the website across a variety of platforms: mobile vs. desktop, touch vs. mouse, etc.
"shift/control-clicking to get multiple things is a flat no-go from a UX perspective" - Do you mean that this is NOT how you should do multiselects? If that is what you mean, then how _do_ you do them? If I have a list of items and I want to select 10 or 15 of them in a row, I currently don't know of a better UI to do that with than shift+click.
Most normal users (aka if you read Hacker News, you're not one of those) don't and won't know about shift and control clicking. A more UX-friendly alternative is to have checkboxes; you can still have shift/control-clicking on top of that (for selecting many things quickly), but it shouldn't be the only option.
Yeah, shift/control-click is a longstanding workflow for multiselect and macOS, Linux and windows all support it with various platform-specific subtleties. The worst part of the web is losing all these sorts of features because some web designer thinks they’re a “bad UX”
> Is the look/feel/controls consistent across browsers? (No.)
And to think that having a native look-and-feel used to be considered a must for any desktop application!
I would be much happier if all websites's UI components (not the actual website design of course) had the look-and-feel native to my browser (and if browsers actually cared to make them look good, TBH) rather than inventing their own dumb styles for everything.
<Aside> from the rest of the discussion, `input` element is infamously shitty for having vastly different ways of interacting with it, whereas most other HTML elements have one signature.
While I agree with most of the arguments here, this article feels a little contradictory - it recommends Tailwind but also tells us to 'stay clear of build steps'. Shipping massive CSS/JS resources goes against the whole inclusivity principle - many people don't have super fast internet connections or powerful enough computers..
I don't use Tailwind anymore at the moment, but in my experience, it does not lead to shipping massive CSS/JS resources.
In fact I don't know about any client-side JS at all that is emitted by Tailwind (my last experience was 2.x, not sure if anything has changed).
Regarding the CSS size, my experience was the opposite, Tailwind output was usually a lot smaller than hand-written CSS.
I have nothing against plain CSS either though; but it's at least as easy to make a mess.
I think GP is talking about how, without the tailwind build step, you ship all of tailwind, which is unquestionably a lot of CSS that you aren't using.
Maybe if you use a CDN, so hopefully the user might have a local cache of it from somewhere else, that can be avoided?
Still though, tailwind is pitched WITH it's build step normally, making the author's point about avoiding a build step a bit odd.
Did your experience involve the (recommended) build step? It uses, at least last I checked, a purgeCSS step to remove unused rules and decrease the css size.
But IIRC it doesn't offer quite everything full Tailwind does. In general I was frustrated with Fresh for not being up-front about the fact that it is largely built on Preact and Twind, and you must buy into those libraries first.
Tailwind has a standalone CLI which can be used for the build step. I do sort of agree that it's a bit of an odd recommendation in the context of this article, but the build step can be extremely minimalistic, and another nice thing is that the results are still inspectable CSS.
I use tailwind on my personal site, which is otherwise entirely just vanilla HTML, and it doesn't feel very intrusive to run the CLI in watch mode when I'm writing styles.
The OP was making a point that the build step should not be required to run/display a web app.
For example, tailwind without a build step is just the entire library. This means one can go a long way and even have a functioning web application without introducing a build step.
I would say stripping unused CSS is in the same context as optimizing images, fonts etc perhaps generally a "cleanup & prepare assets for production" step.
Tailwind doesn't exist without a build step (at least since v2) — the whole point of Tailwind is that it is a build tool, an alternative syntax for writing CSS.
There's a kind of development version where that build step runs in the browser on page load, but it's still a build step in the sense of generating all that CSS dynamically, and it will produce a poor experience for a user if you try and use it in production.
I read it the same way, yes. Good point with the cleanup:
- it’s one thing to need to run build for it to work;
- it’s another thing to run build to make it smaller;
The only thing a build step changes about CSS/JS resources of this kind, is a minimization of the libs...which is entirely achievable without building, by simply including the already-minimized version.
I think what the article is about when it says to steer clear of builds, is complex builds, where transpilations and similar changes in format have to happen, in order for the page to work.
> The only thing a build step changes about CSS/JS resources of this kind, is a minimization of the libs...which is entirely achievable without building, by simply including the already-minimized version.
This isn't true, though—Tailwind's build step isn't just stripping out white space, it removes unused selectirs, too, which can't be done in advance.
This is fun in to theory and in simple examples, but show me a big project that applies this and how it made a difference.
There are some bold objectives at the start that would be wonderful, but I’m a bit disappointed by the advice. I really don’t see how these would work in anything other than very basic scenarios, even less how they would achieve the objectives.
I’m all for using the web platform to the max, and I’m absolutely for reducing complexity as much as possible, but I’m highly skeptical these principles will achieve that and I would not be surprised if it increases complexity by having multiple ways to do something.
With peace and love but I can’t see from this list if you actually put these principles to the test or you just assumed it will do what you hope it will.
Why? Why does it need to be good for big projects in order to be good practice?
I’m genuinely asking. I never understood this argument that people bring.
In my view, the web is 95% small to medium projects. Most technologies should be focused on that - simple solutions for simple projectS. Add complexity later.
Because in practice there is little value in making easier things easier. While 95% on the web are small projects, 95% of work is done on large projects.
Many developers also dislike using many different frameworks, because that would require more learning. If you have to choose one technology it's better to use one where you can do everything. Not one where you can do 95% really fast, but 5% not at all.
I personally always use "complex" frameworks like Angular or React because sooner or later feature requests come in, where those frameworks pay off. On average it saves time for me to always use those frameworks. That might be different for you depending on the work you do.
I'd be more than happy to see small or medium projects and how these tips improved them. Any real world examples would be great.
I would also add that a lot of us do work on the bigger projects, which makes sense as bigger projects require more people. So at least in my life, and I expect many others, it is quite relevant.
I also don't believe the article qualifies that these tips are only for small to medium projects, I'd read it very differently if it did, but I would still like to see some real world examples though.
> In my view, the web is 95% small to medium projects.
I'm not sure that view is correct. For one thing, I'm not sure how you even define this.
As technology has advanced, many of the "small/medium" projects that used to require lots of dev time have turned into fully-built alternatives. The days when you need a dev to setup a blog are gone, as are the days you need one to setup a store, or a simple marketing website.
Are these part of the 95%? In some sense, yes, but in the sense of giving advice on a framework to choose, not at all, because no dev will even be making that choice - it's irrelevant.
As for medium-sized projects, there are tens of thousands of small, internal company tools that aren't even on the open internet. They probably fit your definition of being medium sized, let's say have a few devs working on them for many years, certainly medium sized compared to FB etc - but I'm not sure that whether this approach is right or wrong for them. (Genuinely not sure!)
I'm just saying, you need to much better define what you mean by small and medium sized, because some people might be thinking of my definition, while for some people medium-sized is, idk, AirBNB, which is tiny compared to FB but gigantic compared to most projects out there.
I would say that explaining where guidelines are applicable is on author side.
For text at hand I understand that author expects this way should be “the best” for everyone.
Then author gives examples that even in medium project at first requirement change or first additional non-trivial rule are going to have 2-way bindings plus bunch of other plumbing implemented where “html first” doesn’t have it and someone will have to write some JS monster to handle state etc.
Their second principle, to use inline styling, makes it harder to create a consistent look across the entire website. Defining that in one place and referencing it everywhere is better and easier.
I tried to push for an "HTML first" style frontend at my job, but we hired some run-of-the-mill frontend devs and they basically didn't get it and just wanted everything to be divs with VueJS controlling all of the logic and content.
One semi-objective thing we lost was accessibility. Much of the site is impossible to navigate via keyboard due to naively re-implemented behavior like links being divs with click event listeners. It's actually somewhat worrying - when regulations hit us, we'll have to scramble hard to get back what we threw out.
But all in all I kind of agree with you that it's very hard to find high profile examples of sites that are "HTML first". I believe in it, but haven't actually seen it pan out. But I suspect the reasons for it not panning out might be purely in education. By the time HTML became powerful, frontend dev education was already deeply framework-focused.
> it's very hard to find high profile examples of sites that are "HTML first"
Because the tools to create it are relatively new, and the sites you're speaking of had already been written. What high-profile site didn't already have a massive legacy React/Angular/Vue codebase, and a team of framework-trained developers, as of 2021?
For a frontend developer who is younger than jQuery, starting a project following this advice would be a good opportunity to learn why we do the things we do like build steps, and remember how much development sucked before HMR.
I suspect the author hasn't actually done this on a project with more than one person, supporting 99% of browsers in the wild. I also suspect they didn't run their own code, because either my screen is not as tasty, or "onlick" is not an handler of div.
Good old times, I used to have a file watcher that would refresh the page on change using a browser extension, not anywhere near the convenience of HMR though haha.
I do agree with you, it's why I'm skeptical about the results of following this advice. I vividly remember how much things sucked, and obv the web has come a long way, but the tools have gotten even further. If I see how much mileage I get out of the tools I use on the daily, I would not be nearly as productive without them, and produce a lot more buggy, inaccessible, and shitty apps.
> starting a project following this advice would be a good opportunity to learn why we do the things we do like build steps
Honestly, and sarcasm aside, I think this is an incredibly important thing for any new web developer to do.
Trying to learn web development in 2023 (or even in 2014, when I started my career) is so hard, because you're constantly standing on top of the shoulders of giants without even knowing how far you are from the ground.
I started a refresh of my personal site a few months back and resolved to write all the html and css "by hand", vanilla style, as a way of forcing myself to relearn the basics, and it was really refreshing to strip away all the layers of extra stuff and build it with simple tools. And I actually learned a ton of stuff that was useful on a daily basis while I worked on a React project during my day job, stuff that I just had never had to do or learn or use because some framework was always helping me out.
Recently I've been working on a little toy app in Phoenix, and I had the "revelation" that Eex / Phoenix components were slowing me down instead of speeding me up, because I didn't understand the underlying concepts as well as I needed to. As soon as I said "fuck it, I'm writing vanilla html and only using Eex where it's absolutely necessary" I was able to get through a whole host of issues that were giving me friction and actually build what I wanted.
I had a similar experience a few years ago when learning Phoenix. I just didn't get Ecto at all, and the reason was simple - I didn't know SQL and database design. Once I resolved to just figure out how to do the thing I was doing with raw SQL, Ecto immediately made way more sense.
We obviously can't peel back layers of the onion forever, or we'd never get anything done. At some point you have to get comfortable with abstracting away the details. But what I've found in web dev is that the big frameworks are written by people who've done the "vanilla" way so much that they've identified places where things hurt and built solutions that abstract that pain away. That's all well and good when you understand why the abstraction exists and the problem it solves, but it can really be confusing before you've put in the work to gain some of that context.
We have forgotten the old lessons. Backend first was a defense mechanism against people who believe their eyes and not the experts.
When you make a product that looks like it works but doesn’t, they don’t understand. They put you on a path to overpromise and underdeliver.
One of the lesser known features of unit tests are that they give the code that management can’t see more QA prior to being wired up. They narrow that awkward window from first paint to shipping.
I've created my first website in 1999 with plain HTML, CSS, vanilla JS, hosted on Geocities.
Since then I've been using PHP/WordPress/Yii/Laravel, Ruby/Rails/Sinatra/Jekyll, React/Typescript, ClojureScript to create both sites and apps.
With React / TSX components / CSS-in-TS / Effects / Context I'm home. Finally a fully fledged programming language for the web / front-end. A language made explicitly for the front-end, built om modern principles like functional, reactive programming.
Now I can do software development. Before that, with HTML, CSS, plain JS, PHP it was ... just hacking, nothing else. (Rails was good for full-stack, was not shining on the front-end)
I'll skip frameworks when the web stack will be ready for the apps, too. Now it's (perhaps) good enough for sites, I should admit.
All of the technologies you used previously were considered "finally fully fledged" until they weren't!
We will be doing something different in a couple of years and saying that the stuff we do now is out of date and the new stuff is home. It's always been this way. We are tech nomads finding ourselves new homes as and when we move on.
"I really don’t see how these would work in anything other than very basic scenarios, even less how they would achieve the objectives."
Well, what are the objectives? If they are complex, so should the code be complex. That's the nature of our job. By adding an advanced framework you up the complexity by default. Instead of adding more code, you add more build dependencies. This is especially wasteful on websites.
In my opinion, people today are afraid of writing code. Everyone wants some framework to write code for them. That is not how we push our industry forward.
No one is afraid of writing code, we're afraid of maintaining code, and solving tedious and repetitive problems that already have solutions. Frameworks abstract complexity, which in practical terms decreases the complexity I personally have to deal with, and shifts the complexity to the minds of a team of open-source developers who support the framework or library in parallel. Abstraction is exactly how we push the industry forward, not by building less, more basic, and shittier applications in a some faux-noble quest to use inline event handlers.
In some sense I agree, I am very mindful of the dependencies I add and I am not afraid to write something custom if that better fits the situation.
But this article is not showing me how to do that and the things listed are not going to have an impact on the complexity of my projects as these basic things are solved quite well.
> By adding an advanced framework you up the complexity by default
If you know your project will remain simple then by all means. That's often not how it works though and then you end up writing a framework yourself once the scope gets increased and features are added.
Adding to that that using a framework gives you so many things for free. There are so many aspects to a good website and leaning on a group of people specialising in all those things is often a smart move with better outcomes.
I think the initial complexity might be a little bit higher, but there often is a big return on investment later on, and also immediately in terms of productivity.
I'm not going to stop you from not using a framework, I think it's great to experience it, have been there many times before, got burned (badly), and now make different decisions.
Too many people today are being pointed toward React to do very simple things, like a single-page site that is nothing more than a little text, some images, and a few links out to various other places. These sites could easily be HTML/CSS. There is a lot of complexity for the sake of complexity on the web right now.
The worldwide jobs program that is modern-day Javascript would have you believe that elaborate front-ends are necessary to give end users the experiences they expect, which is an utter pile of lies. What users want is something simple that works.
One of the worst feelings is building a site without a view library like React, getting 80% there, and then realizing you absolutely need dynamic functionality and/or state management because the project's scope changed or started calling for it, only to realize you now need to refactor much (or all) of the site to make it easier to maintain across the board.
This is why I reach for a view library like React, Vue, or Svelte, even if I'm creating something simple. This is because I'm familiar with using these and they provide me with the ability to implement nearly any kind of dynamic functionality or interactions I could want, fine control over component lifecycle, and tight integration with my CSS library of choice to speed up development.
End-users are none the wiser, that is of course unless I do a terrible job using these tools.
Even if this isn’t always practical for larger projects today, I would argue that this should “ultimately” be the goal—-at some point the “standard” browser runtime should be expressive enough to not require lots of tooling to make most apps.
I'm not sure that's always the case — we don't expect assembly to be a high-level language after all! The more specific and batteries-included the browser becomes, the harder it is to go off the beaten track. My standard example here is date pickers — theoretically it's just a simple component, and yet there is no one-size-fits-all option. What works for booking an appointment won't work as well for putting in a date of birth. What works for a date of birth won't work if you're trying to book a set of nights in a hotel. You might want to include prices for individual days directly in the date picker. You might want to show which days are valid and which aren't. You might want to show several months, you might want to show just a week.
I don't necessarily disagree that more components in browsers is a bad thing (I've been very happy to use the new modal element, for example), but I think the browser is working better right now as a lower-level (albeit still fairly high-level) platform that allows people to build a variety of documents and applications on top.
Counterpoint: The article is titled "HTML First" not "HTML Only"
Admittedly I had the same reaction you did as I was reading the article. All I could think was how poorly these approaches would scale when the need arose for even modest ly complex state management.
While the body of the article doesn't address this, IMO the title does. I think it's generally good advice to suggest only reaching for the tools intended for complex environments when they become necessary, not before.
To be fair, 37 Signals (Basecamp) which is behind ruby on rails, changes their front end philosophy with every new major version of rails
And to be fairer, it's generally a pretty good philosophy for greenfield apps at that particular point in time, but if I started an app on rails 4 or 5 there's no way I'm updating my front end every time they change their minds about how front end should work
They believe this now, in 4-5 years it'll be XYZ next thing
It's been a while since I used it but I did a limited trial of Hey and it was genuinely miserable to use. Not only did everything take a few ms extra, the entire interface was so incredibly janky and had no accessibility concerns at all. They were shipping their website to their mobile apps and it was horrid. I would not put Hey as a shining example anywhere for this philosophy
It's been a long time since I used Basecamp but that was decent enough
I gave up this year, it's no longer possible to keep fighting these react, vue, angular monsters with their bundlers and transpilers and all the junk that comes with them, like node with npm which always throws at you the message that your brand new, seconds old project has 7 severe and 8 critical vulnerabilities in it, because it must download thousands of files, possibly including a wrapper for boolean values.
The times where I could use the Python backend servers to also serve the frontend seem to be over for me.
You (and indeed everyone else) has no real need for heavy JS front end bloatware. Just say no. Serve light pages from the backend (or cache) and do all the fancy work in CSS (if you must).
Say hello to fast render times and global accessibility.
Yeah but have you noticed how hard it has gotten to include simple libraries like day.js or three.js? It's still doable, but it's not easy anymore. Then there's some real value to be found in frameworks like Quasar which gets really painful to use via a cdn, which I've been doing for years now. Vue3 complicated things further so why not just move to react once and for all?
Vanilla JavaScript just didn't manage to solve what it kind of promised to solve via web components, the lack of templating is really a bad shortcoming.
I really do need things like Quasar and the stuff that Vue (and react) offer because it really helps a lot in usability. I've just been doing Vue with Quasar "by hand" via local CDN without compiling and it was such a win, but the more these frameworks evolve the less it's possible to ignore the fact that these are "modern frameworks" and the pain just grows too much.
For smallish or personal sites I'm 100% on board. For a large enterprise-y app, which in a previous era would have been a rich client deployable, the benefits of these hulking UI frameworks outweighs the costs.
Hit the eject button! A lot of the great things about the web are there in Phoenix Liveview. Sexy reactive websites without the need of lunatic React/Node shenanigans.
Thought TBH the article's examples are whack to me. Why would I write pure <form> tags instead of using the Phoenix form helpers that print out errors and other nice shit for me automatically. I get the spirit though.
The main thesis seems to be that the user should be able to press View Source and understand what's going on. I agree, at least for web sites. For web apps, at least anything over 50 lines, you are probably going to want to use a typed language. (Well, you could technically use .js with TypeScript compiler and type annotations in special comments but I did not find that very pleasant.)
I used to be really big on this, though (and it makes me sad that these days most sites are 1 big unreadable line of HTML). In fact, I find a beauty and elegance in shipping the whole thing as a single HTML file with no dependencies (1 network request!), though I did eventually make a "build system" (my build system is cat) so I could have a sane editing experience. Boom, self-contained portable software in 1 human-readable file!
Along the same lines, I think the coolest thing about web development is that you can make your first (interactive!) programs with Notepad and whatever browser ships with your machine. (Just drag the HTML file onto the browser!) It's magic!
Edit: Just found an unexpected benefit of self-contained HTML: makes your software immune to bit rot. I tried loading an old project of mine on Web Archive but it hadn't archived the external JS file! Sad! Meanwhile, this one loads fine because all the JS is in the HTML! Winning!
I'm sceptical on this: by now there are lots of ways to "peek behind the curtain" in the developer panel: we have the DOM view, network tab, heap view, built-in javascript prettifyer. Sure, "view source" is still important, but I'd question if its importance is still as absolute as the HTMX people make it out to be.
I wanted to show people how easy it is to do cool things with a computer, how low the barrier to entry is.* One HTML file. Your OS's built in text editor and browser. You look at the code and you can see it there in front of you! It isn't obfuscated. It isn't even minified. Everything is right there, all together. You can just save the file and drag it onto your browser. No Git, no zipped project folders. No build system! No server! (No MIME type errors!).
The "frictionlessness" of lowering the "activation energy", is not just nice for beginners... heck, it's nice for me! I can just download this file from Wayback Machine and continue hacking on it!
It does, of course, limit you to very small codebases, but that's what I like! Many small projects. The spring physics creature linked above is about 300 LOC total. The ASCII bonsai tree is ~100: https://web.archive.org/web/20220823113003id_/https://andai....
*In terms of tooling/setup, at least! Learning to code is another matter... (Neither of these projects are very readable for a beginner though, so that aspect needs some work.)
Edit: There is a proposal to extend JavaScript with type annotations, which would allow ("a reasonably large subset") of TypeScript to run directly in the browser. Yay!
“Locality of behaviour” is such a poorly defined rule. It’s just an invented name for going against separation of concerns. Calling CSS “spooky action at a distance” is a massive stretch too. Good principles here but the arguments are quite weak and could be much simpler.
(coming from a C# shop with too many interfaces) I think it's a natural counter-reaction to overly abstracted systems.
If "making the red-bouncy-plonk button instead become the blue-wobble-thunk button" requires chasing through a maze of 3-to-7 interfaces and classes to find which classes need new implementations and which can be reused... Suddenly what sounded like a 10 minute change becomes half a day of swearing under your breath at either the compiler or the previous engineer.
Sure, abstract things, but make sure there's also a way to bundle behavior together in one common spot, so I don't have to touch 6 files to update one component.
Tailwind is a build step with just as much 'spooky action at a distance'. If it wasn't, we'd just use inline CSS.
With Tailwind you're trusting a 3rd party library to abstract the CSS spec for you, and for that abstracted quasi-spec to be followed by your build configuration.
Inline CSS is a total anti-pattern apart from the absolute most basic pages IMO.
There is no harm in a plain CSS file, and applying classes at the element level in the HTML.
If you put in-line CSS in the HTML is not only leads to severe duplication, but is also a maintenance nightmare if you want to change anything. It works fine if all you are changing is a colour or whatever, but often there are margins, paddings, letter spacings, font sizes etc etc which lead to quite a lot of extra crap in each element on your page. Repeating those all over each if your HTML files is a real burden. Just use a single CSS file with sensibly named classes - it doesn't need to be sass or anything, just plain CSS is fine.
I think there are some other benefits of tailwind. Say you have a set of css classes for different components.
Then say one component on one page now needs to be styled differently.
You have 3 options:
1. Create a whole new class which duplicates much of the previous class
2. Create a smaller class which is intended to override some rules from the first class
3. Factor out the common styles into smaller classes.
All have some maintainability concerns, but taking the 3rd option to the extreme makes the problem non existent in the first place.
That’s the real strength of tailwind. Preventing a critical mass of one off styles from making your whole css setup unmanageable
It’s hard for me not to see Tailwind as using the class attribute to reproduce the style attribute - burying your html tags under a pile of css classes doesn’t feel that much different than defining those styles inline.
> “Locality of behaviour” is such a poorly defined rule.
And "separation of concerns" isn't?
What should be separated? Along what lines? How do we determine these lines? When does it make sense to pull some concerns out into another class/framework/markup/whatever? When does it make more sense to leave things stuck together?
The answer is: "It depends".
Not separating anything leads to spaghetti. Separating as much as possible, all the time, everywhere, leads to overly abstracted code that is easily as hard to maintain as spaghetti.
The primary stated goal is “substantially widen the pool of people who can work on web software codebases.” That’s very different from typical advice for programmers.
Customization isn’t required as long as defaults work. As such the efficiency gains from CSS etc take a back seat to simplicity.
> Where possible, default to defining style and behaviour with inline HTML attributes
but their example wouldn't work.
should probably be which I think makes it a little more clear how weird this could get if you wanted to add more styles. You just keep growing the params passed to the ClassList add method, in a string. I would personally find to be much more readable, but the author seems to imply this is complicated due to their approach of "Locality of Behaviour".--
> Where libraries are necessary, use libraries that leverage html attributes over libraries built around javascript or custom syntax
I totally agree! But why is your example of a library not built around javascript or a custom syntax feature:
That "on input put me into #output" I think is more jarring that the library shown as the bad example. Even better, this could just be some JS, without a framework at all.--
> Prefer "naked" HTML to obfuscation layers that compile down to HTML
Their example is to not use Rails ERB tag helpers, in your templates. I don't think this is that big of an issue, these helpers can actually handle a lot of things that will look messy if you use the minimal amount of templating to write them. The example leaves off unique dom ids, turbo tags (very HTML/no JS focused!) and iteration.
This was my critiquing point as well. Style configurations should be separate from actions, with only references linking the two. It's the only way that makes sense for anything meaningful.
Yes, I could technically write a python app where all SQL database connections have hardcoded SQL as one one long file, but that is poor practice. This suggested principle seems the same to me.
> Where libraries are necessary, use libraries that leverage html attributes over libraries built around javascript or custom syntax
And then they demo using _hyperscript [0] as encouraged. However, that's a library built around a custom syntax. It's only using an HTML attribute to encode a script that's in a new language you need to learn. Is this serious?
[0] https://hyperscript.org
Its example is also pretty weak. Vanilla solutions (Tip 1) works just as well:
<div> <input type="text" oninput="this.nextElementSibling.innerText = this.value"/> <div id="output"></div> </div>
https://html.spec.whatwg.org/dev/embedded-content.html#the-i...
1. Can't be easily done with vanilla JS and so can justify using a library.
2. Don't need a library with a DSL in direct opposition to the claimed principle (avoid DSLs).
I would rather there was just an explicit `onclick` (or some other event) in there with a vanilla JavaScript statement so I know what is happening.
source: I'm the creator of hyperscript.
Six principles and one of them is presented with an example that blatantly violates that same principle. I could see a mistake like this slipping through if there were many more principles and examples, but this is a relatively short piece for such an error to slip in.
My go-to questions with anything like this are: how do things look if I want a dropdown? Multiselect? Datepicker? If we use <input type="date" /> do we get a datepicker across browsers? (Looks like yes.) Is the look/feel/controls consistent across browsers? (No.) Can we style them to get there? (Also no.)
Multiselects are similar - shift/control-clicking to get multiple things is a flat no-go from a UX perspective - but at my last check, this is still how the default elements work, and it can't be changed. Similarly, the look/feel of multiselects (and even selects!) is terrible and largely cannot be changed.
There's a reason third-party components for this kind of thing get built for any new framework. The built-in stuff just doesn't get it done. It's the same reason 90% of my projects still have lodash as a dependency, even though the list of built-in stuff on MDN's Array page grows year by year. It's better than it was 10 years ago for sure - but its still not there.
> Is the look/feel/controls consistent across browsers? (No.) Can we style them to get there? (Also no.)
Assuming you design this website for users. Each users may use a different browser, but they probably use this same browser for all websites they visit. Hence IMO its more important that date pickers are consistent across all websites on 1 browser, then across 1 website on all browsers. (Its of course a different story if you need custom functionality.)
Of the classic form controls, choosing a date is probably the one that has the most application-specific needs, and therefore the one that I would most expect to vary between applications.
Deleted Comment
And to think that having a native look-and-feel used to be considered a must for any desktop application!
I would be much happier if all websites's UI components (not the actual website design of course) had the look-and-feel native to my browser (and if browsers actually cared to make them look good, TBH) rather than inventing their own dumb styles for everything.
Regarding the CSS size, my experience was the opposite, Tailwind output was usually a lot smaller than hand-written CSS.
I have nothing against plain CSS either though; but it's at least as easy to make a mess.
Maybe if you use a CDN, so hopefully the user might have a local cache of it from somewhere else, that can be avoided?
Still though, tailwind is pitched WITH it's build step normally, making the author's point about avoiding a build step a bit odd.
Tailwind output is massively bigger than the one with semantic CSS. Here's a comparison:
https://nuejs.org/blog/tailwind-vs-semantic-css/
But IIRC it doesn't offer quite everything full Tailwind does. In general I was frustrated with Fresh for not being up-front about the fact that it is largely built on Preact and Twind, and you must buy into those libraries first.
I use tailwind on my personal site, which is otherwise entirely just vanilla HTML, and it doesn't feel very intrusive to run the CLI in watch mode when I'm writing styles.
* Self host tailwind v3 CDN.
* https://github.com/gnat/css-scope-inline
Both are surprisingly fast- parse 10,000+ <style> or class="..." in under a second.
For example, tailwind without a build step is just the entire library. This means one can go a long way and even have a functioning web application without introducing a build step.
I would say stripping unused CSS is in the same context as optimizing images, fonts etc perhaps generally a "cleanup & prepare assets for production" step.
There's a kind of development version where that build step runs in the browser on page load, but it's still a build step in the sense of generating all that CSS dynamically, and it will produce a poor experience for a user if you try and use it in production.
I think what the article is about when it says to steer clear of builds, is complex builds, where transpilations and similar changes in format have to happen, in order for the page to work.
This isn't true, though—Tailwind's build step isn't just stripping out white space, it removes unused selectirs, too, which can't be done in advance.
There are some bold objectives at the start that would be wonderful, but I’m a bit disappointed by the advice. I really don’t see how these would work in anything other than very basic scenarios, even less how they would achieve the objectives.
I’m all for using the web platform to the max, and I’m absolutely for reducing complexity as much as possible, but I’m highly skeptical these principles will achieve that and I would not be surprised if it increases complexity by having multiple ways to do something.
With peace and love but I can’t see from this list if you actually put these principles to the test or you just assumed it will do what you hope it will.
I’m genuinely asking. I never understood this argument that people bring.
In my view, the web is 95% small to medium projects. Most technologies should be focused on that - simple solutions for simple projectS. Add complexity later.
Many developers also dislike using many different frameworks, because that would require more learning. If you have to choose one technology it's better to use one where you can do everything. Not one where you can do 95% really fast, but 5% not at all.
I personally always use "complex" frameworks like Angular or React because sooner or later feature requests come in, where those frameworks pay off. On average it saves time for me to always use those frameworks. That might be different for you depending on the work you do.
I would also add that a lot of us do work on the bigger projects, which makes sense as bigger projects require more people. So at least in my life, and I expect many others, it is quite relevant.
I also don't believe the article qualifies that these tips are only for small to medium projects, I'd read it very differently if it did, but I would still like to see some real world examples though.
Big companies are in the minority and what they do would not classify as "normal" or "standard practice".
It really bugs when people say "it's standard industry practice" when actually what they mean is "thats how the big businesses do it".
I'm not sure that view is correct. For one thing, I'm not sure how you even define this.
As technology has advanced, many of the "small/medium" projects that used to require lots of dev time have turned into fully-built alternatives. The days when you need a dev to setup a blog are gone, as are the days you need one to setup a store, or a simple marketing website.
Are these part of the 95%? In some sense, yes, but in the sense of giving advice on a framework to choose, not at all, because no dev will even be making that choice - it's irrelevant.
As for medium-sized projects, there are tens of thousands of small, internal company tools that aren't even on the open internet. They probably fit your definition of being medium sized, let's say have a few devs working on them for many years, certainly medium sized compared to FB etc - but I'm not sure that whether this approach is right or wrong for them. (Genuinely not sure!)
I'm just saying, you need to much better define what you mean by small and medium sized, because some people might be thinking of my definition, while for some people medium-sized is, idk, AirBNB, which is tiny compared to FB but gigantic compared to most projects out there.
For text at hand I understand that author expects this way should be “the best” for everyone.
Then author gives examples that even in medium project at first requirement change or first additional non-trivial rule are going to have 2-way bindings plus bunch of other plumbing implemented where “html first” doesn’t have it and someone will have to write some JS monster to handle state etc.
Their second principle, to use inline styling, makes it harder to create a consistent look across the entire website. Defining that in one place and referencing it everywhere is better and easier.
And what's good for one small project is likely to be good for many small projects.
In what terms? Sites in existence? I suppose. But sites by usage? Virtually all huge.
One semi-objective thing we lost was accessibility. Much of the site is impossible to navigate via keyboard due to naively re-implemented behavior like links being divs with click event listeners. It's actually somewhat worrying - when regulations hit us, we'll have to scramble hard to get back what we threw out.
But all in all I kind of agree with you that it's very hard to find high profile examples of sites that are "HTML first". I believe in it, but haven't actually seen it pan out. But I suspect the reasons for it not panning out might be purely in education. By the time HTML became powerful, frontend dev education was already deeply framework-focused.
Because the tools to create it are relatively new, and the sites you're speaking of had already been written. What high-profile site didn't already have a massive legacy React/Angular/Vue codebase, and a team of framework-trained developers, as of 2021?
TBH that's not a framework vs HTML issue, that's just sloppy or inexperienced devs
Frameworks also have linters that will help a lot, it’s on you to use them
I suspect the author hasn't actually done this on a project with more than one person, supporting 99% of browsers in the wild. I also suspect they didn't run their own code, because either my screen is not as tasty, or "onlick" is not an handler of div.
I do agree with you, it's why I'm skeptical about the results of following this advice. I vividly remember how much things sucked, and obv the web has come a long way, but the tools have gotten even further. If I see how much mileage I get out of the tools I use on the daily, I would not be nearly as productive without them, and produce a lot more buggy, inaccessible, and shitty apps.
Honestly, and sarcasm aside, I think this is an incredibly important thing for any new web developer to do.
Trying to learn web development in 2023 (or even in 2014, when I started my career) is so hard, because you're constantly standing on top of the shoulders of giants without even knowing how far you are from the ground.
I started a refresh of my personal site a few months back and resolved to write all the html and css "by hand", vanilla style, as a way of forcing myself to relearn the basics, and it was really refreshing to strip away all the layers of extra stuff and build it with simple tools. And I actually learned a ton of stuff that was useful on a daily basis while I worked on a React project during my day job, stuff that I just had never had to do or learn or use because some framework was always helping me out.
Recently I've been working on a little toy app in Phoenix, and I had the "revelation" that Eex / Phoenix components were slowing me down instead of speeding me up, because I didn't understand the underlying concepts as well as I needed to. As soon as I said "fuck it, I'm writing vanilla html and only using Eex where it's absolutely necessary" I was able to get through a whole host of issues that were giving me friction and actually build what I wanted.
I had a similar experience a few years ago when learning Phoenix. I just didn't get Ecto at all, and the reason was simple - I didn't know SQL and database design. Once I resolved to just figure out how to do the thing I was doing with raw SQL, Ecto immediately made way more sense.
We obviously can't peel back layers of the onion forever, or we'd never get anything done. At some point you have to get comfortable with abstracting away the details. But what I've found in web dev is that the big frameworks are written by people who've done the "vanilla" way so much that they've identified places where things hurt and built solutions that abstract that pain away. That's all well and good when you understand why the abstraction exists and the problem it solves, but it can really be confusing before you've put in the work to gain some of that context.
whereas you can addEventListener anywhere you want
When you make a product that looks like it works but doesn’t, they don’t understand. They put you on a path to overpromise and underdeliver.
One of the lesser known features of unit tests are that they give the code that management can’t see more QA prior to being wired up. They narrow that awkward window from first paint to shipping.
I've created my first website in 1999 with plain HTML, CSS, vanilla JS, hosted on Geocities.
Since then I've been using PHP/WordPress/Yii/Laravel, Ruby/Rails/Sinatra/Jekyll, React/Typescript, ClojureScript to create both sites and apps.
With React / TSX components / CSS-in-TS / Effects / Context I'm home. Finally a fully fledged programming language for the web / front-end. A language made explicitly for the front-end, built om modern principles like functional, reactive programming.
Now I can do software development. Before that, with HTML, CSS, plain JS, PHP it was ... just hacking, nothing else. (Rails was good for full-stack, was not shining on the front-end)
I'll skip frameworks when the web stack will be ready for the apps, too. Now it's (perhaps) good enough for sites, I should admit.
We will be doing something different in a couple of years and saying that the stuff we do now is out of date and the new stuff is home. It's always been this way. We are tech nomads finding ourselves new homes as and when we move on.
Well, what are the objectives? If they are complex, so should the code be complex. That's the nature of our job. By adding an advanced framework you up the complexity by default. Instead of adding more code, you add more build dependencies. This is especially wasteful on websites.
In my opinion, people today are afraid of writing code. Everyone wants some framework to write code for them. That is not how we push our industry forward.
But this article is not showing me how to do that and the things listed are not going to have an impact on the complexity of my projects as these basic things are solved quite well.
> By adding an advanced framework you up the complexity by default
If you know your project will remain simple then by all means. That's often not how it works though and then you end up writing a framework yourself once the scope gets increased and features are added.
Adding to that that using a framework gives you so many things for free. There are so many aspects to a good website and leaning on a group of people specialising in all those things is often a smart move with better outcomes.
I think the initial complexity might be a little bit higher, but there often is a big return on investment later on, and also immediately in terms of productivity.
I'm not going to stop you from not using a framework, I think it's great to experience it, have been there many times before, got burned (badly), and now make different decisions.
This is why I reach for a view library like React, Vue, or Svelte, even if I'm creating something simple. This is because I'm familiar with using these and they provide me with the ability to implement nearly any kind of dynamic functionality or interactions I could want, fine control over component lifecycle, and tight integration with my CSS library of choice to speed up development.
End-users are none the wiser, that is of course unless I do a terrible job using these tools.
I don't necessarily disagree that more components in browsers is a bad thing (I've been very happy to use the new modal element, for example), but I think the browser is working better right now as a lower-level (albeit still fairly high-level) platform that allows people to build a variety of documents and applications on top.
Admittedly I had the same reaction you did as I was reading the article. All I could think was how poorly these approaches would scale when the need arose for even modest ly complex state management.
While the body of the article doesn't address this, IMO the title does. I think it's generally good advice to suggest only reaching for the tools intended for complex environments when they become necessary, not before.
Not everything has be a Large Enterprise Application.
And to be fairer, it's generally a pretty good philosophy for greenfield apps at that particular point in time, but if I started an app on rails 4 or 5 there's no way I'm updating my front end every time they change their minds about how front end should work
They believe this now, in 4-5 years it'll be XYZ next thing
It's been a long time since I used Basecamp but that was decent enough
Dead Comment
The times where I could use the Python backend servers to also serve the frontend seem to be over for me.
You (and indeed everyone else) has no real need for heavy JS front end bloatware. Just say no. Serve light pages from the backend (or cache) and do all the fancy work in CSS (if you must).
Say hello to fast render times and global accessibility.
Vanilla JavaScript just didn't manage to solve what it kind of promised to solve via web components, the lack of templating is really a bad shortcoming.
I really do need things like Quasar and the stuff that Vue (and react) offer because it really helps a lot in usability. I've just been doing Vue with Quasar "by hand" via local CDN without compiling and it was such a win, but the more these frameworks evolve the less it's possible to ignore the fact that these are "modern frameworks" and the pain just grows too much.
Deleted Comment
Thought TBH the article's examples are whack to me. Why would I write pure <form> tags instead of using the Phoenix form helpers that print out errors and other nice shit for me automatically. I get the spirit though.
I used to be really big on this, though (and it makes me sad that these days most sites are 1 big unreadable line of HTML). In fact, I find a beauty and elegance in shipping the whole thing as a single HTML file with no dependencies (1 network request!), though I did eventually make a "build system" (my build system is cat) so I could have a sane editing experience. Boom, self-contained portable software in 1 human-readable file!
Along the same lines, I think the coolest thing about web development is that you can make your first (interactive!) programs with Notepad and whatever browser ships with your machine. (Just drag the HTML file onto the browser!) It's magic!
Edit: Just found an unexpected benefit of self-contained HTML: makes your software immune to bit rot. I tried loading an old project of mine on Web Archive but it hadn't archived the external JS file! Sad! Meanwhile, this one loads fine because all the JS is in the HTML! Winning!
https://web.archive.org/web/20210508133239id_/https://andai....
This is my homage to the old SodaPlay Constructor. (Never made an editor, sorry!) Feel free to view-source!
The "frictionlessness" of lowering the "activation energy", is not just nice for beginners... heck, it's nice for me! I can just download this file from Wayback Machine and continue hacking on it!
It does, of course, limit you to very small codebases, but that's what I like! Many small projects. The spring physics creature linked above is about 300 LOC total. The ASCII bonsai tree is ~100: https://web.archive.org/web/20220823113003id_/https://andai....
*In terms of tooling/setup, at least! Learning to code is another matter... (Neither of these projects are very readable for a beginner though, so that aspect needs some work.)
This is even easier now we have 'data:' URLs. It's also useful for avoiding problems on file:// URLs, which don't even need a HTTP server.
https://github.com/tc39/proposal-type-annotations
If "making the red-bouncy-plonk button instead become the blue-wobble-thunk button" requires chasing through a maze of 3-to-7 interfaces and classes to find which classes need new implementations and which can be reused... Suddenly what sounded like a 10 minute change becomes half a day of swearing under your breath at either the compiler or the previous engineer.
Sure, abstract things, but make sure there's also a way to bundle behavior together in one common spot, so I don't have to touch 6 files to update one component.
https://htmx.org/essays/locality-of-behaviour/#conflict-with...
With Tailwind you're trusting a 3rd party library to abstract the CSS spec for you, and for that abstracted quasi-spec to be followed by your build configuration.
There is no harm in a plain CSS file, and applying classes at the element level in the HTML.
If you put in-line CSS in the HTML is not only leads to severe duplication, but is also a maintenance nightmare if you want to change anything. It works fine if all you are changing is a colour or whatever, but often there are margins, paddings, letter spacings, font sizes etc etc which lead to quite a lot of extra crap in each element on your page. Repeating those all over each if your HTML files is a real burden. Just use a single CSS file with sensibly named classes - it doesn't need to be sass or anything, just plain CSS is fine.
Then say one component on one page now needs to be styled differently.
You have 3 options: 1. Create a whole new class which duplicates much of the previous class 2. Create a smaller class which is intended to override some rules from the first class 3. Factor out the common styles into smaller classes.
All have some maintainability concerns, but taking the 3rd option to the extreme makes the problem non existent in the first place.
That’s the real strength of tailwind. Preventing a critical mass of one off styles from making your whole css setup unmanageable
And "separation of concerns" isn't?
What should be separated? Along what lines? How do we determine these lines? When does it make sense to pull some concerns out into another class/framework/markup/whatever? When does it make more sense to leave things stuck together?
The answer is: "It depends".
Not separating anything leads to spaghetti. Separating as much as possible, all the time, everywhere, leads to overly abstracted code that is easily as hard to maintain as spaghetti.
JS, CSS, and HTML aren't concerns, they are technologies that have cross-cutting concerns.
Customization isn’t required as long as defaults work. As such the efficiency gains from CSS etc take a back seat to simplicity.
If we look at HTML as a document / presentation language, we cannot deny styling. CSS is not only styling, but also adds animation.
In any case, CSS can be seen as aspect oriented programming.
The real problem described here is the lack of great tooling across languages / frameworks.