I was never able to make this work without waits.
The elements are invisible for maybe 0.5 seconds before becoming visible but the test controller would never wait in these cases. We have some slight animations (mainly those that ship with bootstrap) and those are where the largest amount of "The element that matches the specified selector is not visible" occur.
For example, we have a side bar, that can be activated via a button and contains a search form. It's animated in how it appears on the stage.
await t.typeText(Selector(element), "text") // this always will throw the not visible error
await t.typeText(Selector(element), "text") // this always works
The element is always on the page and just invisible until the animation is complete, yet the test controller never waits for the timeout in this case. It's just something I've come to accept for the time being.