fix(FN-1304): reduce mobile button heights from 44px to 36px

- Reduce mobile media query button heights from 44px to 36px across all dashboard components
- Update mobile CSS tests (nav bar, board, modals, mission planning) to expect 36px touch targets
- Add CSS testing patterns to project memory for consistent test guidelines
This commit is contained in:
gsxdsm
2026-04-09 04:59:44 -07:00
parent 7a9148d25d
commit e170073dcf
6 changed files with 88 additions and 78 deletions

View File

@@ -56,3 +56,13 @@
- QuickEntryBox control test IDs are reused in `ListView` integration tests; when control layout changes (for example nested menu → inline buttons), update both `QuickEntryBox.test.tsx` and `ListView.test.tsx` together to avoid cascading failures.
- When `InlineCreateCard` layout changes, also check `Column.test.tsx` and `board-mobile.test.tsx` for references to moved/removed test IDs like `inline-create-description-actions`.
- `mission-store.test.ts` has a flaky test (`getMissionHealth computes mission metrics and latest error context`) that fails intermittently when timestamps collide in the same millisecond — this is pre-existing and not related to dashboard changes.
- When adding light-theme overrides for CSS components that already use `var(--*)` tokens, most selectors inherit correctly from the light-theme root variable redefinitions. Only add explicit `[data-theme="light"]` overrides where fine-tuning is needed (e.g., slightly different opacity values, subtle box-shadows for contrast).
- `--surface-hover` is used but never defined as a CSS custom property in the root or light theme blocks — it resolves to invalid/empty. Components using `var(--surface-hover)` (like `.github-import-tab:hover`) get no background. Either define it in the theme roots or use fallbacks like `var(--surface-hover, rgba(0,0,0,0.03))`.
- `.form-error` and similar error-state selectors should use `color-mix(in srgb, var(--color-error) 10%, transparent)` instead of hardcoded `rgba(248, 81, 73, 0.1)` for theme adaptability.
- When styling `input[type="radio"]` elements in `.imported` items, the selector must match `.issue-item.imported input[type="radio"]` (classes on the same element, not nested), because the HTML structure is `<div class="issue-item imported"><input type="radio">`.
## CSS Testing Patterns
- Several test files assert specific CSS values in `styles.css` mobile media query blocks (e.g., `board-mobile.test.tsx`, `core-modals-mobile.test.tsx`, `mission-planning-modals-mobile.test.ts`, `mobile-nav-bar-css.test.ts`). When changing mobile CSS values (like `min-height`), update both the CSS and the corresponding test assertions + regex patterns.
- Mobile-specific selectors like `.mobile-nav-tab` and `.mobile-more-item` may exist as base styles (not inside media queries) but are still mobile-only components. The `.touch-target` utility class at the top of `styles.css` is intentionally 44px and should not be changed when reducing mobile button sizes.

View File

