When a dirty page (i.e. one updated by a transaction) is written to disk is independent of when the tlog records for that transaction are written.
The buffer manager is free to evict a page whenever it needs, even one dirtied by one or more transactions.
To commit a transaction the system need not persist all the pages it dirtied. Ensuring the tlog records are persisted is sufficient.
Together these are known as a "steal /no force" policy. It has advantages over the alternatives and is widely adopted.
A database-wide checkpoint can occur at any time persisting all dirtied pages to disk. Those from committed transactions and those from open transactions both can be persisted.
Tlog records from all transactions are held in memory in a single buffer. When it is persisted the whole buffer is persisted, including records from open transactions. It is only after a transaction's commit record is acknowledged as having been persisted that the client can be informed.
This all is coreographed by the ARIES protocol. It dictates what metadata is captured, when, and the order in which processes occur. Is has more requirements than I have mentioned here.
As an aside it is possible that the target page of a write operation is never in memory during the life of a transaction. Consider an insert to a table with no constraints. There is no need to compare the new data to existing data to check for conflicts. So why read any pages into memory, even the one that will hold the new row? Persisting the log records and allowing lazy update of the data page would work. I don't believe SQL Server actually works this way but it's interesting to consider.