Skip to content

fix: polish pass — label noise, drag handle, toast spacing (#522)#563

Merged
Chris0Jeky merged 3 commits intomainfrom
fix/522-polish-label-toast
Mar 29, 2026
Merged

fix: polish pass — label noise, drag handle, toast spacing (#522)#563
Chris0Jeky merged 3 commits intomainfrom
fix/522-polish-label-toast

Conversation

@Chris0Jeky
Copy link
Copy Markdown
Owner

Summary

  • Hides "Drag card" text label by default on cards; text fades in only when the drag handle is hovered so the dotted-grid icon carries affordance in compact mode
  • Adds a colour swatch dot next to each label name in the CardModal picker so label colours are identifiable before the row is selected (previously colour only appeared after selection)
  • Trims the card title before interpolating into the success toast, removing the leading-space artefact (Card " First test card" created successfullyCard "First test card" created successfully)
  • Adds a native title tooltip to the "Precision Mode Active" sidebar subtitle explaining the mode and pointing users to Preferences

Closes #522

Changes

  • CardItem.vue: .td-board-card__drag-label--hidden class; opacity 0 by default, 1 on .td-card-drag-handle:hover
  • CardModal.vue: <span> colour swatch inserted before the label name in the picker list
  • cardStore.ts: newCard.title.trim() in the success toast message
  • ShellSidebar.vue: native title attribute on .td-sidebar__subtitle

Test plan

  • Cards render without "Drag card" text; it appears on drag-handle hover
  • Label picker in CardModal shows a colour dot for every label regardless of selection state
  • Creating a card with leading/trailing whitespace in the title shows a clean name in the toast
  • "Precision Mode Active" subtitle shows a tooltip on hover
  • npm run typecheck passes (clean)
  • All 1107 unit tests pass

- Hide "Drag card" text on the drag handle by default; text fades in
  only when the handle is hovered, letting the dotted-grid icon carry
  affordance in compact mode
- Add a colour swatch (3×3 rounded dot) next to each label name in the
  CardModal picker so label colour is visible before the row is selected
- Add native tooltip to "Precision Mode Active" sidebar subtitle
  explaining the mode and pointing users to Preferences
Success toast for card creation was showing a leading space inside the
quoted name (e.g. Card " First test card" created successfully) because
the raw API-returned title was not trimmed before interpolation.
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Self-review (adversarial pass)

All issue items addressed?

Light/dark mode

The drag-label change uses only opacity — no colour token, no risk. The colour swatch uses label.colorHex inline style (same as the card chip), which is colour-token-independent and already correct in both modes. The tooltip is a native HTML title attribute — renders in the OS default style regardless of theme.

Responsiveness

The drag-label CSS uses scoped opacity; the drag handle button is already w-[calc(100%+1rem)] so it scales with the card. No breakpoint risk.

Toast stacking

The toast container (ToastContainer.vue) uses flex flex-col gap-2 — individual toast items are unchanged, so stacking layout is unaffected.

Tailwind class consistency

CardModal.vue swatch uses inline-block w-3 h-3 rounded-full flex-shrink-0, matching the chip pattern already in the codebase. The drag-label uses scoped CSS rather than Tailwind (consistent with the existing .td-board-card__drag-label pattern in the same <style scoped> block).

Risk of unintended layout impact

  • Drag handle: adding opacity: 0 to the text does not remove it from the flow — the handle button retains its min-h-10 height and the card's vertical rhythm is unchanged. This is intentional; the handle still has the same click target.
  • Modal picker: inserting the swatch span between the checkbox and the label name adds ~12px + gap. The row is inline-flex items-center gap-2 so it accommodates the extra element without wrapping.

One finding to address post-merge

The drag-label text at opacity: 0 still occupies horizontal space, keeping the button visually wide even when the text is hidden. This is acceptable for now (the button spans full card width anyway) but could be revisited with max-width: 0; overflow: hidden if a purely icon-only compact mode is desired later.

No blockers found. Diff is correct and minimal.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several UI and UX improvements, including a hover-triggered drag label on cards, visible color swatches for labels in the card modal, and an informative tooltip for Precision Mode in the sidebar. Feedback focuses on ensuring data consistency by trimming card titles before they are sent to the API, rather than just in the UI toast, and simplifying the CSS implementation for the hidden drag label.

state.currentBoardCards.value.push(newCard)
helpers.updateColumnCardCount(newCard.columnId, 1)
helpers.toast.success(`Card "${newCard.title}" created successfully`)
helpers.toast.success(`Card "${newCard.title.trim()}" created successfully`)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This change correctly trims the whitespace for the toast message, but the underlying card data in the state and on the server will still contain the whitespace. This can lead to inconsistent display of the card title.

A more robust fix would be to trim the title before sending it to the API. This ensures data consistency across the application. You could modify the createCard function like this:

async function createCard(boardId: string, card: CreateCardDto) {
  // ...
  const cardData = { ...card, title: card.title.trim() };
  const newCard = await cardsApi.createCard(boardId, cardData);
  state.currentBoardCards.value.push(newCard);
  helpers.updateColumnCardCount(newCard.columnId, 1);
  // The title from `newCard` should now be trimmed, so no .trim() is needed here.
  helpers.toast.success(`Card "${newCard.title}" created successfully`);
  // ...
}

This would fix the issue at its source.

Comment on lines +206 to 216
transition: opacity var(--td-transition-fast);
}

/* Hide "Drag card" text by default; reveal only on drag-handle hover */
.td-board-card__drag-label--hidden {
opacity: 0;
}

.td-card-drag-handle:hover .td-board-card__drag-label--hidden {
opacity: 1;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This implementation can be simplified by setting opacity: 0 on the base .td-board-card__drag-label class and removing the td-board-card__drag-label--hidden class modifier. This makes the template cleaner and the CSS more direct.

To apply this, you would:

  1. Modify this block with the suggestion below.
  2. Remove the td-board-card__drag-label--hidden class from the <span> in the template (line 86).
  transition: opacity var(--td-transition-fast);
  opacity: 0;
}

.td-card-drag-handle:hover .td-board-card__drag-label {
  opacity: 1;
}

- CardItem.vue: collapse hidden drag-label width to zero so the invisible
  text does not consume horizontal flex space and widen the button layout;
  restore width:auto on hover so the transition still works
- CardModal.vue: add bg-outline-variant fallback class on colour swatch so
  the dot remains visible when colorHex is empty/falsy
@Chris0Jeky
Copy link
Copy Markdown
Owner Author

Adversarial Review — #563

Reviewed all four change areas. Two genuine issues found and fixed directly in the branch (commit 1237ce09).


Finding 1 — CardItem.vue: hidden label still occupies layout space (FIXED)

Problem: .td-board-card__drag-label--hidden used opacity: 0 only. In a flex container with gap-2, an invisible element with text content still has non-zero width — the hidden "Drag card" text occupied ~70px of horizontal space in every card's drag handle even when not shown. This widened every drag button silently.

Fix: Added width: 0; overflow: hidden to the hidden state, and width: auto; overflow: visible to the hover reveal. The element now truly collapses when hidden. The transition: opacity on the parent class still provides the fade effect.


Finding 2 — CardModal.vue: invisible swatch when colorHex is empty (FIXED)

Problem: The swatch <span> applied :style="{ backgroundColor: label.colorHex }" unconditionally. The Label interface types colorHex as string (non-nullable), but UpdateLabelDto.colorHex is string | null — if the API ever returns a label with an empty string, the backgroundColor style is set to "" and the dot renders as a transparent hole in the layout with no visual fallback.

Fix: Changed to label.colorHex ? { backgroundColor: label.colorHex } : {} and added a bg-outline-variant Tailwind class as a visible fallback so the dot always renders in some colour.


Non-issues (no fix needed)

  • colorHex field name: Confirmed correct — matches Label.colorHex in /frontend/taskdeck-web/src/types/board.ts.
  • .trim() scope: Applied only in the toast string interpolation; state.currentBoardCards.value stores the untrimmed API response. No silent mutation.
  • newCard.title null safety: Card.title is typed string (non-nullable), so .trim() on a null is not a runtime risk.
  • Touch devices / hover: The hidden drag label not appearing on touch is acceptable — the drag icon alone carries the affordance. The title tooltip on Precision Mode is desktop-only by nature of native tooltips, which is acceptable for a sidebar subtitle.
  • Swatch accessibility: aria-hidden="true" is correct for a purely decorative indicator; the label name in the adjacent <span> provides the accessible name.
  • --hidden class always applied (never toggled): Intentional — visibility is managed entirely via CSS hover state. No JS toggling needed.

All 1107 unit tests pass, npm run typecheck clean.

@Chris0Jeky Chris0Jeky merged commit 02d49a0 into main Mar 29, 2026
18 checks passed
@Chris0Jeky Chris0Jeky deleted the fix/522-polish-label-toast branch March 29, 2026 18:25
@github-project-automation github-project-automation bot moved this from Pending to Done in Taskdeck Execution Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

UX: Polish pass — drag card label noise, label color in picker, card toast spacing, PRECISION MODE tooltip

1 participant