RSS FeedTwitterMastodonBlueskyShare IconHeart IconGithub IconArrow IconClock IconGUI Challenges IconHome IconNote IconBlog IconCSS IconJS IconHTML IconShows IconOpen Source Software IconSpeaking IconTools IconShuffle IconNext IconPrevious IconCalendar IconCalendar Edit IconNotebook IconObservable Notebooks IconSlash IconGoogle G Icon
Background grid of 2023's and the GUI Skull logo in the middle
A cartoon skull with a hotpink hat on.11 min read

New Year, New Site

cssjs

I've joined the indie web #

Viva RSS, viva owning your content, viva to free expression. Join that Indie Web y'all 🤘🏻💀.

let life = new Year().next()

Instead of joining a different social network, investing in some new walled garden (Mastadon 👀), I've decided to take the popular UX/UI patterns, like a social feed, and build them here, where they can't be taken away or stifled.

Ain't no one gonna take my site. Adam Argyle

the stack #

After much testing and research, I chose to invest in Deno and their Fresh framework. I appreciate their investment in web standards and focus on being minimal.

It's also a server side rendered framework, at the edge, which means I can fix my site within seconds and don't have to wait for long static build and deploy times. Deno Fresh also caches at the edge, just in time 😍

SSR and progressive enhancement FTW.

the styles #

Fresh is pretty minimal out of the box, especially in regards to styling. It only offers a just in time atomic stylesheet setup, but I wanted to use Open Props (naturally). So I ended up writing my own file system watcher task that compiles PostCSS and pops it out to the static/ directory for cached serving.

import { debounce } from '$std/async/mod.ts'

export async function watchAndBuildStyles() {
  const watcher = Deno.watchFs([
    './styles/', 
    './components/', 
    './islands/',
  ])

  const protectedBuildCall = debounce(buildStyles, 200)
  
  for await (const _event of watcher)
    protectedBuildCall()
}

This route also means I'm in full control of the stylesheet. That was very critical to me as I want to use my site as a playground for new CSS features, progressively enhancing UX when available but otherwise serving a great static experience.

Here's my list of plugins if you're curious:

import cssNesting from 'npm:postcss-nesting'
import customMediaPlugin from 'npm:postcss-custom-media'
import mqRanges from 'npm:postcss-media-minmax'

import inlineImports from 'npm:postcss-import'
import importUrl from 'npm:postcss-import-url'
import importGlob from 'npm:postcss-import-ext-glob'
import cssnano from 'npm:cssnano'

import OpenProps from 'npm:open-props'
import jitProps from 'npm:postcss-jit-props'

A small preview of my index.css file. Spoiler, it's layers all the way, and I looooved it.

@import "https://unpkg.com/open-props/normalize.min.css" layer(base.normalize);
@import "https://unpkg.com/open-props/theme.light.switch.min.css" layer(base.theme);
@import "https://unpkg.com/open-props/theme.dark.switch.min.css" layer(base.theme);
@import "utilities.css" layer(base.utilities);
@import "nojs.css" layer(base.nojs);

@import "toast.css" layer(components.toast);
@import "markdown.css" layer(components.markdown);
@import "syntax-highlighting.css" layer(components.syntax);
@import "neon.css" layer(components.p3);
@import "quotes.css" layer(components.quote);

@import-glob "../components/**/*.css" layer(components.fresh);
@import-glob "../islands/**/*.css" layer(components.fresh);

@layer base.normalize-overrides {…}
@layer overrides {…}

It's 3 layers: base, components, overrides. Named sublayers for easier debugging and organization.

With that in place, I match a classname to a component name and that's pretty much it. I have global styles and component styles, all sharing the props and JIT Props makes sure I only ship the props I use. Good stuff.

Try the site in Chrome with #experimental-web-platform-features enabled. I'll be constantly trialing new features there! I'm currently trialing Scroll Linked Animations!

light n' dark #

Hopefully you noticed there was no "flash of an unwanted color scheme" (FOAUCS) when the page loaded.

