Skip to content

jhalickman/NeoBrutalism

 
 

Repository files navigation

nb

GitHub Actions Workflow Status GitHub Tag GitHub commits since latest release

NeoBrutalism

A set of SwiftUI components inspired by the NeoBrutalism design trend.

This started as a learning project for SwiftUI but grew into a reusable UI library. It's useful for anyone looking to build apps with a bold, minimal style.

Feel free to contribute by reporting bugs or submitting fixes.

Checkout the library in action

How to install

You can add NeoBrutalism to your Swift project using Swift Package Manager.

  1. In Xcode, go to File -> Swift Packages -> Add Package Dependency.
  2. Enter the repository URL: https://github.com/rational-kunal/NeoBrutalism.git
  3. Choose the version or branch you want to use.

How to use

NeoBrutalism offers a variety of components that can be seamlessly integrated into your SwiftUI project.

import NeoBrutalism
import SwiftUI

struct ContentView: View {
    var body: some View {
        ZStack {
            NBTheme.default.background
                .ignoresSafeArea()
            Toggle(isOn: .constant(true)) { Text("Are you a wizard?") }
                .toggleStyle(.neoBrutalismChecklist)
        }
    }
}

Styling

NeoBrutalism supports theming, with both light and dark mode options. You can customize or create your own themes. To apply a theme to a view, use the nbTheme() modifier.

struct ContentView: View {
    var theme = NBTheme.default.updateBy(background: .black, mainText: .white)

    var body: some View {
        ZStack {
            theme.background
                .ignoresSafeArea()
            Toggle(isOn: .constant(true)) { Text("Are you a wizard?") }
                .toggleStyle(.neoBrutalismChecklist)
        }.nbTheme(theme)
    }
}

Components

NeoBrutalism includes commonly used UI components, with plans to expand as needed. Feel free to contribute!

Checkbox

image image

Toggle(isOn: $checkboxState) { Text(checkboxState ? "(Alohomora!)" : "(Colloportus!)") }
    .toggleStyle(.neoBrutalismChecklist)

Switch

image
image

Toggle(isOn: $switchState) { Text(switchState ? "(Lumos!)" : "(Nox!)") }
    .toggleStyle(.neoBrutalismSwitch)

Accordion

image image

DisclosureGroup("Expecto Patronum") {
    Text("Pitradev Sanrakshanam - पितृदेव संरक्षणम्")
}.disclosureGroupStyle(.neoBrutalismAccordion)

Button

image image

Button {
    counter += 1
} label: {
    Text("Accio")
}.buttonStyle(.neoBrutalism())

Button {
    counter += 1
} label: {
    Image(systemName: "wand.and.sparkles.inverse")
        .bold()
}.buttonStyle(.neoBrutalism(type: .neutral, variant: .reverse))

Card

image image

NBCard {
    Text("Hogwarts Letter")
} main: {
    Text("You have been accepted to Hogwarts School of Witchcraft and Wizardry!")
} footer: {
    Button {
        // No-op
    } label: {
        Text("Open Letter").frame(maxWidth: .infinity)
    }.buttonStyle(.neoBrutalism())
}

NBCard(type: .neutral) {
    Text("Quidditch Gear")
} main: {
    Text("Get your broomstick, Quidditch robes, and golden snitch!")
} footer: {
    HStack(spacing: 12.0) {
        Button {
            // No-op
        } label: {
            Text("Open Firebolt")
        }.buttonStyle(.neoBrutalism(type: .neutral))

        Spacer()

        Button {
            // No-op
        } label: {
            Text("Snitch")
        }.buttonStyle(.neoBrutalism())
    }
}

Input

image image

TextField("Enter your spell", text: $text)
    .textFieldStyle(.neoBrutalism)

Progress

image
image

ProgressView(value: 0.7)
    .progressViewStyle(.neoBrutalism)

Slider

image
image

struct SliderExampleView: View {
    @State var sliderValue: CGFloat = 0.0

    var body: some View {
        HStack {
            Text("\(sliderValue, specifier: "%.2f")")
                .frame(width: 50.0, alignment: .leading)
            NBSlider(value: $sliderValue)
        }
    }
}

Radio

image image

