Is there a recommended way to set custom DNS (over TLS / HTTPS) servers globally *that will not break captive portal logins?*

Currently I use the ‘drop in’ method for systemd-resolved, but it breaks captive portal logins, and is tedious to undo and redo each time I need to login at a cafe or something.

edit: or if there is no straightforward way to do this globally, is there another approach that would make sense?

I think you cannot completely trust any captive portal as it can be compromised or just be a honeypot that looks like the real thing but actively tampers your traffic.

They may block DoH and DoT clients running in opportunistic mode to make them degrade to plain DNS and then utilize DNS hijacking.

A possible workaround is using a script to temporarily disable encrypted DNS or switch it to opportunistic mode, then revert the changes after a few seconds and flush the DNS cache:

resolvectl dnsovertls NET_IFACE opportunistic
sleep 20
resolvectl revert NET_IFACE
resolvectl flush-caches

You can try to invoke it automatically with NetworkManager dispatcher:
NetworkManager-dispatcher: NetworkManager Reference Manual

Also consider asking the NetworkManager developers directly:
Issues · NetworkManager / NetworkManager · GitLab