Skip to content

Commit 4b610df

Browse files
committed
✨ feat(ui): overhaul UI with micro-interactions, animations, and 3 new themes
- Add font rendering: antialiased smoothing, optimizeLegibility, smooth scroll - Enhance animation system: easing/duration tokens, entrance keyframes, reduced-motion - Add utilities: theme-aware selection, focus rings, global transitions, styled scrollbars - Improve shadow depth: refined 5-level system (xs/sm/md/lg/xl) - Button: hover shadow lift, active press scale, icon color transitions - Card: upgraded radius, hover border highlight + shadow elevation - Dialog: frosted glass overlay (backdrop-filter blur), smoother scale+translate entrance - Icon-button: tactile press scale(0.92), icon color transitions - Tabs: color/background/border transitions on triggers - Tooltip: enabled directional slide-in transitions, upgraded radius/padding - Popover: upgraded shadow/radius, added translateY to animations - List: smooth background/radius transition on items - Add Rosé Pine theme (light + dark) - Add Kanagawa theme (light + dark) - Add Everforest theme (light + dark)
1 parent 0d9bd36 commit 4b610df

17 files changed

Lines changed: 847 additions & 183 deletions

docs/09-temp/ui-overhaul-plan.md

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,94 @@
11
# UI/UX Overhaul Plan — OpenCode Desktop
22

33
**Date:** 2026-02-24
4-
**Status:** TODO — needs brainstorming session with ui-ux-pro-max + frontend-pe skills
4+
**Status:** ✅ PHASE 1 COMPLETE
55

