Sign in

🌫️ Grant Forrest

@gfor.rest
580 followers 766 following 1.4K posts

"God is the garden I tend to believe" Day job: Design Systems gfor.rest

PostsRepliesMedia
🌫️ Grant Forrest @gfor.rest · 04/10/2026
Did a big reset on my website, I'll probably move most of the old content to ATProto based stuff whenever I find the time. In lieu of a portfolio it generates a unique puffy cloud for you on every page load.
My website, which has a little lineart logo inspired by a lowercase cursive "g" and "f" conjoined, a barebones list of links, and one big cumulonimbus cloud with dithered shading.
130
🌫️ Grant Forrest @gfor.rest · 24/09/2026
Forgive me, I did a vibe code
An e-ink display with a puffy white procedurally generated cloud. It shows the date and basic weather conditions
280
🌫️ Grant Forrest @gfor.rest · 20/09/2026
Cold pan salmon skin technique is legit
A plate with a salmon filet featuring very crispy, perfectly brown skin. Accompanied by cabbage pan fried with a touch of berbere spice.
000
🌫️ Grant Forrest @gfor.rest · 09/09/2026
You should read it
A dog-woman dressed for a fight saying, "It is a tragedy - for a friend to die. I don't expect forgiveness for that. It's a tragedy for an emperor... Even to exist"
000
🌫️ Grant Forrest @gfor.rest · 09/09/2026
The way TanStack Query attaches the query data shape to the `.queryKey` property of reusable options so you can get magically typed mutations on the query is beautiful.
A code editor showing a React Query mutation. I've passed "followSuggestionsQueryOptions.queryKey" to queryClient.setQueryData as the first parameter, and the data type of the updated query data is automatically typechecked. I'm hovering over the queryKey itself and Typescript reveals they've assigned the shape of the follow data to a [dataTagSymbol] type property on the key itself which lets the query client infer the shape just from the key.
3250
🌫️ Grant Forrest @gfor.rest · 07/09/2026
🦵🦵
A fluffy silver long hair cat lounging in a basket with both of his rabbit feet sticking straight out
010
🌫️ Grant Forrest @gfor.rest · 04/09/2026
This is captures what it feels like working in an engineer-dominant enterprise org
A Reddit post titled "An engineer created a crossing that doesn't need traffic lights" and the attached video is the most disastrously dangerous and convoluted distortion of a roundabout you can imagine which requires merging across four lanes to turn left
2121
🌫️ Grant Forrest @gfor.rest · 29/08/2026
This feature made me feel a strong sense of secondhand loneliness, is that good UX
Spotifys new AI feature prompts me, "Let's talk about this artist"
020
🌫️ Grant Forrest @gfor.rest · 19/08/2026
Old reliable
Miso glazed salmon and broccoli over rice
010
🌫️ Grant Forrest @gfor.rest · 16/08/2026
Is that a TARDIS?
A religious icon depicting Jesus encountering a woman seeking a miracle. Jesus is standing in front of a tall blue box with vertical small-paned windows near the top, topped with what appears to be a glowing light.
000
🌫️ Grant Forrest @gfor.rest · 16/08/2026
This is very smart
A firmware update loading screen from Chipolo has you play tic tac toe against a computer while you wait to convince you to keep the app foregrounded
000
🌫️ Grant Forrest @gfor.rest · 15/08/2026
In case you weren't aware, you can save hours of your life not sliding bits of potato off the side of your knife. May not be good for the tip, idk.
110
🌫️ Grant Forrest @gfor.rest · 15/08/2026
lets go
A cast iron pan full of sliced onions
000
🌫️ Grant Forrest @gfor.rest · 01/08/2026
A lawn statue of the Virgin Mary treading on a serpent with a neon sign arching above her which reads "Break your guns". A museum exhibit.
000
🌫️ Grant Forrest @gfor.rest · 15/06/2026
My first dinner fail in a while. Supposed to be padakjeon... Looks like somewhere between condensed chicken soup and aspic. All the browned bits remained on the pan... had made a double portion so tried the other half in the wok. Fared better (and nailed a wok flip).
A pan full of very gross looking chicken mushA wok with a recognizably browned, thick pancake thing
100
🌫️ Grant Forrest @gfor.rest · 07/06/2026
Dipping into the lore
A Switch running Mina the Hollower in the foreground, a children's book about a mouse titled "Mina" in the background
120
🌫️ Grant Forrest @gfor.rest · 06/06/2026
Shadow overlaps may qualify as 'happy accident' enough to keep on this one. Plus the funky corners. I like it.
000
🌫️ Grant Forrest @gfor.rest · 06/06/2026
I will never not love implementing automatic contextual tinted neutrals
A bunch of buttons and inputs with color themes. Starts with a section labeled "@mode-user" which has an orange tint. There's a button labeled "neutral" which is a very slightly orange-gray. Below is a whole section labeld "@mode-neutral" with the same orange-gray tint applied to all controls. Below that is "@mode-success," a pleasant green, whose own neutral button is a greenish gray.
010
🌫️ Grant Forrest @gfor.rest · 30/05/2026
Still one of my favorite little components I've made.
000
🌫️ Grant Forrest @gfor.rest · 29/05/2026
So I got a little carried away with this project
A unit test. The describe block is "css template - if() pre-baking" and the test case is "pre-bakes matching style() conditions when property value is known."

