Stack Components using Twin.macro

I like to use Tailwind via twin.macro for styling. These are a VStack and HStack components that can be used to layout UI in React.

<HStack space={4}>
  <div tw="bg-blue-500 w-20 h-20" />
  <div tw="bg-blue-500 w-20 h-20" />
  <div tw="bg-blue-500 w-20 h-20" />
</HStack>

Implementation

twin.macro runs at build time so we need to define all possible styles upfront. This gives us ability to space between 0 and 16 in horizontal and vertical directions.

import tw, { TwStyle } from "twin.macro";

export interface StackProps {
  justify?: JustifyValues;
  align?: AlignValues;
  space?: StackSpace;
  className?: string;
}

type JustifyValues = "center" | "between" | "start" | "end";
type AlignValues = "center" | "start" | "end" | "baseline";

const justifyValues: Record<JustifyValues, TwStyle> = {
  center: tw`justify-center`,
  between: tw`justify-between`,
  start: tw`justify-start`,
  end: tw`justify-end`,
};

const alignValues: Record<AlignValues, TwStyle> = {
  center: tw`items-center`,
  baseline: tw`items-baseline`,
  start: tw`items-start`,
  end: tw`items-end`,
};

type StackSpace = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 14 | 16;

const vSpaces: Record<StackSpace, TwStyle> = {
  0: tw`space-y-0`,
  1: tw`space-y-1`,
  2: tw`space-y-2`,
  3: tw`space-y-3`,
  4: tw`space-y-4`,
  5: tw`space-y-5`,
  6: tw`space-y-6`,
  7: tw`space-y-7`,
  8: tw`space-y-8`,
  9: tw`space-y-8`,
  10: tw`space-y-8`,
  12: tw`space-y-12`,
  14: tw`space-y-14`,
  16: tw`space-y-16`,
};

const hSpaces: Record<StackSpace, TwStyle> = {
  0: tw`space-x-0`,
  1: tw`space-x-1`,
  2: tw`space-x-2`,
  3: tw`space-x-3`,
  4: tw`space-x-4`,
  5: tw`space-x-5`,
  6: tw`space-x-6`,
  7: tw`space-x-7`,
  8: tw`space-x-8`,
  9: tw`space-x-8`,
  10: tw`space-x-8`,
  12: tw`space-x-12`,
  14: tw`space-x-14`,
  16: tw`space-x-16`,
};

const BaseStack: React.FC<StackProps & { dir: "vertical" | "horizontal" }> = ({
  dir,
  justify,
  align,
  space,
  ...props
}) => (
  <div
    {...props}
    css={[
      dir === "vertical" ? tw`flex flex-col` : tw`flex`,
      dir === "vertical" ? vSpaces[space ?? 0] : hSpaces[space ?? 0],
      justify != null && justifyValues[justify],
      align != null && alignValues[align],
    ]}
  />
);

export const HStack: React.FC<StackProps> = props => (
  <BaseStack {...props} dir="horizontal" />
);

export const VStack: React.FC<StackProps> = props => (
  <BaseStack {...props} dir="vertical" />
);

tags: web

Last Updated February 27, 2021