Hacker News new | past | comments | ask | show | jobs | submit login
CSS written in pure Go (github.com/accentdesign)
107 points by andrewfromx 8 months ago | hide | past | favorite | 64 comments



As a front end dev, this is great. Now I can get back to my true job responsibility, which is writing HTML files in pure Haskell.


Not a bad idea actually. I had used the lucid library to do just that. It was intuitive and easy to read.


Sample syntax:

    table_ (tr_ (td_ (p_ "Hello, World!")))
More complex:

    table_ [rows_ "2"]
           (tr_ (do td_ [class_ "top",colspan_ "2",style_ "color:red"]
                        (p_ "Hello, attributes!")
                    td_ "yay!"))

Source: https://hackage.haskell.org/package/lucid


Yeah Haskell is far from the worst langage to write a tree of elements in.

That is essentially what Elm proposes and it works fine.


HTML isn't a "tree of elements" though; it's about content token sequences described by regular languages where the symbols can also represent nested markup. The regular content model stuff is what drives SGML/HTML tag omission/inference and is a major feature of markup languages as a text format compared to primitive co-inductive expression syntax a la s-expr and co.


You can already write your Javascript in Scala so the game is afoot.


I just shot beer through my nose LOL-ing when I read this.


Also see a full web rendering engine (modern HTML + CSS + the whole layout engine) made in pure Python, that can export to PDFs: https://github.com/Kozea/WeasyPrint

They even support a lot of properties that aren't in Gecko/WebKit/Blink, like the @page rules.


Unfortunately all the attempt to build a web rendering engine can only end up in a barely usable renderer full of not properly supported edge cases.

Even the historic browser vendors with lots of manpower struggle with implementing it and they don't even agree on the implementation details.


My main gripe with CSS is that rules are selectors only. Classes can't compose or inherit (or, if they can, I don't know about it and would welcome a correction).

For example, let's say I use a third-party css library (materializecss, or similar) that provides a class `btn-warning` and `warning-text`, which sets say 10 fields (colors, elevation, etc) on the former and `text-transform` on the latter.

I just want to write one new class that inherits fields from both the existing classes.

I can't specify a new class `my-btn-warning` that inherits all the fields from `btn-warning` and `warning-text`.

I can't create a new class `my-btn-warning` that is composed of `btn-warning` and `warning-text`.

My only option (open to correction) is to copy-and-paste the existing `btn-warning` and `warning-text` fields into my new `my-btn-warning` class.

There's simply no reusability here.

If I want reusability, I need to go outside the spec i.e. have a build step. Not just any build step, but one that exactly matches the upstream library. Which is impossible if there are two upstream libraries, or if my existing build step is different.

This project looks nice but it combines all the disadvantages of writing CSS with all the disadvantages of a build step and adds the extra downside of not being compatible with anyone else's (including mine) build step.

To me, this is the worst of all worlds, with no redeeming advantage at all.


It sounds like you're blaming CSS for the shortcomings of the library you're using.

CSS cascades, embrace it rather than fighting it.

Have a .btn rule that styles all your buttons. Have a .warning one that makes it yellow. Then instead of `<button class="btn-warning">` use `<button class="btn warning">`.

Or just use Sass instead!


It is better to use BEM naming, so the example becomes

    <button class="btn btn--warning">
Here double minus means that "warning" is a state of an element named "btn".


> It sounds like you're blaming CSS for the shortcomings of the library you're using.

...

> Or just use Sass instead!

Maybe I was unclear: my gripe is that this functionality is such a necessary thing to have, that external non-spec third-party tools have to be used to get this functionality.

It is a shortcoming of CSS, because CSS is not providing this functionality that users want.

The fact that everyone is using a third party tool to get this functionality only reinforces my point: if everyone is doing it, then it's a shortcoming.

> Have a .btn rule that styles all your buttons. Have a .warning one that makes it yellow. Then instead of `<button class="btn-warning">` use `<button class="btn warning">`.

I do this, but it ends up being `class="<godawful number of named classes?"` and is prone to breakage when you need to update or modify a theme.


Skill issue


Right. How different would it be from just writing inline CSS, exactly?


You're composing in the wrong abstration layer, you just use the both classes and if you need any extra stuff you want, then you just create a selector for `.btn-warning.warning-text.my-btn` and as the selector is more specific than the classes that is uses then you can override the functionality it inherits.


Same here, bothers me for decades now,

I want to define a button that inherits from a library like tailwind (define button by using tailwind classes).


Correct.

You can look at CSS Modules (not supported by browser) which has class inheritance.


*CSS generator

(Not a parser, a renderer, a query engine, etc)


After dealing with the current state of affairs regarding the front end, I think this is pretty interesting. I've been writing Go web apps with templ and htmx. I've punted on dealing with CSS using a cdn and bulma. I did get Tailwind and friends working, but it required a lot of work being outside a React/JS framework like next.js. It also felt really weird using npm to install CSS.

