Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alanbsmith/c960da5c23127d74929e00ca9c67fbc9 to your computer and use it in GitHub Desktop.
Save alanbsmith/c960da5c23127d74929e00ca9c67fbc9 to your computer and use it in GitHub Desktop.
Canvas Kit V5 June Workshop | Session 1 Walkthrough

Session 1 Walkthrough | June Workshop

Table of Contents

Fork the Codesandbox Template

Build a PageHeader

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

First, we'll add the file

src/components/PageHeader.tsx

Add Initial Header

We could create an initial PageHeader like this:

/** @jsx jsx */
import { jsx, css } from "@emotion/react";
// import styled from '@emotion/styled';
import * as React from "react";
import { gradients, space, type } from "@workday/canvas-kit-react/tokens";

const pageHeaderStyles = css({
  backgroundImage: gradients.blueberry,
  padding: `${space.m} ${space.xl}`,
});

const pageHeadingStyles = css({
  ...type.levels.heading.medium,
  ...type.variants.inverse,
  fontWeight: type.properties.fontWeights.regular,
  margin: 0,
});

export const PageHeader: React.FC = ({ children, ...props }) => (
  <header {...props} css={pageHeaderStyles}>
    <h1 css={pageHeadingStyles}>Canvas Coffee Roasters</h1>
  </header>
);

Or we could use Box

View the Box docs

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

import { Box, BoxProps } from "@workday/canvas-kit-labs-react/common";
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 as="h1" fontWeight="regular" variant="inverse" {...props}>
      {props.children}
    </Heading>
  );
};

Build a Sidebar

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

First, we'll add the file:

src/components/Sidebar.tsx
import * as React from "react";

import { Box, BoxProps } from "@workday/canvas-kit-labs-react/common";

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

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

Et voilà! A simple Sidebar.

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.

Note: IE11 does not properly support flexBasis: 0. If you're using the flexBasis prop or flex (which implicitly sets flex-basis to 0), you'll likely want to manually set flexBasis to auto. E.g. flex="1 1 auto" or flex={1} flexBasis="auto"

export const Home: React.FC = () => {
  const { coffee } = useAllCoffee();
  return (
    <Flex as="main" flexDirection="column" flex="1 1 auto" 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!

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.

First, we'll add the file:

src/components/Card.tsx

Create Initial Card

Now 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, Subtext } 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>
      <Subtext size="large" variant="hint">
        $17.40 | 12 oz.
      </Subtext>
    </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.

View the Stack docs

A Quick Refactor

import * as React from "react";

import { Flex, Stack } from "@workday/canvas-kit-labs-react/layout";
import { Heading, Body, Subtext } 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>
        <Subtext size="large" variant="hint">
          $17.40 | 12 oz.
        </Subtext>
      </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.

// src/pages/Home/index.tsx

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

export const Home: React.FC = () => {
  const { coffee } = useAllCoffee();
  return (
    <Flex as="main" flexDirection="column" flex="1 1 auto" 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 "@workday/canvas-kit-labs-react/layout";
import { Body, BodyProps, Subtext, SubtextProps } 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.Subtext = ({ children, ...props }: SubtextProps) => {
  return (
    <Subtext size="large" variant="hint" {...props}>
      {children}
    </Subtext>
  );
};

And here's what the new composable Card API will look like:

// src/pages/Home/index.tsx
// ...
<Card>
  <Card.Image type="blackberry" alt="blackberry coffee bag" />
  <Card.Content>
    <Card.Heading>El Salvador Apaneca</Card.Heading>
    <Card.Body>Toffee & Cocoa</Card.Body>
    <Card.Subtext>$17.40 | 12 oz.</Card.Subtext>
  </Card.Content>
</Card>

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

Wiring Up Cards

// src/pages/Home/index.tsx

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.Subtext>
                ${brew.price} | {brew.bagWeight} oz.
              </Card.Subtext>
            </Card.Content>
          </Card>
        ))}
      </Flex>
    </Flex>
  );
};

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