r/reactjs 18h ago

Show /r/reactjs Ambient CSS v3 - Blender meets CSS

https://ambientcss.vercel.app/

I started building a physically based shadow system for CSS 5 years back and gave up after it became too complex. Then, leveraging coding agents, I was finally able to ship v1 earlier this year. Thanks to the very kind and warm reception for v1 from the members of this community and r/css , I was motivated to develop it further.

Today, I'm announcing Ambient CSS v3. This version steps up the realism considerably - each effect and base component was first built in Blender and rendered using an identical lighting setup. Then, based on the renders the CSS formulae were adjusted to match the Blender render. All the Blender files and their parametric generators are also in the source repo.

Besides this, we also have new CSS modifiers - thickness, material (matte/shiny/glass/brushed/spun/blasted). We also have some new components for the react package. Also, the component system is refactored and split into base components and skins, allowing for the ability to create custom component kits.

Thanks for your love!

48 Upvotes

15 comments sorted by

5

u/dev3-studio 11h ago

Really cool demo! Any thoughts given to performance and accessibility?

Would be curious to see how this scales with a real app

2

u/Piposhi 11h ago

Thanks! To be frank, I didn't think about performance and accessibility. I will now and work on them next before implementing more features.

3

u/creaturefeature16 13h ago edited 12h ago

I guess I am just REALLY confused as to what the Blender process' role is here, at least for such simple elements like this:

https://kikkupico.github.io/ambientcss/ambient-components/button

What was so challenging about CSS box shadows that made these so difficult to create otherwise? 

Edit - My guy...why all this for something that looks so basic?

box-shadow: hsl(var(--amb-light-hue) var(--amb-light-saturation) 0% / calc(var(--_amb-sh-gate) * ((var(--amb-key-light-intensity) - var(--amb-fill-light-intensity)) * .13 + var(--amb-elevation) * -.077 + .368 - var(--_amb-body-gate) * max(0, 1 - var(--amb-elevation)) * .14))) calc(var(--amb-light-x) * (var(--amb-elevation) * 6.8px + var(--amb-thickness) * 3.8px) * -1) calc(var(--amb-light-y) * (var(--amb-elevation) * 6.8px + var(--amb-thickness) * 3.8px) * -1) calc(var(--amb-elevation) * 4.8px + var(--amb-thickness) * 2.7px) .8px, hsl(var(--amb-light-hue) var(--amb-light-saturation) 0% / var(--_amb-sweep-a)) calc(var(--amb-light-x) * (var(--amb-elevation) * 6.8px + var(--amb-thickness) * 2.85px) * -1) calc(var(--amb-light-y) * (var(--amb-elevation) * 6.8px + var(--amb-thickness) * 2.85px) * -1) calc(var(--amb-elevation) * 4.8px + var(--amb-thickness) * 2px) calc(var(--amb-thickness) * .9px), hsl(var(--amb-light-hue) var(--amb-light-saturation) 0% / var(--_amb-sweep-a)) calc(var(--amb-light-x) * (var(--amb-elevation) * 6.8px + var(--amb-thickness) * 1.9px) * -1) calc(var(--amb-light-y) * (var(--amb-elevation) * 6.8px + var(--amb-thickness) * 1.9px) * -1) calc(var(--amb-elevation) * 4.8px + var(--amb-thickness) * 1.35px) calc(var(--amb-thickness) * .6px), hsl(var(--amb-light-hue) var(--amb-light-saturation) 0% / var(--_amb-sweep-a)) calc(var(--amb-light-x) * (var(--amb-elevation) * 6.8px + var(--amb-thickness) * .95px) * -1) calc(var(--amb-light-y) * (var(--amb-elevation) * 6.8px + var(--amb-thickness) * .95px) * -1) calc(var(--amb-elevation) * 4.8px + var(--amb-thickness) * .7px) calc(var(--amb-thickness) * .3px), inset calc(var(--amb-light-x) * var(--_amb-chamfer-w) * -1px) calc(var(--amb-light-y) * var(--_amb-chamfer-w) * -1px) 0 0 hsl(var(--amb-light-hue) var(--amb-light-saturation) 100% / calc(var(--amb-chamfer) * max(0, min(var(--amb-thickness), 1)) * (var(--amb-key-light-intensity) * 1.57 + var(--amb-fill-light-intensity) * .85 - 1.03))), inset calc(var(--amb-light-x) * var(--_amb-chamfer-w) * 1px) calc(var(--amb-light-y) * var(--_amb-chamfer-w) * 1px) 0 0 hsl(var(--amb-light-hue) var(--amb-light-saturation) 0% / calc(var(--amb-chamfer) * max(0, min(var(--amb-thickness), 1)) * ((var(--amb-key-light-intensity) - var(--amb-fill-light-intensity)) * .19 + .24))), inset calc(var(--amb-light-x) * var(--_amb-fillet-w) * -1.4px) calc(var(--amb-light-y) * var(--_amb-fillet-w) * -1.4px) 2px 0 hsl(var(--amb-light-hue) var(--amb-light-saturation) 100% / calc(var(--amb-fillet) * max(0, min(var(--amb-thickness), 1)) * (var(--amb-key-light-intensity) * 1.44 + var(--amb-fill-light-intensity) * .85 - .99))), inset calc(var(--amb-light-x) * var(--_amb-fillet-w) * 1.4px) calc(var(--amb-light-y) * var(--_amb-fillet-w) * 1.4px) 2px 0 hsl(var(--amb-light-hue) var(--amb-light-saturation) 0% / calc(var(--amb-fillet) * max(0, min(var(--amb-thickness), 1)) * ((var(--amb-key-light-intensity) - var(--amb-fill-light-intensity)) * .23 + .3)));}

