Often layouting a web page is an afterthought. Put a div
here and there, sprinkle some CSS, and call it done. Perhaps you are more advanced and use CSS Grids to figure out exact positioning.
What if there was an alternative way to achieve the same while having more power as a developer? That is something that Artem Zakharchenko is exploring with atomic-layout, a layout solution designed for React.
Hi! My name is Artem, and I am a Full-stack JavaScript developer from Ukraine. I have graduated a medical university and decided to switch my occupation to programming because it's something I enjoy doing since I was a kid.
I'm grateful to find a job and turn my hobby into a full-time activity. That hasn't stopped me, however, from endeavoring into side projects and open source. Today I'd like to share with you one of such projects.
Atomic layout is a React library that provides you with a separate layer responsible for spacial distribution for your layouts. The layer decouples components and spacing, which opens vast opportunities to reuse layout units, as they are no longer bound to some context via specific spacing properties.
Practically speaking, it gives you a fast and maintainable way of developing responsive layouts that share global layout settings and create a unified system.
It generates React components based on provided CSS Grid template areas and controls their spacial relation via props. It also supports a feature called responsive props, that allows applying prop values conditionally, based on a breakpoint.
I will demonstrate the workflow below, but we need to install the library first:
npm install atomic-layout
The current version of the library uses styled-components
, so we need to install it too:
npm install styled-components
Would you like to use Atomic layout with another styling solution? Join the discussion!
Let's say we want to create a simple Header
component that consists of three areas: logo, menu, and actions. First, we define a verbose areas definition of our Header
:
const areas = "logo menu actions";
Areas definition uses pristine grid-template-areas
syntax.
That's all it takes to create a layout of three equally spaced areas positioned inline. Now we can provide this area string to the Composition
component of the library:
import React from 'react'
import { Composition } from 'atomic-layout'
// Layout areas definition
const areas = 'logo menu actions'
const Header = () => (
<Composition areas={areas} gutter={10}>
{({ Logo, Menu, Actions }) => (
<>
<Logo>
<img src="logo.png" />
</Logo>
<Menu as="nav">
<ul>{...}</ul>
</Menu>
<Actions>
<a href="/login">Log in</a>
</Actions>
</>
)}
</Composition>
)
After passing our areas to the areas
prop of the composition, it exposes a function as its children. That function accepts generated area components associated with the respective CSS Grid areas.
There are plenty of props to apply to the Composition and other components exported from the library to achieve the desired effect.
Atomic layout is mobile-first and responsive by default which means that it has rich support of conditional rendering and responsive props application.
To conditionally render one or multiple components, we can wrap them in the Only
component, providing it with breakpoint constraints.
import { Only } from "atomic-layout";
const Disclaimer = () => (
<Only from="sm" to="lg">
<p>
Content displayed between small and large breakpoints.
</p>
</Only>
);
Only
component supports from
, to
and except
props, and any combination of those. You can pass breakpoint names as the values, or use an Object in case of a custom breakpoint.
You can use Only
component just as any other React component. For example, you can render it nested within generated layout areas!
When using the except
prop, the children will be rendered everywhere except the given breakpoints range:
import { Only } from 'atomic-layout'
const Disclaimer = () => (
<Only except from="sm" to="lg">
{...}
</Only>
)
Read more about the Only
helper component.
Whenever a prop name is suffixed with a breakpoint name, its value is being applied only from that breakpoint and up. Take a look at how easy it is to have a per-breakpoint gap between the Header
's composites (areas):
import { Composition } from 'atomic-layout'
const Header = () => (
<Composition
areas={...}
gutter={10}
gutterMd={20}
gutterLg={30}
>
{...}
</Composition>
)
Responsive props respect overrides, which means that would be applied as follows:
gutter={10}
on the xs
breakpoint and up;gutterMd={20}
on the md
breakpoint and up;gutterLg={30}
on the lg
breakpoint and up.You can define custom breakpoints and use them as the suffixes of your responsive props (i.e.
paddingRetina
oralignItemsDesktop
).
The areas
prop can be responsive as well! By providing different areas definitions on different breakpoints, we can alter the position and presence of our layout areas with a single prop.
const areasMobile = `
logo hamburger
`;
const areasTablet = `
logo menu actions
`;
const Header = () => (
<Composition
areas={areasMobile}
areasMd={areasTablet}
gutter={10}
>
{({ Logo, Hamburger, Menu, Actions }) => (
<>
{/**
* "Logo" is present in both area definitions,
* and is always rendered.
*/}
<Logo />
{/**
* "Hamburger" is present in "areas", but missing
* in "areasMd". It's automatically rendered only
* on "xs" and "sm" breakpoints.
*/}
<Hamburger />
{/**
* "Menu" and "Actions" are present in "areasMd",
* and are automatically rendered on "md" breakpoint
* and up.
*/}
<Menu />
<Actions />
</>
)}
</Composition>
);
Welcome declarative layouts: you describe what and when to render, and let Atomic layout handle media queries and conditions.
Unlike other solutions, Atomic layout's purpose is to distribute spacing. Spacing effectively defines a layout composition. That's why there is no predefined Col
or Row
components, but a Composition
that can be anything you want.
A grid is a composition of rows and columns, and a header may be a composition of logo and menu, and so on. There is no need to be specific when you are wielding the entire power of composition as a physical entity.
One of my favorite differences is that Atomic layout teaches you to think in terms of composition, which you can configure and render. Since its counterparts compose any layout element, you can get consistent components declaration throughout the entire application. Having a predictable way how components are defined makes their maintenance superb.
Instead of deciding what CSS properties I need to create a layout, I started asking myself: "What does this layout consist of? What is the relation between its counterparts?"
We try to make an experience of working with Atomic layout a fun way to learn CSS Grid and gain the knowledge you could apply without any libraries whatsoever. To do so, we minimize the amount of library-specific transformations of the options you provide to your composition.
<Composition
areas="header footer"
templateCols="1fr auto"
/>
.composition {
grid-template-areas: "header footer";
grid-template-columns: "1fr auto";
}
Verbose prop names and no value transformations grant almost 1-1 compatibility with the emitted CSS.
Needless to say that layout development becomes fast and efficient. You can develop production-ready components in a few minutes, without writing a single CSS line (if you want). And that involves responsive as well!
During the work on one of the side projects, I've noticed that I repeat the same layout patterns over and over. So I've tried to abstract the logic that makes those patterns into a contextless layout unit.
My admiration of Atomic design came into play, and in no time I realized that atoms and molecules could be described using CSS Grid template areas. One proof-of-concept later Atomic layout has been open-sourced.
The roadmap is to refine the existing API, improve server-side rendering, and listen to the community to evolve this library. The mission is to provide a great experience when implementing layouts.
I hope that CSS Grid will be getting more usage, as it's indeed a future of the web. There's also a lot of attention bound to TypeScript and GraphQL, and I believe they will be the main trends this year.
As of Atomic layout, I would love to see people creating layouts with it, and sharing their experience. I hope together we can improve our process, encourage us to use modern technologies, and teach developers to think in terms of composition.
I wish newcomers to find a balance between practical and theoretical knowledge and don't neglect to have a deeper understanding of a subject, even if it means spending more time. Don't be afraid to fail, and don't fear the unknown. In the end, programming is about challenging yourself every day.
I suggest interviewing Honza Javorek (@honzajavorek), who is a person behind an API testing tool called Dredd. I'm also excited to join his team full-time to work on that project.
Thank you for the interview! I want to invite everybody to the upcoming React Finland 2019, where I will be giving a talk on Atomic layout. I will be glad to answer your questions there, or via Twitter.
Thanks for the interview, Artem! At least for me, it is a refreshing new way to look at how to develop and compose layouts. You can learn more about the approach in the video below:
Check out Atomic layout on GitHub and read its documentation to grasp it better.