1
0
mirror of https://github.com/locomotivemtl/locomotive-boilerplate.git synced 2026-01-15 00:55:08 +08:00

37 Commits

Author SHA1 Message Date
Lucas Vallenet
2c19d14765 Rename config css class 2022-05-25 13:36:51 +02:00
Lucas Vallenet
f1544d24f9 Update config.js vars and export 2022-05-24 14:23:38 +02:00
Lucas Vallenet
26f261ce47 Remove js error 2022-05-20 14:25:22 +02:00
Lucas Vallenet
ce0788bdc9 Add Barba 2022-05-20 14:19:35 +02:00
Lucas
32ba1c0eeb Merge pull request #114 from locomotivemtl/refactor/css-comments
Standardize scss comments
2022-05-18 13:58:18 +02:00
Lucas Vallenet
5199ee2025 Double comments / Paragraphs and lists line-breaks / Wrap block comments / File-level annotations syntax / Remove leading comments 2022-05-17 12:08:01 +02:00
Lucas Vallenet
d6c8fdac23 Standardize scss comments 2022-05-12 16:15:44 +02:00
Deven Caron
15a4f2e64f Merge pull request #111 from locomotivemtl/feature/font-face
Update fontface system with scss lists, function and mixin
2022-05-12 09:41:33 -04:00
Lucas Vallenet
9e35894ef1 Revert comments changes to apply to document.scss only 2022-05-12 11:01:51 +02:00
Lucas Vallenet
cabeba55c0 Fix font-face $dir error | Update description comments to SASS synthax 2022-05-02 16:56:25 +02:00
Lucas Vallenet
47974a77a9 - Merge _fonts.scss to _page.scss
- Rename _page.scss to _document.scss
- Rename $fontfaces to $font-faces for consistency
- Document _fonts.scss mixins and functions
- Create mixins @font-face for one face
2022-05-02 10:30:35 +02:00
Lucas Vallenet
e8b2a86798 Update fontface system with scss lists, function and mixin 2022-04-29 13:51:46 +02:00
Chauncey McAskill
53beae26b0 Fix missing link in Development documentation
Amends 9a01c0f17f
2022-03-24 13:59:02 -04:00
Chauncey McAskill
9a01c0f17f Update README and documentation
Changed:
- Features, Installation, and Development sections in README.
- Introduction, Configuration, and Tasks in Development.
- Heading levels in Technologies.

Fixed:
- Anchors to option sections in Development.
2022-03-24 13:57:28 -04:00
Deven Caron
97d9f1ec00 Merge pull request #107 from locomotivemtl/feature/documentation
Rewrite README and add documentation
2022-03-24 10:03:58 -04:00
Chauncey McAskill
8b8b267e9d Rewrite README
And split sections from README into dedicated documentation files.

Added:
- "Features" section to summarize the boilerplate's architecture.
- "Getting Started" section to describe how to create a project from the boilerplate.
- "Development" documentation to describe how NPM dependencies, configuring assets and tasks.
- "Technologies" documentation to describe CSS, JS, Locomotive Scroll, ModularLoad, ModularJS.

Changed:
- Moved section "Configuration" to 'docs/development.md'.
- Moved sections "Styles", "Scripts", "Page transitions", and "Scroll detection" to 'docs/technologies.md'.

TODO:
- Move "Environment configuration" section from "Development" to 'feature/local-config' branch.
2022-03-24 09:58:40 -04:00
Deven Caron
822cf7daa8 Merge pull request #106 from locomotivemtl/feature/local-config
Add support for loconfig.local.json
2022-03-24 09:55:02 -04:00
Chauncey McAskill
7f452f1fcc Add example of loconfig.local.json 2022-03-23 14:06:11 -04:00
Chauncey McAskill
5010560ee3 Improve HTTPS/Proxy URL support in watch.js
Added logic to prepend "https://" to the proxy URL if missing.
2022-03-23 13:13:46 -04:00
Chauncey McAskill
0cfb3fbc7d Add support for loconfig.local.json
If a 'loconfig.local.json' file is present (ignored by git), its settings will be merged with those in 'loconfig.json'. Useful for customizing localhost development (see example below).

Added:
- Utility 'config.js' to prepare build settings.
- Function 'merge()' for recursively merging objects and concatenating arrays.

Example:

```json
{
    "paths": {
        "url": "yourlocal.dev"
    },
    "server": {
        "open": true,
        "https": {
            "key": "~/.config/valet/Certificates/{% paths.url %}.key",
            "cert": "~/.config/valet/Certificates/{% paths.url %}.crt"
        }
    }
}
```
2022-03-22 16:40:51 -04:00
Chauncey McAskill
86f88c3f14 Refactor watch.js
Switched to BrowserSync's recommended post-2.0.0 syntax.

Organized logic into functions for easier reading.

Add support for customizing the BrowserSync server from 'loconfig.json'.

Example:

```json
"server": {
    "open": true,
    "https": {
        "key": "~/.config/valet/Certificates/{% paths.url %}.key",
        "cert": "~/.config/valet/Certificates/{% paths.url %}.crt"
    }
}
```
2022-03-22 16:33:19 -04:00
Chauncey McAskill
48bd911804 Refactor template.js
Added:
- Function `resolve()` to process any template tags (in a string) in objects and arrays.

Changed:
- Renamed function `template()` to `resolveValue()`.
- Replaced default export `resolveValue()` with new function `resolve()`.
2022-03-22 16:33:19 -04:00
Chauncey McAskill
d49d3eabb2 Add file comment to notification.js 2022-03-22 16:32:24 -04:00
Chauncey McAskill
28aa6c7de6 Update NPM dependencies
Resolves #105

Updated:
- autoprefixer v10.4.2 → v10.4.4
- browser-sync v2.27.7 → v2.27.9
- esbuild v0.14.21 → v0.14.27
- postcss v8.4.6 → v8.4.12
2022-03-21 17:24:10 -04:00
Chauncey McAskill
38dd28832e Update NPM dependencies
Resolves #102

Updated:
- esbuild v0.14.14 → v0.14.21
- locomotive-scroll v4.1.3 → v4.1.4
- node-notifier v10.0.0 → v10.0.1
- postcss v8.4.5 → v8.4.6
2022-02-15 09:27:37 -05:00
Chauncey McAskill
757c26c772 Fix glob.js file comment 2022-02-15 09:16:03 -05:00
Chauncey McAskill
5e07473396 Make glob optional in concats.js
Added:
- Condition to process includes with glob if it's available.

Changed:
- Utility 'glob.js' to not throw an error if a glob function is unavailable.
2022-02-08 15:18:18 -05:00
Chauncey McAskill
c9056b27d8 Add support for removing duplicates in concats.js
Added:
- Argument `concatOptions` to `concatFiles()` function to customize concatenation.
- Option 'removeDuplicates' to `concatOptions` to remove duplicate paths from the array of globbed paths. Defaults to `true`.

The 'removeDuplicates' is useful for customizing the order of files to concatenate, in which globbing will usually sort paths alphabetically.

This option is convenient when the installed glob library does not support removing duplicates on their own (feature supported in 'fast-glob' and 'glob').

Example:

```
/assets/scripts/vendors/a.js
/assets/scripts/vendors/b.js
/assets/scripts/vendors/c.js
/assets/scripts/vendors/d.js
```

```json
"concats": [
    {
        "includes": [
            "{% paths.scripts.src %}/vendors/c.js",
            "{% paths.scripts.src %}/vendors/*.js"
        ],
        "outfile": "{% paths.scripts.dest %}/vendors.js"
    }
]
```
2022-02-08 14:46:34 -05:00
Chauncey McAskill
38a6c73d2f Add support for customizing glob in concats.js
Added:
- Argument `globOptions` to `concatFiles()` function to customize glob library.
2022-02-08 14:40:00 -05:00
Chauncey McAskill
9d18205b0f Update NPM dependencies
Updated:
- autoprefixer v10.4.0 → v10.4.2
- esbuild v0.13.12 → v0.14.14
- node-sass v6.0.1 → v7.0.1
- postcss v8.3.11 → v8.4.5
2022-01-28 16:00:57 -05:00
Chauncey McAskill
1e7e90c8aa Add support for custom task labels
If ever the basename from the outfile or outdir is an insufficient description.

Example:

```json
"concats": [
    {
        "label": "third-parties",
        "includes": [
            "{% paths.scripts.src %}/vendors/*.js"
        ],
        "outfile": "{% paths.scripts.dest %}/vendors.js"
    }
]
```
2021-12-17 10:39:35 -05:00
Chauncey McAskill
47007cddaf Fix glob.js
Amends 8dec2c69fe

Fixed:
- Forgot to rename all occurrences of 'modules' with 'candidates'.
2021-12-03 15:10:21 -05:00
Chauncey McAskill
5dd3fa843f Add support for watching multiple view paths
Added routine to convert `paths.views` into an array.

Supports a single path as a string, a map of paths, or a list of paths:

```json
"views": "./views/boilerplate/template"
```

```json
"views": {
    "src": "./views/boilerplate/template"
}
```

```json
"views": [
    "./views/boilerplate/template"
]
```
2021-12-03 11:55:35 -05:00
Chauncey McAskill
a5623d3122 Improve comments in watch.js 2021-12-03 11:52:27 -05:00
Chauncey McAskill
67e1fae8f4 Update block comment in styles.js 2021-11-03 17:08:38 -04:00
Chauncey McAskill
a95fe4523c Update postcss.js
Remove extraneous `null` assignment on optional exports.
2021-11-03 16:50:08 -04:00
Chauncey McAskill
eadc414329 Fix merging of default options for PostCSS
Amends 9e3d304654
2021-11-03 16:16:08 -04:00
63 changed files with 9320 additions and 3068 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
node_modules
.DS_Store
Thumbs.db
loconfig.*.json
!loconfig.example.json

257
README.md
View File

