Keith

Shoelace Web Component Sandbox

by Keith Rowles • 01/12/2023HTML

A black plain mobile phone resting on the top of a laptop

Summary

Testing out the Shoelace Style highly customisable ui web component library using their CDN.

Some of the components being tested include:

  • button and button group
  • alert
  • badge
  • avatar
  • breadcrumb
  • details
  • divider
  • icon
  • menu
  • tab
  • drawer
  • tooltip
  • …and more!

I have integrated the components into a basic CSS Grid template as well utilised the design tokens.

Here are the links to the CDN hosted on jsdeliver.

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.12.0/cdn/themes/light.css"
/>
<script
  type="module"
  src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.12.0/cdn/shoelace-autoloader.js"
></script>

Tech

  • HTML
  • CSS
  • Shoelace Style (Web Components and Design Tokens)
  • Glitch

Demo

Here is a link to the demo hosted on Glitch.

Link to Demo

Here is the link to Shoelace.

Link to Shoelace

HTML

I have included the code below.

<!-- site grid -->

<div class="site">
  <!-- header -->

  <header>
    <div class="header-container">
      <!-- navbar -->

      <nav class="nav-bar">
        <sl-button-group label="Alignment">
          <sl-button>Item</sl-button>
          <sl-button>Item</sl-button>
          <sl-button>Item</sl-button>
        </sl-button-group>

        <sl-button-group label="Alignment">
          <sl-tooltip content="Github">
            <sl-button
              ><sl-icon name="github" label="GitHub"></sl-icon
            ></sl-button>
          </sl-tooltip>

          <sl-tooltip content="Stack Overflow">
            <sl-button
              ><sl-icon name="stack-overflow" label="Stack Overflow"></sl-icon
            ></sl-button>
          </sl-tooltip>

          <sl-tooltip content="Twitter X">
            <sl-button
              ><sl-icon name="twitter-x" label="Twitter X"></sl-icon
            ></sl-button>
          </sl-tooltip>
        </sl-button-group>
      </nav>
    </div>
  </header>

  <!-- main content -->

  <main>
    <div class="main-container">
      <sl-alert variant="success" open>
        <sl-icon slot="icon" name="check2-circle"></sl-icon>

        <strong>Your changes have been saved</strong><br />

        You can safely exit the app now.
      </sl-alert>

      <h1>Lorem ipsum dolor sit amet consectetur adipisicing elit.</h1>

      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Fuga
        consequuntur delectus aspernatur perferendis quos dolorum.
      </p>

      <p>
        Lorem ipsum, dolor sit amet consectetur adipisicing elit. Earum sit
        officia magnam, fugit perspiciatis modi eaque odit ex neque animi
        consectetur quod culpa! Impedit repellendus id quaerat cum veritatis
        quasi assumenda quod reiciendis, magni adipisci, et iure libero quo
        sequi in unde expedita? Officiis possimus corrupti at expedita,
        distinctio fugit aut, debitis id ut consequatur, rem porro asperiores
        molestiae animi!
      </p>

      <p><strong>List</strong></p>

      <ul>
        <li>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolores,
          voluptatem.
        </li>

        <li>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolores,
          voluptatem.
        </li>

        <li>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolores,
          voluptatem.
        </li>

        <li>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolores,
          voluptatem.
        </li>

        <li>
          Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolores,
          voluptatem.
        </li>
      </ul>

      <sl-divider></sl-divider>

      <h2>Button</h2>

      <sl-button variant="default">Default</sl-button>

      <sl-button variant="primary">Primary</sl-button>

      <sl-button variant="success">Success</sl-button>

      <sl-button variant="neutral">Neutral</sl-button>

      <sl-button variant="warning">Warning</sl-button>

      <sl-button variant="danger">Danger</sl-button>

      <sl-divider></sl-divider>

      <h2>Badge</h2>

      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Laboriosam
        tenetur nihil qui totam! Earum nobis aspernatur rem quis, neque qui modi
        quod tempore dignissimos officia.
      </p>

      <sl-badge variant="primary" pill>Primary</sl-badge>

      <sl-badge variant="success" pill>Success</sl-badge>

      <sl-badge variant="neutral" pill>Neutral</sl-badge>

      <sl-badge variant="warning" pill>Warning</sl-badge>

      <sl-badge variant="danger" pill>Danger</sl-badge>

      <sl-divider></sl-divider>

      <h3>Tabs</h3>

      <sl-tab-group placement="start">
        <sl-tab slot="nav" panel="general">General</sl-tab>

        <sl-tab slot="nav" panel="custom">Custom</sl-tab>

        <sl-tab slot="nav" panel="advanced">Advanced</sl-tab>

        <sl-tab-panel name="general"
          >This is the general tab panel.</sl-tab-panel
        >

        <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel>

        <sl-tab-panel name="advanced"
          >This is the advanced tab panel.</sl-tab-panel
        >
      </sl-tab-group>

      <sl-divider></sl-divider>

      <h3>Details</h3>

      <sl-details summary="Toggle Me">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
        veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
        commodo consequat.
      </sl-details>

      <sl-divider></sl-divider>

      <h3>Drawer</h3>

      <sl-drawer
        label="Drawer"
        placement="start"
        class="drawer-placement-start"
      >
        This drawer slides in from the start.

        <sl-button slot="footer" variant="primary">Close</sl-button>
      </sl-drawer>

      <sl-button>Open Drawer</sl-button>

      <sl-divider></sl-divider>

      <h3>Icons</h3>
      <div style="font-size: 32px">
        <sl-icon name="exclamation-triangle"></sl-icon>
        <sl-icon name="archive"></sl-icon>
        <sl-icon name="battery-charging"></sl-icon>
        <sl-icon name="bell"></sl-icon>
        <sl-icon name="clock"></sl-icon>
        <sl-icon name="cloud"></sl-icon>
        <sl-icon name="download"></sl-icon>
        <sl-icon name="file-earmark"></sl-icon>
        <sl-icon name="flag"></sl-icon>
        <sl-icon name="heart"></sl-icon>
        <sl-icon name="image"></sl-icon>
        <sl-icon name="lightning"></sl-icon>
        <sl-icon name="mic"></sl-icon>
        <sl-icon name="search"></sl-icon>
        <sl-icon name="star"></sl-icon>
        <sl-icon name="trash"></sl-icon>
      </div>

      <sl-divider></sl-divider>

      <h4>Tooltip</h4>

      <sl-tooltip content="This is a tooltip">
        <sl-button>Hover Me</sl-button>
      </sl-tooltip>

      <sl-divider></sl-divider>

      <h4>Breadcrumb</h4>

      <sl-breadcrumb>
        <sl-breadcrumb-item>Catalog</sl-breadcrumb-item>
        <sl-breadcrumb-item>Clothing</sl-breadcrumb-item>
        <sl-breadcrumb-item>Women's</sl-breadcrumb-item>
        <sl-breadcrumb-item>Shirts &amp; Tops</sl-breadcrumb-item>
      </sl-breadcrumb>

      <sl-divider></sl-divider>

      <h4>Avatar</h4>

      <sl-avatar initials="SL" label="Avatar with initials: SL"></sl-avatar>

      <sl-divider></sl-divider>

      <h4>Button Group</h4>

      <div class="button-group-toolbar">
        <sl-button-group label="History">
          <sl-tooltip content="Undo">
            <sl-button
              ><sl-icon name="arrow-counterclockwise" label="Undo"></sl-icon
            ></sl-button>
          </sl-tooltip>
          <sl-tooltip content="Redo">
            <sl-button
              ><sl-icon name="arrow-clockwise" label="Redo"></sl-icon
            ></sl-button>
          </sl-tooltip>
        </sl-button-group>

        <sl-button-group label="Formatting">
          <sl-tooltip content="Bold">
            <sl-button
              ><sl-icon name="type-bold" label="Bold"></sl-icon
            ></sl-button>
          </sl-tooltip>
          <sl-tooltip content="Italic">
            <sl-button
              ><sl-icon name="type-italic" label="Italic"></sl-icon
            ></sl-button>
          </sl-tooltip>
          <sl-tooltip content="Underline">
            <sl-button
              ><sl-icon name="type-underline" label="Underline"></sl-icon
            ></sl-button>
          </sl-tooltip>
        </sl-button-group>

        <sl-button-group label="Alignment">
          <sl-tooltip content="Align Left">
            <sl-button
              ><sl-icon name="justify-left" label="Align Left"></sl-icon
            ></sl-button>
          </sl-tooltip>
          <sl-tooltip content="Align Center">
            <sl-button
              ><sl-icon name="justify" label="Align Center"></sl-icon
            ></sl-button>
          </sl-tooltip>
          <sl-tooltip content="Align Right">
            <sl-button
              ><sl-icon name="justify-right" label="Align Right"></sl-icon
            ></sl-button>
          </sl-tooltip>
        </sl-button-group>
      </div>
    </div>
  </main>

  <!-- left sidebar -->

  <aside class="left-sidebar">
    <div class="left-sidebar-container">
      <sl-menu>
        <sl-menu-label>Heading</sl-menu-label>

        <sl-menu-item value="undo">Undo</sl-menu-item>

        <sl-menu-item value="redo">Redo</sl-menu-item>

        <sl-divider></sl-divider>

        <sl-menu-label>Heading</sl-menu-label>

        <sl-menu-item value="cut">Cut</sl-menu-item>

        <sl-menu-item value="copy">Copy</sl-menu-item>

        <sl-menu-item value="paste">Paste</sl-menu-item>

        <sl-menu-item value="delete">Delete</sl-menu-item>
      </sl-menu>

      <section class="left-sidebar-icons">
        <sl-avatar label="Avatar with an image icon" class="avatar-basic">
          <sl-icon slot="icon" name="image"></sl-icon>
        </sl-avatar>

        <sl-avatar label="Avatar with an archive icon" class="avatar-basic">
          <sl-icon slot="icon" name="archive"></sl-icon>
        </sl-avatar>

        <sl-avatar label="Avatar with a briefcase icon" class="avatar-basic">
          <sl-icon slot="icon" name="briefcase"></sl-icon>
        </sl-avatar>
      </section>
    </div>
  </aside>

  <!-- right sidebar -->

  <aside class="right-sidebar">
    <div class="right-sidebar-container">
      <sl-card class="card-basic">
        <p>
          Lorem, ipsum dolor sit amet consectetur adipisicing elit. Autem
          eveniet repudiandae tempora quisquam accusantium quo praesentium totam
          assumenda laboriosam reiciendis pariatur, aspernatur laborum debitis
          laudantium voluptatem, ea nihil nesciunt? Deserunt?
        </p>
      </sl-card>

      <sl-card class="card-basic">
        <p>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Fugiat velit
          magnam deserunt ipsum dolores! Officia laboriosam illum porro ipsum.
          Consectetur accusantium nam velit optio eveniet deserunt quia, quae,
          veniam dolores maxime dicta cupiditate aliquam ad repellendus,
          perferendis ratione neque debitis suscipit rerum iusto non labore
          ipsum dolore omnis! In, voluptates!
        </p>
      </sl-card>
    </div>
  </aside>

  <!-- footer -->

  <footer>
    <div class="footer-container">
      <div class="footer-details">
        Copyright &copy; Logo <sl-format-date></sl-format-date>
      </div>

      <div class="footer-social">
        <a href="#"><sl-icon name="github"></sl-icon></a>

        <a href="#"><sl-icon name="stack-overflow"></sl-icon></a>

        <a href="#"><sl-icon name="twitter-x"></sl-icon></a>
      </div>
    </div>
  </footer>