The test itself starts by defining an equation variable from a tagged template literal called "css." The template in question is a CSS if() condition: "if(style(--size: "2xl"): 1em; else: 0.25em;)". The test case first passes it into a function called "computeEquation", also passing in a "propertyValues" configuration which includes a value for the --size custom property set to "2xl" as shown in the original CSS statement. It also passes "skipBaking: false" implying the computation should bake values.

The result of the computation is then passed to a function "printComputationResult." An assertion then runs that the final returned value is the string "1em" and the test is marked as passing.
100
🌫️ Grant Forrest @gfor.rest · 25/05/2026
... For a 3 year old???
020
🌫️ Grant Forrest @gfor.rest · 25/05/2026
And... Broccoli...
Some broccoli. Probably good?
110
🌫️ Grant Forrest @gfor.rest · 25/05/2026
And garlic scape compound butter????
A bundled up log of compound butter with little green bits
220
🌫️ Grant Forrest @gfor.rest · 25/05/2026
And filet mignon ???
Two filet mignon cuts resting on a wood cutting board. I think the crust is pretty decent!
120
🌫️ Grant Forrest @gfor.rest · 25/05/2026
Making risotto
A pan full of partially cooked risotto next to a small pot of stock
270
🌫️ Grant Forrest @gfor.rest · 25/05/2026
All of these for ~100 lines of CSS. Add a new color with 1 more line. This project is finally getting to the fun part.
A grid of 12 combinations of button color and emphasis, each having 7 visual states: normal, with icon, disabled, small, wrapping other content, icon only, and small icon only. Technically there are more possible permutations.

The emphases are primary, light, default (white), and ghost. Three color variants are shown: a brand color, a success, color, and a danger color.

Fun fact, there isn't any CSS in the button itself to handle the color variation! It's completely based on the containing surface's color mode.
100
🌫️ Grant Forrest @gfor.rest · 25/05/2026
What if you could just drop a ghost button on any surface and get a color-matched interaction by just setting it to inherit.
100
🌫️ Grant Forrest @gfor.rest · 25/05/2026
I'm adding a ring shadow here which copies the background color, creating this effect. It respects the `bg-darken` mixin. It is hard to explain why the previous code is so exciting to me unless you've tried to scale something like this to a bunch of color variants and/or user colors.
100
🌫️ Grant Forrest @gfor.rest · 25/05/2026
What if this was all you needed to show a button in any color - primary, success, destructive, user-themed, whatever - and define consistent and interesting interaction states?
CSS hover, active, and focus states. Each is 2 lines of code. I'm applying a "bg-darken" mixin to darken the background, and then adding a ring shadow which references the background color. Darken amount and ring size varies for each state. Focus state lightens instead of darkening.
100
🌫️ Grant Forrest @gfor.rest · 25/05/2026
It's always bothered me that CSS properties don't have intellisense and typechecking. So I made a VS Code plugin, too.
Some CSS. Inline with the custom property named "--m-action-primary-color-bg", there's a yellow color preview box. I'm hovering that token name as well, and a hover tooltip reveals its "purpose" (color), a brief description, and the full OKCLH color definition.Some CSS where I've misspelled a mixin name. The editor has underlined the mixin with red.
100
🌫️ Grant Forrest @gfor.rest · 25/05/2026
I'm tired of utility classes . I was waiting for native mixins, but eh, I'll just make my own. Good ol' PostCSS.
A CSS button class, featuring lots of @apply mixin lines. If you read it closely there are some interesting things going on. For example, the foreground color is being set to a contrast-color from the background color, which is being referenced via a "bg-ref" custom property contributed by the "bg" mixin. The border color is copying the foreground color in the same way.