So many sites strobe light my face with the light theme when the page loads (I'm generally in dark mode), and it makes me feel like a vampire who just got blasted with a sun ray. Sometime they blast me on every single page load… 😱

To make this feature, I followed my own Theme Switch GUI Challenge! I just integrated it into Fresh 🙂

A few features of it that make me happy:

  1. Works without JS
  2. Remembers your choice
  3. Syncs with the system as it changes
  4. Has a rad animation between a sun and moon with SVG and transforms
  5. No FOAUCS anywhere
  6. Has accessibility considerations

Also, don't miss that adaptive favicon 😎

comments and likes #

I really like the idea of aggregating mentions of my site's work onto this site itself, and WebMentions.io let me do that. Once set, with Deno I server side fetch mentions for any sub pages and send the data to some components to handle for rendering.

I think the result is cool. I feel like it's peer to peer in nature but with a good amount of optional content filtering from the owner side.

I'm not in any webrings yet… should I be?

multiple personas #

Sometimes people have multiple accounts with a service so they can provide different branded feeds, maybe a personal and a business one for example. I have 6 personas I can be on my site lol:

  1. admin: when I'm making site updates or announcements
  2. google: when posts are related to my work at Google
  3. argyleink: when the posts are rando Adam thoughts or comments
  4. csspodcast: when new episodes or moments happen for the CSS Podcast
  5. guichallenges: when new episodes or moments happen for the GUI Challenges
  6. pops: dad updates

I even have an open feature request, to myself, about co-tweeting personas. lol, taking multiple personalities to a new site level.

404 #

Make a _404.tsx handler in the routes/ directory and you too can make a custom 404 page. Want to see it? Visit https://nerdy.dev/you-wont-find-this.

Open Props made this page easy to style because I could easily bring in the normalize and props from a CDN and use them in the template.

localized dates #

With a little custom middleware I parse preferred languages from the request headers and then provide a getter to components on the server.

req.headers.get('accept-language')

This feature is both for localization but also for accuracy. The dates shows how long ago the post was made, and by knowing where you are in the world I can provide a date relative to you.

logical properties #

I used logical properties everywhere, which means the site can do rad stuff like this:

and i didnt have to do anything, the browser adapts it for me.

Turned out Media Queries made this hard though, and Container Queries saved the day. Blog post will def come out about it.

pretty URLs #

I really like minimal URLs and with Deno + Fresh it was really easy. I appreciate that it was the default.

analytics #

No client side analytics.

I went with Pirsch, and am happy! Cost seems right so they stay alive, great APIs and SDKs, and a really nice dashboard that's simple but powerful.

I reeeeeeally didn't want to run some open source containers on the cloud and host my own analytics… just not my kind of Tuesday night activity. Aka, I'm down with a couple extra bucks for a managed solution.

progressive web app #

This site is also a PWA. Go ahead, install it or add it to your homescreen. It launches with a nice splash image, is full screen, and can really feel like a system application.

I've implemented the following PWA features so far:

  1. manifest.json
  2. A service worker, mine is very lightweight
  3. Custom icon
  4. Custom install banner image

It's also got a great landscape layout when in fullscreen 🤓

forced colors #

Where there are shadows and colors to help distinguish UI elements, they're replaced with a transparent 1px border so that in forced-colors-mode there are visual affordances for distinguishing elements.

I also think the retro colors are super rad. Sometimes I use this mode if a site doesn't have a dark theme, it'll force a cool retro one!

works without javascript #

The site tries to only use JavaScript to enhance the experience, not relying on it for the baseline behavior.

uses some GUI Challenges #

You'll find the toast, dialog, theme switch, adaptive favicon and more to come.

Checkout all the GUI Challenges

keyboard navigation #

Give it a shot, try navigating around with the keyboard. There's a skip link in the nav bar, special scroll snap UX in the home feed, arrow key support in the filter aside, and great focus styles.

rss #

I freakin love RSS. Peer to peer social interation, nothin in the middle, so good. I've been an RSS reader for over 10 years, it's by far the best place for me to get meaningful information. Well, now I have a feed!

The RSS feed is the backbone of the site, it's an artifact that allows the content to travel and adapt to reader's preferences. You can subscribe from an RSS reader, from Chrome using the follow feature, or just follow along on Twitter where I syndicate the content.

Subscribe why don't ya?!

media #

Everything is currently uploaded into Cloudinary and then I've created a few authoring conveniences in Fresh and when writing Markdown that utilize all their great features.

I try to be respectful with the media delivery:

  • reliable alt content
  • lazy loading
  • async decoding
  • client hints
  • multiple formats (webp, avif, etc)
  • compressed
  • delivered from CDNs near you
  • videos always offer controls and only loop on demand

pride moment #

this can't be found in my styles:

body {
  overflow-x: hidden;
}

🤓

55 comments #

188likes
12reposts
  • Craig Webb
  • Mike-麥-Mai/index.html
  • Denis TRUFFAUT ⭐️
  • Jem Young
  • nullptr
  • WebPerformance Report
  • Bramus
  • 🄰🄻🄸
  • Fronteers
  • L!on 💙💛
  • Povilas
  • DevLobster🐻⛓
1 pingbacks

Join the conversation on

Awesome job, nice way to ring in the new year! Also, happy new year 😀
Kevin PowellKevin Powell
Looks good, Adam. Great job.
Orges MihajOrges Mihaj
Seriously, great job on the new site Adam. Can't wait to give this a read and get some inspiration for my Fresh sites. I think Deno is in for a big year next year 🙌🏼
Jaydan UrwinJaydan Urwin
I like the idea of having different personas instead of categories or tags. Having the profile picture difference is so nice. Also lots of fun things to check out you linked.
moji ///moji ///
Could you explain a bit about webmentions and your experience with it maybe? I remember blog pings from back in the day but not in detail and the indieweb explanation isn't helping me much
moji ///moji ///
Sick new site full of explanations using Deno and fresh. Looks slick af check it out!
Nicholas CharriereNicholas Charriere
Is that awesome! 😱🤩 have to read it fully tomorrow 😄
CodeMonumentCodeMonument
I’m getting this error
AraAra
Love the site!
AraAra
Congrats on the Re-design. Happy New Year Adam. More YouTube videos in 2023 please?? 🥲
Vipin Mishra 🧑‍💻☕Vipin Mishra 🧑‍💻☕
“Instead of joining a different social network, investing in some new walled garden (Mastadon 👀), I've decided to take the popular UX/UI patterns, like a social feed, and build them here, where they can't be taken away or stifled.” nerdy.dev/new-year-new-s…
🖖🖖
“I have 6 personas I can be on my site “ nerdy.dev/new-year-new-s…
🖖🖖
Did you work on this with @jh3yy ???
Helge AhrensHelge Ahrens
nice, learned of few new things I need to catch up on in your post! thanks!
Ahmad NassriAhmad Nassri
Love this, go indieweb!
🄰🄻🄸🄰🄻🄸
can you explain your decision for Fresh and Deno, over alternatives?
AlexAlex
Lol it looks like 1:1 the same though ! Will be interesting to see which site holds better perf scores :o
Helge AhrensHelge Ahrens
Thanks. How to include it seems clear and mostly frictionless to me. I don't get how they collect the references though. How would I make a valid reference that gets shown?
moji ///moji ///
Ah ok, so its just urls. I thought it would be far to much effort for them to scrape the web. Then that seems like it would be easy to include some social interaction without going full heavy comment system 👍
moji ///moji ///
Love the site and your post all about it!
Iain SimmonsIain Simmons
Looks fantastic (as expected). Nice choice of Fresh... love the fast deploys, speed up optimising a site no end
RodneyRodney
Love Adam Argyle's new site, been tinkering with Deno 🍋 Fresh recently and appreciate the instant deploys - it makes such a difference for optimising to get feedback that quickly from a deployed site
RodneyRodney
Oro puro 👇♥️
MarcoMarco
Love it! I'll give it a try, it looks awesome Adam!
Alvaro IsornaAlvaro Isorna
We're always happy to see Pirsch being adopted. Especially when our backend solution is used 🥰 Check out @argyleink's blog article for details 👇 nerdy.dev/new-year-new-s…
Pirsch AnalyticsPirsch Analytics
I regret to inform you of a horz scroll bug with your grid 😬 great job tho really interesting stuff! is it possible to follow your feed on mastodon?
felixfelix
fraid not – looks like its a firefox issue as it's fine in chrome and safari
felixfelix
looks like theres a section gui-toast-group outside the body tag also, not sure if intentional!
felixfelix
tried refreshing but no joy, tried a fresh install of FF on a VM and still the same :(
felixfelix
interesting! why?
felixfelix
you should check out this dope design token framework with things like --layer-important open-props.style/#z-index 😬
felixfelix
yeah looks like the .TopicsAside is display none ¯\_(ツ)_/¯
felixfelix
Just read another post with a similar vibe nerdy.dev/new-year-new-s…
John Derr he/himJohn Derr he/him
Works on his new Deno Fresh site... he uses JavaScript in a <script> tag in the <head> to set the theme CSS class on <html> while the head is still being parsed. Try changing theme and refreshing on his site 👇🏽 to see it in action: nerdy.dev/new-year-new-s…
RodneyRodney
Watching the system section 🔥
DevLobster🐻⛓DevLobster🐻⛓
Of course, you and Miriam. :) Thanks!
Manuel MatuzovićManuel Matuzović