</div>

<script>
  const drawer = document.querySelector('.drawer-placement-start');

  const openButton = drawer.nextElementSibling;

  const closeButton = drawer.querySelector('sl-button[variant="primary"]');

  openButton.addEventListener('click', () => drawer.show());

  closeButton.addEventListener('click', () => drawer.hide());
</script>

CSS

/* css reset */
*,
*::before,
*::after {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

body,
html {
  font-family: var(--sl-font-sans);
  font-size: var(--sl-font-size-medium);
  background-color: var(--sl-color-warning-50);
  letter-spacing: var(--sl-letter-spacing-normal);
  line-height: var(--sl-line-height-normal);
}

:is(h1, h2, h3) {
  margin-bottom: var(--sl-spacing-medium);
  margin-top: var(--sl-spacing-medium);
}

ul {
  list-style: none;
  margin-bottom: var(--sl-spacing-medium);
}

p {
  margin-bottom: var(--sl-spacing-medium);
}

/* shoe lace fix */

:not(:defined) {
  visibility: hidden;
}

/* grid */

.site {
  min-height: 100vh;
  display: grid;
  grid-template-areas: 'header' 'main' 'right-sidebar' 'left-sidebar' 'footer';
  grid-template-rows: min-content 1fr min-content min-content min-content;
  gap: 1rem;
  padding: 1rem;
  background-color: var(--sl-color-neutral-50);
}

/* header and navbar */

header {
  grid-area: header;
}

.header-container {
  background-color: var(--sl-color-primary-50);
  padding: var(--sl-spacing-small);
  border-radius: var(--sl-border-radius-large);
}

header nav.nav-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--sl-spacing-medium);
}

/* content */

main {
  grid-area: main;
}

.main-container {
  background-color: var(--sl-color-danger-50);
  padding: var(--sl-spacing-small);
  border-radius: var(--sl-border-radius-large);
}

/* footer */

footer {
  grid-area: footer;
}

.footer-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--sl-spacing-medium);
  background-color: var(--sl-color-success-50);
  padding: var(--sl-spacing-small);
  border-radius: var(--sl-border-radius-large);
}

.footer-social {
  font-size: var(--sl-font-size-large);
}

.footer-social a {
  color: var(--sl-color-gray-500);
}

.footer-social a:hover {
  color: var(--sl-color-gray-700);
}

.footer-social sl-icon {
  margin-left: var(--sl-spacing-medium);
}

/* left menu bar */

.left-sidebar {
  grid-area: left-sidebar;
}

.left-sidebar-container {
  background-color: var(--sl-color-violet-50);
  padding: var(--sl-spacing-small);
  display: flex;
  flex-direction: column;
  gap: var(--sl-spacing-medium);
  justify-content: space-between;
  height: 100%;
  border-radius: var(--sl-border-radius-large);
}

.left-sidebar .left-sidebar-container sl-menu {
  margin-bottom: var(--sl-spacing-medium);
}

.left-sidebar .left-sidebar-container .left-sidebar-icons {
  display: flex;
  flex-direction: row;
  gap: var(--sl-spacing-small);
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
}

