Skip to content

Instantly share code, notes, and snippets.

@alanbsmith
Created April 27, 2021 08:02
Show Gist options
  • Save alanbsmith/bfb7ec325d02a97c38d0be81b473c71b to your computer and use it in GitHub Desktop.
Save alanbsmith/bfb7ec325d02a97c38d0be81b473c71b to your computer and use it in GitHub Desktop.
Canvas Kit v5 Workshop Session 1 | Walkthrough Guide

Session 1 Walkthrough

Table of Contents

Checkout the Branch

git checkout session-1-start

Build a PageHeader

The PageHeader in Canvas Kit is very similar, but we can build our own too. Let's do that!

touch src/components/PageHeader.tsx

Add Initial Header

We'll create an initial PageHeader like this:

import * as React from "react";
import { gradients } from "@workday/canvas-kit-react/tokens";

import { Box } from "./common/primitives";
import { Heading } from "./common/type";

export const PageHeader: React.FC = (props) => {
  return (
    <Box
      as="header"
      paddingX="xl"
      paddingY="m"
      backgroundImage={gradients.blueberry}
      {...props}
    >
      <Heading fontWeight="regular" variant="inverse" {...props}>
        Canvas Coffee Roasters
      </Heading>
    </Box>
  );
};

Small (Optional) Refactor

But maybe you want this header to be more flexible and composable. You could also write it like this:

import * as React from "react";
import { gradients } from "@workday/canvas-kit-react/tokens";

import { Box, BoxProps } from "./common/primitives";
import { Heading, HeadingProps } from "./common/type";

export const PageHeader = ({ children, ...props }: BoxProps) => {
  return (
    <Box
      as="header"
      paddingX="xl"
      paddingY="m"
      backgroundImage={gradients.blueberry}
      {...props}
    >
      {children}
    </Box>
  );
};

PageHeader.Heading = (props: HeadingProps) => {
  return (
    <Heading fontWeight="regular" variant="inverse" {...props}>
      {props.children}
    </Heading>
  );
};

Add it to App

export const App: React.FC = (props) => {
  return (
    <CanvasProvider>
      <FontContainer>
        <Global styles={css(globalStyles)} />
        <PageHeader>
          <PageHeader.Heading>Canvas Coffee Roasters</PageHeader.Heading>
        </PageHeader>
        {props.children}
      </FontContainer>
    </CanvasProvider>
  );
};

Commit

git add .
git status
git commit -m "Add PageHeader component"

Build a Sidebar

Now that we have a PageHeader, we can build a Sidebar.

touch src/components/Sidebar.tsx

Create the Sidebar

First we'll use Box to create our Sidebar.

import * as React from "react";

import { Box } from "./common/primitives/Box";

export const Sidebar: React.FC = ({ children }) => {
  return (
    <Box
      as="aside"
      minWidth={400}
      borderRight="solid 1px"
      borderRightColor="soap400"
      padding="xl"
    >
      {children}
    </Box>
  );
};

Then you'll import it to App.tsx to implement it. We'll wrap it in a Flex to build our layout.

export const App: React.FC = (props) => {
  return (
    {/* other JSX omitted */}
    <Flex minHeight="100vh">
      <Sidebar>Hello, Sidebar!</Sidebar>
      {props.children}
    </Flex>
    {/* other JSX omitted */}
  );
}

Et voilà! A simple Sidebar.

Commit

git add .
git status
git commit -m "Add Sidebar component"

Update Home Content

Now we'll update the main content in Home/index.tsx. We'll update the element to main with the as prop, add 40px of padding and set flex to 1.

export const Home: React.FC = () => {
  const { coffee } = useAllCoffee();
  return (
    <Flex as="main" flexDirection="column" flex={1} padding="xl">
      <Title>Hello, Canvas Kit Workshop!</Title>
      <Body>Coffee Count: {coffee.length}</Body>
    </Flex>
  );
};

Boom! Done! That's our page layout! Next up: Card Layout!

Commit

git add .
git status
git commit -m "Update Home content"

Build a Card Layout

Similar to PageHeader, we could use the Card component in Canvas Kit, and modify it. Or we could create our own. Let's see what we can do.

touch src/components/Card.tsx

Create Initial Card

Let's start by building a static card following the spec in Figma.

import * as React from "react";

import { Flex } from "./common/layout";
import { Box } from "./common/primitives";
import { Body, Detail } from "./common/type";
import { Image } from "./Image";

