← Back to Blog webmention-feed component hero

I Published My First NPM Package

A customizable Lit web component for displaying webmention feeds from webmention.io. themeable via CSS custom properties and ::part() selectors, available on npm.

by Keiji Lohier
  • #npm
  • #Web Components
  • #Lit
  • #IndieWeb
  • #Webmentions

I published my first package to npm. It’s a Lit web component that renders a webmention feed. It works with webmention.io and paired with brid.gy, enables backfeeding social interactions from Mastodon and Bluesky into your own site. It was built for my own site but I wanted to experiment seeing how it could be reused by others. I chose to make it a web component so that it can be used in any framework or none at all and be as simple as a tag in your HTML.

I used Lit as the base for the components for its developer experience and reduced boilerplate compared to vanilla web components.

The part I enjoyed most was thinking beyond my own use case. The component ships with sensible defaults but exposes CSS custom properties for easy theming and ::part() selectors for deeper structural overrides. You can even swap out the like and repost icons and pagination labels via slots.

I further want to explore web components as a way to build and share UI components as I feel like interoperabliity is a very nice feature to have. Excited to learn more about the shadow DOM and ::part() selectors.

The source code is open on GitHub, and the package is published under the name webmention-feed. If you want to add a webmention feed to your site, give it a try! Check out the source on GitHub.

Some example looks below


Default Look

See the configuration
<webmention-feed
  post-url="https://yoursite.com/your-post/"
  endpoint="https://webmention.io/yoursite.com/webmention"
  per-page="3"
></webmention-feed>

Dark neon

Monospace font, magenta accents, and sharp corners via ::part().

◀ prevnext ▶
See the configuration
<webmention-feed
  post-url="https://yoursite.com/your-post/"
  endpoint="https://webmention.io/yoursite.com/webmention"
  per-page="3"
  style="
    --wm-accent-color: #f0f;
    --wm-text-color: #e0e0e0;
    --wm-border-color: #f0f;
    --wm-reply-bg: #0d0d0d;
    --wm-reply-border-color: #f0f;
    --wm-input-bg: #111;
    --wm-input-border-color: #f0f;
    --wm-avatar-bg: #2a002a;
    --wm-button-text-color: #fff;
    font-family: monospace;
  "
>
  <span slot="like-icon">♦</span>
  <span slot="repost-icon">⟳</span>
  <span slot="prev-label">◀ prev</span>
  <span slot="next-label">next ▶</span>
</webmention-feed>
webmention-feed::part(reply) {
  border-radius: 0;
  border-left: 2px solid #f0f;
  border-top: none;
  border-right: none;
  border-bottom: none;
  padding-left: 1rem;
}
webmention-feed::part(button) {
  border-radius: 0;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
webmention-feed::part(page-button) {
  background: transparent;
  color: #f0f;
  border: 1px solid #f0f;
  border-radius: 0;
  font-family: monospace;
}
webmention-feed::part(page-button--disabled) {
  opacity: 0.2;
}

Brutalist

Bold typography, yellow accents, and offset box shadows via ::part().

← BACKMORE →
See the configuration
<webmention-feed
  post-url="https://yoursite.com/your-post/"
  endpoint="https://webmention.io/yoursite.com/webmention"
  per-page="3"
  style="
    --wm-accent-color: #ffe600;
    --wm-text-color: #fff;
    --wm-border-color: #ffe600;
    --wm-reply-bg: #111;
    --wm-reply-border-color: #ffe600;
    --wm-input-bg: #222;
    --wm-input-border-color: #ffe600;
    --wm-avatar-bg: #333;
    --wm-button-text-color: #111;
    font-family: 'Arial Black', Arial, sans-serif;
  "
>
  <span slot="like-icon">▲</span>
  <span slot="repost-icon">⬛</span>
  <span slot="prev-label">← BACK</span>
  <span slot="next-label">MORE →</span>
</webmention-feed>
webmention-feed::part(reply) {
  border-radius: 0;
  border: 3px solid #ffe600;
  box-shadow: 5px 5px 0 #ffe600;
  margin-bottom: 4px;
}
webmention-feed::part(heading) {
  font-size: 2rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  border-bottom: 4px solid #ffe600;
  padding-bottom: 0.25rem;
}
webmention-feed::part(button) {
  border-radius: 0;
  border: 3px solid #ffe600;
  box-shadow: 3px 3px 0 #ffe600;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
webmention-feed::part(input) {
  border-radius: 0;
  border: 3px solid #ffe600;
  font-weight: 700;
  color: #fff;
}
webmention-feed::part(page-button) {
  background: #111;
  color: #ffe600;
  border: 3px solid #ffe600;
  border-radius: 0;
  box-shadow: 3px 3px 0 #ffe600;
  font-weight: 900;
  text-transform: uppercase;
}
webmention-feed::part(page-button--disabled) {
  opacity: 0.2;
  box-shadow: none;
}