Column Pinning

Fixes columns while horizontally scrolling.

Wide order table
Order Customer Status Method Placed Notes Total
#1042A. WhitfieldShippedCourier12 AugLeave with neighbour if out£64.00
#1041R. OkaforProcessingPost13 AugGift wrap requested£128.50
#1040M. IqbalShippedCourier13 AugSignature required£22.00
#1039S. NovakRefundedPost14 AugDamaged in transit£40.00

html

<div class="cpin cpin--shadow" data-frozen="1" style="--accent:#241430;">
  <div class="cpin__scroll">
    <table class="cpin__table" style="min-width:720px;">
      <caption>Wide order table</caption>
      <thead><tr>
        <th scope="col" class="cpin__c1">Order</th>
        <th scope="col" class="cpin__c2">Customer</th>
        <th scope="col">Status</th>
        <th scope="col">Method</th>
        <th scope="col">Placed</th>
        <th scope="col">Notes</th>
        <th scope="col">Total</th>
      </tr></thead>
      <tbody>
        <tr><td class="cpin__c1">#1042</td><td class="cpin__c2">A. Whitfield</td><td>Shipped</td><td>Courier</td><td>12 Aug</td><td>Leave with neighbour if out</td><td>£64.00</td></tr>
        <tr><td class="cpin__c1">#1041</td><td class="cpin__c2">R. Okafor</td><td>Processing</td><td>Post</td><td>13 Aug</td><td>Gift wrap requested</td><td>£128.50</td></tr>
        <tr><td class="cpin__c1">#1040</td><td class="cpin__c2">M. Iqbal</td><td>Shipped</td><td>Courier</td><td>13 Aug</td><td>Signature required</td><td>£22.00</td></tr>
        <tr><td class="cpin__c1">#1039</td><td class="cpin__c2">S. Novak</td><td>Refunded</td><td>Post</td><td>14 Aug</td><td>Damaged in transit</td><td>£40.00</td></tr>
      </tbody>
    </table>
  </div>
