Laying out elements on the screen is always a challenge for web developers.
Nobody remembers how to do it, especially vertically 😁 because it is not as simple as in mobile development, it is different.
Every time I switch to web development I miss the autolayout feature, classic CSS layout paradigm literally makes me cry sometimes, so at some point I decided that I have to have some library that can make my web dev life much simpler. I spent few days making one and named it Autolayout 📏
The first idea was to make it work exactly the same way as constraints work on iOS. I was so sure that it was possible, so I started working on it and implemented it. It worked great until I had only a few elements on the screen. However, with a lot of elements, I faced a huge amount of redraws. So, I realized there was an issue and accepted defeat, at least for the time being.
But did I give up? No!
It is art for me and I had to build a great solution to keep my code beautiful and easy to maintain!
I implemented autolayout in pure CSS!
Well not everything. It doesn’t have an ability to stick side or size to another view since CSS doesn’t have mechanisms for that. But it is something on the same level! Just take a look!
Let’s assume you have a Div and you want to set the width to 600px.
Div().width(600.px)
But now you want to have a different width for different screen sizes.
Div().width(600.px) // will be used for extra large screens
.width(200.px, breakpoints: .xs, .s) // for extra small and small screens
.width(400.px, breakpoints: .m, .l) // for medium and large screens
Got the idea? Yeah it‘s cool! But now you probably want to know more about the breakpoints.
.xs or .extraSmall // <576px
.s or .small // ≥576px and <768px
.m or .medium // ≥768px and <992px
.l or .large // ≥992px and <1200px
.xl or .extraLarge // ≥1200px and <1400px
.xxl or .extraExtraLarge // ≥1400px
Yeah, it's a classic pattern that you may have seen before in Bootstrap or other UI frameworks.
You also can declare your own (just notice how long they are):
extension MediaRule.MediaType {
static var extraSmall: MediaRule.MediaType { .init(.all.maxWidth(575.px), label: "xs") }
static var small: MediaRule.MediaType { .init(.all.minWidth(576.px).maxWidth(767.px), label: "s") }
static var medium: MediaRule.MediaType { .init(.all.minWidth(768.px).maxWidth(991.px), label: "m") }
}
Use label: "xs"
to prettify your breakpoint in the source code.
Otherwise it will use just the whole rule text.
Breakpoints can be added to the end of any autolayout-method.
.top(100.px, breakpoints: .extraSmall, .small, .medium)
And you can use breakpoints within the classic stylesheet.
Stylesheet {
Rule(Body.pointer)
.margin(all: 0.px)
.padding(all: 0.px)
MediaRule(.extraSmall, .small) { // will be applied only for extra-small and small screens
Rule(Body.pointer)
.backgroundColor(0x9bc4e2)
}
MediaRule(.medium, .large, .extraLarge) { // will be applied only for medium, large, and extra-large screens
Rule(Body.pointer)
.backgroundColor(0xffd700)
}
}
All of the methods have breakpoints:
as a last argument.
width
and height
are simple, I just added widthToParent
and heightToParent
convenience methods to be able to expand the view to its parent size.
Let’s talk about top
, right
, bottom
, left
.
Please just keep in mind that these methods don’t work with static
position.
Sticking the view to the certain sides:
.top() // will set top to 0px
.bottom() // will set bottom to 0px
.left() // will set left to 0px
.right() // will set right to 0px
All of the methods above can be used with a value and reactive @State value.
// will set top to 0px only for extra-small, small and medium screens
.top(breakpoints: .xs, .s, .m)
.top(100.px) // will set top to 100px
.top(100.px, multiplier: 0.5) // will set top to 50px
// will set top to 50px only for extra-small, small and medium screens
.top(50.px, breakpoints: .xs, .s, .m)
// will set top to 25px only for extra-small, small and medium screens
.top(50.px, multiplier: 0.5, breakpoints: .xs, .s, .m)
Same for bottom
, left
, and right
.
But there are extra convenience methods!
You can stick all of these sides to the center of parent or to the opposite side.
Let’s take a look at the top to center and top to bottom examples:
.top(side: .center) // will set top to 0px from the center
.top(100.px, side: .center) // will set top to 100px from the center
// will set top to 0px from the center only for extra-small, small and medium screens
.top(side: .center, breakpoints: .xs, .s, .m)
// will set top to 50px from the center only for extra-small, small and medium screens
.top(50.px, side: .center, breakpoints: .xs, .s, .m)
// multiplier is also available
Top can stick to center and bottom
Bottom can stick to center and top
Left can stick to center and right
Right can stick to center and left
I believe you got the idea! 😀
But that is not all! I have more convenient methods for you!
All sides
// All sides
.edges() // will set top, right, bottom, and left to 0px
.edges(10.px) // will set top, right, bottom, and left to 10px
.edges(5.px, breakpoints: .xs, .s, .m) // you know, only for certain screens
Horizontal
.edges(h: 0.px) // left and right to 0px
.edges(h: 10.px) // left and right to 10px
.edges(h: 5.px, breakpoints: .xs, .s, .m)
Vertical
.edges(v: 0.px) // top and bottom to 0px
.edges(v: 10.px) // top and bottom to 10px
.edges(v: 5.px, breakpoints: .xs, .s, .m)
Both
.edges(h: 0.px, v: 0.px) // left and right to 0px, and top and bottom to 0px
.edges(h: 0.px, v: 10.px) // left and right to 0px, and top and bottom to 10px
.edges(h: 2.px, v: 4.px, breakpoints: .xs, .s, .m)
Finally! So how to center the Div? 😁 😁 😁
Not that fast, we usually pronounce X and Y, so horizontal centering first! 😁
Center X
.centerX() // will set centerX to 0px
.centerX(100.px) // will set centerX to 100px
.centerX(breakpoints: .xs, .s, .m) // center horizontally only for certain screens
.centerX(50.px, breakpoints: .xs, .s, .m)
Center Y (yeah!)
.centerY() // will set centerY to 0px
.centerY(100.px) // will set centerY to 100px
.centerY(breakpoints: .xs, .s, .m) // center vertically only for certain screens
.centerY(50.px, breakpoints: .xs, .s, .m)
Also with side:
extra argument!
.left
and .right
for horizontal
.centerX(side: .left) // centerX to 0px of the left
.centerX(100.px, side: .right) // centerX to 100px of the right
.centerX(side: .left, breakpoints: .xs, .s, .m)
.centerX(50.px, side: .left, breakpoints: .xs, .s, .m)
.top
and .bottom
for vertical
.centerY(side: .top) // centerY to 0px of the top
.centerY(100.px, side: .bottom) // centerY to 100px of the bottom
.centerY(side: .top, breakpoints: .xs, .s, .m)
.centerY(50.px, side: .top, breakpoints: .xs, .s, .m)
Center for both X and Y
.center() // centerX and centerY to 0px
.center(100.px) // centerX and centerY to 100px
.center(breakpoints: .xs, .s, .m) // centerX and centerY to 0px only certain screens
.center(50.px, breakpoints: .xs, .s, .m) // you got the idea!
Yeah, you should already feel the infinite power! 😎
Control the type of positioning like static, relative, absolute, fixed
with the breakpoints:
// set the position to absolute
.position(.absolute)
// set the position to fixed for certain screens
.position(.fixed, breakpoints: .xs, .s, .m)
.display(.block)
.display(.none, breakpoints: .xs, .s, .m)
.visibility(.visible)
.visibility(.hidden, breakpoints: .xs, .s, .m)
.opacity(1)
.opacity(0, breakpoints: .xs, .s, .m)
Yes! I know you really need it during the prototyping!
To make it work just add into Preview.Content either:
AppStyles.all
or AppStyles.id(.autolayoutStyles)
If you are new to SwifWeb then you have to create a new project using the Webber CLI tool.
In the project open Package.swift and edit dependencies
section to make it look like this:
dependencies: [
// the rest of the other dependencies incuding swifweb/web
.package(url: "https://github.com/swifweb/autolayout", from: "1.0.0"),
]
Then edit executableTarget
to make it look like this:
.executableTarget(name: "App", dependencies: [
.product(name: "Web", package: "web"),
.product(name: "Autolayout", package: "autolayout")
]),
Next, just import Autolayout
where needed.
Wanna learn more? Stay tuned for the upcoming articles!
Don’t hesitate to ask any questions in our Discord and feel free to contribute!