Skip to content

A component for adding to a bundle

If you are using WebC and the Bundle plugin and you want to add content to a bundle from within a WebC file, you can use this component to make that simple. I call it add-to-bucket.webc.

html
<template webc:ignore>
  How to use this component:

  <add-to-bundle @name="[bundle_name]" @bucket="[bucket_name]">
    <!-- bundle content goes here -->
  </add-to-bundle>

  The bundle name is required. Bucket name is optional and can be used for asset bucketing (see
  https://www.11ty.dev/docs/plugins/bundle/#asset-bucketing). TIP: You can use `webc:is` to get
  proper syntax highlighting, like this:

  <style webc:is="add-to-bundle" @name="css">
    body {
      color: rebeccapurple;
    }
  </style>
</template>

<script webc:type="js" webc:nokeep webc:root>
  if (!name) {
    throw new Error("the `name` prop is required");
  }
  webc.helpers[name](slots.text.default.trim(), bucket);
</script>

How to make layout components

Here's an example for making a WebC component that has the same API as the web components that come from Every Layout—in this case, the Stack component.

Note that the whole component is using JavaScript to generate its content. This enables us to set things up per-instance based on the attributes/props supplied to the component.

  1. In the Eleventy Layout web component, there are getters which parse the given attributes and provide default values. Here, they are local variables.
  2. The custom per-instance ID is also set as a local variable.
  3. There is currently no way to dynamically set the host component's attributes, so we render a <div> that has a class name matching the component name and the data-i attribute that is unique to the instance attributes.
html
<!-- stack-l.webc -->
<script webc:type="js">
  const space = webc.attributes.space ?? "var(--s1)";
  const recursive = webc.attributes.recursive !== undefined;
  const splitAfter = webc.attributes.splitAfter ?? null;

  const i = `Stack-${[space, recursive, splitAfter].join("")}`;

  `
  <div class="stack-l" data-i="${i}"><slot></slot></div>

  <style>
    .stack-l[data-i="${i}"]${recursive ? "" : " >"} * + * {
      margin-block-start: ${space};
    }

    ${
      splitAfter
        ? `
      .stack-l[data-i="${i}"]:only-child {
        block-size: 100%;
      }

      .stack-l[data-i="${i}"] > :nth-child(${splitAfter}) {
        margin-block-end: auto;
      }`
        : ""
    }
  </style>
  `;
</script>

Something worth noting is that the CSS here will be hoisted to the css bundle. I specifically do not use scoped styling here because I want my CSS minifier to de-duplicate common styles for multiple instances using the same configuration (for the same reason that the web component skips adding <style> elements if one with that ID already exists).

Here is an example for how to use this component in another WebC template. It's the same as the examples in Every Layout, except in WebC you can use the @prop-name syntax to erase the custom attributes from the output. Also, note the kebab-case vs. camelCase naming change between the @prop-name here and the propName used within the component definition.

html
<stack-l @space="1.5rem" @split-after="5">
  <h2>Some title</h2>
  <p>Some text</p>
  <p>More text</p>
</stack-l>

The build output is

html
<!-- in the <head>, assuming you used getBundle('css') here... -->
<style>
  .stack-l[data-i="Stack-1.5remfalse5"] > * + * {
    margin-block-start: 1.5rem;
  }
  .stack-l[data-i="Stack-1.5remfalse5"]:only-child {
    block-size: 100%;
  }
  .stack-l[data-i="Stack-1.5remfalse5"] > :nth-child(5) {
    margin-block-end: auto;
  }
</style>

<!-- rendered content: -->
<div data-i="Stack-1.5remfalse5" class="stack-l">
  <h2>Some title</h2>
  <p>Some text</p>
  <p>More text</p>
</div>

This work is licensed under CC BY-NC-ND 4.0