Can you write CSS using pure Swift? Yes you can. Uut is a domain specific language implemented as a pure Swift library. Import it into your project, specify your styles and compile to CSS strings.
// Extensions ala SASS
let ext = styleExtension(
bottom(0.em)
)
// Media queries, which are then attached to styles
let query = MediaQueries.maxWidth(600.px)
let styles = [
// A basic style, but this one has a media query attached to it. It will be
// grouped with other styles that attach the same query.
style(query, Selectors.classname("what"),
float(.Left)
),
// A simple style that just uses an extension. It's selector will be hoisted
// up and rendered out with the extension properties.
style(Selectors.classname("what"),
extends(ext)
),
// A more complete example.
// - Use an extension
// - A child style; which will have the selector .articles .article
style(Selectors.classname("articles"),
extends(ext),
backgroundColor(Values.Color(0, 0, 0)),
style(Selectors.classname("article"),
borderStyle(.Solid),
backgroundColor(Values.Color(0, 255, 0)),
style(Selectors.el("h2"),
fontWeight(.Bold),
fontFamily("Helvetica Neue", "Helvetica", "Arial", fallback: .SansSerif),
color(Values.Color(255, 0, 0))
)
)
)
]
// A compiler is initialized and can optionally have middleware functions
// configured which are used to pre/post-process styles.
let compiler = Compiler()
let result = compiler.compile(styles)
I've used a few different CSS super-sets, preprocessors or compile-to-CSS languages. They are a fantastic idea. I now feel that writing plain CSS is painful for any moderately complicated project. However, I do have some issues with the current approaches. There is either an impedance mismatch between CSS and the processing language or a library expands to encompass an ad-hoc scripting language e.g. SASS-script.
This problem can be stated a different way; these libraries don't expose the full power of a programming language. So, the goal of Uut is to embed a dialect of CSS inside of Swift.
Aside from being an interesting experiment, I was attracted to this project because of some advantages I perceived.
This library does of course have some serious disadvantages, which may be more or less serious depending on your needs.
Even with these caveats, I still thing this approach is worthwhile.
For the initial release of this library, the aim is to have to following features:
Beyond this initial release, future plans include:
Selectors can be constructed in a way that's — mostly — type-safe. Also, because the selectors are Swift values, they can be passed around as arguments to functions, stored and composed together .
let s = Selectors.id("header") |- Selectors.classname("nav-entry")
print(s.stringValue)
// #id .nav-entry
Or maybe you want to use some psuedo-selectors.
let s = Selectors.classname("nav-entry", Selectors.firstChild(), Selectors.hover())
print(s.stringValue)
// .nav-entry:first-child:hover
Or go nuts with something really complicated with operators and all the other exciting things in CSS selectors.
let s = Selectors.id("dialog", Selectors.classname("warning")) |> Selectors.el("div", Selectors.attrContains("data-id", "foo"))
print(s.stringValue)
// #dialog.warning > div[data-id~="foo"]
Style blocks are also Swift values. The selector and properties are constructed using structs and enums. Here is a simple example.
let header = style(Selectors.id("header"),
backgroundImage("/images/header-background.png"),
width(100.percent)
)
let compiler = Compiler()
print(compiler.compile(header))
/*
#header {
background-image: url("/images/header-background.png");
width: 100%;
}
*/
Style blocks can also be composed of mixins, which are analogous to what you see in LESS or SASS. Here is constructing and using a simple mixin.
let foo = mixin(
backgroundColor(.Red)
)
let bar = style(Selectors.classname("herp") |+ Selectors.classname("derp"),
mixesIn(foo)
)
let compiler = Compiler()
print(compiler.compile(bar))
/*
.herp + .derp {
background-color: red
}
*/
Mixins are just values, but if you needed to parameterize them for some reason, you can wrap them in a function. Here is the same result as above, but using a function to provide the background-color
.
func foo(color: Values.Color) -> Mixin {
return mixin(backgroundColor(color))
}
let bar = Style(Selectors.Class("herp") |+ Selectors.Class("derp"),
mixesIn(foo(Values.Color(0, 0, 0)))
)
let compiler = Compiler()
print(compiler.compile(bar))
/*
.herp + .derp {
background-color: rgba(0, 0, 0, 1)
}
*/
This is not a feature per se, but something nice that falls out of constructing styles with Swift values. It's very simple to parameterize whole style blocks.
func foo(name: String, width: Measurement) -> Block {
return style(Selectors.classname(name),
width(width)
)
}