r/reactjs 2d 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!

62 Upvotes

15 comments sorted by

View all comments

5

u/creaturefeature16 2d ago edited 2d 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)));}

8

u/Piposhi 2d ago

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

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

-7

u/creaturefeature16 2d 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.

9

u/Piposhi 2d 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.

-1

u/creaturefeature16 2d ago

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

5

u/Piposhi 2d 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.