Compound used uint96 as an optimization to pack multiple values into the same storage slot. They could do this because the Comp token has a supply cap of 10 million tokens (with 18 decimals). OpenZeppelin Contracts, however, offers a generic ERC20 implementation and our users can create tokens with much higher supply or even dynamic supply that can't risk overflowing a uint96 number. So we couldn't use such a small limit.
Some optimization was important to us though so we used uint224 in order to pack the number of votes and the block number in a single slot. The number is 256 - 32, where 32 bits are necessary to store a block number.
What the further optimization implemented by Comp achieves is to pack two Checkpoints in a single storage slot. This will make every other transfer a little cheaper because the current and new checkpoints share the storage slot so it is already warm on the second access.
getPastVotes() return uint256 because we see the uint224 type as an internal implementation detail. Another contract could implement the same interface in a different way and potentially use the full 256 bits.
Note that Comp has
getPriorVotes with a return type of uint96. We would've loved to use the same function names in order to maximize compatibility, but the small return type made this impossible because Solidity would (silently!) truncate all bits past the 96th if someone invoked a function from ERC20Votes thinking it followed the Comp interface. We think the right thing would've been for Comp to return uint256, and so we did that as well.