/* right general information */

.right-sidebar {
  grid-area: right-sidebar;
}

.right-sidebar-container {
  background-color: var(--sl-color-pink-50);
  padding: var(--sl-spacing-small);
  height: 100%;
  border-radius: var(--sl-border-radius-large);
}

/* shoelace styles for components */

.card-basic {
  margin-bottom: var(--sl-spacing-medium);
}

.avatar-basic {
  --size: var(--sl-spacing-2x-large);
}

sl-divider {
  margin-bottom: var(--sl-spacing-medium);
  margin-top: var(--sl-spacing-medium);
}

/* break points */
@media screen and (min-width: 600px) {
  .site {
    grid-template-columns: minmax(auto, 250px) minmax(250px, 1fr) minmax(auto, 250px);

    grid-template-rows: min-content 1fr min-content min-content;

    grid-template-areas:
      'header header header'
      'main main right-sidebar'
      'left-sidebar left-sidebar left-sidebar'
      'footer footer footer';
  }

  .header-container {
    background-color: var(--sl-color-orange-50);
  }
}

@media screen and (min-width: 900px) {
  .site {
    grid-template-columns: minmax(auto, 250px) minmax(250px, 1fr) minmax(auto, 250px);

    grid-template-rows: min-content 1fr min-content;

    grid-template-areas:
      'header header header'
      'left-sidebar main right-sidebar'
      'footer footer footer';
  }

  .header-container {
    background-color: var(--sl-color-rose-50);
  }
}