aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/client/components/SyncStatusIndicator.tsx
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-08 00:18:03 +0900
committernsfisis <nsfisis@gmail.com>2025-12-08 00:18:03 +0900
commit65c0adfd769b9ef11b897c96a3634c61120055b8 (patch)
tree74668feef8f134c1b132beaab125e42fa9d77b2e /src/client/components/SyncStatusIndicator.tsx
parent7cf55a3b7e37971ea0835118a26f032d895ff71f (diff)
downloadkioku-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.tsx116
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>
);
}