</div>
<style>
.cpin { font: 14px system-ui, sans-serif; color: #241430; background: #FAF7F2; max-width: 480px; border: 1px solid #24143020; border-radius: 10px; }
.cpin__scroll { overflow-x: auto; }
.cpin__table { border-collapse: collapse; width: 100%; }
.cpin__table caption { text-align: left; font-weight: 600; padding: 10px 14px; }
.cpin__table th, .cpin__table td { padding: 10px 14px; text-align: left; border-bottom: 1px solid #24143020; white-space: nowrap; background: #FAF7F2; }
.cpin__table th { background: var(--accent); color: #FAF7F2; }
.cpin[data-frozen="1"] .cpin__c1, .cpin[data-frozen="2"] .cpin__c1 { position: sticky; left: 0; z-index: 2; }
.cpin[data-frozen="2"] .cpin__c2 { position: sticky; left: 90px; z-index: 2; }
.cpin--shadow[data-frozen="1"] .cpin__c1,
.cpin--shadow[data-frozen="2"] .cpin__c2 { box-shadow: 4px 0 6px -4px #24143066; }
</style>

tailwind

<div class="max-w-[480px] rounded-[10px] border border-[#24143020] font-sans text-sm text-[#241430] bg-[#FAF7F2]" data-frozen="1" style="--accent:#241430;">
  <div class="overflow-x-auto">
    <table class="border-collapse w-full" style="min-width:720px;">
      <caption class="text-left font-semibold px-3.5 py-2.5">Wide order table</caption>
      <thead><tr class="text-[#FAF7F2]" style="background:#241430">
        <th scope="col" class="cpin__c1 p-3.5 text-left whitespace-nowrap">Order</th>
        <th scope="col" class="cpin__c2 p-3.5 text-left whitespace-nowrap">Customer</th>
        <th scope="col" class="p-3.5 text-left whitespace-nowrap">Status</th>
        <th scope="col" class="p-3.5 text-left whitespace-nowrap">Method</th>
        <th scope="col" class="p-3.5 text-left whitespace-nowrap">Placed</th>
        <th scope="col" class="p-3.5 text-left whitespace-nowrap">Notes</th>
        <th scope="col" class="p-3.5 text-left whitespace-nowrap">Total</th>
      </tr></thead>
      <tbody>
        <tr><td class="cpin__c1 bg-[#FAF7F2] p-3.5 border-b border-[#24143020] whitespace-nowrap">#1042</td><td class="cpin__c2 bg-[#FAF7F2] p-3.5 border-b border-[#24143020] whitespace-nowrap">A. Whitfield</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Shipped</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Courier</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">12 Aug</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Leave with neighbour if out</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">£64.00</td></tr>
        <tr><td class="cpin__c1 bg-[#FAF7F2] p-3.5 border-b border-[#24143020] whitespace-nowrap">#1041</td><td class="cpin__c2 bg-[#FAF7F2] p-3.5 border-b border-[#24143020] whitespace-nowrap">R. Okafor</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Processing</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Post</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">13 Aug</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Gift wrap requested</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">£128.50</td></tr>
        <tr><td class="cpin__c1 bg-[#FAF7F2] p-3.5 border-b border-[#24143020] whitespace-nowrap">#1040</td><td class="cpin__c2 bg-[#FAF7F2] p-3.5 border-b border-[#24143020] whitespace-nowrap">M. Iqbal</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Shipped</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Courier</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">13 Aug</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Signature required</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">£22.00</td></tr>
        <tr><td class="cpin__c1 bg-[#FAF7F2] p-3.5 border-b border-[#24143020] whitespace-nowrap">#1039</td><td class="cpin__c2 bg-[#FAF7F2] p-3.5 border-b border-[#24143020] whitespace-nowrap">S. Novak</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Refunded</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Post</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">14 Aug</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">Damaged in transit</td><td class="p-3.5 border-b border-[#24143020] whitespace-nowrap">£40.00</td></tr>
      </tbody>
    </table>
  </div>
</div>
<style>
[data-frozen="1"] .cpin__c1, [data-frozen="2"] .cpin__c1 { position: sticky; left: 0; z-index: 2; }
[data-frozen="2"] .cpin__c2 { position: sticky; left: 90px; z-index: 2; }
[data-frozen="1"] .cpin__c1, [data-frozen="2"] .cpin__c2 { box-shadow: 4px 0 6px -4px #24143066; }
</style>

react

function ColumnPinning({
  caption = "Wide order table",
  accent = "#241430",
  frozen = "1",
  shadow = true,
  tableWidth = 720,
}) {
  const cols = ["Order", "Customer", "Status", "Method", "Placed", "Notes", "Total"];
  const rows = [
    ["#1042", "A. Whitfield", "Shipped", "Courier", "12 Aug", "Leave with neighbour if out", "£64.00"],
    ["#1041", "R. Okafor", "Processing", "Post", "13 Aug", "Gift wrap requested", "£128.50"],
    ["#1040", "M. Iqbal", "Shipped", "Courier", "13 Aug", "Signature required", "£22.00"],
    ["#1039", "S. Novak", "Refunded", "Post", "14 Aug", "Damaged in transit", "£40.00"],
  ];
  const n = Number(frozen);
  const left = [0, 90];
  const cellStyle = (i, bg) => {
    const pinned = i < n;
    return {
      padding: "10px 14px", whiteSpace: "nowrap", borderBottom: "1px solid #24143020", background: bg,
      position: pinned ? "sticky" : "static", left: pinned ? left[i] : undefined, zIndex: pinned ? 2 : undefined,
      boxShadow: pinned && shadow && i === n - 1 ? "4px 0 6px -4px #24143066" : undefined,
    };
  };
  return (
    <div style={{ maxWidth: 480, borderRadius: 10, border: "1px solid #24143020", font: "14px system-ui, sans-serif", color: "#241430", background: "#FAF7F2" }}>
      <div style={{ overflowX: "auto" }}>
        <table style={{ borderCollapse: "collapse", width: "100%", minWidth: tableWidth }}>
          <caption style={{ textAlign: "left", fontWeight: 600, padding: "10px 14px" }}>{caption}</caption>
          <thead>
            <tr>
              {cols.map((c, i) => <th key={c} scope="col" style={cellStyle(i, accent)}>{i < n ? c : c}</th>)}
            </tr>
          </thead>
          <tbody>
            {rows.map((row) => (
              <tr key={row[0]}>{row.map((cell, i) => <td key={i} style={cellStyle(i, "#FAF7F2")}>{cell}</td>)}</tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}