Mødernity is an 11ty-based starter built specifically for remote, open-source and volunteer-driven web projects.
It is a good fit for teams or individuals that require the following:
- The benefits of a modern JAMstack website.
- Contributions from developers across the entire skill spectrum.
Mødernity is made up of beginner-friendly tooling that automatically enforce best practices and have ready-to-go documentation. The primary goal is to make it effortless for contributors to write, read and test code. In addition, pre-configured linting and testing tools makes it easier for you to maintain/merge external changes with confidence.
Mødernity isn't a library or framework. It is a pre-configured bundle of modern, yet beginner-friendly front-end tooling. This means that what you see is what you get. There are no Mødernity config files nor any Mødernity-specific abstractions in this codebase.
Table of Contents:
- Installation
- Getting started
- Managing contributions
- Customizing Mødernity
- Tech-stack
- Future Roadmap
Make sure you have the latest versions of both NodeJS and Git installed. In addition, for real-time linting and syntax-higlighting make sure to add the following to your IDE:
Open either the Unix Shell (OSX or Linux), Powershell (Windows) or your custom terminal complete the following:
-
Navigate to the location where you want to place your project folder.
-
Clone the repository via
git clone https://github.com/schalkventer/modernity.git
-
Install all dependencies via
npm install
. -
Replace delete
README.md
and renameREADME.md.prod
. toREADME.md
-
Run the following commands as needed:
- To build the website into the
dist
folder run:npm run build
. - To start a local development server at http://localhost:8080/ run:
npm start
. - To start the Storybook component-testing framework run:
npm test
. - To check and auto-fix code against defined conventions run:
npm run lint
.
- To build the website into the
Mødernity is built on 11ty and uses lit-html for both client-side and static-html templating.
html-lit, initially created by the Google-backed team working on Polymer, was chosen because it is very easy to learn, while still being extremely expressive. It is currently one of the few well-known templating langauge that relies exclusively on plain JavaScript without any abstractions.
Since our instance of 11ty is set to build our pages from the src/pages
directory and we can create a homepage file at src/page/index.11ty.js
.
In this file we can add the following:
/* src/pages/index.11ty.js */
import { html } from 'lit-html';
import createPage from '../helpers/createPage';
const page = html`
<html>
<head>
<title>Our home page</title>
</head>
<body>
<h1>Hello world!</h1>
<p>This is your very first Mødernity page.</p>
</body>
</html>
`;
export default createPage(page);
That's it! We should see the following if we run yarn start
and navigate to https://localhost:8080:
You can add additional pages by using the same 11ty convention for generating pages. For example:
src/pages/index.11ty.js
createswww.example.com
.src/pages/about/index.11ty.js
createswww.example.com/about
.src/pages/product/potato-cat/index.11ty.js
createswww.example.com/product/potato-cat
However, after you've added several pages you start to notice that it becomes quite cumbersome to add all the surrounding HTML tags everytime (especially as our site grows).
Luckily this can be solved by wrapping these tags in a shareable component. Let us create a component named 'wrapper.js' (note that we only need to add 11ty.js
for files that create website pages). You will notice that all a component is, is a run-of-the-mill JavaScript function that return HTML based on the context and children passed to it.
/* src/components/wrapper/index.js */
import { html } from 'lit-html';
export default (context, children) => html`
<html>
<head>
<title>${context.title}</title>
</head>
<body>
${children}
</body>
</html>
`;
We can then change our homepage as follows:
/* src/pages/index.11ty.js */
import { html } from 'lit-html';
import createPage from '../helpers/createPage';
import wrapper from '../components/wrapper';
const content = html`
<h1>Hello world!</h1>
<p>This is your very first Mødernity page.</p>
`;
const page = html`
${wrapper({ title: 'Homepage' }, content)}
`;
export default createPage(page)
We can even set a component's styling by importing a CSS file and using a helper function included in the Mødernity itself. Also notice the @click=${}
lit-html-specific value below.
/* src/components/button/styles.css */
.button {
display: inline-block;
background: #e0e0e0;
color: rgba(0, 0, 0, 0.87);
padding: 4px 8px;
box-shadow:
0px 3px 1px -2px rgba(0,0,0,0.2),
0px 2px 2px 0px rgba(0,0,0,0.14),
0px 1px 5px 0px rgba(0,0,0,0.12);
}
.button.blue {
background: #1976d2;
color: white;
}
/* src/components/button/index.js */
import { html } from 'lit-html';
import calcStyling from '../../helpers/calcStyling';
import './styles.css';
// `calcStyling` adds a CSS class name if it's condition is true.
const cssClasses = calcStyling({
'button': true,
'blue': context.primary === true,
})
// `<button>` will have the following classes: "button blue"
export default (context, content) => html`
<button
class="${cssFromContext(context)}
@click=${context.handleClick}
>
Click me!
</button>
`;
Components can also be nested inside other components:
// src/pages/index.11ty.js
import { html } from 'lit-html';
import wrapper from '../components/wrapper';
import button from '../components/button';
const handleClick = () => alert('Button was clicked!');
const content = html`
<h1>Hello world!</h1>
<p>This is your very first Mødernity page.</p>
${button({ primary: true, handleClick })}
`;
export default html`
${wrapper({ title: 'Homepage' }, content)}
`;
Now for the part where lit-html really shines! We can effortlessly change the template real-time on the client-side. This is where lit-html really shines over other beginner-friendly templating languages! We can use the included updatePage
helper function to make real-time updates to a page as follows.
Note that dynamic values should only be stored in page level .11ty.js
files and not in components.
// src/pages/index.11ty.js
import { html, render } from 'lit-html';
import updatePage from '../helpers/updatePage';
import createPage from '../helpers/createPage';
import wrapper from '../components/wrapper';
import button from '../components/button';
let primary = true;
const handleClick = () => {
primary = !primary;
updatePage();
}
const content = html`
<h1>Hello world!</h1>
<p>This is your very first Mødernity page.</p>
${button({ primary })}
`;
const page = html`
${wrapper({ title: 'Homepage' }, content)}
`;
export default createPage(page);
Lastly, if we want to update a value once the JavaScript loads we can do this following with the checkIfPageLoaded
helper function. This is useful if you want to load data from a remote endpoint, the localStorage API or even if you want to add dynamic data (for example the current date) once the page is loaded.
In the following example we are saving the state of the button in the localStorage browser API and only showing the button once we checked the localStorage value:
// src/pages/index.11ty.js
import { html, render } from 'lit-html';
import updatePage from '../helpers/updatePage';
import createPage from '../helpers/createPage';
import checkIfPageLoaded from '../helpers/checkIfPageLoaded';
import condition from '../helpers/condition';
import wrapper from '../components/wrapper';
import button from '../components/button';
let primary = undefined;
if (checkIfPageLoaded()) {
primary = localStorage.get('primary') === 'true';
}
const handleClick = () => {
localStorage.set('primary', !primary)
primary = !primary;
updatePage();
}
const content = html`
<h1>Hello world!</h1>
<p>This is your very first Mødernity page.</p>
${primary !== undefined ? button({ primary }) : ''}
`;
const page = html`
${wrapper({ title: 'Homepage' }, content)}
`;
export default createPage(page);
...
Mødernity is guided by three core principles:
- Readability: Developers should be able to automatically enforce project-defined conventions at all times.
- Accesibility: Developers should require only limited HTML, CSS and JavaScript experience to contribute.
- Transparency: Should use modern, well-documented third-party tooling instead of bespoke opaque abstractions.
11ty conventions pending....
lit-html is tiny library that allows developers to write both client-side and static-html by using regular JavaScript syntax.
Prettier conventions pending....
All Sass files are linted via Stylelint with the following plugins:
BEM CSS methodology is enforced to keep styling consistent with the underlying Material Design Components styling. If you are not familiar with BEM please consult the The BEM quickstart documentation.
All Sass files are linted via ESLint with the following plugins:
BEM CSS methodology is enforced to keep styling consistent with the underlying Material Design Components styling. If you are not familiar with BEM please consult the The BEM quickstart documentation.
Husky conventions pending...
- Add ability to install starter with NPX.
- Add built-in support for native web-components.
- Adds progressively enhanced SPA-like behaviour (similar to Gatsby/Gridsome).
- Add built-in basic service worker.