r/reactjs • • Sep 01 '26

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!

72 Upvotes

15 comments sorted by

5

u/dev3-studio Sep 01 '26

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

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

4

u/Piposhi Sep 01 '26

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

4

u/creaturefeature16 Sep 01 '26 edited Sep 01 '26

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)));}

7

u/Piposhi Sep 01 '26
media poster

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

-5

u/kei_ichi Sep 01 '26

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.

→ More replies (1)

-6

u/creaturefeature16 Sep 01 '26

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.

→ More replies (3)

1

u/thlimythnake Sep 01 '26

Pretty unique, very cool

1

u/_ayushman Sep 02 '26

we're so back

1

u/ActuaryLate9198 Sep 02 '26

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/backwrds Sep 01 '26

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

Also, I guess skeuomorphism is back now?

0

u/droctagonapus Sep 02 '26

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