Contents

nerdsnipe-inc/design-foundation

A SwiftUI design system I built because every new project I started, I was rebuilding the same buttons, inputs, cards, and modals, losing another two weeks to it.

Installation

Add the package via Swift Package Manager in Xcode or Package.swift:

Xcode: File → Add Package Dependencies → https://github.com/NerdSnipe-Inc/design-foundation → from version 1.0.0

Package.swift:

dependencies: [
    .package(url: "https://github.com/NerdSnipe-Inc/design-foundation", from: "1.0.0")
],
targets: [
    .target(name: "YourApp", dependencies: ["DesignFoundation"])
]

Quick Start

import DesignFoundation

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .dfThemePreset(.slate)
        }
    }
}

struct ContentView: View {
    var body: some View {
        DFButton("Get started") { print("tapped") }
    }
}

Five presets ship: .slate, .aurora, .copper, .sage, .garnet. Each swaps automatically for light and dark mode. Build your own from tokens if none of them fit, see the Theme System section below.


Component Reference

Primitives

| Component | Built-in Styles | |---|---| | DFButton | .filled, .outlined, .ghost, .tinted, .glass¹ | | DFText | display, title, headline, body, caption, label | | DFIcon | SF Symbol wrapper with token-driven size and color | | DFBadge | .default, .subtle, .outlined, .glass¹ | | DFAvatar | .circle, .rounded, .ring, .glass¹ (image or initials, presence indicators) | | DFDivider | .solid, .dashed, .gradient (horizontal/vertical, labeled variant) |

Inputs

| Component | Built-in Styles | |---|---| | DFTextField | .outlined, .filled | | DFSecureField | .outlined, .filled (reveal toggle built in) | | DFToggle | .switch, .checkbox | | DFSlider | .standard, .labeled | | DFPicker | .segmented, .menu, .wheel | | DFDatePicker | .compact, .graphical, .wheel | | DFCheckbox | .default |

All input components share DFValidationState (.idle / .valid / .error(String)) so error display looks consistent everywhere.

Layout

| Component | Built-in Styles | |---|---| | DFCard | .elevated, .outlined, .filled, .glass¹ |

Overlays

| Component | Built-in Styles | |---|---| | DFModal | .standard, .glass¹ | | DFSheet | .standard, .compact, .glass¹ | | DFPopover | .arrow, .compact, .glass¹ | | DFTooltip | .bubble, .glass¹ |

Navigation

| Component | Built-in Styles | |---|---| | DFTabBar | .standard, .minimal | | DFNavigationBar | .standard, .transparent | | DFSidebar | .standard, .plain |

Supplementary

| Component | Notes | |---|---| | DFAlert | Convenience wrapper over the native SwiftUI alert | | DFToast | Queue management and auto-dismiss | | DFSkeleton | Shimmer animation | | DFProgressBar | Linear, circular, and indeterminate variants | | DFList | Swipe-delete, reorder, and multi-select | | DFListRow | Leading/trailing slots and disclosure indicator | | DFTable | Sortable columns |

¹ .glass styles require iOS 26+ / macOS 26+.


Theme System

One DFTheme struct sits in SwiftUI's environment and drives every component. Set it at the app root, override it anywhere below.

// Token namespaces: colors, typography, spacing, radius, shadow, animation, components
MyApp()
    .dfTheme(DFTheme(
        colors: DFColorTokens(
            primary: .indigo,
            surface: Color(.systemBackground)
        ),
        spacing: DFSpacingTokens(md: 20),
        radius: DFRadiusTokens(md: 12)
    ))

Every component reads from the nearest DFTheme in the environment. Change a token, everything that uses it updates. No manual wiring.


Preset Themes

Five presets ship in the box. Each one pairs a light and dark DFTheme and switches automatically based on @Environment(\.colorScheme).

MyApp()
    .dfThemePreset(.aurora)

| Preset | Notes | Fits well with | |---|---|---| | .slate | Closest to Apple system defaults, neutral palette | SaaS dashboards, developer tools | | .aurora | Violet primary, larger corner radii, softer shadows | Creative tools, social apps | | .copper | Warm orange-brown palette | Finance, content readers | | .sage | Muted green, calmer contrast | Health, wellness | | .garnet | Bold, saturated deep garnet red (#C8102E), white cards, off-white background | Bold consumer brands, retail, sports |

The differences read better in a preview than in a description, spin them up and see which one feels right for your app.

// Automatic light/dark
MyApp().dfThemePreset(.slate)

// Force a specific variant (previews, sub-tree overrides)
MyView().dfTheme(.copperDark)

// Build a custom preset from named themes
let myPreset = DFThemePreset(light: .slateLight, dark: .auroraDark)

// Mutate one token, keep the rest
var custom = DFTheme.sageLight
custom.colors.primary = .purple
MyView().dfTheme(custom)

Style System

Every component exposes a makeBody(configuration:) style protocol, the same pattern SwiftUI uses for ButtonStyle. Styles compose, propagate through the environment, and apply hierarchically.

// Apply a style to an entire section
VStack { ... }
    .dfButtonStyle(.outlined)
    .dfCardStyle(.glass)

// Override for a single component
DFButton("Delete", role: .destructive) { }
    .dfButtonStyle(.ghost)

// Liquid Glass across your whole UI
ContentView()
    .dfButtonStyle(.glass)
    .dfCardStyle(.glass)
    .dfTooltipStyle(.glass)

Writing a custom style means implementing one function. Built-in styles are concrete structs, copy any of them as a starting point.


Platforms

| Platform | Minimum Version | |---|---| | iOS | 18.0 | | macOS | 15.0 | | visionOS | 2.0 |

Liquid Glass (.glass styles) requires iOS 26+ / macOS 26+. Everything else works on the minimums above.

The DFPlatformVariant enum (automatic / compact / expanded / immersive) lets components adapt their form factor at runtime, or lets you force a specific layout for previews and testing.


Why I built this

Short version: every SwiftUI project I started ended up rebuilding the same primitives. Buttons that needed 40 lines of styling to match brand. Text fields with validation states I always got slightly wrong. The design system would drift within months because there was no single source of truth for spacing, radius, color, or elevation. Six months in, half my app used one theme and half used whatever I shipped in a busy sprint.

DesignFoundation makes the tokens the source of truth. Components read from the environment. Brand refreshes are a theme file edit, not a file hunt.

There's a paid tier at nerdsnipe-inc.github.io/design-foundation/pro that adds pre-built screens for auth, dashboard, CRM, analytics, and other verticals, composed from these same primitives. That's optional. The primitives on this repo stay MIT and get maintained regardless.

Feedback and issues are welcome, especially on the theme API. If something's rough, open an issue.


License

MIT © 2026 NerdSnipe Inc. See LICENSE.

Package Metadata

Repository: nerdsnipe-inc/design-foundation

Default branch: master

README: README.md