npx skills add ...
npx skills add fayazara/macos-app-skills --skill macos-settings-ui
Build a proper macOS settings/preferences window with liquid glass support for macOS 26 (Tahoe). Use this skill whenever the user asks to create a settings window, preferences UI, settings view, or preferences pane for a macOS app. Also trigger when the user mentions "liquid glass settings", "NavigationSplitView settings", "grouped Form settings", "macOS settings layout", or wants to add/fix/rebuild a settings screen in any native macOS SwiftUI app. This includes phrases like "add settings to my app", "create a preferences window", "my settings look wrong", "fix my settings UI", or even just "I need a settings view". If the user is building a macOS app and mentions settings or preferences in any context, use this skill.
npx skills add fayazara/macos-app-skills --skill macos-settings-ui
This skill produces a native macOS settings window that follows Apple's macOS 26 design guidelines. The result is a sidebar + detail NavigationSplitView with liquid glass window chrome, back/forward toolbar navigation, grouped Form sections with transparent backgrounds, and proper scroll edge effects.
The settings UI is composed of 3 layers:
Window Controller (SettingsWindowController.swift) — An NSWindowController that creates the NSWindow programmatically with .fullSizeContentView. This is what gives the window rounded liquid glass corners. You cannot get this effect from a SwiftUI Window scene.
Root View (SettingsView.swift) — A NavigationSplitView with a sidebar list and detail pane. Includes back/forward navigation history in the toolbar, which also forces the creation of an NSToolbar (required for the liquid glass title bar treatment).
Detail Panes (one file per tab) — Each pane uses Form { Section(...) { ... } }.formStyle(.grouped).scrollContentBackground(.hidden).
SwiftUI's declarative Window scene does not expose the NSWindow style mask. The .fullSizeContentView flag must be set at window creation time for macOS 26 to render the liquid glass chrome (rounded corners, translucent sidebar, blurred title bar). Trying to inject it later via NSViewRepresentable is unreliable because SwiftUI resets the window's configuration.
The NSWindowController approach also lets you control the toolbar style, frame autosave, minimum size, and delegate lifecycle directly.
Every detail pane MUST have these three modifiers on its Form:
.formStyle(.grouped) — gives the native inset rounded-rect section appearance.scrollContentBackground(.hidden) — makes the form background transparent so the liquid glass window chrome shows through. Without this, you get an opaque white/dark background that breaks the glass effect..contentMargins(.top, 8) — adds breathing room between the toolbar and the first sectionThe sidebar List MUST have:
The NavigationSplitView MUST have:
Read the reference files for complete, working code:
references/SettingsWindowController.swift — The window controller (copy as-is, adapt the activation policy calls to your app)references/SettingsView.swift — The root view with sidebar, detail routing, and navigation historyreferences/ExampleDetailPane.swift — A template detail pane showing common control patterns (Toggle, Picker, Slider, LabeledContent)Define your settings categories. Each case needs a title and SF Symbol icon:
Copy references/SettingsWindowController.swift into your project. Adapt:
contentRect size (default 700x540 is good for most apps)minSize (default 620x460)AppActivationPolicy.enter()/leave()) — if your app isn't a menu-bar-only app, remove theseCopy references/SettingsView.swift. Adapt:
SettingsTab enum cases to match your categoriesSettingsDetailView switch to return your panesFor each tab, create a pane file. Use this template:
From a menu bar, button, or anywhere:
Do NOT use a SwiftUI Window scene. Do NOT use openWindow(id:). The window controller handles everything.
If you previously had a Window("Settings", id: "SETTINGS") scene in your App struct, remove it entirely. The SettingsWindowController replaces it.
Toggle with description:
Picker (dropdown):
Picker (segmented):
Slider with value label:
Button row:
If your app uses NSApp.setActivationPolicy(.accessory) (no Dock icon), you need activation policy management so the settings window brings the app to the foreground. The reference SettingsWindowController calls AppActivationPolicy.enter() on show and .leave() on close. Implement this as a simple reference-counting wrapper:
If your app always shows in the Dock, remove the AppActivationPolicy calls from the window controller and just use NSApp.activate(ignoringOtherApps: true) in showWindow.
The scrollEdgeEffectStyle(.soft) API is macOS 26+ only. Always wrap it in an availability check:
The rest of the pattern (NSWindowController with .fullSizeContentView, grouped Form, NavigationSplitView) works on macOS 14+.
.listStyle(.sidebar)
.scrollEdgeEffectStyleSoftIfAvailable() // macOS 26 progressive blur at scroll edges
.navigationTitle("Settings")NavigationSplitView(columnVisibility: .constant(.all)) // sidebar always visible
// ...
.navigationTitle("Settings")
.navigationSplitViewStyle(.balanced)enum SettingsTab: String, CaseIterable, Identifiable {
case general
case appearance
case about
var id: Self { self }
var title: String {
switch self {
case .general: "General"
case .appearance: "Appearance"
case .about: "About"
}
}
var systemImage: String {
switch self {
case .general: "gearshape"
case .appearance: "paintbrush"
case .about: "info.circle"
}
}
}import SwiftUI
struct GeneralSettingsPane: View {
@AppStorage("someKey") private var someValue = false
var body: some View {
Form {
Section("Section Name") {
Toggle("Toggle label", isOn: $someValue)
}
}
.formStyle(.grouped)
.scrollContentBackground(.hidden)
.contentMargins(.top, 8, for: .scrollContent)
}
}SettingsWindowController.show(tab: .general)Toggle(isOn: $value) {
VStack(alignment: .leading, spacing: 2) {
Text("Primary label")
Text("Description text explaining what this does.")
.font(.caption)
.foregroundStyle(.secondary)
}
}
.toggleStyle(.switch)Picker("Label", selection: $value) {
Text("Option A").tag(OptionEnum.a)
Text("Option B").tag(OptionEnum.b)
}
.pickerStyle(.menu)Picker("Label", selection: $value) {
ForEach(SomeEnum.allCases) { item in
Text(item.title).tag(item)
}
}
.pickerStyle(.segmented)LabeledContent("Size") {
HStack(spacing: 12) {
Slider(value: $size, in: 24...96, step: 2)
.frame(width: 180)
Text("\(Int(size)) pt")
.monospacedDigit()
.foregroundStyle(.secondary)
.frame(width: 46, alignment: .trailing)
}
}HStack(spacing: 8) {
Button("Action") { doSomething() }
.controlSize(.small)
Button("Reset") { reset() }
.controlSize(.small)
.disabled(isDefault)
}@MainActor
enum AppActivationPolicy {
private static var count = 0
static func enter() {
count += 1
NSApp.setActivationPolicy(.regular)
NSApp.activate(ignoringOtherApps: true)
}
static func leave() {
count = max(0, count - 1)
guard count == 0 else { return }
Task { @MainActor in
NSApp.setActivationPolicy(.accessory)
}
}
}private extension View {
@ViewBuilder
func scrollEdgeEffectStyleSoftIfAvailable() -> some View {
if #available(macOS 26.0, *) {
scrollEdgeEffectStyle(.soft, for: .all)
} else {
self
}
}
}