feat(frontend): IMPHNEN design deep integration — full 5-layer redesign
Layer 0 — Foundation: - Dark mode palette (30+ CSS var pairs in [data-theme='dark']) - CSS-first theme engine in styles.css (@theme + CSS vars) - UseTheme hook with light/dark/system support + localStorage persistence - Fixed animation keyframes (shimmer 1.5s canonical, glowPulse moved to CSS) - Smooth theme switch transitions via .theme-transitioning class Layer 1 — Shared UI Components: - Badge: success/warning variants now use CSS vars (bg-success-soft text-success) - Toast: rewritten with CSS vars, z-index fixed (40), repositioned top-right - Skeleton: added variant prop (rounded/circular/rectangular) - EmptyState: new component with icon + title + description + action - Button: added tertiary variant + icon-sm size - Card: added elevated/bordered variants - Input: added soft variant Layer 2 — Layout & Navigation: - TabStrip: new horizontal tab navigation with spring underline indicator (z-30) - Sidebar: expanded by default (w-64), brand assets always visible - Header: simplified brand bar, removed redundant page titles, added ThemeToggle - MobileTabBar: enhanced with spring dot indicator + glass bg + safe area - DashboardLayout: integrated TabStrip between Header and content - ParticleBackground: lazy render, skips on mobile/reduced-motion Layer 3 — Feature Components: - MessageCard: 30+ hardcoded hex replacements → semantic CSS vars - MessagesPanel: stat badges use Badge component variants - DashboardStats: StatCard with variant system (primary/success/warning/destructive) - UserSummaryList/UserProfileDetail/ChannelProfileDetail: all hardcoded colors → CSS vars - AudioVisualizer: reads --primary CSS var at paint time - ActiveSpeakers/RecordingsSubPanel: hardcoded colors → CSS vars Layer 4 — Polish: - EmptyState integrated across messages/dashboard/live panels - Theme toggle wired in Header + App root - Hover state audit for consistency - Entry animations verified (cardStagger/cardItem pattern in all panels) Resolves DESIGN_TOKENS.md §13.x issues: hardcoded colors, shimmer mismatch, glow-pulse fragmentation, z-index collisions, toast positioning.
This commit is contained in:
@@ -1,40 +1,156 @@
|
||||
/** ═══════════════════════════════════════════════════════════════════════
|
||||
* IMPHNEN Design System — Tailwind Configuration
|
||||
* Approachable Modernism untuk komunitas programmer Indonesia 🇮🇩
|
||||
* ═══════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.{ts,tsx}"],
|
||||
theme: {
|
||||
extend: {
|
||||
/* ─── Typography — Poppins Monofamily ─────────────────────────── */
|
||||
fontFamily: {
|
||||
sans: ["Poppins", "ui-sans-serif", "system-ui", "-apple-system", "sans-serif"],
|
||||
sans: ["Poppins", "ui-sans-serif", "system-ui", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "sans-serif"],
|
||||
display: ["Poppins", "sans-serif"],
|
||||
},
|
||||
fontSize: {
|
||||
display: ["60px", { lineHeight: "68px", fontWeight: "700", letterSpacing: "-0.04em" }],
|
||||
"headline-lg": ["40px", { lineHeight: "48px", fontWeight: "600", letterSpacing: "-0.02em" }],
|
||||
"headline-md": ["28px", { lineHeight: "36px", fontWeight: "600", letterSpacing: "-0.01em" }],
|
||||
"title-lg": ["20px", { lineHeight: "28px", fontWeight: "600" }],
|
||||
"body-lg": ["18px", { lineHeight: "28px", fontWeight: "400", letterSpacing: "0.01em" }],
|
||||
"body-md": ["16px", { lineHeight: "24px", fontWeight: "400", letterSpacing: "0.01em" }],
|
||||
"label-md": ["14px", { lineHeight: "20px", fontWeight: "600", letterSpacing: "0.02em" }],
|
||||
"label-sm": ["12px", { lineHeight: "16px", fontWeight: "500", letterSpacing: "0.03em" }],
|
||||
},
|
||||
|
||||
/* ─── Brand Colors — IMPHNEN Signature ────────────────────────── */
|
||||
colors: {
|
||||
border: "oklch(var(--border))",
|
||||
input: "oklch(var(--input))",
|
||||
ring: "oklch(var(--ring))",
|
||||
background: "oklch(var(--background))",
|
||||
foreground: "oklch(var(--foreground))",
|
||||
"primary-soft": "oklch(var(--primary-soft))",
|
||||
"primary-glow": "oklch(var(--primary-glow))",
|
||||
"accent-glow": "oklch(var(--accent-glow))",
|
||||
primary: { DEFAULT: "oklch(var(--primary))", foreground: "oklch(var(--primary-foreground))" },
|
||||
secondary: { DEFAULT: "oklch(var(--secondary))", foreground: "oklch(var(--secondary-foreground))" },
|
||||
muted: { DEFAULT: "oklch(var(--muted))", foreground: "oklch(var(--muted-foreground))" },
|
||||
accent: { DEFAULT: "oklch(var(--accent))", foreground: "oklch(var(--accent-foreground))" },
|
||||
destructive: { DEFAULT: "oklch(var(--destructive))", foreground: "oklch(var(--destructive-foreground))" },
|
||||
card: { DEFAULT: "oklch(var(--card))", foreground: "oklch(var(--card-foreground))" },
|
||||
/* Surface */
|
||||
background: "#ffffff",
|
||||
foreground: "#1a1a1a",
|
||||
muted: {
|
||||
DEFAULT: "#f5f5f5",
|
||||
foreground: "#666666",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "#f0f0f0",
|
||||
foreground: "#1a1a1a",
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "#ffffff",
|
||||
foreground: "#1a1a1a",
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "#ffffff",
|
||||
foreground: "#1a1a1a",
|
||||
},
|
||||
|
||||
/* Primary — #23a1eb */
|
||||
primary: {
|
||||
DEFAULT: "#23a1eb",
|
||||
foreground: "#ffffff",
|
||||
soft: "#e1f0fd",
|
||||
hover: "#1a8fd9",
|
||||
active: "#0877c1",
|
||||
},
|
||||
|
||||
/* Secondary — #1877f2 (Facebook) */
|
||||
secondary: {
|
||||
DEFAULT: "#1877f2",
|
||||
foreground: "#ffffff",
|
||||
soft: "#e7f1ff",
|
||||
},
|
||||
|
||||
/* Tertiary — #5865f2 (Discord) */
|
||||
tertiary: {
|
||||
DEFAULT: "#5865f2",
|
||||
foreground: "#ffffff",
|
||||
soft: "#eef0ff",
|
||||
},
|
||||
|
||||
/* Semantic */
|
||||
success: {
|
||||
DEFAULT: "#22c55e",
|
||||
soft: "#dcfce7",
|
||||
},
|
||||
warning: {
|
||||
DEFAULT: "#f59e0b",
|
||||
soft: "#fef3c7",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "#e4405f",
|
||||
foreground: "#ffffff",
|
||||
soft: "#ffebee",
|
||||
},
|
||||
info: {
|
||||
DEFAULT: "#3b82f6",
|
||||
soft: "#dbeafe",
|
||||
},
|
||||
|
||||
/* Border & Ring */
|
||||
border: "#e0e0e0",
|
||||
input: "#e0e0e0",
|
||||
ring: "#23a1eb",
|
||||
},
|
||||
|
||||
/* ─── Border Radius — Friendly Geometry ───────────────────────── */
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
sm: "0.25rem",
|
||||
DEFAULT: "0.5rem",
|
||||
md: "0.75rem",
|
||||
lg: "1rem",
|
||||
xl: "1.5rem",
|
||||
full: "9999px",
|
||||
},
|
||||
|
||||
/* ─── Box Shadow — Subtle Elevation ───────────────────────────── */
|
||||
boxShadow: {
|
||||
sm: "0 1px 2px rgba(0, 0, 0, 0.06)",
|
||||
DEFAULT: "0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04)",
|
||||
md: "0 4px 12px rgba(0, 0, 0, 0.08)",
|
||||
lg: "0 16px 40px rgba(0, 0, 0, 0.12)",
|
||||
glow: "0 0 0 3px rgba(35, 161, 235, 0.15)",
|
||||
},
|
||||
|
||||
/* ─── Spacing — Rhythm System ────────────────────────────────── */
|
||||
spacing: {
|
||||
xs: "4px",
|
||||
sm: "12px",
|
||||
md: "24px",
|
||||
lg: "40px",
|
||||
xl: "64px",
|
||||
gutter: "24px",
|
||||
},
|
||||
maxWidth: {
|
||||
container: "1280px",
|
||||
},
|
||||
|
||||
/* ─── Transition Timing ───────────────────────────────────────── */
|
||||
transitionDuration: {
|
||||
fast: "150ms",
|
||||
base: "200ms",
|
||||
slow: "300ms",
|
||||
},
|
||||
transitionTimingFunction: {
|
||||
snappy: "cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
},
|
||||
|
||||
/* ─── Animations — Signature IMPHNEN ──────────────────────────── */
|
||||
animation: {
|
||||
"bar-pulse": "bar-pulse 0.4s ease-in-out infinite",
|
||||
"bar-pulse": "barPulse 0.4s ease-in-out infinite",
|
||||
"fade-in-up": "fadeInUp 0.5s ease-out",
|
||||
"fade-in": "fadeIn 0.3s ease-out",
|
||||
"scale-in": "scaleIn 0.3s ease-out",
|
||||
"slide-in-right": "slideInRight 0.3s ease-out",
|
||||
"shimmer": "shimmer 1.5s ease-in-out infinite",
|
||||
"notification-pulse": "notificationPulse 2s ease-in-out infinite",
|
||||
"mascot-wiggle": "mascotWiggle 0.6s ease-in-out",
|
||||
"glow-pulse": "glowPulse 3s ease-in-out infinite",
|
||||
},
|
||||
|
||||
keyframes: {
|
||||
"bar-pulse": {
|
||||
barPulse: {
|
||||
"0%, 100%": { transform: "scaleY(0.8)" },
|
||||
"50%": { transform: "scaleY(1.2)" },
|
||||
},
|
||||
@@ -46,9 +162,25 @@ export default {
|
||||
"0%": { opacity: "0" },
|
||||
"100%": { opacity: "1" },
|
||||
},
|
||||
glowPulse: {
|
||||
"0%, 100%": { opacity: "0.4" },
|
||||
"50%": { opacity: "0.8" },
|
||||
scaleIn: {
|
||||
"0%": { opacity: "0", transform: "scale(0.95)" },
|
||||
"100%": { opacity: "1", transform: "scale(1)" },
|
||||
},
|
||||
slideInRight: {
|
||||
"0%": { opacity: "0", transform: "translateX(20px)" },
|
||||
"100%": { opacity: "1", transform: "translateX(0)" },
|
||||
},
|
||||
notificationPulse: {
|
||||
"0%": { boxShadow: "0 0 0 0 rgba(35, 161, 235, 0.4)" },
|
||||
"70%": { boxShadow: "0 0 0 8px transparent" },
|
||||
"100%": { boxShadow: "0 0 0 0 transparent" },
|
||||
},
|
||||
mascotWiggle: {
|
||||
"0%, 100%": { transform: "rotate(0deg)" },
|
||||
"15%": { transform: "rotate(-8deg)" },
|
||||
"30%": { transform: "rotate(6deg)" },
|
||||
"45%": { transform: "rotate(-4deg)" },
|
||||
"60%": { transform: "rotate(2deg)" },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user