diff options
| author | Xe Iaso <me@christine.website> | 2022-11-06 15:15:53 -0500 |
|---|---|---|
| committer | Xe Iaso <me@christine.website> | 2022-11-06 15:20:25 -0500 |
| commit | 7a8123435b381db82b557d630bdae5df77b86608 (patch) | |
| tree | 44b2197c850a24921a0d508661359716e440cf2e | |
| parent | 434747f4bc342ddee75aaf0cc3a39a6543474e5f (diff) | |
| download | xesite-7a8123435b381db82b557d630bdae5df77b86608.tar.xz xesite-7a8123435b381db82b557d630bdae5df77b86608.zip | |
Xess 2.0: HSL
Signed-off-by: Xe Iaso <me@christine.website>
10 files changed, 457 insertions, 12 deletions
diff --git a/blog/xess-css-variables.markdown b/blog/xess-css-variables.markdown new file mode 100644 index 0000000..db831dd --- /dev/null +++ b/blog/xess-css-variables.markdown @@ -0,0 +1,202 @@ +--- +title: "Xess 2: CSS variable edition" +date: 2022-11-06 +tags: + - css + - frontend + - nix + - noxp +--- + +<xeblog-hero ai="Waifu Diffusion v1.3 (float16)" file="aoi-chan" prompt="1girl, fox ears, blue hair, blue eyes, paintbrush, canvas, easel, chibi, hoodie, smile, solo, very colorful, heart, pupils"></xeblog-hero> + +As a hacker with too many side projects, I like to have a certain _look_ to my +websites that makes it instantly identifiable. I have a very brutalist approach +to web design that makes it _very easy_ to get things off the ground and get +hacking. + +One of my longer-standing projects is a CSS framework called +[Xess](https://github.com/Xe/Xess). Xess is my go-to CSS file when I just need +to throw some words on a page. I've used it for at least the following projects: + +- [When Then Zen](https://when-then-zen.christine.website) +- [waifud](https://github.com/Xe/waifud)'s admin panel +- [hlang](https://h.christine.website) +- [Printer Facts](https://printerfacts.cetacean.club/) + +And other internal projects at jobs that I can't talk about due to NDA +restrictions. The really big thing that it does is lets you use _normal semantic +HTML_ and then just tries to make _that_ look pretty for you. It's a classless +framework (you don't need to use CSS classes to make it work) and I love it so +much. + +However, after using it for a while, it's started to get very bland and +repetitive. Everything looks _the same_. This is getting boring to me. I've been +considering various ways to fix this, but I recently had a golden moment of +inspiration when I saw one of my favorite Fediverse bots come across my feed: + +<xeblog-toot url="https://botsin.space/@randomColorContrasts/109212579424662451"></xeblog-toot> + +I've been following +[@randomColorContrasts@botsin.space](https://botsin.space/@randomColorContrasts) +for years on the Fediverse. Twice a day it generates two colors with good +contrast and posts an example image with them in it. This gets you results that +look like this: + +<div style="padding=1rem;color:#F1DD15;background:#0E102E;"><h2>Example +thing</h2><p>I'm baby umami truffaut beard hashtag squid mixtape tilde photo +booth etsy drinking vinegar humblebrag intelligentsia. Squid shabby chic +pinterest yuccie. Lomo organic pork belly man bun chillwave. Mlkshk coloring +book chia, kinfolk shoreditch pabst edison bulb marfa salvia vibecession fit +tumblr stumptown heirloom mixtape. Ugh yes plz shabby chic ennui pinterest +drinking vinegar tbh truffaut. Church-key big mood distillery trust fund +asymmetrical cray cliche. Tonx typewriter poutine before they sold out try-hard +umami fashion axe post-ironic JOMO normcore gochujang man bun glossier +butcher.</p></div> + +This looks pretty great as-is, but Xess has more than just text being styled. +Xess also styles links, blockquotes, and code blocks. I really wanted those +colors to be derived on the fly and then I stumbled across [HSL calculations on +the fly with CSS +variables](https://elad.medium.com/why-css-hsl-colors-are-better-83b1e0b6eead). +This piqued my interest. I could pick three basic hues and use those to +dynamically generated everything else. I did some hacking and now I am happy to +announce Xess 2.0. + +## Xess 2.0 + +Here are some screenshots of one of the themes I created for this: cherry + +<xeblog-toot url="https://pony.social/@cadey/109298544735230581"></xeblog-toot> + +My favorite part about all of this is how easy it is to customize a Xess theme. +You only need to change _three_ variables to recolor the page: one for the +background color, one for the text color, and one for the "accent" color (used +for selection, blockquotes and links). + +Don't believe me? Here's the [theme +file](https://github.com/Xe/Xess/blob/master/custom/cherry.css) for the cherry +I showed off earlier: + +```css +:root { + --background-color: 0; + --text-color: 43; + --accent-color: 344; + + --width: 80ch; + --padding: 0; +} +``` + +That's it. Just three colors, the max-width of the page and how much padding you +want around the main element. From here these can be infinitely customized to +your heart's content. + +Here are some other themes I've cooked up: + +<xeblog-toot url="https://pony.social/@cadey/109298533570134274"></xeblog-toot> + +<xeblog-toot url="https://pony.social/@cadey/109298538717562125"></xeblog-toot> + +I could easily see this being used with some kind of dynamic generation of theme +colors based on user settings or even dynamically generated at a per-user level. + +## Customization with Nix flakes + +I have a slight reputation as being a NixOS user. One of the biggest things that +I like to do with Xess is consume it from [Nix +flakes](https://nixos.wiki/wiki/Flakes) so that the CSS will automatically be +squashed into one tiny file. I have made a Nix function that generates a package +with the CSS customizations into one big file so that I can solve two problems +at once: + +* Making per-project customizations of Xess without having to build things + manually or edit Xess itself +* Make that a Nix function so that I don't have to think about it too much at + the build stage + +If you want to use a customized version of Xess in your Nix build, here's what +you need to do: + +First, import Xess into your flake and add its outputs to your flake outputs: + +```nix +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + utils.url = "github:numtide/flake-utils"; + xess.url = "github:Xe/Xess"; + }; + + outputs = { self, nixpkgs, utils, xess }@inputs: + utils.lib.eachDefaultSystem (system: + let pkgs = nixpkgs.legacyPackages.${system}; + in { + # ... + }); +} +``` + +Make your theme somewhere in your repo. I'm going to assume it's at +`./css/theme.css`. Then you can build a custom version of Xess with this: + +```nix +in { + packages = { + xess = Xess.packages.${system}.customized ./css/theme.css; + }; + + # ... +} +``` + +You can test this with `nix build`: + +```shell +$ nix build .#xess +``` + +And then view it in your browser with `python -m http.server`: + +```shell +$ python -m http.server +``` + +Add `/result/sample.html` to the end of the URL that python generates for you, +and then you can view your CSS changes in all their glory. The CSS file that +Xess generates will be in `$out/static/css/xess.css`. You can use this with +something like `pkgs.symlinkJoin` to make sure things percolate out to the right +place: + +```nix +in { + packages = rec { + xess = Xess.packages.${system}.customized ./css/theme.css; + bin = doSomeBuild; + + # the default output + default = pkgs.symlinkJoin { + name = "myProject-${bin.version}"; + paths = [ bin xess ]; + }; + }; + + # ... +} +``` + +And then as long as your website expects to pull things from +`/static/css/xess.css`, everything will just work! Nix will take your +customizations, splice them into Xess and then emit a composite CSS file with +everything in it. + +<xeblog-conv name="Mara" mood="happy">Also, if you're an existing user of Xess +via Nix, this will _not_ break your builds. When you use the default package in +the Xess flake, it will build the "classic" version of Xess without any of the +HSL customizations.</xeblog-conv> + +That's it! I'm really glad that I'm bringing Xess into the future and making it +more extensible for the next few years of effort. I'm excited to hear what you +can do with Xess. Be sure to let me know what fun you get up to on +[Mastodon](https://pony.social/@cadey)! diff --git a/data/toots/159a9073785dca2f6a140d46f814589d8202c513d61c14a2f92c7487c3ada3a3.json b/data/toots/159a9073785dca2f6a140d46f814589d8202c513d61c14a2f92c7487c3ada3a3.json new file mode 100644 index 0000000..b0479b9 --- /dev/null +++ b/data/toots/159a9073785dca2f6a140d46f814589d8202c513d61c14a2f92c7487c3ada3a3.json @@ -0,0 +1,54 @@ +{ + "id": "https://pony.social/users/cadey/statuses/109298544735230581", + "type": "Note", + "inReplyTo": "https://pony.social/users/cadey/statuses/109298538717562125", + "published": "2022-11-06T19:37:18Z", + "url": "https://pony.social/@cadey/109298544735230581", + "attributedTo": "https://pony.social/users/cadey", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://pony.social/users/cadey/followers" + ], + "sensitive": false, + "atomUri": "https://pony.social/users/cadey/statuses/109298544735230581", + "inReplyToAtomUri": "https://pony.social/users/cadey/statuses/109298538717562125", + "conversation": "tag:pony.social,2022-11-06:objectId=5855448:objectType=Conversation", + "summary": null, + "content": "<p>cherry, a theme with lots of sweet reds</p>", + "contentMap": { + "en": "<p>cherry, a theme with lots of sweet reds</p>" + }, + "attachment": [ + { + "type": "Document", + "mediaType": "image/png", + "url": "https://cdn.pony.social/file/tscs37-pony-social/media_attachments/files/109/298/540/983/689/743/original/f3eedf70037145f6.png", + "name": null, + "blurhash": "U47Jtu?Ht7W:9ttQt7n*}[ozbHj[n+xaofR*", + "width": 735, + "height": 517 + }, + { + "type": "Document", + "mediaType": "image/png", + "url": "https://cdn.pony.social/file/tscs37-pony-social/media_attachments/files/109/298/541/500/428/070/original/929181ef39b7807b.png", + "name": null, + "blurhash": "UKQ0HyMxj[of.8WUWBja_NayayfQRPV@ayof", + "width": 728, + "height": 515 + } + ], + "tag": [], + "replies": { + "id": "https://pony.social/users/cadey/statuses/109298544735230581/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "https://pony.social/users/cadey/statuses/109298544735230581/replies?only_other_accounts=true&page=true", + "partOf": "https://pony.social/users/cadey/statuses/109298544735230581/replies", + "items": [] + } + } +}
\ No newline at end of file diff --git a/data/toots/19b491caf03fcb8ce5ce2e2ef1a117981a51727db9bc7088712ff0012162f3c9.json b/data/toots/19b491caf03fcb8ce5ce2e2ef1a117981a51727db9bc7088712ff0012162f3c9.json new file mode 100644 index 0000000..b6a14b6 --- /dev/null +++ b/data/toots/19b491caf03fcb8ce5ce2e2ef1a117981a51727db9bc7088712ff0012162f3c9.json @@ -0,0 +1,56 @@ +{ + "id": "https://botsin.space/users/randomColorContrasts/statuses/109212579424662451", + "type": "Note", + "inReplyTo": null, + "published": "2022-10-22T15:15:12Z", + "url": "https://botsin.space/@randomColorContrasts/109212579424662451", + "attributedTo": "https://botsin.space/users/randomColorContrasts", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://botsin.space/users/randomColorContrasts/followers" + ], + "sensitive": false, + "atomUri": "https://botsin.space/users/randomColorContrasts/statuses/109212579424662451", + "inReplyToAtomUri": null, + "conversation": "tag:botsin.space,2022-10-22:objectId=57700332:objectType=Conversation", + "summary": null, + "content": "<p>Ripe Lemon <a href=\"https://botsin.space/tags/F1DD15\" class=\"mention hashtag\" rel=\"tag\">#<span>F1DD15</span></a><br />Midnight <a href=\"https://botsin.space/tags/0E102E\" class=\"mention hashtag\" rel=\"tag\">#<span>0E102E</span></a></p><p>(Contrast ratio: 13.3:1 | AAA)</p>", + "contentMap": { + "en": "<p>Ripe Lemon <a href=\"https://botsin.space/tags/F1DD15\" class=\"mention hashtag\" rel=\"tag\">#<span>F1DD15</span></a><br />Midnight <a href=\"https://botsin.space/tags/0E102E\" class=\"mention hashtag\" rel=\"tag\">#<span>0E102E</span></a></p><p>(Contrast ratio: 13.3:1 | AAA)</p>" + }, + "attachment": [ + { + "type": "Document", + "mediaType": "image/png", + "url": "https://files.botsin.space/media_attachments/files/109/212/579/401/911/853/original/479970995d705b8f.png", + "name": "Ripe Lemon (#F1DD15) and Midnight (#0E102E)", + "blurhash": "U~KT:njtj@j@~NfQt4j@odfQazfQItfQWDfQ", + "width": 460, + "height": 260 + } + ], + "tag": [ + { + "type": "Hashtag", + "href": "https://botsin.space/tags/F1DD15", + "name": "#F1DD15" + }, + { + "type": "Hashtag", + "href": "https://botsin.space/tags/0E102E", + "name": "#0E102E" + } + ], + "replies": { + "id": "https://botsin.space/users/randomColorContrasts/statuses/109212579424662451/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "https://botsin.space/users/randomColorContrasts/statuses/109212579424662451/replies?only_other_accounts=true&page=true", + "partOf": "https://botsin.space/users/randomColorContrasts/statuses/109212579424662451/replies", + "items": [] + } + } +}
\ No newline at end of file diff --git a/data/toots/7900d46c0c9e816f4f4aea23ca54d79647e92a239f6c2151cd79d42057e64a1f.json b/data/toots/7900d46c0c9e816f4f4aea23ca54d79647e92a239f6c2151cd79d42057e64a1f.json new file mode 100644 index 0000000..32ea303 --- /dev/null +++ b/data/toots/7900d46c0c9e816f4f4aea23ca54d79647e92a239f6c2151cd79d42057e64a1f.json @@ -0,0 +1,56 @@ +{ + "id": "https://pony.social/users/cadey/statuses/109298533570134274", + "type": "Note", + "inReplyTo": "https://pony.social/users/cadey/statuses/109298530078002329", + "published": "2022-11-06T19:34:28Z", + "url": "https://pony.social/@cadey/109298533570134274", + "attributedTo": "https://pony.social/users/cadey", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://pony.social/users/cadey/followers" + ], + "sensitive": false, + "atomUri": "https://pony.social/users/cadey/statuses/109298533570134274", + "inReplyToAtomUri": "https://pony.social/users/cadey/statuses/109298530078002329", + "conversation": "tag:pony.social,2022-11-06:objectId=5855448:objectType=Conversation", + "summary": null, + "content": "<p>kafon, a theme with a lot of rich brown colors like a cup of coffee</p>", + "contentMap": { + "en": "<p>kafon, a theme with a lot of rich brown colors like a cup of coffee</p>" + }, + "attachment": [ + { + "type": "Document", + "mediaType": "image/png", + "url": "https://cdn.pony.social/file/tscs37-pony-social/media_attachments/files/109/298/532/254/949/324/original/da29227cbce00204.png", + "name": null, + "blurhash": "U47KF:?Ht7Wo9atRt7n%~BofbHj[aLxaofR*", + "width": 728, + "height": 516 + }, + { + "type": "Document", + "mediaType": "image/png", + "url": "https://cdn.pony.social/file/tscs37-pony-social/media_attachments/files/109/298/532/984/044/844/original/4fc9f144cba1d5b7.png", + "name": null, + "blurhash": "UJQ0Q|IUj[of.8WBWBjt_NayayfQV@V@ayof", + "width": 729, + "height": 512 + } + ], + "tag": [], + "replies": { + "id": "https://pony.social/users/cadey/statuses/109298533570134274/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "https://pony.social/users/cadey/statuses/109298533570134274/replies?min_id=109298538717562125&page=true", + "partOf": "https://pony.social/users/cadey/statuses/109298533570134274/replies", + "items": [ + "https://pony.social/users/cadey/statuses/109298538717562125" + ] + } + } +}
\ No newline at end of file diff --git a/data/toots/a44d17e5c5afa90752954b320e984582906c525926eca2d053425c7a683a8115.json b/data/toots/a44d17e5c5afa90752954b320e984582906c525926eca2d053425c7a683a8115.json new file mode 100644 index 0000000..1ba9609 --- /dev/null +++ b/data/toots/a44d17e5c5afa90752954b320e984582906c525926eca2d053425c7a683a8115.json @@ -0,0 +1,56 @@ +{ + "id": "https://pony.social/users/cadey/statuses/109298538717562125", + "type": "Note", + "inReplyTo": "https://pony.social/users/cadey/statuses/109298533570134274", + "published": "2022-11-06T19:35:47Z", + "url": "https://pony.social/@cadey/109298538717562125", + "attributedTo": "https://pony.social/users/cadey", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://pony.social/users/cadey/followers" + ], + "sensitive": false, + "atomUri": "https://pony.social/users/cadey/statuses/109298538717562125", + "inReplyToAtomUri": "https://pony.social/users/cadey/statuses/109298533570134274", + "conversation": "tag:pony.social,2022-11-06:objectId=5855448:objectType=Conversation", + "summary": null, + "content": "<p>aoi, a theme with lots of lovely blue hues</p>", + "contentMap": { + "en": "<p>aoi, a theme with lots of lovely blue hues</p>" + }, + "attachment": [ + { + "type": "Document", + "mediaType": "image/png", + "url": "https://cdn.pony.social/file/tscs37-pony-social/media_attachments/files/109/298/536/741/221/518/original/00a5340b49d55605.png", + "name": null, + "blurhash": "U44B|W-=t7f5I9xat8bI-@t7j?j[bbtRofWA", + "width": 736, + "height": 513 + }, + { + "type": "Document", + "mediaType": "image/png", + "url": "https://cdn.pony.social/file/tscs37-pony-social/media_attachments/files/109/298/538/213/457/494/original/44bb8933db4df702.png", + "name": null, + "blurhash": "UKPZr.Inj[of?HWBWBa}~payayfQRkR*ayof", + "width": 725, + "height": 510 + } + ], + "tag": [], + "replies": { + "id": "https://pony.social/users/cadey/statuses/109298538717562125/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "https://pony.social/users/cadey/statuses/109298538717562125/replies?min_id=109298544735230581&page=true", + "partOf": "https://pony.social/users/cadey/statuses/109298538717562125/replies", + "items": [ + "https://pony.social/users/cadey/statuses/109298544735230581" + ] + } + } +}
\ No newline at end of file diff --git a/data/users/a83e23736eb81745c08464f913685f6e8479db6096355aa8c217c66c3e120be3.json b/data/users/a83e23736eb81745c08464f913685f6e8479db6096355aa8c217c66c3e120be3.json index 15e8f2a..572774f 100644 --- a/data/users/a83e23736eb81745c08464f913685f6e8479db6096355aa8c217c66c3e120be3.json +++ b/data/users/a83e23736eb81745c08464f913685f6e8479db6096355aa8c217c66c3e120be3.json @@ -9,7 +9,7 @@ "featuredTags": "https://pony.social/users/cadey/collections/tags", "preferredUsername": "cadey", "name": "Xe", - "summary": "<p>🔞 Minors, please DNI</p><p>Any statements are my own, not my employer's</p><p>Archmage of Infrastructure at that VPN company that isn't actually a VPN company I guess it's complicated</p><p>ΘΔ</p><p>zi ai-uh-so /zi ai.ə.soʊ/</p>", + "summary": "<p>🔞 Minors, please DNI</p><p>Any statements are my own, not my employer's</p><p>Archmage of Infrastructure, NixOS nephlemancer, kastermakfa, acquirer of S-tier waifus, one of six <a href=\"https://pony.social/tags/pluralgang\" class=\"mention hashtag\" rel=\"tag\">#<span>pluralgang</span></a></p><p>ΘΔ</p><p>zi ai-uh-so /zi ai.ə.soʊ/</p>", "url": "https://pony.social/@cadey", "manuallyApprovesFollowers": false, "discoverable": true, diff --git a/data/users/dd6596c91a8e6aecc7b12177322e3b9b57fda2fe03405682ce2c45fe2fe99df3.json b/data/users/dd6596c91a8e6aecc7b12177322e3b9b57fda2fe03405682ce2c45fe2fe99df3.json new file mode 100644 index 0000000..66fe52a --- /dev/null +++ b/data/users/dd6596c91a8e6aecc7b12177322e3b9b57fda2fe03405682ce2c45fe2fe99df3.json @@ -0,0 +1,24 @@ +{ + "id": "https://botsin.space/users/randomColorContrasts", + "type": "Service", + "following": "https://botsin.space/users/randomColorContrasts/following", + "followers": "https://botsin.space/users/randomColorContrasts/followers", + "inbox": "https://botsin.space/users/randomColorContrasts/inbox", + "outbox": "https://botsin.space/users/randomColorContrasts/outbox", + "featured": "https://botsin.space/users/randomColorContrasts/collections/featured", + "featuredTags": "https://botsin.space/users/randomColorContrasts/collections/tags", + "preferredUsername": "randomColorContrasts", + "name": "random color contrasts", + "summary": "<p>Inspired by and using: <a href=\"https://randoma11y.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><span class=\"invisible\">https://</span><span class=\"\">randoma11y.com</span><span class=\"invisible\"></span></a></p><p>Gets colours, finds their WCAG contrast ratio. Attempts to name them.</p><p>See pinned toots for more info.</p>", + "url": "https://botsin.space/@randomColorContrasts", + "manuallyApprovesFollowers": false, + "discoverable": true, + "published": "2020-08-11T00:00:00Z", + "devices": "https://botsin.space/users/randomColorContrasts/collections/devices", + "icon": { + "type": "Image", + "mediaType": "image/png", + "url": "https://files.botsin.space/accounts/avatars/000/248/959/original/dcea077e7cba981b.png" + }, + "image": null +}
\ No newline at end of file diff --git a/lib/xesite_markdown/src/lib.rs b/lib/xesite_markdown/src/lib.rs index 5bd6dfa..941761d 100644 --- a/lib/xesite_markdown/src/lib.rs +++ b/lib/xesite_markdown/src/lib.rs @@ -196,9 +196,9 @@ pub fn render(inp: &str) -> Result<String> { toot_url = format!("{toot_url}.json"); } - let toot_fname = format!("./data/toots/{}.json", hash_string(toot_url)); + let toot_fname = format!("./data/toots/{}.json", hash_string(toot_url.clone())); tracing::debug!("opening {toot_fname}"); - let mut fin = fs::File::open(&toot_fname)?; + let mut fin = fs::File::open(&toot_fname).context(toot_url)?; let t: Toot = from_reader(&mut fin)?; let user_fname = format!( @@ -206,7 +206,7 @@ pub fn render(inp: &str) -> Result<String> { hash_string(format!("{}.json", t.attributed_to.clone())) ); tracing::debug!("opening {user_fname}"); - let mut fin = fs::File::open(&user_fname)?; + let mut fin = fs::File::open(&user_fname).context(t.attributed_to.clone())?; let u: User = from_reader(&mut fin)?; diff --git a/lib/xesite_types/src/mastodon.rs b/lib/xesite_types/src/mastodon.rs index 56567e6..942bbc7 100644 --- a/lib/xesite_types/src/mastodon.rs +++ b/lib/xesite_types/src/mastodon.rs @@ -55,7 +55,7 @@ pub struct User { pub icon: Icon, #[serde(rename = "image")] - pub image: Icon, + pub image: Option<Icon>, } #[derive(Serialize, Deserialize)] diff --git a/templates/index.rs.html b/templates/index.rs.html index 424c804..142bee1 100644 --- a/templates/index.rs.html +++ b/templates/index.rs.html @@ -19,7 +19,7 @@ "https://tulpa.dev/cadey", "https://twitter.com/theprincessxena", "https://pony.social/@@cadey", - "https://www.linkedin.com/in/christine-dodrill-1827a010b/", + "https://www.linkedin.com/in/xe-iaso-87a883254/", "https://www.youtube.com/user/shadowh511" ] @} @@ -60,19 +60,16 @@ <h5>Highlighted Projects</h5> <ul> <li><a href="https://github.com/PonyvilleFM/aura">Aura</a> - PonyvilleFM live DJ recording bot</li> - <li><a href="https://github.com/Elemental-IRCd/elemental-ircd">Elemental-IRCd</a> - IRC Server Software</li> <li><a href="https://github.com/Xe/site">This website</a> - The backend and templates for this website</li> <li><a href="https://github.com/Xe/olin">Olin</a> - WebAssembly on the server</li> <li><a href="https://github.com/Xe/when-then-zen">when-then-zen</a> - Meditation instructions in Gherkin</li> - <li><a href="https://github.com/Xe/creators-code">Creator's Code</a> - Minimal code of conduct for communities</li> <li><a href="https://github.com/Xe/printerfacts">printerfacts</a> - Informative facts about printers</li> <li><a href="https://github.com/Xe/x">x</a> - Experiments and toys</li> - <li><a href="https://github.com/Xe/PonyAPI">PonyAPI</a> - My Little Pony: Friendship is Magic Episode information API</li> - <li><a href="https://github.com/Xe/bsnk">bsnk</a> - Battlesnake bots</li> - <li><a href="https://tulpa.dev/cadey/xeos">XeOS</a> - An experimental microkernel in Rust</li> <li><a href="https://h.christine.website">h</a> - A satirical programming language</li> - <li><a href="https://github.com/Xe/gruvbox-css">gruvbox-css</a> - My minimal Gruvbox CSS theme</li> + <li><a href="https://github.com/Xe/gruvbox-css">Xess</a> - My personal CSS framework</li> + <li><a href="https://github.com/Xe/Xeact">Xeact</a> - My personal JavaScript femtoframework for high productivity development</li> <li><a href="https://tulpa.dev/Xe/quickserv">quickserv</a> - A quick HTTP fileserver</li> + <li><a href="https://github.com/Xe/waifud">waifud</a> - A VM manager that I use in my homelab</li> </ul> <h5>Quick Links</h5> |
