Skip to content

Commit 62b4611

Browse files
committed
update uiux
1 parent bcab35a commit 62b4611

File tree

3 files changed

+196
-32
lines changed

3 files changed

+196
-32
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# macOS 26 Liquid Glass Style Guide
2+
3+
This guide defines the "macOS 26" Liquid Glass design language, characterized by physical volume, complex light interactions, dynamic morphing animations, and a complete absence of traditional borders.
4+
5+
## Core Philosophy
6+
7+
1. **No Borders (`border-none`)**: Edges are defined solely by internal highlights and external shadows, simulating a thick, translucent material.
8+
2. **Liquid Physics**: Interactive elements behave like viscous, glowing liquid—morphing (scaling/blurring) during movement and settling without bounce (over-damped).
9+
3. **Complex Lighting**: Uses multiple layers of inner shadows (`inset`) to simulate surface reflection (top-left) and subsurface scattering (bottom-right).
10+
4. **Volume & Texture**: High saturation (`backdrop-saturate-150`) and heavy blur (`backdrop-blur-[14px]`) create a rich, crystal-like texture.
11+
12+
## Component Implementation
13+
14+
### 1. Liquid Glass Button
15+
16+
Buttons use a complex shadow stack to replace borders and create depth.
17+
18+
**Tailwind Utilities:**
19+
```tsx
20+
className="
21+
relative overflow-hidden rounded-full border-none
22+
bg-black/5 dark:bg-white/10
23+
backdrop-blur-[14px] backdrop-saturate-150
24+
25+
/* Light Mode Shadows: Bright top-left highlight, soft bottom-right shade, subtle drop shadow */
26+
shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.7),inset_-1px_-1px_0_0_rgba(0,0,0,0.05),0_4px_12px_rgba(0,0,0,0.05)]
27+
28+
/* Dark Mode Shadows: Sharper, glassier highlights on both sides */
29+
dark:shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.3),inset_-1px_-1px_0_0_rgba(255,255,255,0.1),0_4px_12px_rgba(0,0,0,0.2)]
30+
31+
transition-all
32+
33+
/* Hover State: Lift up, intensify highlights and shadows */
34+
hover:-translate-y-0.5
35+
hover:bg-black/10 dark:hover:bg-white/20
36+
hover:shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.9),inset_-1px_-1px_0_0_rgba(0,0,0,0.1),0_6px_16px_rgba(0,0,0,0.1)]
37+
38+
/* Active State: Press down */
39+
active:translate-y-0 active:scale-95
40+
"
41+
```
42+
43+
**Volume Gradient (Inner Element):**
44+
Add an absolute positioned child to simulate internal light refraction.
45+
```tsx
46+
<span className="
47+
absolute inset-0 -z-10
48+
bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.4),transparent_50%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.1),transparent_50%)]
49+
opacity-0 group-hover:opacity-100 transition-opacity
50+
" />
51+
```
52+
53+
### 2. Morphing Tab Indicator
54+
55+
The active tab indicator behaves like a sliding drop of liquid light.
56+
57+
**Container:**
58+
Minimalist groove to let the indicator shine.
59+
```tsx
60+
<div className="relative flex w-full justify-center rounded-full bg-muted/50 p-1">
61+
{/* Tabs... */}
62+
</div>
63+
```
64+
65+
**Indicator (Motion Component):**
66+
Uses `framer-motion` for the liquid morphing effect.
67+
68+
**Visual Style:**
69+
```tsx
70+
className="
71+
absolute inset-0 rounded-full
72+
bg-primary
73+
/* Strong glass edges + colored drop shadow */
74+
shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.4),inset_-1px_-1px_0_0_rgba(0,0,0,0.2),0_4px_12px_rgba(0,0,0,0.3)]
75+
backdrop-blur-md
76+
"
77+
```
78+
79+
**Animation Physics:**
80+
- **Scale**: Expands to 115% mid-flight to simulate surface tension.
81+
- **Filter**: Blurs (12px) and Brightens (200%) during fast movement (Motion Blur + Flash).
82+
- **Timing**: Fast attack (35% to peak), over-damped spring (no bounce).
83+
84+
```tsx
85+
<motion.div
86+
initial={{ scale: 1, filter: "blur(0px)" }}
87+
animate={{
88+
// Morph: Expand -> Contract
89+
scale: [1, 1.15, 1],
90+
// Effect: Clear -> Motion Blur + Flash -> Clear
91+
filter: ["blur(0px) brightness(1)", "blur(12px) brightness(2)", "blur(0px) brightness(1)"]
92+
}}
93+
transition={{
94+
layout: {
95+
type: "spring",
96+
stiffness: 350, // High stiffness
97+
damping: 40, // Over-damped (Critical damping ~37.4)
98+
},
99+
scale: {
100+
duration: 0.25,
101+
ease: "easeInOut",
102+
times: [0, 0.35, 1] // Peak early at 35%
103+
},
104+
filter: {
105+
duration: 0.25,
106+
ease: "easeInOut",
107+
times: [0, 0.35, 1]
108+
}
109+
}}
110+
>
111+
{/* Inner Volume Gradient */}
112+
<div className="absolute inset-0 rounded-full bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.25),transparent_60%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.1),transparent_60%)]" />
113+
</motion.div>
114+
```
115+
116+
## Usage Checklist
117+
118+
- [ ] Remove all `border-*` classes.
119+
- [ ] Ensure `z-index` handling prevents clipping when elements expand (e.g., set container `z-30`, active item `z-20`).
120+
- [ ] Check Light/Dark mode contrast for inner highlights (Light mode needs stronger white highlights, Dark mode needs subtler ones).
121+
- [ ] Verify `backdrop-blur` and `saturate` are applied for that "thick glass" look.

