---
# System prepended metadata

title: o3-on-handling-markdown-table-styling

---

### What’s really happening under the hood

1. **The browser’s “auto” table-layout algorithm**
   By default (`table-layout: auto`) every column’s width is negotiated between:

   * **min-content width** – the narrowest the column can get if the text wraps at every possible break.
   * **max-content width** – the width of the longest unbreakable piece (a long URL, a `superLongVariableName`, etc.).
     The browser tries to fit the table inside the available block, but if a scroll container (`overflow-x:auto`) says *“don’t worry, the table can spill out horizontally”*, the algorithm happily lets the table grow until it hits something closer to the max-content widths. That’s why you don’t end up with 2-word columns but you also don’t get a single **100-word-wide** column: the browser lands somewhere between the two extremes.

2. **OpenAI’s tweak**
   ChatGPT wraps each `<table>` in a div that scrolls **and** sets a *reasonable cap* on every cell:

   ```css
   td, th {
     max-width: 40ch;          /* ~40 characters before wrapping */
     white-space: normal;      /* allow wrapping */
     overflow-wrap: anywhere;  /* break long URLs/words */
   }
   ```

   *Short cells stay short, long paragraphs wrap after \~40 characters, and horizontal scroll still kicks in once the total table width exceeds the viewport.*

---

### Bring that behaviour to your Next.js / Docusaurus site

#### 1. Global CSS you can drop in today

```css
/* 1️⃣  Make tables scroll when needed */
table {
  display: block;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* 2️⃣  Let rows grow naturally, but stop cells from getting ridiculous */
table td,
table th {
  max-width: 40ch;        /* tweak to taste: 35–50ch feels good */
  white-space: normal;    /* wrap as needed */
  overflow-wrap: anywhere;
  /* optional polish */
  padding: 0.4rem 0.8rem;
  vertical-align: top;
}
```

*Result:* long prose wraps after \~40 characters, URLs break when they have to, and the whole table scrolls horizontally once its total width tops the viewport.

#### 2. A reusable MDX component (the nice-to-maintain way)

```tsx
// components/ScrollableTable.tsx
export default function ScrollableTable(
  props: React.TableHTMLAttributes<HTMLTableElement>
) {
  return (
    <div className="overflow-x-auto my-4 -mx-4">
      <table {...props} className="min-w-full border-collapse prose-table" />
    </div>
  );
}
```

Then shadow/override the `<table>` element just once:

* **Next.js (with `@mdx-js/react` or `next-mdx-remote`)**

  ```tsx
  import { MDXProvider } from '@mdx-js/react';
  import ScrollableTable from '@/components/ScrollableTable';

  const components = { table: ScrollableTable };

  export default function MyApp({ Component, pageProps }) {
    return (
      <MDXProvider components={components}>
        <Component {...pageProps} />
      </MDXProvider>
    );
  }
  ```

* **Docusaurus v2**

  ```jsx
  // src/theme/MDXComponents.js
  import React from 'react';
  import MDXComponents from '@theme-original/MDXComponents';
  import ScrollableTable from '@site/src/components/ScrollableTable';

  export default {
    ...MDXComponents,
    table: ScrollableTable,
  };
  ```

Add the same CSS from the previous step (or Tailwind equivalents like `max-w-[40ch]` and `break-words`) and every markdown table now behaves like ChatGPT’s—no matter who writes the docs.

---

### Fine-tuning knobs you can play with

| What you want                           | Tweak                                                                                                                                      |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Narrower / wider wrap**               | Change `max-width: 35ch` … `50ch` or use `em` units.                                                                                       |
| **Keep header sticky**                  | In the scroll wrapper add `position: relative`; then:<br>`thead th { position: sticky; top: 0; background: var(--ifm-background-color); }` |
| **Don’t wrap code blocks inside cells** | `td code { white-space: nowrap; }`                                                                                                         |
| **A11y for keyboard users**             | Give the wrapper `role="region"` and an `aria-labelledby` linking to a `<caption>` or heading so screen-reader users know they can scroll. |

---

**TL;DR**
Put your table inside a horizontal-scroll wrapper **and** cap each cell’s width (`max-width: 40ch; overflow-wrap:anywhere;`). That yields the same “sensible middle ground” ChatGPT uses—easy to scan, impossible to break your layout.
