/skeletor

:skull: Front-end boilerplate for the web platform with Deno

Primary LanguageTypeScript


Image of Skeletor, the lead villain, from Masters of the Universe

Skeletor

Front-end boilerplate for the web platform with Deno.

License MIT


Includes

Notes

This boilerplate is still a work in progress...

Project requires deno and velociraptor to be installed

Custom element names require a hyphen (see using custom elements). Change this during setup (default: x)

Getting started

Clone the repository and give the project a name

git clone git@github.com:shinobi5/skeletor.git <project-name>

Initialise new git repository

cd <project-name> && rm -rf .git && git init

Setup project

vr setup

Serve the project at localhost:1234

vr start

Create build for production in the root of the project at build/

vr build

Components

Create boilerplate web component in src/js/components/

vr create-component

Usage examples

<head>
  <script
    type="module"
    src="js/components/custom-component/custom-component.js"
    defer
  ></script>
</head>
<body>
  <x-custom-component>Custom component</x-custom-component>
</body>
import { component, html } from 'https://cdn.skypack.dev/haunted';
import '../custom-component/custom-component.js';

const app = () => {
  return html`
    <x-custom-component>Custom component</x-custom-component>
  `;
};

customElements.define('x-app', component(app, { useShadowDOM: false }));

Resources

CSS

ShadowDOM

For web components, styles can be set within the shadowDOM and will be encapsulated within the component.

CSS-in-JS

A framework agnostic CSS-in-JS solution like csz can be used to generate scoped styles for custom elements without a shadowDOM.

Alternatively create component scoped global styles with a convention like BEM and remove the need for JS to scope and load styles at runtime.

import { component, html } from 'https://cdn.skypack.dev/haunted';
import css from 'https://cdn.skypack.dev/csz';

const customElement = () => {
  return html`
    <div class=${
      css`
        color: rebeccapurple;
        background-color: black;
        padding: 20px;
      `
    }>
      Custom element with scoped runtime generated styles!
    </div>
  `;
};

customElements.define('x-custom-element', component(customElement, { useShadowDOM: false }));

Global CSS

Basic global styles are provided by default (but can be disabled through the setup CLI) with tools to watch for changes and concatenate the individual files into a single minified styles.css.

The concatenating happens in order from broad to specific styles based on the folder they're in (in the following order): settings, global, elements, components, utilities.

State

Haunted provides internal component state management (same as react hooks).

Global state can be handled with redux and boilerplate files can be generated through the setup step or by running:

vr create-redux

Routing

Routing examples using router-component.

<head>
  <script src="https://cdn.skypack.dev/router-component"></script>
  <script src="js/components/firstPage/firstPage.js"></script>
  <script src="js/components/secondPage/secondPage.js"></script>
  <script src="js/components/pageNotFound/pageNotFound.js"></script>
</head>
<body>
  <router-component>
    <x-first-page path="^/(index.html)?$"></x-first-page>
    <x-second-page path="second-page"></x-second-page>
    <x-page-not-found path=".*"></x-page-not-found>
  </router-component>
</body>
import { component, html } from 'https://cdn.skypack.dev/haunted';
import 'https://cdn.skypack.dev/router-component';
import '../firstPage/firstPage.js';
import '../secondPage/secondPage.js';
import '../pageNotFound/pageNotFound.js';

const app = () => {
  return html`
    <router-component>
      <x-first-page path="^/(index.html)?$"></x-first-page>
      <x-second-page path="second-page"></x-second-page>
      <x-page-not-found path=".*"></x-page-not-found>
    </router-component>
  `;
};

customElements.define('x-app', component(app, { useShadowDOM: false }));

Modules / Bundle

Bundling is not really a thing in Deno (yet) so the best option seems to be a full-scale native es module approach (which is a good thing in my opinion anyway).

Deno has a bundler that works out of the box but it's not at all suitable for production builds. The resulting frontend SystemJS bundled file produced by the internal bundler is massive (lots of module loader boilerplate and no tree-shaking).

This is a great starting point for exploring how browsers handle es modules:
https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/

Deno bundling v3:
denoland/deno#4549

Possible option for bundling:
https://github.com/denofn/denopack