diff --git a/Sources/MenubucketApp/Dock/DockFolderStack.swift b/Sources/MenubucketApp/Dock/DockFolderStack.swift index 80d4ef9..50b502c 100644 --- a/Sources/MenubucketApp/Dock/DockFolderStack.swift +++ b/Sources/MenubucketApp/Dock/DockFolderStack.swift @@ -53,11 +53,11 @@ struct DockFolderStack: View { .frame(maxWidth: .infinity, minHeight: 120) } else { ScrollView { - LazyVGrid(columns: Array(repeating: GridItem(.fixed(Self.cellWidth), spacing: 8), count: Self.columns), + LazyVGrid(columns: Array(repeating: GridItem(.fixed(Self.cellWidth), spacing: Spacing.xs), count: Self.columns), spacing: Self.rowSpacing) { ForEach(entries) { entry in cell(entry) } } - .padding(12) + .padding(Spacing.s) } // A scroll view has no height of its own, and the popover // took the smallest it could: one row. Rows decide it, up to @@ -91,8 +91,8 @@ struct DockFolderStack: View { } .controlSize(.small) } - .padding(.horizontal, 12) - .padding(.vertical, 8) + .padding(.horizontal, Spacing.s) + .padding(.vertical, Spacing.xs) } private func cell(_ entry: Entry) -> some View { @@ -104,7 +104,7 @@ struct DockFolderStack: View { onClose() } } label: { - VStack(spacing: 4) { + VStack(spacing: Spacing.xxs) { Image(nsImage: DockActions.fileIcon(at: entry.url.path)) .resizable() .interpolation(.high) diff --git a/Sources/MenubucketApp/Dock/DockSettingsPage.swift b/Sources/MenubucketApp/Dock/DockSettingsPage.swift index 7937849..6953fec 100644 --- a/Sources/MenubucketApp/Dock/DockSettingsPage.swift +++ b/Sources/MenubucketApp/Dock/DockSettingsPage.swift @@ -276,11 +276,7 @@ struct DockSettingsPage: View { Text(profile.name) .fontWeight(isSelected ? .semibold : .regular) if isActive { - Text("Active") - .font(.caption) - .padding(.horizontal, 6) - .padding(.vertical, 1) - .background(Color.accentColor.opacity(0.15), in: Capsule()) + Pill(text: String(localized: "Active"), color: .accentColor) } Spacer() if config.profileHotkeysEnabled, let label = DockHotkeys.label(forPosition: position) { @@ -527,7 +523,7 @@ struct DockSettingsPage: View { Toggle("Switch the Apple Dock's apps with the profile", isOn: binding(\.appleDockLayouts)) if let layout = profile.appleDock { LabeledContent("Saved in \(profile.name)") { - VStack(alignment: .trailing, spacing: 2) { + VStack(alignment: .trailing, spacing: Spacing.hair) { Text(Self.summary(layout.appNames + layout.otherNames)) .lineLimit(2) .multilineTextAlignment(.trailing) diff --git a/Sources/MenubucketApp/Dock/DockView.swift b/Sources/MenubucketApp/Dock/DockView.swift index d2c833b..46ec537 100644 --- a/Sources/MenubucketApp/Dock/DockView.swift +++ b/Sources/MenubucketApp/Dock/DockView.swift @@ -235,8 +235,8 @@ struct DockView: View { .overlay(alignment: edge == .bottom ? .top : .center) { profileBanner } .fixedSize() .onChange(of: hoveredID == nil) { _, resting in onHoverChange(!resting) } - .animation(reduceMotion ? nil : .spring(response: 0.22, dampingFraction: 0.82), value: hoveredID) - .animation(reduceMotion ? nil : .easeInOut(duration: 0.2), value: tiles.map(\.id)) + .animation(Motion.respecting(reduceMotion, Motion.quick), value: hoveredID) + .animation(Motion.respecting(reduceMotion, Motion.arrange), value: tiles.map(\.id)) .onChange(of: tiles.map(\.id)) { _, ids in DockMenuRouter.shared.keep(only: Set(ids)) if let hoveredID, !ids.contains(hoveredID) { self.hoveredID = nil } @@ -280,7 +280,7 @@ struct DockView: View { } else { shape .fill(isClassic ? AnyShapeStyle(.ultraThinMaterial) : AnyShapeStyle(.regularMaterial)) - .overlay(shape.strokeBorder(Color.primary.opacity(0.12), lineWidth: 0.5)) + .overlay(shape.strokeBorder(Fill.hairline, lineWidth: 0.5)) .shadow(color: .black.opacity(0.18), radius: 10, y: 2) } } @@ -298,8 +298,8 @@ struct DockView: View { if let profile = store.announcedProfile { Label(profile.name, systemImage: profile.symbol) .font(.headline) - .padding(.horizontal, 14) - .padding(.vertical, 7) + .padding(.horizontal, Spacing.s + Spacing.hair) + .padding(.vertical, Spacing.stack) .background(.regularMaterial, in: Capsule()) .offset(y: edge == .bottom ? -44 : 0) .transition(.opacity.combined(with: .scale(scale: 0.9))) @@ -593,7 +593,7 @@ struct DockView: View { @ViewBuilder private func widgetTile(item: DockItem, widgetID: String) -> some View { let frame = widgetFrame(for: widgetID) - let shape = RoundedRectangle(cornerRadius: 12, style: .continuous) + let shape = RoundedRectangle(cornerRadius: Radius.card, style: .continuous) if isClassic, let widget = runtime.widgets.first(where: { $0.id == widgetID }) { // Classic keeps the Apple Dock's height: a glance, not a card. // A button, so keyboard and VoiceOver users can reach and open it. @@ -609,7 +609,7 @@ struct DockView: View { WidgetCardView(widget: widget, runtime: runtime, compactHeight: frame.height, placement: .single) .frame(width: frame.width, height: frame.height) .clipShape(shape) - .overlay(shape.strokeBorder(Color.primary.opacity(0.08), lineWidth: 0.5)) + .overlay(shape.strokeBorder(Fill.hairline, lineWidth: 0.5)) } else { VStack(spacing: Spacing.xxs) { Image(systemName: "questionmark.square.dashed").font(.title2) @@ -617,7 +617,7 @@ struct DockView: View { } .foregroundStyle(.secondary) .frame(width: frame.width, height: frame.height) - .background(Color.primary.opacity(0.05), in: shape) + .background(Fill.card, in: shape) } } @@ -784,7 +784,7 @@ struct DockOverflowStack: View { ) .onChange(of: focusedID) { _, id in guard let id else { return } - withAnimation(reduceMotion ? nil : .easeOut(duration: 0.2)) { proxy.scrollTo(id) } + withAnimation(Motion.respecting(reduceMotion, Motion.quick)) { proxy.scrollTo(id) } } } } else { @@ -809,11 +809,11 @@ private struct HoverLabel: View { var body: some View { Text(title) - .font(.system(size: 13)) - .padding(.horizontal, 10) - .padding(.vertical, 4) + .font(.body) + .padding(.horizontal, Spacing.s) + .padding(.vertical, Spacing.xxs) .background(.regularMaterial, in: Capsule()) - .overlay(Capsule().strokeBorder(Color.primary.opacity(0.1), lineWidth: 0.5)) + .overlay(Capsule().strokeBorder(Fill.hairline, lineWidth: 0.5)) } } diff --git a/Sources/MenubucketApp/Dock/DockWidgetGlance.swift b/Sources/MenubucketApp/Dock/DockWidgetGlance.swift index d835f4b..c311889 100644 --- a/Sources/MenubucketApp/Dock/DockWidgetGlance.swift +++ b/Sources/MenubucketApp/Dock/DockWidgetGlance.swift @@ -33,7 +33,7 @@ struct DockWidgetGlance: View { let summary = self.summary let metrics = Array((summary?.metrics ?? []).prefix(2)) VStack(alignment: .leading, spacing: height * 0.06) { - HStack(spacing: 4) { + HStack(spacing: Spacing.xxs) { if let symbol = summary?.symbol { Image(systemName: symbol).imageScale(.small) } @@ -69,7 +69,7 @@ struct DockWidgetGlance: View { private func meter(_ metric: SharedShelf.Metric) -> some View { let barHeight = max(3, height * 0.08) - return HStack(spacing: 4) { + return HStack(spacing: Spacing.xxs) { if let label = metric.label { Text(label) .font(.system(size: max(8, height * 0.18))) @@ -79,7 +79,7 @@ struct DockWidgetGlance: View { } GeometryReader { proxy in ZStack(alignment: .leading) { - Capsule().fill(Color.primary.opacity(0.12)) + Capsule().fill(Fill.track) Capsule() .fill(nodeColor(metric.tone) ?? .accentColor) .frame(width: proxy.size.width * CGFloat(min(max(metric.fraction ?? 0, 0), 1))) diff --git a/Tests/MenubucketAppTests/Fixtures/design-literal-baseline.json b/Tests/MenubucketAppTests/Fixtures/design-literal-baseline.json index 1e2c7fb..55cfd45 100644 --- a/Tests/MenubucketAppTests/Fixtures/design-literal-baseline.json +++ b/Tests/MenubucketAppTests/Fixtures/design-literal-baseline.json @@ -1,9 +1,6 @@ { "Sources\/MenubucketApp\/Builder\/WidgetBuilderView.swift" : 107, - "Sources\/MenubucketApp\/Dock\/DockFolderStack.swift" : 5, - "Sources\/MenubucketApp\/Dock\/DockSettingsPage.swift" : 4, - "Sources\/MenubucketApp\/Dock\/DockView.swift" : 21, - "Sources\/MenubucketApp\/Dock\/DockWidgetGlance.swift" : 3, + "Sources\/MenubucketApp\/Dock\/DockView.swift" : 8, "Sources\/MenubucketApp\/Hub\/ShelfView.swift" : 1, "Sources\/MenubucketApp\/Popover\/RootView.swift" : 2, "Sources\/MenubucketApp\/Renderer\/ViewTreeRenderer.swift" : 1,