The nice thing about this is that you can get a tailwind Go lib to programmatically build into your binary. There are no extra files, one build step, and one binary output. After trying out Fly and Vercel, I went to running things with docker on a VM, and a single binary in a container makes things much simpler IMO.

This looks pretty cool and I look forward to seeing folks do interesting things with it.


I do this too. Using a hot reload server and having live reload in the browser as I'm changing tailwind classes, go files or templ files is a very efficient workflow.

Then for the actual build, everything is built and embedded in a single binary by conditionally using embed with build tags.

I think that live feedback development cycle is probably the most satisfying way to code something by oneself.

It did need quite a bit of messing around with tooling and my Makefiles are pretty big but at least I can reuse that in every new project.


Could you go into more details about the live reload (I use air + Echo), what hot reload server do you use for templ?

And what about "embed with build tags."? I embed every build now.


I use the "live reload with other tools" as it is described in the templ docs [0].

Basically, templ will start a live reload server in "proxy mode". Other tools can then request a reload by notifying the proxy. I started with the makefile described in the docs. But I also have tailwind in watch mode, esbuild builds and more.

[0]: https://templ.guide/commands-and-tools/live-reload-with-othe...


Same here, Go + HTMX + templ. And I'm also need npx for tailwind.

So I would like to try something like this too. Integration with templ for local CSS components would be nice.

Also agree to the single binary, wrote here https://www.inkmi.com/blog/simplicity-of-golang-systemd-depl... (with systemd instead of a container)


Is CSS not Turing-complete? Flip it around! Go written in pure CSS would be impressive. Any takers?


CSS is absolutely not. One can argue about the merits of CSS3 + HTML (+ human input) _together_ being so, but that’s something else entirely.


We've been writing CSS on the backend for a while now:

https://dev.to/thormeier/dont-try-this-at-home-css-as-the-ba...


I use the standard library html/template package for generating CSS dynamically, it's got pretty good CSS support. I've found it useful for things like custom themes and UI preferences.


So, good old CSS wasn’t pure enough!

Anyways, good exercise for your experience. Have fun.


This is cool, for binaries that provide a web interface, for admin purposes, maybe a blog, or whatever, keeping it simple with some styling to make it a little more swish, this is all I'd be looking for. I'll give it go for sure.


Not sure if this is satire but Go's "embed" package was specifically made for this...


I’m just curious why you want to create a type of programming language that does the things it is supposed not to do, such as replacing JavaScript with Python on the front end or running an ML project in a browser with pure JavaScript...


To be fair, the project sounds completely pointless.

Why is why I absolutely love it.

It's the true hacker's spirit - about testing the frontiers of what's possible. Note what was said as well:

> This is really just a bit of fun and a way to write CSS in Go. I wanted to see if it was possible and it is with ease.

And I thought this was aspirational as well:

> CSS written in Pure Go. No JS builders, no preprocessors, no linters, no frameworks, no classes, no variables, no overrides, no plugins, no dependencies, no javascript, no templates, no bs, no nothing. Just Go.

Rust people are all about "let's rewrite it in Rust". I don't see why Gophers can't have their fun as well.


It's the hacker spirit


>I don't need linters when I have Go's static typing.

Indeed! When there's effort to make proofs or assertions about a program's behaviour before it is executed then good things happen!


Errr cool I guess? Definitely triggers my 'possibly overengineered' intuition, but hey, even if it is, at least it's not gonna waste cycles on the user's browser.

Dev experience does look pretty good.

To be fair, i have been quite happy with templ's style support, which would not really be useable in a non-templ project.


I write a kind of html-and-css-in-go all the time with Templ. It's nice.


I took this a step further and just wrote the pure css in one go.


Computer scientists were so preoccupied with whether or not they could, that they didn't stop to think if they should


A bit verbose, but I can see it working if media queries are added. Why one would chose this over vanilla CSS I don't quite know but there is worse out there.


"I wanted to find a way to easily control the CSS from the server side and not have to worry about pre-building the css to take variables and stuff. I didnt want to use UI libraries that are written for JS frameworks and I didn't want to use preprocessors or linters that add more steps to the build process.

The benefit of all this are

Keeps the css free of variables.

Keeps html free of classes like bg-gray-50 text-black dark:bg-slate-800 dark:text-white and eliminates the need to remember to add the dark variant.

I recently saw a button component on an html page 10 times with over 1800 characters in the class attribute of each!"


Why is keeping the css free of variables a benefit?


I am also yet to ever find a reasonable explanation for why adding HTML classes a la tailwind is ever bad for the end user. I can see the argument for the DX suffering cause you have a ton of classes in one long line and that can make things a bit unreadable but why does the user or the browser care how many classes there are? The browser has optimised workflows to bunch up all the styles in all those classes to render the elements and all more classes would mean is a few extra bytes to download


You've literally answered you own question - DX. To some, developer experience and the ergonomics of utility is more important. Others aren't of that mindset which is equally okay. I'm guessing you're of the latter camp in this case.


