diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b0f0b110900..e0dedb76a38 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -17,3 +17,7 @@ src/pages/data-visualization/* @theiliad # @natashadecoste # Lee Chase for Vue tutorial /src/pages/tutorial/vue/* @lee-chase + +# Lee Chase for Web components tutorial + +/src/pages/tutorial/web-components/* @lee-chase diff --git a/gatsby-node.js b/gatsby-node.js index 0b4d93b595e..48ab419461c 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -61,6 +61,11 @@ exports.createPages = ({ actions }) => { toPath: '/developing/react-tutorial/overview', isPermanent: true, }); + createRedirect({ + fromPath: '/tutorial/web-components/overview', + toPath: '/developing/web-components-tutorial/overview', + isPermanent: true, + }); createRedirect({ fromPath: '/tutorial/angular/overview', toPath: '/developing/angular-tutorial/overview', @@ -192,12 +197,12 @@ exports.createPages = ({ actions }) => { toPath: '/elements/motion/overview', isPermanent: true, }); - createRedirect({ + createRedirect({ fromPath: '/guidelines/motion/choreography', toPath: '/elements/motion/choreography', isPermanent: true, }); - createRedirect({ + createRedirect({ fromPath: '/guidelines/motion/code', toPath: '/elements/motion/code', isPermanent: true, diff --git a/src/data/nav-items.yaml b/src/data/nav-items.yaml index e5a54b63f75..10fa229ffc8 100644 --- a/src/data/nav-items.yaml +++ b/src/data/nav-items.yaml @@ -35,6 +35,8 @@ path: /developing/angular-tutorial/overview/ - title: React tutorial path: /developing/react-tutorial/overview/ + - title: Web components tutorial + path: /developing/web-components-tutorial/overview/ - title: Vue tutorial path: /developing/vue-tutorial/overview/ - title: Contributing diff --git a/src/pages/all-about-carbon/who-uses-carbon.mdx b/src/pages/all-about-carbon/who-uses-carbon.mdx index 14228923bbd..9aa05043770 100755 --- a/src/pages/all-about-carbon/who-uses-carbon.mdx +++ b/src/pages/all-about-carbon/who-uses-carbon.mdx @@ -134,6 +134,7 @@ Here are some ways developers can begin engaging with Carbon. - Check out a tutorial in your framework of choice ([React using Next.js](/developing/react-tutorial/overview/), + ([Web components using Vanilla.js/HTML](/developing/web-components-tutorial/overview/), [Angular (community)](/developing/angular-tutorial/overview/) or [Vue (community)](/developing/vue-tutorial/overview/)). diff --git a/src/pages/developing/get-started.mdx b/src/pages/developing/get-started.mdx index 4bc6cb47233..9a974cd3c53 100644 --- a/src/pages/developing/get-started.mdx +++ b/src/pages/developing/get-started.mdx @@ -57,6 +57,16 @@ the way. + + + + + + + diff --git a/src/pages/developing/web-components-tutorial/images/step-5/asset-404.png b/src/pages/developing/web-components-tutorial/images/step-5/asset-404.png new file mode 100644 index 00000000000..42e4f4fe66b Binary files /dev/null and b/src/pages/developing/web-components-tutorial/images/step-5/asset-404.png differ diff --git a/src/pages/developing/web-components-tutorial/images/step-5/linter-error-details.png b/src/pages/developing/web-components-tutorial/images/step-5/linter-error-details.png new file mode 100644 index 00000000000..4a240f62429 Binary files /dev/null and b/src/pages/developing/web-components-tutorial/images/step-5/linter-error-details.png differ diff --git a/src/pages/developing/web-components-tutorial/images/step-5/linter-error.png b/src/pages/developing/web-components-tutorial/images/step-5/linter-error.png new file mode 100644 index 00000000000..3929ef4d303 Binary files /dev/null and b/src/pages/developing/web-components-tutorial/images/step-5/linter-error.png differ diff --git a/src/pages/developing/web-components-tutorial/images/step-5/spelling-illo.png b/src/pages/developing/web-components-tutorial/images/step-5/spelling-illo.png new file mode 100644 index 00000000000..1355b833524 Binary files /dev/null and b/src/pages/developing/web-components-tutorial/images/step-5/spelling-illo.png differ diff --git a/src/pages/developing/web-components-tutorial/images/step-5/spelling-suggestions.png b/src/pages/developing/web-components-tutorial/images/step-5/spelling-suggestions.png new file mode 100644 index 00000000000..1d2fcdd8995 Binary files /dev/null and b/src/pages/developing/web-components-tutorial/images/step-5/spelling-suggestions.png differ diff --git a/src/pages/developing/web-components-tutorial/overview.mdx b/src/pages/developing/web-components-tutorial/overview.mdx new file mode 100644 index 00000000000..2568649cfac --- /dev/null +++ b/src/pages/developing/web-components-tutorial/overview.mdx @@ -0,0 +1,105 @@ +--- +title: Web components tutorial +description: + Welcome to Carbon! This tutorial will guide you in creating a Vanilla JS/HTML + app using Web Components from the Carbon Design System. +tabs: ['Overview', 'Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5'] +--- + +import Preview from 'components/Preview'; + + + +Welcome to Carbon! This tutorial will guide you in creating a Vanilla JS/HTML +app using Web Components from the Carbon Design System. We'll teach you the ins +and outs of using Carbon components, while introducing web development best +practices along the way. + +Web components are native custom components based on standards that can be used +in any modern browser with any JavaScript library or framework just like plain +HTML elements. + +For more information +[see Web Components](https://www.webcomponents.org/introduction). + + + + + +Audience +Prerequisites +Outline + + + +Here's a [preview](https://carbon-tutorial-nextjs.vercel.app/) of what you will +build: + + + +## Audience + +This tutorial is intended for people with all amounts of web development +experience. If you want to jump straight to code, you may want to skip this +tutorial and go to the [developers getting started](/developing/get-started) +page. + +## Prerequisites + +### HTML + +This is a web development tutorial that uses HTML. If you're not sure that you +are quite ready then hop over to +[W3Schools](https://www.w3schools.com/html/default.asp) + +### SCSS (CSS preprocessor) + +This tutorial uses SASS, or rather the CSS styled flavour called SCSS. If you +need help with CSS then head to +[W3Schools](https://www.w3schools.com/css/default.asp). For help with SCSS head +to [sass-lang.com](https://sass-lang.com/guide/). + +### ECMAScript (Javascript) + +Javascript is fundemental to adding interaction to your HTML. There are many +great resources out there, such as +[W3Schools](https://www.w3schools.com/js/default.asp), which is more than enough +for everything in this tutorial. + +### GitHub + +We'll be using GitHub to build an app together, so if you're new to GitHub, make +sure you've [made an account](https://github.com/join). Their +[getting started guide](https://guides.github.com/activities/hello-world) is a +great way to learn GitHub. + +### Pnpm + +This tutorial uses [Pnpm](https://pnpm.io/) for dependency management as that +was the default for `create-vite`. Make sure that you have +[Pnpm installed](https://pnpm.io/installation) prior to starting the tutorial so +you can follow along step-by-step. + +## Outline + +Each step in this tutorial illustrates a different aspect of developing web +applications with Carbon. We recommend starting with step 1, but you can pick up +any step and take it from there. + +1. [**Installing Carbon**](/developing/web-components-tutorial/step-1) + - Create a web app with the UI shell component. +2. [**Building pages**](/developing/web-components-tutorial/step-2) + - Build pages with the grid and various components. +3. [**Using APIs**](/developing/web-components-tutorial/step-3) + - Populate the data table with an external data source. +4. [**Creating components**](/developing/web-components-tutorial/step-4) + - Extend Carbon by creating your own components. +5. [**Deploying**](/developing/web-components-tutorial/step-5) + - Build and host your app in a production environment. diff --git a/src/pages/developing/web-components-tutorial/step-1.mdx b/src/pages/developing/web-components-tutorial/step-1.mdx new file mode 100644 index 00000000000..d1400b87b49 --- /dev/null +++ b/src/pages/developing/web-components-tutorial/step-1.mdx @@ -0,0 +1,777 @@ +--- +title: 1. Installing Carbon +description: + Welcome to Carbon! This tutorial will guide you in creating a Vanilla JS/HTML + app using Web Components from the Carbon Design System. +tabs: ['Overview', 'Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5'] +--- + +import Preview from 'components/Preview'; + + + +Starting from a base `create-vite` app, created using the `Vanilla` and +`Javascript` options, let's install Carbon and begin using Carbon components. By +the end you will have a Vanilla app that uses the UI Shell to navigate between +pages. + + + + + +Fork, clone and branch +Build and start +Install Carbon +Install Sass +A working Carbon button +Add UI Shell +Push to GitHub + + + +## Preview + +A [preview](https://solid-carnival-1pg38np.pages.github.io/) of what you will +build: + + + +## Fork, clone and branch + +This tutorial has an accompanying GitHub repository called +[carbon-tutorial-web-components](https://github.com/carbon-design-system/carbon-tutorial-web-components) +that we'll use as a starting point for each step. + +### Fork + +To begin, fork +[carbon-tutorial-web-components](https://github.com/carbon-design-system/carbon-tutorial-web-components) +using your GitHub account. Please note when forking you must untick “Copy the +main branch only” so you can access all branches / steps of the tutorial. + +### Clone + +Go to your forked repository, copy the SSH or HTTPS URL and in your terminal run +the two commands to get the repository in your local file system and enter that +directory. + +```bash +git clone [your fork SSH/HTTPS] +cd carbon-tutorial-web-components +``` + +### Add upstream remote + +Add a remote called `upstream` so we can eventually submit a pull request once +you have completed this tutorial step. There are two choices: SSH or HTTPS + +#### SSH + +```bash +git remote add upstream git@github.com:carbon-design-system/carbon-tutorial-web-components.git +``` + +#### HTTPS + +```bash +git remote add upstream https://github.com/carbon-design-system/carbon-tutorial-web-components.git +``` + +Verify that your forked repository remotes are correct: + +```bash +git remote -v +``` + +Your terminal should output something like this: + +```bash +origin [your forked repo] (fetch) +origin [your forked repo] (push) +upstream git@github.com:carbon-design-system/carbon-tutorial-web-components.git (fetch) +upstream git@github.com:carbon-design-system/carbon-tutorial-web-components.git (push) +``` + +### Branch + +Now that we have our repository set up, let's check out the branch for this +tutorial step's starting point. Run the two commands: + +```bash +git fetch upstream +git checkout -b step-1 upstream/step-1 +``` + +## Build and start + +We have the repository forked to your GitHub account, cloned down to your +machine, and the starting branch checked out. Next, install the app's +dependencies (Vite) with: + +```bash +pnpm i +``` + +After the dependencies are installed, you can start the app with: + +```bash +pnpm dev +``` + +Open Your default browser should open up with an empty page that says: +`Hello Carbon! Well, not quite yet. This is the starting point for the Carbon Web Components tutorial.` + +## Install Carbon + +Even though we installed existing dependencies, we've yet to install our any +Carbon packages. + +Stop your development server with `CTRL-C` and install Carbon dependencies with: + +```bash +pnpm add @carbon/web-components @carbon/styles +``` + +## Install Sass + +We need to run a Sass build as the Carbon styles are authored in Sass, so run +the following command to install `sass` as a dependency. + +```bash +pnpm add sass +``` + +Before restarting our app rename `style.css` to `style.scss` and change the +inport in `main.js` from `import './style.css';` to `import './style.scss';`. + +Then, start the app again. If your app's currently running, you'll need to +restart it for the new packages to be used. + +```bash +pnpm dev +``` + +The app looks as it did before. We, need to import Carbon styles have an impact. + +### Import Carbon styles + +Replace the contents of `style.scss` with + +```scss path=style.scss +@use '@carbon/styles/scss/reset'; +``` + +This has reset the styles to a common base from which Carbon applications are +built. What you see, if you run the application, is a largely unstyled page. It +is however making use of the IBM Plex font, standard in all Carbon applications. + +## A working Carbon button + +### Import the button + +Next, we'll import a `Button` from Carbon to test that our dependencies are +working properly. At the top of `main.js`, import the `Button` and delete +everything else, leaving just: + +```javascript path=main.js +import './style.scss'; +import '@carbon/web-components/es/components/button/button.js'; +``` + +### Tidy up our HTML file + +In `index.html` first move this script tag up inside the `head` tag, so we don't +accidentally delete it later. It's location does not matter in this tutorial. + +```html path=index.html + +``` + +Then replace the Vite logo + +```html path=index.html + +``` + +with the Carbon one: + +```html path=index.html + +``` + +Update the title `Vite App` to `Carbon tutorial web components`. + +### Add the button by replacing: + +```html path=index.html +
+``` + +with: + +```html path=index.html +Click more than once +``` + +Congratulations, you've imported your first component! You should see a Carbon +styled button on the page. + +### Make the button do something + +Before giving the button something to do, add the following to `style.scss` +which will allow our button to theme the app. + +```scss path=style.scss +@use '@carbon/styles/scss/theme' as *; +@use '@carbon/styles/scss/themes'; + +:root { + @include theme(themes.$g10); + + @media (prefers-color-scheme: dark) { + @include theme(themes.$g100); + } +} + +.g10 { + @include theme(themes.$g10); +} + +.g100 { + @include theme(themes.$g100); +} +``` + +Then in `main.js` add the following code to handle the button clicks. + +```javascript path=main.js +const bodyEl = document.querySelector('body'); + +// button click handler +const handleClick = () => { + bodyEl.classList.toggle('g10'); + bodyEl.classList.toggle('g100'); +}; +document.querySelector('.button').addEventListener('click', handleClick); + +// set initial theme based on preferences +if (matchMedia('prefers-color-scheme').matches) { + bodyEl.classList.add('g100'); +} else { + bodyEl.classList.add('g10'); +} +``` + +After these changes clicking the button will toggle theme classes, which in the +app changes the background color. + +## Add UI Shell + +Now we're going to add the UI shell. + +### UI Shell for the landing page + +First import the header into `main.js` + +```javascript path=main.js +import '@carbon/web-components/es/components/ui-shell/index'; +``` + + + +**Note:** you can find a description of the different components used in the UI +Shell in our +[Storybook](https://web-components.carbondesignsystem.com/?path=/docs/components-ui-shell--overview) +package. + + + +Before we add the header to `index.html` add the class `app` to the body tag. + +```html path=index.html + +``` + +Wrap the button as follows. + +```html path=index.html +
+ Click more than once +
+``` + +Then above `
` add our header. + +```html path=index.html +
+ + Carbon Tutorial + +
+``` + +Running the application at this point it looks like the button has dissapeared. +Add the following to `style.scss`. + +```path=style.scss +@use '@carbon/styles/scss/spacing' as *; /* near top of file */ + +.app { + display: grid; + grid-template-rows: $spacing-09 1fr; + height: 100vh; + overflow: hidden; +} + +.main { + height: 100%; + overflow-y: auto; +} +``` + +This imports the Carbon `spacing` and establishes a grid to contain our header +and main. The overflow settings are there to ensure it is our `
` that +scrolls if needed. + +### Adding the repositories page + +After the `` closing tag add a link to a new page. + +```html path=index.html + + Repositories + +``` + +Duplicate `index.html` and name it `repositories.html`. + +In this new file replace the contents of the `main` tag with the words +`REPOSITORIES PAGE`. + +You can now switch between the two pages by clicking on `Repositories` and +`IBM Carbon Tutorial` in the header. This will look a little glitchy, this is +because it is a genuine page navigation, and the CSS is still being processed. +When using Web Components inside libraries such as Lit, React, Angular, Vue etc +this is resolved by taking control of the routing. We will not investigate +further here. + +### Behaving responsively + +Switch back to the landing page by clicking on `IBM Carbon Tutorial`. + +Checking responsive behavior (window narrower than 1080px) you will notice the +repositories page disappear from the menu. This goes into a sidebar controlled +by a hamburger menu as follows. + +Before the `` tag add + +```html path=index.html + +``` + +Then after the closing `` add + +```html path=index.html + + + + Repositories + + + +``` + +### Global actions + +As part of the Carbon header we can also add global actions this involves making +use of some Carbon Icons so first we will add that dependency. + +```bash +pnpm add @carbon/icons +``` + +There are various ways to add SVGs to our page. Often the SVG is copied directly +into source HTML or a bundler is used to load it. Rather than rely on a bundler +or add them inline, which can make our HTML harder to read, we will use CSS to +add refer directly to the icon files (which have been conveniently copied into +the `./public` folder) from the `@carbon/icons` pacakge. + +```scss path=style.scss +.action-icon { + width: 1.25rem; + height: 1.25rem; + background-color: $text-primary; +} + +.notification .action-icon { + mask: url('/notification.svg') no-repeat center; +} + +.user-avatar .action-icon { + mask: url('/user--avatar.svg') no-repeat center; +} + +.app-switcher .action-icon { + mask: url('/switcher.svg') no-repeat center; +} +``` + +The above CSS allows us to simply add the two classes associated with each icon +to display it in a themable way in our application. + +Next we need to add the global actions and related panels to the `index.html` +file after the closing ``. The Carbon icons are applied to the +slotted icon element using CSS. + +```html path=index.html +
+ +
+
+ +
+
+ +
+
+
+``` + + + +**Note:** this is the first time we have seen the `slot` attribute. Standard +HTML elements often have the ability to host child content. Web components are +more flexible allowing both the default child and named child areas, it uses the +term `slot` to refer to these. The slot attribute is used to target named slots, +in this case `icon`. + +Further details on slots can be found on the +[mdn docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot) +package. + + + +The global action buttons are passed a `panel-id` used to identify the panels +they toggle the visibility of. Just below the last global action add the +follwoing HTML. + +```html path=index.html +Notification Panel +User profile Panel +App switcher Panel +``` + +Note as web components behave like native components we can add event handlers +and interact with them directly. + +The default panel behavior simply toggles the panel when clicked. This can +result in multiple panels being open at once. Adding the following to `main.js` +changes this behavior by listening for clicks and closing the other panels. + +```javascript path=main.js +const handleGlobalActionClick = (ev) => { + const targetPanelId = ev.currentTarget.getAttribute('panel-id'); + const panels = document.querySelectorAll('cds-header-panel'); + // check to see if other panels are open and close them + panels.forEach((panel) => { + if (panel.id !== targetPanelId) { + panel.expanded = false; + } + }); +}; +const globalActions = document.querySelectorAll('cds-header-global-action'); +[...globalActions].forEach((action) => + action.addEventListener('click', handleGlobalActionClick) +); +``` + +### A better them switcher + +The current theme switcher is not very practical, here we will move it inside +the profile panel. + +First replace the button in `index.html` with `LANDING PAGE` leaving the main +tag looking like this. + +```html path=index.html +
LANDING PAGE
+``` + +In `main.js` remove this code handling the button click and initial load. + +```javascript path=main.js +// button click handler +const handleClick = () => { + bodyEl.classList.toggle('g10'); + bodyEl.classList.toggle('g100'); +}; +document.querySelector('.button').addEventListener('click', handleClick); + +// set initial theme based on preferences +if (matchMedia('(prefers-color-scheme: dark)')) { + bodyEl.classList.add('g100'); +} else { + bodyEl.classList.add('g10'); +} +``` + +Still in `main.js` add imports for checkbox and content-switcher. + +```javascript path=main.js +import '@carbon/web-components/es/components/checkbox/index'; +import '@carbon/web-components/es/components/content-switcher/index'; +``` + +Locate the `cds-header-panel` with the id="user-profile-panel" in `index.html` +and replace it's content with the following. + +```html path=index.html +
+

User profile Panel

+ + + +
+ Light theme +
+ +
+ System theme +
+ +
+ Dark theme +
+
+ + Global header reverse theme +
+``` + +This adds a content switcher and checkbox to our user profile side panel. If you +view it now it works but is in need of some styling. + +These styles import the Carbon typography features, define a layout for our +panel, and set the title size. + +```scss path=style.scss +@use '@carbon/styles/scss/type' as *; /* place at top of file */ + +.header-panel__content { + display: flex; + flex-direction: column; + gap: $spacing-05; + padding: $spacing-05; +} + +.header-panel__title { + @include type-style('productive-heading-02'); +} +``` + +As per the global actions we will use additional styling to add icons to our +content switcher. + +```scss path=style.scss +.theme-selector__icon { + width: 1.25rem; + height: 1.25rem; + background-color: $text-primary; +} + +cds-content-switcher-item[selected] .theme-selector__icon { + /* switch icon color when selected */ + background-color: $background; +} + +.theme-selector__icon--light { + mask: url('/sun.svg') no-repeat center; +} + +.theme-selector__icon--system { + mask: url('/brightness-contrast.svg') no-repeat center; +} + +.theme-selector__icon--dark { + mask: url('/moon.svg') no-repeat center; +} +``` + +Currently the theme switcher looks good but needs the following Javascript to +switch themes. + +```javascript path=main.js +const handleSwitch = (ev) => { + // Applies new theme or defers to system preferences by removing theme + switch (ev.detail.item.value) { + case 'light': + bodyEl.classList.remove('g100'); + bodyEl.classList.add('g10'); + break; + case 'dark': + bodyEl.classList.remove('g10'); + bodyEl.classList.add('g100'); + break; + default: + bodyEl.classList.remove('g10'); + bodyEl.classList.remove('g100'); + } +}; +document + .querySelector('.theme-selector') + .addEventListener('cds-content-switcher-selected', handleSwitch); + +const handleHeaderCompliment = (ev) => { + document + .querySelector('cds-header') + .classList.toggle('compliment', ev.target.checked); +}; +document + .querySelector('.theme-header__compliment') + .addEventListener('cds-checkbox-changed', handleHeaderCompliment); +``` + +At this point our theme switcher is mostly working, only the checkbox +`Global header reverse theme` appears to do nothing. In the script above the +class `compliment` is being toggled on and off in the `
` tag. + +A little more CSS is needed to make this functionality work. Add the following +in addition to the existing theme classes. + +```scss path=style.scss +:root .compliment { + @include theme(themes.$g100); + + @media (prefers-color-scheme: dark) { + @include theme(themes.$g10); + } +} + +.g10 .compliment { + @include theme(themes.$g100); +} + +.g100 .compliment { + @include theme(themes.$g10); +} +``` + +Then replace the cds-header tags `g100` class in `index.html` with `compliment`. + +```html path=index.html + . . . +``` + +### Skip to content + +When creating navigation headers, it's important to have a `Skip to content` +link so keyboard users can skip the navigation items and go straight to the main +content. + +Import th ecomponent in `main.js` + +```javascript path=main.js +import '@carbon/web-components/es/components/skip-to-content/index.js'; +``` + +Add in to our header in `index.html` as the first child of our `cds-header` +component. + +```html path=index.html + + + + +``` + +Then update the main tag to include the id `main-content` + +```html path=index.html +
+ +
+``` + +### Update the repositories page + +One final task before completing step 1. Our repositories page has missed out on +all of the HTML updates we have been making to the landing page. Simply copy the +contents of `index.html` to `repositories.html` and replace `LANDING` with +`REPOSITORIES`. + +NOTE: We could do something better than duplicating our pages. This could be +pure Javascript, HTML templates or native Web Components. However, that might +distract from the message that no library is required. + +## Push to GitHub + +That is it you are done. Just one more push to save your completion of step 1. + +### Git commit and push + +First, stage and commit all of your changes: + +```bash +git add --all && git commit -m "feat(tutorial): complete step 1" +``` + +Then, push to your repository: + +```bash +git push -u origin step-1 +``` + + + +**Note:** If your Git remote protocol is HTTPS instead of SSH, you may be +prompted to authenticate with GitHub when you push changes. If your GitHub +account has two-factor authentication enabled, we recommend that you follow +these instructions to +[create a personal access token for the command line](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line). +That lets you use your token instead of password when performing Git operations +over HTTPS. + + diff --git a/src/pages/developing/web-components-tutorial/step-2.mdx b/src/pages/developing/web-components-tutorial/step-2.mdx new file mode 100644 index 00000000000..3d426dc5e6a --- /dev/null +++ b/src/pages/developing/web-components-tutorial/step-2.mdx @@ -0,0 +1,665 @@ +--- +title: 2. Building pages +description: + Welcome to Carbon! This tutorial will guide you in creating a Vanilla JS/HTML + app using Web Components from the Carbon Design System. +tabs: ['Overview', 'Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5'] +--- + +import Preview from 'components/Preview'; + + + +Now that we have our 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. + + + + + +Fork, clone and branch +Add landing page grid +Build landing page +Style landing page +Add repo page grid +Build repo page +Push to GitHub + + + +## Preview + +A +[preview](https://carbon-tutorial-nextjs-git-step-3-carbon-design-system.vercel.app/) +of what you'll build: + + + +## Fork, clone and branch + +This tutorial has an accompanying GitHub repository called +[carbon-tutorial-web-components](https://github.com/carbon-design-system/carbon-tutorial-web-components) +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]([previous step](/developing/web-components-tutorial/step-1#fork-clone-and-branch). + +### Branch + +With your repository all set up, let's check out the branch for this tutorial +step's starting point. + +```bash +git fetch upstream +git checkout -b step-2 upstream/step-2 +``` + + + +**Note:** This builds on top of step 1, but be sure to check out the upstream +step 2 branch because it includes the static assets required to get through this +step. + + + +### 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): + +```bash +pnpm i +``` + +Then, start the app: + +```bash +pnpm dev +``` + +You should see something similar to where the +[previous step](/developing/web-components-tutorial/step-1#fork-clone-and-branch/step-1) +left off. + +## Add landing page grid + +Let's add our grid elements to our `LandingPage` page component. + +Now the Carbon grid does not currently exist in web component form. This may be +because it is difficult to apply a CSS grid when traversing the shadow DOM of a +grid and column component. This leaves us with writing our grid and columns out +long hand, writing the styles ourselves, or using javascript utility to add +classes to our columns. + +For the purposes of this tutorial the classes are written out long hand. In + +In `index.html` replace `LANDING PAGE` with + +```html path=index.html +
+
+ 1 +
+
+
+
+ 7/16 +
+
+ 8/16 +
+
+
+
+
+
+ 1/4 +
+
+ 1/4 +
+
+ 1/4 +
+
+ 1/4 +
+
+
+
+``` + + + +**Grid education** There are many options for the Carbon Grid. For more +information review +[2x Grid](https://carbondesignsystem.com/elements/2x-grid/overview/) on the +Carbon Design System website. If apply classes manually it is also well worth +checking out the +[CSS Grid Demo](https://carbon-elements.netlify.app/grid/examples/css-grid/) to + + + +Then import the grid styles in `style.scss`. + +```scss path=style.scss +@use '@carbon/styles/scss/grid'; +``` + +In order to use the grid, we need to wrap everything in a `cds--css-grid`. +Because we're building with the new CSS Grid, we won't be using typical rows. We +have used a combination of `cds--css-grid-column` classes to create our layout. + +The CSS Grid is a 16 column grid. The column widths and positions are specified +using a combination of `cds--BP:col-span-N` and `cds--BP:col-start-N`classes. +Where BP is the Carbon breakpoint and N is column count or starting position. +For example, `
` means the +column will start at column 9 and span 8 columns when the large (lg) breakpoint +is active. large breakpoint. + +https://carbon-elements.netlify.app/grid/examples/css-grid/ + +We've included the designs for this tutorial app in the `design.figma` file +found as a top-level file in the `carbon-tutorial` repository. But, if you don't +have Figma installed and available to inspect the design, we'll provide +screenshots. + +![Landing page grid](../shared/step-2/images/landing-layout.png) + +Landing page grid + + + +**Pro tip:** `SHIFT-G` toggles the layout in Figma. + + + +## Build landing page + +We'll start adding HTML elements and components by row. + +### First row + + + + +![Banner vertical spacing](../shared/step-2/images/landing-r1-spacing.png) + +Banner vertical spacing + + + + +In our first row we'll need a `Breadcrumb` component. First, let's import the +components we need. + +```javascript path=main.js +import '@carbon/web-components/es/components/breadcrumb/index'; +``` + +We can now add our component to the first row, replace the content of the `div` +with class `page-landing__banner` with: + +```html path=index.html + + + Getting started + + +

Design & build with Carbon

+``` + +### Second row + +In our second row we'll need `Tabs` and `Button` components. Add the following +import: + +```javascript path=main.js +import '@carbon/web-components/es/components/tabs/index'; +``` + +The tabs come next going inside `page-landing__r2` and before the sub +`cds--subgrid`. + +```html path=index.html + + About + Design + Develop + +``` + +Each of the `cds-tab` components has a `target` attribute. This is used to +identify the content visible when that tab is selected. + +Wrap the subgrid element immediately after the closing `` with the +following. This is where we will place our first tab panel. + +```html path=index.html +
+ ... grid element is here +
+``` + +Replace the content of the first column `7/16` with: + +```html path=index.html +

What is Carbon?

+

+ Carbon is IBM’s open-source design system for digital products and + experiences. With the IBM Design Language as its foundation, the system + consists of working code, design tools and resources, human interface + guidelines, and a vibrant community of contributors. +

+Learn more +``` + +The second column content `8/16` is replaced with: + +```html path=index.html +Carbon illustration +``` + +The `tab-illo.png` image is already located in the `public` folder. + +After the closing `
` of `id="panel-about"`, still inside +`page-landing__r2` we add two further tab panels. This one + +```html path=index.html +
+
+
+
+

+ Rapidly build beautiful and accessible experiences. The Carbon kit + contains all resources you need to get started. +

+
+
+
+
+``` + +and + +```html path=index.html +
+
+
+
+

+ Carbon provides components and styles for all. Whether using Vanilla, + Web Components, React, or another reactive library, you can build with + Carbon. +

+
+
+
+
+``` + +### Third row + +Here we replace all four columns entirely adding some offsets for medium and +large column sizes after the first column. + +```html path=index.html +
+ The principles +
+
+ Carbon is open +
+
+ Carbon is modular +
+
+ Carbon is consistent +
+``` + +## Style landing page + +### First row + +Row one styling is fairly straight forward with some typography and positional +adjustment so to align it with our other content. + +```scss path=style.scss +.page { + padding: 0; + + > * { + padding-inline: $spacing-06; + margin: 0; + } +} + +.page-landing__banner { + padding-block: $spacing-05 $spacing-13; + background: $layer-01; + box-shadow: $spacing-06 0 0 $layer-01, -1 * $spacing-06 0 0 $layer-01; +} + +.page-landing__heading { + @include type-style('productive-heading-05'); + + margin: 0; +} +``` + +### Second row + +The styling for the second row adds further layout and typography changes. It +also positions the image and prevents it from causing horizontal overflow. In +order to make use of the Carbon SCSS mixin `breakpoint-down` we also add the +breakpoint import to our list of `@use` near the top of the file. + +```scss path=style.scss +@use '@carbon/styles/scss/breakpoint' as *; /* add near top of file */ + +.page-landing__illo { + max-width: 100%; + float: inline-end; + height: auto; +} + +@include breakpoint-down(md) { + .page-landing__illo { + max-width: 528px; + width: 100%; + height: 100%; + float: inline-start; + } +} + +.page-landing__tabs { + margin: (-1 * $spacing-08) 0 $spacing-08; +} + +.page-landing__tab-content { + padding-block: $spacing-06; +} + +.page-landing__subheading { + @include type-style('productive-heading-03'); + + font-weight: 600; +} + +.page-landing__p { + @include type-style('productive-heading-03'); + margin-top: $spacing-06; + margin-bottom: $spacing-08; +} +``` + +### Third row + +```scss path=style.scss +.page-landing__r3 { + padding-block: $spacing-09; + background: $layer-01; +} +``` + +Ta-da! You should see a step 2 complete landing page! Now we can move on to the +repo page. + +## Build repo page + +### Add a grid to contain our content + +Now in our `repositories` page we will first add a grid wrapping +`REPOSITORIES PAGE` + +```html path=repositories.html +
+
+ REPOSITORIES PAGE +
+
+``` + +Add a minimal amount of styling to move our content away from the edge of the +page in `style.scss`. + +```scss style.scss +.repo-page__r1 { + padding-block: $spacing-05; +} +``` + +### Adding a table + +Before we can add the table we need to import the web component. As this is only +used in our `repositories.html` page lets create a new script file `repos.js` +and then add. + +```javascript path=repos.js +import '@carbon/web-components/es/components/data-table/index.js'; +``` + +We need to include this file in `repositories.html` which we can do by adding +the following next to the script tag that includes `main.js`. + +```html path=repositories.html + +``` + +Next we add the table header and column titles to replace the text +`REPOSITORIES PAGE` + +```html path=repositories.html + + Carbon Repositories + A collection of public Carbon repositories. + + + Name + Created + Updated + Open issues + Stars + Links + + + Table body goes here + +``` + +The table header should already be visible on the repositories page. + +Now we can add the rows replacing `Table body goes here` with: + +```html path=repositories.html + + Repo 1 + Date + Date + 123 + 456 + Links + +Repo description + + Repo 2 + Date + Date + 123 + 456 + Links + +Repo description + + Repo 3 + Date + Date + 123 + 456 + Links + +Repo description +``` + +### Using HTML templates + +With the app running we can see that the repositories page now hosts a table. +However, it is not realistic to populate a table with hard coded data way so +we'll refactor to build the table from data. + +This involves the use of HTML Templates, take a look at +[W3 Schools](https://www.w3schools.com/tags/tag_template.asp) if you need a +quick refresh. + +In `index.html` remove the contents of the `` tag and return it +to `Table body goes here`. + +Then before the end of the html tag and after the body closing tag add the +following to define our table row template. + +```html path=repositories.html + +``` + +Next in `repos.js` add the following data that we will use to populate the table +rows. + +```javascript path=repos.js +// cds-table-row data +let data = [ + { + name: 'Repo A', + created: 'Date', + updated: 'Date', + openIssues: 123, + stars: 456, + links: 'Links', + expansion: 'Row description', + }, + { + name: 'Repo B', + created: 'Date', + updated: 'Date', + openIssues: 123, + stars: 456, + links: 'Links', + expansion: 'Row description', + }, + { + name: 'Repo C', + created: 'Date', + updated: 'Date', + openIssues: 123, + stars: 456, + links: 'Links', + expansion: 'Row description', + }, +]; +``` + +Next we create the function `updateTable` and make a call to it to populate add +our rows. + +```javascript path=repos.js +const updateTable = () => { + const tableRowTemplate = document.querySelector( + 'template#template--table-row' + ); + const tableBody = document.querySelector('cds-table-body'); + if (tableBody && tableRowTemplate) { + tableBody.innerHTML = ''; + // iterate over data and render rows + data.forEach((row) => { + let newRow = tableRowTemplate.content.cloneNode(true); + const keys = Object.keys(row); + keys.forEach((key) => { + const keyEl = newRow.querySelector(`[key="${key}"]`); + keyEl.innerHTML = row[key]; + }); + tableBody.appendChild(newRow); + }); + } +}; + +updateTable(); +``` + +Verify that the table rows are being generated by changing the data and running +the app. + +Congratulations! We've now created our static repo page! + +## Push to GitHub + +That is it you are done. Just one more push to save your completion of step 2. + +### Git commit and push + +First, stage and commit all of your changes: + +```bash +git add --all && git commit -m "feat(tutorial): complete step 2" +``` + +Then, push to your repository: + +```bash +git push -u origin step-2 +``` + + + +**Note:** If your Git remote protocol is HTTPS instead of SSH, you may be +prompted to authenticate with GitHub when you push changes. If your GitHub +account has two-factor authentication enabled, we recommend that you follow +these instructions to +[create a personal access token for the command line](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line). +That lets you use your token instead of password when performing Git operations +over HTTPS. + + diff --git a/src/pages/developing/web-components-tutorial/step-3.mdx b/src/pages/developing/web-components-tutorial/step-3.mdx new file mode 100644 index 00000000000..791db255ab6 --- /dev/null +++ b/src/pages/developing/web-components-tutorial/step-3.mdx @@ -0,0 +1,477 @@ +--- +title: 3. Using APIs +description: + Welcome to Carbon! This tutorial will guide you in creating a Vanilla JS/HTML + app using Web Components from the Carbon Design System. +tabs: ['Overview', 'Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5'] +--- + +import Preview from 'components/Preview'; + + + +This step takes our static components and populates them with data from the +GitHub GraphQL API – loading states and all. We'll be displaying Carbon +repository information in a data table. + + + + + +Fork, clone and branch +Install dependencies +Fetch and render data +Ppagination +Push to GitHub + + + +## Preview + +The [GitHub REST API](https://docs.github.com/en/rest?apiVersion=2022-11-28) is +very well documented, we'll use it to fetch Carbon-related data for this Carbon +tutorial. + +To do so, we'll be using +[Octokit Core](https://github.com/octokit/core.js/#readme), a client that makes +it easy to interact with GitHub's APIs. + +A +[preview](https://carbon-tutorial-nextjs-git-step-4-carbon-design-system.vercel.app/) +of what you will build (see repositories page): + + + +## Fork, clone and branch + +This tutorial has an accompanying GitHub repository called +[carbon-tutorial-web-components](https://github.com/carbon-design-system/carbon-tutorial-web-components) +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]([previous step](/developing/web-components-tutorial/step-1#fork-clone-and-branch). + +### Branch + +With your repository all set up, let's check out the branch for this tutorial +step's starting point. + +```bash +git fetch upstream +git checkout -b step-3 upstream/step-3 +``` + +### Build and start app + +Install the app's dependencies and build the app: + +```bash +pnpm i +``` + +Then, start the app: + +```bash +pnpm dev +``` + +You should see something similar to where the +[previous step](/developing/web-components-tutorial/step-1#fork-clone-and-branch/step-2) +left off. Stop your app with `CTRL-C` and let's get everything installed. + +## Install dependencies + +We'll need to install `@octokit/core`, a package that allows us to query GitHub +APIs easily. Stop your development server with `CTRL-C` and install the octokit +dependency with: + +```bash +pnpm add @octokit/core +``` + +Then, start the app again. If your app's currently running, you'll need to +restart it. + +```bash +pnpm dev +``` + +## Fetch and render data + +### Imports + +Add the following import Octokit into `repos.js` and create a new instance of +Octokit. + +```javascript path=repos.js +import { Octokit } from '@octokit/core'; + +const octokitClient = new Octokit({}); +``` + +### API Request + +Next, we'll assemble our GitHub API request to fetch a list of repositories that +belong to the `carbon-design-system` GitHub organization. + +First empty the data array in `repos.js` + +```javascript path=repos.js +let data = []; +``` + +Then add the function `fetchData` calling it immediately afterwards. + +```javascript path=repos.js +const fetchData = async () => { + const res = await octokitClient.request('GET /orgs/{org}/repos', { + org: 'carbon-design-system', + per_page: 75, + sort: 'updated', + direction: 'desc', + }); + + if (res.status === 200) { + data = res.data.map((row) => ({ + name: row.name, + created: new Date(row.created_at).toLocaleDateString(), + updated: new Date(row.updated_at).toLocaleDateString(), + openIssues: row.open_issues_count, + stars: row.stargazers_count, + links: 'link', + expansion: row.description, + })); + + // replace table here + // replaceSkeleton(); + } else { + console.log('Error obtaining repository data'); + } +}; +fetchData(); +``` + +### Rendering the data + +If you have the application running then the only change you see is an empty +table. Let's fix that next. + +In `repositories.html` just above the `` add a table skeleton. + +```html path=repositories.html + +``` + +Then move the `` into a template called `template--table` at the +bottom of the file. + +```html path=repositories.html + +``` + +With the application running the repositories page now shows the skeleton table. +Skeleton components are used in the Carbon Design System to information is still +being loaded. For further details on +[Carbon loading patterns](https://carbondesignsystem.com/patterns/loading-pattern/). + +Returning to `repos.js` we will makes use of the fetched data to replace the +skeleton table. Find the current call to `updateTable` + +```javascript path=repos.js +updateTable(); +``` + +and replace it with the new function called `replaceSkeleton` below: + +```javascript path=repos.js +const replaceSkeleton = () => { + const tableSkeleton = document.querySelector('cds-table-skeleton'); + const tableTemplate = document.querySelector('template#template--table'); + + if (tableSkeleton && tableTemplate) { + tableSkeleton.replaceWith(tableTemplate.content.cloneNode(true)); + // update table rows + updateTable(); + } +}; +``` + +This function locates the `template--table` and replaces the skeleton with it. +It then makes a call to `updateTable` to add the rows. + +We are now ready to display the data by adjusting the function `fetchData` by +ucommenting the call to `replaceSkeleton`. + +```javascript path=repos.js +// replace table here +// replaceSkeleton(); +``` + +to leave: + +```javascript path=repos.js +// replace table here +replaceSkeleton(); +``` + +At this point when you refresh the `repositories` page the table skeleton is +briefly shown before the table is populated with data from github. The link +column however just shows `link` we will fix that next. + +At the top of `repos.js` import the `cds-link` component. + +```javascript path=repos.js +import '@carbon/web-components/es/components/link/index'; +``` + +Find `links: 'link'` in the `fetchData` function and replace it with: + +```javascrpt path=repos.js +links: { url: row.html_url, homepage: row.homepage }, +``` + +In our `updateTable` function we need to do something different for the links +key. Replace + +```javascript path=repos.js +keyEl.innerHTML = row[key]; +``` + +with + +```javascript path=repos.js +if (key === 'links') { + keyEl.innerHTML = ``; +} else { + keyEl.innerHTML = row[key]; +} +``` + +Now it we could have added the HTML for the links in `repositories.html` but +this serves to demonstrate that as with standard HTML tags it is possible to +simply insert Carbon Web Components as innerHTML using a string. Just a little +bit of CSS is needed to present this as per our tutorial design. + +Open `styles.scss` and add the following. + +```scss path=style.scss +.link-list { + display: flex; + list-style: none; + padding: 0; +} + +.link-list li:not(:last-child) { + padding-inline-end: $spacing-02; + + &::after { + content: '|'; + display: inline; + } +} + +.link-list li:not(:first-child) { + padding-left: $spacing-02; +} +``` + +## Pagination + +The data rendered in our table produces quite a tall page which grows with each +new Carbon repository. To complete our repositories page we will add pagination +to the table. + +In `repos.js` import the pagination component. + +```javascript path=repos.js +import '@carbon/web-components/es/components/pagination/index'; +``` + +Now, as part of the `template--table` template we can add the pagination to +`repositories.html` after the closing `` tag. + +```html path=repositories.html + + 10 + 20 + 30 + 40 + 50 + +``` + + + +**Note:** The `Pagination` component isn't inherently connected in any way to +the `DataTable` - we need to tell it what to do when a change occurs using the +`onChange` prop. This includes both page size changes and displaying different +rows. + + + + + +**Note:** Like the other Carbon Web Components components, `Pagination` +component examples can be found in +[Storybook](https://web-components.carbondesignsystem.com/?path=/story/components-pagination--overview) +by browsing the story and knobs. + + + +If you scroll to the bottom of the `repositories` page in the browser you should +see the pagination component rendered. + +Back in `repos.js` next to the declaration of our data array add two further +variables to work with the pagination component. Where we declare the data +variable, add variables for page size and row index. + +```javascript path=repos.js +let data = []; +let pageSize = 10; +let firstRowIndex = 0; +``` + +Next we need to add some script to handle events raised by the pagination +component and update it with the values defined for `pageSize` and +`firstRowIndex`. + +```javascript path=repos.js +const handlePageChangeCurrent = ({ detail }) => { + firstRowIndex = (detail.page - 1) * detail.pageSize; + updateTable(); +}; + +const handlePageSizeChange = ({ detail }) => { + pageSize = detail.pageSize; + updateTable(); +}; + +const updatePagination = () => { + // update pagination to match data fetched + const paginationEl = document.querySelector('cds-pagination'); + paginationEl.setAttribute('total-items', data.length); + + setTimeout(() => { + // defer until after the dom is updated + paginationEl.addEventListener( + 'cds-pagination-changed-current', + handlePageChangeCurrent + ); + paginationEl.addEventListener( + 'cds-page-sizes-select-changed', + handlePageSizeChange + ); + }, 10); +}; +``` + +Add a call to `updatePagination` in `replaceSkeleton` just after the call to +`updateTable` + +```javascript path=repos.js +// update table rows +updateTable(); + +// update pagination +updatePagination(); +``` + +When triggered the handlers update `firstRowIndex` and `pageSize` before calling +`updateTable` which re-renders our table rows. Before it all works we need to +make a change to `updateTable` to render just the rows on the current page. + +Currently, we iterate over the data as follows: + +```javascript path=repos.js +// iterate over data and render rows +data.forEach((row) => { + // rows update here +}); +``` + +Change this introducing a filter before the `forEach`. + +```javascript path=repos.js +// iterate over data and render rows +data + .filter((v, i) => i >= firstRowIndex && i < firstRowIndex + pageSize) + .forEach((row) => { + // rows update here + }); +``` + +Refreshing the repositories page should now show just ten rows. Try changing the +page size and the current page number, this should result in new data being +loaded. + +That does it! Your data table should fetch GitHub data on first render. You can +expand each row to see the repository's description. You can modify the +pagination items per page and cycle through pages or jump to a specific page of +repositories. + +## Push to GitHub + +That is it you are done. Just one more push to save your completion of step 3. + +### Git commit and push + +First, stage and commit all of your changes: + +```bash +git add --all && git commit -m "feat(tutorial): complete step 3" +``` + +Then, push to your repository: + +```bash +git push -u origin step-3 +``` + + + +**Note:** If your Git remote protocol is HTTPS instead of SSH, you may be +prompted to authenticate with GitHub when you push changes. If your GitHub +account has two-factor authentication enabled, we recommend that you follow +these instructions to +[create a personal access token for the command line](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line). +That lets you use your token instead of password when performing Git operations +over HTTPS. + + diff --git a/src/pages/developing/web-components-tutorial/step-4.mdx b/src/pages/developing/web-components-tutorial/step-4.mdx new file mode 100644 index 00000000000..d78b526e60e --- /dev/null +++ b/src/pages/developing/web-components-tutorial/step-4.mdx @@ -0,0 +1,365 @@ +--- +title: 4. Laanding the principles +description: + Welcome to Carbon! This tutorial will guide you in creating a Vanilla JS/HTML + app using Web Components from the Carbon Design System. +tabs: ['Overview', 'Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5'] +--- + +import Preview from 'components/Preview'; + + + +With two pages comprised mostly of Carbon components, let's revisit the landing +page and complete the principles section using Carbon pictograms and tokens. + + + + + +Fork, clone and branch +Review design +The info section +The info card +Add styling +Check accessibility +Push to GitHub + + + +## Preview + +Carbon provides a solid foundation for building web applications through its +color palette, layout, spacing, type, as well as common building blocks in the +form of components. So far, we've only used Carbon components to build out two +pages. + +Other tutorials at this point build a component using Carbon, as we are staying +valilla here, and we don't want to turn this into a Web Component tutorial, we +will use a HTML template and BEM class names to prevent our CSS affecting +others. + +A +[preview](https://carbon-tutorial-nextjs-git-step-5-carbon-design-system.vercel.app/) +of what you'll build (see bottom of page): + + + +## Fork, clone and branch + +This tutorial has an accompanying GitHub repository called +[carbon-tutorial-web-components](https://github.com/carbon-design-system/carbon-tutorial-web-components) +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]([previous step](/developing/web-components-tutorial/step-1#fork-clone-and-branch). + +### Branch + +With your repository all set up, let's check out the branch for this tutorial +step's starting point. + +```bash +git fetch upstream +git checkout -b step-4 upstream/step-4 +``` + + + +**Note:** This builds on top of step 3, but be sure to check out the upstream +step 4 branch because it includes the static assets required to get through this +step. + + + +### 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): + +```bash +pnpm i +``` + +Then, start the app: + +```bash +pnpm dev +``` + +You should see something similar to where the +[previous step](/developing/web-components-tutorial/step-1#fork-clone-and-branch/step-3) +left off. + +## Review design + +Here's what we're building – an informational section that has a heading and +three subheadings. Each subheading has accompanying copy and a pictogram. We'll +assume that this informational section is used elsewhere on the site, meaning +it's a great opportunity to build it as a reusable component. As for naming, +we'll call it an `InfoSection` with three `InfoCard`s as children. + +![Info section layout](../shared/step-4/images/info-layout.png) + +Info section layout + +## The info section + +Inside `page-landing__r3` in `index.html` we will make a couple of small +changes. Add the class `info-section` to the `cds--subgrid` + +```html path=index.html +
+``` + +and replace `The principles` and surrounding column with which changes the +column settings and provides a class for us to style the section heading. + +```html path=index.html +
+

The Principles

+
+``` + +Style the `info-section__heading` with: + +```scss path=style.scss +.info-section__heading { + @include type-style('heading-01'); + + padding-block-end: $spacing-08; +} +``` + +## The info card + +At the bottom of `index.html` between the closing body and html tags add the +info card template. + +### The template + +```html path=index.html + +``` + +Before we make use of the template we need to remove the column settings from +the remaining three elements `page-landing__title` and replace with `info-card +to leave: + +```html index.html +
Carbon is open
+
Carbon is modular
+
Carbon is consistent
+``` + +### The script + +As the script in this file only affects our landing page, first lets create +`landing.js`. Kicking things off by moving the breadcrumb and tabs import from +`main.js`. + +```javascript path=landing.js +import '@carbon/web-components/es/components/breadcrumb/index'; +import '@carbon/web-components/es/components/tabs/index'; +``` + +Then add a script tag to `index.html` to import `landing.js` + +```html path=index.html + +``` + +After making sure everything add, to `landing.js`, the script to create the info +card elements. First adding the data we will use to construct the info cards +from. + +```javascript path=landing.js +const infoCardDetails = [ + { + strongMsg: 'Open', + bodyMsg: `It's a distributed effort, guided by the principles of the open-source movement. Carbon's users are also it's makers, and everyone is encouraged to contribute.`, + pictogramName: 'advocate', + }, + { + strongMsg: 'Modular', + bodyMsg: `Carbon's modularity ensures maximum flexibility in execution. It's components are designed to work seamlessly with each other, in whichever combination suits the needs of the user.`, + pictogramName: 'accelerating-transformation', + }, + { + strongMsg: 'Consistent', + bodyMsg: `Based on the comprehensive IBM Design Language, every element and component of Carbon was designed from the ground up to work elegantly together to ensure consistent, cohesive user experiences.`, + pictogramName: 'globe', + }, +]; +``` + +Then create the utility function `updateInfoCard` + +```javascript path=landing.js +const updateInfoCard = (here, { strongMsg, bodyMsg, pictogramName }) => { + const infoCardTemplate = document.querySelector( + 'template#template--info-card' + ); + + if (here && infoCardTemplate) { + const newInfoCard = infoCardTemplate.content.cloneNode(true); + + const strongEl = newInfoCard.querySelector('.info-card__heading--strong'); + strongEl.innerHTML = strongMsg; + + const infoBodyEl = newInfoCard.querySelector('.info-card__body'); + infoBodyEl.innerHTML = bodyMsg; + + const pictogramEl = newInfoCard.querySelector('.info-card__pictogram'); + pictogramEl.classList.add(`info-card__pictogram--${pictogramName}`); + + here.innerHTML = ''; + here.replaceWith(newInfoCard); + } +}; +``` + +Followed by a query to find the info card elements and a loop to add our data. + +```javascript path=landing.js +const infoCards = document.querySelectorAll('.info-card'); +[...infoCards].forEach((infoCard, index) => { + updateInfoCard(infoCard, infoCardDetails[index]); +}); +``` + +### The styles + +![Info section spacing](../shared/step-4/images/info-spacing.png) + +Looking at the landing page you should see the updated information where +previously only the title was rendred. In order to complete our info card we +need to add some styling. + +First up let's make the picograms visible with the following. + +```scss path=style.scss +.info-card__pictogram { + width: $spacing-10; + height: $spacing-10; + background-color: $text-primary; +} + +.info-card__pictogram--accelerating-transformation { + mask: url('/accelerating-transformation.svg') no-repeat center; +} + +.info-card__pictogram--advocate { + mask: url('/advocate.svg') no-repeat center; +} + +.info-card__pictogram--globe { + mask: url('/globe.svg') no-repeat center; +} +``` + +The remaining styling is to make the info cards pleasing on the eye. Use of +Carbon breakpoints, which also control the number of grid columns, keeps our +page looking great even on the narrowest of devices. + +```scss path=style.scss +.info-card { + display: flex; + height: 300px; + flex-direction: column; + justify-content: space-between; + padding-inline: $spacing-05; + border-left: 1px solid $border-subtle; + + @include breakpoint-down(xlg) { + &:nth-of-type(2) { + border-left: none; + padding-left: 0; + } + } + + @include breakpoint-down(lg) { + flex-direction: row-reverse; + border-left: none; + padding-inline: 0; + gap: $spacing-07; + padding-top: $spacing-10; + height: initial; + + &:nth-of-type(2) { + padding-top: 0; + } + } +} + +.info-card__heading { + @include type-style('heading-03'); + + margin-top: 0; +} + +.info-card__body { + @include type-style('body-long-01'); + + margin-top: $spacing-06; +} +``` + +## Check accessibility + +We've added new markup and styles, so it's a good practice to check +[Equal Access Checker](https://www.ibm.com/able/toolkit/tools/) and make sure +our rendered markup is on the right track for accessibility. + +With the browser extension installed, Chrome in this example, open Dev Tools and +run Accessibility Assessment. + +## Push to GitHub + +That is it you are done. Just one more push to save your completion of step 4. + +### Git commit and push + +First, stage and commit all of your changes: + +```bash +git add --all && git commit -m "feat(tutorial): complete step 4" +``` + +Then, push to your repository: + +```bash +git push -u origin step-4 +``` + + + +**Note:** If your Git remote protocol is HTTPS instead of SSH, you may be +prompted to authenticate with GitHub when you push changes. If your GitHub +account has two-factor authentication enabled, we recommend that you follow +these instructions to +[create a personal access token for the command line](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line). +That lets you use your token instead of password when performing Git operations +over HTTPS. + + diff --git a/src/pages/developing/web-components-tutorial/step-5.mdx b/src/pages/developing/web-components-tutorial/step-5.mdx new file mode 100644 index 00000000000..23a7dca0650 --- /dev/null +++ b/src/pages/developing/web-components-tutorial/step-5.mdx @@ -0,0 +1,601 @@ +--- +title: Wrapping up +description: + Welcome to Carbon! This tutorial will guide you in creating a Vanilla JS/HTML + app using Web Components from the Carbon Design System. +tabs: ['Overview', 'Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5'] +--- + +import Preview from 'components/Preview'; + + + +This is an optional step that takes what we've built so far and optimizes the +app for a production environment. + + + + + +Fork, clone and branch +Linting +Deplying to gh-pages +Using the CDN +Push to GitHub +Step 5 complete branch + + + +## Preview + +A [preview](https://carbon-tutorial-nextjs.vercel.app/) of what you'll build +(visually no different, but built for production): + + + +## Fork, clone and branch + +This tutorial has an accompanying GitHub repository called +[carbon-tutorial-web-components](https://github.com/carbon-design-system/carbon-tutorial-web-components) +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](/developing/web-components-tutorial/step-1#fork-clone-and-branch). + +### Branch + +With your repository all set up, let's check out the branch for this tutorial +step's starting point. + +```bash +git fetch upstream +git checkout -b step-5 upstream/step-5 +``` + + + +**Note:** This builds on top of step 4, but be sure to check out the upstream +step 5 branch because it includes the static assets required to get through this +step. + + + +### 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): + +```bash +pnpm i +``` + +Then, start the app: + +```bash +pnpm dev +``` + +You should see something similar to where the +[previous step](/developing/web-components-tutorial/step-1#fork-clone-and-branch/step-4) +left off. + +## Linting + + + +**Note:** Linters are great Linters, spell checkers, code formatters are all +really useful tools to ensure code consistency and guard against potential +errors. They often integrate and run within your IDE and should form an +essential part of your development process. Early really is the best and +cheapest time to avoid issues. + + + +### Stylelint + +While the Carbon component styles are encapsulated in the components there we +have written some SCSS ourselves it would great if we could check we are +applying Carbon as expected. + +For more information on Stylelint [visit stylelint.io](https://stylelint.io/). + +Add Stylelint, a Carbon plugin as development dependencies. + +```bash +pnpm add -D stylelint stylelint-plugin-carbon-tokens +``` + +In `package.json` add the following script: + +```json path=package.json + "lint:style": "pnpm stylelint '**/*.scss'", +``` + +In the root folder create a file called `.stylelintrc.json` and add the +following settings. The +[recommended config](https://github.com/carbon-design-system/stylelint-plugin-carbon-tokens/blob/main/config/recommended.js) +for the plugin checks layout, motion, theme and type usage against expectations. +For more details take a look at the packages +[README.md](https://github.com/carbon-design-system/stylelint-plugin-carbon-tokens/blob/main/README.md) + +```json path=.stylelintrc.json +{ + "extends": ["stylelint-plugin-carbon-tokens/config/recommended"], + "plugins": ["stylelint-plugin-carbon-tokens"], + "reportNeedlessDisables": true, + "reportInvalidScopeDisables": true, + "reportDescriptionlessDisables": true +} +``` + +At the command line you can now run the script `lint:style`. This should +highlight just the one issue with the setting of `font-weight` to 600. + +```bash +$pnpm run lint:style + +> carbon-tutorial-web-components@0.0.0 lint:style /Users/leechase/Source/carbon/carbon-tutorial-web-components +> pnpm stylelint '**/*.scss' + +style.scss + 158:16 ✖ Expected carbon type token, mixin or function for carbon/type-use + "font-weight" found "600". + +✖ 1 problem (1 error, 0 warnings) + 1 error potentially fixable with the "--fix" option. +``` + +If you open the file you will see that `600` is marked with a wiggly underline. + +![Linter error highlight](./images/step-5/linter-error.png) + +Hovering over it you will see a popup explanation. + +![Linter error popup](./images/step-5/linter-error-details.png) + +Change `600` to `font-weight('semibold')` + +It is recommended that you use a more extensive rule set for stylelint. This can +help keep code consistent and auto fix many potential issues. + +If for instance you were to adopt the following configuration it would check for +a number of important issues such as accessiblity, right to left, and even +consistency patterns like nesting depty and CSS class selector pattern. + +Even better the small number of issues that it raises, regarding use of logical +properties, can be auto fixed bringing your CSS up to date. + +```json path=.stylelintrc.json +{ + "extends": [ + "stylelint-config-standard-scss", + "stylelint-plugin-carbon-tokens/config/recommended" + ], + "plugins": [ + "@double-great/stylelint-a11y", + "stylelint-use-logical-spec", + "stylelint-plugin-carbon-tokens" + ], + "reportNeedlessDisables": true, + "reportInvalidScopeDisables": true, + "reportDescriptionlessDisables": true, + "rules": { + "a11y/media-prefers-reduced-motion": true, + "a11y/no-outline-none": true, + "a11y/selector-pseudo-class-focus": true, + "max-nesting-depth": [ + 3, + { + "ignoreAtRules": ["if", "else", "each", "include", "mixin"] + } + ], + "liberty/use-logical-spec": true, + "selector-class-pattern": [ + "^[a-z]([-]?[a-z0-9]+)*(__[a-z0-9]([-]?[a-z0-9]+)*)?(--[a-z0-9]([-]?[a-z0-9]+)*)?$", + { + "message": "should match Harry Roberts'' style BEM block[__element][--modifier]" + } + ] + } +} +``` + +Don't forget to add these packages if you want to try out this config. + +```bash +pnpm add -D @double-great/stylelint-a11y stylelint-config-standard-scss stylelint-use-logical stylelint-use-logical-spec +``` + +Running `pnpm run lint:style --fix` will fix the issues identified. + +### ESLint & Prettier + +In addition to linting styles, it is recommended that you also check your script +for dev time errors and consitency. + +For more information on ESLint [visit eslint.org](https://eslint.org/). + +For more information on Prettier [visit prettier.io](https://prettier.io/). + +NOTE: If you are using a reactive library then there may well be configurations +better suited to your purposes. + +ESLint checks your code for potential bugs, Prettier ensures your whole teams +code uses the same formatting rules. Config for these two is often added at the +same time, the package `eslint-config-prettier` specifically made to help the +two work together. + +Create a file called `.prettierrc` in the root of your project and add the +following. + +```json path=.prettierrc +{ + "bracketSameLine": true, + "bracketSpacing": true, + "printWidth": 120, + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "proseWrap": "always" +} +``` + +Then add a file called `eslint.config.js` in the root of the project and include +the following config. + +```javascript path=eslint.config.js +import js from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import globals from 'globals'; + +export default [ + { + languageOptions: { + ecmaVersion: 2024, + globals: { + ...globals.es2024, + ...globals.browser, + }, + }, + }, + { ignores: ['node_modules', 'dist'] }, + js.configs.recommended, + eslintConfigPrettier, +]; +``` + +Add the following dev dependencies + +```bash +pnpm add -D eslint eslint-config-prettier prettier globals +``` + +Two more scripts for `package.json` to run eslint and prettier. + +```json path=package.json +"lint:es": "eslint . main.js --report-unused-disable-directives --max-warnings 0", +"lint:format": "prettier ./**/*.{js,jsx,ts,tsx,md,mdx,scss} --write --ignore-unknown --no-error-on-unmatched-pattern --log-level warn", +``` + +Running these scripts will, not at the time of writing highlight any ESLint +issues. If it does in future it may be possible to auto fix them. + +It will almost certainly refomat the code compared to your chosen editors +preferences. + +### Spell checkers + +If we all had perfect spelling and typing then maybe a spell checker is not +needed. However, given that the word `typo` exists let us assume that somebody +on your team is not the perfect vessel to sit between their keyboard and chair. + +A spell checker ensures that `typo` do not, or at least should not go unnoticed. + +For more information on cspell [visit cspell.org](https://cspell.org/). + +Add `cspell` as a dependency with: + +```bash +pnpm add -D cspell +``` + +There are many config options, including the ability to create and share custom +dictionaries so your brand, pet name or other desirable creations do not get +flagged up as spelling errors. + +Here we will add the following config file `cspell.json`. + +```json path=cspell.json +{ + "version": "0.1", + "language": "en", + "dictionaries": [], + "dictionaryDefinitions": [], + "enabledLanguageIds": [ + "css", + "git-commit", + "html", + "javascript", + "json", + "markdown", + "plaintext", + "scss", + "text" + ], + "ignorePaths": [ + "node_modules", + "dist", + "package.json", + "*.log", + "CHANGELOG.md" + ], + "words": [""] +} +``` + +Add this spelling script to `package.json` + +```json path=package.json +"lint:spell": "cspell lint --quiet \"**/*.{js,css,scss,md}\"", +``` + +Running this script will find a number of spellings `cspell` is not happy with. + +In our style file for instance it will highlight our image name `illo` as not +being a word, which is great because it is not. + +We can also see in our IDE that this is highlighted with a blue wiggly +underline. + +![Spelling of illo highlighted](./images/step-5/spelling-illo.png) + +There are a number of ways to fix this. As this is not a spelling mistake we +will choose `Add: "illo" to config:...` from the list of options. + +![Spelling suggested quick fixes](./images/step-5/spelling-suggestions.png) + +If you view the `cspell.json` file you will find it has been added. + +## Deplying to gh-pages + +GitHub packages can use a branch called `gh-pages` to deploy to. In order to do +this a couple of important changes are needed first. Without completing these +steps correctly you will either end up attempting to deploy somewhere you do not +have permission to do so, or deploy a page that cannot find any of its +dependencies. + +Update and/or add `name` and `homepage` to `pacakge.json`. + +```json path=package.json +"name": "carbon-tutorial-web-components", +"homepage": "https://github.com/[YOUR-ORG-OR-NAME]/[REPO-NAME]", +``` + +Back to `package.json` we need one last dependency to automate the deployment +process. + +```bash +pnpm add -D gh-pages +``` + +Next to run the deployment we will add two scripts to `package.json` + +```json path=package.json +"predeploy": "npm run build", +"deploy": "gh-pages -d dist", +``` + +Way back in step 1 you creted a fork of the tutorial repo. It is this URL you +use for the projects homepage. + +Next you will need a `vite.config.js` file to ensure both `index.html` and +`repositories.html` are included in the build. The config may also need to set a +value for `base` that depends on various factors. + +If a personal GitHub it is likely your vite config will look like this: + +```javascript path=vite.config.js +export default { + base: '/[REPO-NAME]/', + build: { + rollupOptions: { + input: { + landing: fileURLToPath(new URL('./index.html', import.meta.url)), + repositories: fileURLToPath( + new URL('./repositories.html', import.meta.url) + ), + }, + }, + }, +}; +``` + +If part of an organisation use `base: '/'` instead. + +After running the deploy script you will have a publicly visible page. You can +locate the page by browsing to the settings page of your GitHub repository. At +the time of writing the URL shown in the `Pages` section where you can choose to +unpublish if needed. + +For ease of access: + +- Select the `Code` tab/page +- Click on the cog (right side of page) to the right of `About` to view the + repository details. +- Check the `Use your GitHub Pages website` and save changes. The GitHub pages + URL should now appear on the landing page of your repository. + +### Debugging your deploy + +#### Build + +Run a build and makes sure your assets are all included in the `dist` folder. + +```bash +pnpm build +``` + +This should include the contents of public, the two html files, CSS and +Javascript. If something is missing check your Vite config and `package.json` +against the guidance above and the latest Vite documentation. + +#### Deploy + +Deploy your application with a base value of `/`. If the page does not look +correctc, e.g. not styled or missing icons, then you need to determine what the +difference is between the asset location and your base. + +Open the deveveloper tools and view the console. Any missing assets should cause +messages such as the following to be displayed. + +![Asset not found 404](./images/step-5/asset-404.png) + +In this case + +- GitHub pages location `https://[ACCOUNT].github.io/[REPO-NAME]` +- Asset location `https://[ACCOUNT].github.io/[ASSET]` + +The fix would be to change your base setting to `/[REPO-NAME]/`. + +## Using the CDN + +Lastly we've been using `main.js` to load components from `node_modules` but Web +Components do not require is to do this. All of Carbon Web Components is +published via a CDN see +[the documentation for further details](https://web-components.carbondesignsystem.com/?path=/docs/introduction-carbon-cdn-style-helpers--overview). + + + +**Note:** The +[deployed version](https://solid-carnival-1pg38np.pages.github.io/) of this +tutorial is using the CDN. + + + +Remove the component imports from `main.js`. + +```javascript path=main.js +import '@carbon/web-components/es/components/button/button.js'; +import '@carbon/web-components/es/components/ui-shell/index'; +import '@carbon/web-components/es/components/checkbox/index'; +import '@carbon/web-components/es/components/content-switcher/index'; +import '@carbon/web-components/es/components/skip-to-content/index.js'; +``` + +Remove the component imports from `landing.js`. + +```javascript path=landing.js +import '@carbon/web-components/es/components/breadcrumb/index'; +import '@carbon/web-components/es/components/tabs/index'; +``` + +Remove the component imports from `repos.js`. + +```javascript path=repos.js +import '@carbon/web-components/es/components/data-table/index.js'; +import '@carbon/web-components/es/components/link/index'; +import '@carbon/web-components/es/components/pagination/index'; +``` + +In the `head` of `index.html` add the following script tags. + +```html path=index.html + + + + + + + +``` + +We also need to update `repositories.html` with: + +```html path=repositories.html + + + + + + + +``` + +If you run the application again you are running with no dependency on +`node_modules/@carbon/web-components`. + +## Push to GitHub + +Just one more push to save your completion of step 5. + +### Git commit and push + +First, stage and commit all of your changes: + +```bash +git add --all && git commit -m "feat(tutorial): complete step 5" +``` + +Then, push to your repository: + +```bash +git push -u origin step-5 +``` + + + +**Note:** If your Git remote protocol is HTTPS instead of SSH, you may be +prompted to authenticate with GitHub when you push changes. If your GitHub +account has two-factor authentication enabled, we recommend that you follow +these instructions to +[create a personal access token for the command line](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line). +That lets you use your token instead of password when performing Git operations +over HTTPS. + + + +## Step 5 complete branch + +That is it you are done. If you want to review the completed step 5 you can do +so by checking out + +```bash +git fetch upstream +git checkout -b step-5 upstream/step-5-complete +```