In my experience it's much more important to modularize/capsulate/segmentate by groupings of business logic then by groupings of implementation details.
Applying this to web applications (not simple text focused websites) this would mean that its preferable to have everything (HTML+JS+CSS) related to a specific widget (e.g. counter) in one place, instead of splitting it into many parts sprinkled across many files potentially in different folders. Similarly you also would e.g. want the CSS to be encapsulated as to the component as far as possible.
Its a bit of a different matter for classical html documents where the HTML made sense without JS or CSS and both where thinks you added "on top" to "just make it a bit nicer" but this isn't the think anymore for modern webapps (and many fancy websites).
Also if you work in a situation where you have many huge teams working on the same UI at the same time touching the same logical component (but normally different aspects of it) then having a more clear segregation can also make sense, but >99% of all companies are not ever in that situation.
This is also for example a major critic I have with react, it splits one logical unit across different files making it much harder to reason about it for the benefit of adding a bit more decoupling which isn't needed by >99% of dev teams.
I guess if I were to rationalize my position, I'd say it's because I have a hard time finding related things when they're separate. If a button or "a" tag have a special click handler, I want to know when looking at it. If it just has classes, then I don't know if that are for style or if they're for behavior. I know there are some conventions out there where you prefix classes with "js-" etc, but if you just use "onclick" then it's obvious and you don't need a convention.
The gist is that folder-by-feature is generally preferrable because it requires less context switching (in the literal sense of jumping between multiple different far away folders and scanning each for the thing you're looking for)
Single file components force you to organize code in a folder-by-feature structure, to a large extent. You can use folder-by-feature structure alongside with separation of technologies, it's just not that common to see it because the tooling to support it is not quite as optimized.
[0] https://softwareengineering.stackexchange.com/questions/3385...
Angular is a pretty good example of this. The HTML, CSS, unit tests, and code are all in separate files, but they have the same base name and are grouped together in the same place.
> I have a hard time finding related things when they're separate.
Store them in the same directory?
I agree on the events/business logic that they make sense to couple with the template code.
At best you will achieved a few smaller files. At worst you make working within the system a real pain.
Some practical reasons for separate files, off the top of my head:
In-lining JS, and CSS means enabling unsafe-eval, which can open up XSS vulnerabilities if you use any content from users or gathered from a source you don't control. You could also do the unsafe-hashes, but that's kind of a pain.
If you're not making a SPA, it would not be fun to copy the CSS to all of your files.
If you do not couple your CSS to your site, you can reuse it across many sites and have the same style. Say if you have separate site for your blog, but want it to look the same as your main site.
If you're working on a team splitting things up into separate files will avoid some merge conflicts.
Chances are they will not be updated all at the same time, so caching would speed up page load times, as the user would only have to download the changed files
For files where the logic, structure, and styling fits on a single page - yeah, splitting it into separate files doesn't make much sense.
> At worst you make working within the system a real pain.
That's what I don't understand - what pain? That it's better not to work with it using nano or notepad? It simply looks like a pain I would never experience, and I'm wondering if I'm right.
Exactly! By putting everything into one file, you give the reader the option of splitting their view if they want, or looking at the file all in one pane; with multiple files, you have to have each file open independently. Editors give pretty nice ways of navigating to specific functions/methods/points within a file these days, so traversing a large file is not really an issue IME.
Personally, I don't understand the fascination with splitting everything into super small files; given the large number of files I generally have open in my editor, I'd rather look at one file with 1k lines of code than 3 files with 100-500 each. With separate files, if I'm trying to debug something that touches three different components, I need to have 9 files open instead of just 3.
The differences between my and your environments mean that our preferred workflows differ, too, because what can be problematic for you can be easy for me, and vice versa.
That being said, I also don't advocate "splitting everything into super small files". Splitting a file that is less than a "page or two" (I see 61 lines of code on one page, so less than 120 loc) is a mistake and should be done only in special circumstances, while files that grew larger than 500 loc should be refactored if it makes sense. Files over 1k loc have to be split. That's the policy we use at work, and I think it results in a pretty nice and easy to navigate code bases.
Back to the editors, though: as long as the codebase uses a convention - any convention - consistently, you can make it nice to work with, by altering your environment to match the convention. This is where the more configurable and easier to script editors win big time - if there's something in the codebase you don't like, and can't change, you can script it out of sight and out of mind easily. Coming from such an editor, I personally don't have a strong opinion on how the codebase should be structured (which includes the lenghts of files) - I do have some preferences, but I'm not very attached to them. That's just to signal that I'm not the one of people "fascinated" with splitting files for the sake of it :)
If your convention involves using the same name throughout different files (e.g. every component has a `styles` object), this becomes cumbersome to do. (Arguably, that's a problem with the convention, but sometimes you don't have control over this...)
Other than that, I mostly agree with you and fully admit that it's a matter of preference and highly dependent on your workflow; I was just trying to point out that a single file is generally easier to navigate with an out-of-the-box experience in most editors. I also agree with your stance on when to split files, with a small caveat: even when splitting something into multiple files, I still tend to group them by their high-level function as opposed to by "type". In other words, I would rather split a helper component into its own file with its own styling in the same file, rather than split all of the styling into its own file. (Of course, sometimes you really do have 800 lines of styles, in which case they probably need to be in their own file regardless.)
> That being said, I also don't advocate "splitting everything into super small files".
That's great! :) I see this too often in frontend web development, though: people will go out of their way to keep their files as small as possible to an irrational degree—almost as if they are trying to ensure that you can view every file in its entirety without scrolling.
Actual workflows diverge significantly from the above. It's a matter of cohesion. Yes, co-locating each of the files (and only these files) in a single "component" directory can help ease navigation and discovery, but most of the time it's just easier to keep everything as cohesive as possible.
Really? HTML is already heavy on syntax, and the whole point of SGML-style angle-bracket markup is to invisibly structure text hierarchically and sneak in rendering properties or other "metadata" not rendered to the user, via attributes. In which universe, then, has it ever made sense to write
<div style="color: red">
rather than <div color=red>
in a document representation language? Let alone today's CSS atrocities. Mind, I'm not talking about CSS' out-of-control layout model complexity, but inventing a new syntax (or a thousand microsyntaxes really) on top of already syntax-heavy markup.If you think about it, CSS appears to have gotten these ninja super-powers because HTML was locked in a decade-long stagnation while W3C was busy with XML, RDF, and whatnot. Then again, in the last decade using <div>s for nearly everything was frowned upon, so with the precedent set in the decade before, CSS just had to re-order, re-place, re-arrange ad absurdum without any discernible mental discipline. Or, maybe the CSS WG people just couldn't stop.
The end effect is that CSS isn't approachable for even seasoned graphic artists let alone laymen; another effect being browser complexity resulting in monopolies.
A false separation-of-concerns narrative has ruined many languages and approaches (such as enterprise OOP); for the web it was particularly idiotic given it's founded on composable documents.
JS? Once it was out there, evolution of HTML and declarative UIs basically stalled because there wasn't anything you couldn't do using JS. Nevertheless, CSS had also grown into outlandish complexity. Basically, the "web stack" of today is what any standardization effort should've avoided.
This is so true. CSS has become a multi-headed Hydra whose parts appear completely unrelated to each other. I've been a developer and designer for more than 20 years and I have no idea what the parameter names and orders are for position, versus grids, versus float, etc. It's parameter soup. Who said the hardest part of programming is naming things? The CSS folks didn't get the memo.
To do any real work with CSS means you have memorize a bunch of conflicting weirdness and/or keep a reference page open at all times. The idea of CSS frameworks to simplify or fix this doesn't work because, in order to write or debug what you create in those frameworks, you must know regular old CSS!
It's turtles all the way down.
I agree with CSS being complicated, but this statement is true for pretty much everything. Not a day goes by I don’t close 20+ reference tabs at the end of the day, not counting what I closed throughout the day. It’s simply impossible to be a polyglot and not use references.
Who said the hardest part of programming is naming things? Some people do a better job than others. CSS is really wildly inconsistent within in itself. Is it another case of worse is better? Ugh. Probably.
I also made SnackUI to solve the last problems of fully inline styles: namely to make them work 100% between React Native and Web, to make them extract to atomic CSS on web automatically, to optimize even conditionals into CSS, and finally to make media queries and themes work the same between Native/Web. We’re launching a very large app with it here in not long, and I absolutely love how clear it is to work on.
Most of these "new" frameworks are more because someone wanted to scratch an itch or solve a specific problem they had, and frontend code simply lends itself well to being open sourced. If you don't like the new js tools that are made, just ignore them.
What I’ll forever complain about (as a front end developer) is that there’s no distinction between a document and an app. You can start from a blog post and turn it into a rocket just by throwing more JS at it.
I think only Google can fix it at this point:
- come up with a better alternative for apps
- reward websites that are content-only (or are guaranteed to support Reader view)
Now you could start to have simple documents and a powerful app platform.
Unfortunately this is hard to do as most developers will call bloody murder. So let’s enjoy CSS for the next hundred years, shall we?
Web app dev has started focusing on single, separate, reusable components instead of trying to design everything on the page at once.
Often, in these components, the HTML, CSS, and JS is still separated, but now (theoretically) you can plug that component in anywhere (even a different app) and, as long as you feed it the right data, it should just work.
Replying to you because this is the shortest expression of this sentiment, expressed in many different comments:
No, it's not because of components. It's because of emulation of components by means of precompilation or JS execution. First-class components are available already, there's no reason to continue to emulate them, unless you need backward compatibility, or have another goal other than separating components.
It's also not about "components" in the meaning of "widgets" - reusable bits of content+view+behavior - because these can be trivially made with standard HTML + CSS + jQuery plugin. It was done since before the DHTML was a thing (hello <iframe>!).
Instead, it's all about web apps, how they achieve responsiveness, and the fact that the more the framework knows about the content (including how it should look in all its states), the easier it is to optimize it consistently. I get that, it's ok for web apps, and it gets better the more nice syntax you throw at the problem (up to a point, as usual), because once you have enough of syntax extensions to replicate the important features of HTML and CSS, you can go absolutely nuts on the actually generated code and nobody would care.
On the other hand, using web app frameworks to write web pages is bad - do you really need to optimize for responsiveness if almost everything you see on the page has exactly one (or two, with hover) state? The same is probably true for languages designed for web apps: using them to write web pages can be tedious and more trouble than it's worth.
TLDR: it's because ~components~ web apps.
But to me, there's a parallel to be made between both the newer trends of components and micro-services, and the old idea of Object Oriented Programming. In all cases, you get sold on the idea that everything should be cut down into tiny pieces.
Theoretically the separation of concerns has tons of merits, but in practice there are a lot of rough edges when those separate pieces have to interact with each others.
And the tradeoffs may not always be worth it when your project isn't on the scale of those large companies (it very rarely is).
The key though is that componentization is useful when you find yourself writing the same code over and over again. That's where DRY steps in and says yeah this should be its own component.
This is one reason monoliths are great as an app structure, because they let you be as DRY as possible and have as few seams as possible (making a component within a monolith doesn't create an external dependency, no seam). Additionally, there are options nowadays (ruby on jets for example) that let you have a monolith repo structure but each controller endpoint gets deployed as a separate lambda. So you get to have your cake and eat it too.
The analogy really does work fairly well at every level of software engineering, from frontend components to backend services.
Intuitively you may not want too much, until you find out that maybe you could have needed a bit more because of the repetitiveness. So frameworks that give you the option to gradually increase it when you need are the best. And when the documentation follows those patterns it's really great (I'd put Vue in that category).
Usually the thinking was, while it may not make sense, it will someday when my project grows, so let's adopt it right away. And for years I would have said that was a decent advice. Nowadays I think that with some of the more extreme scales behind some of those trends though, it's not always as clear cut.
And even then you will definitely run into issues such as "in this particular case this particular tab will look like this".
While the web was mostly leaf components (text, articles, images, links) this separation kinda worked. The moment you move into app territory, there are not that many things that are reusable and benefit from separation, because your UI is directly dependent on business logic and vice versa. And because every screen in your app is a unique view into a unique part of your functionality.
This should, ideally, be as short and sweet as possible. In reality, like you say, the two ideas (visual + biz logic) will always be married in any non-trival application.
In other words, you are likely to need too many customizations even to leaf components to be able to use someone else's.
If you are actually using components, that isn’t much of an issue.
In a time when you’d be returning a whole document every single time, it might make sense to say ‘style this one document with this one stylesheet’, and even more so in a time when HTMl was still more or less semantic (e.g. an actual document).
There's also some nice stuff with modern tooling, where CSS is global by nature but writing it with your JS allows for the platform to automatically namespace your CSS, making styling behavior much more consistent.
Separately it also often makes sense to style things not just based on the HTML produced but also based on data stored in JS; writing your CSS with your components allows for a greater level of dynamic styles (which, to be fair, if you can achieve with pure static CSS is preferable, but it's not always the case).
In olden days, the DOM was treated like something shared. This could lead to a single DOM elements receiving changes from all over the place: Multiple CSS selectors would include it and apply style rules, and multiple JavaScript scripts might select and manipulate it. Spitting the CSS/HTML/JavaScript into multiple files just makes it harder to know what's doing what.
The new frameworks are built around the idea that when we need to manipulate or style a DOM element then it should be isolated from the rest of the DOM, and so should the CSS and JavaScript that do the manipulating. In other words, what we often call "components".
For convenience, we often group these three things into the same file, or into small files sharing the same directory.
Reasoning about them much simpler, because each one is like a tiny HTML document with just a few DOM elements. No need to review hundreds of CSS selectors or JavaScript event handlers, because everything is together in one small package.
It handles CSS by letting you define it in within an individual component (file) but also import a style from another file. That way, your shared styles are in one place and only the overrides or extra styles needed for a special component are in its file.
I think it's wonderful!
Not to denigrate this project in any way, but most or all of its goals are already met by Lit[0]. Instead of a new language, it uses regular TS and regular CSS.
[0] http://lit.dev
Granted, some UI frameworks do add a lot of redundant code to maximise component independence, then offer JS-based or class-based theming to keep development DRY.
So, if you split by technology, you don't have the code belonging to one feature in one place.
Because the horizontal way separates the concern the wrong way, or more precisely, the non-scalable way.
The idea is the same way that backend architectures are migrating from layered architectures to microservice or DDD alike architectures.
HTML, CSS, JavaScript separation of concern: 3 layers. Layered architectures: presentation, application, domain, infrastructure etc. There's only O(1) number of layers, so it's not scalable enough for modern applications. You can came up with more layers, but it's still O(1) and would be more strechy, despite the business and codespace are growing fast. People will struggle because everytime they change one layer, they have to find the corresponding other layers of code - it starts to defeat the purpose of separation where it should help people not caring other layers when modifying code.
A more critical separation of concern is to have O(n) separation: you can different domains like products, inventory, sellers, account managment, custommer support etc. If your business continue to grow, you could have more of them. And more importantly, it's much easier to separate teams into pizza-sized teams than layered separation.
Don't get me wrong, layered architectures are still useful. In fact, most of the DDD implementations are layered. But there's mental overhead when you introduce separation. Communities like React and Vue decide that it makes sense to merge layers in Front-end for local reasoning over separation, and accoding to my experience it works well (because I find it's very common to modify HTML, JavaScript, and CSS at the same time before I even started to use React).
I think what you were going for is the idea that, beyond a trivial application, the visual appearance stuff is always going to be tied to some app logic or business logic or state or whatever word we want to use.
And that's very true regardless of what framework or language we choose.
That situation most likely requires HTML (DOM) generation in the browser, which can only happen in JavaScript.
So, I reiterate, the HTML is generated by JS in these interactive modules.
You could generate the HTML on the server and then use jQuery to toggle elements… or just create full-JS components that take care of everything, giving some input data, instead of separating the HTML from the JS.
I also think most people are quite bad at organizing CSS, so I'm personally thankful for this change even though I love a well organized simple CSS/HTML site. It means less projects I inherit are rabbit warrens of legacy CSS to unravel.
When all three of these things have to care very directly about eachother, separating them makes less sense.
Our designer wouldn't have been able to deal with HTML markup within the JS files in React. Having the HTML and (S)CSS separated out made life much easier for him in Angular.
(edit: Naturally downvoted due to being slightly critical of React on HN. Which happens every time.)
You are being critical of separation choices. To which I am curious why embedding the CSS and HTML inside a Javascript file makes any difference to the designer? In practice, we often see slightly different syntax to accommodate, but I cannot imagine that is a true barrier. The CSS and HTML (or their slightly modified equivalents) are still logically separated from the rest of the logic in the file.
He has no idea about how the TypeScript/JavaScript files work. He doesn't really have to touch them, unless to maybe see what extra data the component model contains if he wanted to dig deeper and display other things.
If the designer was handed an HTML page with <style> tags embedded, I cannot imagine he would fall down unable to figure out where the CSS is, and this is not much different.
https://adamwathan.me/css-utility-classes-and-separation-of-...