Skip to main contentCarbon Design System

2. Building pages

Now that we have a React app using the UI Shell, it’s time to build a few static pages. In this step, we’ll become comfortable with the Carbon grid and various Carbon components.

Preview

A preview of what you’ll build:

Fork, clone and branch

This tutorial has an accompanying GitHub repository called carbon-tutorial that we’ll use as a starting point for each step. If you haven’t forked and cloned that repository yet, and haven’t added the upstream remote, go ahead and do so by following the step 1 instructions.

Branch

With your repository all set up, let’s check out the branch for this tutorial step’s starting point.

git fetch upstream
git checkout -b react-step-2 upstream/react-step-2

Build and start app

Install the app’s dependencies (in case you’re starting fresh in your current directory and not continuing from the previous step):

yarn

Then, start the app:

yarn start

You should see something similar to where the previous step left off.

IE11 polyfills

Before we get started with this step, we’ll be adding some components that require IE11 polyfills. As shown in the Carbon React documentation, go ahead and add these imports to the top of the root index.js. They aren’t all needed, but we’ll add them all to play it safe for the duration of the tutorial.

src/index.js
import 'core-js/modules/es7.array.includes';
import 'core-js/modules/es6.array.fill';
import 'core-js/modules/es6.string.includes';
import 'core-js/modules/es6.string.trim';
import 'core-js/modules/es7.object.values';

Install grid

In our last step we added our styles, component and icon packages. Now that we’re building the pages with grid, we need to install one more Carbon package. Stop your development environment (CTRL-C) and:

yarn add @carbon/[email protected]

In index.scss, we need to configure our grid to use 16 columns instead of the default 12 columns. We do this by adding grid-columns-16: true in our $feature-flags.

src/index.scss
$feature-flags: (
grid-columns-16: true,
);

Run yarn start so we can begin building.

Add landing page grid

Let’s add our grid elements to LandingPage.js.

In order to use the grid, we need to wrap everything in a <div className="bx--grid">. We can continue to make rows by adding a <div className="bx--row"> inside the grid, as well as make columns within those rows by adding <div className="bx--col-[breakpoint]-[size]">.

Our column sizes are specified by the number of columns they’ll be spanning. If we use bx--col-lg-4, it means it’ll span 4 of the 16 columns. If we use bx--col-lg-8 it means it’ll span 8 of the 16 columns, and so on.

We’ve included the designs for this tutorial app in the design.sketch file found as a top-level file in the carbon-tutorial repository. But, if you don’t have Sketch installed and available to inspect the design, we’ll provide screenshots.

Landing page grid

Landing page grid

We’ll break this down into three rows. The first row with the gray background doesn’t appear to need any columns. The second row with the white background looks like it has two columns of different widths. The third row with the gray background looks like it has four columns of equal width.

We’ll make rows like so:

