>>
Column Layouts in the EditorUsers can't place content side by side in the editor. We're adding a /Columns command that creates horizontal layouts by extending the existing block system — no new block types, no backend changes.

    Problem

      Our editor forces all content into a single vertical stack. Every block — paragraphs, headings, images, code — sits directly above or below the next one. There is no way for users to place content side by side.

      This is a significant limitation. Users regularly need multi-column layouts for:

        Comparisons: two approaches, before/after, pros/cons

        Mixed media: text alongside an image or embed

        Structured sections: splitting a page into logically distinct areas

        Information density: making better use of wide screens instead of leaving margins empty

      Competing editors (Notion, Google Docs, Coda) all support columns or similar horizontal layout. Users who come from these tools expect it. Without columns, users resort to workarounds like embedding tables (that de do not support either) or creating images, both of which break the editing experience.

    Solution

      Add a column layout feature that lets users split content into side-by-side columns directly in the editor.

      How it works for the user:

        Type /Columns in the slash menu

        A 2-column layout appears where the current block is

        Each column accepts any kind of block: paragraphs, headings, images, code, embeds — anything the editor already supports

        Multiple blocks can be added inside each column (it's not limited to one block per column)

        Column widths are adjustable via relative proportions (e.g., one column wider than the other)

      How it works technically:

        We don't introduce new block types. Instead, we extend the existing block structure by adding a new childrenType value: ColumnList. When a parent block has childrenType: ColumnList, its children render side by side (flex row) instead of stacking vertically. Each child block gets a columnWidth attribute (a relative number like 1.0, 1.5) that controls how much horizontal space it occupies.

        2

        This reuses the exact same nesting mechanism already in place for ordered lists, unordered lists, and blockquotes — making it architecturally clean and compatible with the existing data model, conversions, and rendering pipelines.

    Scope

      Core feature (this project):

        Slash menu creation (/Columns → 2 equal columns)

        Any block type inside columns, multiple blocks per column

        Column width stored as relative numbers

        Works in both editor and read-only views

        Data model compatible with existing block structure (no new block types)

        Conversion round-trips (HMBlock ↔ EditorBlock) fully working and tested

      Estimated effort: ~1 week for the remaining work (resize handles, edge cases, keyboard nav, polish).

    Rabbit Holes

      Drag-to-side creation: Dragging a block to the left/right edge of another block to automatically create a column layout. This is a great UX pattern (Notion does it), but it touches deep ProseMirror drag-and-drop mechanics and is a significant effort on its own. Should be a follow-up project.

      Column presets: Offering templates like "2 equal", "1/3 + 2/3", "3 columns" in the slash menu or a popover. Nice to have but adds UI complexity. Start with 2 equal columns and let users resize manually.

      CSS attr() for column widths: Using --column-width: attr(data-column-width number, 1) would be elegant but has limited browser support. We use inline styles (flex: <width> 1 0%) instead — less elegant, works everywhere.

      Column gap control: Letting users adjust the gap between columns. Hardcoded at 16px for now. Could be a setting later.

      Column backgrounds/borders: Styling individual columns with background colors or separator lines. Related but separate concern — can layer on top later.

      Paste normalization: When pasting content that contains column structures from external sources. The existing paste normalization in BlockGroup handles basic cases, but complex column structures from Notion or Google Docs would need dedicated handling.

    No Gos

      Nested columns: Columns inside columns. This creates deeply nested structures that are hard to navigate and edit. Deliberately prevented. Users should use different layout approaches if they need more complex grids.

      CSS Grid / arbitrary grid layouts: This project is specifically about columns (1-dimensional horizontal split). True 2D grid layouts (rows + columns) are a much larger feature that would require a fundamentally different data model.

      Backend/protocol changes: The ColumnList childrenType and columnWidth attribute work within the existing HMBlock protobuf schema. We don't add new protobuf messages or fields — just new attribute values on existing block attributes.

      Column-level settings: Per-column background color, padding, vertical alignment, or other styling. Keep columns purely structural for now.

      Responsive breakpoint control: Letting users choose at which screen width columns stack. We pick a sensible default (stack below 768px in read-only view) and don't expose this as a setting.

      Undo/redo edge cases: Column creation/deletion with complex undo history. ProseMirror's transaction system handles basic undo, but multi-step column operations (create columns → move content → resize) may have rough undo edges. We accept this for now rather than building custom undo logic.

    Photo by Jesse Bauer on Unsplash