grid

How I made this blog

2024-02-12 ยท 8min read

Introduction

Welcome to my website!

It's been a while since I wanted to have a personal site on which to share open source projects I've been working on, but also to share some quick notes or even longer articles.

So building this site was an excellent opportunity to try out some new technology. That's what I'd like to discuss in this article. Be careful not to take everything too seriously, these are just some of the opinions I formed while building this very, very simple website. I haven't had the opportunity to test these technologies on more serious projects.

Choosing the stack

The goal was to try out a few new "hype" tools, to see what they were worth and form an opinion. The choice fell on :

  • Vike as a meta-framework.
  • Solid.js as front-end framework.
  • PandaCSS as the styling solution.
  • Bun. Really light usage : just for developpement and generating the site, as it is full SSG.
  • Biome to replace Eslint and prettier for linting and code formatting.

So a real Javascript hipster stack.

Vike

Why Vike ? Initially, I wanted to try out Astro. It was theoretically the ideal framework for my project. But I was soon confronted with a minor problem that immediately made me abandon Astro: the .astro custom file format.

This custom format implies the use of a custom LSP for having syntax support, autocompletion and so on. So I installed the Vscode Astro plugin for this purpose. And this custom LSP has a few quirks: in particular, IntelliSense wasn't showing in some cases. And that was immediately a no-go for me. It's probably a small bug in the LSP, but it's symptomatic of the underlying problem of custom LSPs and custom formats : they are not standards and therefore not natively supported, often not compatible with existing tools or requiring additional configuration.

So yeah, I'm getting more and more frustrated by that, and that's also what's prompting me to abandon Vue.JS for JSX: more on that later.

So after that, I looked for another framework, and the choice fell on Vike. Vike is a meta-framework similar to Next.JS / Nuxt, obviously much less feature-complete, but with the advantage of supporting several front-end frameworks, including Solid.JS. SolidStart was in early-early beta at the time of writing, so I opted for Vike instead.

There's also Vinxi, which seems to be a newly emerging alternative to Vike. It also seems to be very early for the moment, but gonna keep a close eye on it.

Vike is pretty cool, flexible, can do SSG, SSR, full client side, supports several front-end frameworks ... A lot of cool stuff.

Things I didn't like :

  • File-system routing is currently mandatory. I would have liked classic routing. Having files named +config.h.ts or posts/@slug/+onBeforePrerenderStart.ts isn't really my thing. File-system routing has its advantages, but also a lot of disadvantages as soon as the project grows, in my opinion. That said, it's only a matter of time before it's optional, see this issue that talks about it.

  • The framework is still, it seems to me, fairly early. A V1 is in in the works. As a result: breaking changes are fairly recurrent, some features are missing, and many many parts of the framework are moving.

  • Related to the previous point: the documentation is generally pretty poor. You shouldn't be afraid to dig straight into the examples and into the Vike source code to understand how certain things work. But that's part of the game with early technologies. All this will improve with time

  • The API, which sometimes seems rather obscure and low-level. But it's all by-design: Vike's philosophy is to "Build your own framework", giving developers a a total freedom. It's a very good choice, but in my use-case: a simple blog, I'd have preferred a more high-level API and a thing that "just works" like Astro. But I am pretty sure we'll see more advanced and specialized starter-kit/framework, based on Vike, in the future.

    For example, I had to manually setup the MDX/Blog part of the site, whereas with Astro, it works right out of the box, and with far more functionality than what I built myself with Vike. So yes, I had to reinvent the wheel a bit. But again, it's by-design. Can't wait to see future Vike-based starters and frameworks developed by the community.

What I liked:

  • The agnosticism of the front-end framework. You can use Solid.JS, React, Vue ... It's very, very cool. An application will be built in the same way, regardless of the front-end framework, and I like the idea of a standardized format. Where a Nuxt and Next.JS project may have very different concepts and APIs, forcing the developer to learn two different frameworks.
  • Flexibility: total control of SSR, renderer, SSG, SPA, routing client side, server side, both... Everything is possible, and quite simply.
  • Can easily plug your own HTTP server. Vike can be plugged directly into Adonis, for example. This would enable us to use all Adonis features in our backend/vike logic. So, theoretically, we could have a meta-framework backed by Adonis.
  • Powered by Vite.

