Kanban: card font size doesn't scale with the card #51

Open
opened 2026-04-22 11:26:10 +00:00 by AhmedHanafy725 · 4 comments
Member

Problem

Resizing a kanban grows card rects and column widths, but the text inside them stays fixed (card text fontSize 11, column title fontSize 12). On a large kanban the text looks tiny.

Evidence

  • renderCard(): cardText always fontSize: 11.
  • renderColumn(): colTitle always fontSize: 12.
  • Neither depends on state.cardHeight or state.colWidth.

Fix

  • Derive font sizes from the card height / column width.
  • Card text: fontSize = clamp(round(cardH * 0.25), 9, 20) (tweak factor to taste).
  • Column title: fontSize = clamp(round(colW * 0.06 + 8), 10, 18).
  • Also scale the 3-dot menu font with the card height so it stays visually centered.
  • Recompute these inside renderCard / renderColumn each render.
## Problem Resizing a kanban grows card rects and column widths, but the text inside them stays fixed (card text fontSize 11, column title fontSize 12). On a large kanban the text looks tiny. ## Evidence - `renderCard()`: `cardText` always `fontSize: 11`. - `renderColumn()`: `colTitle` always `fontSize: 12`. - Neither depends on `state.cardHeight` or `state.colWidth`. ## Fix - Derive font sizes from the card height / column width. - Card text: `fontSize = clamp(round(cardH * 0.25), 9, 20)` (tweak factor to taste). - Column title: `fontSize = clamp(round(colW * 0.06 + 8), 10, 18)`. - Also scale the 3-dot menu font with the card height so it stays visually centered. - Recompute these inside `renderCard` / `renderColumn` each render.
Author
Member

Implementation Spec for Issue #51

Objective

Make text inside kanban cards and column titles scale with the user-resizable cardHeight / colWidth state values, so that large kanbans no longer have tiny text. This is a bug fix / UI tweak only — no refactor, no new options, no behavior changes beyond font sizing and positional centering required to keep things visually aligned.

Requirements

  1. cardText font size scales with cardH.
  2. 3-dot menu button (menuBtn) font size scales with cardH (so it stays visually balanced with the card text).
  3. colTitle font size scales with colW.
  4. "+ Add card" button label inside each column scales with colW (same magnitude family as colTitle, but small like its current size).
  5. The following stay at their current hardcoded sizes (intentional non-scaling):
    • Top "Kanban Board" meta titleText (fontSize 11) — meta label, not per-item.
    • "+ Column" button at the far right (fontSize 11) — not column-specific.
    • Column delete × button (fontSize 14) — single glyph, does not need to scale.
  6. Y offsets inside cardGroup for cardText and menuBtn must adjust so the text stays vertically centered / visible at both the 22-min cardH and large cardH values.
  7. At the default colWidth=200 / cardHeight=44, the computed values must equal the current hardcoded values so existing kanbans look identical on first re-render.
  8. No changes outside font sizing + necessary positional offsets. No emojis. No comment churn beyond what the changed lines already carry.

Files to Modify/Create

Modify only:

  • crates/hero_whiteboard_ui/static/web/js/whiteboard/kanban.js

No new files. No changes in any other file.

Implementation Plan

Step 1 — Add a small local helper for clamped rounding

Files: kanban.js

  • Inside the WhiteboardKanban IIFE, near the other var constants at the top (around line 17, just below var cardGap = 6;), add:
    function clampFont(v, min, max) {
        v = Math.round(v);
        if (v < min) return min;
        if (v > max) return max;
        return v;
    }
    
  • Keep it file-scoped and non-exported.

Dependencies: none.

Step 2 — Scale colTitle and the "+ Add card" button font in renderColumn

Files: kanban.js

  • At the top of renderColumn, right after var cy = headerH; (~line 181), compute:
    var titleFs = clampFont(colW * 0.04 + 4, 10, 18);   // 12 at colW=200 (matches current)
    var addFs   = clampFont(colW * 0.035 + 4, 9, 16);   // 11 at colW=200 (matches current)
    
  • colTitle fontSize: 12 → use titleFs.
  • addBtn fontSize: 11 → use addFs.
  • Leave the × delColBtn (fontSize 14) unchanged.
  • Leave colTitle's x/y/width unchanged — the color indicator already re-derives its Y from colTitle.getClientRect() so it will auto-center against the new title height.
  • Leave addBtn's y/width unchanged.