* at least FF dev tools get unusable with enough variables (and in general, it's not a fun experience to debug which one out of a thousand others is wrong)

* efficiency (one less hash lookup, less bookkeeping for the browser)

* works on older browsers (or mine ;)

* you become a member of the exclusive club of devs that don't believe in turning CSS into a turing-complete language (old fashioned, I know)


Yeah CSS variables are great, and enable a bunch of super dynamic stuff without needing any JS!


> I recently saw a button component on an html page 10 times with over 1800 characters in the class attribute of each!"

In my head you went full bene gesserit and screamed "abomination" when you saw it.

Candidly I want to know where this thing was, you really need to name and shame.


+10! I was just going to try hand writing a css for a go web-app. Doing in in go is better. Thanks!


I complimented the OP and code. Downvoted? What's the beef here?


I’m not one of your downvoters, but the critical HN reader in me says that your comment isn’t substantive and doesn’t move the conversation forward.

To put it one way, you’ve phrased your compliment and thoughts in such a way that they only relate to you and don’t have any bearing on the reader.


Fine, I buy that. So see

https://news.ycombinator.com/item?id=40546415

I went after that harder.


>I wanted to see if it was possible and it is with ease. I wanted to find a way to easily control the CSS from the server side

Well, this would be possible in any language, I presume. Maybe it's easier in Go but is that the point?


> no bs

> Just Go

So BS?


The project looks like it’s done for fun. From an architecture standpoint - the server is something you pay for, while logic and design outsourced to the browser is handled by the client’s compute and thus fun is free to you (unless your clients are on underpowered devices).Take advantage of free. See if you can move more stuff to the client instead :)


Correct me if I'm wrong but..

It seems like the point of this is to run during a compilation step with the added capability of rebuilding a CSS response on the fly, if you wanted.

Building a CSS during compile is no different than using something like Tailwind and is essentially free.


Unsure if you're trolling: your attitude is basically big cashed up corporates squatting on their users' devices, because their devs are too lazy, unempathetic, incompetent, or all three, to pick a better solution.

This shit is why basic webpages drain my battery and burn my lap.


No, the attitude is that CSS is for decoration to meet the User’s needs (colors, dark or light mode, font sizes and adjustments for accessibility, etc). So moving it to backend is a bad idea because you remove the users ability to customize or at worst make your entire service delayed both for all users and for the user who needs the customization by doing it on the server. (now I understand the tool featured here is for precompile, but this is the argument i was making before I knew that).

The whole industry of React and similar giant frameworks is to offload functionality to the browser and make the server only a data source - you reduce the amount of data that has to go through tight pipes and speed up the responsiveness of the site for your users. Basic pages drain your battery for sure. There is a gradient of that and putting CSS in the front end is the wise minimal end of that gradient, while React is the opposite side of it, then add SQLite and you go even more client heavy. It depends on the tool you are building - some justify it, but many don’t.


Please note that using slash at the end of the tag is not supported in HTML:

    <link rel="stylesheet" href="base.css" />
I often see such code and it seems that people simply don't know HTML and copy random garbage they found on the Internet. In the example above, the slash is simply ignored, but in this example the code will be parsed not as intended:

    <div/>
This is why you should read proper documentation or specification instead of blindly copying garbage code from Internet.

MDN link: https://developer.mozilla.org/en-US/docs/Glossary/Void_eleme...


> Please note that using slash at the end of the tag is not supported in HTML: `<link rel="stylesheet" href="base.css" />`

It is supported in HTML5.

https://html.spec.whatwg.org/multipage/syntax.html#start-tag...

> ... if the element is one of the void elements ... then there may be a single U+002F SOLIDUS character (/) (at the end of the element, before the closing bracket)

https://html.spec.whatwg.org/multipage/syntax.html#void-elem...

> Void elements: area, base, br, col, embed, hr, img, input, link, meta, source, track, wbr

--

> This is why you should read proper documentation or specification instead of blindly copying garbage code from Internet.

Physician, heal thyself :)


No, try writing

    <div/>
And it will be interpreted as <div>, not as a pair of <div></div>.

HTML just ignores the slash character, it doesn't support XML-style self-closing tags. HTML is not XML.


We're not talking about div, we're talking about link.

Self-closing tags with the trailing slash are absolutely supported by HTML5, in some circumstances.


No, spec authors simply added a rule to ignore the slash because Internet was full of garbage code and they realized that it is hopeless to expect low-quality frontend developers to fix their code. There is no such thing as "self-closing element" in HTML spec (only SVG and MathML have them because they are based on XML).

Quoting the spec [1]:

> Then, if the element is one of the void elements, or if the element is a foreign element, then there may be a single U+002F SOLIDUS character (/), which on foreign elements marks the start tag as self-closing. On void elements, it does not mark the start tag as self-closing but instead is unnecessary and has no effect of any kind. For such void elements, it should be used only with caution — especially since, if directly preceded by an unquoted attribute value, it becomes part of the attribute value rather than being discarded by the parser.

[1] https://html.spec.whatwg.org/#start-tags


Yes, but that clearly indicates it's a supported syntax, which contradicts what you said up-thread. That's the only point I was making.


wow codedokode for the win!




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: