Web development without ready-to-use UI libraries wouldn't be as fast and efficient. You may argue that you're an HTML/CSS ninja, and I believe you because I am too. Nonetheless, it's still cool to use predefined stylish components, and that's why we are looking for some beautiful UI libraries.
My name is Mikhail Isaev, and I'm the author of SwifWeb framework. Today, I'm going to tell you about the MaterializeCSS framework and how to use it with SwifWeb. Let's begin!
MaterializeCSS is a modern responsive open-source front-end framework based on Material Design which was created by Google to combine the classic principles of successful design along with innovation and technology. I enjoyed writing a wrapper for this framework because it is very easy to use!
If you are new to SwifWeb then you have to create new project using the Webber CLI tool.
In the project open Package.swift and edit the dependencies
section to make it look like this:
dependencies: [
// the rest of the other dependencies incuding swifweb/web
.package(url: "https://github.com/swifweb/materialize", from: "1.0.0"),
]
Next edit executableTarget
to make it look like this:
.executableTarget(name: "App", dependencies: [
.product(name: "Web", package: "web"),
.product(name: "Materialize", package: "materialize")
]),
Then open App.swift
and configure Materialize in didFinishLaunching
method like this:
Lifecycle.didFinishLaunching {
Materialize.configure()
}
You will face missing styles if you forget to configure it 🙃
If you generated a custom Materialize style from SCSS then configure it this way:
Lifecycle.didFinishLaunching { app in
Materialize.configure(avoidStyles: true)
app.addStylesheet("/css/custom-materialize.min.css")
}
Add your custom style into /Sources/App/css/custom-materialize.min.css
open Package.swift
and declare resources in executableTarget
as follows:
.executableTarget(name: "App", dependencies: [
.product(name: "Web", package: "web"),
.product(name: "Materialize", package: "materialize")
], resources: [
.copy("css/custom-materialize.min.css"),
.copy("css")
]),
Now you are ready to start building the UI!
I have beautifully wrapped all the components into Swift, but the article would be too long if I were to describe all of them.
Let me examine a few of them here, and you can refer to the readme on GitHub for the rest.
You can make any element as material button just by calling .materialButton(...)
method.
A element can be used for links, and Div or other elements can be used with onClick
handler.
Div("My Button")
.materialButton(type: .raised)
.onClick {}
A("My Link")
.href("https://google.com")
.materialButton(type: .raised)
Both elements will look same, the only difference is that A is a link and will act like a link.
Method .materialButton
have few options:
type
can be raised, floating, floatingHalfWay, flat
size
can be small, large
waves
can be light, red, yellow, orange, purple, green, teal
or custom, light
by default
disabled
is just a flag to mark button disabled, pass @State value here to change it on the fly
Also you can add a material icon to it simply by calling .addMaterialIcon(...)
.addMaterialIcon("announcement")
// or optionally
.addMaterialIcon("announcement".size(.tiny).side(.right))
Find more about the material icon below.
It is a fixed floating action button with multiple actions. Read more about it in the official docs.
FloatingActionButton(icon: "add", color: .pink)
.item(icon: "public", color: .red)
.item(icon: "adb", color: .purple)
.item(icon: "announcement", color: .blue) {
Toast.show("Announcement!")
}
Optional arguments
size
can be large
and small
, it is large
by default
waves
can be light, red, yellow, orange, purple, green, teal
or custom, light
by default
direction
can be top, right, bottom, left
, it is top
by default
mode
can be hover, click, toolbar
it is hover
by default
Control it programmatically:
lazy var fab = FloatingActionButton(...)
fab.open() // to show the menu/toolbar
fab.close() // to show the menu/toolbar
fab.isOpen // the check if it is open
You can add an icon anywhere by initializing the MaterialIcon object.
This object accepts icon type as the initializer argument. List of the icon types is available here.
MaterialIcon("announcement")
You can change icon type on the fly later by calling method .type(...)
on it.
Optional methods
.size(...)
accepts tiny, small, medium, large
.side(...)
accepts left, right
All the methods can accept reactive @State value, so you can change the icon and its properties on the fly.
All colors that are listed in the official documentation are available including their modifiers.
Text Color
Span("Hello").textColor(.red) // just red color
Span("Hello").textColor(.red.lighten3) // red color with lighten-3 modifier
Span("Hello").textColor(.red.darken4) // red color with darken-4 modifier
Background Color
Div().materialBackground(.red) // just red color
Div().textColor(.red.lighten3) // red color with lighten-3 modifier
Div().textColor(.red.darken4) // red color with darken-4 modifier
// or
Div().red()
Div().red(.lighten3)
Div().red(.darken4)
The main thing in grid is Column which has its width and optional offest, push, pull
modifiers.
Container {
Row {
Column(.small(.one)) { "1" } // <div class="col s1">1</div>
Column(.small(.one)) { "2" } // <div class="col s1">2</div>
Column(.small(.one)) { "3" } // <div class="col s1">3</div>
Column(.small(.one)) { "4" } // <div class="col s1">4</div>
Column(.small(.one)) { "5" } // <div class="col s1">5</div>
Column(.small(.one)) { "6" } // <div class="col s1">6</div>
Column(.small(.one)) { "7" } // <div class="col s1">7</div>
Column(.small(.one)) { "8" } // <div class="col s1">8</div>
Column(.small(.one)) { "9" } // <div class="col s1">9</div>
Column(.small(.one)) { "10" } // <div class="col s1">10</div>
Column(.small(.one)) { "11" } // <div class="col s1">11</div>
Column(.small(.one)) { "12" } // <div class="col s1">12</div>
}
Divider()
Row {
Column(.small(.twelve)) { "screen-wide" }
Column(.small(.six)) { "one-half" }
Column(.small(.six)) { "one-half" }
}
Divider()
Row {
Column(.small(.twelve)) { "12-columns" }
Column(.small(.six, offset: .six)) { "6-columns at the right" }
}
Divider()
Row {
Column(.small(.seven, push: .five)) { "7-columns pushed to the right" }
Column(.small(.five, pull: .seven)) { "5-columns pulled to the left" }
}
}
The examples above are 1:1 like in the official documentation, so you can just compare.
You can set more than one size class to a Column:
Column(.extraLarge(.twelve), .large(.ten), .medium(.eight), .small(.six))
Vertical Align
VAlignWrapper {
H5("This should be vertically aligned")
}
Text Align
H5("This should be left aligned").leftAlign()
H5("This should be right aligned").rightAlign()
H5("This should be center aligned").centerAlign()
Quick Floats
Div().floatLeft() // Quickly float things to left
Div().floatRight() // Quickly float things to right
Div().floatCenter() // Quickly float things to center
Hiding/Showing Content
Div().hideOnSmallOnly() // Hidden for Mobile Only
Div().hideOnMedOnly() // Hidden for Tablet Only
Div().hideOnMedAndDown() // Hidden for Tablet and Below
Div().hideOnMedAndUp() // Hidden for Tablet and Above
Div().hideOnLargeOnly() // Hidden for Desktop Only
Div().showOnSmall() // Show for Mobile Only
Div().showOnMedium() // Show for Tablet Only
Div().showOnLarge() // Show for Desktop Only
Div().showOnMediumAndUp() // Show for Tablet and Above
Div().showOnMediumAndDown() // Show for Tablet and Below
Div().pinned() // Pins element
Truncation
H4("This is an extremely long title").truncate() // also with @State
Hover
This feature adds an animation for box shadow on hover.
Card().hoverable() // also with @State
Browser Defaults
You can revert element styles to the original state.
Ul().browserDefault()
Responsive Images
Img().src("cool_pic.jpg").responsive() // resizes responsively to page width
Circular images
Img().src("images/yuna.jpg").circle() // square image that appears circular
Responsive Embeds
Div {
IFrame(...)
}.videoContainer()
Responsive Videos
Video {
Source().src("movie.mp4").type("video/mp4")
}.responsive()
Navbar {
A("Logo")
.floatLeft() // align left
.floatRight() // align right
.floatCenter() // align center
}
.deepPurple(.lighten4) // background
.left() // left aligned links
.right() // right aligned links
.item("Sass", href: "/sass")
.item("Components", href: "/badges")
.item("JavaScript", active: true, href: "/collapsible") // as active
// dropdown menu
.dropdownItem("More", background: .white, hover: false) {
DropdownItem("Dropdown 1", color: .red) { Toast.show("Hi") }
DropdownItem("Dropdown 2", href: "")
}
Card("Card Title")
.blueGrey(.darken1)
.textColor(.white)
.message("""I am a very simple card.
I am good at containing small bits of information.
I am convenient because I require little markup to use effectively.
""")
.action("THIS IS A BUTTON") {}
.action("THIS IS A LINK", href: "#")
Circular
PreloaderCircular(colors: .blue, .red, .green, .yellow).active()
Indeterminate
PreloaderLinearIndeterminate()
Determinate
@State var value: Double = 20 // change it to update the progress
PreloaderLinearDeterminate($value)
Pagination()
.back(disabled: true, icon: "chevron_left") {}
.item(1, href: "#1")
.item(2, href: "#2")
.item(3) { Toast.show("Going to 3!") }
.forward() { Toast.show("Going forward!", rounded: true) }
All right, I described about ~20% of the framework.
If you are familiar with Materialize CSS you may notice that its Swift representation is much easier to use!
Wanna learn more? Stay tuned for the upcoming articles!
Don’t hesitate to ask any questions in our Discord and feel free to contribute!