Most web layout is a combination of grids and stacks of elements that we create with either CSS grid or flexbox. In this post, I want to share how to write significantly less CSS by leveraging Stack
components.
import styled, { css } from "styled-components"
import { getCSSUnit } from "lib/ui/utils/getCSSUnit"
interface Props {
gap?: React.CSSProperties["gap"]
alignItems?: React.CSSProperties["alignItems"]
justifyContent?: React.CSSProperties["justifyContent"]
wrap?: React.CSSProperties["flexWrap"]
children: React.ReactNode
fullWidth?: boolean
fullHeight?: boolean
}
const formatFlexAlignment = (
value:
| React.CSSProperties["alignItems"]
| React.CSSProperties["justifyContent"]
) => {
if (value === "end" || value === "start") {
return `flex-${value}`
}
return value
}
const stackCSS = css<Props>`
display: flex;
${({ gap }) =>
gap &&
css`
gap: ${getCSSUnit(gap)};
`}
${({ alignItems }) =>
alignItems &&
css`
align-items: ${formatFlexAlignment(alignItems)};
`}
${({ justifyContent }) =>
justifyContent &&
css`
justify-content: ${formatFlexAlignment(justifyContent)};
`}
${({ wrap }) =>
wrap &&
css`
flex-wrap: ${wrap};
`}
${({ fullWidth }) =>
fullWidth &&
css`
width: 100%;
`}
${({ fullHeight }) =>
fullHeight &&
css`
height: 100%;
`}
`
export const VStack = styled.div`
${stackCSS}
flex-direction: column;
`
export const HStack = styled.div`
${stackCSS}
flex-direction: row;
`
interface StackProps extends Props {
direction: React.CSSProperties["direction"]
}
export const Stack = styled.div<StackProps>`
${stackCSS}
flex-direction: ${({ direction }) => direction};
`
Here we have three components: VStack
, HStack
, and Stack
. While the first two have fixed directions, the Stack component receives a direction as a property. It could be helpful for responsive views, but I rarely use it since flex-wrap
is enough in most cases.
Both vertical and horizontal stacks have shared styles except flex-direction
property. These components have only optional properties and don't rewrite flexbox attributes unless there is a prop for it. To set types for gap
, alignItems
, justify-content
, and wrap
, we leverage React'sCSSProperties
. Often we want to make the stack take full width or height. Safaris don't support end and start values yet, so we have a formatFlexAlignment
helper that will add a flex prefix to them.
In my product at increaser.org, I use these components all the time. VStack
has 443 occurrences, and HStack
201.