Layout Components
Layout components are an abstraction on top of CSS.
Made popular by Mark Dalgleish's talk https://www.youtube.com/watch?v=jnV1u67_yVg (opens in a new tab) on Braid's design-system https://seek-oss.github.io/braid-design-system/components/Stack/ (opens in a new tab).
Article on the subject by styled-component's creator https://mxstbr.com/thoughts/margin (opens in a new tab)
At LesFurets, we were able to build a complete app without a single line of css. CSS was only used in design-system components, which included a Box and a a Stack component for layout.
Pros:
- Share a common language with UX designers
- Ease of use (more so for devs uncomfortable with css)
- Use React composition
- Better encapsulation
Cons:
- Not as flexible as CSS to face high layout complexity
- hard to overcome unecessary divs in the DOM
- "responsive-in-JS" can be a bit verbose
Box
An abstraction that represents space around a component. (Our UX designers use the word "spacing" so we went with it, but it's just margins)
<Box horizontalSpacing="4px">
<Child/>
</Box>
<Box spacingTop="2px" spacingLeft="10px">
<Child/>
</Box>
Those "2px"
declarations can be replaced with const
s or a TS enum
Stack
An abstraction that stacks components in a line or a pile.
Could use /*prettier-ignore*/
for a visual code representation:
;<Stack column>
<Child1 />
<Child2 />
<Child3 />
</Stack>
;<Stack line gap="2px">
<Child1 />
<Child2 />
<Child3 />
</Stack>
We could use /*prettier-ignore*/
for a visual code representation where "Row" childrens are in the same line.
Making the bundler translate indentation to line/column might be an idea to explore...
Aiming for dev Flow
At Koober, we went for a shorter API, we used concise naming and minimized effort in typing/auto-importing/completing.
We reached flow state more often that way.
Switching files, browsing imports, bringing up auto-completion, live-reload delays etc. are all tiny details that can break flow.
API Example:
<Row m={2} bg="red.100">
<Box px={4}>Hello</Box>
<Column ml={4} gap={2}>
<Box>World 1</Box>
<Box>World 2</Box>
</Column>
</Row>
Using boolean props can help making the API even more concise but it can cause confusion.
<Stack column />
<Stack column row>Can be confusing</Stack>
Responsive in JS
import { useState, useEffect } from 'react'
export function useMediaQuery(query) {
const [matches, setMatches] = useState(false)
useEffect(() => {
const media = window.matchMedia(query)
if (media.matches !== matches) {
setMatches(media.matches)
}
const listener = () => {
setMatches(media.matches)
}
media.addListener(listener)
return () => media.removeListener(listener)
}, [matches, query])
return matches
}
function Page() {
let isPageWide = useMediaQuery('(min-width: 800px)')
return (
<>
{isPageWide && <UnnecessarySidebar />}
<ImportantContent />
</>
)
}
We abstracted on top of this to match UX designer's language and their breakpoints:
const { isMobile, isTablet, isDesktop } = useResponsive()