struct RadioGroupExampleView: View {
    @State private var selectedSpell: Int = 0

    var body: some View {
        VStack(alignment: .leading, spacing: 8.0) {
            Text("Selected Spell: \(selectedSpell)")
                .font(.title3)

            NBRadioGroup(value: $selectedSpell) {
                VStack(alignment: .leading) {
                    NBRadioItem(value: 0) {
                        Text("Expelliarmus")
                    }
                    NBRadioItem(value: 1) {
                        Text("Lumos")
                    }
                    NBRadioItem(value: 2) {
                        Text("Wingardium Leviosa")
                    }
                }.frame(maxWidth: .infinity, alignment: .leading)
            }
        }
    }
}

Tabs

image image

struct TabsExampleView: View {
    @State private var selectedTab: Int = 0

    var body: some View {
        VStack(spacing: 8.0) {
            Text("House Selection: \(selectedTab)")
                .font(.title3)

            NBTabs(selectedTabItem: $selectedTab) {
                NBTabsList {
                    NBTabsTrigger(tabItem: 0) { Image(systemName: "flame.fill") }
                    NBTabsTrigger(tabItem: 1) { Image(systemName: "lanyardcard.fill") }
                    NBTabsTrigger(tabItem: 2) { Image(systemName: "book.fill") }
                    NBTabsTrigger(tabItem: 3) { Image(systemName: "leaf.fill") }
                }
                NBFlatCard {
                    ZStack {
                        NBTabsContent(tabItem: 0) { Text("Bravery and Daring!") }
                        NBTabsContent(tabItem: 1) { Text("Cunning and Ambition!") }
                        NBTabsContent(tabItem: 2) { Text("Wisdom and Learning!") }
                        NBTabsContent(tabItem: 3) { Text("Loyalty and Hard Work!") }
                    }
                }
            }
        }
        .padding()
    }
}

Collapsable

image image

NBCollapsable(isExpanded: $isExpanded) {
    NBFlatCard {
        HStack {
            Text("Need something?")
            Spacer()
            NBCollapsibleTrigger {
                Image(systemName: isExpanded ? "door.left.hand.open" : "door.left.hand.closed")
            }
        }
    }
    NBCollapsableContent {
        NBFlatCard(type: .neutral) {
            Text("Here’s what you need!")
        }
    }
}

Drawer

image image

struct DrawerExampleView: View {
    @State private var isDrawerOpen: Bool = false

    var body: some View {
        VStack(spacing: 16.0) {
            NBButton {
                Text("Open the Chamber")
            } action: {
                isDrawerOpen.toggle()
            }
        }
        .nbDrawer(isPresented: $isDrawerOpen) {
            VStack(spacing: 16) {
                Text("Parseltongue Required")
                    .font(.title2)

                Text("Only those who can speak to snakes may proceed.")
                    .padding(.horizontal, 4.0)

                NBButton {
                    Text("I Understand")
                } action: {
                    isDrawerOpen.toggle()
                }
            }
        }
    }
}

Flat Card

image image

NBFlatCard {
    Text("Quidditch Tryouts - This Saturday!")
}

NBFlatCard(type: .neutral) {
    Text("O.W.L. Exams Approaching - Study Hard!")
}

Alert

image image

NBAlert {
    Text("The Chamber of Secrets has been opened. Enemies of the heir, beware!")
} icon: {
    Image(systemName: "exclamationmark.triangle")
} head: {
    Text("Warning")
}

NBAlert(type: .neutral) {
    Text("Dementors are nearby. Expecto Patronum!")
} head: {
    Text("Caution")
}

Badge

image
image

NBBadge {
    Text("Gryffindor")
        .font(.title3)
}
NBBadge(type: .neutral) {
    Text("Slytherin")
}

Round Skeleton

image
image

NBFlatCard {
    NBRoundSkeleton()
}
.frame(width: 120, height: 120)

Text Skeleton

image image

VStack(alignment: .leading, spacing: 12.0) {
    NBTextSkeleton()

    NBTextSkeleton()
        .frame(width: 120)
}

The credit for the design belongs to https://www.neobrutalism.dev.

About

NeoBrutalism SwiftUI components

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 100.0%