src/content/LandingPage/LandingPage.js
return (
<div className="bx--grid bx--grid--full-width landing-page">
<div className="bx--row landing-page__banner">
<div className="bx--col-lg-16">1</div>
</div>
<div className="bx--row landing-page__r2">
<div className="bx--col-md-4 bx--col-lg-7">7/16</div>
<div className="bx--col-md-4 bx--offset-lg-1 bx--col-lg-8">8/16</div>
</div>

We added a class of bx--grid--full-width to the grid container since our rows need to expand the whole page without any margins. We also added some custom classes like landing-page, landing-page__banner, landing-page__r2, etc., which we will use later.

Also, take notice of the second row. The tab content only covers 7 columns at this large viewport to prevent overly-large line lengths, so we needed to add a 1 column offset bx--offset-lg-1 to second column to fill the full 16 columns in the grid. Then, both of those columns have bx--col-md-4 classes so they are of equal width at medium-sized viewports.

Build landing page

We’ll start adding HTML elements and components by row.

First row

In our first row we’ll need a BreadCrumb component. First, let’s import the components we need. Because we’ll be importing several components for this page, we’ll import them directly from the carbon-components-react package instead of the direct path for each one.

src/content/LandingPage/LandingPage.js
import { Breadcrumb, BreadcrumbItem } from 'carbon-components-react';

We can now add our component to the first row, along with a header, like so:

src/content/LandingPage/LandingPage.js
<div className="bx--row landing-page__banner">
<div className="bx--col-lg-16">
<Breadcrumb noTrailingSlash>
<BreadcrumbItem>
<a href="/">Getting started</a>
</BreadcrumbItem>
</Breadcrumb>
<h1 className="landing-page__heading">Design &amp; build with Carbon</h1>
</div>

You may notice that the styles look off. Don’t worry, we’ll fix these later.

Second row

In our second row we’ll need Tabs and Button components. We’ll update the carbon-components-react import to:

src/content/LandingPage/LandingPage.js
import {
Breadcrumb,
BreadcrumbItem,
Button,
Tabs,
Tab,
} from 'carbon-components-react';

Before we can render the tabs, we need to add some props for the component after the import. These example props came from the React Tabs Story in Storybook.

src/content/LandingPage/LandingPage.js
const props = {
tabs: {
selected: 0,
role: 'navigation',
},
tab: {
role: 'presentation',
tabIndex: 0,
},

Modify the second row to use the Tab component.

src/content/LandingPage/LandingPage.js
<div className="bx--row landing-page__r2">
<div className="bx--col bx--no-gutter">
<Tabs {...props.tabs}>
<Tab {...props.tab} label="About">
<div className="bx--grid bx--grid--no-gutter bx--grid--full-width">
<div className="bx--row landing-page__tab-content">
<div className="bx--col-md-4 bx--col-lg-7">7/16</div>
<div className="bx--col-md-4 bx--offset-lg-1 bx--col-lg-8">
8/16

Hold up! If you were to run DAP to check for accessibility violations, you’d see Multiple navigation landmarks must have unique labels specified with aria-label or aria-labelledby because both the Breadcrumb and Tabs components use <nav> elements. To fix, add aria-label to the Breadcrumb opening tag:

<Breadcrumb noTrailingSlash aria-label="Page navigation">

Same goes for the Tabs opening tag:

<Tabs {...props.tabs} aria-label="Tab navigation">

Next, we’ll need to add some styling overrides to move the tabs to the right on large viewports. Create a file _overrides.scss in src/content/LandingPage with this declaration block.

src/content/LandingPage/_overrides.scss
.landing-page__r2 .bx--tabs--scrollable {
transform: translateZ(0);
justify-content: flex-end;
}
.landing-page__r2 .bx--tab-content {
padding: 0;
}

Then in _landing-page.scss add this import at the top of the file.

src/content/LandingPage/_landing-page.scss
@import './overrides.scss';

We can now add our images and text for each column in the first Tab in LandingPage.js.

src/content/LandingPage/LandingPage.js
<Tab {...props.tab} label="About">
<div className="bx--grid bx--grid--no-gutter bx--grid--full-width">
<div className="bx--row landing-page__tab-content">
<div className="bx--col-md-4 bx--col-lg-7">
<h2 className="landing-page__subheading">
What is Carbon?
</h2>
<p className="landing-page__p">
Carbon is IBM’s open-source design system for digital

Now let’s set the image size in _landing-page.scss:

src/content/LandingPage/_landing-page.scss
.landing-page__illo {
max-width: 100%;
}

Assuming that the second and third tab would have a similar design, we would set them up in the same way. However, since our design specs don’t show those tabs, we’ll leave the code as is.

Third row

The third row will be created in a later tutorial, so we’ll just add the headers for now.

src/content/LandingPage/LandingPage.js
<div className="bx--row landing-page__r3">
<div className="bx--col-md-4 bx--col-lg-4">
<h3 className="landing-page__label">The Principles</h3>
</div>
<div className="bx--col-md-4 bx--col-lg-4">Carbon is Open</div>
<div className="bx--col-md-4 bx--col-lg-4">Carbon is Modular</div>
<div className="bx--col-md-4 bx--col-lg-4">Carbon is Consistent</div>
</div>

Style landing page

We’ve added basic layout styles in _landing-page.scss, so now let’s add type, color, and spacing styles to match the design. We’ll be using our spacing tokens. In app.scss, add these imports at the top of the file so we can use Carbon breakpoints, tokens, and typography Sass mixins and functions:

src/app.scss
@import 'carbon-components/scss/globals/scss/vendor/@carbon/type/scss/font-family.scss';
@import 'carbon-components/scss/globals/scss/vendor/@carbon/layout/scss/breakpoint.scss';
@import 'carbon-components/scss/globals/scss/typography.scss';
@import 'carbon-components/scss/globals/scss/vars.scss';
Banner vertical spacing

Banner vertical spacing

Back to _landing-page.scss, we need to add space above the breadcrumb and below the heading. For that, add:

src/content/LandingPage/_landing-page.scss
.landing-page__banner {
padding-top: $spacing-05;
padding-bottom: $spacing-07 * 4;
}

Referencing the spacing token table, 16px can be set with the $spacing-05 token. The design calls for 128px of space below the heading and that’s not in the spacing scale. We can achieve this in Sass by multiplying 32px ($spacing-07) by 4. We could use 128px or 8rem directly in our styling, but using our tokens preserves consistency should the token values get updated in the future.

Looking at the design, we need a wall-to-wall light gray background behind the banner and also behind the third row. This is a great opportunity to use a Sass mixin. We could put this at the top of _landing-page.scss, but it’s best practice to place mixins in a dedicated file, so create a _mixins.scss file in src/content/LandingPage.

Add the following in _mixins.scss. Per the design we need to use Gray 10 for our banner background color, which can be set with the $ui-01 color token. Also, we want the background to extend into the grid’s outermost gutters to go the full width of the viewport, so given the DOM structure, we can achieve that by setting the background in an absolutely positioned pseudo element.

src/content/LandingPage/_mixins.scss
@mixin landing-page-background() {
background-color: $ui-01;
position: relative;
&::before {
content: '';
position: absolute;
left: -$spacing-06;
top: 0;

After you have created _mixins.scss, import it at the top of _landing-page.scss. By now you should have two imports:

src/content/LandingPage/_landing-page.scss
@import './mixins.scss';
@import './overrides.scss';

Now to use the new mixin, update the .landing-page__banner declaration block to:

src/content/LandingPage/_landing-page.scss
.landing-page__banner {
padding-top: $spacing-05;
padding-bottom: $spacing-07 * 4;
@include landing-page-background;
}

Next, we can see that the h1 is using the heading-05 type token.