export const Card: React.FC = () => {
  return (
    <Flex
      margin="s"
      flexDirection="column"
      flexBasis="240px"
      alignSelf="stretch"
      flexGrow={0}
      padding="l"
      depth={2}
      backgroundColor="frenchVanilla100"
    >
      <Image
        height="100%"
        padding="s"
        type="blackberry"
        alt={`blackberry coffee bag`}
      />
      <Flex marginBottom="xxs">
        <Body as="h3" fontWeight="bold" size="large">
          El Salvador Apaneca
        </Body>
      </Flex>
      <Box marginBottom="xxs">
        <Body>Toffee & Cocoa</Body>
      </Box>
      <Detail size="large" variant="hint">
        $17.40 | 12 oz.
      </Detail>
    </Flex>
  );
};

Great! This looks good, but it would be nice to clean up those Flex wrappers around the text. This is a situation where Stack comes in handy.

A Quick Refactor

import * as React from "react";

import { Flex, Stack } from "./common/layout";
import { Box } from "./common/primitives";
import { Body, Detail } from "./common/type";
import { Image } from "./Image";

export const Card: React.FC = () => {
  return (
    <Flex
      margin="s"
      flexDirection="column"
      flexBasis="240px"
      alignSelf="stretch"
      flexGrow={0}
      padding="l"
      depth={2}
      backgroundColor="frenchVanilla100"
    >
      <Image
        height="100%"
        padding="s"
        type="blackberry"
        alt={`blackberry coffee bag`}
      />
      <Stack spacing="xxs" flexDirection="column">
        <Body as="h3" fontWeight="bold" size="large">
          El Salvador Apaneca
        </Body>
        <Body>Toffee & Cocoa</Body>
        <Detail size="large" variant="hint">
          $17.40 | 12 oz.
        </Detail>
      </Stack>
    </Flex>
  );
};

Nice! Stack is providing consistent spacing between those text elements.

Building a Card Layout

Now that we have our initial Card built, we can render it in the layout. We'll add a Flex wrapper to build the layout.

import { Card } from "../../components/Card";

export const Home: React.FC = () => {
  const { coffee } = useAllCoffee();
  return (
    <Flex as="main" flexDirection="column" flex={1} padding="xl">
      <Flex flexWrap="wrap" alignItems="flex-start">
        <Card />
      </Flex>
    </Flex>
  );
};

Cool! It's looking good. Now we need to make Card more flexible to support dynamic data.

Another Card Refactor

We'll refactor Card to be more to be more composable.

import * as React from "react";

import {
  Flex,
  FlexProps,
  Stack,
  StackProps,
  SpacingValue,
} from "./common/layout";
import { Body, BodyProps, Detail, DetailProps } from "./common/type";
import { Image, ImageProps } from "./Image";

export const Card = ({ children, ...props }: FlexProps) => {
  return (
    <Flex
      margin="s"
      flexDirection="column"
      flexBasis="240px"
      alignSelf="stretch"
      flexGrow={0}
      padding="l"
      depth={2}
      backgroundColor="frenchVanilla100"
      {...props}
    >
      {children}
    </Flex>
  );
};

Card.Image = ({ type, alt }: ImageProps) => {
  return <Image height="100%" padding="s" type={type} alt={alt} />;
};

type CardContent = Omit<StackProps, "spacing"> & {
  spacing?: SpacingValue;
};

Card.Content = ({ children, spacing = "xxs", ...props }: CardContent) => {
  return (
    <Stack spacing={spacing} flexDirection="column" {...props}>
      {children}
    </Stack>
  );
};

Card.Heading = ({ children, ...props }: BodyProps) => {
  return (
    <Body as="h3" fontWeight="bold" size="large" {...props}>
      {children}
    </Body>
  );
};

Card.Body = ({ children, ...props }: BodyProps) => {
  return <Body {...props}>{children}</Body>;
};

Card.Detail = ({ children, ...props }: DetailProps) => {
  return (
    <Detail size="large" variant="hint" {...props}>
      {children}
    </Detail>
  );
};

Awesome! You did it! Now let's wire up the cards to our data!

Wiring Up Cards

import { Card } from "../../components/Card";

export const Home: React.FC = () => {
  const { coffee } = useAllCoffee();
  return (
    <Flex as="main" flexDirection="column" flex={1} padding="xl">
      <Flex flexWrap="wrap" alignItems="flex-start">
        {coffee.map((brew) => (
          <Card key={brew.id}>
            <Card.Image type={brew.img} alt={`${brew.name} coffee bag`} />
            <Card.Content>
              <Card.Heading>{brew.name}</Card.Heading>
              <Card.Body>{brew.flavorProfile}</Card.Body>
              <Card.Detail>
                ${brew.price} | {brew.bagWeight} oz.
              </Card.Detail>
            </Card.Content>
          </Card>
        ))}
      </Flex>
    </Flex>
  );
};

Awesome! Way to go!

Commit

git add .
git status
git commit -m "Add Card component and wire up card layout"

Congrats!

You completed Session 1! We learned how to use style props with layout and type components. We also learned how to refactor components to make them more composable and flexible. Good job! See you in Session 2!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment