Increasing Velocity
After interning at Alto Pharmacy, I have a new thought on approaching frontend development.
Product in big companies is often just putting buttons, modals, and existing layouts together to create features. Design systems ossify things. They make things hard to change and reinterpret. If I want to change a button for a certain context, it can be difficult. People try to fix that by adding in overrides for their design system.
Don’t over-constrain. Duplicate if needed.
Rather than a ‘design system’, what if there’s a ‘design standard’ and anyone can make changes to parts of the website and compare it to the standard? I do think that going over multiple pages and saying ‘hey, that looks wrong.’ might be a better approach to development than writing tons of tests and so on.
You’ll also want multiple devices: the smallest phone, a giant phone, Thinkpads and Macbooks, tablets, ultrawide monitors. All just to test how the website actually is.
The codebase will nevertheless be littered with various Tailwind overrides of slightly different colors and sizes like bg-[#FCF3DD]
and bg-[#FCF6E5]
, but rather than changing the config, keeping a list of them in a single file to copy-paste may be better during the development stages. Can always polish later.
I get why people make tests, but I’m not fully convinced the friction is worth it in practice, except in the case of migrating the codebase where you want to ensure it meets a certain spec. I’ve yet to fully understand what “test coverage” exactly means.
I’d say my way of doing it is similar to the idea of code duplication rather than pre-mature abstraction. Yet LLM-coding litters the codebase with suboptimal solutions, so don’t take my word as an opportunity to spam.
That’s not to say components should never be reused—I don’t think having hundreds of similar buttons in files make sense—just that the bar for doing it is much much higher than people realize.
Rather than creating design systems, design system components, spacer components, enforcing eslint rules, etc., what about just locating everything important in the files itself with Tailwind? Have a set of default components that anyone can easily override. What I realized about Tailwind is that it already is kind of a ‘design system.’ I felt like it didn’t make sense to have a component that would have an option just to change its flex from row to column.
How useful is design now?
People who can code and design well are becoming a significant force in software development. Some call this a “design engineer” role. As LLMs get better at writing other types of code, those who can design well have a significant advantage.
What is the point of maintaining a design system in Figma or publishing it online if something like Radix displays numerous reusable components while providing the code? I feel even Storybook is overcomplicating things when you could have just a really long page full of different components.
I think I’d rather have a Storybook website rather than maintaining some sort of parallel design system in Figma.
I think in general across American society, there is a stark absence of competence and quality. Many of the tools at universities are barely used. So I suspect designers will still be useful in the average company and in general, but at the top level they will need to upskill or die.
Levels of Frontend Explained
- Web Standards (W3C, WHATWG)
- Browser Implementations (Chromium, Firefox, Safari)
- see https://caniuse.com/
- use MDN for reference
- Browser-specific styles
- Browser CSS Reset, CSS Utilities (Tailwind)
- Base Components (Radix UI, Headless UI)
- UI Component Libraries (shadcn)
- Sometimes these two are combined (MUI)
- Frontend Framework (Svelte, React, Vue)
- Frontend App (SvelteKit, Next.js, Nuxt)
- Frontend Framework (Svelte, React, Vue)
- Base Components (Radix UI, Headless UI)
- Browser CSS Reset, CSS Utilities (Tailwind)
- Browser Implementations (Chromium, Firefox, Safari)
Tailwind usage
Tailwind is becoming its own CSS ecosystem in a way. Though some disagree with the class spam, semantic separation like this combined with an auto-formatter could be effective.
I do think separating JS and CSS/HTML is good, as the former is too unwieldly to be inline. I like Svelte’s <script/>
tags rather than putting the js inside the top of a function like in React and the formatting in a return statement. It’s a small detail, but enough to where Svelte is a significant winner and improvement. Vue has similar separation of concerns, but I wonder if the additional <template/>
is needed.
Put things like block
, inline
, flex
, grid
, in their own row. All the prose
elements go together. Margin and padding elements on their own line, sorted by order. There’s no plugin that sorts it on different lines rather than the same line.
<div
class="
prose prose-sm prose-lead:font-white prose-blockquote:not-italic prose-blockquote:text-sm prose-pre:text-xs font-serif [overflow-wrap:anywhere] hyphens-auto
lg:col-span-6
col-span-12
lg:col-start-3
mx-auto
[&>img:first-child]:mt-4
[&>h3:first-child]:mt-6
[&>p:first-child]:mt-6
prose-inline-code:before:content-none prose-inline-code:after:content-none prose-inline-code:bg-gray-200 prose-inline-code:border prose-inline-code:border-[rgb(220,214,195)] prose-inline-code:rounded-md prose-inline-code:px-1 prose-inline-code:py-0.5
"
>
Styling unknown components is hard. Copy-paste over npm install.
It is difficult to style components that are reused, as I often found out when using Next’s <Image/>
component due to them being nested multiple layers and opaque to someone using them for the first time. You don’t know how the components are composed.
You have to pass down props like Image.Container={styles}
. Similar situation when doing npm install
. There’s a lot of bloat around something that could just be a copy-paste single file.
There’s an RFC for Svelte that pointed out this specific situation. I’m imagining importing a component, then clicking into it and editing a specific version of that to do overrides rather than passing down a bunch of props. No idea how it would be implemented in practice though. You’d probably have a folder that lists all the variations of a component in the design system, and when you click on it you’re editing that specific component variant. It’d probably require too much editor tooling to be effective.
Basically, I’m saying a design system of repeatable components are important, but there should be an easy way to introduce flexibility into the system, and I haven’t quite figured that out yet.
Co-location helps discovery
I also think co-locating things is important, such as in a blog with images along with each post rather than a static folder. Yet the difficulty with this is that you can’t combine it with a CDN easily unless you wrote some custom script to parse the images from your co-located folder structure into a CDN. Otherwise, your markdown files would just reference online links, so you’re sort of managing two different parallel file structures.
Being as close as possible to the CSS would be the best, meaning you can change things easily just by going to the source of the problem. Changing one thing shouldn’t break other things. Isn’t that one of the purposes of tests? Inline CSS would be the best answer, but it’s too verbose and too unconstrained. You want some scaffolding. I still find myself overriding Tailwind and using arbitrary styles rather than the defaults, as I feel there isn’t enough detail. Sometimes Tailwind still runs into weird errors—it’s still a layer over the browser CSS specs.
Don’t over-constrain
Things like Prettier and ESLint sometimes just make me ‘feel off’. There’s this constraint and push. Yes, I know writing everything in barebones HTML + CSS is possible, but not totally viable. But I feel like as it is now, things are overconstrained. As a software developer, I want to keep pushing forward in a way where there are no tics and friction. And sometimes it’s that slight friction which makes writing software suck, but you also get tired of adding new features as well. Mentally speaking, you have to love the pain and friction of starting somthing new.
Intuitively, I understand why Typescript is important, but I haven’t personally run into the situations where it’s important.
At the moment, this is just a set of ideas and theories that will inform me should I make a large-scale coding project. It’s to be seen whether these techniques work at a large scale and can still deliver a polished product.