Solid.JS

This section should be taken with a grain of salt. I'm not at all familiar with the React ecosystem, and I'm not at all up to date on the subject.

For the record, I've pretty much been doing Vue.JS all my life and have avoided JSX-based frameworks ( especially React. ). Recently, I've become increasingly frustrated by the Vue format ( SFC, and its LSP ) which can sometimes be limiting:

A custom file format such as Vue SFC means that all tooling must be customized for this format:

  • LSP. Volar. Volar is great, the maintainer is a genius, but day by day I'm getting more and more frustrated by its use. I often find it slow and buggy.
  • Typescript : some features are not supported by SFCs.
  • The tools for linting, formatting, typechecking... All this needs to be customized for SFC. You need a custom parser for ESLint, use vue-tsc, activate Volar....'s Take over mode. All this works pretty well, but it's generally a bit complicated to configure and above all: slow. Where with JSX, everything is native, no custom tooling to configure.

For me, going back to React was a no-go. Although the React ecosystem is fantastic, I find React's reactivity system and paradigm (re-rendering) much less intuitive than Vue's (and signals in general). Consequently, I decided to try Solid.JS, which seems to be the best of both worlds: signals and JSX.

I know we can do JSX with Vue, but the support is pretty poor, and mostly nobody does it. I challenge you to using JSX with Vue in your company: everyone will want to kill you.

A second, perhaps very stupid/nitpicky trick that has always put me off JSX and React is the fact that you have to use ternary conditions or make .map in the middle of an HTML template to render conditionally or display lists. Where as with Vue, a simple v-for or v-if will be enough.

I've always preferred the second, more legible solution. It's probably also a matter of habit, as you get used to the first method very quickly. But in any case, it put me off. And Solid.JS has a built-in solution for this: the <For> and <Show> components, for example, which allow you to do this in a sexier way:

export function ReactHome() {
	return (
		<>
			{user ? (
				<>
					<h1>Welcome {user.name}</h1>
					{user.posts.map((post) => (
						<div>{post.title}</div>
					))}
				</>
			) : (
				<h1>Welcome Guest</h1>
			)}
		</>
	)
}
export function SolidHome() {
	return (
		<Show when={user} fallback={<h1>Welcome Guest</h1>}>
			<h1>Welcome {user.name}</h1>
			<For each={user.posts}>
				{(post) => <div>{post.title}</div>}
			</For>
		</Show>
	)
}

For me and my little brain, the Solid version is much easier to read. Note this is a syntax that could be used in React, but after some research it looks like it's not super recommended.

In the end, I had very few unpleasant surprises with Solid: I quickly found my feet. The paradigm is very similar to Vue, many things are similar. For me, Solid combines the best aspects of Vue and React, without the drawbacks of the latter two. Signals + Best Typescript Support + JSX.

PandaCSS

I've been a long-time user of TailwindCSS. More precisely, of UnoCSS, which is a steroidal alternative to Tailwind. I've rarely had any problems with it, so I've never been too keen to try out an alternative. But after seeing a lot of good words about the Panda, I decided to play around with it a bit. PandaCSS is a CSS-in-JS library with a very light runtime, as everything is generated at build time using static analysis. The library places great emphasis on the following points:

  • Type safety. Everything is type safe, whereas with Tailwind nothing is.
  • Very light runtime. Everything is generated at build time, unlike conventional CSS-in-JS libraries.
  • Full featured: PandaCSS offers a lot of features.

Typesafe CSS ? Why ?

