From 12608fe618fc35af6f529bbb0e0c57528a6ca80b Mon Sep 17 00:00:00 2001 From: Rocky <72559939+hkgood@users.noreply.github.com> Date: Thu, 18 Jun 2026 21:22:33 +0800 Subject: [PATCH] design: drop disc drop-shadow, scale up processing-state spinner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two design cleanups, both for visual polish on the record disc (the hero control of the keyboard preview AND the live keyboard extension). The shadows were a leftover from the iOS 18 mockup that visually fought the brand-green disc; the processing-state spinner was the system default at 1.1x, which on a 96-120pt disc reads as "a dot in the middle" rather than "an active process". Changes: 1) Remove the disc body's drop shadow on BOTH surfaces. - `KeyboardPreviewStub.swift:244` — `.shadow(color: .black.opacity(0.4), radius: 10, y: 6)` deleted. Hardcoded dark shadow on a light surface, completely theme-blind (looked like a 2009 skeuomorphic button). - `RecordButton.swift:112` — `.shadow(color: .black.opacity(0.45), radius: 14, y: 8)` deleted. Same problem on the real keyboard's disc, plus the shadow competed with the existing "halo" radial gradient and the outer breathing ring. The internal "ambient glow" (radial green gradient inside the disc on idle) is NOT touched — that's brand colour, not a drop shadow, and matches the live keyboard's idle accent. 2) Scale up the processing-state spinner on BOTH surfaces. - Preview: `ProgressView().tint(.white).scaleEffect(1.1)` → `ProgressView().progressViewStyle(.circular).tint(palette.textPrimary) .scaleEffect(2.5)`. - 1.1x (~22pt) → 2.5x (~50pt) = 52% of the 96pt disc. - Tint changed from hardcoded `.white` to `palette.textPrimary` because the processing-state disc gradient is `surfaceElevated → surface` (light grey in light mode); a white spinner on a light grey disc is invisible regardless of scale. - Real keyboard: `ProgressView().scaleEffect(1.2)` → `ProgressView().scaleEffect(2.5)`. Same absolute spinner size (~50pt) — 42% of the 120pt disc — so the user gets the same visual weight whether they're looking at the in-app preview or the live keyboard. Tint was already `palette.textPrimary` so no change there. Proportion rationale: ~50% of the disc diameter is the sweet spot. 100% would crowd the disc's edge, <30% reads as "decoration" rather than "process". 50pt absolute size matches the same spinner across both surfaces — same weight, different disc sizes — so the user doesn't have to re-learn the visual language when they switch from preview to real keyboard. Tests: 27/27 pass (no behavioural change, just visual). Build: BUILD SUCCEEDED. 🤖 Generated with Claude Code --- OSGKeyboard/Views/KeyboardPreviewStub.swift | 15 +++++++++++++-- OSGKeyboardExt/Views/RecordButton.swift | 10 ++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/OSGKeyboard/Views/KeyboardPreviewStub.swift b/OSGKeyboard/Views/KeyboardPreviewStub.swift index 4fe5cb0..c490fce 100644 --- a/OSGKeyboard/Views/KeyboardPreviewStub.swift +++ b/OSGKeyboard/Views/KeyboardPreviewStub.swift @@ -241,7 +241,6 @@ struct KeyboardPreviewStub: View { .fill(discGradient) .frame(width: 96, height: 96) .overlay(Circle().stroke(palette.accentGlow, lineWidth: 1.5)) - .shadow(color: .black.opacity(0.4), radius: 10, y: 6) Group { switch phase { case .idle: @@ -258,7 +257,19 @@ struct KeyboardPreviewStub: View { } .frame(width: 60, height: 32) case .processing: - ProgressView().tint(.white).scaleEffect(1.1) + // Scaled to ~50pt inside a 96pt disc (~52%) so the + // spinner is the dominant visual element of the + // loading state without crowding the disc's edge. + // `palette.textPrimary` (instead of hardcoded + // `.white`) keeps the spinner visible in both light + // and dark themes — the processing-state disc + // gradient is `surfaceElevated → surface`, which is + // light in light mode and would swallow a white + // spinner. + ProgressView() + .progressViewStyle(.circular) + .tint(palette.textPrimary) + .scaleEffect(2.5) } } } diff --git a/OSGKeyboardExt/Views/RecordButton.swift b/OSGKeyboardExt/Views/RecordButton.swift index f8e0993..79b50b3 100644 --- a/OSGKeyboardExt/Views/RecordButton.swift +++ b/OSGKeyboardExt/Views/RecordButton.swift @@ -96,10 +96,17 @@ struct RecordButton: View { .frame(width: 80, height: 44) .transition(.opacity) case .processing: + // Scaled to ~50pt inside a 120pt disc (~42%) — + // same absolute size as the preview stub's + // spinner (2.5x of the default ProgressView), + // just a smaller fraction because the real + // disc is bigger. The user gets the same + // visual weight whether they're looking at + // the in-app preview or the live keyboard. ProgressView() .progressViewStyle(.circular) .tint(palette.textPrimary) - .scaleEffect(1.2) + .scaleEffect(2.5) case .error: Image(systemName: "exclamationmark.triangle.fill") .font(.system(size: 30, weight: .medium)) @@ -109,7 +116,6 @@ struct RecordButton: View { } .frame(width: 120, height: 120) .scaleEffect(isPressed ? 0.94 : 1.0) - .shadow(color: .black.opacity(0.45), radius: 14, y: 8) .animation(Motion.quick, value: isPressed) .animation(Motion.soft, value: phase) }