Further down you can see the primary button's hover and active states are using "bg-lighten" mixins. Presumably these take the current background color and lighten it a bit.
110
🌫️ Grant Forrest @gfor.rest · 11/05/2026
fun
000
🌫️ Grant Forrest @gfor.rest · 25/04/2026
Progress
Various color palettes with light and dark schemes; each section is wrapped in a mode which changes the main color but adheres to the light/dark scheme. One section has a "green buttons" mode which keeps everything the same except the buttons are green now.
000
🌫️ Grant Forrest @gfor.rest · 25/04/2026
Automatic dark mode needs some contrast work, but I'm liking how this feels to use so far. - All color ranges are generated from a single source hue - Each color gets a complementary neutral range - Modes are auto-interpreted in color scheme - Partial modes (greenButtons) stack onto parent values
Multiple layers of schemes and modes applied to color palette swatches and example UI elements.

It starts with @mode-base which has a yellow color range, white text field, yellow button, gray secondary button.

Nested inside is a @mode-alt area with a blue color range, blue-tinted background white, blue text field and buttons.

Nested in that is @scheme-dark @mode-greenButton, and it's all in dark mode. While the color range and text field are still blue from the parent, the buttons are now both green.

Beneath this, also nested under @mode-alt, is an area with @scheme-contrast @mode-dense. The color range is now compressed to just 2 colors: a very light blue and a dark, almost-black blue. Everything is padded more tightly.
200
🌫️ Grant Forrest @gfor.rest · 25/04/2026
Getting somewhere. Same components, only the class shown being applied. Each class is defined from one config which covers both light, dark and the (custom) contrast color schemes.
A stack of a bunch of nested copies of the same UI. Each level has a class shown that's applied to it. Some have "@schema-dark", this turns on light or dark mode. Some have "@mode-blue" or "@mode-greenButtons" or "@mode-dense" - these have various effects... blue makes everything blue, greenButtons turns just the button green, and dense reduces the padding. Stacked mode effects persist until another mode overrides them.
100
🌫️ Grant Forrest @gfor.rest · 24/04/2026
So I managed to get something working with @scope, but I'm concerned about whether it's efficient perf-wise. I can't tell if a bare @scope (.class) { --var: foo; } is evaluating these props on *every* element in scope, or lazily evaling them on use.. I may need to read the spec. But the usage... 🧑‍🍳👌
A set of four demo UIs. Each is an input next to a button. The first one uses yellow theme colors in a light scheme. The second uses blue in a light scheme. The third is yellow and dark, the fourth is blue and dark.A snippet of JSX. We begin with a <style> tag which injects some generated styling from a function named "generateThemeWithModes". It's configured with some named color hues, a "base mode" (not shown) and a "demo mode" which configures some semantic tokens with 'secondary' color values.

Below that is the HTML to render the prior image. It first renders a bare version of the demo UI. One is meant to infer that this "base mode" uses yellow as the primary color.

Then it renders the same demo UI, but wrapped with "@mode-demo" as a class. This is what renders the same stuff in blue.

