How to build better layouts with GSS polyfills

CSS has made it somewhat easier for developers to work with things like typography and colours, but it never truly solved one fundamental problem: layout. At The Grid (thegrid.io) we’re developing a product that requires adapting complex layouts to content and devices. Constraints represent two-way relationships between properties. This is true source-order independence. Then, we’ll   throw in a chain function to align the   center-x of the two aligned elements with the cover’s center-x. You shouldn’t wait, either. 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. 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. Each dash (-) represents a gap, defined using gap(). It doesn’t account for the layout problems today’s developers face. 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. 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. 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. In this tutorial, I’m going to introduce you   to the fundamentals of the first two. @h |~-~[#name]~-~| in(#cover) gap([gap]*2) !strong;
Let’s use a GSS conditional to define two different layouts depending on the aspect ratio of the card. @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. That’s because we’re writing constraints, not variable assignments. If one side changes, the other does too. 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. 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. GSS comes with four strengths: !weak, !medium, !strong and !require, which makes a   constraint mandatory. Using CCSS, it would take five lines:

#panel[left] + 10 == #b1[left];
#b1[right] + 10 == #b2[left];
#b2[right] + 10 == #b3[left];
#b3[right] + 10 == #b4[left];
#panel[right] – 10 == #b4[right];
When Apple adopted Cassowary, it developed Visual Format Language (VFL) to make it easier and more intuitive to define one-dimensional layouts. Given the flat HTML:

div id=”profile-card”/div
div id=”cover”/div
div id=”avatar”/div
h1 id=”name”Dan Daniels/h1
button id=”follow” class=”primary”Follow/button
button id=”following”Following/button
button id=”followers”Followers/button
button id=”message”Message/button

Let’s create the layout below, where the follow button has a weak constraint to stay in the middle of the panel, but stronger constraints to not get too close to   the other buttons. 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. Fortunately, a better future is   arriving early. Words: Dan Tocchini
Dan Tocchini is an expert in front-end development. Forget the hacks. Not Just An Experiment

GSS is still under development and there are a few missing pieces to be added, but we’re so confident about it that we’re using GSS to build our flagship consumer product, The Grid. 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. In   our button example:

button {
width: == ::this[intrinsic-width];
}
As a performance best-practice, minimise the use of   intrinsic properties. 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. Aligned elements are specified inside square brackets ([]). In CSS, you would use @media to adapt to the viewport. This article originally appeared in net magazine issue 256. With GSS conditionals, you can design layouts that adapt not just to screen size, but to any property. 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. Ask yourself this: why is it so hard to centre one element inside another with CSS? GSS doesn’t know about those native properties unless you tell it to measure them from the DOM. Polyfills from the distant future
GSS is a set of CSS polyfills for three new layout languages. 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? To   do this, you prefix properties with intrinsic-. Here, we’re using required intrinsic values to make sure the solver understands the size of the name element’s text. It’s   called Grid Style Sheets, or GSS. 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. Let’s say you want a layout to be responsive to a panel’s width:

@if #panel[width] = 900 { … The result was Cassowary-based Cocoa Auto Layout, which now   powers Mac OS   X and iOS. 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. Try doing that with old-school CSS,   or   even Flexbox! 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. That’s a useful tool, but we believe designers need more flexibility. There’s   actually a good reason: maths. In our four-button example, how would you specify button width if you wanted it to be the width of its text plus padding? For sake of brevity, constraints will primarily use id selectors, only structure.gss will be covered and   texture.css will be omitted (check out the for a live demo). Think of them as a more expressive, natural replacement for @media. We’ve implemented a webified version of VFL in GSS. To compute optimal solutions to these constraints, Badros and Borning developed the Cassowary Constraint Solving Toolkit. Let’s say you want to build a panel with a row of four buttons and 10px of spacing. By default, all constraints are weak. The third language – Visual Grid Language (VGL) – is beyond the scope of this particular tutorial. The pipe (|) defines the edge of a container element, which is   specified using in(). 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. 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. @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. A   cushion tells the solver not to let the elements overlap. Variables
Of course, GSS supports custom constraint variables.

Updated: 29.11.2014 — 21:12