r/reactjs • u/Piposhi • 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!
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?
-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
1
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?
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