Profile Components is a set of web components with zero dependencies that display publicly-available profile information from various social networks. Currently two: GitHub and dev.to.
Being native web components, these can be used in any HTML page, framework-based site, or wherever you can use HTML. They are available via unpkg.com or you can add the NPM module to your project.
100% All Natural Features!
🎨 New hotness CSS w/nesting & container queries
👷 DX: Separate files for Javascript, HTML, and CSS
✅ Native unit testing with node:test
🎁 Bonus! A sneaky SSR workaround for server side rendering!
tl;dr
use via unpkg.com:
<!-- add to HEAD -->
<script
type="module"
src="https://unpkg.com/profile-components/dist/github-user.js"></script>
<!-- shows a GitHub profile with fetched content for user `scottnath` -->
<github-user username="scottnath" fetch="true"></github-user>
install via NPM:
npm i profile-components
links to learn more:
- profile-components on GitHub
- play with the components in Storybook
- See demos on stackblitz
🔥 Framework-free in 2023!
There have been a lot of feature drops across the major browsers this year, allowing us to more easily build shareable and reusable web components without any frameworks and without pre-or-post style-processors like Sass or PostCSS. This includes full implementation most of the original web components spec (🫗 r.i.p. HTML imports.) This year also includes lots of long-sought-after CSS features like container queries and nesting.
profile-components
contain user interfaces without interactions or changing state making them simple to build cross-browser. As web components with unique styling, the isolation of styles inside the shadow dom is a benefit because each component uses a different set of root variables and styles. The style isolation allows these old school "widgets" to visually represent the social network they are displaying without affecting the rest of your page.
🚫 Zero dependencies
Any dependencies on this project are only for development. Meaning there are dependencies listed in devDependencies
, but those are for testing and building the distributed components. The only external code which goes into the final build are the style variables and icons pulled from the social network's open source code.
🔓 Fetches live data - no api keys needed!
There are two options for sourcing content into these web components: fetch it live from the social's rest API or feed the component static data via the HTML attributes. You may also mix in your own data to overwrite what comes from the APIs - like if you wanted to have a local avatar image instead.
note: future components may need an API key(s), but for now, these use public, AUTH-free endpoints.
Fetching live data (fetch="true"
)
<github-user
username="scottnath"
fetch="true"
></github-user>
...or... Skip fetching and use static data
<github-user
username="scottnath"
name="Meowy McMeowerstein"
bio="Spending time purring and sleepin"
followers="500000"
following="2980"
avatar_url="/cat-avatar.png"
></github-user>
🎨 Styles
Stylesheets are written in pure CSS and only use features which are supported in all major browsers.
Nesting
Stylesheets have their styles nested to reduce adding extra classes to the HTML and to make them easier to maintain.
/* uses `:has` to target the dl with a .post inside */
& dl:has(.post) {
border-bottom: 1px solid var(--color-shadow);
padding-bottom: 1em;
/* any `dt` inside a `dl` with a `.post` inside */
& dt {
color: var(--color-light);
font-size: var(--font-size-light);
Container Queries
Container queries allow the components to be responsive to their container, not the viewport - a more realistic usage scenario.
Colors and CSS variables sourced from the social network
To make the components feel like the sites they represent, they need to use the same colors, icons, and fonts. So to build these components, I sourced CSS variables from their open source repositories or modules. For GitHub, this means styles from the primer design system and for dev.to, which is built using the Forem community software, I sourced styles from the forem/forem repo on GitHub.
/* from the GitHub design-system, primer */
/* Light Theme */:host([data-theme="light"]) {
--color-avatar-border: rgba(31,35,40,0.15);
--color-border-default: #d0d7de;
--color-canvas-default: #ffffff;
--color-canvas-subtle: #f6f8fa;
--color-fg-default: #1F2328;
--color-fg-muted: #656d76;
--color-fg-subtle: #6e7781;
--color-fg-onemphasis: #ffffff;
--color-fg-accent: #0969da;
--color-fg-danger: #d1242f;
}
/* Light Protanopia & Deuteranopia Theme */:host([data-theme="light_colorblind"]) {
--color-avatar-border: rgba(27,31,36,0.15);
...
👷 DX: Separate files for Javascript, HTML, and CSS
"Easy to maintain" requires a good Developer Experience (DX). To make these components easier to iterate on and update, they're built like a web page. This means separate HTML, Javascript and CSS files. While the development happens in separate files, the content from the various files is compiled into a single file for distribution.
I was inspired by Leon Eck's post Splitting Web Components into .ts, .html, and .scss files, which detailed a similar approach, using esbuild to compile the files into a single file. Esbuild is pretty simple to set up and configure, and it easily takes every import
ed file and converts it to an in-file variable.
HTML generation without frameworks or libraries
To maintain the zero dependencies goal, the HTML is living inside a Javascript file as a string returned from a method that accepts a single, JSDoc-documented parameter. This is not ideal, but allows using Javascript to generate the HTML without a framework or templating library like Lit or Handlebars. This is also what makes the SSR trick easy to pull off.
Javascript methods outside of the customElements class
Testing is paramount to easy maintenance. The JS methods used by these components perform fairly simple tasks which can be unit tested without the need for a browser. These web components get data using the Fetch API, which is fully implemented in both Node and browsers, making it easy to create mock responses and unit tests. They can also be used outside of the web component, so a separate file makes sense.
Separate stylesheets for styles and source variables.
There are separate sheets for maintainability. Generally, there is one auto-generated file with variables from the social network, one with stylesheet with global styles (since there are multiple components with unique styles combined), and one stylesheet per component. Generally the files are:
- vars-[source].css
- e.g. `vars-devto.css`.
- Variables from the social network's open source code
- global.css
- Global style variables
- Shared across all components
- [component].css
- e.g. `user.css` or `repository.css`
- Styles specific to the component
These are then imported by the web component and exported with the HTML inside a <style>
tag.
♿ Fully accessible
These components are tested using screen readers and AXE via Storybook. The HTML structure focuses on semantic HTML and when read aloud via screen reader, screen-reader-only content is available to provide context to the user.
For example, in the GitHub component, the header looks like this:
And the HTML is this:
<section aria-label="GitHub user profile" itemscope itemtype="http://schema.org/Person">
<header>
<span><span itemprop="memberOf">GitHub</span> user</span>
<span itemprop="alternativeName">scottnath</span>
</header>
The styles convert the first span into the GitHub logo using CSS' mask-image
property. This visually hides the text, but it's still available to screen readers, so the screen reader reads this to the user:
"GitHub user profile GitHub user scottnath"
note: The itemscope
and other item[thing]
attributes are from schema.org. These are used to structutre the data into microformats. This is more for SEO and content structure than for accessibility.
✅ Native Unit Testing with Node 20's node:test
Might as well go all in! Node 20 shipped with a native test runner, node:test. Fairly simple test runner, but it includes code coverage and has all the functionality needed to unit test these components.
The latest unit test runs are visible in the unit tests GitHub action workflow for profile-components
🎁 bonus! Server Side Rendering cheatcode!
Because these components were built with separate HTML, CSS, and JS files, you can use those pieces to generate HTML on the server. This example is what I did to make an Astro component for scottnath.com.
// DevToUser.astro
---
import devto from 'profile-components/devto-utils.js';
const user = devto.user;
const userContent = await user.generateContent({
username: 'scottnath',
},true);
let userHTML = '<style>' + user.styles + '</style>';
userHTML += user.html(userContent);
---
<devto-user>
<template shadowrootmode="open" set:html={userHTML}>
</template>
</devto-user>
Top comments (0)