I'm not sure where the notion of fast-enough UI code is coming from - in my experience it's the slowest thing there is.
This. Especially with some popular expensive UI middleware in the industry (you know which one), trying to hit performance targets even with simple UI can be a huge a challenge.
Thankfully, that same middleware has lots of support for offloading various parts of UI updates to other threads, and the remaining bits are trivial to offload if you stop thinking in terms of threads and start thinking in terms of concurrency (e.g., stop trying to share state between threads).
Remember, you don't need to do constant communication between the UI and the game. At most, the game needs to package up any updates to game state that must be reflected in the UI and hand that off as a package to a UI script thread, and the UI script thread has to package up events like "button XYZ clicked" and dispatch those off to the main thread. Two SPSC concurrent queues are a very simple solution there. The UI thread can then update scripts, update layout resulting from scrips or data changes, then run the render to an overlay surface (or just generate a command list to be dispatched to the render thread).