dify-helm-watchdog/src/components/modals/diff-comparison-modal.tsx

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -110,30 +110,49 @@ export default function DiffComparisonModal({
110110

111111
{/* Tabs and Filter Toggle */}
112112
<div className="flex items-center gap-4">
113-
<div className="relative flex flex-[9] justify-center rounded-full border border-border bg-muted p-1 min-w-0">
113+
<div className="relative flex flex-[9] justify-center rounded-full bg-black/10 p-1 min-w-0 dark:bg-muted/50">
114114
{tabs.map((tab) => {
115115
const isActive = tab.id === activeTabId;
116116
return (
117117
<button
118118
key={tab.id}
119119
type="button"
120120
onClick={() => onTabChange(tab.id)}
121-
className={`relative z-10 flex-1 rounded-full px-4 py-2 text-xs font-semibold uppercase tracking-[0.25em] transition-colors ${
121+
className={`relative flex-1 rounded-full px-4 py-2 text-xs font-semibold uppercase tracking-[0.25em] transition-colors ${
122122
isActive
123-
? "text-primary-foreground"
124-
: "text-muted-foreground hover:text-foreground"
123+
? "z-20 text-primary-foreground shadow-sm"
124+
: "z-10 text-muted-foreground hover:text-foreground"
125125
}`}
126126
>
127127
{isActive && (
128128
<motion.div
129129
layoutId="diff-tab-indicator"
130-
className="absolute inset-0 rounded-full border border-primary bg-primary"
130+
className="absolute inset-0 rounded-full bg-primary shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.4),inset_-1px_-1px_0_0_rgba(0,0,0,0.2),0_4px_12px_rgba(0,0,0,0.3)] backdrop-blur-md"
131+
initial={{ scale: 1, filter: "blur(0px)" }}
132+
animate={{
133+
scale: [1, 1.4, 1],
134+
filter: ["blur(0px) brightness(1)", "blur(12px) brightness(2)", "blur(0px) brightness(1)"]
135+
}}
131136
transition={{
132-
type: "spring",
133-
stiffness: 500,
134-
damping: 35,
137+
layout: {
138+
type: "spring",
139+
stiffness: 350,
140+
damping: 40,
141+
},
142+
scale: {
143+
duration: 0.25,
144+
ease: "easeInOut",
145+
times: [0, 0.35, 1]
146+
},
147+
filter: {
148+
duration: 0.25,
149+
ease: "easeInOut",
150+
times: [0, 0.35, 1]
151+
}
135152
}}
136-
/>
153+
>
154+
<div className="absolute inset-0 rounded-full bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.25),transparent_60%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.1),transparent_60%)]" />
155+
</motion.div>
137156
)}
138157
<span className="relative z-10">{tab.label}</span>
139158
</button>
@@ -143,17 +162,20 @@ export default function DiffComparisonModal({
143162
<motion.button
144163
type="button"
145164
onClick={() => setShowDiffOnly((prev) => !prev)}
146-
whileHover={{ scale: 1.05 }}
147-
whileTap={{ scale: 0.95 }}
148-
className={`inline-flex items-center gap-2 rounded-full border px-4 py-2 text-xs font-semibold uppercase tracking-[0.25em] transition-colors whitespace-nowrap shrink-0 flex-[1] justify-center ${
165+
whileHover={{ scale: 1.02 }}
166+
whileTap={{ scale: 0.98 }}
167+
className={`group inline-flex items-center gap-2 overflow-hidden rounded-full border-none px-4 py-2 text-xs font-semibold uppercase tracking-[0.25em] transition-all whitespace-nowrap shrink-0 flex-[1] justify-center shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.4),inset_-1px_-1px_0_0_rgba(0,0,0,0.1),0_2px_8px_rgba(0,0,0,0.1)] backdrop-blur-[14px] backdrop-saturate-150 ${
149168
showDiffOnly
150-
? "border-primary bg-primary text-primary-foreground"
151-
: "border-border bg-muted text-muted-foreground hover:border-accent hover:bg-accent/10 hover:text-foreground"
169+
? "bg-primary text-primary-foreground shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.4),inset_-1px_-1px_0_0_rgba(0,0,0,0.2),0_4px_12px_rgba(var(--color-primary),0.4)]"
170+
: "bg-white/50 text-muted-foreground hover:bg-white/70 hover:text-foreground dark:bg-white/5 dark:hover:bg-white/10"
152171
}`}
153172
aria-label={showDiffOnly ? "Show all lines" : "Show diff only"}
154173
>
155-
<Filter className="h-3.5 w-3.5" />
156-
<span>Diff Only</span>
174+
{showDiffOnly && (
175+
<div className="absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.25),transparent_60%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.1),transparent_60%)]" />
176+
)}
177+
<Filter className={`h-3.5 w-3.5 ${showDiffOnly ? "drop-shadow-sm" : ""}`} />
178+
<span className={showDiffOnly ? "drop-shadow-sm" : ""}>Diff Only</span>
157179
</motion.button>
158180
</div>
159181

dify-helm-watchdog/src/components/version-explorer.tsx

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -963,12 +963,13 @@ export function VersionExplorer({ data }: VersionExplorerProps) {
963963
event.stopPropagation();
964964
handleOpenWizard();
965965
}}
966-
className="absolute bottom-3 right-3 z-10 inline-flex items-center gap-1.5 rounded-full border border-white/20 bg-gradient-to-br from-white/25 to-white/10 px-3 py-1 text-[10px] font-medium text-white shadow-[0_4px_16px_rgba(0,0,0,0.15),inset_0_1px_0_rgba(255,255,255,0.3)] backdrop-blur-md transition-all hover:-translate-y-0.5 hover:from-white/35 hover:to-white/20 hover:shadow-[0_8px_20px_rgba(0,0,0,0.2),inset_0_1px_0_rgba(255,255,255,0.4)] active:translate-y-0 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50"
966+
className="group/wizard absolute bottom-3 right-3 z-10 inline-flex items-center gap-1.5 overflow-hidden rounded-full border-none bg-white/20 px-3 py-1 text-[10px] font-medium text-white shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.6),inset_-1px_-1px_0_0_rgba(255,255,255,0.3),0_4px_16px_rgba(0,0,0,0.2)] backdrop-blur-[14px] backdrop-saturate-150 transition-all hover:-translate-y-0.5 hover:bg-white/30 hover:shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.8),inset_-1px_-1px_0_0_rgba(255,255,255,0.5),0_6px_20px_rgba(0,0,0,0.25)] active:translate-y-0 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50"
967967
aria-label="Open tag update wizard"
968968
title="Open tag update wizard"
969969
>
970-
<MapPinned className="h-3.5 w-3.5" />
971-
update tag to this
970+
<span className="absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.3),transparent_50%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.15),transparent_50%)] opacity-100 transition-opacity group-hover/wizard:opacity-100" />
971+
<MapPinned className="h-3.5 w-3.5 drop-shadow-sm" />
972+
<span className="drop-shadow-sm">update tag to this</span>
972973
</button>
973974
) : null}
974975
{showDiffIcon ? (
@@ -978,7 +979,7 @@ export function VersionExplorer({ data }: VersionExplorerProps) {
978979
event.stopPropagation();
979980
openDiffModal(version.version);
980981
}}
981-
className="absolute bottom-3 right-3 z-10 inline-flex items-center gap-1.5 rounded-full border border-primary/10 bg-gradient-to-br from-primary/10 to-primary/5 px-3 py-1 text-[10px] font-medium text-foreground shadow-[0_4px_12px_rgba(0,0,0,0.05),inset_0_1px_0_rgba(255,255,255,0.4)] backdrop-blur-md transition-all hover:-translate-y-0.5 hover:from-primary/20 hover:to-primary/10 hover:shadow-[0_8px_16px_rgba(0,0,0,0.1),inset_0_1px_0_rgba(255,255,255,0.6)] active:translate-y-0 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 dark:shadow-[0_4px_12px_rgba(0,0,0,0.2),inset_0_1px_0_rgba(255,255,255,0.1)] dark:hover:shadow-[0_8px_16px_rgba(0,0,0,0.3),inset_0_1px_0_rgba(255,255,255,0.15)]"
982+
className="group/diff absolute bottom-3 right-3 z-10 inline-flex items-center gap-1.5 overflow-hidden rounded-full border-none bg-black/5 px-3 py-1 text-[10px] font-medium text-foreground shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.7),inset_-1px_-1px_0_0_rgba(0,0,0,0.05),0_4px_12px_rgba(0,0,0,0.05)] backdrop-blur-[14px] backdrop-saturate-150 transition-all hover:-translate-y-0.5 hover:bg-black/10 hover:shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.9),inset_-1px_-1px_0_0_rgba(0,0,0,0.1),0_6px_16px_rgba(0,0,0,0.1)] active:translate-y-0 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 dark:bg-white/10 dark:shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.3),inset_-1px_-1px_0_0_rgba(255,255,255,0.1),0_4px_12px_rgba(0,0,0,0.2)] dark:hover:bg-white/20 dark:hover:shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.4),inset_-1px_-1px_0_0_rgba(255,255,255,0.15),0_6px_16px_rgba(0,0,0,0.3)]"
982983
aria-label={
983984
selectedVersion
984985
? `Compare v${version.version} with v${selectedVersion}`
@@ -990,8 +991,9 @@ export function VersionExplorer({ data }: VersionExplorerProps) {
990991
: "Compare versions"
991992
}
992993
>
993-
<FileDiff className="h-3.5 w-3.5" />
994-
diff
994+
<span className="absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.4),transparent_50%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.1),transparent_50%)] opacity-0 transition-opacity group-hover/diff:opacity-100 dark:bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.2),transparent_50%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.05),transparent_50%)]" />
995+
<FileDiff className="h-3.5 w-3.5 drop-shadow-sm" />
996+
<span className="drop-shadow-sm">diff</span>
995997
</button>
996998
) : null}
997999
</div>
@@ -1022,18 +1024,18 @@ export function VersionExplorer({ data }: VersionExplorerProps) {
10221024
) : null}
10231025

10241026
<div className="flex flex-1 flex-col gap-4 overflow-hidden">
1025-
<div className="flex items-center justify-center gap-4">
1026-
<div className="relative flex w-full justify-center rounded-full border border-border/40 bg-muted/30 p-1.5 backdrop-blur-sm shadow-inner">
1027+
<div className="relative z-30 flex items-center justify-center gap-4 py-2">
1028+
<div className="relative flex w-full justify-center rounded-full bg-black/10 p-1 dark:bg-muted/50">
10271029
{artifactTabs.map((tab) => {
10281030
const isActive = tab.id === activeTab.id;
10291031
return (
10301032
<motion.button
10311033
key={tab.id}
10321034
type="button"
10331035
onClick={() => setActiveArtifact(tab.id)}
1034-
className={`relative z-10 flex-1 rounded-full px-4 py-2 text-xs font-semibold uppercase tracking-[0.25em] transition-colors ${isActive
1035-
? "text-primary-foreground shadow-sm"
1036-
: "text-muted-foreground hover:text-foreground"
1036+
className={`relative flex-1 rounded-full px-4 py-2 text-xs font-semibold uppercase tracking-[0.25em] transition-colors ${isActive
1037+
? "z-20 text-primary-foreground shadow-sm"
1038+
: "z-10 text-muted-foreground hover:text-foreground"
10371039
}`}
10381040
whileHover={
10391041
!isActive
@@ -1055,13 +1057,32 @@ export function VersionExplorer({ data }: VersionExplorerProps) {
10551057
{isActive && (
10561058
<motion.div
10571059
layoutId="artifact-tab-indicator"
1058-
className="absolute inset-0 rounded-full border border-primary/20 bg-gradient-to-br from-primary to-primary/80 shadow-[0_4px_12px_rgba(0,51,255,0.3),inset_0_1px_0_rgba(255,255,255,0.2)] backdrop-blur-md"
1060+
className="absolute inset-0 rounded-full bg-primary shadow-[inset_1px_1px_0_0_rgba(255,255,255,0.4),inset_-1px_-1px_0_0_rgba(0,0,0,0.2),0_4px_12px_rgba(0,0,0,0.3)] backdrop-blur-md"
1061+
initial={{ scale: 1, filter: "blur(0px)" }}
1062+
animate={{
1063+
scale: [1, 1.4, 1],
1064+
filter: ["blur(0px) brightness(1)", "blur(12px) brightness(2)", "blur(0px) brightness(1)"]
1065+
}}
10591066
transition={{
1060-
type: "spring",
1061-
stiffness: 500,
1062-
damping: 35,
1067+
layout: {
1068+
type: "spring",
1069+
stiffness: 350,
1070+
damping: 40,
1071+
},
1072+
scale: {
1073+
duration: 0.25,
1074+
ease: "easeInOut",
1075+
times: [0, 0.35, 1]
1076+
},
1077+
filter: {
1078+
duration: 0.25,
1079+
ease: "easeInOut",
1080+
times: [0, 0.35, 1]
1081+
}
10631082
}}
1064-
/>
1083+
>
1084+
<div className="absolute inset-0 rounded-full bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.25),transparent_60%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.1),transparent_60%)]" />
1085+
</motion.div>
10651086
)}
10661087
{/* Hover 状态的微妙高光效果 */}
10671088
{!isActive && (

0 commit comments

Comments
 (0)