There's something to be said for that. I've long been (and still am, I think) a bit dubious about CSS typesafety. When I develop an application with Uno/Tailwind, here's my flow:

  • I modify a component, I add a "bg-rad-400" ( oops, I made a typo not catched by my IDE )
  • Constantly, I assume you do the same, I check my browser to see how my component looks now that I've changed my color.
  • As a result, I notice that my component isn't red. So I notice and immediately correct my typography.

I don't suppose I'll surprise anyone by saying that I'm constantly checking my browser to see how my component looks after a styling change. That's where I was a bit dubious about the typesafety aspect of CSS. Not sure of the real benefit of it.

That said, it's not the only advantage of having CSS-in-TS and therefore being backed by your LSP. There's also autocompletion, which can come in very handy when you have a lot of custom tokens. Refactoring is also safer when you change the name of a token.

The biggest value I see is in being able to use the types in our design system to interface our props. Code is better than words:

// Instead of manually typing our props
type ButtonProps = {
	color: 'primary' | 'secondary' | 'danger'
	size: 'sm' | 'md' | 'lg'
}

// We can use the types autogenerated by Panda according to our configuration
import { ColorToken } from "styled-system/tokens";
type ButtonProps = {
	color: keyof ColorToken
}

It's a silly example and not necessarily relevant in the case of Panda, there are better ways of doing this.

Another fear I have with this is the speed of LSP and typechecking. I've worked on projects with a lot of typescript, and sometimes we'd have to wait 2/3 seconds for Intellisense to suggest stuff. That's why I've always thought that typesafety is cool, but, let's not abuse it. Otherwise, in 2 months' time, we'll end up with a super-slow auto-completion.

To conclude, I'd say that typesafety adds an extra check to build time, which is cool but yeah, still a bit dubious about the real value of it.

Opinions on PandaCSS

Starting with the negative, here are the points I didn't like:

  • The learning curve. I've been working with PandaCSS for a few days now, and I'm still nowhere near as productive and fast as I was with UnoCSS. This learning curve is probably much lower for those used to working with CSS-in-JS libraries, but that wasn't my case: as far as I know, it doesn't exist in Vue (except for pinceau, which is fairly recent. ).

  • The API surface. PandaCSS offers a lot of cool features. But one drawback for me is that you sometimes end up with 5 different ways of doing the same thing. And in my opinion, the documentation isn't clear enough about the fundamental differences and the criteria to be taken into account when choosing one or the other.

  • Static analysis. This is both a positive and a negative point. Okay, it allows us to have almost 0 runtime, but sometimes we find ourselves in situations where such and such code doesn't work because static analysis isn't able to understand it. And so we end up going to the Panda playground to make a minimalist reproduction of our problem and try to understand why it doesn't work, and then look for an alternative that does. I suppose it's something you get used to as you use it: you better understand the limitations of static analysis and know which pattern to use to avoid those limitations. But it cost me a few hours on this project.

    That said, static analysis is sometimes bluffing, and you sometimes forget that it's static, as Alex raises it here. Note that there's also an escape-hatch with the staticCss option to generate styles even if not found by static analysis.

  • The feeling of over-engineering. Here I've just built a simple, dumb blog. And I feel like I've used a totally overkill solution for my needs. In fact, I'm wondering whether PandaCSS is the right tool for small-scale projects. Or if it's a tool that should only be used when you have complex design systems. Maybe it has something to do with the learning curve and the time I've spent assimilating certain concepts from the library, but I still have this thought: do I really need all this for simple sites? Wouldn't I be 100x faster and more productive with Uno/Tailwind? And as I was saying above: on a day-to-day basis, I very rarely encounter critical problems with UnoCSS, so is it relevant to "encumber" myself with Panda, a more powerful but also more complex tool?

Now to the positives:

