diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-12-08 00:18:03 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-12-08 00:18:03 +0900 |
| commit | 65c0adfd769b9ef11b897c96a3634c61120055b8 (patch) | |
| tree | 74668feef8f134c1b132beaab125e42fa9d77b2e /src/client/components/SyncStatusIndicator.tsx | |
| parent | 7cf55a3b7e37971ea0835118a26f032d895ff71f (diff) | |
| download | kioku-65c0adfd769b9ef11b897c96a3634c61120055b8.tar.gz kioku-65c0adfd769b9ef11b897c96a3634c61120055b8.tar.zst kioku-65c0adfd769b9ef11b897c96a3634c61120055b8.zip | |
feat(client): redesign frontend with TailwindCSS v4
Replace inline styles with TailwindCSS, implementing a cohesive Japanese-inspired
design system with custom colors (cream, teal primary), typography (Fraunces,
DM Sans), and animations. Update all pages and components with consistent styling,
improve accessibility by adding aria-hidden to decorative SVGs, and configure
Biome for Tailwind CSS syntax support.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/client/components/SyncStatusIndicator.tsx')
| -rw-r--r-- | src/client/components/SyncStatusIndicator.tsx | 116 |
1 files changed, 84 insertions, 32 deletions
diff --git a/src/client/components/SyncStatusIndicator.tsx b/src/client/components/SyncStatusIndicator.tsx index 23e3ec6..0f555ca 100644 --- a/src/client/components/SyncStatusIndicator.tsx +++ b/src/client/components/SyncStatusIndicator.tsx @@ -20,63 +20,115 @@ export function SyncStatusIndicator() { return "Synced"; }; - const getStatusColor = (): string => { + const getStatusStyles = (): string => { if (!isOnline) { - return "#6c757d"; // gray + return "bg-muted/10 text-muted"; } if (isSyncing) { - return "#007bff"; // blue + return "bg-info/10 text-info"; } if (status === SyncStatus.Error) { - return "#dc3545"; // red + return "bg-error/10 text-error"; } if (pendingCount > 0) { - return "#ffc107"; // yellow + return "bg-warning/10 text-warning"; } - return "#28a745"; // green + return "bg-success/10 text-success"; }; - const getStatusIcon = (): string => { + const getStatusIcon = () => { if (!isOnline) { - return "\u25CB"; // hollow circle + return ( + <svg + className="w-3.5 h-3.5" + fill="currentColor" + viewBox="0 0 20 20" + aria-hidden="true" + > + <circle cx="10" cy="10" r="4" /> + </svg> + ); } if (isSyncing) { - return "\u21BB"; // rotating arrows + return ( + <svg + className="w-3.5 h-3.5 animate-spin" + fill="none" + viewBox="0 0 24 24" + aria-hidden="true" + > + <circle + className="opacity-25" + cx="12" + cy="12" + r="10" + stroke="currentColor" + strokeWidth="4" + /> + <path + className="opacity-75" + fill="currentColor" + d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" + /> + </svg> + ); } if (status === SyncStatus.Error) { - return "\u2717"; // cross mark + return ( + <svg + className="w-3.5 h-3.5" + fill="currentColor" + viewBox="0 0 20 20" + aria-hidden="true" + > + <path + fillRule="evenodd" + d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" + clipRule="evenodd" + /> + </svg> + ); } if (pendingCount > 0) { - return "\u25D4"; // partial circle + return ( + <svg + className="w-3.5 h-3.5" + fill="currentColor" + viewBox="0 0 20 20" + aria-hidden="true" + > + <path + fillRule="evenodd" + d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" + clipRule="evenodd" + /> + </svg> + ); } - return "\u2713"; // check mark + return ( + <svg + className="w-3.5 h-3.5" + fill="currentColor" + viewBox="0 0 20 20" + aria-hidden="true" + > + <path + fillRule="evenodd" + d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" + clipRule="evenodd" + /> + </svg> + ); }; return ( <div data-testid="sync-status-indicator" - style={{ - display: "inline-flex", - alignItems: "center", - gap: "0.25rem", - padding: "0.25rem 0.5rem", - borderRadius: "4px", - backgroundColor: "#f8f9fa", - border: "1px solid #dee2e6", - fontSize: "0.875rem", - }} + className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium transition-colors ${getStatusStyles()}`} title={lastError || undefined} > - <span - style={{ - color: getStatusColor(), - fontWeight: "bold", - }} - aria-hidden="true" - > - {getStatusIcon()} - </span> - <span style={{ color: getStatusColor() }}>{getStatusText()}</span> + {getStatusIcon()} + <span>{getStatusText()}</span> </div> ); } |
