Then, we’ll throw in a chain function to align the center-x of the two aligned elements with the cover’s center-x. The second language, adapted from AutoLayout, is Visual Format Language (VFL). Here, we’re using required intrinsic values to make sure the solver understands the size of the name element’s text. Aligned elements are specified inside square brackets ([]). That’s because we’re writing constraints, not variable assignments. The good news is that even though the web’s layout mechanisms haven’t evolved all that much since 1998, browsers have. Cushions
In addition to gaps, which generate equality (==) constraints, VFL can also generate inequality constraints (=, =) with cushions, specified using a tilde (~). As platforms like iOS and OS X evolve to give their developers powerful new layout tools, the web platform looks increasingly unattractive to new developers. Constraints represent two-way relationships between properties. @If, Or @Else! @v |-[#avatar]-[#name]-| in(#cover)
gap([gap]) outer-gap([flex-gap])
chain-center-x(#cover[center-x]);
Now, we’ll stretch the cover horizontally across the profile-card with a 10px gap:
@h |-10-[#cover]-10-| in(#profile-card);
Next, we’ll align the follow button below the cover image within the card. First, let’s define our variables and set up the card constraints:
[gap] == 20 !require;
[flex-gap] = [gap] * 2 !require;
[radius] == 10 !require;
[outer-radius] == [radius] * 2 !require;
#profile-card {
width: == ::window[width] — 480;
height: == ::window[height] — 480;
border-radius: == [outer-radius];
center-x: == ::window[center-x];
center-y: == ::window[center-y];
}
Let’s make the avatar a 160px diameter circle:
#avatar {
height: == 160 !require;
width: == ::[height];
border-radius: == ::[height] / 2;
}
Remember that GSS doesn’t know anything except what you explicitly tell it. We’ve implemented a webified version of VFL in GSS. Forget the hacks. At The Grid (thegrid.io) we’re developing a product that requires adapting complex layouts to content and devices. To do this, you prefix properties with intrinsic-. To accomplish that same four-button layout with VFL, all you need is one line:
@h |-[#b1]-[#b2]-[#b3]-[#b4]-| in(#panel) gap(10);
Visual formatting
One nice thing about working with VFL is that it’s visual: you can tell what’s going on with just a quick glance. The @h directive defines a horizontal layout, @v specifies vertical ones. When the panel gets too narrow, all of the buttons should vertically stack. To change this, you can add a strength, which look like CSS’s !important. A cushion tells the solver not to let the elements overlap. We believe that if something as groundbreaking as GSS is possible to make happen in today’s browser, we ought to do it instead of waiting for standards to catch up. GSS: The basics
So, how would you centre an element inside another in CSS? Setting up GSS is simple: just include the necessary JavaScript files and use type=”text/gss” on link tags that load .gss files or style tags with inline GSS. This is where the Cassowary algorithm and constraint programming show their true power. You shouldn’t wait, either. For example, this element should be to the left of another one; this column should take up one-third of the page; this element should be centred inside this other one, and so on. As powerful as one-line arithmetic constraints are, marginally complex layouts quickly become verbose. #name {
height: == ::[intrinsic-height] !require;
width: == ::[intrinsic-width] !require;
}
Let’s add some rounded corners and constrain the buttons:
#cover, button {
border-radius: == [radius];
}
button {
width: == ::[intrinsic-width] !require;
height: == ::[intrinsic-height] !require;
padding: == [gap];
padding-top: == [gap] / 2;
padding-bottom: == [gap] / 2;
}
It’s time to bust out the VFL. You can centre any element inside another with a one-liner:
#element1[center-y] == #element2[center-y];
Want to give all of your buttons a border-radius relative to their height? Unfortunately, Cassowary remained a purely academic accomplishment for about a decade, until Apple decided that it needed a better layout paradigm for the growing number of different screen resolutions being used. In CSS, you would use @media to adapt to the viewport. By using cushions around the follow button, it can slide around to satisfy the centre constraint if there is space for it to do so:
@h |-[#message]~-~[#follow]~-~[#following]-[#followers]-|
in(#profile-card)
gap([gap])
chain-top
!strong;
}
Finally, let’s finish our conditional. In this tutorial, I’m going to introduce you to the fundamentals of the first two. However, it’s been almost 15 years since Badros proposed CCSS, and we need this now. When you allow developers to describe element properties relative to each other, you end up naturally creating cyclic dependencies – logic loops – that are extremely difficult to resolve. button[border-radius] = button[height] / 6;
With one line of GSS, you can accomplish an incredible amount of dynamism that’s maddeningly impossible in CSS. The result was Cassowary-based Cocoa Auto Layout, which now powers Mac OS X and iOS. To define one, just use it in a constraint:
[my-col-size] == ::window[width] / 12 !require;
.avatar[width] == [my-col-size] * 2;
A little too verbose? Layouts, unconstrained
In 1998, frustrated with traditional layout engines, former Facebook VP Greg Badros and co-author Alan Borning realised that almost any layout can be expressed as a series of linear constraints. So, let’s refactor our border-radius example, adding a strong constraint so that the Cassowary solver knows that keeping the radius above a minimum value is more important than scaling it all the way down:
button {
border-radius: = ::this[height] / 6;
border-radius: = 2 !strong;
Pseudo-selectors
You might have noticed that GSS gives you a few new pseudo-selectors: ::window, ::this (shortened to ::), and ::parent. Strengths
When constraints conflict, the stronger one overcomes. That’s a useful tool, but we believe designers need more flexibility. Unfortunately, new CSS features such as Flexbox insist on coupling layout to content, and even proposed future features won’t address basic problems such as relative centre alignment. @if #profile-card[width] = #profile-card[height] {
Next, when we vertically align the avatar and name within the cover, we’ll vertically centre the alignment by using [flex-gap] as the outer-gap. Think of them as a more expressive, natural replacement for @media. This article originally appeared in net magazine issue 256. And, internally a VFL statement simply generates many CCSS statements – so unlike CSS with its several incompatible layout modes, GSS offers a holistic layout paradigm. Notice how we can explicitly set a 10px gap between the cover and card, while using the larger [gap] variable for the other gaps on the page:
@v |-10-[#cover]-[#follow]-| in(#profile-card) gap([gap]);
Again using one line, let’s centre the follow button horizontally relative to the card:
#follow[center-x] == #profile-card[center-x];
Now we can lay out our row of buttons inside the card and chain the button tops. If one side changes, the other does too. That’s why GSS comes with @if and @else. This is true source-order independence. Why go through all the trouble just for better layouts? The pipe (|) defines the edge of a container element, which is specified using in(). Two-way relationship
You’ve probably noticed that we’re using == instead of =. Let’s use cushions to keep the name within the horizontal bounds of the cover; we’ll make the alignment !strong to ensure the width of the cover and panel stay large enough to satisfy this constraint. CSS’s dominant layout mechanism, the float, was first published over a decade ago, when most web pages were just that: static pages, not interactive, responsive web apps. }
Putting it all together
Now that you’ve got a feel for CCSS and VFL, let’s put it together into a simple, real-world layout. It doesn’t account for the layout problems today’s developers face. Try doing that with old-school CSS, or even Flexbox! By default, all constraints are weak. CSS is simply not good enough. In our four-button example, how would you specify button width if you wanted it to be the width of its text plus padding? We think GSS has the potential to be the future of web layout, and we’d love to see it standardised. GSS doesn’t know about those native properties unless you tell it to measure them from the DOM. It’s called Grid Style Sheets, or GSS.