Overall, it was a cool experience. The tool is very powerful compared to a simple library of utility classes. Lots of cool discoveries for me, as I've never used a CSS-in-JS library before.

  • Recipes. With UnoCSS, when I need to re-use a styling set, then I make a new Vue/React Component. Under Panda, if the same component doesn't have its own logic, you can just define a recipe, which is much simpler and faster.

  • Patterns and JSX. Panda ships JSX components out-of-the-box. So we have components like <Flex> <Center> <HStack> <VStack> <Grid> that we can use directly. We can pass our css properties directly to these components. It's pretty cool and makes the code elegant.

import { Box, Stack, Circle } from './styled-system/jsx'

function App() {
	return (
		<Stack direction="row" p="4" rounded="md" shadow="lg" bg="white">
			<Circle size="5rem" overflow="hidden">
				<img src="https://via.placeholder.com/150" alt="avatar" />
			</Circle>
			<Box mt="4" fontSize="xl" fontWeight="semibold">
				John Doe
			</Box>
			<Box mt="2" fontSize="sm" color="gray.600">
				[email protected]
			</Box>
		</Stack>
	)
}
  • Full-featured. I'm not going to list all Panda's features. But there are many: Semantic tokens, variants, cva, slot recipes, responsive design... Lots of good stuff that I won't be able to detail here.

So yeah, to conclude, PandaCSS is a very good tool, very well thought out, a bit like the advent of CSS-in-JS. I'm going to keep using it to see if I regain my productivity and if this impression of over-engineering finally disappears with time and habit.

Bun

Not much to say about Bun, except that I haven't encountered any particular problems. The package manager is fast (faster than pnpm?). That said, I haven't encountered any significant difference in speed between with Node and Bun while developping. So yeah, not used enough to give a relevant opinion. At least I didn't run into any problems!

Also note that I don't use Bun in production, as the site is full SSG ( so rendered at build time ).

EDIT: I finally ditched Bun because I encountered a bug during the build with Vite. And no bug with Node. On top of that, Cloudflare doesn't support bun. So there's no point to use Bun on this project.

Biome

I'm a fan of Eslint. I've spent a lot of time setting up Eslint custom presets, especially for the teams I have to work with, because I hate making nitpicking comments on PRs. I'd rather have everything automated than waste time making this kind of comment.

However, ESLint can sometimes be very slow on large codebases. I'm not a big fan of speed for speed's sake, but sometimes yeah, it's a bit annoying.

So, out of curiosity, I wanted to try Biome, which is a tool designed to replace Eslint and Prettier for linting and formatting code. It's pretty cool, it's really super fast (~15x faster than Eslint ), configuration is super simple ( no dozens of plugins to install ).

But then comes the biggest drawback for me: interoperability with the ESLint ecosystem. For example, I love eslint-plugin-perfectionnist for sorting my imports, or eslint-plugin-unicorn for certain code quality rules, and typescript-eslint for linting typescript. Well, these plugins aren't compatible with Biome and they don't have an alternative. For me, this is Biome's biggest problem: you lose a lot of interest if you have 90% of the rules you used to use that are no longer available.

Also, Biome formatter works great and is super fast: at the time of writing, Biome's formatter only works with JS/TS and Jsonc. HTML, CSS, Vue... are not yet supported, but are planned. So kind of not usable in another project yet for me.

In conclusion, this is a promising tool, very fast and one to keep a close eye on. The team plans to improve on the points I raised: support new languages for the formatter, add a system enabling types linting, a plugin system that will enable the community to port ESLint rules.

That said, porting the ESLint ecosystem to Biome and re-developing an alternative to typescript-eslint is, it seems to me, a huge work. So stay tuned!

There's also oxc, another tool that aims to replace ESLint. ( In fact, OXC is more than a "replacement for eslint". But let's keep that aside ). I haven't had a chance to try it, but it also looks promising. The tool has ported quite a few of ESLint's popular plugin rules

Conclusion

In the end, after a bit of trouble configuring the whole thing, the experience was relatively pleasant. However, all those configuration and gluing problems confirmed once again why I love AdonisJS. It was a good reminder. But having said that, there are a few tools I'll continue to use in the future, and I'm looking forward to seeing how they evolve