Empty blocks can mean a lot of things, many of them mentioned in the thread here.
Right now the block subsidy significantly outweighs the fees in a block. If miners feel that the additional weight in the block may cause them to lose a block race, they may choose to put very few or no transactions in a block. Over time you imagine this will be untenable as fees becomes a more significant proportion of miner income. The unfortunate side effect of that new paradigm is that if there is not a significant backlog of transactions in the mempool, miners may attempt to "fee snipe" transactions from the tip and re-mine the top most transactions instead of pushing the chain forward. There are some partial mitigations to this in Bitcoin Core by tweaking a transaction's locktime to the current block height so miners are incentivized to push the chain forward to capture fees from these types of transactions.
When it comes to validating a block, depending on the composition of transactions, it can take upwards of a few seconds (even more in some pathological/adversarial scenarios) to complete. Given the miner income ratio right now, it makes sense to mine an empty block before you have fully validated the tip because you can't be sure exactly what block template you can construct without possibly making it invalid (unless the block only includes transactions from the miner or their direct associates in some out-of-band transaction confirmation deal). This has sometimes been called header-first/validation-less or SPV mining, because the miners have only validated the proof of work, not any of the transactions.
There is also Spy mining, where competing pools connect to one another and monitor for discovered blocks. When one of a pool's clients find a block, it immediately publishes the header of that block to all the other clients so it can begin mining right away. In this case if you are a competing pool and hear about this block you will not know its composition, so you have no choice but to tell your clients to mine an empty block on top of it.
The obvious danger with the above strategies is that you are building on an invalid block. While this is exceedingly rare, it can and has happened. Here is a historical example:
https://bitcoin.stackexchange.com/questions/38437/what-is-spv-mining-and-how-did-it-inadvertently-cause-the-fork-after-bip66-waAnother phenomena that may cause near-empty blocks but is largely irrelevant now is covert-ASICBOOST, where miners attempt to compose transactions in the block in a certain way that allows them to increase their efficiency by causing collisions in the midstate of the first chunk of the block header when it is being hashed. In effect they can re-use some of the proof of work. It was already mentioned in the thread, but segwit significantly undermined the covert-ASICBOOST strategy by adding an additional merkle root in the coinbase transaction for witness txids, so the strategy of reordering transactions become more computationally difficult.
I saw a mention of selfish mining in the thread, but to clarify: there are a few different strategies for selfish mining, but the most common one is to withhold a single block that you have mined and wait for a competing pool to discover a block, then force a block race. It is not a common strategy to wait for the selfish pool to mine multiple blocks and then broadcast the chain. That would be highly detectable by the network. As is, we very rarely see "stale" blocks that have lost block races today. When it comes to races, you want your block to propagate as quickly as possible so you will want to be extremely well connected to the rest of the network and also for the block to be as small as possible so it propagates quickly.
Of course there is also the possibility that there are no transactions in the mempool, although that is fairly rare these days. There are even transactions that have been evicted from the mempool that may be stored by some miners in the event that there are no other transactions to mine.