Fix production class extraction by replacing template-literal classNames with cn()
Tailwind v4's static extractor missed classes adjacent to ${} in template
literals (e.g. `pb-8${...}`), causing missing styles in production builds.
Migrated all dynamic classNames to cn() and added a check script to prevent
regressions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -137,12 +137,14 @@ function AddModeSuggestions({
|
||||
<li key={key}>
|
||||
<button
|
||||
type="button"
|
||||
className={`flex w-full items-center justify-between px-3 py-1.5 text-left text-sm ${(() => {
|
||||
if (isQueued) return "bg-accent/30 text-foreground";
|
||||
if (i === suggestionIndex)
|
||||
return "bg-accent/20 text-foreground";
|
||||
return "text-foreground hover:bg-hover-neutral-bg";
|
||||
})()}`}
|
||||
className={cn(
|
||||
"flex w-full items-center justify-between px-3 py-1.5 text-left text-foreground text-sm",
|
||||
isQueued && "bg-accent/30",
|
||||
!isQueued && i === suggestionIndex && "bg-accent/20",
|
||||
!isQueued &&
|
||||
i !== suggestionIndex &&
|
||||
"hover:bg-hover-neutral-bg",
|
||||
)}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={() => onClickSuggestion(result)}
|
||||
onMouseEnter={() => onSetSuggestionIndex(i)}
|
||||
@@ -502,11 +504,12 @@ export function ActionBar({
|
||||
<li key={creatureKey(result)}>
|
||||
<button
|
||||
type="button"
|
||||
className={`flex w-full items-center justify-between px-3 py-1.5 text-left text-sm ${
|
||||
className={cn(
|
||||
"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-hover-neutral-bg"
|
||||
}`}
|
||||
: "text-foreground hover:bg-hover-neutral-bg",
|
||||
)}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={() => handleBrowseSelect(result)}
|
||||
onMouseEnter={() => setSuggestionIndex(i)}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
Sparkles,
|
||||
ZapOff,
|
||||
} from "lucide-react";
|
||||
import { cn } from "../lib/utils.js";
|
||||
|
||||
const ICON_MAP: Record<string, LucideIcon> = {
|
||||
EyeOff,
|
||||
@@ -75,7 +76,10 @@ export function ConditionTags({
|
||||
type="button"
|
||||
title={def.label}
|
||||
aria-label={`Remove ${def.label}`}
|
||||
className={`inline-flex items-center rounded p-0.5 transition-colors hover:bg-hover-neutral-bg ${colorClass}`}
|
||||
className={cn(
|
||||
"inline-flex items-center rounded p-0.5 transition-colors hover:bg-hover-neutral-bg",
|
||||
colorClass,
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRemove(condId);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
|
||||
import { getSourceDisplayName } from "../adapters/bestiary-index-adapter.js";
|
||||
import type { BulkImportState } from "../hooks/use-bulk-import.js";
|
||||
import { useSwipeToDismiss } from "../hooks/use-swipe-to-dismiss.js";
|
||||
import { cn } from "../lib/utils.js";
|
||||
import { BulkImportPrompt } from "./bulk-import-prompt.js";
|
||||
import { SourceFetchPrompt } from "./source-fetch-prompt.js";
|
||||
import { SourceManager } from "./source-manager.js";
|
||||
@@ -55,9 +56,10 @@ function CollapsedTab({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggleCollapse}
|
||||
className={`flex h-full w-[40px] cursor-pointer items-center justify-center text-muted-foreground hover:text-hover-neutral ${
|
||||
side === "right" ? "self-start" : "self-end"
|
||||
}`}
|
||||
className={cn(
|
||||
"flex h-full w-[40px] cursor-pointer items-center justify-center text-muted-foreground hover:text-hover-neutral",
|
||||
side === "right" ? "self-start" : "self-end",
|
||||
)}
|
||||
aria-label="Expand stat block panel"
|
||||
>
|
||||
<span className="writing-vertical-rl font-medium text-sm">
|
||||
@@ -152,7 +154,11 @@ function DesktopPanel({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`fixed top-0 bottom-0 flex w-[400px] flex-col border-border bg-card transition-slide-panel ${sideClasses} ${isCollapsed ? collapsedTranslate : "translate-x-0"}`}
|
||||
className={cn(
|
||||
"fixed top-0 bottom-0 flex w-[400px] flex-col border-border bg-card transition-slide-panel",
|
||||
sideClasses,
|
||||
isCollapsed ? collapsedTranslate : "translate-x-0",
|
||||
)}
|
||||
>
|
||||
{isCollapsed ? (
|
||||
<CollapsedTab
|
||||
@@ -194,7 +200,10 @@ function MobileDrawer({
|
||||
aria-label="Close stat block"
|
||||
/>
|
||||
<div
|
||||
className={`absolute top-0 right-0 bottom-0 w-[85%] max-w-md border-border border-l bg-card shadow-xl ${isSwiping ? "" : "animate-slide-in-right"}`}
|
||||
className={cn(
|
||||
"absolute top-0 right-0 bottom-0 w-[85%] max-w-md border-border border-l bg-card shadow-xl",
|
||||
!isSwiping && "animate-slide-in-right",
|
||||
)}
|
||||
style={
|
||||
isSwiping ? { transform: `translateX(${offsetX}px)` } : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user