auto-claude: 202-fix-kanban-board-scaling-collisions (#1731)

* auto-claude: subtask-1-1 - Add rem conversion helper and update width constants

- Add BASE_FONT_SIZE constant (16) for rem conversion
- Export pxToRem helper function for converting pixels to rem strings
- Add rem-formatted width constants that scale with UI:
  - DEFAULT_COLUMN_WIDTH_REM (20rem)
  - MIN_COLUMN_WIDTH_REM (11.25rem)
  - MAX_COLUMN_WIDTH_REM (37.5rem)
  - COLLAPSED_COLUMN_WIDTH_REM (3rem)
- Keep existing pixel constants unchanged for backward compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* auto-claude: subtask-1-2 - Update KanbanBoard.tsx column styles to use rem-ba

Convert column width styles from pixel values to rem units for UI scale
compatibility:
- Import pxToRem function and rem constants from kanban-settings-store
- Use COLLAPSED_COLUMN_WIDTH_REM for collapsed column width styles
- Convert columnWidth (stored as px) to rem using pxToRem() for rendering
- Use MIN_COLUMN_WIDTH_REM and MAX_COLUMN_WIDTH_REM for expanded column bounds

This ensures Kanban board column widths scale properly with the UI scale
system, preventing collisions and layout issues at different zoom levels.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: scale resize deltaX by root font-size and remove unused import

Divide mouse deltaX by the actual UI scale factor (root font-size /
BASE_FONT_SIZE) so column resize drag tracks 1:1 with the cursor at
non-100% UI scales. Remove unused COLLAPSED_COLUMN_WIDTH import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Test User <test@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Andy
2026-02-09 10:25:33 +01:00
committed by GitHub
parent 087091cef8
commit d09ebb8504
2 changed files with 35 additions and 4 deletions
@@ -31,7 +31,7 @@ import { TASK_STATUS_COLUMNS, TASK_STATUS_LABELS } from '../../shared/constants'
import { cn } from '../lib/utils';
import { persistTaskStatus, forceCompleteTask, archiveTasks, deleteTasks, useTaskStore } from '../stores/task-store';
import { updateProjectSettings, useProjectStore } from '../stores/project-store';
import { useKanbanSettingsStore, COLLAPSED_COLUMN_WIDTH, DEFAULT_COLUMN_WIDTH, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH } from '../stores/kanban-settings-store';
import { useKanbanSettingsStore, DEFAULT_COLUMN_WIDTH, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH, COLLAPSED_COLUMN_WIDTH_REM, MIN_COLUMN_WIDTH_REM, MAX_COLUMN_WIDTH_REM, BASE_FONT_SIZE, pxToRem } from '../stores/kanban-settings-store';
import { useToast } from '../hooks/use-toast';
import { WorktreeCleanupDialog } from './WorktreeCleanupDialog';
import { BulkPRDialog } from './BulkPRDialog';
@@ -338,7 +338,7 @@ const DroppableColumn = memo(function DroppableColumn({ status, tasks, onTaskCli
'border-t-2',
isOver && 'drop-zone-highlight'
)}
style={{ width: COLLAPSED_COLUMN_WIDTH, minWidth: COLLAPSED_COLUMN_WIDTH, maxWidth: COLLAPSED_COLUMN_WIDTH }}
style={{ width: COLLAPSED_COLUMN_WIDTH_REM, minWidth: COLLAPSED_COLUMN_WIDTH_REM, maxWidth: COLLAPSED_COLUMN_WIDTH_REM }}
>
{/* Expand button at top */}
<div className="flex justify-center p-2 border-b border-white/5">
@@ -381,7 +381,7 @@ const DroppableColumn = memo(function DroppableColumn({ status, tasks, onTaskCli
return (
<div
className="relative flex"
style={columnWidth ? { width: columnWidth, minWidth: MIN_COLUMN_WIDTH, maxWidth: MAX_COLUMN_WIDTH, flexShrink: 0 } : undefined}
style={columnWidth ? { width: pxToRem(columnWidth), minWidth: MIN_COLUMN_WIDTH_REM, maxWidth: MAX_COLUMN_WIDTH_REM, flexShrink: 0 } : undefined}
>
<div
ref={setNodeRef}
@@ -1232,7 +1232,8 @@ export function KanbanBoard({ tasks, onTaskClick, onNewTaskClick, onRefresh, isR
const handleResizeMove = useCallback((clientX: number) => {
if (!resizingColumn) return;
const deltaX = clientX - resizeStartX.current;
const scaleFactor = parseFloat(getComputedStyle(document.documentElement).fontSize) / BASE_FONT_SIZE;
const deltaX = (clientX - resizeStartX.current) / scaleFactor;
const newWidth = Math.max(MIN_COLUMN_WIDTH, Math.min(MAX_COLUMN_WIDTH, resizeStartWidth.current + deltaX));
setColumnWidth(resizingColumn, newWidth);
}, [resizingColumn, setColumnWidth]);
@@ -52,6 +52,9 @@ interface KanbanSettingsState {
/** localStorage key prefix for kanban settings persistence (sync cache) */
const KANBAN_SETTINGS_KEY_PREFIX = 'kanban-column-prefs';
/** Base font size in pixels for rem conversion (matches CSS default) */
export const BASE_FONT_SIZE = 16;
/** Default column width in pixels */
export const DEFAULT_COLUMN_WIDTH = 320;
@@ -64,6 +67,33 @@ export const MAX_COLUMN_WIDTH = 600;
/** Collapsed column width in pixels */
export const COLLAPSED_COLUMN_WIDTH = 48;
// ============================================
// Rem Conversion Helpers
// ============================================
/**
* Convert a pixel value to a rem string.
* Used for CSS width values that should scale with the UI scale system.
*
* @param px - The pixel value to convert
* @returns A rem string (e.g., "20rem" for 320px)
*/
export function pxToRem(px: number): string {
return `${px / BASE_FONT_SIZE}rem`;
}
/** Default column width in rem (scales with UI) */
export const DEFAULT_COLUMN_WIDTH_REM = pxToRem(DEFAULT_COLUMN_WIDTH);
/** Minimum column width in rem (scales with UI) */
export const MIN_COLUMN_WIDTH_REM = pxToRem(MIN_COLUMN_WIDTH);
/** Maximum column width in rem (scales with UI) */
export const MAX_COLUMN_WIDTH_REM = pxToRem(MAX_COLUMN_WIDTH);
/** Collapsed column width in rem (scales with UI) */
export const COLLAPSED_COLUMN_WIDTH_REM = pxToRem(COLLAPSED_COLUMN_WIDTH);
// ============================================
// Debounce timer for saving kanban preferences to main process
// ============================================