Then it wraps the same thing again within "@scheme-dark @mode-base" - flipping the color scheme to dark. The nested UI uses dark versions of the same yellow/blue mode colors.The CSS evaluated for the blue demo UI. It's inheriting overridden blue versions of the configured mode token props because it matches "@scope (.\@mode-demo)".
100
🌫️ Grant Forrest @gfor.rest · 15/04/2026
I'm free
An error alert in Windows which says "Windows cannot find C:\Program Files (x86)\Microsoft\Copilot\Application\Copilot.exe"
110
🌫️ Grant Forrest @gfor.rest · 04/04/2026
I drew these with my finger in Figma because I figured I'd do real ones later but now I might like them too much.
Two options in a UI: "play online" and "play hotseat." The illustration for "play online" is a crudely drawn purple guy with weird little hands raising his arms up to a golden globe. One hand has a glowing yellow phone. The illustration for "play hotseat" is a squiggly purple hand holding a pink phone. It's surrounded by floating eyes. One of them is a cat's eye.
000
🌫️ Grant Forrest @gfor.rest · 03/04/2026
Anchor positioning is such a headache-saver. Also nice to finally take the time to spruce this UI up.
220
🌫️ Grant Forrest @gfor.rest · 29/03/2026
Having spent the last week just dealing with dumb build tooling issues (my fault for writing my own UnoCSS bundler plugin) it's nice to have pretty styles back. I enjoy making games.
A game board grid with some tiles placed. The tiles depict little pathways and flowers. Each one has a number attached which indicates its point value. The whole game UI is tinted with a pleasant, calming green.
010
🌫️ Grant Forrest @gfor.rest · 15/03/2026
Somewhere along the way it went from challenge to fascination at how well my brain was calibrating. Really neat little game.
171
🌫️ Grant Forrest @gfor.rest · 01/03/2026
I've been burned out for a while, but it is really cool being able to pick up this games platform project again and have a prototype like this done in one weekend. Really helps to be able to act quickly when I feel up to it.
090
🌫️ Grant Forrest @gfor.rest · 22/02/2026
I feel When Marnie was There gets overlooked for not being Miyazaki but both the opening and ending really get me good.
An old woman watching over a sleeping child. A still from an animated movie.
010
🌫️ Grant Forrest @gfor.rest · 20/02/2026
I love anchor positioning. I cannot imagine the obnoxious overflow mess I'd have to do to get this without it.
A UI for selecting a game to play in a gaming app. A little "playing!" badge, tilted, hovering above the corner of a section showing the current game (Hearts) and a button which says "need more players."
130
🌫️ Grant Forrest @gfor.rest · 15/02/2026
This is probably about the final form, if not done. I'm not obsessed with it but I'm also not sure what to do about it. Still, it's a long way from my first attempt to paint a sunrise/sunset cloud.
Same painting, the gradient of the highlights and lowlights on the cloud is a bit smoother, some foreground dark clouds have changed shape so they don't look weirdly similar to background clouds.
030
🌫️ Grant Forrest @gfor.rest · 11/02/2026
I'm learning interesting things about how we see and what's salient. I didn't bother documenting the stage between the last two photos, but I'd attempted to reproduce some of the scattered dark foreground clouds from the reference. It was a complete disaster and I had to repaint the sky to undo.
A photo of a big cloud at sunset, partially obscured by whispy dark clouds in the foregeound
100
🌫️ Grant Forrest @gfor.rest · 11/02/2026
It was a very winding journey from there to here. I ruined it, ditched my reference photo, kept going, suddenly it's sunrise instead of sunset, and there's the cloud.
A more voluminous and lighted cloud. The sky light gradient is more intense and there's more green in it. Upper portions of the cloud are much lighter, as if lit from within.
100
🌫️ Grant Forrest @gfor.rest · 10/02/2026
Eh well it was too tempting, had to keep going. As far as messy middles I'm actually pretty happy with this one.
The cloud is now filled in with a deep, dark blue. Pink orange highlights near the top suggest dusk. Part of the cloudscape is still an empty patch of yellow wash. It's a bit messier, less charming, but clearly working toward something definite.
110
🌫️ Grant Forrest @gfor.rest · 10/02/2026
Always a little tempted to stop here. It's basically art already right
Early stages of a painting of a cloud. The blue sky and sunset highlights are sketches in. The cloud itself is just a big yellow washed blob.
260
🌫️ Grant Forrest @gfor.rest · 07/02/2026
In practice this means, if you're willing to stick to systematic spatial scaling, you don't have to manually create visual groupings of content sections anymore. Styling is reduced to "should be padded, should have gap" etc, the design system takes care of the sizes based on context.
Another split screen, this time there are two distinct sections separated by a large gap. One section has two items, separated by a smaller gap so they look related to one another, not the second section. Once again all the padding is scaled by nesting factor and the border radius al line up.
110