@@ -56,9 +56,9 @@ function findMobileBlockContaining(needle: string): string {
}
describe("mission + planning modal mobile CSS", () => {
it("MissionManager: mission icon button touch targets are 44px", () => {
it("MissionManager: mission icon button touch targets are 36px", () => {
const missionBlock = findMobileBlockContaining(".mission-manager-overlay");
expect(missionBlock).toMatch(/\.mission-list__item-actions \.mission-icon-btn,[\s\S]*?\.mission-feature__actions \.mission-icon-btn\s*\{[\s\S]*?min-width:\s*44px;[\s\S]*?min-height:\s*44px;/s);
expect(missionBlock).toMatch(/\.mission-list__item-actions \.mission-icon-btn,[\s\S]*?\.mission-feature__actions \.mission-icon-btn\s*\{[\s\S]*?min-width:\s*36px;[\s\S]*?min-height:\s*36px;/s);
});
it("MissionManager: body prevents horizontal overflow", () => {
@@ -76,24 +76,24 @@ describe("mission + planning modal mobile CSS", () => {
expect(missionBlock).toMatch(/\.mission-detail\s*\{[^}]*env\(safe-area-inset-bottom/s);
});
it("SubtaskBreakdown: drag handle is touch-friendly (44px)", () => {
it("SubtaskBreakdown: drag handle is touch-friendly (36px)", () => {
const planningBlock = findMobileBlockContaining(".planning-modal");
expect(planningBlock).toMatch(/\.subtask-drag-handle\s*\{[^}]*min-width:\s*44px;[^}]*min-height:\s*44px;/s);
expect(planningBlock).toMatch(/\.subtask-drag-handle\s*\{[^}]*min-width:\s*36px;[^}]*min-height:\s*36px;/s);
});
it("SubtaskBreakdown: subtask action icon buttons are 44px", () => {
it("SubtaskBreakdown: subtask action icon buttons are 36px", () => {
const planningBlock = findMobileBlockContaining(".planning-modal");
expect(planningBlock).toMatch(/\.subtask-item-actions \.btn-icon\s*\{[^}]*min-width:\s*44px;[^}]*min-height:\s*44px;/s);
expect(planningBlock).toMatch(/\.subtask-item-actions \.btn-icon\s*\{[^}]*min-width:\s*36px;[^}]*min-height:\s*36px;/s);
});
it("SubtaskBreakdown: dependency chips are touch-friendly", () => {
const planningBlock = findMobileBlockContaining(".planning-modal");
expect(planningBlock).toMatch(/\.planning-dep-chip\s*\{[^}]*min-height:\s*44px;/s);
expect(planningBlock).toMatch(/\.planning-dep-chip\s*\{[^}]*min-height:\s*36px;/s);
});
it("PlanningMode: confirm buttons meet touch targets", () => {
const planningBlock = findMobileBlockContaining(".planning-modal");
expect(planningBlock).toMatch(/\.planning-confirm-btn\s*\{[^}]*min-height:\s*44px;/s);
expect(planningBlock).toMatch(/\.planning-confirm-btn\s*\{[^}]*min-height:\s*36px;/s);
});
it("ModelSelection: combobox dropdown has mobile sizing", () => {
@@ -120,6 +120,6 @@ describe("mission + planning modal mobile CSS", () => {
it("ModelSelection: combobox options meet touch targets", () => {
const modelBlock = findMobileBlockContaining(".model-combobox-option");
expect(modelBlock).toMatch(/\.model-combobox-option\s*\{[^}]*min-height:\s*44px;/s);
expect(modelBlock).toMatch(/\.model-combobox-option\s*\{[^}]*min-height:\s*36px;/s);
});
});

View File

@@ -50,9 +50,9 @@ describe("mobile-nav-bar.css", () => {
expect(cssContent).toMatch(/@media\s*\(max-width:\s*768px\)\s*\{[\s\S]*?\.mobile-nav-bar\s*\{[\s\S]*?display:\s*flex[;\s]/);
});
it("tab touch targets are at least 44px", () => {
it("tab touch targets are at least 36px", () => {
const block = extractRuleBlock(cssContent, ".mobile-nav-tab");
expect(block).toContain("min-height: 44px");
expect(block).toContain("min-height: 36px");
});
it("footer-aware positioning places nav above footer", () => {
@@ -73,9 +73,9 @@ describe("mobile-nav-bar.css", () => {
expect(block).toContain("z-index: 45");
});
it("sheet items maintain 44px touch targets", () => {
it("sheet items maintain 36px touch targets", () => {
const block = extractRuleBlock(cssContent, ".mobile-more-item");
expect(block).toContain("min-height: 44px");
expect(block).toContain("min-height: 36px");
});
it("defines content padding rule for mobile nav", () => {

View File

@@ -132,11 +132,11 @@ describe("Board and Column mobile CSS", () => {
expectRuleToContain(mobileSection, ".board > .column", "min-width: 280px;");
});
it("contains .column-header min-height: 44px in the mobile media block", () => {
it("contains .column-header min-height: 36px in the mobile media block", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileSection = getMainMobileSection(css);
expectRuleToContain(mobileSection, ".column-header", "min-height: 44px;");
expectRuleToContain(mobileSection, ".column-header", "min-height: 36px;");
});
it("hides board scrollbars in the mobile media block", () => {
@@ -184,12 +184,12 @@ describe("TaskCard mobile", () => {
expectRuleToContain(mobileSection, ".card-session-files", "min-height: 32px;");
});
it("keeps .card-edit-btn width and height at 44px in the mobile media block", () => {
it("keeps .card-edit-btn width and height at 36px in the mobile media block", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileSection = getMainMobileSection(css);
expectRuleToContain(mobileSection, ".card-edit-btn", "width: 44px;");
expectRuleToContain(mobileSection, ".card-edit-btn", "height: 44px;");
expectRuleToContain(mobileSection, ".card-edit-btn", "width: 36px;");
expectRuleToContain(mobileSection, ".card-edit-btn", "height: 36px;");
});
it("opens task detail on quick tap", async () => {
@@ -363,18 +363,18 @@ describe("InlineCreateCard mobile", () => {
expectRuleToContain(mobileSection, ".inline-create-input", "font-size: 16px;");
});
it("contains .inline-create-toggle min-height: 44px in the mobile media block", () => {
it("contains .inline-create-toggle min-height: 36px in the mobile media block", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileSection = getMainMobileSection(css);
expectRuleToContain(mobileSection, ".inline-create-toggle", "min-height: 44px;");
expectRuleToContain(mobileSection, ".inline-create-toggle", "min-height: 36px;");
});
it("contains .inline-create-controls .btn min-height: 44px in the mobile media block", () => {
it("contains .inline-create-controls .btn min-height: 36px in the mobile media block", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileSection = getMainMobileSection(css);
expectRuleToContain(mobileSection, ".inline-create-controls .btn", "min-height: 44px;");
expectRuleToContain(mobileSection, ".inline-create-controls .btn", "min-height: 36px;");
});
it("renders Plan and Subtask buttons when expanded", () => {

View File

@@ -88,11 +88,11 @@ describe("core modals mobile css coverage", () => {
expect(mobileBlock).toContain("flex-direction: row;");
});
it("GitManagerModal: nav items keep 44px touch target on mobile", () => {
it("GitManagerModal: nav items keep 36px touch target on mobile", () => {
const css = fs.readFileSync(stylesPath, "utf-8");
const mobileBlock = getMainMobileBlock(css);
expect(mobileBlock).toContain(".gm-nav-item {");
expect(mobileBlock).toContain("min-height: 44px;");
expect(mobileBlock).toContain("min-height: 36px;");
});
});

View File

@@ -4057,7 +4057,7 @@ body {
/* Larger touch targets for file entries on mobile */
.changed-files-sidebar.mobile .changed-files-entry {
padding: 10px var(--space-md);
min-height: 44px;
min-height: 36px;
}
/* Clearer active state on mobile */
@@ -5742,12 +5742,12 @@ body {
/* Global touch target enforcement on mobile */
.btn:not(.btn-icon):not(.btn-badge):not(.btn-sm):not(.btn--sm) {
min-height: 44px;
min-height: 36px;
}
.btn-icon {
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
}
input[type="text"],
@@ -5758,11 +5758,11 @@ body {
input[type="url"],
select,
textarea {
min-height: 44px;
min-height: 36px;
}
.detail-tab {
min-height: 44px;
min-height: 36px;
}
/* Dropdown items and overflow menu actions */
@@ -5770,7 +5770,7 @@ body {
.mobile-overflow-item,
.mobile-overflow-split-toggle,
.list-column-dropdown-item {
min-height: 44px;
min-height: 36px;
}
/* Small tappable controls that are explicitly sized below 44px at desktop */
@@ -5785,8 +5785,8 @@ body {
.workflow-results-edit-toggle,
.agent-tree__toggle,
.quick-chat-panel-input button {
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
}
/* Board: flex layout with single horizontal scroll + snap */
@@ -5818,7 +5818,7 @@ body {
/* Column header touch target */
.column-header {
min-height: 44px;
min-height: 36px;
}
/* Column count badge: slightly larger on mobile for tapping */
@@ -5988,7 +5988,7 @@ body {
.settings-content .btn,
.auth-provider-row .btn {
min-height: 44px;
min-height: 36px;
}
.auth-provider-row {
@@ -6006,8 +6006,8 @@ body {
/* Card edit button: always visible on mobile (no hover) */
.card-edit-btn {
opacity: 1;
width: 44px;
height: 44px;
width: 36px;
height: 36px;
margin-right: -8px;
margin-top: -8px;
margin-bottom: -8px;
@@ -6022,8 +6022,8 @@ body {
/* Git Manager remote edit buttons: always visible on mobile (no hover) */
.gm-remote-edit-btn {
opacity: 1;
width: 44px;
height: 44px;
width: 36px;
height: 36px;
margin-right: -8px;
margin-top: -8px;
margin-bottom: -8px;
@@ -6102,8 +6102,8 @@ body {
/* Inline create: 44px touch targets */
.inline-create-toggle {
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
flex-shrink: 0;
}
@@ -6114,7 +6114,7 @@ body {
}
.inline-create-controls .btn {
min-height: 44px;
min-height: 36px;
font-size: 12px;
}
@@ -6139,7 +6139,7 @@ body {
}
.model-combobox-option {
min-height: 44px;
min-height: 36px;
}
/* Task detail modal: prevent header clipping + full-screen refine flow */
@@ -6268,7 +6268,7 @@ body {
border-bottom: 2px solid transparent;
text-align: center;
justify-content: center;
min-height: 44px;
min-height: 36px;
}
.gm-nav-item.active {
@@ -7098,7 +7098,7 @@ body {
.issue-item {
flex-wrap: wrap;
min-height: 44px;
min-height: 36px;
}
.imported-badge {
@@ -7926,7 +7926,7 @@ body {
gap: var(--space-sm);
padding: var(--space-md) var(--space-md) var(--space-sm);
cursor: pointer;
min-height: 44px;
min-height: 36px;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
@@ -7949,7 +7949,7 @@ body {
margin: var(--space-xs) 0;
cursor: pointer;
transition: background var(--transition-fast), border-color var(--transition-fast);
min-height: 44px;
min-height: 36px;
position: relative;
-webkit-tap-highlight-color: transparent;
}
@@ -8008,8 +8008,8 @@ body {
position: absolute;
top: var(--space-xs);
left: var(--space-xs);
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
display: flex;
align-items: center;
justify-content: center;
@@ -9357,7 +9357,7 @@ body {
}
.model-combobox-option {
min-height: 44px;
min-height: 36px;
}
}
@@ -13506,7 +13506,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
}
.changes-diff-file-item {
min-height: 44px;
min-height: 36px;
}
.changes-diff-content {
@@ -13775,8 +13775,8 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
}
.file-browser-modal-header .modal-close {
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
padding: 10px;
display: inline-flex;
align-items: center;
@@ -14708,8 +14708,8 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
/* Subtask breakdown: touch-friendly drag handles with padding */
.subtask-drag-handle {
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
padding: 10px;
display: flex;
align-items: center;
@@ -14718,8 +14718,8 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
/* Subtask action buttons meet 44px touch target */
.subtask-item-actions .btn-icon {
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
}
/* Subtask items: prevent overflow */
@@ -14730,7 +14730,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
/* Dependency chips: touch-friendly */
.planning-dep-chip {
min-height: 44px;
min-height: 36px;
}
/* Size select: prevent iOS zoom */
@@ -14749,7 +14749,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
/* Confirm buttons meet 44px touch target */
.planning-confirm-btn {
min-height: 44px;
min-height: 36px;
padding: var(--space-sm) var(--space-md);
}
@@ -17649,7 +17649,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
/* Entries: allow content to wrap freely */
.activity-log-entry {
min-height: 44px;
min-height: 36px;
gap: var(--space-sm);
padding: var(--space-sm);
}
@@ -20190,7 +20190,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
}
.background-tasks-indicator__pill {
min-height: 44px;
min-height: 36px;
}
}
@@ -22033,8 +22033,8 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
.mission-milestone__actions .mission-icon-btn,
.mission-slice__actions .mission-icon-btn,
.mission-feature__actions .mission-icon-btn {
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
}
/* Prevent horizontal overflow in body */
@@ -23862,7 +23862,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
padding: 12px 14px;
font-size: 13px;
white-space: nowrap;
min-height: 44px;
min-height: 36px;
}
.agent-detail-content {
@@ -23958,7 +23958,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
.config-section .select,
.config-section textarea {
font-size: 16px;
min-height: 44px;
min-height: 36px;
}
.config-actions {
@@ -24010,7 +24010,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
.agent-board-actions .btn,
.agent-card-actions .btn {
min-height: 44px;
min-height: 36px;
}
.agent-controls {
@@ -24027,12 +24027,12 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
.agent-controls-actions .btn {
width: 100%;
min-height: 44px;
min-height: 36px;
}
.agent-state-filter {
width: 100%;
min-height: 44px;
min-height: 36px;
}
.agent-state-filter-select {
@@ -24055,8 +24055,8 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
.agent-tree__indent--4 { padding-left: 64px; }
.agent-tree__toggle {
min-width: 44px;
min-height: 44px;
min-width: 36px;
min-height: 36px;
}
}
@@ -26013,7 +26013,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
align-items: center;
justify-content: center;
gap: 2px;
min-height: 44px;
min-height: 36px;
padding: 6px 0;
background: none;
border: none;
@@ -26105,7 +26105,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
gap: 12px;
width: 100%;
padding: 12px 16px;
min-height: 44px;
min-height: 36px;
background: none;
border: none;
color: var(--text-primary);
@@ -26176,7 +26176,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
@media (max-width: 768px) {
.file-browser-context-menu__item {
min-height: 44px;
min-height: 36px;
}
}
@@ -26205,7 +26205,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
/* === Quick Entry Mobile Touch + Overflow Fixes (FN-1140) === */
@media (max-width: 768px) {
.quick-entry-actions .btn {
min-height: 44px;
min-height: 36px;
}
.quick-entry-box .dep-dropdown {
@@ -26216,8 +26216,8 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
@media (max-width: 640px) {
.quick-entry-toggle {
min-height: 44px;
min-width: 44px;
min-height: 36px;
min-width: 36px;
padding: var(--space-xs);
}
}
@@ -26447,7 +26447,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
.agent-dialog-field .select,
.agent-dialog-field textarea {
font-size: 16px; /* prevent iOS zoom */
min-height: 44px;
min-height: 36px;
}
/* Summary rows adapt — used by AgentGenerationModal and NewAgentDialog */
@@ -26830,7 +26830,7 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
.conversation-thinking-toggle {
width: 100%;
justify-content: center;
min-height: 44px;
min-height: 36px;
}
}