@@ -4,230 +4,95 @@
</a>
</p>
<h1 align="center">Locomotive Boilerplate</h1>
<p align="center">Front-end boilerplate for projects by Locomotive.</p>
<p align="center">Front-end boilerplate for projects by <a href="https://locomotive.ca/">Locomotive</a>.</p>
## Requirements
## Features
| Name | Version |
| ---------- | -------- |
| [Node] | >= 14.17 |
| [NPM] | >= 6.0 |
* Uses a custom [task runner](docs/development.md) for handling assets.
* Uses [BrowserSync] for fast development and testing in browsers.
* Uses [Sass] for a feature rich superset of CSS.
* Uses [ESBuild] for extremely fast processing of JS/ES modules.
* Uses [SVG Mixer] for processing SVG files and generating spritesheets.
* Uses [ITCSS] for a sane and scalable CSS architecture.
* Uses [Locomotive Scroll] for smooth scrolling with parallax effects.
[Node]: https://nodejs.org/
[NPM]: https://npmjs.com/
Learn more about [languages and technologies](docs/technologies.md).
You can use [nvm](https://github.com/nvm-sh/nvm) to install the node version in `.nvmrc`.
## Getting started
## Installation
Make sure you have the following installed:
* [Node] — at least 14.17, the latest LTS is recommended.
* [NPM] — at least 6.0, the latest LTS is recommended.
> 💡 You can use [NVM] to install and use different versions of Node via the command-line.
```sh
npm i
# Clone the repository.
git clone https://github.com/locomotivemtl/locomotive-boilerplate.git my-new-project
# Enter the newly-cloned directory.
cd my-new-project
```
## Usage
Then replace the original remote repository with your project's repository.
```sh
# start it
npm start
```
Then update the following files to suit your project:
## Configuration
There are a few occurrences that should be renamed for your project:
* [package.json](package.json):
* [`README.md`](README.md):
The file you are currently reading.
* [`package.json`](package.json):
* Package name: `@locomotivemtl/boilerplate`
* Package title: `Locomotive Boilerplate`
* [package-lock.json](package-lock.json):
* [`package-lock.json`](package-lock.json):
* Package name: `@locomotivemtl/boilerplate`
* [loconfig.json](loconfig.json):
* Browser Sync proxy URL: `locomotive-boilerplate.test`
Remove `paths.url` to use Browser Sync's built-in server which uses `paths.dest`.
* [`loconfig.json`](loconfig.json):
* BrowserSync proxy URL: `locomotive-boilerplate.test`
Remove `paths.url` to use BrowserSync's built-in server which uses `paths.dest`.
* View path: `./views/boilerplate/template`
* [environment.js](assets/scripts/utils/environment.js):
* [`environment.js`](assets/scripts/utils/environment.js):
* Application name: `Boilerplate`
* [site.webmanifest](www/site.webmanifest):
* [`site.webmanifest`](www/site.webmanifest):
* Manifest name: `Locomotive Boilerplate`
* Manifest short name: `Boilerplate`
* HTML files:
* Page title: `Locomotive Boilerplate`
## Build
## Installation
#### Tasks
```sh
# watch
# Switch to recommended Node version from .nvmrc
nvm use
# Install dependencies from package.json
npm install
```
## Development
```sh
# Start development server, watch for changes, and compile assets
npm start
# build
# Compile and minify assets
npm run build
```
## Styles
Learn more about [development and building](docs/development.md).
[Sass](https://github.com/sass/node-sass) is our CSS preprocessor. [Autoprefixer](https://github.com/postcss/autoprefixer) is also included.
## Documentation
#### Architecture
* [Development and building](docs/development.md)
* [Languages and technologies](docs/technologies.md)
[ITCSS](https://github.com/itcss) is our CSS architecture.
* `settings`: Global variables, site-wide settings, config switches, etc.
* `tools`: Site-wide mixins and functions.
* `generic`: Low-specificity, far-reaching rulesets (e.g. resets).
* `elements`: Unclassed HTML elements (e.g. `a {}`, `blockquote {}`, `address {}`).
* `objects`: Objects, abstractions, and design patterns (e.g. `.o-layout {}`).
* `components`: Discrete, complete chunks of UI (e.g. `.c-carousel {}`).
* `utilities`: High-specificity, very explicit selectors. Overrides and helper
classes (e.g. `.u-hidden {}`)
[_source_](https://github.com/inuitcss/inuitcss#css-directory-structure)
#### Naming
We use a simplified [BEM](https://github.com/bem) syntax.
```
.block .block_element -modifier
```
#### Namespaces
We namespace our classes for more [transparency](https://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/).
* `o-`: Object that it may be used in any number of unrelated contexts to the one you can currently see it in. Making modifications to these types of class could potentially have knock-on effects in a lot of other unrelated places.
* `c-`: Component is a concrete, implementation-specific piece of UI. All of the changes you make to its styles should be detectable in the context youre currently looking at. Modifying these styles should be safe and have no side effects.
* `u-`: Utility has a very specific role (often providing only one declaration) and should not be bound onto or changed. It can be reused and is not tied to any specific piece of UI.
* `s-`: Scope creates a new styling context. Similar to a Theme, but not necessarily cosmetic, these should be used sparingly—they can be open to abuse and lead to poor CSS if not used wisely.
* `is-`, `has-`: Is currently styled a certain way because of a state or condition. It tells us that the DOM currently has a temporary, optional, or short-lived style applied to it due to a certain state being invoked.
[_source_](https://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/#the-namespaces)
#### Example
```html
<div class="c-block -large">
<div class="c-block_layout o-layout">
<div class="o-layout_item u-1/2@from-medium">
<div class="c-block_heading o-h -medium">Heading</div>
</div>
<div class="o-layout_item u-1/2@from-medium">
<a class="c-block_button o-button -outline" href="#">Button</a>
</div>
</div>
</div>
```
```scss
.c-block {
&.-large {
padding: rem(60px);
}
}
.c-block_heading {
@media (max-width: $to-medium) {
.c-block.-large & {
margin-bottom: rem(40px);
}
}
}
```
## Scripts
[modularJS](https://github.com/modularorg/modularjs) is a small framework we use on top of ES modules. It compiles with [Rollup](https://github.com/rollup/rollup) and [Babel](https://github.com/babel/babel).
#### Why
* Automatically init visible modules.
* Easily call other modules methods.
* Quickly set scoped events with delegation.
* Simply select DOM elements scoped in their module.
[_source_](https://github.com/modularorg/modularjs#why)
#### Example
```html
<div data-module-example>
<div data-example="main">
<h2>Example</h2>
</div>
<button data-example="load">More</button>
</div>
```
```js
import { module } from 'modujs';
export default class extends module {
constructor(m) {
super(m);
this.events = {
click: {
load: 'loadMore'
}
}
}
loadMore() {
this.$('main')[0].classList.add('is-loading');
}
}
```
[Learn more](https://github.com/modularorg/modularjs)
## Page transitions
[modularLoad](https://github.com/modularorg/modularload) is used for page transitions and lazy loading.
#### Example
```html
<nav>
<a href="/">Home</a>
<a href="/page" data-load="transitionName">Page</a>
</nav>
<div data-load-container>
<img data-load-src="assets/images/hello.jpg">
</div>
```
```js
import modularLoad from 'modularload';
this.load = new modularLoad({
enterDelay: 300,
transitions: {
transitionName: {
enterDelay: 450
}
}
});
```
[Learn more](https://github.com/modularorg/modularload)
## Scroll detection
[Locomotive Scroll](https://github.com/locomotivemtl/locomotive-scroll) is used for elements in viewport detection and smooth scrolling with parallax.
#### Example
```html
<div data-module-scroll>
<div data-scroll>Trigger</div>
<div data-scroll data-scroll-speed="1">Parallax</div>
</div>
```
```js
import LocomotiveScroll from 'locomotive-scroll';
this.scroll = new LocomotiveScroll({
el: this.el,
smooth: true
});
````
[Learn more](https://github.com/locomotivemtl/locomotive-scroll)
[BrowserSync]: https://npmjs.com/package/browser-sync
[ESBuild]: https://npmjs.com/package/esbuild
[ITCSS]: https://itcss.io/
[Locomotive Scroll]: https://npmjs.com/package/locomotive-scroll
[modularJS]: https://npmjs.com/package/modujs
[modularLoad]: https://npmjs.com/package/modularload
[Sass]: https://sass-lang.com/
[SVG Mixer]: https://npmjs.com/package/svg-mixer
[Node]: https://nodejs.org/
[NPM]: https://npmjs.com/
[NVM]: https://github.com/nvm-sh/nvm

View File

@@ -1,7 +1,8 @@
import modular from 'modujs';
import * as modules from './modules';
import globals from './globals';
import { html } from './utils/environment';
import { $html } from './utils/dom';
import config from './config';
const app = new modular({
modules: modules
@@ -28,8 +29,8 @@ function init() {
app.init(app);
html.classList.add('is-loaded');
html.classList.add('is-ready');
html.classList.remove('is-loading');
$html.classList.add(config.CSS_CLASS.LOADED);
$html.classList.add(config.CSS_CLASS.READY);
$html.classList.remove(config.CSS_CLASS.LOADING);
}

15
assets/scripts/config.js Normal file
View File

@@ -0,0 +1,15 @@
const env = process.env.NODE_ENV
export default config = Object.freeze({
// Environments
ENV: env,
IS_PROD: env === 'production',
IS_DEV: env === 'development',
// CSS class names
CSS_CLASS: {
LOADING: 'is-loading',
READY: 'is-ready',
LOADED: 'is-loaded',
},
})

View File

@@ -1,5 +1,6 @@
import { module } from 'modujs';
import modularLoad from 'modularload';
import barba from '@barba/core';
import config from '../config'
export default class extends module {
constructor(m) {
@@ -7,16 +8,20 @@ export default class extends module {
}
init() {
const load = new modularLoad({
enterDelay: 0,
transitions: {
customTransition: {}
}
});
load.on('loaded', (transition, oldContainer, newContainer) => {
this.call('destroy', oldContainer, 'app');
this.call('update', newContainer, 'app');
barba.init({
debug: config.IS_DEV,
schema: {
prefix: 'data-load',
},
transitions: [{
name: 'default-transition',
leave: (data) => {
this.call('destroy', data.current.container, 'app');
},
enter: (data) => {
this.call('update', data.next.container, 'app');
}
}]
});
}
}

View File

@@ -0,0 +1,4 @@
const $html = document.documentElement;
const $body = document.body;
export { $html, $body };

View File

@@ -1,8 +0,0 @@
const APP_NAME = 'Boilerplate';
const DATA_API_KEY = '.data-api';
const html = document.documentElement;
const body = document.body;
const isDebug = html.hasAttribute('data-debug');
export { APP_NAME, DATA_API_KEY, html, body, isDebug };

View File

@@ -1,6 +1,5 @@
import { isFunction } from './is';
import { arrayContains, findByKeyValue, removeFromArray } from './array';
import { $document, $window, $html, $body } from './environment';
const CALLBACKS = {
hidden: [],
@@ -22,7 +21,7 @@ const PREFIX = 'v-';
let UUID = 0;
// Main event
$document.on('visibilitychange', function(event) {
document.addEventListener('visibilitychange', function(event) {
if (document.hidden) {
onDocumentChange('hidden');
} else {

View File

@@ -1,3 +1,7 @@
// ==========================================================================
// Components / Buttons
// ==========================================================================
.c-button {
padding: rem(15px) rem(20px);
background-color: lightgray;

View File

@@ -1,6 +1,7 @@
// ==========================================================================
// Form
// Components / Form
// ==========================================================================
.c-form {
}
@@ -12,6 +13,7 @@
// Label
// ==========================================================================
.c-form_label {
display: block;
margin-bottom: rem(10px);
@@ -19,6 +21,7 @@
// Input
// ==========================================================================
$input-icon-color: 424242; // No #
.c-form_input {
@@ -41,6 +44,7 @@ $input-icon-color: 424242; // No #
// Checkbox
// ==========================================================================
$checkbox: rem(18px);
$checkbox-icon-color: $input-icon-color;
@@ -108,6 +112,7 @@ $checkbox-icon-color: $input-icon-color;
// Radio
// ==========================================================================
$radio-icon-color: $input-icon-color;
.c-form_radioLabel {
@@ -129,6 +134,7 @@ $radio-icon-color: $input-icon-color;
// Select
// =============================================================================
$select-icon: rem(40px);
$select-icon-color: $input-icon-color;
@@ -163,6 +169,7 @@ $select-icon-color: $input-icon-color;
// Textarea
// =============================================================================
.c-form_textarea {
@extend .c-form_input;

View File

@@ -1,3 +1,7 @@
// ==========================================================================
// Components / Headings
// ==========================================================================
.c-heading {
line-height: $line-height-h;
margin-bottom: rem(30px);

View File

@@ -1,3 +1,7 @@
// ==========================================================================
// Components / Scrollbar
// ==========================================================================
.c-scrollbar {
position: absolute;
right: 0;

View File

@@ -1 +1,5 @@
// ==========================================================================
// Critical CSS
// ==========================================================================
$assets-path: "assets/";

View File

@@ -1,19 +1,22 @@
// ==========================================================================
// Base / Page
// Elements / Document
// ==========================================================================
//
// Simple page-level setup.
//
// 1. Set the default `font-size` and `line-height` for the entire project,
// sourced from our default variables.
// 1. Include web fonts
// 2. Ensure the page always fills at least the entire height of the viewport.
//
// 3. Set the default `font-size` and `line-height` for the entire project,
// sourced from our default variables.
@include font-faces($font-faces, $font-dir); // [1]
html {
min-height: 100%; /* [2] */
min-height: 100%; // [2]
line-height: $line-height;
font-family: ff("sans");
color: $color;
font-family: $font-family;
line-height: $line-height; /* [1] */
line-height: $line-height; // [3]
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@@ -30,7 +33,7 @@ html {
}
@media (min-width: $from-large) and (max-width: $to-huge) {
font-size: $font-size; /* [1] */
font-size: $font-size; // [1]
}
@media (min-width: $from-huge) and (max-width: $to-gigantic) {

View File

@@ -1,36 +0,0 @@
// ==========================================================================
// Base / Fonts
// ==========================================================================
// @include font-face(
// $font-foobar,
// "fonts/Foobar/Regular"
// ) {
// font-style: normal;
// font-weight: 400;
// }
// @include font-face(
// $font-foobar,
// "fonts/Foobar/Medium"
// ) {
// font-style: normal;
// font-weight: 500;
// }
// @include font-face(
// $font-foobar,
// "fonts/Foobar/Semibold"
// ) {
// font-style: normal;
// font-weight: 600;
// }
// @include font-face(
// $font-bazqux,
// "fonts/Bazqux/Regular",
// ("eot", "woff2", "woff", "ttf")
// ) {
// font-style: normal;
// font-weight: 400;
// }

View File

@@ -2,34 +2,33 @@
// Generic / Buttons
// ==========================================================================
//
// 1. Allow us to style box model properties.
// 2. Fixes odd inner spacing in IE7.
// 3. Reset/normalize some styles.
// 4. Line different sized buttons up a little nicer.
// 5. Make buttons inherit font styles (often necessary when styling `input`s as buttons).
// 6. Force all button-styled elements to appear clickable.
//
button,
.c-button {
@include u-hocus {
text-decoration: none;
}
display: inline-block; /* [1] */
overflow: visible; /* [2] */
margin: 0; /* [3] */
display: inline-block; // [1]
overflow: visible; // [2]
margin: 0; // [3]
padding: 0;
outline: 0;
border: 0;
background: none transparent;
color: inherit;
vertical-align: middle; /* [4] */
text-align: center; /* [3] */
vertical-align: middle; // [4]
text-align: center; // [3]
text-decoration: none;
text-transform: none;
font: inherit; /* [5] */
font: inherit; // [5]
line-height: normal;
cursor: pointer; /* [6] */
cursor: pointer; // [6]
user-select: none;
}

View File

@@ -31,11 +31,11 @@ select {
color: inherit;
}
// Remove Firefox :focus dotted outline, breaks color inherit
// &:-moz-focusring {
// color: transparent;
// text-shadow: 0 0 0 #000000; // Text :focus color
// }
// // Remove Firefox :focus dotted outline, breaks color inherit
// // &:-moz-focusring {
// // color: transparent;
// // text-shadow: 0 0 0 #000000; // Text :focus color
// // }
}
textarea {

View File

@@ -6,11 +6,10 @@ html {
box-sizing: border-box;
}
//
// Add the correct display in IE 10-.
// 1. Add the correct display in IE.
//
template, /* [1] */
template, // [1]
[hidden] {
display: none;
}
@@ -62,11 +61,10 @@ h1, h2, h3, h4, h5, h6 {
margin: 0;
}
/**
* 1. Single taps should be dispatched immediately on clickable elements
*/
// 1. Single taps should be dispatched immediately on clickable elements
a, area, button, input, label, select, textarea, [tabindex] {
-ms-touch-action: manipulation; /* [1] */
-ms-touch-action: manipulation; // [1]
touch-action: manipulation;
}

View File

@@ -2,57 +2,51 @@
// Generic / Media
// ==========================================================================
//
// 1. Setting `vertical-align` removes the whitespace that appears under `img`
// elements when they are dropped into a page as-is. Safer alternative to
// using `display: block;`.
//
audio,
canvas,
iframe,
img,
svg,
video {
vertical-align: middle; /* [1] */
vertical-align: middle; // [1]
}
//
// Add the correct display in iOS 4-7.
//
audio:not([controls]) {
display: none;
height: 0;
}
//
// 2. Fluid media for responsive purposes.
//
img,
svg {
max-width: 100%; /* [2] */
max-width: 100%; // [2]
height: auto;
//
// 4. If a `width` and/or `height` attribute have been explicitly defined, lets
// not make the image fluid.
//
&[width], /* [4] */
// 4. If a `width` and/or `height` attribute have been explicitly defined,
// lets not make the image fluid.
&[width], // [4]
&[height] {
/* [4] */
// [4]
max-width: none;
}
}
//
// 4. Offset `alt` text from surrounding copy.
//
img {
font-style: italic; /* [4] */
font-style: italic; // [4]
}
//
// 5. SVG elements should fallback to their surrounding text color.
//
svg {
fill: currentColor; /* [5] */
fill: currentColor; // [5]
}

View File

@@ -4,6 +4,7 @@
// Settings
// ==========================================================================
@import "settings/config.eases";
@import "settings/config.colors";
@import "settings/config";
@@ -11,6 +12,7 @@
// ==========================================================================
// Tools
// ==========================================================================
@import "tools/functions";
@import "tools/mixins";
@import "tools/fonts";
@@ -20,6 +22,7 @@
// Generic
// ==========================================================================
@import "node_modules/normalize.css/normalize";
@import "generic/generic";
@import "generic/media";
@@ -28,11 +31,12 @@
// Elements
// ==========================================================================
@import "elements/fonts";
@import "elements/page";
@import "elements/document";
// Objects
// ==========================================================================
@import "objects/scroll";
@import "objects/container";
@import "objects/ratio";
@@ -46,6 +50,7 @@
// Components
// ==========================================================================
@import "components/scrollbar";
@import "components/heading";
@import "components/button";
@@ -57,6 +62,7 @@
// Utilities
// ==========================================================================
@import "utilities/ratio";
@import "utilities/widths";
// @import "utilities/align";

View File

@@ -2,7 +2,6 @@
// Objects / Container
// ==========================================================================
//
// Page-level constraining and wrapping elements.
//
// > In programming languages the word *container* is generally used for structures
@@ -10,7 +9,6 @@
// > A *wrapper* instead is something that wraps around a single object to provide
// more functionalities and interfaces to it.
// @link http://stackoverflow.com/a/13202141/140357
//
.o-container {
margin-right: auto;

View File

@@ -2,45 +2,42 @@
// Objects / Crop
// ==========================================================================
//
// @link https://github.com/inuitcss/inuitcss/blob/19d0c7e/objects/_objects.crop.scss
//
// A list of cropping ratios that get generated as modifier classes.
$crop-ratios: (
(2:1),
(4:3),
(16:9),
) !default;
/**
* Provide a cropping container in order to display media (usually images)
* cropped to certain ratios.
*
* 1. Set up a positioning context in which the image can sit.
* 2. This is the crucial part: where the cropping happens.
*/
// Provide a cropping container in order to display media (usually images)
// cropped to certain ratios.
//
// 1. Set up a positioning context in which the image can sit.
// 2. This is the crucial part: where the cropping happens.
.o-crop {
position: relative; /* [1] */
position: relative; // [1]
display: block;
overflow: hidden; /* [2] */
overflow: hidden; // [2]
}
/**
* Apply this class to the content (usually `img`) that needs cropping.
*
* 1. Images default positioning is top-left in the cropping box.
* 2. Make sure the media doesnt stop itself too soon.
*/
// Apply this class to the content (usually `img`) that needs cropping.
//
// 1. Images default positioning is top-left in the cropping box.
// 2. Make sure the media doesnt stop itself too soon.
.o-crop_content {
position: absolute;
top: 0; /* [1] */
left: 0; /* [1] */
max-width: none; /* [2] */
top: 0; // [1]
left: 0; // [1]
max-width: none; // [2]
// We can position the media in different locations within the cropping area.
/**
* We can position the media in different locations within the cropping area.
*/
&.-right {
right: 0;
left: auto;
@@ -60,22 +57,20 @@ $crop-ratios: (
/* stylelint-disable */
//
// Generate a series of crop classes to be used like so:
//
// @example
// <div class="o-crop -16:9">
//
//
.o-crop {
@each $crop in $crop-ratios {
@each $antecedent, $consequent in $crop {
@if (type-of($antecedent) != number) {
@error "`#{$antecedent}` needs to be a number."
@error "`#{$antecedent}` needs to be a number.";
}
@if (type-of($consequent) != number) {
@error "`#{$consequent}` needs to be a number."
@error "`#{$consequent}` needs to be a number.";
}
&.-#{$antecedent}\:#{$consequent} {

View File

@@ -2,41 +2,41 @@
// Objects / Layout
// ==========================================================================
//
// Grid-like layout system.
//
// The layout object provides us with a column-style layout system. This file
// contains the basic structural elements, but classes should be complemented
// with width utilities, for example:
//
// @example
// <div class="o-layout">
// <div class="o-layout_item u-1/1 u-1/3@medium">
// </div>
// <div class="o-layout_item u-1/2 u-1/3@medium">
// </div>
// <div class="o-layout_item u-1/2 u-1/3@medium">
// </div>
// </div>
//
// We can also manipulate entire layout systems by adding a series of modifiers
// to the `.o-layout` block. For example:
//
// @example
// <div class="o-layout -reverse">
//
// This will reverse the displayed order of the system so that it runs in the
// opposite order to our source, effectively flipping the system over.
//
// @example
// <div class="o-layout -[right|center]">
//
// This will cause the system to fill up from either the centre or the right
// hand side. Default behaviour is to fill up the layout system from the left.
//
// @requires tools/layout
// @link https://github.com/inuitcss/inuitcss/blob/0420ba8/objects/_objects.layout.scss
//
////
/// Grid-like layout system.
///
/// The layout object provides us with a column-style layout system. This file
/// contains the basic structural elements, but classes should be complemented
/// with width utilities, for example:
///
/// @example
/// <div class="o-layout">
/// <div class="o-layout_item u-1/1 u-1/3@medium">
/// </div>
/// <div class="o-layout_item u-1/2 u-1/3@medium">
/// </div>
/// <div class="o-layout_item u-1/2 u-1/3@medium">
/// </div>
/// </div>
///
/// We can also manipulate entire layout systems by adding a series of modifiers
/// to the `.o-layout` block. For example:
///
/// @example
/// <div class="o-layout -reverse">
///
/// This will reverse the displayed order of the system so that it runs in the
/// opposite order to our source, effectively flipping the system over.
///
/// @example
/// <div class="o-layout -[right|center]">
///
/// This will cause the system to fill up from either the centre or the right
/// hand side. Default behaviour is to fill up the layout system from the left.
///
/// @requires tools/layout
/// @link https://github.com/inuitcss/inuitcss/blob/0420ba8/objects/_objects.layout.scss
////
.o-layout {
@include o-layout;

View File

@@ -2,14 +2,13 @@
// Objects / Ratio
// ==========================================================================
/**
* Create ratio-bound content blocks, to keep media (e.g. images, videos) in
* their correct aspect ratios.
*
* http://alistapart.com/article/creating-intrinsic-ratios-for-video
*
* 1. Default cropping is a 1:1 ratio (i.e. a perfect square).
*/
// Create ratio-bound content blocks, to keep media (e.g. images, videos) in
// their correct aspect ratios.
//
// http://alistapart.com/article/creating-intrinsic-ratios-for-video
//
// 1. Default cropping is a 1:1 ratio (i.e. a perfect square).
.o-ratio {
position: relative;
display: block;
@@ -17,7 +16,7 @@
&:before {
display: block;
padding-bottom: 100%; /* [1] */
padding-bottom: 100%; // [1]
width: 100%;
content: "";
}

View File

@@ -1,3 +1,7 @@
// ==========================================================================
// Objects / Scroll
// ==========================================================================
.o-scroll {
min-height: 100vh;
}

View File

@@ -1,14 +1,14 @@
// ==========================================================================
// Objects / Tables
// ==========================================================================
.o-table {
width: 100%;
/**
* Force all cells within a table to occupy the same width as each other.
*
* @link https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout#Values
*/
// Force all cells within a table to occupy the same width as each other.
//
// @link https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout#Values
&.-fixed {
table-layout: fixed;
}

View File

@@ -4,21 +4,25 @@
// Palette
// =============================================================================
$white: #FFFFFF;
$black: #000000;
// Specific
// =============================================================================
// Link
$link-color: #1A0DAB;
$link-focus-color: #1A0DAB;
$link-hover-color: darken(#1A0DAB, 10%);
// Selection
$selection-text-color: #3297FD;
$selection-background-color: #FFFFFF;
// Social Colors
// =============================================================================
$facebook-color: #3B5998;
$instagram-color: #E1306C;
$youtube-color: #CD201F;

View File

@@ -1,3 +1,7 @@
// ==========================================================================
// Settings / Config / Eases
// ==========================================================================
$Power1EaseOut: cubic-bezier(0.250, 0.460, 0.450, 0.940);
$Power2EaseOut: cubic-bezier(0.215, 0.610, 0.355, 1.000);
$Power3EaseOut: cubic-bezier(0.165, 0.840, 0.440, 1.000);

View File

@@ -4,6 +4,7 @@
// Context
// =============================================================================
// The current stylesheet context. Available values: frontend, editor.
$context: frontend !default;
@@ -12,15 +13,28 @@ $assets-path: "../" !default;
// Typefaces
// =============================================================================
$font-sans-serif: sans-serif;
$font-dir: "../fonts/";
$font-families: (
"sans": ("Webfont Sans", "Helvetica Neue", Arial, sans-serif),
// "serif": ("Webfont Serif", Georgia, serif)
);
$font-faces: (
// "Webfont Sans" "webfont-sans_regular" 400 normal,
// "Webfont Sans" "webfont-sans_regular-italic" 400 italic,
// "Webfont Serif" "webfont-sans_bold" 700 normal,
);
// Typography
// =============================================================================
// Base
$font-size: 16px;
$line-height: 24px / $font-size;
$font-family: $font-sans-serif;
$color: #222222;
// Headings
$font-size-h1: 36px !default;
$font-size-h2: 28px !default;
@@ -29,6 +43,7 @@ $font-size-h4: 20px !default;
$font-size-h5: 18px !default;
$font-size-h6: 16px !default;
$line-height-h: $line-height;
// Weights
$light: 300;
$normal: 400;
@@ -37,21 +52,25 @@ $bold: 700;
// Transitions
// =============================================================================
$speed: 0.3s;
$easing: $Power2EaseOut;
// Spacing Units
// =============================================================================
$unit: 60px;
$unit-small: 30px;
// Container
// ==========================================================================
// =============================================================================
$container-width: 2000px;
$padding: $unit;
// Breakpoints
// =============================================================================
$from-tiny: 500px !default;
$to-tiny: $from-tiny - 1 !default;
$from-small: 700px !default;

View File

@@ -1,12 +1,14 @@
//
//
// ==========================================================================
// Tools / Family
// ==========================================================================
// DOCS : https://lukyvj.github.io/family.scss/
//
//
/// Select all children from the first to `$num`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
// Select all children from the first to `$num`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
@mixin first($num) {
@if $num == 1 {
&:first-child {
@@ -19,108 +21,118 @@
}
}
/// Select all children from the last to `$num`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
// Select all children from the last to `$num`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
@mixin last($num) {
&:nth-last-child(-n + #{$num}) {
@content;
}
}
/// Select all children after the first to `$num`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
// Select all children after the first to `$num`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
@mixin after-first($num) {
&:nth-child(n + #{$num + 1}) {
@content;
}
}
/// Select all children before `$num` from the last.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
// Select all children before `$num` from the last.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
@mixin from-end($num) {
&:nth-last-child(#{$num}) {
@content;
}
}
/// Select all children between `$first` and `$last`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select all children between `$first` and `$last`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin between($first, $last) {
&:nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
}
/// Select all even children between `$first` and `$last`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select all even children between `$first` and `$last`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin even-between($first, $last) {
&:nth-child(even):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
}
/// Select all odd children between `$first` and `$last`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select all odd children between `$first` and `$last`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin odd-between($first, $last) {
&:nth-child(odd):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
}
/// Select all `$num` children between `$first` and `$last`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select all `$num` children between `$first` and `$last`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin n-between($num, $first, $last) {
&:nth-child(#{$num}n):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
}
// Select all children but `$num`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
/// Select all children but `$num`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin all-but($num) {
&:not(:nth-child(#{$num})) {
@content;
}
}
/// Select children each `$num`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
/// @alias every
// Select children each `$num`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
// @alias every
@mixin each($num) {
&:nth-child(#{$num}n) {
@content;
}
}
/// Select children each `$num`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
// Select children each `$num`.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
@mixin every($num) {
&:nth-child(#{$num}n) {
@content;
}
}
/// Select the `$num` child from the start and the `$num` child from the last.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
// Select the `$num` child from the start and the `$num` child from the last.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
@mixin from-first-last($num) {
&:nth-child(#{$num}),
&:nth-last-child(#{$num}) {
@@ -128,57 +140,59 @@
}
}
// Select the item in the middle of `$num` child. Only works with odd number
// chain.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
/// Select the item in the middle of `$num` child. Only works with odd number
/// chain.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin middle($num) {
&:nth-child(#{round($num / 2)}) {
@content;
}
}
// Select all children between the `$num` first and the `$num` last.
// @group with-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - id of the child
/// Select all children between the `$num` first and the `$num` last.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin all-but-first-last($num) {
&:nth-child(n + #{$num}):nth-last-child(n + #{$num}) {
@content;
}
}
// This quantity-query mixin will only select the first of `$limit` items. It will not
// work if there is not as much as item as you set in `$limit`.
// @group Quantity queries
// @param {number} $limit
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// This quantity-query mixin will only select the first of `$limit` items. It will not
/// work if there is not as much as item as you set in `$limit`.
/// @group Quantity queries
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin first-of($limit) {
&:nth-last-child(#{$limit}):first-child {
@content;
}
}
/// This quantity-query mixin will only select the last of `$limit` items. It will not
/// if there is not as much as item as you set in `$limit`.
/// @group Quantity queries
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// This quantity-query mixin will only select the last of `$limit` items. It will not
// if there is not as much as item as you set in `$limit`.
// @group Quantity queries
// @param {number} $limit
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin last-of($limit) {
&:nth-of-type(#{$limit}):nth-last-of-type(1) {
@content;
}
}
/// This quantity-query mixin will select every items if there is at least `$num` items. It will not
/// if there is not as much as item as you set in `$num`.
/// @group Quantity queries
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// This quantity-query mixin will select every items if there is at least `$num` items. It will not
// if there is not as much as item as you set in `$num`.
// @group Quantity queries
// @param {number} $limit
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin at-least($num) {
$selector: &;
$child: nth(nth($selector, -1), -1);
@@ -189,11 +203,12 @@
}
}
/// This quantity-query mixin will select every items if there is at most `$num` items. It will not
/// if there is not as much as item as you set in `$num`.
/// @group Quantity queries
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// This quantity-query mixin will select every items if there is at most `$num` items. It will not
// if there is not as much as item as you set in `$num`.
// @group Quantity queries
// @param {number} $limit
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin at-most($num) {
$selector: &;
$child: nth(nth($selector, -1), -1);
@@ -204,10 +219,11 @@
}
}
/// This quantity-query mixin will select every items only if there is between `$min` and `$max` items.
/// @group Quantity queries
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// This quantity-query mixin will select every items only if there is between `$min` and `$max` items.
// @group Quantity queries
// @param {number} $limit
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin in-between($min, $max) {
$selector: &;
$child: nth(nth($selector, -1), -1);
@@ -218,45 +234,50 @@
}
}
/// Select the first exact child
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select the first exact child
// @group no-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin first-child() {
&:first-of-type {
@content
}
}
/// Select the last exact child
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select the last exact child
// @group no-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin last-child() {
&:last-of-type {
@content
}
}
/// Select all even children.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select all even children.
// @group no-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin even() {
&:nth-child(even) {
@content;
}
}
/// Select all odd children.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select all odd children.
// @group no-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin odd() {
&:nth-child(odd) {
@content;
}
}
/// Select only the first and last child.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Select only the first and last child.
// @group no-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin first-last() {
&:first-child,
&:last-child {
@@ -264,43 +285,46 @@
}
}
/// Will only select the child if its unique.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @alias only
// Will only select the child if its unique.
// @group no-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @alias only
@mixin unique() {
&:only-child {
@content;
}
}
/// Will only select the child if its unique.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Will only select the child if its unique.
// @group no-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin only() {
&:only-child {
@content;
}
}
/// Will only select children if they are not unique. Meaning if there is at
/// least 2 children, the style is applied.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// Will only select children if they are not unique. Meaning if there is at
// least 2 children, the style is applied.
// @group no-arguments
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin not-unique() {
&:not(:only-child) {
@content;
}
}
// This mixin is used to automatically sort z-index in numerical order. But it
// can also sort them in anti-numerical order, depending the parameters you use.
// @group using functions
// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
// @param {number} $num - Number of children
// @param {string} $direction [forward] - Direction of the sort
// @param {number} $index [0] - Index of the sorting
/// This mixin is used to automatically sort z-index in numerical order. But it
/// can also sort them in anti-numerical order, depending the parameters you use.
/// @group using functions
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - Number of children
/// @param {string} $direction [forward] - Direction of the sort
/// @param {number} $index [0] - Index of the sorting
@mixin child-index($num, $direction: 'forward', $index: 0) {
@for $i from 1 through $num {
@if ($direction == 'forward') {
@@ -317,11 +341,12 @@
}
}
/// Used by the child-index mixin. It will returned the proper sorted numbers
/// depending on the `$index` value.
/// @access private
/// @param {number} $num - Number of children
/// @param {number} $index - Index of the sorting
// Used by the child-index mixin. It will returned the proper sorted numbers
// depending on the `$index` value.
// @access private
// @param {number} $num - Number of children
// @param {number} $index - Index of the sorting
@function order-index($i, $index) {
@return ($index + $i);
}

View File

@@ -2,97 +2,45 @@
// Tools / Font Faces
// ==========================================================================
$global-font-file-formats: "woff2", "woff" !default;
// Import the webfont with font-face as woff and woff2
//
// @param {List} $webfont (font name, filename, font-weight, font-style) - Each webfont to import.
// @param {String} $dir - The webfont directory path
// @output void
//
// Builds the `src` list for an `@font-face` declaration.
//
// @link https://github.com/thoughtbot/bourbon/blob/master/core/bourbon/utilities/_font-source-declaration.scss
// @link http://goo.gl/Ru1bKP
//
// @param {String} $font-family - The font family name.
// @param {String} $file-path - The path to the font family.
// @param {List} $file-formats - The file formats to request.
// @return {List}
//
// @require {function} list-contains
//
// @access private
//
@function font-source-declaration(
$font-family,
$file-path,
$file-formats
) {
$src: ();
$formats-map: (
eot: "#{$file-path}.eot?#iefix" format("embedded-opentype"),
woff2: "#{$file-path}.woff2" format("woff2"),
woff: "#{$file-path}.woff" format("woff"),
ttf: "#{$file-path}.ttf" format("truetype"),
svg: "#{$file-path}.svg##{$font-family}" format("svg"),
);
@each $key, $values in $formats-map {
@if list-contains($file-formats, $key) {
$file-path: nth($values, 1);
$font-format: nth($values, 2);
$src: append($src, url("#{$assets-path}#{$file-path}") $font-format, comma);
}
}
@return $src;
}
//
// Generates an `@font-face` declaration.
//
// You can choose the specific file formats you need to output; the mixin supports
// `eot`, `ttf`, `svg`, `woff2` and `woff`.
//
// @link https://github.com/thoughtbot/bourbon/blob/master/core/bourbon/library/_font-face.scss
//
// @param {String} $font-family - The font family name.
// @param {String} $file-path - The path to the font family.
// @param {String|List} $file-formats [("ttf", "woff2", "woff")]
// A list of file formats to support,
// for example ("eot", "ttf", "svg", "woff2", "woff").
//
// @content
// Any additional CSS properties that are included in the `@include`
// directive will be output within the `@font-face` declaration, e.g.
// you can pass in `font-weight`, `font-style` and/or `unicode-range`.
//
// @example scss
// @include font-face(
// "source-sans-pro",
// "fonts/source-sans-pro-regular",
// ("woff2", "woff")
// ) {
// font-style: normal;
// font-weight: 400;
// }
//
// // CSS Output
// @font-face {
// font-family: "source-sans-pro";
// src: url("fonts/source-sans-pro-regular.woff2") format("woff2"),
// url("fonts/source-sans-pro-regular.woff") format("woff");
// font-style: normal;
// font-weight: 400;
// }
//
// @require {function} _font-source-declaration
// @require {function} _retrieve-bourbon-setting
//
@mixin font-face(
$font-family,
$file-path,
$file-formats: $global-font-file-formats
) {
@mixin font-face($webfont, $dir) {
@font-face {
font-family: $font-family;
src: font-source-declaration( $font-family, $file-path, $file-formats);
@content;
font-display: swap;
font-family: nth($webfont, 1);
src: url("#{$dir}#{nth($webfont, 2)}.woff2") format("woff2"),
url("#{$dir}#{nth($webfont, 2)}.woff") format("woff");
font-weight: #{nth($webfont, 3)};
font-style: #{nth($webfont, 4)};
}
}
// Loops through a list of local fonts and import each font-face as woff and woff2
//
// @param {List} $webfonts [(font name, filename, font-weight, font-style)] - Each webfont to import.
// @param {String} $dir - The webfont directory path
// @output void
@mixin font-faces($webfonts, $dir) {
@each $webfont in $webfonts {
@include font-face($webfont, $dir);
}
}
// Map the font-family requires with the existing imported font-families
//
// @param {String} $font-family - The name of the webfont.
// @return {String} The webfont and it's fallbacks.
@function ff($font-family) {
@if not map-has-key($font-families, $font-family) {
@error "No font-family found in $font-families map for `#{$font-family}`. Property omitted.";
}
$value: map-get($font-families, $font-family);
@return $value;
}

View File

@@ -1,13 +1,13 @@
// ==========================================================================
// Tools / Functions
// ==========================================================================
//
// Converts the given pixel value to its EM quivalent.
//
// @param {Number} $size - The pixel value to convert.
// @param {Number} $base [$font-size] - The assumed base font size.
// @return {Number} Scalable pixel value in EMs.
//
@function em($size, $base: $font-size) {
@if (type-of($size) == number) {
@if (unit($size) != "px") {
@@ -28,13 +28,12 @@
@return ($size / $base) * 1em;
}
//
// Converts the given pixel value to its REM quivalent.
//
// @param {Number} $size - The pixel value to convert.
// @param {Number} $base [$font-size] - The assumed base font size.
// @return {Number} Scalable pixel value in REMs.
//
@function rem($size, $base: $font-size) {
@if (type-of($size) == number) {
@if (unit($size) != "px") {
@@ -55,19 +54,17 @@
@return ($size / $base) * 1rem;
}
//
// Converts a number to a percentage.
//
// @alias percentage()
// @link http://sassdoc.com/annotations/#alias
// @param {Number} $number - The value to convert.
// @return {Number} A percentage.
//
@function span($number) {
@return percentage($number);
}
//
// Checks if a list contains a value(s).
//
// @link https://github.com/thoughtbot/bourbon/blob/master/core/bourbon/validators/_contains.scss
@@ -75,7 +72,7 @@
// @param {List} $values - A single value or list of values to check for.
// @return {Boolean}
// @access private
//
@function list-contains(
$list,
$values...
@@ -89,38 +86,35 @@
@return true;
}
//
// Resolve whether a rule is important or not.
//
// @param {Boolean} $flag - Whether a rule is important (TRUE) or not (FALSE).
// @return {String|Null} Returns `!important` or NULL.
//
@function important($flag: false) {
@if ($flag == true) {
@return !important;
} @elseif ($important == false) {
@return null;
} @else {
@error "`#{$flag}` needs to be `true` or `false`."
@error "`#{$flag}` needs to be `true` or `false`.";
}
}
//
// Determine if the current context is for a WYSIWYG editor.
//
// @requires {String} $context - The global context of the stylesheet.
// @return {Boolean} If the $context is set to "editor".
//
@function is-editor() {
@return ('editor' == $context);
}
//
// Determine if the current context is for the front-end.
//
// @requires {String} $context - The global context of the stylesheet.
// @return {Boolean} If the $context is set to "frontend".
//
@function is-frontend() {
@return ('frontend' == $context);
}

View File

@@ -2,7 +2,6 @@
// Tools / Layout
// ==========================================================================
//
// Grid-like layout system.
//
// The layout tools provide a column-style layout system. This file contains
@@ -10,7 +9,6 @@
//
// @link https://github.com/inuitcss/inuitcss/blob/0420ba8/objects/_objects.layout.scss
//
//
// Generate the layout container.
//
@@ -19,7 +17,7 @@
//
// @requires {function} u-list-reset
// @output `font-size`, `margin`, `padding`, `list-style`
//
@mixin o-layout($gutter: 0, $fix-whitespace: true) {
margin: 0;
padding: 0;
@@ -34,7 +32,6 @@
}
}
//
// Generate the layout item.
//
// 1. Required in order to combine fluid widths with fixed gutters.
@@ -45,7 +42,7 @@
// 4. By default, all layout items are full-width (mobile first).
// 5. Gutters provided by left padding:
// http://csswizardry.com/2011/08/building-better-grid-systems/
//
@mixin o-layout_item($gutter: 0, $fix-whitespace: true) {
display: inline-block; // [2]
width: 100%; // [4]

View File

@@ -2,37 +2,34 @@
// Tools / Mixins
// ==========================================================================
//
// Set the color of the highlight that appears over a link while it's being tapped.
//
// By default, the highlight is suppressed.
//
// @param {Color} $value [rgba(0, 0, 0, 0)] - The value of the highlight.
// @output `-webkit-tap-highlight-color`
//
@mixin tap-highlight-color($value: rgba(0, 0, 0, 0)) {
-webkit-tap-highlight-color: $value;
}
//
// Set whether or not touch devices use momentum-based scrolling for the given element.
//
// By default, applies momentum-based scrolling for the current element.
//
// @param {String} $value [rgba(0, 0, 0, 0)] - The type of scrolling.
// @output `-webkit-overflow-scrolling`
//
@mixin overflow-scrolling($value: touch) {
-webkit-overflow-scrolling: $value;
}
//
// Micro clearfix rules for containing floats.
//
// @link http://www.cssmojo.com/the-very-latest-clearfix-reloaded/
// @param {List} $supports The type of clearfix to generate.
// @output Injects `:::after` pseudo-element.
//
@mixin u-clearfix($supports...) {
&::after {
display: if(list-contains($supports, table), table, block);
@@ -41,7 +38,6 @@
}
}
//
// Generate a font-size and baseline-compatible line-height.
//
// @link https://github.com/inuitcss/inuitcss/c14029c/tools/_tools.font-size.scss
@@ -49,7 +45,7 @@
// @param {Number} $line-height [auto] - The line box height.
// @param {Boolean} $important [false] - Whether the font-size is important.
// @output `font-size`, `line-height`
//
@mixin font-size($font-size, $line-height: auto, $important: false) {
$important: important($important);
font-size: rem($font-size) $important;
@@ -62,12 +58,11 @@
line-height: $line-height $important;
}
@elseif ($line-height != "none" and $line-height != false) {
@error "Doh! `#{$line-height}` is not a valid value for `$line-height`."
@error "Doh! `#{$line-height}` is not a valid value for `$line-height`.";
}
}
}
//
// Vertically-center the direct descendants of the current element.
//
// Centering is achieved by displaying children as inline-blocks. Any whitespace
@@ -75,7 +70,7 @@
// and its children.
//
// @output `font-size`, `display`, `vertical-align`
//
@mixin o-vertical-center {
font-size: 0;
@@ -93,13 +88,12 @@
}
}
//
// Generate `:hover` and `:focus` styles in one go.
//
// @link https://github.com/inuitcss/inuitcss/blob/master/tools/_tools.mixins.scss
// @content Wrapped in `:focus` and `:hover` pseudo-classes.
// @output Wraps the given content in `:focus` and `:hover` pseudo-classes.
//
@mixin u-hocus {
&:focus,
&:hover {
@@ -107,13 +101,12 @@
}
}
//
// Generate `:active` and `:focus` styles in one go.
//
// @see {Mixin} u-hocus
// @content Wrapped in `:focus` and `:active` pseudo-classes.
// @output Wraps the given content in `:focus` and `:hover` pseudo-classes.
//
@mixin u-actus {
&:focus,
&:active {
@@ -121,7 +114,6 @@
}
}
//
// Prevent text from wrapping onto multiple lines for the current element.
//
// An ellipsis is appended to the end of the line.
@@ -131,7 +123,7 @@
//
// @param {Number} $width [100%] - The maximum width of element.
// @output `max-width`, `word-wrap`, `white-space`, `overflow`, `text-overflow`
//
@mixin u-truncate($width: 100%) {
overflow: hidden;
text-overflow: ellipsis;
@@ -142,12 +134,11 @@
}
}
//
// Applies accessible hiding to the current element.
//
// @param {Boolean} $important [true] - Whether the visibility is important.
// @output Properties for removing the element from the document flow.
//
@mixin u-accessibly-hidden($important: true) {
$important: important($important);
position: absolute $important;
@@ -160,12 +151,11 @@
border: 0;
}
//
// Allows an accessibly hidden element to be focusable via keyboard navigation.
//
// @content For styling the now visible element.
// @output Injects `:focus`, `:active` pseudo-classes.
//
@mixin u-accessibly-focusable {
@include u-actus {
clip: auto;
@@ -176,7 +166,6 @@
}
}
//
// Hide the current element from all.
//
// The element will be hidden from screen readers and removed from the document flow.
@@ -184,14 +173,13 @@
// @link http://juicystudio.com/article/screen-readers-display-none.php
// @param {Boolean} $important [true] - Whether the visibility is important.
// @output `display`, `visibility`
//
@mixin u-hidden($important: true) {
$important: important($important);
display: none $important;
visibility: hidden $important;
}
//
// Show the current element for all.
//
// The element will be accessible from screen readers and visible in the document flow.
@@ -199,7 +187,7 @@
// @param {String} $display [block] - The rendering box used for the element.
// @param {Boolean} $important [true] - Whether the visibility is important.
// @output `display`, `visibility`
//
@mixin u-shown($display: block, $important: true) {
$important: important($important);
display: $display $important;

View File

@@ -10,19 +10,21 @@
// .u-pull-2/4
// .u-pull-1/5
// .u-push-2/3
$widths-offsets: false !default;
// By default, the boilerplate uses fractions-like classes like `<div class="u-1/4">`.
// You can change the `/` to whatever you fancy with this variable.
$fractions-delimiter: \/ !default;
// When using Sass-MQ, this defines the separator for the breakpoints suffix
// in the class name. By default, we are generating the responsive suffixes
// for the classes with a `@` symbol so you get classes like:
// <div class="u-3/12@mobile">
$breakpoint-delimiter: \@ !default;
//
// Generate a series of width helper classes
//
// @example scss
@@ -45,7 +47,7 @@ $breakpoint-delimiter: \@ !default;
// @param {List} $colums - The columns we want the widths to have.
// @param {String} $breakpoint - Optional suffix for responsive widths.
// @output `width`, `position`, `right`, `left`
//
@mixin widths($columns, $breakpoint: null, $important: true) {
$important: important($important);

View File

@@ -4,6 +4,7 @@
// Floats
// ==========================================================================
.u-float-left {
float: left !important;
}
@@ -14,6 +15,7 @@
// Horizontal Text
// ==========================================================================
.u-text-center {
text-align: center !important;
}
@@ -28,6 +30,7 @@
// Vertical Text
// ==========================================================================
.u-align-baseline {
vertical-align: baseline !important;
}

View File

@@ -4,18 +4,21 @@
// Layout
// ==========================================================================
.u-clearfix {
@include u-clearfix;
}
// Decorative
// =============================================================================
.u-truncate {
@include u-truncate;
}
// Visibility / Display
// ==========================================================================
[hidden][aria-hidden="false"] {
position: absolute;
display: inherit;
@@ -30,35 +33,33 @@
// display: block;
// }
// /**
// * 1. Fix for Firefox bug: an image styled `max-width:100%` within an
// * inline-block will display at its default size, and not limit its width to
// * 100% of an ancestral container.
// */
// // 1. Fix for Firefox bug: an image styled `max-width:100%` within an
// // inline-block will display at its default size, and not limit its width to
// // 100% of an ancestral container.
//
// .u-inline-block {
// display: inline-block !important;
// max-width: 100%; /* 1 */
// }
//
// .u-inline {
// display: inline !important;
// }
//
// .u-table {
// display: table !important;
// }
//
// .u-tableCell {
// display: table-cell !important;
// }
//
// .u-tableRow {
// display: table-row !important;
// }
/**
* Completely remove from the flow but leave available to screen readers.
*/
// Completely remove from the flow but leave available to screen readers.
.u-screen-reader-text {
@include u-accessibly-hidden;
}
@@ -69,13 +70,12 @@
}
}
/*
* Extends the `.screen-reader-text` class to allow the element
* to be focusable when navigated to via the keyboard.
*
* @link https://www.drupal.org/node/897638
* @todo Define styles when focused.
*/
// Extends the `.screen-reader-text` class to allow the element
// to be focusable when navigated to via the keyboard.
//
// @link https://www.drupal.org/node/897638
// @todo Define styles when focused.
.u-screen-reader-text.-focusable {
@include u-accessibly-focusable;
}

View File

@@ -11,9 +11,9 @@
////
@media print {
/**
* 1. Black prints faster: http://www.sanbeiji.com/archives/953
*/
// 1. Black prints faster: http://www.sanbeiji.com/archives/953
*,
*:before,
*:after,
@@ -21,7 +21,7 @@
*:first-line {
background: transparent !important;
box-shadow: none !important;
color: #000000 !important; /* [1] */
color: #000000 !important; // [1]
text-shadow: none !important;
}
@@ -38,10 +38,9 @@
content: " (" attr(title) ")";
}
/**
* Don't show links that are fragment identifiers, or use the `javascript:`
* pseudo protocol.
*/
// Don't show links that are fragment identifiers, or use the `javascript:`
// pseudo protocol.
a[href^="#"]:after,
a[href^="javascript:"]:after {
content: "";
@@ -53,9 +52,8 @@
page-break-inside: avoid;
}
/**
* Printing Tables: http://css-discuss.incutio.com/wiki/Printing_Tables
*/
// Printing Tables: http://css-discuss.incutio.com/wiki/Printing_Tables
thead {
display: table-header-group;
}

View File

@@ -2,12 +2,9 @@
// Utilities / Ratio
// ==========================================================================
//
// @link https://github.com/inuitcss/inuitcss/blob/19d0c7e/objects/_objects.ratio.scss
//
// A list of aspect ratios that get generated as modifier classes.
$aspect-ratios: (
(2:1),
(4:3),
@@ -16,13 +13,11 @@ $aspect-ratios: (
/* stylelint-disable */
//
// Generate a series of ratio classes to be used like so:
//
// @example
// <div class="o-ratio u-16:9">
//
//
@each $ratio in $aspect-ratios {
@each $antecedent, $consequent in $ratio {
@if (type-of($antecedent) != number) {

View File

@@ -2,9 +2,8 @@
// Utilities / States
// ==========================================================================
/**
* ARIA roles display visual cursor hints
*/
// ARIA roles display visual cursor hints
[aria-busy="true"] {
cursor: progress;
}
@@ -17,9 +16,7 @@
cursor: default;
}
/**
* Control visibility without affecting flow.
*/
// Control visibility without affecting flow.
.is-visible {
visibility: visible !important;
@@ -31,9 +28,7 @@
opacity: 0 !important;
}
/**
* Completely remove from the flow and screen readers.
*/
// Completely remove from the flow and screen readers.
.is-hidden {
@include u-hidden;
@@ -56,29 +51,27 @@
// display: none;
// }
// }
//
// .is-hidden\@from-large {
// @media (min-width: $from-large) {
// display: none;
// }
// }
// /**
// * Display a hidden-by-default element.
// */
// // Display a hidden-by-default element.
//
// .is-shown {
// @include u-shown;
// }
//
// table.is-shown {
// display: table !important;
// }
//
// tr.is-shown {
// display: table-row !important;
// }
//
// td.is-shown,
// th.is-shown {
// display: table-cell !important;

View File

@@ -4,8 +4,8 @@
////
/// @link https://github.com/inuitcss/inuitcss/blob/6eb574f/utilities/_utilities.widths.scss
////
///
///
/// Which fractions would you like in your grid system(s)?
/// By default, the boilerplate provides fractions of one whole, halves, thirds,
/// quarters, and fifths, e.g.:
@@ -15,6 +15,8 @@
/// .u-2/5
/// .u-3/4
/// .u-2/3
////
$widths-fractions: 1 2 3 4 5 !default;
@include widths($widths-fractions);

View File

@@ -1,10 +1,54 @@
import loconfig from '../../loconfig.json';
import loconfig from '../utils/config.js';
import glob from '../utils/glob.js';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import template from '../utils/template.js';
import resolve from '../utils/template.js';
import concat from 'concat';
import { basename } from 'node:path';
import {
basename,
normalize,
} from 'node:path';
/**
* @const {object} defaultGlobOptions - The default shared glob options.
* @const {object} developmentGlobOptions - The predefined glob options for development.
* @const {object} productionGlobOptions - The predefined glob options for production.
*/
export const defaultGlobOptions = {
};
export const developmentGlobOptions = Object.assign({}, defaultGlobOptions);
export const productionGlobOptions = Object.assign({}, defaultGlobOptions);
/**
* @typedef {object} ConcatOptions
* @property {boolean} removeDuplicates - Removes duplicate paths from
* the array of matching files and folders.
* Only the first occurrence of each path is kept.
*/
/**
* @const {ConcatOptions} defaultConcatOptions - The default shared concatenation options.
* @const {ConcatOptions} developmentConcatOptions - The predefined concatenation options for development.
* @const {ConcatOptions} productionConcatOptions - The predefined concatenation options for production.
*/
export const defaultConcatOptions = {
removeDuplicates: true,
};
export const developmentConcatOptions = Object.assign({}, defaultConcatOptions);
export const productionConcatOptions = Object.assign({}, defaultConcatOptions);
/**
* @const {object} developmentConcatFilesArgs - The predefined `concatFiles()` options for development.
* @const {object} productionConcatFilesArgs - The predefined `concatFiles()` options for production.
*/
export const developmentConcatFilesArgs = [
developmentGlobOptions,
developmentConcatOptions,
];
export const productionConcatFilesArgs = [
productionGlobOptions,
productionConcatOptions,
];
/**
* Concatenates groups of files.
@@ -12,37 +56,77 @@ import { basename } from 'node:path';
* @todo Add support for minification.
*
* @async
* @param {object|boolean} [globOptions=null] - Customize the glob options.
* If `null`, default production options are used.
* If `false`, the glob function will be ignored.
* @param {object} [concatOptions=null] - Customize the concatenation options.
* If `null`, default production options are used.
* @return {Promise}
*/
export default async function concatFiles() {
export default async function concatFiles(globOptions = null, concatOptions = null) {
if (glob) {
if (globOptions == null) {
globOptions = productionGlobOptions;
} else if (
globOptions !== false &&
globOptions !== developmentGlobOptions &&
globOptions !== productionGlobOptions
) {
globOptions = Object.assign({}, defaultGlobOptions, globOptions);
}
}
if (concatOptions == null) {
concatOptions = productionConcatOptions;
} else if (
concatOptions !== developmentConcatOptions &&
concatOptions !== productionConcatOptions
) {
concatOptions = Object.assign({}, defaultConcatOptions, concatOptions);
}
loconfig.tasks.concats.forEach(async ({
includes,
outfile
outfile,
label = null
}) => {
const filename = basename(outfile || 'undefined');
if (!label) {
label = basename(outfile || 'undefined');
}
const timeLabel = `${filename} concatenated in`;
const timeLabel = `${label} concatenated in`;
console.time(timeLabel);
try {
includes = includes.map((path) => template(path));
outfile = template(outfile);
includes = resolve(includes);
outfile = resolve(outfile);
const files = await glob(includes);
let files;
if (glob && globOptions) {
files = await glob(includes, globOptions);
} else {
files = includes;
}
if (concatOptions.removeDuplicates) {
files = files.map((path) => normalize(path));
files = [ ...new Set(files) ];
}
await concat(files, outfile);
if (files.length) {
message(`${filename} concatenated`, 'success', timeLabel);
message(`${label} concatenated`, 'success', timeLabel);
} else {
message(`${filename} is empty`, 'notice', timeLabel);
message(`${label} is empty`, 'notice', timeLabel);
}
} catch (err) {
message(`Error concatenating ${filename}`, 'error');
message(`Error concatenating ${label}`, 'error');
message(err);
notification({
title: `${filename} concatenation failed 🚨`,
title: `${label} concatenation failed 🚨`,
message: `${err.name}: ${err.message}`
});
}

View File

@@ -1,7 +1,7 @@
import loconfig from '../../loconfig.json';
import loconfig from '../utils/config.js';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import template from '../utils/template.js';
import resolve from '../utils/template.js';
import esbuild from 'esbuild';
import { basename } from 'node:path';
@@ -56,20 +56,23 @@ export default async function compileScripts(esBuildOptions = null) {
loconfig.tasks.scripts.forEach(async ({
includes,
outdir = '',
outfile = ''
outfile = '',
label = null
}) => {
const filename = basename(outdir || outfile || 'undefined');
if (!label) {
label = basename(outdir || outfile || 'undefined');
}
const timeLabel = `${filename} compiled in`;
const timeLabel = `${label} compiled in`;
console.time(timeLabel);
try {
includes = includes.map((path) => template(path));
includes = resolve(includes);
if (outdir) {
outdir = template(outdir);
outdir = resolve(outdir);
} else if (outfile) {
outfile = template(outfile);
outfile = resolve(outfile);
} else {
throw new TypeError(
'Expected \'outdir\' or \'outfile\''
@@ -82,11 +85,11 @@ export default async function compileScripts(esBuildOptions = null) {
outfile,
}));
message(`${filename} compiled`, 'success', timeLabel);
message(`${label} compiled`, 'success', timeLabel);
} catch (err) {
// errors managments (already done in esbuild)
notification({
title: `${filename} compilation failed 🚨`,
title: `${label} compilation failed 🚨`,
message: `${err.errors[0].text} in ${err.errors[0].location.file} line ${err.errors[0].location.line}`
});
}

View File

@@ -1,8 +1,8 @@
import loconfig from '../../loconfig.json';
import loconfig from '../utils/config.js';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import postcss, { pluginsMap as postcssPluginsMap } from '../utils/postcss.js';
import template from '../utils/template.js';
import resolve from '../utils/template.js';
import { writeFile } from 'node:fs/promises';
import { basename } from 'node:path';
import { promisify } from 'node:util';
@@ -47,8 +47,8 @@ export const developmentPostCSSOptions = Object.assign({}, defaultPostCSSOptions
export const productionPostCSSOptions = Object.assign({}, defaultPostCSSOptions);
/**
* @const {object} developmentStylesArgs - The predefined `compileStyles()` options for development.
* @const {object} productionStylesArgs - The predefined `compileStyles()` options for production.
* @const {object|boolean} developmentStylesArgs - The predefined `compileStyles()` options for development.
* @const {object|boolean} productionStylesArgs - The predefined `compileStyles()` options for production.
*/
export const developmentStylesArgs = [
developmentSassOptions,
@@ -88,8 +88,8 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
postcssOptions = productionPostCSSOptions;
} else if (
postcssOptions !== false &&
postcssOptions !== developmentSassOptions &&
postcssOptions !== productionSassOptions
postcssOptions !== developmentPostCSSOptions &&
postcssOptions !== productionPostCSSOptions
) {
postcssOptions = Object.assign({}, defaultPostCSSOptions, postcssOptions);
}
@@ -97,16 +97,17 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
loconfig.tasks.styles.forEach(async ({
infile,
outfile
outfile,
label = null
}) => {
const name = basename((outfile || 'undefined'), '.css');
const filestem = basename((outfile || 'undefined'), '.css');
const timeLabel = `${name}.css compiled in`;
const timeLabel = `${label || `${filestem}.css`} compiled in`;
console.time(timeLabel);
try {
infile = template(infile);
outfile = template(outfile);
infile = resolve(infile);
outfile = resolve(outfile);
let result = await sassRender(Object.assign({}, sassOptions, {
file: infile,
@@ -132,7 +133,7 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
if (result.warnings) {
const warnings = result.warnings();
if (warnings.length) {
message(`Error processing ${name}.css`, 'warning');
message(`Error processing ${label || `${filestem}.css`}`, 'warning');
warnings.forEach((warn) => {
message(warn.toString());
});
@@ -144,17 +145,17 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
await writeFile(outfile, result.css);
if (result.css) {
message(`${name}.css compiled`, 'success', timeLabel);
message(`${label || `${filestem}.css`} compiled`, 'success', timeLabel);
} else {
message(`${name}.css is empty`, 'notice', timeLabel);
message(`${label || `${filestem}.css`} is empty`, 'notice', timeLabel);
}
} catch (err) {
message(`Error compiling ${name}.css`, 'error');
message(`Error compiling ${label || `${filestem}.css`}`, 'error');
message(err);
notification({
title: `${name}.css save failed 🚨`,
message: `Could not save stylesheet to ${name}.css`
title: `${label || `${filestem}.css`} save failed 🚨`,
message: `Could not save stylesheet to ${label || `${filestem}.css`}`
});
}
@@ -162,21 +163,21 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
try {
await writeFile(outfile + '.map', result.map.toString());
} catch (err) {
message(`Error compiling ${name}.css.map`, 'error');
message(`Error compiling ${label || `${filestem}.css.map`}`, 'error');
message(err);
notification({
title: `${name}.css.map save failed 🚨`,
message: `Could not save sourcemap to ${name}.css.map`
title: `${label || `${filestem}.css.map`} save failed 🚨`,
message: `Could not save sourcemap to ${label || `${filestem}.css.map`}`
});
}
}
} catch (err) {
message(`Error compiling ${name}.scss`, 'error');
message(`Error compiling ${label || `${filestem}.scss`}`, 'error');
message(err.formatted || err);
notification({
title: `${name}.scss compilation failed 🚨`,
title: `${label || `${filestem}.scss`} compilation failed 🚨`,
message: (err.formatted || `${err.name}: ${err.message}`)
});
}

View File

@@ -1,7 +1,7 @@
import loconfig from '../../loconfig.json';
import loconfig from '../utils/config.js';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import template from '../utils/template.js';
import resolve from '../utils/template.js';
import { basename } from 'node:path';
import mixer from 'svg-mixer';
@@ -49,28 +49,31 @@ export default async function compileSVGs(mixerOptions = null) {
loconfig.tasks.svgs.forEach(async ({
includes,
outfile
outfile,
label = null
}) => {
const filename = basename(outfile || 'undefined');
if (!label) {
label = basename(outfile || 'undefined');
}
const timeLabel = `${filename} compiled in`;
const timeLabel = `${label} compiled in`;
console.time(timeLabel);
try {
includes = includes.map((path) => template(path));
outfile = template(outfile);
includes = resolve(includes);
outfile = resolve(outfile);
const result = await mixer(includes, mixerOptions);
await result.write(outfile);
message(`${filename} compiled`, 'success', timeLabel);
message(`${label} compiled`, 'success', timeLabel);
} catch (err) {
message(`Error compiling ${filename}`, 'error');
message(`Error compiling ${label}`, 'error');
message(err);
notification({
title: `${filename} compilation failed 🚨`,
title: `${label} compilation failed 🚨`,
message: `${err.name}: ${err.message}`
});
}

64
build/utils/config.js Normal file
View File

@@ -0,0 +1,64 @@
/**
* @file Provides simple user configuration options.
*/
import loconfig from '../../loconfig.json';
let usrconfig;
try {
usrconfig = await import('../../loconfig.local.json');
usrconfig = usrconfig.default;
merge(loconfig, usrconfig);
} catch (err) {
// do nothing
}
export default loconfig;
/**
* Creates a new object with all nested object properties
* merged into it recursively.
*
* @param {object} target - The target object.
* @param {object[]} ...sources - The source object(s).
* @throws {TypeError} If the target and source are the same.
* @return {object} Returns the `target` object.
*/
export function merge(target, ...sources) {
for (const source of sources) {
if (target === source) {
throw new TypeError(
'Cannot merge, target and source are the same'
);
}
for (const key in source) {
if (source[key] != null) {
if (isObjectLike(source[key]) && isObjectLike(target[key])) {
merge(target[key], source[key]);
continue;
} else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
target[key] = target[key].concat(source[key]);
continue;
}
}
target[key] = source[key];
}
}
return target;
}
/**
* Determines whether the passed value is an `Object`.
*
* @param {*} value - The value to be checked.
* @return {boolean} Returns `true` if the value is an `Object`,
* otherwise `false`.
*/
function isObjectLike(value) {
return (value != null && typeof value === 'object');
}

View File

@@ -4,8 +4,9 @@
* Note that options vary between libraries.
*
* Candidates:
*
* - {@link https://npmjs.com/package/tiny-glob tiny-glob}
* - {@link https://npmjs.com/package/globby}
* - {@link https://npmjs.com/package/globby globby}
* - {@link https://npmjs.com/package/fast-glob fast-glob}
* - {@link https://npmjs.com/package/glob glob}
*/
@@ -22,7 +23,15 @@ const candidates = [
'glob',
];
export default await importGlob();
let glob;
try {
glob = await importGlob();
} catch (err) {
// do nothing
}
export default glob;
/**
* Imports the first available glob function.
@@ -63,7 +72,7 @@ async function importGlob() {
}
throw new TypeError(
`No glob library was found, expected one of: ${modules.join(', ')}`
`No glob library was found, expected one of: ${candidates.join(', ')}`
);
}

View File

@@ -1,3 +1,7 @@
/**
* @file Provides a decorator for cross-platform notification.
*/
import notifier from 'node-notifier';
/**

View File

@@ -12,8 +12,7 @@ try {
autoprefixer = await import('autoprefixer');
autoprefixer = autoprefixer.default;
} catch (err) {
postcss = null;
autoprefixer = null;
// do nothing
}
export default postcss;

View File

@@ -2,7 +2,7 @@
* @file Provides simple template tags.
*/
import loconfig from '../../loconfig.json';
import loconfig from './config.js';
const templateData = flatten({
paths: loconfig.paths
@@ -14,17 +14,53 @@ const templateData = flatten({
* If replacement pairs contain a mix of substrings, regular expressions,
* and functions, regular expressions are executed last.
*
* @param {string} input - The string being searched and replaced on.
* @param {object} data - An object in the form `{ 'from': 'to', … }`.
* @param {*} input - The value being searched and replaced on.
* If input is, or contains, a string, tags will be resolved.
* If input is, or contains, an object, it is mutated directly.
* If input is, or contains, an array, a shallow copy is returned.
* Otherwise, the value is left intact.
* @param {object} [data] - An object in the form `{ 'from': 'to', … }`.
* @return {*} Returns the transformed value.
*/
export default function resolve(input, data = templateData) {
switch (typeof input) {
case 'string': {
return resolveValue(input, data);
}
case 'object': {
if (input == null) {
break;
}
if (Array.isArray(input)) {
return input.map((value) => resolve(value, data));
} else {
for (const key in input) {
input[key] = resolve(input[key], data);
}
}
}
}
return input;
}
/**
* Replaces all template tags in a string from a map of keys and values.
*
* If replacement pairs contain a mix of substrings, regular expressions,
* and functions, regular expressions are executed last.
*
* @param {string} input - The string being searched and replaced on.
* @param {object} [data] - An object in the form `{ 'from': 'to', … }`.
* @return {string} Returns the translated string.
*/
export default function template(input, data) {
export function resolveValue(input, data = templateData) {
const tags = [];
if (data) {
if (data !== templateData) {
data = flatten(data);
} else {
data = templateData;
}
for (let tag in data) {
@@ -55,7 +91,7 @@ export default function template(input, data) {
return '';
});
};
}
/**
* Creates a new object with all nested object properties

View File

@@ -1,82 +1,195 @@
import loconfig from '../loconfig.json';
import concatFiles from './tasks/concats.js';
import concatFiles, { developmentConcatFilesArgs } from './tasks/concats.js';
import compileScripts, { developmentScriptsArgs } from './tasks/scripts.js';
import compileStyles, { developmentStylesArgs } from './tasks/styles.js' ;
import compileSVGs, { developmentSVGsArgs } from './tasks/svgs.js';
import template from './utils/template.js';
import server from 'browser-sync';
import loconfig, { merge } from './utils/config.js';
import message from './utils/message.js';
import notification from './utils/notification.js';
import resolve from './utils/template.js';
import browserSync from 'browser-sync';
import { join } from 'node:path';
const { paths, tasks } = loconfig;
const serverConfig = {
open: false,
notify: false
};
if (typeof paths.url === 'string' && paths.url.length > 0) {
// Use proxy
serverConfig.proxy = paths.url;
} else {
// Use base directory
serverConfig.server = {
baseDir: paths.dest
};
}
// Start the Browsersync server
server.init(serverConfig);
// Match a URL protocol.
const regexUrlStartsWithProtocol = /^[a-z0-9\-]:\/\//i;
// Build scripts, compile styles, concat files,
// and generate spritesheets on first hit
concatFiles();
concatFiles(...developmentConcatFilesArgs);
compileScripts(...developmentScriptsArgs);
compileStyles(...developmentStylesArgs);
compileSVGs(...developmentSVGsArgs);
// and call any methods on it.
server.watch(
[
paths.views.src,
join(paths.scripts.dest, '*.js'),
join(paths.styles.dest, '*.css'),
join(paths.svgs.dest, '*.svg'),
]
).on('change', server.reload);
// Create a new BrowserSync instance
const server = browserSync.create();
// Watch scripts
server.watch(
[
join(paths.scripts.src, '**/*.js'),
]
).on('change', () => {
compileScripts(...developmentScriptsArgs);
// Start the BrowserSync server
server.init(createServerOptions(loconfig), (err) => {
if (err) {
message('Error starting development server', 'error');
message(err);
notification({
title: 'Development server failed',
message: `${err.name}: ${err.message}`
});
}
});
// Watch concats
server.watch(
tasks.concats.reduce(
(patterns, { includes }) => patterns.concat(includes),
[]
).map((path) => template(path))
).on('change', () => {
concatFiles();
});
configureServer(server, loconfig);
// Watch styles
server.watch(
[
join(paths.styles.src, '**/*.scss'),
]
).on('change', () => {
compileStyles(...developmentStylesArgs);
});
/**
* Configures the BrowserSync options.
*
* @param {BrowserSync} server - The BrowserSync API.
* @param {object} loconfig - The project configset.
* @param {object} loconfig.paths - The paths options.
* @param {object} loconfig.tasks - The tasks options.
* @return {void}
*/
function configureServer(server, { paths, tasks }) {
const views = createViewsArray(paths.views);
// Watch svgs
server.watch(
[
join(paths.svgs.src, '*.svg'),
]
).on('change', () => {
compileSVGs(...developmentSVGsArgs);
});
// Reload on any changes to views or processed files
server.watch(
[
...views,
join(paths.scripts.dest, '*.js'),
join(paths.styles.dest, '*.css'),
join(paths.svgs.dest, '*.svg'),
]
).on('change', server.reload);
// Watch source scripts
server.watch(
[
join(paths.scripts.src, '**/*.js'),
]
).on('change', () => {
compileScripts(...developmentScriptsArgs);
});
// Watch source concats
server.watch(
resolve(
tasks.concats.reduce(
(patterns, { includes }) => patterns.concat(includes),
[]
)
)
).on('change', () => {
concatFiles(...developmentConcatFilesArgs);
});
// Watch source styles
server.watch(
[
join(paths.styles.src, '**/*.scss'),
]
).on('change', () => {
compileStyles(...developmentStylesArgs);
});
// Watch source SVGs
server.watch(
[
join(paths.svgs.src, '*.svg'),
]
).on('change', () => {
compileSVGs(...developmentSVGsArgs);
});
}
/**
* Creates a new object with all the BrowserSync options.
*
* @param {object} loconfig - The project configset.
* @param {object} loconfig.paths - The paths options.
* @param {object} loconfig.server - The server options.
* @return {object} Returns the server options.
*/
function createServerOptions({
paths,
server: options
}) {
const config = {
open: false,
notify: false
};
// Resolve the URL for the BrowserSync server
if (isNonEmptyString(paths.url)) {
// Use proxy
config.proxy = paths.url;
} else if (isNonEmptyString(paths.dest)) {
// Use base directory
config.server = {
baseDir: paths.dest
};
}
merge(config, resolve(options));
// If HTTPS is enabled, prepend `https://` to proxy URL
if (options?.https) {
if (isNonEmptyString(config.proxy?.target)) {
config.proxy.target = prependSchemeToUrl(config.proxy.target, 'https');
} else if (isNonEmptyString(config.proxy)) {
config.proxy = prependSchemeToUrl(config.proxy, 'https');
}
}
return config;
}
/**
* Creates a new array (shallow-copied) from the views configset.
*
* @param {*} views - The views configset.
* @throws {TypeError} If views is invalid.
* @return {array} Returns the views array.
*/
function createViewsArray(views) {
if (Array.isArray(views)) {
return Array.from(views);
}
switch (typeof views) {
case 'string':
return [ views ];
case 'object':
if (views != null) {
return Object.values(views);
}
}
throw new TypeError(
'Expected \'views\' to be a string, array, or object'
);
}
/**
* Prepends the scheme to the URL.
*
* @param {string} url - The URL to mutate.
* @param {string} [scheme] - The URL scheme to prepend.
* @return {string} Returns the mutated URL.
*/
function prependSchemeToUrl(url, scheme = 'http') {
if (regexUrlStartsWithProtocol.test(url)) {
return url.replace(regexUrlStartsWithProtocol, `${scheme}://`);
}
return `${scheme}://${url}`;
}
/**
* Determines whether the passed value is a string with at least one character.
*
* @param {*} value - The value to be checked.
* @return {boolean} Returns `true` if the value is a non-empty string,
* otherwise `false`.
*/
function isNonEmptyString(value) {
return (typeof value === 'string' && value.length > 0);
}

344
docs/development.md Normal file
View File

@@ -0,0 +1,344 @@
# Development
* [Installation](#installation)
* [Usage](#usage)
* [Configuration](#configuration)
* [Environment Configuration](#environment-configuration)
* [Development Configuration](#development-configuration)
* [`paths` option](#paths-option)
* [`paths.url` option](#pathsurl-option)
* [`paths.dest` option](#pathsdest-option)
* [`tasks` option](#tasks-option)
* [`server` option](#server-option)
* [Tasks](#tasks)
* [`concats`](#concats)
* [`scripts`](#scripts)
* [`styles`](#styles)
* [`svgs`](#svgs)
---
The boilerplate provides a custom, easily configured, and very simple,
task runner for [Node] to process assets and test quickly in browsers.
Learn more about the boilerplate's [tasks](#tasks) below.
## Installation
Make sure you have the following installed:
* [Node] — at least 14.17, the latest LTS is recommended.
* [NPM] — at least 6.0, the latest LTS is recommended.
```sh
# Switch to recommended Node version from .nvmrc
nvm use
# Install dependencies from package.json
npm install
```
## Usage
```sh
# Start development server, watch for changes, and compile assets
npm start
# Compile and minify assets
npm run build
```
See [`build.js`](../build/build.js) and [`watch.js`](../build/watch.js)
for details.
## Configuration
For development, most configuration values for processing front-end assets
are defined in the [`loconfig.json`](../loconfig.json) file that exists at
the root directory of your project.
### Environment Configuration
If any configuration options vary depending on whether your project is
running on your computer, a collaborator's computer, or on a web server,
these values should be stored in a `loconfig.local.json` file.
In fresh copy of the boilerplate, the root directory of your project
will contain a [`loconfig.example.json`](../loconfig.example.json) file.
> 💡 The boilerplate's default example customizes the development server
> to use a custom SSL certificate.
That file can be copied to `loconfig.local.json` and customized to suit
your local environment.
Your `loconfig.local.json` _should not_ be committed to your project's
source control.
> 💡 If you are developing with a team, you may wish to continue
> including a `loconfig.example.json` file with your project.
### Development Configuration
The boilerplate provides a few configuration settings to control the
behaviour for processing front-end assets.
#### `paths` option
The `paths` option defines URIs and file paths.
It is primarily used for template tags to reference any configuration
properties to reduce repetition.
Template tags are specified using `{% %}` delimiters. They will be
automatically expanded when tasks process paths.
```jsonc
{
"paths": {
"styles": {
"src": "./assets/styles",
"dest": "./www/assets/styles"
}
},
"tasks": {
"styles": [
{
"infile": "{% paths.styles.src %}/main.scss", // → ./assets/styles/main.scss
"outfile": "{% paths.styles.dest %}/main.css" // → ./www/assets/styles/main.css
}
]
}
}
```
#### `paths.url` option
The `paths.url` option defines the base URI of the project.
By default, it is used by the development server as a proxy
for an existing virtual host.
```json
{
"paths": {
"url": "locomotive-boilerplate.test"
}
}
```
#### `paths.dest` option
The `paths.dest` option defines the public web directory of the project.
By default, it is used by the development server as the base directory
to serve the website from if a proxy URI is not provided.
```json
{
"paths": {
"dest": "./www"
}
}
```
#### `tasks` option
Which assets and how they should be processed can be configured via
the `tasks` option:
```json
{
"tasks": {
"scripts": [
{
"includes": [
"./assets/scripts/app.js"
],
"outfile": "./www/assets/scripts/app.js"
}
],
"styles": [
{
"infile": "./assets/styles/main.scss",
"outfile": "./www/assets/styles/main.css"
}
]
}
}
```
See [tasks](#tasks) section, below, for details.
#### `server` option
The development server (BrowserSync) can be configured via
the `server` option:
```json
{
"server": {
"open": true,
"https": {
"key": "~/.config/valet/Certificates/{% paths.url %}.key",
"cert": "~/.config/valet/Certificates/{% paths.url %}.crt"
}
}
}
```
Visit [BrowserSync's documentation](https://browsersync.io/docs/options)
for all options.
## Tasks
The boilerplate provides a handful of tasks for handling
the most commonly processed assets.
### `concats`
A wrapper around [concat] (with optional support for globbing) for concatenating multiple files.
By default, [tiny-glob] is installed with the boilerplate.
Example:
```json
{
"concats": [
{
"label": "Application Vendors",
"includes": [
"{% paths.scripts.src %}/vendors/*.js",
"node_modules/focus-visible/dist/focus-visible.min.js",
"node_modules/vue/dist/vue.min.js",
"node_modules/vuelidate/dist/vuelidate.min.js",
"node_modules/vuelidate/dist/validators.min.js"
],
"outfile": "{% paths.scripts.dest %}/app/vendors.js"
},
{
"label": "Public Site Vendors",
"includes": [
"{% paths.scripts.src %}/vendors/*.js",
"node_modules/focus-visible/dist/focus-visible.min.js"
],
"outfile": "{% paths.scripts.dest %}/site/vendors.js"
}
]
}
```
See [`concats.js`](../build/tasks/concats.js) for details.
### `scripts`
A wrapper around [esbuild] for bundling and minifying modern JS/ES modules.
Example:
```json
{
"scripts": [
{
"label": "Application Dashboard JS",
"includes": [
"{% paths.scripts.src %}/app/dashboard.js"
],
"outfile": "{% paths.scripts.dest %}/app/dashboard.js"
},
{
"label": "Public Site JS",
"includes": [
"{% paths.scripts.src %}/site/main.js"
],
"outfile": "{% paths.scripts.dest %}/site/main.js"
}
]
}
```
See [`scripts.js`](../build/tasks/scripts.js) for details.
### `styles`
A wrapper around [node-sass] (and optionally [Autoprefixer] via [PostCSS])
for compiling and minifying Sass into CSS.
By default, [PostCSS] and [Autoprefixer] are installed with the boilerplate.
Example:
```json
{
"styles": [
{
"label": "Text Editor CSS",
"infile": "{% paths.styles.src %}/app/editor.scss",
"outfile": "{% paths.styles.dest %}/app/editor.css"
},
{
"label": "Application Dashboard CSS",
"infile": "{% paths.styles.src %}/app/dashboard.scss",
"outfile": "{% paths.styles.dest %}/app/dashboard.css"
},
{
"label": "Public Site Critical CSS",
"infile": "{% paths.styles.src %}/site/critical.scss",
"outfile": "{% paths.styles.dest %}/site/critical.css"
},
{
"label": "Public Site CSS",
"infile": "{% paths.styles.src %}/site/main.scss",
"outfile": "{% paths.styles.dest %}/site/main.css"
}
]
}
```
See [`styles.js`](../build/tasks/styles.js) for details.
### `svgs`
A wrapper around [SVG Mixer] for transforming and minifying SVG files
and generating spritesheets.
Example:
```json
{
"svgs": [
{
"label": "Application Spritesheet",
"includes": [
"{% paths.images.src %}/app/*.svg"
],
"outfile": "{% paths.svgs.dest %}/app/sprite.svg"
},
{
"label": "Public Site Spritesheet",
"includes": [
"{% paths.images.src %}/site/*.svg"
],
"outfile": "{% paths.svgs.dest %}/site/sprite.svg"
}
]
}
```
See [`svgs.js`](../build/tasks/svgs.js) for details.
[Autoprefixer]: https://npmjs.com/package/autoprefixer
[BrowserSync]: https://npmjs.com/package/browser-sync
[concat]: https://npmjs.com/package/concat
[esbuild]: https://npmjs.com/package/esbuild
[fast-glob]: https://npmjs.com/package/fast-glob
[glob]: https://npmjs.com/package/glob
[globby]: https://npmjs.com/package/globby
[Node]: https://nodejs.org/
[node-sass]: https://npmjs.com/package/node-sass
[NPM]: https://npmjs.com/
[NVM]: https://github.com/nvm-sh/nvm
[PostCSS]: https://npmjs.com/package/postcss
[SVG Mixer]: https://npmjs.com/package/svg-mixer
[tiny-glob]: https://npmjs.com/package/tiny-glob

207
docs/technologies.md Normal file
View File

@@ -0,0 +1,207 @@
# Technologies
* [Styles](#styles)
* [CSS Architecture](#css-architecture)
* [CSS Naming Convention](#css-naming-convention)
* [CSS Namespacing](#css-namespacing)
* [Example](#example-1)
* [Scripts](#scripts)
* [Example](#example-2)
* [Page transitions](#page-transitions)
* [Example](#example-3)
* [Scroll detection](#scroll-detection)
* [Example](#example-4)
## Styles
[SCSS][Sass] is a superset of CSS that adds many helpful features to improve
and modularize our styles.
We use [node-sass] (LibSass) for processing and minifying SCSS into CSS.
We also use [PostCSS] and [Autoprefixer] to parse our CSS and add
vendor prefixes for experimental features.
### CSS Architecture
The boilerplate's CSS architecture is based on [Inuit CSS][inuitcss] and [ITCSS].
* `settings`: Global variables, site-wide settings, config switches, etc.
* `tools`: Site-wide mixins and functions.
* `generic`: Low-specificity, far-reaching rulesets (e.g. resets).
* `elements`: Unclassed HTML elements (e.g. `a {}`, `blockquote {}`, `address {}`).
* `objects`: Objects, abstractions, and design patterns (e.g. `.o-layout {}`).
* `components`: Discrete, complete chunks of UI (e.g. `.c-carousel {}`).
* `utilities`: High-specificity, very explicit selectors. Overrides and helper
classes (e.g. `.u-hidden {}`)
Learn more about [Inuit CSS](https://github.com/inuitcss/inuitcss#css-directory-structure).
### CSS Naming Convention
We use a simplified [BEM] (Block, Element, Modifier) syntax:
* `.block`
* `.block_element`
* `.-modifier`
### CSS Namespacing
We namespace our classes for more UI transparency:
* `o-`: Object that it may be used in any number of unrelated contexts to the one you can currently see it in. Making modifications to these types of class could potentially have knock-on effects in a lot of other unrelated places.
* `c-`: Component is a concrete, implementation-specific piece of UI. All of the changes you make to its styles should be detectable in the context youre currently looking at. Modifying these styles should be safe and have no side effects.
* `u-`: Utility has a very specific role (often providing only one declaration) and should not be bound onto or changed. It can be reused and is not tied to any specific piece of UI.
* `s-`: Scope creates a new styling context. Similar to a Theme, but not necessarily cosmetic, these should be used sparingly—they can be open to abuse and lead to poor CSS if not used wisely.
* `is-`, `has-`: Is currently styled a certain way because of a state or condition. It tells us that the DOM currently has a temporary, optional, or short-lived style applied to it due to a certain state being invoked.
Learn about [namespacing](https://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/).
### Example \#1
```html
<div class="c-block -large">
<div class="c-block_layout o-layout">
<div class="o-layout_item u-1/2@from-medium">
<div class="c-block_heading o-h -medium">Heading</div>
</div>
<div class="o-layout_item u-1/2@from-medium">
<a class="c-block_button o-button -outline" href="#">Button</a>
</div>
</div>
</div>
```
```scss
.c-block {
&.-large {
padding: rem(60px);
}
}
.c-block_heading {
@media (max-width: $to-medium) {
.c-block.-large & {
margin-bottom: rem(40px);
}
}
}
```
## Scripts
We use [esbuild] for bundling and minifying JavaScript/ES modules.
[modularJS] is a small framework we use on top of ES modules.
* Automatically init visible modules.
* Easily call other modules methods.
* Quickly set scoped events with delegation.
* Simply select DOM elements scoped in their module.
[_source_](https://npmjs.com/package/modujs#why)
### Example \#2
```html
<div data-module-example>
<div data-example="main">
<h2>Example</h2>
</div>
<button data-example="load">More</button>
</div>
```
```js
import { module } from 'modujs';
export default class extends module {
constructor(m) {
super(m);
this.events = {
click: {
load: 'loadMore'
}
};
}
loadMore() {
this.$('main')[0].classList.add('is-loading');
}
}
```
Learn more about [modularJS].
## Page transitions
[modularLoad] is used for page transitions and lazy loading.
### Example \#3
```html
<nav>
<a href="/">Home</a>
<a href="/page" data-load="transitionName">Page</a>
</nav>
<div data-load-container>
<img data-load-src="assets/images/hello.jpg">
</div>
```
```js
import modularLoad from 'modularload';
this.load = new modularLoad({
enterDelay: 300,
transitions: {
transitionName: {
enterDelay: 450
}
}
});
```
Learn more about [modularLoad].
## Scroll detection
[Locomotive Scroll][locomotive-scroll] is used for elements in viewport
detection and smooth scrolling with parallax.
### Example \#4
```html
<div data-module-scroll>
<div data-scroll>Trigger</div>
<div data-scroll data-scroll-speed="1">Parallax</div>
</div>
```
```js
import LocomotiveScroll from 'locomotive-scroll';
this.scroll = new LocomotiveScroll({
el: this.el,
smooth: true
});
````
Learn more about [Locomotive Scroll][locomotive-scroll].
[Autoprefixer]: https://npmjs.com/package/autoprefixer
[BEM]: https://bem.info/
[BrowserSync]: https://npmjs.com/package/browser-sync
[esbuild]: https://npmjs.com/package/esbuild
[inuitcss]: https://github.com/inuitcss/inuitcss
[ITCSS]: https://itcss.io/
[locomotive-scroll]: https://npmjs.com/package/locomotive-scroll
[modularJS]: https://npmjs.com/package/modujs
[modularLoad]: https://npmjs.com/package/modularload
[node-sass]: https://npmjs.com/package/node-sass
[PostCSS]: https://npmjs.com/package/postcss
[Sass]: https://sass-lang.com/
[svg-mixer]: https://npmjs.com/package/svg-mixer
[Node]: https://nodejs.org/
[NPM]: https://npmjs.com/
[NVM]: https://github.com/nvm-sh/nvm

8
loconfig.example.json Executable file
View File

@@ -0,0 +1,8 @@
{
"server": {
"https": {
"key": "~/.config/valet/Certificates/{% paths.url %}.key",
"cert": "~/.config/valet/Certificates/{% paths.url %}.crt"
}
}
}

4691
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,21 +14,21 @@
"build": "node --experimental-json-modules --no-warnings build/build.js"
},
"dependencies": {
"locomotive-scroll": "^4.1.3",
"@barba/core": "^2.9.7",
"locomotive-scroll": "^4.1.4",
"modujs": "^1.4.2",
"modularload": "^1.2.6",
"normalize.css": "^8.0.1",
"svg4everybody": "^2.1.9"
},
"devDependencies": {
"autoprefixer": "^10.4.0",
"browser-sync": "^2.27.7",
"autoprefixer": "^10.4.4",
"browser-sync": "^2.27.9",
"concat": "^1.0.3",
"esbuild": "^0.13.12",
"esbuild": "^0.14.27",
"kleur": "^4.1.4",
"node-notifier": "^10.0.0",
"node-sass": "^6.0.1",
"postcss": "^8.3.11",
"node-notifier": "^10.0.1",
"node-sass": "^7.0.1",
"postcss": "^8.4.12",
"svg-mixer": "^2.3.14",
"tiny-glob": "^0.2.9"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -16,8 +16,8 @@
<link id="main-css" rel="stylesheet" href="assets/styles/main.css" media="print" onload="this.media='all'; this.onload=null; this.isLoaded=true">
</head>
<body data-module-load>
<div data-load-container>
<body data-module-load data-load="wrapper">
<div data-load="container">
<div class="o-scroll" data-module-scroll="main">
<header data-scroll-section>
<a href="/"><h1>Locomotive Boilerplate</h1></a>

View File

@@ -16,8 +16,8 @@
<link id="main-css" rel="stylesheet" href="assets/styles/main.css" media="print" onload="this.media='all'; this.onload=null; this.isLoaded=true">
</head>
<body data-module-load>
<div data-load-container>
<body data-module-load data-load="wrapper">
<div data-load="container">
<div class="o-scroll" data-module-scroll="main">
<header data-scroll-section>
<a href="/"><h1>Locomotive Boilerplate</h1></a>

View File

@@ -33,8 +33,8 @@
onload="this.media='all'; this.onload=null; this.isLoaded=true">
</head>
<body data-module-load>
<div data-load-container>
<body data-module-load data-load="wrapper">
<div data-load="container">
<div class="o-scroll" data-module-scroll="main">
<header data-scroll-section>
<a href="/">