66
## User Requirements
7-
- UI looks "very bad" — needs visual polish and tactile feel
8-
- More themes and theme customization
9-
- Better UI rendering quality
7+
- UI looks "very bad" — needs visual polish and tactile feel
8+
- More themes and theme customization
9+
- Better UI rendering quality
1010
- Font size ✅ (fixed in PR #14821)
1111
- Zoom in/out ✅ (already works via Cmd+/-/0)
1212
- Wide mode ✅ (added in PR #14835)
13-
- More UI settings options needed
14-
15-
## Skills to Use
16-
- `ui-ux-pro-max` — Design system, color palettes, typography, UX guidelines
17-
- `frontend-pe` — Avant-garde UI design, micro-interactions, visual polish
18-
- `brainstorming` — Plan before implementing
19-
20-
## Areas to Investigate
21-
1. **Theme system** — opencode already has theming (`packages/ui/src/context/theme/`). How to add more?
22-
2. **Tactile UI** — Micro-interactions, hover states, transitions, shadows
23-
3. **Typography** — Font rendering, line-height, letter-spacing refinements
24-
4. **Spacing** — Consistent padding/margin system
25-
5. **Colors** — More vibrant palettes, better contrast
26-
6. **Animations** — Smooth transitions between states
27-
7. **Settings page** — More appearance options (line-height, letter-spacing, sidebar width, etc.)
28-
29-
## Tonight's Completed Fixes (6 PRs)
30-
- #14820: Streaming content duplication
31-
- #14821: Font size settings (CSS + terminal + UI)
32-
- #14826: ContextOverflowError recovery
33-
- #14827: Prune before compaction
34-
- #14831: Context usage card + compact button
35-
- #14835: Wide mode setting (full-width chat)
13+
- More UI settings options needed (future)
14+
15+
## Phase 1 Changes (Completed)
16+
17+
### 1. Font Rendering (`base.css`)
18+
- Added `-webkit-font-smoothing: antialiased` for crisp text on macOS
19+
- Added `-moz-osx-font-smoothing: grayscale` for Firefox
20+
- Added `text-rendering: optimizeLegibility` for better kerning
21+
- Added `scroll-behavior: smooth` for smooth scrolling
22+
23+
### 2. Animation System (`animations.css`)
24+
- Added CSS custom property easing tokens (`--ease-out-expo`, `--ease-spring`, etc.)
25+
- Added duration tokens (`--duration-instant` through `--duration-slower`)
26+
- Added new keyframes: `fadeIn`, `fadeInScale`, `slideInFromRight/Left/Bottom`
27+
- Added `subtleGlow` for focus states, `shimmer` for loading, `spin`
28+
- Halved stagger delay (50ms instead of 100ms) for snappier text reveals
29+
- Added `prefers-reduced-motion: reduce` media query for accessibility
30+
31+
### 3. Utilities (`utilities.css`)
32+
- Added `::selection` styling with theme-aware color
33+
- Added global transition defaults for all interactive elements
34+
- Added `:focus-visible` ring with theme color
35+
- Added thin scrollbar styling for scroll views
36+
- Suppressed focus ring for components that handle their own
37+
38+
### 4. Shadow/Depth System (`theme.css`)
39+
- Refined `--shadow-xs` with slightly stronger presence
40+
- Added new `--shadow-sm` level for subtle elevation
41+
- Enhanced `--shadow-md` with deeper, more dramatic depth
42+
- Enhanced `--shadow-lg` with softer, more premium feel
43+
- Added new `--shadow-xl` for maximum elevation (modals, floating panels)
44+
45+
### 5. Button Micro-Interactions (`button.css`)
46+
- Added explicit transition for bg-color, border, box-shadow, transform, opacity
47+
- Primary: hover now lifts with `--shadow-sm`, active presses with `scale(0.98)`
48+
- Ghost: icon color transitions on hover, active presses with `scale(0.97)`
49+
- Secondary: hover adds border shadow hint, active presses
50+
- Disabled states now use `opacity: 0.6` for clearer visual feedback
51+
52+
### 6. Card Polish (`card.css`)
53+
- Upgraded border-radius from `--radius-md` to `--radius-lg`
54+
- Added full transition for bg-color, border-color, box-shadow, transform
55+
- Hover state now shows subtle border highlight and `--shadow-xs` elevation
56+
57+
### 7. Dialog Animations (`dialog.css`)
58+
- Overlay now uses `backdrop-filter: blur(4px)` for frosted glass effect
59+
- Overlay opacity increased from 0.2 to 0.35 for better focus
60+
- Content now uses combined `scale(0.96) + translateY(4px)` entrance
61+
- Animation uses `cubic-bezier(0.16, 1, 0.3, 1)` expo-out for premium feel
62+
- Added subtle 1px border ring on dialog content for depth definition
63+
- Overlay entrance/exit now animated separately
64+
65+
### 8. Icon Button Interactions (`icon-button.css`)
66+
- Added explicit transitions for bg-color, box-shadow, transform
67+
- Ghost variant: icon color now transitions on hover (to `--icon-hover`)
68+
- Active state now scales to `0.92` for satisfying tactile press
69+
- Icon SVG color now properly transitions through states
70+
- Disabled state uses `opacity: 0.5`
71+
72+
### 9. New Themes (3 premium additions)
73+
- **Rosé Pine** — Dreamy, soft palette with purple/rose accents. Very popular community theme.
74+
- **Kanagawa** — Japanese-inspired warm palette. Distinctive golden/purple tones based on "The Great Wave."
75+
- **Everforest** — Calming green/earth tones nature-inspired palette. Easy on the eyes for long sessions.
76+
77+
All themes include full light + dark variants with seeds, borders, surfaces, text, syntax highlighting, and markdown colors.
78+
79+
## Files Modified
80+
- `packages/ui/src/styles/base.css` — Font rendering
81+
- `packages/ui/src/styles/animations.css` — Animation system
82+
- `packages/ui/src/styles/utilities.css` — Selection, focus, transitions, scrollbars
83+
- `packages/ui/src/styles/theme.css` — Shadow system
84+
- `packages/ui/src/components/button.css` — Button interactions
85+
- `packages/ui/src/components/card.css` — Card polish
86+
- `packages/ui/src/components/dialog.css` — Dialog animations
87+
- `packages/ui/src/components/icon-button.css` — Icon button interactions
88+
- `packages/ui/src/theme/themes/rosepine.json` — NEW
89+
- `packages/ui/src/theme/themes/kanagawa.json` — NEW
90+
- `packages/ui/src/theme/themes/everforest.json` — NEW
91+
- `packages/ui/src/theme/default-themes.ts` — Theme registration
92+
93+
## Build Status
94+
`vite build` passes with zero errors (7.98s)

packages/ui/src/components/button.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
cursor: default;
1111
outline: none;
1212
white-space: nowrap;
13+
transition:
14+
background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
15+
border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
16+
box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1),
17+
transform 100ms cubic-bezier(0.4, 0, 0.2, 1),
18+
opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
1319

1420
&[data-variant="primary"] {
1521
background-color: var(--button-primary-base);
@@ -22,15 +28,18 @@
2228

2329
&:hover:not(:disabled) {
2430
background-color: var(--icon-strong-hover);
31+
box-shadow: var(--shadow-sm);
2532
}
2633
&:focus:not(:disabled) {
2734
background-color: var(--icon-strong-focus);
2835
}
2936
&:active:not(:disabled) {
3037
background-color: var(--icon-strong-active);
38+
transform: scale(0.98);
3139
}
3240
&:disabled {
3341
background-color: var(--icon-strong-disabled);
42+
opacity: 0.6;
3443

3544
[data-slot="icon-svg"] {
3645
color: var(--icon-invert-base);
@@ -45,20 +54,27 @@
4554

4655
[data-slot="icon-svg"] {
4756
color: var(--icon-base);
57+
transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
4858
}
4959

5060
&:hover:not(:disabled) {
5161
background-color: var(--surface-base-hover);
62+
63+
[data-slot="icon-svg"] {
64+
color: var(--icon-hover);
65+
}
5266
}
5367
&:focus-visible:not(:disabled) {
5468
background-color: var(--surface-base-hover);
5569
}
5670
&:active:not(:disabled) {
5771
background-color: var(--surface-base-active);
72+
transform: scale(0.97);
5873
}
5974
&:disabled {
6075
color: var(--text-weak);
6176
cursor: not-allowed;
77+
opacity: 0.5;
6278

6379
[data-slot="icon-svg"] {
6480
color: var(--icon-disabled);
@@ -80,6 +96,7 @@
8096

8197
&:hover:not(:disabled) {
8298
background-color: var(--button-secondary-hover);
99+
box-shadow: var(--shadow-xs-border-hover);
83100
}
84101
&:focus:not(:disabled) {
85102
background-color: var(--button-secondary-base);
@@ -93,12 +110,14 @@
93110
}
94111
&:active:not(:disabled) {
95112
background-color: var(--button-secondary-base);
113+
transform: scale(0.98);
96114
}
97115
&:disabled {
98116
border-color: var(--border-disabled);
99117
background-color: var(--surface-disabled);
100118
color: var(--text-weak);
101119
cursor: not-allowed;
120+
opacity: 0.6;
102121
}
103122

104123
[data-slot="icon-svg"] {

packages/ui/src/components/card.css

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,20 @@
44
flex-direction: column;
55
background-color: var(--surface-inset-base);
66
border: 1px solid var(--border-weaker-base);
7-
transition: background-color 0.15s ease;
8-
border-radius: var(--radius-md);
7+
transition:
8+
background-color 200ms cubic-bezier(0.4, 0, 0.2, 1),
9+
border-color 200ms cubic-bezier(0.4, 0, 0.2, 1),
10+
box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1),
11+
transform 200ms cubic-bezier(0.16, 1, 0.3, 1);
12+
border-radius: var(--radius-lg);
913
padding: 6px 12px;
1014
overflow: clip;
1115

16+
&:hover {
17+
border-color: var(--border-weaker-hover);
18+
box-shadow: var(--shadow-xs);
19+
}
20+
1221
&[data-variant="error"] {
1322
background-color: var(--surface-critical-weak);
1423
border: 1px solid var(--border-critical-base);

packages/ui/src/components/dialog.css

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
position: fixed;
55
inset: 0;
66
z-index: 50;
7-
background-color: hsl(from var(--background-base) h s l / 0.2);
7+
background-color: hsl(from var(--background-base) h s l / 0.35);
8+
backdrop-filter: blur(4px);
9+
-webkit-backdrop-filter: blur(4px);
810
}
911

1012
[data-component="dialog"] {
@@ -50,7 +52,9 @@
5052
border-radius: var(--radius-xl);
5153
background: var(--surface-raised-stronger-non-alpha);
5254
background-clip: padding-box;
53-
box-shadow: var(--shadow-lg-border-base);
55+
box-shadow:
56+
var(--shadow-lg-border-base),
57+
0 0 0 1px var(--border-weaker-base);
5458

5559
[data-slot="dialog-header"] {
5660
display: flex;
@@ -136,19 +140,29 @@
136140
}
137141

138142
[data-component="dialog"][data-transition] [data-slot="dialog-content"] {
139-
animation: contentHide 100ms ease-in forwards;
143+
animation: contentHide 120ms cubic-bezier(0.4, 0, 1, 1) forwards;
140144

141145
&[data-expanded] {
142-
animation: contentShow 150ms ease-out;
146+
animation: contentShow 250ms cubic-bezier(0.16, 1, 0.3, 1);
147+
}
148+
}
149+
150+
[data-component="dialog"][data-transition] [data-component="dialog-overlay"] {
151+
animation: overlayHide 150ms ease-in forwards;
152+
153+
&[data-expanded] {
154+
animation: overlayShow 200ms ease-out;
143155
}
144156
}
145157

146158
@keyframes overlayShow {
147159
from {
148160
opacity: 0;
161+
backdrop-filter: blur(0);
149162
}
150163
to {
151164
opacity: 1;
165+
backdrop-filter: blur(4px);
152166
}
153167
}
154168
@keyframes overlayHide {
@@ -162,20 +176,20 @@
162176
@keyframes contentShow {
163177
from {
164178
opacity: 0;
165-
transform: scale(0.98);
179+
transform: scale(0.96) translateY(4px);
166180
}
167181
to {
168182
opacity: 1;
169-
transform: scale(1);
183+
transform: scale(1) translateY(0);
170184
}
171185
}
172186
@keyframes contentHide {
173187
from {
174188
opacity: 1;
175-
transform: scale(1);
189+
transform: scale(1) translateY(0);
176190
}
177191
to {
178192
opacity: 0;
179-
transform: scale(0.98);
193+
transform: scale(0.96) translateY(4px);
180194
}
181195
}

packages/ui/src/components/icon-button.css

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
user-select: none;
88
aspect-ratio: 1;
99
flex-shrink: 0;
10+
transition:
11+
background-color 120ms cubic-bezier(0.4, 0, 0.2, 1),
12+
box-shadow 120ms cubic-bezier(0.4, 0, 0.2, 1),
13+
transform 80ms cubic-bezier(0.4, 0, 0.2, 1);
1014

1115
&[data-variant="primary"] {
1216
background-color: var(--icon-strong-base);
@@ -77,37 +81,41 @@
7781

7882
&[data-variant="ghost"] {
7983
background-color: transparent;
80-
/* color: var(--icon-base); */
8184

8285
[data-slot="icon-svg"] {
8386
color: var(--icon-base);
87+
transition: color 120ms cubic-bezier(0.4, 0, 0.2, 1);
8488
}
8589

8690
&:hover:not(:disabled) {
8791
background-color: var(--surface-base-hover);
8892

89-
/* [data-slot="icon-svg"] { */
90-
/* color: var(--icon-hover); */
91-
/* } */
93+
[data-slot="icon-svg"] {
94+
color: var(--icon-hover);
95+
}
9296
}
9397
&:focus-visible:not(:disabled) {
9498
background-color: var(--surface-base-hover);
9599
}
96100
&:active:not(:disabled) {
97101
background-color: var(--surface-base-active);
98-
/* [data-slot="icon-svg"] { */
99-
/* color: var(--icon-active); */
100-
/* } */
102+
transform: scale(0.92);
103+
104+
[data-slot="icon-svg"] {
105+
color: var(--icon-active);
106+
}
101107
}
102108
&:selected:not(:disabled) {
103109
background-color: var(--surface-base-active);
104-
/* [data-slot="icon-svg"] { */
105-
/* color: var(--icon-selected); */
106-
/* } */
110+
111+
[data-slot="icon-svg"] {
112+
color: var(--icon-selected);
113+
}
107114
}
108115
&:disabled {
109116
color: var(--icon-invert-base);
110117
cursor: not-allowed;
118+
opacity: 0.5;
111119
}
112120
}
113121

packages/ui/src/components/list.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,9 @@
237237
align-items: center;
238238
color: var(--text-strong);
239239
scroll-margin-top: 28px;
240+
transition:
241+
background-color 100ms cubic-bezier(0.4, 0, 0.2, 1),
242+
border-radius 100ms cubic-bezier(0.4, 0, 0.2, 1);
240243

241244
/* text-14-medium */
242245
font-family: var(--font-family-sans);

0 commit comments

Comments
 (0)