Compare commits

...

4 Commits

Author SHA1 Message Date
sagit 1987960fce fix(ui): refine glassmorphism and page transition animations (#422)
Added custom light/dark background images to replace unsplash URLs.
Enhanced active sidebar item shadow and backdrop blur for better
visibility. Fixed double loading flickers on user, node, group, and
panel-sharing pages. Removed opacity transitions from AnimatedPage to
prevent backdrop-filter rendering issues during page navigation.
2026-04-06 12:28:25 +08:00
sagitchu 4c782eb815 fix(ui): refine glassmorphism and page transition animations
- Added custom light/dark background images to replace unsplash URLs.
- Enhanced active sidebar item shadow and backdrop blur for better visibility.
- Fixed double loading flickers on user, node, group, and panel-sharing pages.
- Removed opacity transitions from AnimatedPage to prevent backdrop-filter rendering issues during page navigation.
2026-04-06 12:26:29 +08:00
sagit 1e3b38f427 style: apply frosted glass backgrounds to inner cards and nested elements (#421)
Replaced solid backgrounds (bg-white, bg-default-50) with high-blur
translucent glass (backdrop-blur-3xl) inside tunnel topology nodes,
dashboard forward rules, and flow charts. Matched the aesthetic of the
inner components to inherit the adaptive light/dark mode root
backgrounds.
2026-04-06 11:04:16 +08:00
sagitchu 2166936f57 style: apply frosted glass backgrounds to inner cards and nested elements
- Replaced solid backgrounds (bg-white, bg-default-50) with high-blur translucent glass (backdrop-blur-3xl) inside tunnel topology nodes, dashboard forward rules, and flow charts.
- Matched the aesthetic of the inner components to inherit the adaptive light/dark mode root backgrounds.
2026-04-06 11:02:32 +08:00
14 changed files with 45 additions and 49 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 666 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 KiB

@@ -13,10 +13,10 @@ export const AnimatedPage = ({
className?: string; className?: string;
}) => ( }) => (
<motion.div <motion.div
animate={{ opacity: 1, y: 0 }} animate={{ y: 0 }}
className={className} className={className}
exit={{ opacity: 0, y: -8 }} exit={{ y: -8 }}
initial={{ opacity: 0, y: 16 }} initial={{ y: 16 }}
transition={{ duration: 0.28, ease: [0.25, 0.46, 0.45, 0.94] }} transition={{ duration: 0.28, ease: [0.25, 0.46, 0.45, 0.94] }}
> >
{children} {children}
@@ -28,9 +28,8 @@ export const AnimatedPage = ({
* Children should use `staggerItem` as their `variants` prop. * Children should use `staggerItem` as their `variants` prop.
*/ */
export const staggerContainer = { export const staggerContainer = {
hidden: { opacity: 0 }, hidden: {},
show: { show: {
opacity: 1,
transition: { transition: {
staggerChildren: 0.05, staggerChildren: 0.05,
delayChildren: 0.05, delayChildren: 0.05,
@@ -42,9 +41,8 @@ export const staggerContainer = {
* Individual stagger item variant — fade-in + slide-up. * Individual stagger item variant — fade-in + slide-up.
*/ */
export const staggerItem = { export const staggerItem = {
hidden: { opacity: 0, y: 12 }, hidden: { y: 12 },
show: { show: {
opacity: 1,
y: 0, y: 0,
transition: { duration: 0.25, ease: [0.25, 0.46, 0.45, 0.94] }, transition: { duration: 0.25, ease: [0.25, 0.46, 0.45, 0.94] },
}, },
+2 -2
View File
@@ -47,7 +47,7 @@ export default function AdminLayout({
const [isCollapsed, setIsCollapsed] = useState( const [isCollapsed, setIsCollapsed] = useState(
() => localStorage.getItem("sidebar_collapsed") === "true", () => localStorage.getItem("sidebar_collapsed") === "true",
); );
const [isAdmin, setIsAdmin] = useState(false); const [isAdmin, setIsAdmin] = useState(() => getAdminFlag());
const [monitorAllowed, setMonitorAllowed] = useState<boolean | null>(null); const [monitorAllowed, setMonitorAllowed] = useState<boolean | null>(null);
const [monitorAccessReason, setMonitorAccessReason] = useState<string | null>( const [monitorAccessReason, setMonitorAccessReason] = useState<string | null>(
null, null,
@@ -433,7 +433,7 @@ export default function AdminLayout({
> >
{isActive && ( {isActive && (
<motion.div <motion.div
className="absolute inset-0 rounded-2xl bg-white/80 dark:bg-white/10 shadow-[0_2px_8px_rgba(0,0,0,0.04)] border border-white/50 dark:border-white/5" className="absolute inset-0 rounded-2xl bg-white/60 dark:bg-white/10 backdrop-blur-xl shadow-[0_12px_36px_rgba(0,0,0,0.18)] border border-white dark:border-white/20"
layoutId="sidebar-active" layoutId="sidebar-active"
transition={{ transition={{
type: "spring", type: "spring",
+1 -1
View File
@@ -18,7 +18,7 @@ interface TabItem {
export default function H5Layout({ children }: { children: React.ReactNode }) { export default function H5Layout({ children }: { children: React.ReactNode }) {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const [isAdmin, setIsAdmin] = useState(false); const [isAdmin, setIsAdmin] = useState(() => getAdminFlag());
const [monitorAllowed, setMonitorAllowed] = useState<boolean | null>(null); const [monitorAllowed, setMonitorAllowed] = useState<boolean | null>(null);
const [monitorAccessReason, setMonitorAccessReason] = useState<string | null>( const [monitorAccessReason, setMonitorAccessReason] = useState<string | null>(
null, null,
+3 -3
View File
@@ -1067,7 +1067,7 @@ export default function DashboardPage() {
{group.forwards.map((forward) => ( {group.forwards.map((forward) => (
<div <div
key={forward.id} key={forward.id}
className="bg-white dark:bg-default-100/50 border border-gray-200 dark:border-default-200 rounded-lg p-3 hover:shadow-md transition-shadow" className="bg-white/10 dark:bg-black/10 backdrop-blur-md border border-white/20 dark:border-white/10 rounded-lg p-3 shadow-sm hover:shadow-md transition-all duration-300"
> >
<div className="space-y-3"> <div className="space-y-3">
<div> <div>
@@ -1076,7 +1076,7 @@ export default function DashboardPage() {
</h4> </h4>
<div className="space-y-1"> <div className="space-y-1">
<button <button
className={`block px-2 py-1 bg-default-50/50 dark:bg-default-100/20 backdrop-blur-md rounded-lg border border-default-200/50 dark:border-default-700/50 font-mono text-xs truncate text-foreground transition-colors duration-200 ${hasMultipleIps(forward.inIp) ? "cursor-pointer hover:bg-default-100 dark:hover:bg-default-200/50" : ""}`} className={`block px-2 py-1 bg-white/20 dark:bg-black/20 backdrop-blur-md rounded-lg border border-white/20 dark:border-white/10 font-mono text-xs truncate text-foreground transition-all duration-300 ${hasMultipleIps(forward.inIp) ? "cursor-pointer hover:bg-white/30 dark:hover:bg-white/10" : ""}`}
disabled={!hasMultipleIps(forward.inIp)} disabled={!hasMultipleIps(forward.inIp)}
title={formatInAddress( title={formatInAddress(
forward.inIp, forward.inIp,
@@ -1097,7 +1097,7 @@ export default function DashboardPage() {
↓ ↓
</div> </div>
<button <button
className={`block px-2 py-1 bg-default-50/50 dark:bg-default-100/20 backdrop-blur-md rounded-lg border border-default-200/50 dark:border-default-700/50 font-mono text-xs truncate text-foreground transition-colors duration-200 ${hasMultipleRemoteAddresses(forward.remoteAddr) ? "cursor-pointer hover:bg-default-100 dark:hover:bg-default-200/50" : ""}`} className={`block px-2 py-1 bg-white/20 dark:bg-black/20 backdrop-blur-md rounded-lg border border-white/20 dark:border-white/10 font-mono text-xs truncate text-foreground transition-all duration-300 ${hasMultipleRemoteAddresses(forward.remoteAddr) ? "cursor-pointer hover:bg-white/30 dark:hover:bg-white/10" : ""}`}
disabled={ disabled={
!hasMultipleRemoteAddresses( !hasMultipleRemoteAddresses(
forward.remoteAddr, forward.remoteAddr,
@@ -95,7 +95,7 @@ export const FlowChartCard = ({
: 0; : 0;
return ( return (
<div className="bg-white/70 dark:bg-zinc-900/70 backdrop-blur-xl border border-white/50 rounded-2xl shadow-[0_10px_30px_rgba(0,0,0,0.1)] p-4"> <div className="bg-white/20 dark:bg-black/20 backdrop-blur-3xl border border-white/50 rounded-2xl shadow-[0_10px_30px_rgba(0,0,0,0.1)] p-4">
<p className="font-medium text-foreground">{`时间: ${label ?? ""}`}</p> <p className="font-medium text-foreground">{`时间: ${label ?? ""}`}</p>
<p className="text-primary font-semibold mt-1">{`流量: ${formatFlow(flowValue)}`}</p> <p className="text-primary font-semibold mt-1">{`流量: ${formatFlow(flowValue)}`}</p>
</div> </div>
+6 -6
View File
@@ -3689,7 +3689,7 @@ export default function ForwardPage() {
return ( return (
<Card <Card
key={forward.id} key={forward.id}
className="group h-full flex flex-col overflow-hidden" className="group h-full flex flex-col overflow-hidden bg-white/20 dark:bg-zinc-900/20 backdrop-blur-3xl border border-white/80 dark:border-white/10 shadow-[0_15px_35px_rgba(0,0,0,0.1)]"
> >
<CardHeader className="pb-2 md:pb-2"> <CardHeader className="pb-2 md:pb-2">
<div className="flex justify-between items-start w-full"> <div className="flex justify-between items-start w-full">
@@ -3746,7 +3746,7 @@ export default function ForwardPage() {
{/* 地址信息 */} {/* 地址信息 */}
<div className="space-y-1"> <div className="space-y-1">
<button <button
className={`cursor-pointer px-2 py-1 bg-default-50 dark:bg-default-100/50 rounded border border-default-200 dark:border-default-300 transition-colors duration-200 ${ className={`cursor-pointer px-2 py-1 bg-white/20 dark:bg-black/20 backdrop-blur-md rounded border border-default-200 dark:border-default-300 transition-colors duration-200 ${
hasMultipleAddresses(forward.inIp) hasMultipleAddresses(forward.inIp)
? "hover:bg-default-100 dark:hover:bg-default-200/50" ? "hover:bg-default-100 dark:hover:bg-default-200/50"
: "" : ""
@@ -3786,7 +3786,7 @@ export default function ForwardPage() {
</button> </button>
<button <button
className={`cursor-pointer px-2 py-1 bg-default-50 dark:bg-default-100/50 rounded border border-default-200 dark:border-default-300 transition-colors duration-200 ${ className={`cursor-pointer px-2 py-1 bg-white/20 dark:bg-black/20 backdrop-blur-md rounded border border-default-200 dark:border-default-300 transition-colors duration-200 ${
hasMultipleAddresses(forward.remoteAddr) hasMultipleAddresses(forward.remoteAddr)
? "hover:bg-default-100 dark:hover:bg-default-200/50" ? "hover:bg-default-100 dark:hover:bg-default-200/50"
: "" : ""
@@ -4256,7 +4256,7 @@ export default function ForwardPage() {
</Card> </Card>
</> </>
) : ( ) : (
<Card> <Card className="bg-white/20 dark:bg-zinc-900/20 backdrop-blur-3xl border border-white/80 dark:border-white/10 shadow-[0_15px_35px_rgba(0,0,0,0.1)]">
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]"> <CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2"> <h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
暂无规则配置 暂无规则配置
@@ -4302,7 +4302,7 @@ export default function ForwardPage() {
</DndContext> </DndContext>
</> </>
) : ( ) : (
<Card> <Card className="bg-white/20 dark:bg-zinc-900/20 backdrop-blur-3xl border border-white/80 dark:border-white/10 shadow-[0_15px_35px_rgba(0,0,0,0.1)]">
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]"> <CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2"> <h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
暂无规则配置 暂无规则配置
@@ -4562,7 +4562,7 @@ export default function ForwardPage() {
})} })}
</div> </div>
) : ( ) : (
<Card> <Card className="bg-white/20 dark:bg-zinc-900/20 backdrop-blur-3xl border border-white/80 dark:border-white/10 shadow-[0_15px_35px_rgba(0,0,0,0.1)]">
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]"> <CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2"> <h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
暂无规则配置 暂无规则配置
+12 -10
View File
@@ -1,6 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { AnimatedPage } from "@/components/animated-page";
import { PageLoadingState } from "@/components/page-state";
import { Card, CardBody, CardHeader } from "@/shadcn-bridge/heroui/card"; import { Card, CardBody, CardHeader } from "@/shadcn-bridge/heroui/card";
import { Button } from "@/shadcn-bridge/heroui/button"; import { Button } from "@/shadcn-bridge/heroui/button";
import { Input } from "@/shadcn-bridge/heroui/input"; import { Input } from "@/shadcn-bridge/heroui/input";
@@ -22,7 +24,7 @@ import {
TableRow, TableRow,
} from "@/shadcn-bridge/heroui/table"; } from "@/shadcn-bridge/heroui/table";
import { Chip } from "@/shadcn-bridge/heroui/chip"; import { Chip } from "@/shadcn-bridge/heroui/chip";
import { Spinner } from "@/shadcn-bridge/heroui/spinner";
import { import {
assignGroupPermission, assignGroupPermission,
assignTunnelsToGroup, assignTunnelsToGroup,
@@ -469,14 +471,12 @@ export default function GroupPage() {
} }
return ( return (
<div className="px-3 lg:px-6 py-8 space-y-6"> <AnimatedPage className="px-3 lg:px-6 py-8 space-y-6">
{loading && ( {loading ? (
<div className="flex justify-center py-10"> <PageLoadingState message="正在加载..." />
<Spinner size="lg" /> ) : (
</div> <>
)} <Card>
<Card>
<CardHeader className="flex flex-row items-center gap-3 pb-2"> <CardHeader className="flex flex-row items-center gap-3 pb-2">
<h3 className="text-lg font-semibold">隧道分组</h3> <h3 className="text-lg font-semibold">隧道分组</h3>
<Button <Button
@@ -922,6 +922,8 @@ export default function GroupPage() {
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>
</Modal> </Modal>
</div> </>
)}
</AnimatedPage>
); );
} }
+1 -1
View File
@@ -288,7 +288,7 @@ const SortableItem = ({
export default function NodePage() { export default function NodePage() {
const [nodeList, setNodeList] = useState<Node[]>([]); const [nodeList, setNodeList] = useState<Node[]>([]);
const [nodeOrder, setNodeOrder] = useState<number[]>([]); const [nodeOrder, setNodeOrder] = useState<number[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(true);
const [localSearchKeyword, setLocalSearchKeyword] = useLocalStorageState( const [localSearchKeyword, setLocalSearchKeyword] = useLocalStorageState(
"node-search-keyword-local", "node-search-keyword-local",
"", "",
+1 -1
View File
@@ -89,7 +89,7 @@ export default function PanelSharingPage() {
[], [],
); );
const [nodes, setNodes] = useState<Node[]>([]); const [nodes, setNodes] = useState<Node[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(true);
const [remoteUsageLoading, setRemoteUsageLoading] = useState(false); const [remoteUsageLoading, setRemoteUsageLoading] = useState(false);
// Modals // Modals
+9 -9
View File
@@ -1578,7 +1578,7 @@ export default function TunnelPage() {
{/* 隧道卡片网格 */} {/* 隧道卡片网格 */}
{tunnels.length > 0 ? ( {tunnels.length > 0 ? (
viewMode === "list" ? ( viewMode === "list" ? (
<Card> <Card className="bg-white/20 dark:bg-zinc-900/20 backdrop-blur-3xl border border-white/80 dark:border-white/10 shadow-[0_15px_35px_rgba(0,0,0,0.1)]">
<Table <Table
aria-label="隧道列表" aria-label="隧道列表"
className="overflow-x-auto min-w-full" className="overflow-x-auto min-w-full"
@@ -1721,7 +1721,7 @@ export default function TunnelPage() {
{(listeners) => ( {(listeners) => (
<Card <Card
key={tunnel.id} key={tunnel.id}
className="group overflow-hidden" className="group overflow-hidden bg-white/20 dark:bg-zinc-900/20 backdrop-blur-3xl border border-white/80 dark:border-white/10 shadow-[0_15px_35px_rgba(0,0,0,0.1)]"
> >
<CardHeader className="pb-2 md:pb-2"> <CardHeader className="pb-2 md:pb-2">
<div className="flex justify-between items-start w-full"> <div className="flex justify-between items-start w-full">
@@ -1771,7 +1771,7 @@ export default function TunnelPage() {
<div className="pt-2 border-t border-divider"> <div className="pt-2 border-t border-divider">
<div className="flex items-center justify-center gap-2 text-xs"> <div className="flex items-center justify-center gap-2 text-xs">
{/* 入口节点 */} {/* 入口节点 */}
<div className="flex items-center gap-1 px-2 py-1 bg-primary-50 dark:bg-primary-100/20 rounded border border-primary-200 dark:border-primary-300/20"> <div className="flex items-center gap-1 px-2 py-1 bg-primary-50/30 dark:bg-primary-100/20 backdrop-blur-md rounded border border-primary-200/50 dark:border-primary-300/20">
<svg <svg
aria-hidden="true" aria-hidden="true"
className="w-3 h-3 text-primary-600" className="w-3 h-3 text-primary-600"
@@ -1806,7 +1806,7 @@ export default function TunnelPage() {
</svg> </svg>
{/* 转发链 */} {/* 转发链 */}
<div className="flex items-center gap-1 px-2 py-1 bg-secondary-50 dark:bg-secondary-100/20 rounded border border-secondary-200 dark:border-secondary-300/20"> <div className="flex items-center gap-1 px-2 py-1 bg-secondary-50/30 dark:bg-secondary-100/20 backdrop-blur-md rounded border border-secondary-200/50 dark:border-secondary-300/20">
<svg <svg
aria-hidden="true" aria-hidden="true"
className="w-3 h-3 text-secondary-600" className="w-3 h-3 text-secondary-600"
@@ -1844,7 +1844,7 @@ export default function TunnelPage() {
</svg> </svg>
{/* 出口节点 */} {/* 出口节点 */}
<div className="flex items-center gap-1 px-2 py-1 bg-success-50 dark:bg-success-100/20 rounded border border-success-200 dark:border-success-300/20"> <div className="flex items-center gap-1 px-2 py-1 bg-success-50/30 dark:bg-success-100/20 backdrop-blur-md rounded border border-success-200/50 dark:border-success-300/20">
<svg <svg
aria-hidden="true" aria-hidden="true"
className="w-3 h-3 text-success-600" className="w-3 h-3 text-success-600"
@@ -1871,7 +1871,7 @@ export default function TunnelPage() {
<div <div
className={`grid gap-2 ${tunnel.type === 2 && tunnel.ipPreference ? "grid-cols-3" : "grid-cols-2"}`} className={`grid gap-2 ${tunnel.type === 2 && tunnel.ipPreference ? "grid-cols-3" : "grid-cols-2"}`}
> >
<div className="text-center p-1.5 bg-default-50 dark:bg-default-100/30 rounded"> <div className="text-center p-1.5 bg-white/5 dark:bg-black/5 backdrop-blur-3xl rounded-lg border border-divider">
<div className="text-xs text-default-500"> <div className="text-xs text-default-500">
流量计算 流量计算
</div> </div>
@@ -1879,7 +1879,7 @@ export default function TunnelPage() {
{getTunnelFlowDisplay(tunnel.flow)} {getTunnelFlowDisplay(tunnel.flow)}
</div> </div>
</div> </div>
<div className="text-center p-1.5 bg-default-50 dark:bg-default-100/30 rounded"> <div className="text-center p-1.5 bg-white/5 dark:bg-black/5 backdrop-blur-3xl rounded-lg border border-divider">
<div className="text-xs text-default-500"> <div className="text-xs text-default-500">
流量倍率 流量倍率
</div> </div>
@@ -1888,7 +1888,7 @@ export default function TunnelPage() {
</div> </div>
</div> </div>
{tunnel.type === 2 && tunnel.ipPreference && ( {tunnel.type === 2 && tunnel.ipPreference && (
<div className="text-center p-1.5 bg-default-50 dark:bg-default-100/30 rounded"> <div className="text-center p-1.5 bg-white/5 dark:bg-black/5 backdrop-blur-3xl rounded-lg border border-divider">
<div className="text-xs text-default-500"> <div className="text-xs text-default-500">
连接偏好 连接偏好
</div> </div>
@@ -1986,7 +1986,7 @@ export default function TunnelPage() {
) )
) : ( ) : (
/* 空状态 */ /* 空状态 */
<Card> <Card className="bg-white/20 dark:bg-zinc-900/20 backdrop-blur-3xl border border-white/80 dark:border-white/10 shadow-[0_15px_35px_rgba(0,0,0,0.1)]">
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]"> <CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2"> <h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
暂无隧道配置 暂无隧道配置
+1 -5
View File
@@ -185,7 +185,7 @@ const normalizeUserTunnelItem = (item: Partial<UserTunnel>): UserTunnel => {
export default function UserPage() { export default function UserPage() {
// 状态管理 // 状态管理
const [users, setUsers] = useState<User[]>([]); const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(true);
const [searchKeyword, setSearchKeyword] = useLocalStorageState( const [searchKeyword, setSearchKeyword] = useLocalStorageState(
"user-search-keyword", "user-search-keyword",
"", "",
@@ -466,10 +466,6 @@ export default function UserPage() {
void loadMonitorPermissions(); void loadMonitorPermissions();
}, [loadMonitorPermissions, loadSpeedLimits, loadTunnels, loadUserGroups]); }, [loadMonitorPermissions, loadSpeedLimits, loadTunnels, loadUserGroups]);
useEffect(() => {
void loadUsers();
}, [loadUsers]);
useEffect(() => { useEffect(() => {
if (searchDebounceRef.current) { if (searchDebounceRef.current) {
clearTimeout(searchDebounceRef.current); clearTimeout(searchDebounceRef.current);
+3 -3
View File
@@ -241,16 +241,16 @@ body {
bottom: 0; bottom: 0;
z-index: -1; z-index: -1;
pointer-events: none; pointer-events: none;
background: url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2564&auto=format&fit=crop') no-repeat center center; background: url('/images/bg-light.jpg') no-repeat center center;
background-size: cover; background-size: cover;
} }
:root.dark .bg-mesh-gradient { .dark .bg-mesh-gradient {
background-color: #0b1020; background-color: #0b1020;
} }
.dark .bg-mesh-gradient::before { .dark .bg-mesh-gradient::before {
background: url('https://images.unsplash.com/photo-1557682250-33bd709cbe85?q=80&w=2564&auto=format&fit=crop') no-repeat center center; background: url('/images/bg-dark.jpg') no-repeat center center;
background-size: cover; background-size: cover;
} }