Much of this comes down to decoupling different aspects of the system: persistence (DB), domain logic (business rules), delivery mechanism (web, REST), presentation format, UI layout, etc. This aggressive decoupling enables one to isolate and test these different facets of the system individually. Of course, building this decoupling requires some effort, and one has to be careful not to over-engineer. As with many things, it is a balancing act.
And yes, you can do TDD with the UI; separate the UI logic (user actions, UI state, input validation, etc.) from the layout system (and don't try to test the latter automatically). The fun thing is that when the system is decoupled, you can test the user interface of a JavaScript web application without ever starting a real* browser, running a web server or spinning up a database. It is an interesting system design challenge to be able to run even your UI tests at a rate of 100 tests/second.
*A headless browser is not a real browser in this sense