diff --git a/web/components/bets-list.tsx b/web/components/bets-list.tsx
index 0a050596..4dd0fffb 100644
--- a/web/components/bets-list.tsx
+++ b/web/components/bets-list.tsx
@@ -53,6 +53,7 @@ import {
 import { safeLocalStorage } from 'web/lib/util/local'
 import { ExclamationIcon } from '@heroicons/react/outline'
 import { Select } from './select'
+import { Table } from './table'
 
 type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
 type BetFilter = 'open' | 'limit_bet' | 'sold' | 'closed' | 'resolved' | 'all'
@@ -450,7 +451,7 @@ export function ContractBetsTable(props: {
         </>
       )}
 
-      <table className="table-zebra table-compact table w-full text-gray-500">
+      <Table>
         <thead>
           <tr className="p-2">
             <th></th>
@@ -479,7 +480,7 @@ export function ContractBetsTable(props: {
             />
           ))}
         </tbody>
-      </table>
+      </Table>
     </div>
   )
 }
diff --git a/web/components/contract/contract-info-dialog.tsx b/web/components/contract/contract-info-dialog.tsx
index a41be451..cf38c294 100644
--- a/web/components/contract/contract-info-dialog.tsx
+++ b/web/components/contract/contract-info-dialog.tsx
@@ -21,6 +21,7 @@ import { Row } from '../layout/row'
 import { BETTORS, User } from 'common/user'
 import { Button } from '../button'
 import { AddLiquidityButton } from './add-liquidity-button'
+import { Table } from '../table'
 
 export const contractDetailsButtonClassName =
   'group flex items-center rounded-md px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-100 text-gray-400 hover:text-gray-500'
@@ -99,8 +100,7 @@ export function ContractInfoDialog(props: {
       <Modal open={open} setOpen={setOpen}>
         <Col className="gap-4 rounded bg-white p-6">
           <Title className="!mt-0 !mb-0" text="This Market" />
-
-          <table className="table-compact table-zebra table w-full text-gray-500">
+          <Table>
             <tbody>
               <tr>
                 <td>Type</td>
@@ -239,7 +239,7 @@ export function ContractInfoDialog(props: {
                 </tr>
               )}
             </tbody>
-          </table>
+          </Table>
 
           <Row className="flex-wrap">
             {mechanism === 'cpmm-1' && (
diff --git a/web/components/leaderboard.tsx b/web/components/leaderboard.tsx
index 1035e9d1..32caead9 100644
--- a/web/components/leaderboard.tsx
+++ b/web/components/leaderboard.tsx
@@ -2,6 +2,7 @@ import clsx from 'clsx'
 import { Avatar } from './avatar'
 import { Row } from './layout/row'
 import { SiteLink } from './site-link'
+import { Table } from './table'
 import { Title } from './title'
 
 interface LeaderboardEntry {
@@ -31,9 +32,9 @@ export function Leaderboard<T extends LeaderboardEntry>(props: {
         <div className="ml-2 text-gray-500">None yet</div>
       ) : (
         <div className="overflow-x-auto">
-          <table className="table-zebra table-compact table w-full text-gray-500">
+          <Table>
             <thead>
-              <tr className="p-2">
+              <tr>
                 <th>#</th>
                 <th>Name</th>
                 {columns.map((column) => (
@@ -59,7 +60,7 @@ export function Leaderboard<T extends LeaderboardEntry>(props: {
                 </tr>
               ))}
             </tbody>
-          </table>
+          </Table>
         </div>
       )}
     </div>
diff --git a/web/components/limit-bets.tsx b/web/components/limit-bets.tsx
index 254962cb..a5abe803 100644
--- a/web/components/limit-bets.tsx
+++ b/web/components/limit-bets.tsx
@@ -14,6 +14,7 @@ import { Row } from './layout/row'
 import { LoadingIndicator } from './loading-indicator'
 import { BinaryOutcomeLabel, PseudoNumericOutcomeLabel } from './outcome-label'
 import { Subtitle } from './subtitle'
+import { Table } from './table'
 import { Title } from './title'
 
 export function LimitBets(props: {
@@ -74,7 +75,7 @@ export function LimitOrderTable(props: {
   const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC'
 
   return (
-    <table className="table-compact table w-full rounded text-gray-500">
+    <Table className="rounded">
       <thead>
         <tr>
           {!isYou && <th></th>}
@@ -89,7 +90,7 @@ export function LimitOrderTable(props: {
           <LimitBet key={bet.id} bet={bet} contract={contract} isYou={isYou} />
         ))}
       </tbody>
-    </table>
+    </Table>
   )
 }
 
diff --git a/web/components/table.tsx b/web/components/table.tsx
new file mode 100644
index 00000000..76bfc68c
--- /dev/null
+++ b/web/components/table.tsx
@@ -0,0 +1,21 @@
+import clsx from 'clsx'
+
+/** `<table>` with styles. Expects table html (`<thead>`, `<td>` etc) */
+export const Table = (props: {
+  zebra?: boolean
+  className?: string
+  children: React.ReactNode
+}) => {
+  const { className, children } = props
+
+  return (
+    <table
+      className={clsx(
+        'w-full whitespace-nowrap text-left text-sm text-gray-500 [&_td]:p-2 [&_th]:p-2 [&>thead]:font-bold [&>tbody_tr:nth-child(odd)]:bg-white',
+        className
+      )}
+    >
+      {children}
+    </table>
+  )
+}