Implement the 028-semantic-hover-tokens feature that unifies hover colors across all interactive UI components via six CSS custom property tokens (three text, three background) defined in the Tailwind v4 theme, replacing hardcoded hover classes in 9 component files plus the shared Button primitive with semantic token references so all hover colors can be globally reconfigured from one place
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ export function AcShield({ value, onClick, className }: AcShieldProps) {
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
"relative inline-flex items-center justify-center text-sm tabular-nums text-muted-foreground transition-colors hover:text-primary",
|
||||
"relative inline-flex items-center justify-center text-sm tabular-nums text-muted-foreground transition-colors hover:text-hover-neutral",
|
||||
className,
|
||||
)}
|
||||
style={{ width: 28, height: 32 }}
|
||||
|
||||
@@ -107,7 +107,7 @@ export function ActionBar({
|
||||
className={`flex w-full items-center justify-between px-3 py-1.5 text-left text-sm ${
|
||||
i === suggestionIndex
|
||||
? "bg-accent/20 text-foreground"
|
||||
: "text-foreground hover:bg-accent/10"
|
||||
: "text-foreground hover:bg-hover-neutral-bg"
|
||||
}`}
|
||||
onClick={() => handleSelectSuggestion(creature)}
|
||||
onMouseEnter={() => setSuggestionIndex(i)}
|
||||
|
||||
@@ -87,7 +87,7 @@ export function BestiarySearch({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
className="text-muted-foreground hover:text-hover-neutral"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -108,7 +108,7 @@ export function BestiarySearch({
|
||||
className={`flex w-full items-center justify-between px-3 py-1.5 text-left text-sm ${
|
||||
i === highlightIndex
|
||||
? "bg-accent/20 text-foreground"
|
||||
: "text-foreground hover:bg-accent/10"
|
||||
: "text-foreground hover:bg-hover-neutral-bg"
|
||||
}`}
|
||||
onClick={() => onSelectCreature(creature)}
|
||||
onMouseEnter={() => setHighlightIndex(i)}
|
||||
|
||||
@@ -91,7 +91,7 @@ function EditableName({
|
||||
e.stopPropagation();
|
||||
startEditing();
|
||||
}}
|
||||
className="truncate text-left text-sm text-foreground hover:text-primary transition-colors"
|
||||
className="truncate text-left text-sm text-foreground hover:text-hover-neutral transition-colors"
|
||||
>
|
||||
{name}
|
||||
</button>
|
||||
@@ -150,7 +150,7 @@ function MaxHpDisplay({
|
||||
<button
|
||||
type="button"
|
||||
onClick={startEditing}
|
||||
className="inline-block h-7 min-w-[3ch] text-center text-sm leading-7 tabular-nums text-muted-foreground transition-colors hover:text-primary"
|
||||
className="inline-block h-7 min-w-[3ch] text-center text-sm leading-7 tabular-nums text-muted-foreground transition-colors hover:text-hover-neutral"
|
||||
>
|
||||
{maxHp ?? "Max"}
|
||||
</button>
|
||||
@@ -190,7 +190,7 @@ function ClickableHp({
|
||||
type="button"
|
||||
onClick={() => setPopoverOpen(true)}
|
||||
className={cn(
|
||||
"inline-block h-7 min-w-[3ch] text-center text-sm font-medium leading-7 tabular-nums transition-colors hover:text-primary",
|
||||
"inline-block h-7 min-w-[3ch] text-center text-sm font-medium leading-7 tabular-nums transition-colors hover:text-hover-neutral",
|
||||
status === "bloodied" && "text-amber-400",
|
||||
status === "unconscious" && "text-red-400",
|
||||
status === "healthy" && "text-foreground",
|
||||
@@ -324,7 +324,7 @@ function InitiativeDisplay({
|
||||
type="button"
|
||||
onClick={() => onRollInitiative(combatantId)}
|
||||
className={cn(
|
||||
"flex h-7 w-full items-center justify-center text-muted-foreground transition-colors hover:text-primary",
|
||||
"flex h-7 w-full items-center justify-center text-muted-foreground transition-colors hover:text-hover-neutral",
|
||||
dimmed && "opacity-50",
|
||||
)}
|
||||
title="Roll initiative"
|
||||
@@ -344,8 +344,8 @@ function InitiativeDisplay({
|
||||
className={cn(
|
||||
"h-7 w-full text-center text-sm leading-7 tabular-nums transition-colors",
|
||||
initiative !== undefined
|
||||
? "font-medium text-foreground hover:text-primary"
|
||||
: "text-muted-foreground hover:text-primary",
|
||||
? "font-medium text-foreground hover:text-hover-neutral"
|
||||
: "text-muted-foreground hover:text-hover-neutral",
|
||||
dimmed && "opacity-50",
|
||||
)}
|
||||
>
|
||||
@@ -429,7 +429,7 @@ export function CombatantRow({
|
||||
title="Concentrating"
|
||||
aria-label="Toggle concentration"
|
||||
className={cn(
|
||||
"flex w-full items-center justify-center self-stretch -my-2 -ml-[2px] pl-[14px] transition-opacity",
|
||||
"flex w-full items-center justify-center self-stretch -my-2 -ml-[2px] pl-[14px] transition-opacity hover:text-hover-neutral hover:opacity-100",
|
||||
combatant.isConcentrating
|
||||
? dimmed
|
||||
? "opacity-50 text-purple-400"
|
||||
@@ -519,7 +519,7 @@ export function CombatantRow({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-muted-foreground hover:text-destructive opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto focus:opacity-100 focus:pointer-events-auto transition-opacity"
|
||||
className="h-7 w-7 text-muted-foreground hover:text-hover-destructive opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto focus:opacity-100 focus:pointer-events-auto transition-opacity"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRemove(id);
|
||||
|
||||
@@ -102,7 +102,7 @@ export function ConditionPicker({
|
||||
key={def.id}
|
||||
type="button"
|
||||
className={cn(
|
||||
"flex w-full items-center gap-2 rounded px-2 py-1 text-sm transition-colors hover:bg-card",
|
||||
"flex w-full items-center gap-2 rounded px-2 py-1 text-sm transition-colors hover:bg-hover-neutral-bg",
|
||||
isActive && "bg-card/50",
|
||||
)}
|
||||
onClick={() => onToggle(def.id)}
|
||||
|
||||
@@ -75,7 +75,7 @@ export function ConditionTags({
|
||||
type="button"
|
||||
title={def.label}
|
||||
aria-label={`Remove ${def.label}`}
|
||||
className={`inline-flex items-center rounded p-0.5 hover:bg-card transition-colors ${colorClass}`}
|
||||
className={`inline-flex items-center rounded p-0.5 hover:bg-hover-neutral-bg transition-colors ${colorClass}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRemove(condId);
|
||||
@@ -89,7 +89,7 @@ export function ConditionTags({
|
||||
type="button"
|
||||
title="Add condition"
|
||||
aria-label="Add condition"
|
||||
className="inline-flex items-center rounded p-0.5 text-muted-foreground hover:text-foreground hover:bg-card transition-colors opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity"
|
||||
className="inline-flex items-center rounded p-0.5 text-muted-foreground hover:text-hover-neutral hover:bg-hover-neutral-bg transition-colors opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onOpenPicker();
|
||||
|
||||
@@ -32,7 +32,7 @@ export function StatBlockPanel({ creature, onClose }: StatBlockPanelProps) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
className="text-muted-foreground hover:text-hover-neutral"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -63,7 +63,7 @@ export function StatBlockPanel({ creature, onClose }: StatBlockPanelProps) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
className="text-muted-foreground hover:text-hover-neutral"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
@@ -27,7 +27,7 @@ export function TurnNavigation({
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 text-foreground border-foreground hover:text-primary hover:border-primary hover:bg-muted"
|
||||
className="h-8 w-8 text-foreground border-foreground hover:text-hover-action hover:border-hover-action hover:bg-transparent"
|
||||
onClick={onRetreatTurn}
|
||||
disabled={!hasCombatants || isAtStart}
|
||||
title="Previous turn"
|
||||
@@ -55,7 +55,7 @@ export function TurnNavigation({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 text-muted-foreground hover:text-primary"
|
||||
className="h-8 w-8 text-muted-foreground hover:text-hover-action"
|
||||
onClick={onRollAllInitiative}
|
||||
title="Roll all initiative"
|
||||
aria-label="Roll all initiative"
|
||||
@@ -65,7 +65,7 @@ export function TurnNavigation({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 text-muted-foreground hover:text-destructive"
|
||||
className="h-8 w-8 text-muted-foreground hover:text-hover-destructive"
|
||||
onClick={onClearEncounter}
|
||||
disabled={!hasCombatants}
|
||||
>
|
||||
@@ -75,7 +75,7 @@ export function TurnNavigation({
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 text-foreground border-foreground hover:text-primary hover:border-primary hover:bg-muted"
|
||||
className="h-8 w-8 text-foreground border-foreground hover:text-hover-action hover:border-hover-action hover:bg-transparent"
|
||||
onClick={onAdvanceTurn}
|
||||
disabled={!hasCombatants}
|
||||
title="Next turn"
|
||||
|
||||
@@ -9,8 +9,8 @@ const buttonVariants = cva(
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
outline:
|
||||
"border border-border bg-transparent hover:bg-card hover:text-foreground",
|
||||
ghost: "hover:bg-card hover:text-foreground",
|
||||
"border border-border bg-transparent hover:bg-hover-neutral-bg hover:text-hover-neutral",
|
||||
ghost: "hover:bg-hover-neutral-bg hover:text-hover-neutral",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
--color-primary-foreground: #ffffff;
|
||||
--color-accent: #3b82f6;
|
||||
--color-destructive: #ef4444;
|
||||
--color-hover-neutral: var(--color-primary);
|
||||
--color-hover-action: var(--color-primary);
|
||||
--color-hover-destructive: var(--color-destructive);
|
||||
--color-hover-neutral-bg: var(--color-card);
|
||||
--color-hover-action-bg: var(--color-muted);
|
||||
--color-hover-destructive-bg: transparent;
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
|
||||
Reference in New Issue
Block a user