A tiny React helper for CSS layout using named areas and lines from CSS Grid.
Adaptable to any React styling solution. Includes adapters for:
$ yarn add layup
$ npm install --save layup
(Using inline styles – but see below for more examples.)
import Layout from 'layup/inline'
const foodLayout = {
gridTemplateAreas: `
'pizza . . '
'. tacos . '
'. . ramen'
`
}
<Layout style={foodLayout} areas={{
pizza: <Pizza />,
tacos: <Tacos />,
ramen: <Ramen />
}} />
@media
mb
ormarginBottom
) to every component you write just because there’s differentmargin
property for layout is often arbitrary and doesn’t makeStrong opinion time! Using the margin
property never made sense in the web
app/component world. It’s a relic from the very first version of CSS, when we
were laying out paragraphs of text in 1997. CSS Grid antiquates this practice.
People just haven’t caught on yet.
Examples? OK.
Suppose each of these “page actions” on GitHub were a component:
How do you think the spacing between them is defined? Maybe they’re each inside
a container with padding. Maybe they each have a left margin of 10px. Maybe they
each have a right margin of 10px, except for the :last-child
which has 0. (In
this case, the latter is exactly what GitHub has done.)
Which method is “correct”? Well, none in particular. When you’ve been writing CSS for a long time, you just develop an intuition for this. It could depend on what other page elements are nearby. But mostly, it’s arbitrary.
But why do the individual components need to “own” the whitespace between them? What if they’re used in other contexts that call for different spacing? Are we just supposed to override their margins every time? Wouldn’t it be nice if the component’s styles stopped at its actual borders, making it easier to use in other places?
CSS Grid offers a new solution to this problem. In this case, the container element can define the spacing between its children. It’s an inversion of the usual method of controlling whitespace, and one that (in my opinion) makes more sense. The children don’t need to concern themselves with spacing.
.PageActions {
display: grid;
grid-auto-flow: column;
grid-gap: 10px;
}
What if the spacing between elements isn’t constant? Take this screen from dribbble for example:
How is that spacing defined?
Instead of each of those elements worrying about the overall layout, we could leave them margin-less and have the container lay them out with CSS Grid like so:
.SignUp {
display: grid;
grid-template-rows:
[twitter] auto
10px
[facebook] auto
10px
[google] auto
15px
[orSeparator] auto
15px
[email] auto
30px
[signIn];
}
The container element is then free to arrange these rows differently (even
showing them in a different order) at different breakpoints. All the child
elements need to do is define their grid-row
name. This library is a helper
for doing exactly that!
The Layout
component accepts areas
, columns
, and rows
props. You should
only pass one of these. The value can be an object directly mapping area/line
names to child components:
<Layout
rows={{
header: <Title>Leave a comment</Title>,
body: <TextInput />,
footer: <SubmitButton />
}}
/>
…or an array that will be used to pair area/line names with the children
passed to Layout
:
<Layout rows={['header', 'body', 'footer']}>
<Title>Leave a comment</Title>
<TextInput />
<SubmitButton />
</Layout>
How you define the actual layout of the areas/columns/rows depends on your styling solution. Examples follow.
Layout
and its children receive their styling from className
. Define the
grid layout using styled()
:
import styled from 'styled-components'
import Layout from 'layup/styled'
const FoodLayout = styled(Layout)`
grid-template-areas:
'pizza . . '
'. tacos . '
'. . ramen';
`
<FoodLayout areas={{
pizza: <Pizza />,
tacos: <Tacos />,
ramen: <Ramen />
}} />
Layout
and its children receive their styling from className
. Either use
styled()
(see the styled-components example above) or define the grid layout
using css
:
import { css } from 'emotion'
import Layout from 'layup/emotion'
const foodLayout = css`
grid-template-areas:
'pizza . . '
'. tacos . '
'. . ramen';
`
<Layout className={foodLayout} areas={{
pizza: <Pizza />,
tacos: <Tacos />,
ramen: <Ramen />
}} />
Layout
and its children receive their styling from className
. Define the
grid layout using css
:
import { css } from 'glamor'
import Layout from 'layup/glamor'
const foodLayout = css({
gridTemplateAreas: `
'pizza . . '
'. tacos . '
'. . ramen'
`
})
<Layout className={foodLayout} areas={{
pizza: <Pizza />,
tacos: <Tacos />,
ramen: <Ramen />
}} />
Layout receives its styling from style
. Children must also support the style
prop:
import Layout from 'layup/inline'
const foodLayout = {
gridTemplateAreas: `
'pizza . . '
'. tacos . '
'. . ramen'
`
}
<Layout style={foodLayout} areas={{
pizza: <Pizza />,
tacos: <Tacos />,
ramen: <Ramen />
}} />
All you need to do is define two components: Grid and Cell. Pass them to the
createLayout
function to get your Layout component.
Grid should:
display: grid
styling pre-applied.children
, which will be an array of Cell elements.className
or style
, for example – itCell should:
child
prop using React.cloneElement
. child
will begrid-area
, grid-column
, or grid-row
styling to the cloned childarea
, column
, or row
prop, respectively.
className
or style
, forSee the styled-components adapter for an example.