Dependencies: Step 1.

Step 3 — Scale cardText and menuBtn font in renderCard, re-center Y

Files: kanban.js

  • At the top of renderCard, right after var cardY = cy + 28 + cardIdx * (cardH + cardGap);, compute:
    var textFs = clampFont(cardH * 0.25, 9, 20);    // 11 at cardH=44 (matches current)
    var menuFs = clampFont(cardH * 0.32, 10, 24);   // 14 at cardH=44 (matches current)
    var textY  = (cardH <= 48) ? 8 : Math.round((cardH - textFs) / 2);
    var menuY  = (cardH <= 48) ? Math.round((cardH - 14) / 2) : Math.round((cardH - menuFs) / 2);
    
    The cardH <= 48 ? legacy : centered branch preserves exact pixel parity at default (cardH=44: textY=8, menuY=15 — matches current) while vertically centering glyphs on larger cards.
  • cardText x: 8, y: 8x: 8, y: textY.
  • cardText fontSize: 11fontSize: textFs.
  • menuBtn x: colW - 28, y: (cardH - 14) / 2x: colW - 28, y: menuY.
  • menuBtn fontSize: 14fontSize: menuFs.
  • Leave cardText.width: colW - 50, cardRect, and menuHit (cardH - 4) unchanged.

Dependencies: Step 1.

Step 4 — Sanity-check call sites (no code change)