4

u/Piposhi 12h ago

https://reddit.com/link/p79pjyv/video/tg62k1g8kzmh1/player

That's the formula that makes everything respond to global lighting.

-2

u/creaturefeature16 11h ago

Yeah, I guess I get it. Feels rather overengineered, which LLMs definitely make room for us to do! 😅 Yet I also see what you're trying to replicate. You should send this to https://www.joshwcomeau.com/ because he's an incredible front end guy who is also pretty keen on blender. I think he would appreciate this, even as just a tech demo.

5

u/Piposhi 11h ago

This was my own formula written back in Jan 2022 before LLMs were available - https://github.com/kikkupico/ambientcss/commit/7da648a5513c79a2a37e7e7e31d2dbbc32bb289e#diff-337cc548d34c6d80b758ad814ddd793a9ec155eb664e127c1fc4d8c5abd19b8aR35

Actually, back then, Josh Comeau's article on CSS shadows was one reason I wanted to build this - I thought I can do better and went down a rabbit hole.

0

u/creaturefeature16 11h ago

Gotcha. You mentioned LLMs really helped you here. Which parts? The blender/python translation? 

5

u/Piposhi 11h ago

Pretty much, all the code from the past year was written by LLMs. That helped me build the features I had in mind since 2022 without getting exhausted and giving up.

-4

u/kei_ichi 11h ago

Global lighting from “what”? Do CSS have that feature?

For 99.99% sites out there, I’m pretty sure CSS shadow is more than enough.

4

u/Piposhi 11h ago

The lighting system is explained in detail in the docs here, in case you want to dig deeper - https://kikkupico.github.io/ambientcss/guide/concept/#how-it-works

1

u/thlimythnake 10h ago

Pretty unique, very cool

1

u/_ayushman 3h ago

we're so back

1

u/ActuaryLate9198 3h ago

I’ve experimented with the same thing, updating CSS shadows comes with a huge performance cost for larger elements. I ended up putting the shadows on separate layers so I could manipulate them using filters and transforms instead. Anyway, looks neat, my only gripe is that the components feel distinctly skeumorphic, causing them to clash with the neumorphic base system.

1

u/droctagonapus 1h ago

I need to thank Sam Altman for this, good stuff Sam! I wish I could upvote Sam for his work here

1

u/backwrds 13h ago

If it's "calibrated" with blender, why does it look so different?

Also, I guess skeuomorphism is back now?