diff --git a/src/app/globals.css b/src/app/globals.css
index aa9e4720..57bc0cc2 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -30,10 +30,82 @@
--animation-spin-slow: spin 2s linear infinite;
--animation-loading: loading 5s ease-in-out infinite;
--animation-spin-y: spinY 5s ease-in-out infinite;
+
+ /* Enhanced Liquid Glass System */
+ --glass-blur-primary: 16px;
+ --glass-blur-secondary: 12px;
+ --glass-blur-subtle: 8px;
+ --glass-blur-intense: 20px;
+ --glass-blur-liquid: 20px;
+ --glass-blur-navigation: 12px;
+
+ /* Enhanced Opacity for Better Readability */
+ --glass-opacity-light: 0.85;
+ --glass-opacity-medium: 0.9;
+ --glass-opacity-dark: 0.95;
+ --glass-border-opacity: 0.25;
+
+ /* Enhanced Shadows with Inner Shadow Support */
+ --glass-shadow-light: 0 8px 32px rgba(255, 255, 255, 0.1);
+ --glass-shadow-dark: 0 8px 32px rgba(0, 0, 0, 0.15);
+ --glass-inner-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ --glass-inner-shadow-dark: inset 0 1px 2px rgba(0, 0, 0, 0.2);
+
+ /* Enhanced Tinting for Better Contrast */
+ --glass-tint-light: rgba(255, 255, 255, 0.18);
+ --glass-tint-medium: rgba(255, 255, 255, 0.25);
+ --glass-tint-dark: rgba(255, 255, 255, 0.15);
+ --glass-tint-readable: rgba(255, 255, 255, 0.3);
+
+ /* Enhanced Borders */
+ --glass-border-light: rgba(0, 0, 0, 0.02);
+ --glass-border-medium: rgba(0, 0, 0, 0.005);
+ --glass-border-dark: rgba(0, 0, 0, 0.02);
+
+ /* Liquid Glass Noise Properties */
+ --glass-noise-opacity: 0.04;
+ --glass-noise-frequency: 0.02;
+ --glass-noise-scale: 50;
+ --glass-distortion-scale: 77;
}
@custom-variant dark (&:where(.dark, .dark *));
+/* Glass themes inherit base theme color schemes */
+.glass-light {
+ /* Inherits default light theme colors (already defined in @theme) */
+
+ /* Light mode specific glass tinting - all white */
+ --glass-tint-light: rgba(255, 255, 255, 0.1);
+ --glass-tint-medium: rgba(255, 255, 255, 0.05);
+ --glass-tint-dark: rgba(0, 0, 0, 0.05);
+ --glass-tint-readable: rgba(255, 255, 255, 0.3);
+}
+
+.glass-dark {
+ /* Apply dark theme color scheme to glass-dark */
+ --color-neutral: #333333;
+ --color-text: #ffffff;
+ --color-primary-selected: #ffe066;
+ --color-text-neutral: #cccccc;
+ --color-nav-item: #555555;
+ --color-tertiary: #666666;
+ --color-shadow: rgba(255, 255, 255, 0.25);
+
+ /* Dark mode liquid glass overrides */
+ --glass-tint-light: rgba(255, 255, 255, 0.08);
+ --glass-tint-medium: rgba(255, 255, 255, 0.12);
+ --glass-tint-dark: rgba(0, 0, 0, 0.15);
+ --glass-tint-readable: rgba(0, 0, 0, 0.2);
+ --glass-border-light: rgba(255, 255, 255, 0.2);
+ --glass-border-medium: rgba(255, 255, 255, 0.25);
+ --glass-border-dark: rgba(255, 255, 255, 0.1);
+ --glass-shadow-light: 0 8px 32px rgba(0, 0, 0, 0.4);
+ --glass-shadow-dark: 0 8px 32px rgba(0, 0, 0, 0.6);
+ --glass-inner-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.1);
+ --glass-inner-shadow-dark: inset 0 1px 2px rgba(255, 255, 255, 0.05);
+}
+
.dark {
--color-neutral: #333333;
--color-text: #ffffff;
@@ -42,6 +114,19 @@
--color-nav-item: #555555;
--color-tertiary: #666666;
--color-shadow: rgba(255, 255, 255, 0.25);
+
+ /* Dark mode liquid glass overrides */
+ --glass-tint-light: rgba(255, 255, 255, 0.02);
+ --glass-tint-medium: rgba(255, 255, 255, 0.06);
+ --glass-tint-dark: rgba(0, 0, 0, 0.05);
+ --glass-tint-readable: rgba(255, 255, 255, 0.15);
+ --glass-border-light: rgba(255, 255, 255, 0.2);
+ --glass-border-medium: rgba(255, 255, 255, 0.25);
+ --glass-border-dark: rgba(255, 255, 255, 0.1);
+ --glass-shadow-light: 0 8px 32px rgba(0, 0, 0, 0.4);
+ --glass-shadow-dark: 0 8px 32px rgba(0, 0, 0, 0.6);
+ --glass-inner-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.1);
+ --glass-inner-shadow-dark: inset 0 1px 2px rgba(255, 255, 255, 0.05);
}
@layer base {
@@ -65,8 +150,8 @@
}
body {
- background-color: var(--neutral);
- color: var(--text);
+ background-color: var(--color-neutral);
+ color: var(--color-text);
}
/* Disable zoom in iOS Safari when double tapping an element */
@@ -173,10 +258,504 @@ input {
outline: none;
}
+/* Legacy blur - keeping for compatibility */
.background-blur {
backdrop-filter: blur(12px);
}
+/* === SVG NOISE FILTERS FOR LIQUID GLASS === */
+.liquid-glass-filters {
+ position: absolute;
+ width: 0;
+ height: 0;
+ overflow: hidden;
+}
+
+/* Liquid glass with noise requires this SVG filter to be present in DOM */
+
+/* === ENHANCED LIQUID GLASS UTILITY CLASSES === */
+
+/* Core Liquid Glass Effects */
+.glass-light .liquid-glass-card,
+.glass-dark .liquid-glass-card {
+ backdrop-filter: blur(var(--glass-blur-liquid)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ isolation: isolate;
+}
+
+.glass-light .liquid-glass-card::before,
+.glass-dark .liquid-glass-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='liquidNoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.008' numOctaves='2' stitchTiles='stitch'/%3E%3CfeDisplacementMap in='SourceGraphic' scale='50'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23liquidNoise)' opacity='0.04'/%3E%3C/svg%3E");
+ pointer-events: none;
+ opacity: var(--glass-noise-opacity);
+ mix-blend-mode: soft-light;
+}
+
+.glass-light .liquid-glass-card::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(255, 255, 255, 0.05) 100%);
+ pointer-events: none;
+ border-radius: inherit;
+}
+
+.glass-dark .liquid-glass-card::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(0, 0, 0, 0.05) 100%);
+ pointer-events: none;
+ border-radius: inherit;
+}
+
+.liquid-glass-nav {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(160%);
+ background: linear-gradient(180deg, var(--glass-tint-readable) 0%, var(--glass-tint-medium) 100%);
+ border-bottom: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ position: fixed;
+ isolation: isolate;
+}
+
+.liquid-glass-nav::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='navNoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.01' numOctaves='1'/%3E%3CfeDisplacementMap in='SourceGraphic' scale='30'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23navNoise)' opacity='0.02'/%3E%3C/svg%3E");
+ pointer-events: none;
+ opacity: 0.5;
+}
+
+.liquid-glass-modal {
+ backdrop-filter: blur(var(--glass-blur-intense)) saturate(200%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow-dark);
+ border-radius: 4px;
+ isolation: isolate;
+}
+
+.glass-pseudo-modal {
+ position: relative;
+ isolation: isolate;
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow-dark);
+ border-radius: 4px;
+}
+
+.glass-pseudo-modal::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ backdrop-filter: blur(var(--glass-blur-intense)) saturate(200%);
+ z-index: -1;
+ border-radius: inherit;
+}
+
+.liquid-glass-readable {
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ background: var(--glass-tint-readable);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+}
+
+.liquid-glass-intense {
+ backdrop-filter: blur(var(--glass-blur-intense)) saturate(220%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow:
+ var(--glass-shadow-dark),
+ var(--glass-inner-shadow-dark),
+ inset 0 -1px 0 var(--glass-tint-dark);
+ border-radius: 16px;
+ isolation: isolate;
+}
+
+.liquid-glass-button {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ isolation: isolate;
+}
+
+.liquid-glass-button::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='buttonNoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.012' numOctaves='1'/%3E%3CfeDisplacementMap in='SourceGraphic' scale='25'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23buttonNoise)' opacity='0.03'/%3E%3C/svg%3E");
+ pointer-events: none;
+ border-radius: inherit;
+ opacity: 0.6;
+}
+
+.liquid-glass-button:hover {
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ transform: scale(1.05);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+ border-color: var(--glass-border-medium);
+}
+
+.liquid-glass-button:active {
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow-dark);
+}
+
+/* Enhanced Specialized Liquid Glass Components */
+.liquid-glass-dropdown {
+ backdrop-filter: blur(var(--glass-blur-primary)) saturate(180%);
+ background: linear-gradient(
+ 145deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+ isolation: isolate;
+}
+
+.liquid-glass-dropdown-static {
+ backdrop-filter: blur(var(--glass-blur-primary)) saturate(180%);
+ background: linear-gradient(
+ 145deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+ isolation: isolate;
+}
+
+.glass-dropdown-container {
+ backdrop-filter: blur(var(--glass-blur-primary)) saturate(180%);
+ background: linear-gradient(
+ 145deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+ isolation: isolate;
+}
+
+/* Fallback for browsers without backdrop-filter support */
+@supports not (backdrop-filter: blur(1px)) {
+ .glass-dropdown-container {
+ background: var(--color-neutral);
+ box-shadow: var(--shadow-medium);
+ }
+}
+
+/* Mobile Dropdown Sliding Animation */
+.mobile-dropdown-container {
+ position: absolute;
+ top: 0;
+ right: 0;
+ transform: translateX(var(--translate-mobile-menu));
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ width: 100vw;
+ height: 100vh;
+ z-index: 60;
+ backdrop-filter: blur(var(--glass-blur-primary)) saturate(180%);
+ background: linear-gradient(
+ 145deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+}
+
+.mobile-dropdown-slide {
+ backdrop-filter: blur(var(--glass-blur-primary)) saturate(180%);
+ background: linear-gradient(
+ 145deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+ border-radius: 0;
+ isolation: isolate;
+ width: 100%;
+ height: 100%;
+ overflow-y: auto;
+}
+
+/* Desktop hover bridge for smooth transitions */
+.dropdown-hover-bridge {
+ position: absolute;
+ pointer-events: none;
+}
+
+.glass-hover-item {
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ border-radius: inherit;
+}
+
+.glass-hover-item:hover {
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-light) 0%,
+ var(--glass-tint-medium) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+}
+
+/* Fallback for glass hover item */
+@supports not (backdrop-filter: blur(1px)) {
+ .glass-hover-item:hover {
+ background: var(--color-nav-item);
+ box-shadow: var(--shadow-small);
+ }
+}
+
+/* Mobile optimization - reduce motion and blur for performance */
+@media (prefers-reduced-motion: reduce) {
+ .glass-hover-item {
+ transition: background-color 0.2s ease;
+ }
+ .glass-hover-item:hover {
+ transform: none;
+ }
+}
+
+/* Navigation specific liquid glass */
+.liquid-glass-nav {
+ backdrop-filter: blur(var(--glass-blur-navigation)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-light) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-light) 70%,
+ var(--glass-tint-medium) 100%
+ );
+ border-right: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-medium), var(--glass-inner-shadow);
+}
+
+.liquid-glass-nav::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)' opacity='0.08'/%3E%3C/svg%3E");
+ mix-blend-mode: overlay;
+ pointer-events: none;
+ z-index: -1;
+}
+
+/* Mobile/low-power device optimization */
+@media (max-width: 768px) {
+ .glass-dropdown-container {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(150%);
+ }
+ .glass-hover-item:hover {
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(120%);
+ }
+ .liquid-glass-nav {
+ backdrop-filter: blur(calc(var(--glass-blur-navigation) * 0.7)) saturate(160%);
+ }
+}
+
+.glass-light .liquid-glass-tooltip,
+.glass-dark .liquid-glass-tooltip {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(160%);
+ background: var(--glass-tint-readable);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ border-radius: 8px;
+ position: relative;
+}
+
+/* Pseudo-Element Based Glass Classes for Nested Effects */
+.glass-light .glass-pseudo-nav,
+.glass-dark .glass-pseudo-nav {
+ position: fixed;
+ isolation: isolate;
+}
+
+.glass-light .glass-pseudo-nav::before,
+.glass-dark .glass-pseudo-nav::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ backdrop-filter: blur(var(--glass-blur-navigation)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-light) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-light) 70%,
+ var(--glass-tint-medium) 100%
+ );
+ border-right: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-medium), var(--glass-inner-shadow);
+ z-index: -1;
+ pointer-events: none;
+}
+
+.glass-pseudo-dropdown {
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ isolation: isolate;
+}
+
+.glass-pseudo-dropdown::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(180%);
+ z-index: -1;
+ border-radius: inherit;
+ transform-origin: center;
+ will-change: backdrop-filter;
+}
+
+.glass-light .glass-pseudo-item,
+.glass-dark .glass-pseudo-item {
+ position: relative;
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.glass-light .glass-pseudo-item:hover,
+.glass-dark .glass-pseudo-item:hover {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(160%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-light) 0%,
+ var(--glass-tint-medium) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+}
+
+.disable-blur {
+ backdrop-filter: none !important;
+}
+
+.glass-light .liquid-glass-primary::before,
+.glass-dark .liquid-glass-primary::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='primaryNoise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.01' numOctaves='2' stitchTiles='stitch'/%3E%3CfeDisplacementMap in='SourceGraphic' scale='30'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23primaryNoise)' opacity='0.03'/%3E%3C/svg%3E");
+ pointer-events: none;
+ opacity: 0.8;
+ mix-blend-mode: soft-light;
+ border-radius: inherit;
+}
+
+/* Tooltip fallback styles for non-glass themes */
+:not(.glass-light):not(.glass-dark) .tooltip-fallback {
+ background: var(--color-neutral);
+ border: 1px solid var(--color-tertiary);
+ box-shadow: var(--shadow-medium);
+ border-radius: 8px;
+ position: relative;
+}
+
+/* Fallback styles for non-glass themes */
+:not(.glass-light):not(.glass-dark) .glass-fallback-nav {
+ position: fixed;
+ background: var(--color-neutral);
+ box-shadow: var(--shadow-medium);
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+:not(.glass-light):not(.glass-dark) .glass-fallback-dropdown {
+ position: relative;
+ background: var(--color-neutral);
+ box-shadow: var(--shadow-medium);
+ border-radius: 4px;
+}
+
+:not(.glass-light):not(.glass-dark) .glass-fallback-item {
+ position: relative;
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ border-radius: 4px;
+}
+
+:not(.glass-light):not(.glass-dark) .glass-fallback-item:hover {
+ background: var(--color-nav-item);
+}
+
.title {
text-wrap: balance;
}
@@ -259,20 +838,6 @@ input {
transition: all 200ms ease-in-out;
}
-/* @starting-style {
- .nav-menu {
- transform: translateY(100%);
- }
-}
-
-@media (max-width: 1024px) {
- @starting-style {
- .nav-menu {
- transform: translateY(-100%);
- }
- }
-} */
-
.loading-ellipsis:after {
overflow: hidden;
display: inline-block;
@@ -803,8 +1368,8 @@ input {
}
.notifications-tooltip[data-position='right'] {
- top: -4px;
left: 60px;
+ bottom: -4px;
}
.notifications-dropdown[data-position='right'] {
@@ -828,3 +1393,343 @@ input {
.dark .profile-list-row:hover {
background-color: #444444;
}
+
+/* === ETHEREUM IDENTITY KIT GLASS STYLING === */
+/* Only applies when glass themes are active */
+
+/* Main Container Components */
+.glass-light .content-container,
+.glass-dark .content-container,
+.glass-light .summary-container,
+.glass-dark .summary-container {
+ backdrop-filter: blur(var(--glass-blur-liquid)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ isolation: isolate;
+}
+
+/* Modal-like Container Components */
+.glass-light .cart-container,
+.glass-dark .cart-container {
+ backdrop-filter: blur(var(--glass-blur-intense)) saturate(180%);
+ background: linear-gradient(135deg, var(--glass-tint-readable) 0%, var(--glass-tint-medium) 100%);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow-dark);
+ border-radius: 4px;
+ isolation: isolate;
+}
+
+/* List & Dropdown Components */
+.glass-light .cart-changes-list,
+.glass-dark .cart-changes-list,
+.glass-light .cart-recommended-list,
+.glass-dark .cart-recommended-list {
+ backdrop-filter: blur(var(--glass-blur-primary)) saturate(180%);
+ background: linear-gradient(
+ 145deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+ isolation: isolate;
+}
+
+.glass-light .notifications-dropdown-content,
+.glass-dark .notifications-dropdown-content,
+.glass-light .sort-dropdown,
+.glass-dark .sort-dropdown {
+ backdrop-filter: blur(var(--glass-blur-primary)) saturate(180%);
+ background: linear-gradient(
+ 145deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 30%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+ isolation: isolate;
+}
+
+/* Missing Modal Components */
+.glass-light .transaction-modal-container,
+.glass-dark .transaction-modal-container {
+ backdrop-filter: blur(var(--glass-blur-intense)) saturate(200%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow-dark);
+ border-radius: 4px;
+ isolation: isolate;
+}
+
+.glass-light .transaction-modal-close-button,
+.glass-dark .transaction-modal-close-button {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ isolation: isolate;
+}
+
+.glass-light .transaction-modal-close-button:hover,
+.glass-dark .transaction-modal-close-button:hover {
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ transform: scale(1.05);
+ border-color: var(--glass-border-medium);
+}
+
+/* Other Interactive Button Components */
+.glass-light .tag-button,
+.glass-dark .tag-button,
+.glass-light .more-options-refresh-button,
+.glass-dark .more-options-refresh-button,
+.glass-light .header-refresh,
+.glass-dark .header-refresh {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ isolation: isolate;
+}
+
+.glass-light .tag-button:hover,
+.glass-dark .tag-button:hover,
+.glass-light .more-options-refresh-button:hover,
+.glass-dark .more-options-refresh-button:hover,
+.glass-light .header-refresh:hover,
+.glass-dark .header-refresh:hover {
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-readable) 0%,
+ var(--glass-tint-medium) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ transform: scale(1.05);
+ border-color: var(--glass-border-medium);
+}
+
+/* Action Container Components */
+.glass-light .cart-modal-buttons-container,
+.glass-dark .cart-modal-buttons-container,
+.glass-light .transaction-modal-actions-container,
+.glass-dark .transaction-modal-actions-container {
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ background: var(--glass-tint-readable);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+}
+
+/* Data Display Components */
+.glass-light .table-header,
+.glass-dark .table-header {
+ position: relative;
+ isolation: isolate;
+ background: var(--glass-tint-readable);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+}
+
+.glass-light .table-header::before,
+.glass-dark .table-header::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ z-index: -1;
+ border-radius: inherit;
+}
+
+.glass-light .transaction-details,
+.glass-dark .transaction-details {
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ background: var(--glass-tint-readable);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+}
+
+/* Status Display Components */
+.glass-light .user-profile-status-desktop,
+.glass-dark .user-profile-status-desktop,
+.glass-light .user-profile-status-mobile,
+.glass-dark .user-profile-status-mobile {
+ backdrop-filter: blur(var(--glass-blur-liquid)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ position: relative;
+ isolation: isolate;
+}
+
+/* Item Components - Now with Base Glass Styling */
+.glass-light .transaction-item,
+.glass-dark .transaction-item,
+.glass-light .more-options-list-number,
+.glass-dark .more-options-list-number,
+.glass-light .header-left-list-number,
+.glass-dark .header-left-list-number {
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ background: var(--glass-tint-light);
+ border: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.more-options-list-number {
+ padding: 2px 6px;
+}
+
+.glass-light .more-options-list-number:hover,
+.glass-dark .more-options-list-number:hover,
+.glass-light .header-left-list-number:hover,
+.glass-dark .header-left-list-number:hover {
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-readable) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(160%);
+ border-color: var(--glass-border-medium);
+}
+
+/* Tooltip Components */
+.glass-light .notifications-tooltip,
+.glass-dark .notifications-tooltip {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(160%);
+ background: var(--glass-tint-readable);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ border-radius: 8px;
+}
+
+/* Additional Tooltip Components */
+.glass-light .notifications-tooltip-text,
+.glass-dark .notifications-tooltip-text {
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ background: var(--glass-tint-light);
+ border: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+}
+
+/* Sort Option Components */
+.glass-light .sort-option,
+.glass-dark .sort-option {
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.glass-light .sort-option:hover,
+.glass-dark .sort-option:hover {
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-readable) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ backdrop-filter: blur(var(--glass-blur-primary)) saturate(160%);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+ border-color: var(--glass-border-medium);
+}
+
+/* Empty State Components */
+.glass-light .empty-state-container,
+.glass-dark .empty-state-container,
+.glass-light .no-followings-recommendations-container,
+.glass-dark .no-followings-recommendations-container {
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ background: var(--glass-tint-readable);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+}
+
+/* Manual Add Components */
+.glass-light .manual-add-input,
+.glass-dark .manual-add-input {
+ backdrop-filter: blur(var(--glass-blur-subtle)) saturate(140%);
+ background: var(--glass-tint-readable);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.glass-light .manual-add-input:focus,
+.glass-dark .manual-add-input:focus {
+ background: linear-gradient(135deg, var(--glass-tint-readable) 0%, var(--glass-tint-medium) 100%);
+ border-color: var(--glass-border-light);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow);
+}
+
+/* Manual Add Input Placeholder Colors */
+.glass-light .manual-add-input::placeholder {
+ color: rgba(0, 0, 0, 0.5);
+}
+
+.glass-dark .manual-add-input::placeholder {
+ color: rgba(255, 255, 255, 0.7);
+}
+
+/* Cart Modal Info Container - Dark Tinted */
+.glass-light .cart-modal-buttons-container-top-info,
+.glass-dark .cart-modal-buttons-container-top-info {
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(160%);
+ background: linear-gradient(135deg, var(--glass-tint-dark) 0%, rgba(0, 0, 0, 0.25) 50%, var(--glass-tint-dark) 100%);
+ border: 1px solid var(--glass-border-medium);
+ box-shadow: var(--glass-shadow-dark), var(--glass-inner-shadow-dark);
+}
+
+/* Tab Indicator - Aggressive targeting for EIK tab indicators */
+.glass-light .tab-indicator,
+.glass-dark .tab-indicator {
+ opacity: 1;
+ backdrop-filter: blur(var(--glass-blur-secondary)) saturate(180%);
+ background: linear-gradient(
+ 135deg,
+ var(--glass-tint-medium) 0%,
+ var(--glass-tint-light) 50%,
+ var(--glass-tint-dark) 100%
+ );
+ border: 1px solid var(--glass-border-light);
+ box-shadow: var(--glass-shadow-light), var(--glass-inner-shadow);
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ isolation: isolate;
+}
+
+.check-icon {
+ top: 15px;
+}
diff --git a/src/app/integrations/components/examples/complete-profile.tsx b/src/app/integrations/components/examples/complete-profile.tsx
index f5deb966..0cd9f4ee 100644
--- a/src/app/integrations/components/examples/complete-profile.tsx
+++ b/src/app/integrations/components/examples/complete-profile.tsx
@@ -10,14 +10,22 @@ import { ProfileCard } from 'ethereum-identity-kit'
import FollowButton from '#/components/follow-button'
import ExternalLink from 'public/assets/icons/ui/external-link.svg'
import ThreeDotMenu from '#/components/user-profile-card/components/three-dot-menu'
+import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const CompleteProfile = () => {
const router = useRouter()
const { t } = useTranslation()
const { address: userAddress } = useAccount()
+ const { getGlassClass } = useGlassTheme()
return (
-
+
{t('example profile title')}
{t('example profile description')}
diff --git a/src/app/integrations/components/examples/connect-onchain.tsx b/src/app/integrations/components/examples/connect-onchain.tsx
index 56049400..5d6a5274 100644
--- a/src/app/integrations/components/examples/connect-onchain.tsx
+++ b/src/app/integrations/components/examples/connect-onchain.tsx
@@ -6,12 +6,20 @@ import { useTranslation } from 'react-i18next'
import Recommendations from '#/components/recommendations'
import ExternalLink from 'public/assets/icons/ui/external-link.svg'
+import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const ConnectOnchain = () => {
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
return (
-
+
{t('example connect onchain title')}
{t('example connect onchain description')}
@@ -24,8 +32,14 @@ const ConnectOnchain = () => {
-
)
diff --git a/src/app/integrations/components/examples/onchain-activity.tsx b/src/app/integrations/components/examples/onchain-activity.tsx
index e8645cc7..d2e0676a 100644
--- a/src/app/integrations/components/examples/onchain-activity.tsx
+++ b/src/app/integrations/components/examples/onchain-activity.tsx
@@ -4,12 +4,20 @@ import { useTranslation } from 'react-i18next'
import FeedCard from '#/components/feed-card'
import ExternalLink from 'public/assets/icons/ui/external-link.svg'
+import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const OnchainActivity = () => {
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
return (
-
+
{t('example activity title')}
{t('example activity description')}
@@ -22,7 +30,7 @@ const OnchainActivity = () => {
-
diff --git a/src/app/integrations/components/live-intergrations.tsx b/src/app/integrations/components/live-intergrations.tsx
index d78f2f31..4e85191f 100644
--- a/src/app/integrations/components/live-intergrations.tsx
+++ b/src/app/integrations/components/live-intergrations.tsx
@@ -9,12 +9,15 @@ import ExternalLink from 'public/assets/icons/ui/external-link.svg'
import { useState, useEffect } from 'react'
import { useIsClient } from '@uidotdev/usehooks'
import ArrowDown from 'public/assets/icons/ui/arrow-down.svg'
+import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const LiveIntegrations = () => {
const [displayIntegrations, setDisplayIntegrations] = useState(INTEGRATIONS.length)
const isClient = useIsClient()
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
useEffect(() => {
if (isClient) {
@@ -25,7 +28,12 @@ const LiveIntegrations = () => {
return (
-
+
{t('integrations')}
{INTEGRATIONS.length}
@@ -34,7 +42,10 @@ const LiveIntegrations = () => {
{INTEGRATIONS.slice(0, displayIntegrations).map((integration) => (
{
{displayIntegrations < INTEGRATIONS.length && (
setDisplayIntegrations(displayIntegrations * 2)}
- className='bg-neutral shadow-medium flex w-full cursor-pointer flex-row items-center justify-center gap-2 p-3'
+ className={cn(
+ getGlassClass('liquid-glass-button', 'bg-neutral shadow-medium'),
+ 'flex w-full cursor-pointer flex-row items-center justify-center gap-2 p-3'
+ )}
>
Show more
diff --git a/src/app/leaderboard/components/filters.tsx b/src/app/leaderboard/components/filters.tsx
index 795709dd..48f349f0 100644
--- a/src/app/leaderboard/components/filters.tsx
+++ b/src/app/leaderboard/components/filters.tsx
@@ -7,6 +7,7 @@ import { cn } from '#/lib/utilities'
import type { LeaderboardFilter } from '#/types/common'
import { leaderboardFiltersEmojies, leaderboardFilters } from '#/lib/constants'
import ArrowDown from 'public/assets/icons/ui/arrow-down.svg'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface FiltersProps {
filter: LeaderboardFilter
onSelectFilter: (filter: LeaderboardFilter) => void
@@ -15,6 +16,7 @@ interface FiltersProps {
const Filters: React.FC
= ({ filter, onSelectFilter }) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false)
const clickAwayRef = useClickAway(() => setIsDropdownOpen(false))
+ const { getGlassClass } = useGlassTheme()
const { t } = useTranslation()
@@ -22,7 +24,7 @@ const Filters: React.FC = ({ filter, onSelectFilter }) => {
setIsDropdownOpen((prev) => !prev)}
- className='hover:border-text/80 bg-neutral z-30 flex h-[50px] w-full cursor-pointer flex-wrap items-center justify-between gap-2 rounded-sm transition-all hover:scale-110'
+ className='hover:border-text/80 z-30 flex h-12 w-full cursor-pointer flex-wrap items-center justify-between gap-2 rounded-sm transition-all hover:scale-110'
>
= ({ filter, onSelectFilter }) => {
@@ -52,7 +55,10 @@ const Filters: React.FC
= ({ filter, onSelectFilter }) => {
onSelectFilter(item)
setIsDropdownOpen(false)
}}
- className='hover:bg-text/10 flex w-full cursor-pointer items-center gap-2 rounded-sm p-4'
+ className={cn(
+ getGlassClass('glass-hover-item', 'hover:bg-text/10'),
+ 'flex w-full cursor-pointer items-center gap-2 rounded-sm p-4'
+ )}
>
{t(item)}
= ({ currentSearch, handleSearchEvent }) => {
const [searchOpen, setSearchOpen] = useState(false)
+ const { getGlassClass } = useGlassTheme()
const clickAwayRef = useClickAway(() => setSearchOpen(false))
const { t } = useTranslation()
@@ -40,7 +42,10 @@ const Search: React.FC = ({ currentSearch, handleSearchEvent }) =>
placeholder={t('search placeholder')}
value={currentSearch}
onChange={handleSearchEvent}
- className='bg-nav-item block h-10 w-full border-0 pr-10 pl-4 font-medium sm:text-sm md:h-12'
+ className={cn(
+ getGlassClass('liquid-glass-card', 'bg-nav-item'),
+ 'block h-10 w-full border-0 pr-10 pl-4 font-medium sm:text-sm md:h-12'
+ )}
/>
diff --git a/src/app/leaderboard/components/table-headers.tsx b/src/app/leaderboard/components/table-headers.tsx
index 63c1180d..a084337e 100644
--- a/src/app/leaderboard/components/table-headers.tsx
+++ b/src/app/leaderboard/components/table-headers.tsx
@@ -5,6 +5,8 @@ import Search from './search'
import PageSelector from '#/components/page-selector'
import type { LeaderboardFilter } from '#/types/common'
import { useIsClient } from '@uidotdev/usehooks'
+import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface TableHeadersProps {
filter: LeaderboardFilter
@@ -34,9 +36,15 @@ const TableHeaders: React.FC
= ({
fetchPreviousLeaderboard,
}) => {
const isClient = useIsClient()
+ const { getGlassClass } = useGlassTheme()
return (
-
+
diff --git a/src/app/leaderboard/components/table.tsx b/src/app/leaderboard/components/table.tsx
index 6ddf32e0..1cbba92f 100644
--- a/src/app/leaderboard/components/table.tsx
+++ b/src/app/leaderboard/components/table.tsx
@@ -18,6 +18,7 @@ import { useIsClient } from '@uidotdev/usehooks'
import { useEffect } from 'react'
import { useState } from 'react'
import useStickyScroll from '#/components/home/hooks/use-sticky-scroll.ts'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const LeaderboardStatNames = ['addresses', 'lists', 'list ops', 'unique users']
@@ -25,6 +26,7 @@ let lastScrollTop = 0
const LeaderboardTable = () => {
const router = useRouter()
+ const { getGlassClass } = useGlassTheme()
const {
page,
chunk,
@@ -113,7 +115,10 @@ const LeaderboardTable = () => {
{LeaderboardStatNames.map((name, i) => (
{isLeaderboardStatsLoading || !leaderboardStats ? (
@@ -144,7 +149,12 @@ const LeaderboardTable = () => {
isFetchingPreviousLeaderboard={isFetchingPreviousLeaderboard}
/>
-
+
{leaderboard
?.slice(0, chunk * LEADERBOARD_CHUNK_SIZE)
.map((entry: LeaderboardItem, index) => (
@@ -196,7 +206,10 @@ const LeaderboardTable = () => {
{LeaderboardStatNames.map((name, i) => (
{isLeaderboardStatsLoading || !leaderboardStats ? (
@@ -213,13 +226,13 @@ const LeaderboardTable = () => {
limit={10}
endpoint='recommended'
header={t('recommended.title')}
- className={cn('bg-neutral shadow-medium h-fit w-full rounded-sm')}
+ className={cn('h-fit w-full rounded-sm')}
/>
diff --git a/src/app/providers.tsx b/src/app/providers.tsx
index 5c98d97f..1c286d75 100644
--- a/src/app/providers.tsx
+++ b/src/app/providers.tsx
@@ -1,6 +1,6 @@
'use client'
-import { useMemo } from 'react'
+import { useMemo, useEffect } from 'react'
import { useTheme } from 'next-themes'
import { ThirdwebProvider } from 'thirdweb/react'
import { WagmiProvider, type State } from 'wagmi'
@@ -16,13 +16,14 @@ import { translations } from '#/lib/constants/translations'
import TransactionModal from '#/components/transaction-modal'
import { EFPProfileProvider } from '#/contexts/efp-profile-context'
import { RecommendedProfilesProvider } from '#/contexts/recommended-profiles-context'
+import { useIsClient } from '@uidotdev/usehooks'
type ProviderProps = {
children: React.ReactNode
initialState?: State
}
-const darkThemes = ['dark', 'halloween']
+const darkThemes = ['dark', 'glass-dark', 'halloween']
const queryClient = new QueryClient({
defaultOptions: {
@@ -32,6 +33,22 @@ const queryClient = new QueryClient({
const Providers: React.FC
= ({ children, initialState }) => {
const { resolvedTheme } = useTheme()
+ const isClient = useIsClient()
+
+ // Apply both glass and base theme classes for library compatibility
+ useEffect(() => {
+ if (!isClient) return
+
+ const body = document.querySelector('body')
+ if (resolvedTheme === 'glass-dark' || resolvedTheme === 'dark') {
+ // Apply both glass-dark and dark classes
+ body?.classList.add('dark')
+ } else if (resolvedTheme === 'glass-light' || resolvedTheme === 'light') {
+ // Remove dark class for glass-light
+ body?.classList.remove('dark')
+ }
+ // For standard 'dark' and 'light' themes, next-themes handles this automatically
+ }, [resolvedTheme, isClient])
const rainbowKitTheme = useMemo(() => {
return darkThemes.includes(resolvedTheme || 'dark') ? darkTheme() : undefined
diff --git a/src/components/buttons/back-to-top.tsx b/src/components/buttons/back-to-top.tsx
index 9ca7cdc0..b9b0d5e2 100644
--- a/src/components/buttons/back-to-top.tsx
+++ b/src/components/buttons/back-to-top.tsx
@@ -3,9 +3,11 @@
import React, { useEffect, useState } from 'react'
import ArrowUp from 'public/assets/icons/ui/arrow-up.svg'
import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const BackToTop = () => {
const [isVisible, setIsVisible] = useState(false)
+ const { getGlassClass } = useGlassTheme()
useEffect(() => {
const abortController = new AbortController()
@@ -63,13 +65,16 @@ const BackToTop = () => {
return (
Back to top
diff --git a/src/components/feed-card.tsx b/src/components/feed-card.tsx
index a3ed0883..c569bdbd 100644
--- a/src/components/feed-card.tsx
+++ b/src/components/feed-card.tsx
@@ -10,6 +10,7 @@ import { useIsClient } from '@uidotdev/usehooks'
import { cn } from '#/lib/utilities'
import LoadingCell from './loaders/loading-cell'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import { RECOMMENDED_FEED_ADDRESS } from '#/lib/constants'
import { useEFPProfile } from '#/contexts/efp-profile-context'
import InterfaceLight from 'public/assets/icons/socials/interface.png'
@@ -33,6 +34,7 @@ const FeedCard: React.FC = ({ cardSize, contentSize, activityAddr
const { t } = useTranslation()
const { resolvedTheme } = useTheme()
+ const { getGlassClass } = useGlassTheme()
const url = activityAddress
? `https://app.interface.social/elements/profile/${activityAddress}/activity`
@@ -90,7 +92,8 @@ const FeedCard: React.FC = ({ cardSize, contentSize, activityAddr
{!activityAddress && (
@@ -101,7 +104,8 @@ const FeedCard: React.FC
= ({ cardSize, contentSize, activityAddr
@@ -151,7 +155,8 @@ const FeedCard: React.FC
= ({ cardSize, contentSize, activityAddr
)}
diff --git a/src/components/home/components/latest-followers.tsx b/src/components/home/components/latest-followers.tsx
index e51cc2de..8b2de981 100644
--- a/src/components/home/components/latest-followers.tsx
+++ b/src/components/home/components/latest-followers.tsx
@@ -3,16 +3,24 @@ import { useTranslation } from 'react-i18next'
import ProfileList from '#/components/profile-list'
import PageSelector from '#/components/page-selector'
import { useLatestFollowers } from '../hooks/use-latest-followers'
+import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const LatestFollowers = () => {
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
const { subPage, isLoading, setSubPage, displayedProfiles, isFetchingNextPage, isFetchingPreviousPage } =
useLatestFollowers(10, 10)
const isLatestFollowersLoading = isLoading || isFetchingNextPage || isFetchingPreviousPage
return (
-
+
{t('latest followers')}
diff --git a/src/components/home/components/profile-summary-card.tsx b/src/components/home/components/profile-summary-card.tsx
index e7301487..4d632c58 100644
--- a/src/components/home/components/profile-summary-card.tsx
+++ b/src/components/home/components/profile-summary-card.tsx
@@ -8,6 +8,7 @@ import { truncateAddress, isLinkValid } from 'ethereum-identity-kit'
import { Avatar } from '#/components/avatar'
import { formatNumber } from '#/utils/format/format-number'
import LoadingCell from '#/components/loaders/loading-cell'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import { useEFPProfile } from '#/contexts/efp-profile-context'
import ArrowRight from 'public/assets/icons/ui/arrow-right.svg'
import DefaultHeader from 'public/assets/art/default-header.svg?url'
@@ -15,6 +16,7 @@ import DefaultHeader from 'public/assets/art/default-header.svg?url'
const ProfileSummaryCard = () => {
const { t } = useTranslation()
const { profile, profileIsLoading, stats, statsIsLoading, lists, selectedList } = useEFPProfile()
+ const { getGlassClass } = useGlassTheme()
if (!profile && !profileIsLoading) return null
@@ -26,7 +28,7 @@ const ProfileSummaryCard = () => {
return (
{profileIsLoading ? (
<>
diff --git a/src/components/home/index.tsx b/src/components/home/index.tsx
index 40361d9b..9a8794ef 100644
--- a/src/components/home/index.tsx
+++ b/src/components/home/index.tsx
@@ -81,18 +81,13 @@ const Home = () => {
}}
>
-
+
{userAddress && !isFollowersEmpty && }
diff --git a/src/components/language-selector/index.tsx b/src/components/language-selector/index.tsx
index 3c21ae75..e0908d34 100644
--- a/src/components/language-selector/index.tsx
+++ b/src/components/language-selector/index.tsx
@@ -9,6 +9,7 @@ import ArrowLeft from 'public/assets/icons/ui/arrow-left.svg'
import ArrowRight from 'public/assets/icons/ui/arrow-right.svg'
import Image from 'next/image'
import { useTranslation as useIdentityKitTranslation } from 'ethereum-identity-kit'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface LanguageSelectorProps {
setExternalLanguageMenuOpen?: Dispatch
>
@@ -20,6 +21,7 @@ const LanguageSelector = ({ setExternalLanguageMenuOpen, setParentOpen }: Langua
const { t } = useTranslation()
const { changeLanguage, languageMenOpenu, selectedLanguage, setLanguageMenuOpen } = useLanguage()
+ const { getDropdownClass, getItemClass } = useGlassTheme()
const { setLanguage } = useIdentityKitTranslation()
useEffect(() => {
@@ -48,13 +50,24 @@ const LanguageSelector = ({ setExternalLanguageMenuOpen, setParentOpen }: Langua
)
return (
-
+
{
+ setLanguageMenuOpen(true)
+ setExternalLanguageMenuOpen?.(true)
+ }}
+ onMouseLeave={() => {
+ setLanguageMenuOpen(false)
+ setExternalLanguageMenuOpen?.(false)
+ }}
+ className='group relative w-full cursor-pointer'
+ >
{
setLanguageMenuOpen(!languageMenOpenu)
setExternalLanguageMenuOpen?.(!languageMenOpenu)
}}
- className='group-hover:bg-nav-item flex w-full items-center justify-between rounded-sm p-4'
+ className={`${getItemClass()} flex w-full items-center justify-between rounded-sm p-4`}
>
{selectedLanguage && (
@@ -71,14 +84,16 @@ const LanguageSelector = ({ setExternalLanguageMenuOpen, setParentOpen }: Langua
-
+
{t('back')}
@@ -104,7 +119,7 @@ const LanguageSelector = ({ setExternalLanguageMenuOpen, setParentOpen }: Langua
{regularLanguages.map((lang) => (
{
changeLanguage(lang)
@@ -128,7 +143,7 @@ const LanguageSelector = ({ setExternalLanguageMenuOpen, setParentOpen }: Langua
)}
{specialLanguages.map((lang) => (
{
changeLanguage(lang)
diff --git a/src/components/list-settings/components/settings-input.tsx b/src/components/list-settings/components/settings-input.tsx
index d9ac1702..956b44e7 100644
--- a/src/components/list-settings/components/settings-input.tsx
+++ b/src/components/list-settings/components/settings-input.tsx
@@ -7,6 +7,7 @@ import { cn } from '#/lib/utilities'
import { Avatar } from '#/components/avatar'
import { resolveEnsProfile } from '#/utils/ens'
import LoadingCell, { LIGHT_LOADING_GRADIENT } from '#/components/loaders/loading-cell'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import type React from 'react'
interface SettingsInputProps {
@@ -36,6 +37,7 @@ const SettingsInput: React.FC = ({
}) => {
const { t } = useTranslation()
const { address: connectedAddress } = useAccount()
+ const { getGlassClass } = useGlassTheme()
const { data: resolvedProfile, isLoading: isNameLoading } = useQuery({
queryKey: ['ens metadata', isAddress(value) ? value : resolvedAddress],
@@ -68,7 +70,10 @@ const SettingsInput: React.FC = ({
setValue(input)
}}
disabled={!isEditingSettings || connectedAddress?.toLowerCase() !== disableValue?.toLowerCase()}
- className='bg-nav-item w-full truncate rounded-sm p-3 font-medium disabled:cursor-not-allowed disabled:opacity-50'
+ className={cn(
+ getGlassClass('liquid-glass-card', 'bg-nav-item'),
+ 'w-full truncate rounded-sm p-3 font-medium disabled:cursor-not-allowed disabled:opacity-50'
+ )}
/>
)}
{(isSettingsLoading || value.includes('.') || resolvedProfile?.name) && (
diff --git a/src/components/list-settings/index.tsx b/src/components/list-settings/index.tsx
index 22f768e0..11c35f55 100644
--- a/src/components/list-settings/index.tsx
+++ b/src/components/list-settings/index.tsx
@@ -14,6 +14,7 @@ import ResetSlotWarning from './components/reset-slot-warning'
import type { ProfileDetailsResponse } from '#/types/requests'
import { useEFPProfile } from '#/contexts/efp-profile-context'
import PrimaryButton from '#/components/buttons/primary-button'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import List from 'public/assets/icons/ui/list.svg'
import Owner from 'public/assets/icons/ui/key.svg'
import User from 'public/assets/icons/ui/person.svg'
@@ -42,6 +43,7 @@ const ListSettings: React.FC = ({ selectedList, isSaving, onC
const { roles } = useEFPProfile()
const { address: connectedAddress } = useAccount()
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
const {
user,
@@ -152,7 +154,11 @@ const ListSettings: React.FC = ({ selectedList, isSaving, onC
setChainDropdownOpen(!chainDropdownOpen)}
disabled={!isEditingSettings || connectedAddress?.toLowerCase() !== fetchedOwner?.toLowerCase()}
>
@@ -171,7 +177,12 @@ const ListSettings: React.FC = ({ selectedList, isSaving, onC
)}
{chainDropdownOpen && (
-
+
{chains.map((item) => (
= ({ selectedList, isSaving, onC
chain: fetchedChain?.id !== item.id,
})
}}
- className='hover:bg-text/5 flex w-full cursor-pointer items-center gap-3 rounded-sm p-3'
+ className={cn(
+ getGlassClass('glass-hover-item', 'hover:bg-text/5'),
+ 'flex w-full cursor-pointer items-center gap-3 rounded-sm p-3'
+ )}
>
{item?.name}
@@ -242,7 +256,10 @@ const ListSettings: React.FC
= ({ selectedList, isSaving, onC
) : (
setIsEditingSettings(true)}
- className='bg-nav-item hover:bg-text/10 w-full rounded-sm px-6 py-3 text-lg font-bold transition-all'
+ className={cn(
+ getGlassClass('liquid-glass-button', 'bg-nav-item hover:bg-text/10'),
+ 'w-full rounded-sm px-6 py-3 text-lg font-bold transition-all'
+ )}
>
{t('edit settings')}
diff --git a/src/components/modal/index.tsx b/src/components/modal/index.tsx
index 02a65162..2b2dc1a5 100644
--- a/src/components/modal/index.tsx
+++ b/src/components/modal/index.tsx
@@ -2,6 +2,7 @@ import { cn } from '#/lib/utilities'
import Cross from 'public/assets/icons/ui/cross.svg'
import type { ReactNode } from 'react'
import { createPortal } from 'react-dom'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface ModalProps {
onCancel: () => void
children: React.ReactNode
@@ -11,6 +12,8 @@ interface ModalProps {
}
const Modal = ({ onCancel, children, className, disableBackgroundClose, closeButtonClassName }: ModalProps) => {
+ const { getGlassClass } = useGlassTheme()
+
return createPortal(
!disableBackgroundClose && onCancel()}
>
-
+
{
const { cart } = useCart()
const { t } = useTranslation()
+ const { getTooltipClass } = useGlassTheme()
const { address: userAddress } = useAccount()
const { openConnectModal } = useConnectModal()
const { setTxModalOpen, txModalOpen, pendingTxs, setChangesOpen } = useTransactions()
@@ -37,16 +39,16 @@ const CartButton = () => {
}
}}
>
-
+
- {changesCount === 0 ? null : (
+ {!!changesCount && (
{formatNumber(changesCount)}
)}
-
-
+
diff --git a/src/components/navigation/components/eth-balance.tsx b/src/components/navigation/components/eth-balance.tsx
index 8b76a6f9..84bca9ae 100644
--- a/src/components/navigation/components/eth-balance.tsx
+++ b/src/components/navigation/components/eth-balance.tsx
@@ -9,6 +9,7 @@ import { base, mainnet, optimism } from 'viem/chains'
import type { ChainWithDetails } from '#/lib/wagmi'
import { ChainIcon } from '#/components/chain-icon'
import ExternalLink from 'public/assets/icons/ui/external-link.svg'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface EthBalanceProps {
address: Address
@@ -20,6 +21,7 @@ const EthBalance: React.FC
= ({ address, chain }) => {
address: address,
chainId: chain.id,
})
+ const { getItemClass } = useGlassTheme()
const bridges = {
[base.id]: 'https://bridge.base.org/deposit',
@@ -35,7 +37,9 @@ const EthBalance: React.FC = ({ address, chain }) => {
return (
<>
-
+
{balance?.value
? Number(formatEther(balance.value)).toLocaleString(navigator.language, {
@@ -48,7 +52,7 @@ const EthBalance: React.FC
= ({ address, chain }) => {
{`${currencies[chain.id as keyof typeof currencies]} ${t('bridge')}`}
diff --git a/src/components/navigation/components/integrations.tsx b/src/components/navigation/components/integrations.tsx
index 0218cb02..f09fa99c 100644
--- a/src/components/navigation/components/integrations.tsx
+++ b/src/components/navigation/components/integrations.tsx
@@ -6,9 +6,11 @@ import { cn } from '#/lib/utilities'
import Apps from 'public/assets/icons/ui/apps.svg'
import { INTEGRATIONS } from '#/lib/constants/integrations'
import { useTranslation } from 'react-i18next'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const Integrations: React.FC = () => {
const { t } = useTranslation()
+ const { getTooltipClass } = useGlassTheme()
const pathname = usePathname()
const isSelected = pathname === '/integrations'
@@ -19,7 +21,7 @@ const Integrations: React.FC = () => {
{INTEGRATIONS.length}
diff --git a/src/components/navigation/components/list-selector.tsx b/src/components/navigation/components/list-selector.tsx
index 985e2233..53173677 100644
--- a/src/components/navigation/components/list-selector.tsx
+++ b/src/components/navigation/components/list-selector.tsx
@@ -10,6 +10,7 @@ import ArrowLeft from 'public/assets/icons/ui/arrow-left.svg'
import { useEFPProfile } from '#/contexts/efp-profile-context'
import { formatNumber } from '#/utils/format/format-number'
import { useIsEditView } from '#/hooks/use-is-edit-view'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface ListSelectorProps {
setWalletMenuOpen: (open: boolean) => void
@@ -30,6 +31,7 @@ const ListSelector: React.FC
= ({ setWalletMenuOpen, setSubMe
const isProfile = useIsEditView()
const { address: userAddress } = useAccount()
const { selectedList, lists, setSelectedList } = useEFPProfile()
+ const { getDropdownClass, getItemClass } = useGlassTheme()
if (!lists?.lists || lists.lists.length === 0) return null
@@ -46,10 +48,21 @@ const ListSelector: React.FC = ({ setWalletMenuOpen, setSubMe
}
return (
-
+
{
+ setMenuOpen(true)
+ setSubMenuOpen(true)
+ }}
+ onMouseLeave={() => {
+ setMenuOpen(false)
+ setSubMenuOpen(false)
+ }}
+ className='group relative w-full cursor-pointer'
+ >
setMenuOpen(!open)}
- className='group-hover:bg-nav-item flex w-full cursor-pointer items-center justify-between p-4 transition-opacity sm:flex-row-reverse'
+ className={`${getItemClass()} flex w-full cursor-pointer items-center justify-between rounded-sm p-4 transition-all sm:flex-row-reverse`}
>
{selectedList ? `${t('list')} #${formatNumber(selectedList)}` : t('mint new list')}
@@ -60,17 +73,17 @@ const ListSelector: React.FC
= ({ setWalletMenuOpen, setSubMe
lists?.lists && lists?.lists?.length > 0 ? (open ? 'sm:block' : 'sm:hidden') : 'hidden group-hover:hidden'
)}
>
-
+
setMenuOpen(false)}
- className='hover:bg-nav-item flex w-full cursor-pointer items-center justify-between p-4 transition-opacity sm:hidden'
+ className={`${getItemClass()} flex w-full cursor-pointer items-center justify-between rounded-sm p-4 transition-all sm:hidden`}
>
Back
{lists?.lists?.map((list) => (
onListClick(list)}
>
@@ -85,7 +98,7 @@ const ListSelector: React.FC
= ({ setWalletMenuOpen, setSubMe
))}
onListClick('new list')}
>
{selectedList === undefined &&
}
diff --git a/src/components/navigation/components/menu.tsx b/src/components/navigation/components/menu.tsx
index 64d7fc80..1b773f97 100644
--- a/src/components/navigation/components/menu.tsx
+++ b/src/components/navigation/components/menu.tsx
@@ -13,6 +13,7 @@ import { EXTERNAL_LINKS } from '#/lib/constants'
import VolumeSwitcher from '../../volume-switcher'
import LanguageSelector from '../../language-selector'
import ThemeSwitcher from '#/components/theme-switcher'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface MenuProps {
open: boolean
@@ -27,6 +28,7 @@ const Menu: React.FC
= ({ open, setOpen }) => {
const { t } = useTranslation()
const { resolvedTheme } = useTheme()
+ const { getDropdownClass, getItemClass } = useGlassTheme()
const isExtraMenuOpen = languageMenOpenu || themeMenuOpen || volumeMenuOpen
return (
@@ -35,13 +37,13 @@ const Menu: React.FC = ({ open, setOpen }) => {
open
? '-translate-y-mobile-menu flex sm:flex sm:translate-y-0 sm:opacity-100 starting:translate-y-0'
: 'translate-y-12 sm:hidden sm:translate-y-0 sm:opacity-0',
- 'top-screen shadow-menu fixed left-0 -z-20 h-fit w-full overflow-hidden transition-all transition-discrete group-hover/hamburger:flex sm:absolute sm:-top-[182px] sm:left-full sm:z-50 sm:h-fit sm:w-fit sm:overflow-visible sm:pl-9 sm:opacity-0 sm:shadow-none sm:group-hover/hamburger:opacity-100 sm:starting:opacity-0 sm:starting:group-hover/hamburger:opacity-0'
+ 'top-screen shadow-menu fixed left-0 -z-20 h-fit w-full overflow-hidden p-0 transition-all transition-discrete group-hover/hamburger:flex sm:absolute sm:-top-[182px] sm:left-full sm:z-50 sm:h-fit sm:w-fit sm:overflow-visible sm:pl-9 sm:opacity-0 sm:shadow-none sm:group-hover/hamburger:opacity-100 sm:starting:opacity-0 sm:starting:group-hover/hamburger:opacity-0'
)}
>
= ({ open, setOpen }) => {
href={link.href}
target={link.target}
onClick={() => setOpen(false)}
- className='hover:bg-nav-item text-text block w-full rounded-sm p-4 font-bold capitalize transition-colors'
+ className={`${getItemClass()} hover:bg-nav-item text-text block w-full rounded-sm p-4 font-bold capitalize transition-all`}
>
{t(link.text)}
diff --git a/src/components/navigation/components/nav-items.tsx b/src/components/navigation/components/nav-items.tsx
index b2d039f1..5682fa26 100644
--- a/src/components/navigation/components/nav-items.tsx
+++ b/src/components/navigation/components/nav-items.tsx
@@ -15,10 +15,12 @@ import { useQuery } from '@tanstack/react-query'
import { fetchAccount } from 'ethereum-identity-kit'
import LoadingCell from '#/components/loaders/loading-cell'
import { useTranslation } from 'react-i18next'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const NavItems = () => {
const pathname = usePathname()
const { t } = useTranslation()
+ const { getTooltipClass } = useGlassTheme()
const { address: userAddress } = useAccount()
const { openConnectModal } = useConnectModal()
const { selectedList, listToFetch, lists } = useEFPProfile()
@@ -48,7 +50,7 @@ const NavItems = () => {
key={item.name}
className={cn(
'group/nav-item relative z-10 rounded-sm p-1.5 transition-all',
- pathname === item.href(itemUrl) && 'bg-primary/30 text-primary-selected'
+ pathname === item.href(itemUrl) && 'liquid-glass-primary text-primary-selected'
)}
href={item.href(itemUrl)}
prefetch={true}
@@ -76,7 +78,7 @@ const NavItems = () => {
)}
diff --git a/src/components/navigation/components/powered-by-eik.tsx b/src/components/navigation/components/powered-by-eik.tsx
index f0ef790b..dad1c919 100644
--- a/src/components/navigation/components/powered-by-eik.tsx
+++ b/src/components/navigation/components/powered-by-eik.tsx
@@ -8,10 +8,12 @@ import { useIsClient } from '@uidotdev/usehooks'
import EIKLogoDark from 'public/assets/eik-dark.svg?url'
import EIKLogoLight from 'public/assets/eik-light.svg?url'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
export default function PoweredByEIK() {
const isClient = useIsClient()
const { resolvedTheme } = useTheme()
+ const { getTooltipClass } = useGlassTheme()
return (
-
+
Ethereum Identity Kit
diff --git a/src/components/navigation/components/wallet-menu.tsx b/src/components/navigation/components/wallet-menu.tsx
index c3baa742..920a50d5 100644
--- a/src/components/navigation/components/wallet-menu.tsx
+++ b/src/components/navigation/components/wallet-menu.tsx
@@ -12,6 +12,7 @@ import ListSelector from './list-selector'
import { useAutoConnect } from '#/hooks/use-auto-connect'
import WalletIcon from 'public/assets/icons/ui/wallet.svg'
import { useEFPProfile } from '#/contexts/efp-profile-context'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
const WalletMenu = () => {
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
@@ -31,6 +32,7 @@ const WalletMenu = () => {
const listChain = chains.find((chain) => chain.id === roles?.listChainId)
const deviceWidth = isClient ? window.innerWidth : 1080
+ const { getDropdownClass, getItemClass, getTooltipClass } = useGlassTheme()
useAutoConnect()
@@ -44,7 +46,7 @@ const WalletMenu = () => {
}
className={cn(
'flex items-center justify-center rounded-sm transition-all hover:scale-110',
- !userAddress && 'bg-primary text-dark-grey p-2',
+ !userAddress && `bg-primary text-dark-grey p-2`,
walletMenOpen && 'text-primary-selected'
)}
>
@@ -52,7 +54,7 @@ const WalletMenu = () => {
{!userAddress && (
-
+
{t('connect wallet')}
@@ -74,14 +76,14 @@ const WalletMenu = () => {
>
{userAddress &&
}
{
disconnect()
setWalletMenuOpen(false)
diff --git a/src/components/navigation/desktop.tsx b/src/components/navigation/desktop.tsx
index 202499f3..6b3e2dcf 100644
--- a/src/components/navigation/desktop.tsx
+++ b/src/components/navigation/desktop.tsx
@@ -6,6 +6,7 @@ import { Notifications } from 'ethereum-identity-kit'
import { Search } from '../search'
import Logo from 'public/assets/efp-logo.svg'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import NavItems from './components/nav-items.tsx'
import WalletMenu from './components/wallet-menu.tsx'
import CartButton from './components/cart-button.tsx'
@@ -17,11 +18,14 @@ const Desktop = () => {
const { width } = useWindowSize()
const isMobile = width && width < 640
const { address: userAddress } = useAccount()
+ const { getNavClass } = useGlassTheme()
if (isMobile) return null
return (
-
+
diff --git a/src/components/navigation/mobile.tsx b/src/components/navigation/mobile.tsx
index 5e24a6be..5e477c6a 100644
--- a/src/components/navigation/mobile.tsx
+++ b/src/components/navigation/mobile.tsx
@@ -8,6 +8,7 @@ import { usePathname, useRouter } from 'next/navigation'
import { Search } from '../search'
import Logo from 'public/assets/efp-logo.svg'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import NavItems from './components/nav-items'
import WalletMenu from './components/wallet-menu'
import Integrations from './components/integrations'
@@ -21,6 +22,7 @@ const Mobile: React.FC = () => {
const router = useRouter()
const pathname = usePathname()
const { address: userAddress } = useAccount()
+ const { getMobileNavClass, getGlassClass } = useGlassTheme()
const navRef = useRef
(null)
useEffect(() => {
@@ -38,11 +40,11 @@ const Mobile: React.FC = () => {
if (prevScroll - scroll > 0) {
navRef.current?.style.setProperty('transition', 'all 0.21s linear')
navRef.current?.style.setProperty('top', '0px')
- if (scroll < 16) navRef.current?.classList.remove('bg-neutral')
+ if (scroll < 16) navRef.current?.classList.remove(getGlassClass('glass-pseudo-nav', 'bg-neutral'))
} else {
if (scroll > 84) {
+ navRef.current?.classList.add(getGlassClass('glass-pseudo-nav', 'bg-neutral'))
navRef.current?.style.setProperty('top', '-84px')
- navRef.current?.classList.add('bg-neutral')
} else {
navRef.current?.style.setProperty('transition', 'none')
navRef.current?.style.setProperty('top', `-${scroll}px`)
@@ -98,7 +100,7 @@ const Mobile: React.FC = () => {
}
return () => abortController.abort()
- }, [navRef, pathname])
+ }, [navRef, pathname, userAddress])
const { width } = useWindowSize()
const isMobile = width && width < 640
@@ -114,7 +116,10 @@ const Mobile: React.FC = () => {
<>
@@ -135,7 +140,7 @@ const Mobile: React.FC = () => {
diff --git a/src/components/page-selector.tsx b/src/components/page-selector.tsx
index b8794632..5e1230b3 100644
--- a/src/components/page-selector.tsx
+++ b/src/components/page-selector.tsx
@@ -4,6 +4,8 @@ import { useRouter, useSearchParams } from 'next/navigation'
import DoubleArrowLeft from 'public/assets/icons/ui/double-arrow-left.svg'
import ArrowLeft from 'public/assets/icons/ui/short-arrow-left.svg'
import ArrowRight from 'public/assets/icons/ui/short-arrow-right.svg'
+import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface PageSelectorProps {
page: number
@@ -34,6 +36,7 @@ const PageSelector: React.FC = ({
const searchParams = useSearchParams()
const search = searchParams.get('query')
const filter = searchParams.get('filter')
+ const { getGlassClass } = useGlassTheme()
const handlePageChange = (newPage: number, skipsToFirst?: boolean) => {
if (scrollUp) {
@@ -68,7 +71,10 @@ const PageSelector: React.FC = ({
onClick={() => handlePageChange(1)}
disabled={page === 1}
aria-label='skip to first page'
- className='group hover:border-text border-text/40 disabled:border-text/10 flex h-9 w-9 items-center justify-center rounded-sm border-[3px] font-bold transition-all hover:scale-110 disabled:hover:scale-100'
+ className={cn(
+ getGlassClass('liquid-glass-button', 'group hover:border-text border-text/40 disabled:border-text/10'),
+ 'flex h-9 w-9 items-center justify-center rounded-sm border-[3px] font-bold transition-all hover:scale-110 disabled:hover:scale-100'
+ )}
>
@@ -77,12 +83,20 @@ const PageSelector: React.FC = ({
onClick={() => handlePageChange(page - 1)}
disabled={page === 1}
aria-label='previous page'
- className='group hover:border-text border-text/40 disabled:border-text/10 flex h-9 w-9 items-center justify-center rounded-sm border-[3px] font-bold transition-all hover:scale-110 disabled:hover:scale-100'
+ className={cn(
+ getGlassClass('liquid-glass-button', 'group hover:border-text border-text/40 disabled:border-text/10'),
+ 'flex h-9 w-9 items-center justify-center rounded-sm border-[3px] font-bold transition-all hover:scale-110 disabled:hover:scale-100'
+ )}
>
{displayPageNumber && (
-
+
{page}
)}
@@ -90,7 +104,10 @@ const PageSelector: React.FC = ({
onClick={() => handlePageChange(page + 1)}
disabled={!hasNextPage}
aria-label='next page'
- className='glass-card group hover:border-text border-text/40 disabled:border-text/10 flex h-9 w-9 items-center justify-center rounded-sm border-[3px] font-bold transition-all hover:scale-110 disabled:hover:scale-100'
+ className={cn(
+ getGlassClass('liquid-glass-button', 'group hover:border-text border-text/40 disabled:border-text/10'),
+ 'flex h-9 w-9 items-center justify-center rounded-sm border-[3px] font-bold transition-all hover:scale-110 disabled:hover:scale-100'
+ )}
>
diff --git a/src/components/profile-list/index.tsx b/src/components/profile-list/index.tsx
index 68c2b8da..b76c831b 100644
--- a/src/components/profile-list/index.tsx
+++ b/src/components/profile-list/index.tsx
@@ -74,7 +74,8 @@ const ProfileList: React.FC = ({
}
/>
))}
- {displayLoadingRows && new Array(loadingRows).fill(1).map((_, i) => )}
+ {displayLoadingRows &&
+ new Array(loadingRows).fill(1).map((_, i) => )}
)
}
diff --git a/src/components/profile-page-table/components/table-headers.tsx b/src/components/profile-page-table/components/table-headers.tsx
index e15c796f..4bad81bc 100644
--- a/src/components/profile-page-table/components/table-headers.tsx
+++ b/src/components/profile-page-table/components/table-headers.tsx
@@ -9,6 +9,7 @@ import type { ProfileTableTitleType } from '#/types/common'
import type { FollowSortType, TagCountType } from '#/types/requests'
import { QUERY_BLOCK_TAGS } from '#/components/blocked-muted/hooks/use-blocked-muted'
import { BLOCKED_MUTED_TABS, BLOCKED_MUTED_TAGS, SORT_OPTIONS } from '#/lib/constants'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import Tag from 'public/assets/icons/ui/tag.svg'
import Check from 'public/assets/icons/ui/check.svg'
import Cross from 'public/assets/icons/ui/cross.svg'
@@ -59,12 +60,18 @@ const TableHeader: React.FC = ({
})
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
const displayedTags = allTags?.filter((tag) => (isShowingBlocked ? true : !QUERY_BLOCK_TAGS.includes(tag.tag)))
const tagsEmpty = !tagsLoading && (!displayedTags || displayedTags.length === 0)
return (
-
+
@@ -159,7 +166,12 @@ const TableHeader: React.FC
= ({
{showSort && (
-
+
{SORT_OPTIONS.map((option) => (
= ({
className={cn(
'flex max-w-[33%] items-center gap-1.5 rounded-sm px-4 py-2 text-sm font-bold transition-transform hover:scale-110',
selectedTags?.includes(tag.tag)
- ? 'text-dark-grey bg-zinc-100 shadow-inner shadow-black/10'
- : 'bg-zinc-300/80 text-zinc-500'
+ ? 'text-text bg-neutral shadow-inner shadow-black/10'
+ : 'bg-neutral/50 text-text/70'
)}
name={tag.tag.toLowerCase()}
onClick={() => toggleSelectedTags(title, tag.tag)}
>
{BLOCKED_MUTED_TAGS.includes(tag.tag) ? t(tag.tag) : tag.tag}
-
{formatNumber(tag.count)}
+
{formatNumber(tag.count)}
))}
diff --git a/src/components/profile-page-table/index.tsx b/src/components/profile-page-table/index.tsx
index 7292039f..0f25c6ac 100644
--- a/src/components/profile-page-table/index.tsx
+++ b/src/components/profile-page-table/index.tsx
@@ -13,6 +13,7 @@ import type { ProfileTableTitleType } from '#/types/common'
import { FETCH_LIMIT_PARAM, SECOND } from '#/lib/constants'
import { useEFPProfile } from '#/contexts/efp-profile-context'
import type { TagCountType, FollowSortType, FollowerResponse, FollowingResponse } from '#/types/requests'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
let lastScrollTopUserPage = 0
export interface UserProfilePageTableProps {
@@ -91,6 +92,7 @@ const UserProfilePageTable = forwardRef
{profilesEmpty && (
-
+
{noResults}
)}
@@ -203,7 +210,11 @@ const UserProfilePageTable = forwardRef
{!isLoading &&
}
{isFollowingTable && isProfile && (lists?.lists?.length || 0) === 0 && (
diff --git a/src/components/recommendations.tsx b/src/components/recommendations.tsx
index ca40bfc8..75683226 100644
--- a/src/components/recommendations.tsx
+++ b/src/components/recommendations.tsx
@@ -8,6 +8,7 @@ import { Suspense, useEffect, useMemo, useState } from 'react'
import { cn } from '#/lib/utilities'
import ProfileList from '#/components/profile-list'
import PageSelector from '#/components/page-selector'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import { useEFPProfile } from '#/contexts/efp-profile-context'
import { fetchRecommendations } from '#/api/recommended/fetch-recommendations'
@@ -18,6 +19,7 @@ interface RecommendationsProps {
endpoint: 'discover' | 'recommended'
isTopEight?: boolean
showPageSelector?: boolean
+ disableGlass?: boolean
}
const Recommendations = ({
@@ -27,10 +29,12 @@ const Recommendations = ({
endpoint,
isTopEight = false,
showPageSelector = true,
+ disableGlass = false,
}: RecommendationsProps) => {
const [page, setPage] = useState(1)
const { selectedList } = useEFPProfile()
const { address: userAddress } = useAccount()
+ const { getGlassClass } = useGlassTheme()
const {
isLoading,
@@ -75,7 +79,13 @@ const Recommendations = ({
const hasNextPage = displayedProfiles?.length === limit
return (
-
+
{header}
diff --git a/src/components/search/index.tsx b/src/components/search/index.tsx
index 27c36f03..094fb93a 100644
--- a/src/components/search/index.tsx
+++ b/src/components/search/index.tsx
@@ -8,9 +8,11 @@ import ResultItem from './result-item.tsx'
import useSearch from './hooks/useSearch.ts'
import GraySpinner from '../loaders/gray-spinner.tsx'
import MagnifyingGlass from 'public/assets/icons/ui/magnifying-glass.svg'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
export function Search({ disabled }: { disabled?: boolean }) {
const { t } = useTranslation()
+ const { getTooltipClass } = useGlassTheme()
const {
search,
@@ -43,7 +45,7 @@ export function Search({ disabled }: { disabled?: boolean }) {
)}
/>
@@ -58,7 +60,7 @@ export function Search({ disabled }: { disabled?: boolean }) {
}
- className='bg-neutral block h-12 w-full rounded-sm px-4 font-medium shadow-md sm:h-[54px] sm:text-sm'
+ className='liquid-glass-readable placeholder:text-opacity-70 focus:liquid-glass-intense block h-12 w-full rounded-sm px-4 font-medium transition-all duration-300 sm:h-[54px] sm:text-sm'
spellCheck={false}
placeholder={t('search placeholder')}
disabled={disabled}
@@ -77,7 +79,7 @@ export function Search({ disabled }: { disabled?: boolean }) {
}}
autoComplete='off'
/>
-
+
= ({ closeMenu, setExternalThe
const isClient = useIsClient()
const { t } = useTranslation()
const { setTheme, theme: selectedTheme } = useTheme()
+ const { getDropdownClass, getItemClass } = useGlassTheme()
const selectedThemeIcon = isClient ? themesWithIcons.find(({ theme }) => theme === selectedTheme) : { icon: Computer }
return (
-
+
{
+ setThemeMenuOpen(true)
+ setExternalThemeMenuOpen?.(true)
+ }}
+ onMouseLeave={() => {
+ setThemeMenuOpen(false)
+ setExternalThemeMenuOpen?.(false)
+ }}
+ className='group relative h-full w-full cursor-pointer'
+ >
{
setThemeMenuOpen(!themeMenuOpen)
setExternalThemeMenuOpen?.(!themeMenuOpen)
}}
- className='group-hover:bg-nav-item flex w-full cursor-pointer items-center justify-between rounded-sm p-4 transition-opacity'
+ className={`${getItemClass()} flex w-full cursor-pointer items-center justify-between rounded-sm p-4 transition-opacity`}
>
{selectedThemeIcon &&
}
@@ -74,20 +97,22 @@ const ThemeSwitcher: React.FC
= ({ closeMenu, setExternalThe
themeMenuOpen ? 'block' : 'hidden'
)}
>
-
+
{
setThemeMenuOpen(false)
setExternalThemeMenuOpen?.(false)
}}
- className='hover:bg-nav-item flex w-full cursor-pointer items-center justify-between rounded-sm p-4 transition-opacity sm:hidden'
+ className={`${getItemClass()} flex w-full cursor-pointer items-center justify-between rounded-sm p-4 transition-opacity sm:hidden`}
>
{t('back')}
{themesWithIcons.map((theme) => (
{
setTheme(theme.theme as ThemeType)
diff --git a/src/components/top-eight/components/edit-modal.tsx b/src/components/top-eight/components/edit-modal.tsx
index f95d1665..867a05c1 100644
--- a/src/components/top-eight/components/edit-modal.tsx
+++ b/src/components/top-eight/components/edit-modal.tsx
@@ -9,6 +9,7 @@ import TopEightLoadingProfile from './top-eight-loading-profile'
import type { TopEightProfileType } from '../hooks/use-top-eight'
import MagnifyingGlass from 'public/assets/icons/ui/magnifying-glass.svg'
import UserProfilePageTable, { type UserProfilePageTableProps } from '#/components/profile-page-table'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface EditModalProps {
profiles: TopEightProfileType[]
@@ -18,6 +19,7 @@ interface EditModalProps {
const EditModal: React.FC
= ({ profiles, onClose, followingListProps }) => {
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
const {
editedProfiles,
addProfileSearch,
@@ -61,9 +63,17 @@ const EditModal: React.FC = ({ profiles, onClose, followingListP
}}
placeholder={t('search placeholder')}
onChange={(e) => setAddProfileSearch(e.target.value.trim().toLowerCase())}
- className='bg-nav-item block h-12 w-full truncate rounded-sm pr-12 pl-4 font-medium sm:text-sm'
+ className={cn(
+ getGlassClass('liquid-glass-card', 'bg-nav-item'),
+ 'block h-12 w-full truncate rounded-sm pr-12 pl-4 font-medium sm:text-sm'
+ )}
/>
-
@@ -87,7 +97,12 @@ const EditModal: React.FC = ({ profiles, onClose, followingListP
))}
{editedProfiles.length === 0 && (
-
+
{t('no top eight')}
)}
diff --git a/src/components/top-eight/components/top-eight-add-button.tsx b/src/components/top-eight/components/top-eight-add-button.tsx
index 6ac8a6d4..8680a9d1 100644
--- a/src/components/top-eight/components/top-eight-add-button.tsx
+++ b/src/components/top-eight/components/top-eight-add-button.tsx
@@ -10,13 +10,12 @@ interface TopEightAddButtonProps {
const TopEightAddButton: React.FC = ({ address, tags }) => {
const { isPendingTopEight, isPendingRemove, isInTopEight, handleAdd } = useTopEightAddButton(address, tags)
-
const buttonText = isInTopEight || isPendingTopEight ? 'Remove' : 'Add'
return (
{buttonText}
{(isPendingTopEight || isPendingRemove) && }
diff --git a/src/components/top-eight/components/top-eight-loading-profile.tsx b/src/components/top-eight/components/top-eight-loading-profile.tsx
index 2dce4157..3738bb21 100644
--- a/src/components/top-eight/components/top-eight-loading-profile.tsx
+++ b/src/components/top-eight/components/top-eight-loading-profile.tsx
@@ -1,16 +1,19 @@
import React from 'react'
import { cn } from '#/lib/utilities'
import LoadingCell from '#/components/loaders/loading-cell'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface TopEightLoadingProfileProps {
isEditing?: boolean
}
const TopEightLoadingProfile: React.FC = ({ isEditing }) => {
+ const { getGlassClass } = useGlassTheme()
return (
diff --git a/src/components/top-eight/components/top-eight-profile.tsx b/src/components/top-eight/components/top-eight-profile.tsx
index efab8aaa..7fa0aed7 100644
--- a/src/components/top-eight/components/top-eight-profile.tsx
+++ b/src/components/top-eight/components/top-eight-profile.tsx
@@ -18,6 +18,7 @@ import useFollowerState from '#/hooks/use-follower-state'
import LoadingCell from '#/components/loaders/loading-cell'
import type { TopEightProfileType } from '../hooks/use-top-eight'
import { listOpAddListRecord, listOpAddTag, listOpRemoveTag } from '#/utils/list-ops'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
interface TopEightProfileProps {
profile: TopEightProfileType
@@ -25,6 +26,7 @@ interface TopEightProfileProps {
}
const TopEightProfile: React.FC
= ({ profile, isEditing }) => {
+ const { getGlassClass } = useGlassTheme()
const { data: fetchedAccount, isLoading } = useQuery({
queryKey: ['account', profile.address],
queryFn: async () => await fetchAccount(profile.address),
@@ -60,10 +62,11 @@ const TopEightProfile: React.FC = ({ profile, isEditing })
return (
= ({ profile, isEditing })
{isAddingToTopEight ?
:
}
diff --git a/src/components/top-eight/index.tsx b/src/components/top-eight/index.tsx
index 36d62f9c..1e5cb1d7 100644
--- a/src/components/top-eight/index.tsx
+++ b/src/components/top-eight/index.tsx
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
import { cn } from '#/lib/utilities'
import EditModal from './components/edit-modal'
import { useTopEight } from './hooks/use-top-eight'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import ArrowDown from 'public/assets/icons/ui/arrow-down.svg'
import type { UserProfilePageTableProps } from '../profile-page-table'
import TopEightLoadingProfile from './components/top-eight-loading-profile'
@@ -26,6 +27,7 @@ const TopEight: React.FC
= ({
}) => {
const { topEight, displayLimit, setDisplayLimit, topEightIsLoading, topEightIsRefetching } = useTopEight(user)
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
const isTopEightLoading = topEightIsLoading || topEightIsRefetching
const isTopEightEmpty = topEight.length === 0 && !isTopEightLoading
@@ -41,7 +43,9 @@ const TopEight: React.FC = ({
)}
{isTopEightEmpty && (
-
+
{t('no top eight')}
)}
@@ -54,7 +58,7 @@ const TopEight: React.FC
= ({
{topEight.length > displayLimit && (
setDisplayLimit(displayLimit >= 8 ? 2 : 8)}
>
= 8 && 'rotate-180')} />
diff --git a/src/components/user-profile-card/components/three-dot-menu/components/copy-value.tsx b/src/components/user-profile-card/components/three-dot-menu/components/copy-value.tsx
index cc9010b2..2f18e302 100644
--- a/src/components/user-profile-card/components/three-dot-menu/components/copy-value.tsx
+++ b/src/components/user-profile-card/components/three-dot-menu/components/copy-value.tsx
@@ -1,7 +1,9 @@
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
+import { cn } from '#/lib/utilities'
import { SECOND } from '#/lib/constants'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import Copy from 'public/assets/icons/ui/copy.svg'
interface CopyValueProps {
value: string
@@ -11,6 +13,7 @@ interface CopyValueProps {
const CopyValue: React.FC = ({ value, text }) => {
const [hasBeenCopied, setHasBeenCopied] = useState(false)
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
return (
= ({ value, text }) => {
setHasBeenCopied(true)
setTimeout(() => setHasBeenCopied(false), 3 * SECOND)
}}
- className='hover:bg-text/5 relative flex w-full cursor-pointer items-center justify-center gap-1 rounded-sm p-4 text-xs font-bold transition-colors'
+ className={cn(
+ getGlassClass('glass-hover-item', 'hover:bg-text/5'),
+ 'relative flex w-full cursor-pointer items-center justify-center gap-1 rounded-sm p-4 text-xs font-bold transition-colors'
+ )}
>
{t(hasBeenCopied ? 'copied' : text)}
diff --git a/src/components/user-profile-card/components/three-dot-menu/components/open-modal-button.tsx b/src/components/user-profile-card/components/three-dot-menu/components/open-modal-button.tsx
index ae48252c..bd2bf9cd 100644
--- a/src/components/user-profile-card/components/three-dot-menu/components/open-modal-button.tsx
+++ b/src/components/user-profile-card/components/three-dot-menu/components/open-modal-button.tsx
@@ -1,6 +1,9 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
+import { cn } from '#/lib/utilities'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
+
interface OpenModalButtonProps {
onClick: () => void
icon?: React.ReactNode
@@ -9,10 +12,15 @@ interface OpenModalButtonProps {
const OpenModalButton: React.FC = ({ onClick, icon, text }) => {
const { t } = useTranslation()
+ const { getGlassClass } = useGlassTheme()
+
return (
{icon}
{t(text)}
diff --git a/src/components/user-profile-card/components/three-dot-menu/index.tsx b/src/components/user-profile-card/components/three-dot-menu/index.tsx
index b2ca9ad9..f1878e2a 100644
--- a/src/components/user-profile-card/components/three-dot-menu/index.tsx
+++ b/src/components/user-profile-card/components/three-dot-menu/index.tsx
@@ -11,6 +11,7 @@ import { useThreeDotMenu } from '../../hooks/use-three-dot-menu'
import RestrictButton from './components/restrict-button'
import OpenModalButton from './components/open-modal-button'
import { FOLLOW_BUTTON_COOL_EMOJI } from '#/lib/constants/follow-button'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import QrCode from 'public/assets/icons/ui/qr-code.svg'
import Settings from 'public/assets/icons/ui/settings.svg'
@@ -54,6 +55,7 @@ const ThreeDotMenu: React.FC = ({
const { t } = useTranslation()
const { resolvedTheme } = useTheme()
+ const { getGlassClass } = useGlassTheme()
const blockCoolEmoji =
FOLLOW_BUTTON_COOL_EMOJI[followState === 'blocks' || isPendingBlock ? 'Unblock' : 'Block'][resolvedTheme || 'light']
@@ -66,7 +68,10 @@ const ThreeDotMenu: React.FC = ({
return (
setThreeDotMenuOpen(!threeDotMenuOpen)}
>
@@ -76,7 +81,8 @@ const ThreeDotMenu: React.FC
= ({
{!isConnectedUserCard && (
@@ -98,7 +104,10 @@ const ThreeDotMenu: React.FC
= ({
{!isConnectedUserCard && (
{t(
(isInTopEight || isAddingToTopEight) && !isRemovingFromTopEight
diff --git a/src/components/user-profile-card/index.tsx b/src/components/user-profile-card/index.tsx
index a3ff5928..dbfc61c6 100644
--- a/src/components/user-profile-card/index.tsx
+++ b/src/components/user-profile-card/index.tsx
@@ -10,6 +10,7 @@ import Achievements from './components/achievements'
import FollowButton from '#/components/follow-button'
import ThreeDotMenu from './components/three-dot-menu'
import { useProfileCard } from './hooks/use-profile-card'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
import EnsLogo from 'public/assets/icons/socials/ens.svg'
import { useEFPProfile } from '#/contexts/efp-profile-context'
import LoadingProfileCard from './components/loading-profile-card'
@@ -55,9 +56,10 @@ const UserProfileCard: React.FC = ({
const { t } = useTranslation()
const { selectedList } = useEFPProfile()
const { followState, profileName, isConnectedUserCard } = useProfileCard(profile)
+ const { getGlassClass } = useGlassTheme()
return (
-
+
{isLoading ? (
) : profile?.address ? (
@@ -72,7 +74,6 @@ const UserProfileCard: React.FC
= ({
router.push(`/${addressOrName}?ssr=false`)
}}
selectedList={selectedList}
- className='bg-neutral'
options={{
openListSettings: openListSettingsModal,
profileData: profile,
@@ -116,7 +117,14 @@ const UserProfileCard: React.FC = ({
}}
/>
) : (
-
+
{isRecommended ? (
{t('connect to see more')}
diff --git a/src/components/volume-switcher.tsx b/src/components/volume-switcher.tsx
index 6566d120..2f8a3d2a 100644
--- a/src/components/volume-switcher.tsx
+++ b/src/components/volume-switcher.tsx
@@ -9,6 +9,7 @@ import VolumeUp from 'public/assets/icons/ui/volume-up.svg'
import ArrowLeft from 'public/assets/icons/ui/arrow-left.svg'
import VolumeMute from 'public/assets/icons/ui/volume-mute.svg'
import ArrowRight from 'public/assets/icons/ui/arrow-right.svg'
+import { useGlassTheme } from '#/hooks/use-glass-theme'
type VolumeOption = {
label: VolumeType
@@ -53,10 +54,22 @@ const VolumeSwitcher: React.FC
= ({ closeMenu, setExternalV
})
const { t } = useTranslation()
+ const { getDropdownClass, getItemClass } = useGlassTheme()
const selectedVolumeOption = volumeOptions.find(({ label }) => label === selectedVolume)
return (
-
+
{
+ setVolumeMenuOpen(true)
+ setExternalVolumeMenuOpen?.(true)
+ }}
+ onMouseLeave={() => {
+ setVolumeMenuOpen(false)
+ setExternalVolumeMenuOpen?.(false)
+ }}
+ className='group relative w-full cursor-pointer'
+ >
{
setVolumeMenuOpen(!volumeMenuOpen)
@@ -64,7 +77,7 @@ const VolumeSwitcher: React.FC
= ({ closeMenu, setExternalV
}}
className={cn(
'flex cursor-pointer items-center justify-between rounded-sm transition-opacity',
- 'group-hover:bg-nav-item w-full p-4'
+ `${getItemClass()} w-full p-4`
)}
>
@@ -75,24 +88,26 @@ const VolumeSwitcher: React.FC = ({ closeMenu, setExternalV
-
+
{
setVolumeMenuOpen(false)
setExternalVolumeMenuOpen?.(false)
}}
- className='hover:bg-nav-item flex w-full cursor-pointer items-center justify-between rounded-sm p-4 transition-opacity lg:hidden'
+ className={`${getItemClass()} flex w-full cursor-pointer items-center justify-between rounded-sm p-4 transition-opacity lg:hidden`}
>
{t('back')}
{volumeOptions.map((option) => (
{
setSelectedVolume(option.label)
diff --git a/src/hooks/use-glass-theme.ts b/src/hooks/use-glass-theme.ts
new file mode 100644
index 00000000..7d643c86
--- /dev/null
+++ b/src/hooks/use-glass-theme.ts
@@ -0,0 +1,38 @@
+import { useTheme } from 'next-themes'
+import { useIsClient } from '@uidotdev/usehooks'
+
+export const useGlassTheme = () => {
+ const { resolvedTheme } = useTheme()
+ const isClient = useIsClient()
+
+ // Use resolvedTheme for proper hydration, fallback during SSR
+ const currentTheme = isClient ? resolvedTheme : null
+ const isGlassEnabled = currentTheme === 'glass-light' || currentTheme === 'glass-dark'
+ const glassMode = currentTheme === 'glass-light' ? 'light' : currentTheme === 'glass-dark' ? 'dark' : null
+
+ const getGlassClass = (glassClass: string, fallbackClass: string = '') => {
+ return isGlassEnabled ? glassClass : fallbackClass
+ }
+
+ // Helper for tooltip backgrounds
+ const getTooltipClass = () => getGlassClass('liquid-glass-tooltip', 'tooltip-fallback')
+
+ // Helper for common navigation glass patterns
+ const getNavClass = () => getGlassClass('glass-pseudo-nav', 'glass-fallback-nav')
+ const getMobileNavClass = () => getGlassClass('glass-pseudo-nav', 'glass-fallback-nav')
+ const getDropdownClass = () => getGlassClass('liquid-glass-dropdown-static', 'glass-fallback-dropdown')
+ const getItemClass = () => getGlassClass('glass-pseudo-item', 'glass-fallback-item')
+ const getPrimaryClass = () => getGlassClass('liquid-glass-primary', 'glass-fallback-primary')
+
+ return {
+ isGlassEnabled,
+ glassMode,
+ getGlassClass,
+ getTooltipClass,
+ getNavClass,
+ getMobileNavClass,
+ getDropdownClass,
+ getItemClass,
+ getPrimaryClass,
+ }
+}
diff --git a/src/lib/constants/index.ts b/src/lib/constants/index.ts
index fc3809d6..4df9739b 100644
--- a/src/lib/constants/index.ts
+++ b/src/lib/constants/index.ts
@@ -105,7 +105,7 @@ export const EXTERNAL_LINKS = [
export const leaderboardFilters = ['mutuals', 'followers', 'following', 'top8', 'blocked'] as const
export const leaderboardFiltersEmojies = [HuggingEmoji, StarEyesEmoji, EyesEmoji, FireEmoji, PirateFlagEmoji] as const
-export const THEMES = ['light', 'dark']
+export const THEMES = ['light', 'dark', 'glass-light', 'glass-dark']
const DARK_ICON_THEME = ['dark']
export const profileCardSocials = [