From c5fb6030fbeec03c94e171fb13e7516e1c971caf Mon Sep 17 00:00:00 2001
From: Peeyush Tiwari <105871155+Peeyush2006@users.noreply.github.com>
Date: Sun, 5 Jul 2026 12:07:38 +0530
Subject: [PATCH 1/2] Add Skeleton component for loading placeholders
The Skeleton component serves as a loading placeholder with customizable dimensions and shapes.
---
app/components/Skeleton/Skeleton.tsx | 56 ++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 app/components/Skeleton/Skeleton.tsx
diff --git a/app/components/Skeleton/Skeleton.tsx b/app/components/Skeleton/Skeleton.tsx
new file mode 100644
index 0000000..710b679
--- /dev/null
+++ b/app/components/Skeleton/Skeleton.tsx
@@ -0,0 +1,56 @@
+import styles from "./Skeleton.module.css";
+
+type SkeletonVariant = "text" | "rect" | "circle";
+
+type SkeletonProps = {
+ /** Width as a CSS value. Numbers are treated as px. Defaults to 100%. */
+ width?: string | number;
+ /** Height as a CSS value. Numbers are treated as px. Defaults to 1rem. */
+ height?: string | number;
+ /** Visual shape of the placeholder. */
+ variant?: SkeletonVariant;
+ /** Optional border-radius override (CSS value / px number). */
+ radius?: string | number;
+ /** Extra class names to merge with the component styles. */
+ className?: string;
+};
+
+const toCssSize = (value?: string | number) =>
+ typeof value === "number" ? `${value}px` : value;
+
+/**
+ * Skeleton — a lightweight, theme-aware loading placeholder.
+ *
+ * Use it while data is loading to reduce layout shift and improve the
+ * perceived performance of data-heavy views (tables, cards, charts).
+ *
+ * @example
+ *
+ *
+ *
+ */
+export function Skeleton({
+ width = "100%",
+ height = "1rem",
+ variant = "text",
+ radius,
+ className,
+}: SkeletonProps) {
+ return (
+
+ );
+}
+
+export default Skeleton;
From fd7b487bd7b620194844594eea877f88be002987 Mon Sep 17 00:00:00 2001
From: Peeyush Tiwari <105871155+Peeyush2006@users.noreply.github.com>
Date: Sun, 5 Jul 2026 12:08:48 +0530
Subject: [PATCH 2/2] Add Skeleton.module.css for loading skeleton styles
---
app/components/Skeleton/Skeleton.module.css | 63 +++++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 app/components/Skeleton/Skeleton.module.css
diff --git a/app/components/Skeleton/Skeleton.module.css b/app/components/Skeleton/Skeleton.module.css
new file mode 100644
index 0000000..c17a8f7
--- /dev/null
+++ b/app/components/Skeleton/Skeleton.module.css
@@ -0,0 +1,63 @@
+.skeleton {
+ display: block;
+ position: relative;
+ overflow: hidden;
+ background-color: var(--skeleton-base, rgba(15, 23, 42, 0.08));
+ border-radius: var(--radius-sm, 6px);
+}
+
+.skeleton::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ transform: translateX(-100%);
+ background: linear-gradient(
+ 90deg,
+ transparent,
+ var(--skeleton-sheen, rgba(255, 255, 255, 0.45)),
+ transparent
+ );
+ animation: skeleton-shimmer 1.4s ease-in-out infinite;
+}
+
+.text { border-radius: var(--radius-sm, 6px); }
+.rect { border-radius: var(--radius-md, 10px); }
+.circle { border-radius: 50%; }
+
+@keyframes skeleton-shimmer {
+ 100% { transform: translateX(100%); }
+}
+
+:global([data-theme="dark"]) .skeleton {
+ background-color: var(--skeleton-base-dark, rgba(255, 255, 255, 0.09));
+}
+:global([data-theme="dark"]) .skeleton::after {
+ background: linear-gradient(
+ 90deg,
+ transparent,
+ var(--skeleton-sheen-dark, rgba(255, 255, 255, 0.13)),
+ transparent
+ );
+}
+
+@media (prefers-color-scheme: dark) {
+ :global(:root:not([data-theme="light"])) .skeleton {
+ background-color: var(--skeleton-base-dark, rgba(255, 255, 255, 0.09));
+ }
+ :global(:root:not([data-theme="light"])) .skeleton::after {
+ background: linear-gradient(
+ 90deg, transparent,
+ var(--skeleton-sheen-dark, rgba(255, 255, 255, 0.13)), transparent
+ );
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .skeleton::after { animation: none; }
+ .skeleton { animation: skeleton-pulse 1.6s ease-in-out infinite; }
+}
+
+@keyframes skeleton-pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.55; }
+}