Create

Frameworks

HTML

Use WDS in plain HTML, with a bundler or without one.

With a bundler#

For apps built with Vite, webpack, esbuild or another bundler.

Install packages#

lit is a peer dependency of @wds/core, so install it too.

pnpmnpmyarnbun
pnpm add @wds/core @wds/tokens lit
npm install @wds/core @wds/tokens lit
yarn add @wds/core @wds/tokens lit
bun add @wds/core @wds/tokens lit

Import the tokens and components#

main.js
// main.js (Vite, webpack, esbuild...)
import '@wds/tokens/tokens.css'; // --wds-* variables
import '@wds/core';              // registers every <wds-*> element

// or only the components you use:
// import '@wds/core/button';
// import '@wds/core/card';

Without a bundler#

wds.bundle.js has every component with Lit inside: no import map and no CDN, just a static file on your server.

Copy the files#

bash
# copy to your static files:
node_modules/@wds/core/dist/wds.bundle.js   # every component with Lit inside
node_modules/@wds/tokens/src/tokens.css

Add them to the page#

index.html
<!-- tokens + one file with every component (Lit is inside) -->
<link rel="stylesheet" href="/wds/tokens.css" />
<script type="module" src="/wds/wds.bundle.js"></script>

Chart is not in wds.bundle.js, because it includes ECharts. Add its own file when a page has charts.

index.html
<!-- wds-chart is a separate file: it includes ECharts -->
<script type="module" src="/wds/wds-chart.bundle.js"></script>

Import map#

To load single modules and take Lit from a CDN, use an import map instead. Pin every Lit package to one version, otherwise two copies of lit-html are loaded.

index.html
<link rel="stylesheet" href="/wds/tokens.css" />

<!-- before the first <script type="module">; every Lit package in one version -->
<script type="importmap">
  {
    "imports": {
      "lit": "https://cdn.jsdelivr.net/npm/[email protected]/index.js",
      "lit/": "https://cdn.jsdelivr.net/npm/[email protected]/",
      "lit-element/": "https://cdn.jsdelivr.net/npm/[email protected]/",
      "lit-html": "https://cdn.jsdelivr.net/npm/[email protected]/lit-html.js",
      "lit-html/": "https://cdn.jsdelivr.net/npm/[email protected]/",
      "@lit/reactive-element": "https://cdn.jsdelivr.net/npm/@lit/[email protected]/reactive-element.js",
      "@lit/reactive-element/": "https://cdn.jsdelivr.net/npm/@lit/[email protected]/",
      "@wds/core": "/wds/core/index.js"
    }
  }
</script>
<script type="module">
  import '@wds/core';
</script>

Usage#

Properties, attributes and events work like on native elements; the data of wds-* events is in event.detail. The HTML tab of every example on the component pages shows this code.

index.html
<wds-card heading="Sign in" description="Enter your email and password.">
  <form id="login">
    <wds-field>
      <wds-field-label>Email</wds-field-label>
      <wds-input name="email" type="email" required></wds-input>
    </wds-field>
  </form>
  <wds-button slot="footer" type="submit" form="login">Sign in</wds-button>
</wds-card>

<script type="module">
  const input = document.querySelector('wds-input');

  // properties work like on native elements
  input.value = '[email protected]';

  // wds-* events carry their data in event.detail
  input.addEventListener('wds-input', (e) => console.log(e.detail.value));
</script>

Forms#

Form controls are form-associated: they send their value under their name and support required, reset and disabled on a fieldset. The element has the validation API of native fields: validity, validationMessage, checkValidity(), reportValidity() and setCustomValidity(). Messages are in the browser's language.

index.html
<form method="post" action="/signup">
  <wds-input name="email" type="email" required></wds-input>
  <wds-radio-group name="plan" value="free">
    <wds-radio value="free">Free</wds-radio>
    <wds-radio value="pro">Pro</wds-radio>
  </wds-radio-group>
  <wds-checkbox name="newsletter">Newsletter</wds-checkbox>
  <wds-button type="submit">Sign up</wds-button>
</form>
<!-- sends: email=...&plan=free&newsletter=on -->
Footer