Files: kanban.js (read-only)

  • Confirm renderColumn is only invoked from renderKanban with colW, cardH passed through unchanged.
  • Confirm renderCard is only invoked from renderColumn with colW, cardH passed through unchanged.
  • Confirm state.colWidth / state.cardHeight are the only sources of colW / cardH (already written by the resize / transform handler landed in PR #63).
  • No server-side or sync changes are required: font sizes are derived, never persisted.

Dependencies: Steps 2–3.

Acceptance Criteria

  • renderColumn computes titleFs and addFs from colW and uses them for colTitle.fontSize and addBtn.fontSize.
  • renderCard computes textFs and menuFs from cardH and uses them for cardText.fontSize and menuBtn.fontSize.
  • cardText.y and menuBtn.y follow the cardH <= 48 ? legacy : centered rule.
  • At default colWidth=200 / cardHeight=44, a fresh kanban renders with the same font sizes and positions as before this change (no visual diff on load).
  • Resizing the kanban via the transformer visibly grows the card text, column titles, "+ Add card" label, and 3-dot menu glyph proportionally, up to their clamp ceilings.
  • Shrinking a kanban down to the new minimums (colWidth=100, cardHeight=22) does not produce unreadable text — clamp floors hold.
  • Non-scaling text is preserved: the top "Kanban Board" label, "+ Column" button, and × delete glyph keep their current font sizes.
  • No changes in any file other than kanban.js. No new dependencies. No lint warnings.

Notes

  • Formula calibration is intentional: every formula produces exactly the current hardcoded value at default colWidth=200 / cardHeight=44, so existing kanbans don't visually shift after this patch is deployed:
    • clamp(round(cardH * 0.25), 9, 20) at 44 → 11 (was 11).
    • clamp(round(cardH * 0.32), 10, 24) at 44 → 14 (was 14).
    • clamp(round(colW * 0.04 + 4), 10, 18) at 200 → 12 (was 12).
    • clamp(round(colW * 0.035 + 4), 9, 16) at 200 → 11 (was 11).
  • The issue body suggested colW * 0.06 + 8 — rejected because at colW=200 it produces 20, which would jump the default column title from 12 to 20 on every existing kanban. The tuned colW * 0.04 + 4 preserves default parity.
  • The cardText / menuBtn vertical placement uses a simple cardH <= 48 ? legacy : centered branch rather than one continuous formula. A continuous centering formula would shift every existing card's text by ~8 px on load, which requirement 7 forbids.
  • titleBox measurement for the color indicator (reads colTitle.getClientRect()) automatically adapts to the new colTitle height — no explicit change needed.
  • menuHit (invisible hit rect) is sized at cardH - 4, which accommodates the menu glyph across all clamp bounds. No change needed.
  • Font sizes are derived, never persisted. No sync or schema changes.
  • Scope: no refactor of renderCard / renderColumn signatures, no extraction of font logic into a module, no configuration knobs. Only the lines listed above change.
## Implementation Spec for Issue #51 ### Objective Make text inside kanban cards and column titles scale with the user-resizable `cardHeight` / `colWidth` state values, so that large kanbans no longer have tiny text. This is a bug fix / UI tweak only — no refactor, no new options, no behavior changes beyond font sizing and positional centering required to keep things visually aligned. ### Requirements 1. `cardText` font size scales with `cardH`. 2. 3-dot menu button (`menuBtn`) font size scales with `cardH` (so it stays visually balanced with the card text). 3. `colTitle` font size scales with `colW`. 4. "+ Add card" button label inside each column scales with `colW` (same magnitude family as `colTitle`, but small like its current size). 5. The following stay at their current hardcoded sizes (intentional non-scaling): - Top "Kanban Board" meta `titleText` (fontSize 11) — meta label, not per-item. - "+ Column" button at the far right (fontSize 11) — not column-specific. - Column delete `×` button (fontSize 14) — single glyph, does not need to scale. 6. Y offsets inside `cardGroup` for `cardText` and `menuBtn` must adjust so the text stays vertically centered / visible at both the 22-min `cardH` and large `cardH` values. 7. At the default `colWidth=200` / `cardHeight=44`, the computed values must equal the current hardcoded values so existing kanbans look identical on first re-render. 8. No changes outside font sizing + necessary positional offsets. No emojis. No comment churn beyond what the changed lines already carry. ### Files to Modify/Create Modify only: - `crates/hero_whiteboard_ui/static/web/js/whiteboard/kanban.js` No new files. No changes in any other file. ### Implementation Plan #### Step 1 — Add a small local helper for clamped rounding Files: `kanban.js` - Inside the `WhiteboardKanban` IIFE, near the other `var` constants at the top (around line 17, just below `var cardGap = 6;`), add: ``` function clampFont(v, min, max) { v = Math.round(v); if (v < min) return min; if (v > max) return max; return v; } ``` - Keep it file-scoped and non-exported. Dependencies: none. #### Step 2 — Scale `colTitle` and the "+ Add card" button font in `renderColumn` Files: `kanban.js` - At the top of `renderColumn`, right after `var cy = headerH;` (~line 181), compute: ``` var titleFs = clampFont(colW * 0.04 + 4, 10, 18); // 12 at colW=200 (matches current) var addFs = clampFont(colW * 0.035 + 4, 9, 16); // 11 at colW=200 (matches current) ``` - `colTitle` `fontSize: 12` → use `titleFs`. - `addBtn` `fontSize: 11` → use `addFs`. - Leave the `×` `delColBtn` (fontSize 14) unchanged. - Leave `colTitle`'s x/y/width unchanged — the color indicator already re-derives its Y from `colTitle.getClientRect()` so it will auto-center against the new title height. - Leave `addBtn`'s y/width unchanged. Dependencies: Step 1. #### Step 3 — Scale `cardText` and `menuBtn` font in `renderCard`, re-center Y Files: `kanban.js` - At the top of `renderCard`, right after `var cardY = cy + 28 + cardIdx * (cardH + cardGap);`, compute: ``` var textFs = clampFont(cardH * 0.25, 9, 20); // 11 at cardH=44 (matches current) var menuFs = clampFont(cardH * 0.32, 10, 24); // 14 at cardH=44 (matches current) var textY = (cardH <= 48) ? 8 : Math.round((cardH - textFs) / 2); var menuY = (cardH <= 48) ? Math.round((cardH - 14) / 2) : Math.round((cardH - menuFs) / 2); ``` The `cardH <= 48 ? legacy : centered` branch preserves exact pixel parity at default (`cardH=44`: textY=8, menuY=15 — matches current) while vertically centering glyphs on larger cards. - `cardText` `x: 8, y: 8` → `x: 8, y: textY`. - `cardText` `fontSize: 11` → `fontSize: textFs`. - `menuBtn` `x: colW - 28, y: (cardH - 14) / 2` → `x: colW - 28, y: menuY`. - `menuBtn` `fontSize: 14` → `fontSize: menuFs`. - Leave `cardText.width: colW - 50`, `cardRect`, and `menuHit` (`cardH - 4`) unchanged. Dependencies: Step 1. #### Step 4 — Sanity-check call sites (no code change) Files: `kanban.js` (read-only) - Confirm `renderColumn` is only invoked from `renderKanban` with `colW, cardH` passed through unchanged. - Confirm `renderCard` is only invoked from `renderColumn` with `colW, cardH` passed through unchanged. - Confirm `state.colWidth` / `state.cardHeight` are the only sources of `colW` / `cardH` (already written by the resize / transform handler landed in PR #63). - No server-side or sync changes are required: font sizes are derived, never persisted. Dependencies: Steps 2–3. ### Acceptance Criteria - [ ] `renderColumn` computes `titleFs` and `addFs` from `colW` and uses them for `colTitle.fontSize` and `addBtn.fontSize`. - [ ] `renderCard` computes `textFs` and `menuFs` from `cardH` and uses them for `cardText.fontSize` and `menuBtn.fontSize`. - [ ] `cardText.y` and `menuBtn.y` follow the `cardH <= 48 ? legacy : centered` rule. - [ ] At default `colWidth=200` / `cardHeight=44`, a fresh kanban renders with the same font sizes and positions as before this change (no visual diff on load). - [ ] Resizing the kanban via the transformer visibly grows the card text, column titles, "+ Add card" label, and 3-dot menu glyph proportionally, up to their clamp ceilings. - [ ] Shrinking a kanban down to the new minimums (`colWidth=100`, `cardHeight=22`) does not produce unreadable text — clamp floors hold. - [ ] Non-scaling text is preserved: the top "Kanban Board" label, "+ Column" button, and `×` delete glyph keep their current font sizes. - [ ] No changes in any file other than `kanban.js`. No new dependencies. No lint warnings. ### Notes - Formula calibration is intentional: every formula produces exactly the current hardcoded value at default `colWidth=200` / `cardHeight=44`, so existing kanbans don't visually shift after this patch is deployed: - `clamp(round(cardH * 0.25), 9, 20)` at 44 → 11 (was 11). - `clamp(round(cardH * 0.32), 10, 24)` at 44 → 14 (was 14). - `clamp(round(colW * 0.04 + 4), 10, 18)` at 200 → 12 (was 12). - `clamp(round(colW * 0.035 + 4), 9, 16)` at 200 → 11 (was 11). - The issue body suggested `colW * 0.06 + 8` — rejected because at `colW=200` it produces 20, which would jump the default column title from 12 to 20 on every existing kanban. The tuned `colW * 0.04 + 4` preserves default parity. - The `cardText` / `menuBtn` vertical placement uses a simple `cardH <= 48 ? legacy : centered` branch rather than one continuous formula. A continuous centering formula would shift every existing card's text by ~8 px on load, which requirement 7 forbids. - `titleBox` measurement for the color indicator (reads `colTitle.getClientRect()`) automatically adapts to the new `colTitle` height — no explicit change needed. - `menuHit` (invisible hit rect) is sized at `cardH - 4`, which accommodates the menu glyph across all clamp bounds. No change needed. - Font sizes are derived, never persisted. No sync or schema changes. - Scope: no refactor of `renderCard` / `renderColumn` signatures, no extraction of font logic into a module, no configuration knobs. Only the lines listed above change.
Author
Member

Test Results

JavaScript-only change in kanban.js; Rust workspace validated for regressions.

Check Result
cargo check --workspace pass
cargo test --workspace --lib pass (0 executed)
cargo clippy --workspace -- -D warnings pass
cargo fmt --check pass

No Rust tests cover the JS whiteboard modules, so the suite confirms regression safety on the Rust side. Manual verification against a running UI is needed for the font-scaling criteria listed in the spec's Acceptance Criteria.

## Test Results JavaScript-only change in `kanban.js`; Rust workspace validated for regressions. | Check | Result | |---|---| | `cargo check --workspace` | pass | | `cargo test --workspace --lib` | pass (0 executed) | | `cargo clippy --workspace -- -D warnings` | pass | | `cargo fmt --check` | pass | No Rust tests cover the JS whiteboard modules, so the suite confirms regression safety on the Rust side. Manual verification against a running UI is needed for the font-scaling criteria listed in the spec's Acceptance Criteria.
Author
Member

Implementation Summary

Kanban text inside columns and cards now scales with colWidth / cardHeight.

Files changed

  • crates/hero_whiteboard_ui/static/web/js/whiteboard/kanban.js (+21 / -6)

Changes

  • Added a file-local clampFont(v, min, max) helper that rounds and clamps a derived font size.
  • renderColumn: computes titleFs = clampFont(colW * 0.04 + 4, 10, 18) and addFs = clampFont(colW * 0.035 + 4, 9, 16); colTitle.fontSize and addBtn.fontSize use them.
  • renderCard: computes textFs = clampFont(cardH * 0.25, 9, 20) and menuFs = clampFont(cardH * 0.32, 10, 24); cardText.fontSize and menuBtn.fontSize use them.
  • Vertical placement of cardText and menuBtn uses cardH <= 48 ? legacy : centered so default cards keep pixel-exact parity (textY=8, menuY=15 at cardH=44) while larger cards center their glyphs.

Default parity

At colWidth=200 / cardHeight=44 the new formulas produce exactly the previous hardcoded values (12, 11, 11, 14 for title / add-button / card-text / menu-glyph), so existing kanbans render identically on first load.

Out of scope

  • Non-scaling text is intentionally preserved: the top "Kanban Board" meta label, the right-side "+ Column" button, and the per-column × delete glyph.
  • No changes to state schema, sync, history, or any other file. Font sizes are derived per-render, never persisted.

Test results

  • cargo check --workspace: pass
  • cargo test --workspace --lib: pass
  • cargo clippy --workspace -- -D warnings: pass
  • cargo fmt --check: pass
## Implementation Summary Kanban text inside columns and cards now scales with `colWidth` / `cardHeight`. ### Files changed - `crates/hero_whiteboard_ui/static/web/js/whiteboard/kanban.js` (+21 / -6) ### Changes - Added a file-local `clampFont(v, min, max)` helper that rounds and clamps a derived font size. - `renderColumn`: computes `titleFs = clampFont(colW * 0.04 + 4, 10, 18)` and `addFs = clampFont(colW * 0.035 + 4, 9, 16)`; `colTitle.fontSize` and `addBtn.fontSize` use them. - `renderCard`: computes `textFs = clampFont(cardH * 0.25, 9, 20)` and `menuFs = clampFont(cardH * 0.32, 10, 24)`; `cardText.fontSize` and `menuBtn.fontSize` use them. - Vertical placement of `cardText` and `menuBtn` uses `cardH <= 48 ? legacy : centered` so default cards keep pixel-exact parity (`textY=8`, `menuY=15` at `cardH=44`) while larger cards center their glyphs. ### Default parity At `colWidth=200` / `cardHeight=44` the new formulas produce exactly the previous hardcoded values (12, 11, 11, 14 for title / add-button / card-text / menu-glyph), so existing kanbans render identically on first load. ### Out of scope - Non-scaling text is intentionally preserved: the top "Kanban Board" meta label, the right-side "+ Column" button, and the per-column `×` delete glyph. - No changes to state schema, sync, history, or any other file. Font sizes are derived per-render, never persisted. ### Test results - `cargo check --workspace`: pass - `cargo test --workspace --lib`: pass - `cargo clippy --workspace -- -D warnings`: pass - `cargo fmt --check`: pass
Author
Member

Pull request opened: #64

This PR implements the changes discussed in this issue.

Pull request opened: https://forge.ourworld.tf/lhumina_code/hero_whiteboard/pulls/64 This PR implements the changes discussed in this issue.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_whiteboard#51
No description provided.