mirror of
https://github.com/locomotivemtl/locomotive-boilerplate.git
synced 2026-01-15 00:55:08 +08:00
Compare commits
9 Commits
feature/la
...
devenini/d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb5957024d | ||
|
|
68dd79caef | ||
|
|
be89145728 | ||
|
|
8f3034d54a | ||
|
|
3fa1de473c | ||
|
|
ecf60ee507 | ||
|
|
9632d6270e | ||
|
|
bc3a1a6934 | ||
|
|
2f6b353616 |
7
.prettierrc.json
Normal file
7
.prettierrc.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
}
|
||||
@@ -23,7 +23,7 @@ Learn more about [languages and technologies](docs/technologies.md).
|
||||
|
||||
Make sure you have the following installed:
|
||||
|
||||
* [Node] — at least 17.9, the latest LTS is recommended.
|
||||
* [Node] — at least 18.13, the latest LTS is recommended.
|
||||
* [NPM] — at least 8.0, the latest LTS is recommended.
|
||||
|
||||
> 💡 You can use [NVM] to install and use different versions of Node via the command-line.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": 1691082391092
|
||||
"version": 1675197299100
|
||||
}
|
||||
@@ -1,65 +1,79 @@
|
||||
import modular from 'modujs';
|
||||
import * as modules from './modules';
|
||||
import globals from './globals';
|
||||
import { debounce } from './utils/tickers'
|
||||
import { $html } from './utils/dom';
|
||||
import { ENV, FONT, CUSTOM_EVENT, CSS_CLASS } from './config'
|
||||
import { isFontLoadingAPIAvailable, loadFonts } from './utils/fonts';
|
||||
import modular from 'modujs'
|
||||
import * as modules from './modules'
|
||||
import globals from './globals'
|
||||
import { html } from './utils/environment'
|
||||
import config from './config'
|
||||
import { isFontLoadingAPIAvailable, loadFonts } from './utils/fonts'
|
||||
|
||||
const app = new modular({
|
||||
modules: modules,
|
||||
});
|
||||
})
|
||||
|
||||
window.onload = (event) => {
|
||||
const $style = document.getElementById('main-css');
|
||||
const $style = document.getElementById('main-css')
|
||||
|
||||
if ($style) {
|
||||
if ($style.isLoaded) {
|
||||
init();
|
||||
init()
|
||||
} else {
|
||||
$style.addEventListener('load', (event) => {
|
||||
init();
|
||||
});
|
||||
init()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.warn('The "main-css" stylesheet not found');
|
||||
console.warn('The "main-css" stylesheet not found')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const EAGER_FONTS = [
|
||||
{ family: 'Source Sans', style: 'normal', weight: 400 },
|
||||
{ family: 'Source Sans', style: 'normal', weight: 700 },
|
||||
]
|
||||
|
||||
function init() {
|
||||
globals();
|
||||
globals()
|
||||
|
||||
app.init(app);
|
||||
app.init(app)
|
||||
|
||||
$html.classList.add(CSS_CLASS.LOADED);
|
||||
$html.classList.add(CSS_CLASS.READY);
|
||||
$html.classList.remove(CSS_CLASS.LOADING);
|
||||
|
||||
// Bind window resize event with default vars
|
||||
const resizeEndEvent = new CustomEvent(CUSTOM_EVENT.RESIZE_END)
|
||||
window.addEventListener('resize', () => {
|
||||
$html.style.setProperty('--vw', `${document.documentElement.clientWidth * 0.01}px`)
|
||||
debounce(() => {
|
||||
window.dispatchEvent(resizeEndEvent)
|
||||
}, 200, false)
|
||||
})
|
||||
html.classList.add('is-loaded')
|
||||
html.classList.add('is-ready')
|
||||
html.classList.remove('is-loading')
|
||||
|
||||
/**
|
||||
* Eagerly load the following fonts.
|
||||
*/
|
||||
if (isFontLoadingAPIAvailable) {
|
||||
loadFonts(FONT.EAGER, ENV.IS_DEV).then((eagerFonts) => {
|
||||
$html.classList.add(CSS_CLASS.FONTS_LOADED);
|
||||
loadFonts(EAGER_FONTS, config.IS_DEV).then((eagerFonts) => {
|
||||
html.classList.add('fonts-loaded')
|
||||
|
||||
if (ENV.IS_DEV) {
|
||||
console.group('Eager fonts loaded!', eagerFonts.length, '/', document.fonts.size);
|
||||
if (config.IS_DEV) {
|
||||
console.group(
|
||||
'Eager fonts loaded!',
|
||||
eagerFonts.length,
|
||||
'/',
|
||||
document.fonts.size
|
||||
)
|
||||
console.group('State of eager fonts:')
|
||||
eagerFonts.forEach((font) => console.log(font.family, font.style, font.weight, font.status/*, font*/))
|
||||
eagerFonts.forEach((font) =>
|
||||
console.log(
|
||||
font.family,
|
||||
font.style,
|
||||
font.weight,
|
||||
font.status /*, font*/
|
||||
)
|
||||
)
|
||||
console.groupEnd()
|
||||
console.group('State of all fonts:')
|
||||
document.fonts.forEach((font) => console.log(font.family, font.style, font.weight, font.status/*, font*/))
|
||||
document.fonts.forEach((font) =>
|
||||
console.log(
|
||||
font.family,
|
||||
font.style,
|
||||
font.weight,
|
||||
font.status /*, font*/
|
||||
)
|
||||
)
|
||||
console.groupEnd()
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,51 +7,18 @@
|
||||
* > (since `process` is a Node API, not a web API).
|
||||
* > — https://esbuild.github.io/api/#platform
|
||||
*/
|
||||
const env = process.env.NODE_ENV
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV
|
||||
const IS_DESKTOP = typeof window.orientation === 'undefined'
|
||||
export default config = Object.freeze({
|
||||
// Environments
|
||||
ENV: env,
|
||||
IS_PROD: env === 'production',
|
||||
IS_DEV: env === 'development',
|
||||
|
||||
// Main environment variables
|
||||
const ENV = Object.freeze({
|
||||
// Node environment
|
||||
NAME: NODE_ENV,
|
||||
IS_PROD: NODE_ENV === 'production',
|
||||
IS_DEV: NODE_ENV === 'development',
|
||||
|
||||
// Device
|
||||
IS_DESKTOP,
|
||||
IS_MOBILE: !IS_DESKTOP,
|
||||
// CSS class names
|
||||
CSS_CLASS: {
|
||||
LOADING: 'is-loading',
|
||||
READY: 'is-ready',
|
||||
LOADED: 'is-loaded',
|
||||
},
|
||||
})
|
||||
|
||||
// Main CSS classes used within the project
|
||||
const CSS_CLASS = Object.freeze({
|
||||
LOADING: 'is-loading',
|
||||
LOADED: 'is-loaded',
|
||||
READY: 'is-ready',
|
||||
FONTS_LOADED: 'fonts-loaded',
|
||||
IMAGE: "c-image",
|
||||
IMAGE_LAZY_LOADED: "-lazy-loaded",
|
||||
IMAGE_LAZY_LOADING: "-lazy-loading",
|
||||
IMAGE_LAZY_ERROR: "-lazy-error",
|
||||
})
|
||||
|
||||
// Custom js events
|
||||
const CUSTOM_EVENT = Object.freeze({
|
||||
RESIZE_END: 'loco.resizeEnd',
|
||||
// ...
|
||||
})
|
||||
|
||||
// Fonts parameters
|
||||
const FONT = Object.freeze({
|
||||
EAGER: [
|
||||
{ family: 'Source Sans', style: 'normal', weight: 400 },
|
||||
{ family: 'Source Sans', style: 'normal', weight: 700 },
|
||||
],
|
||||
})
|
||||
|
||||
export {
|
||||
ENV,
|
||||
CSS_CLASS,
|
||||
CUSTOM_EVENT,
|
||||
FONT,
|
||||
}
|
||||
|
||||
@@ -1,29 +1,23 @@
|
||||
import svg4everybody from 'svg4everybody';
|
||||
import { ENV } from './config';
|
||||
import { triggerLazyloadCallbacks } from './utils/image';
|
||||
import svg4everybody from 'svg4everybody'
|
||||
import config from './config'
|
||||
|
||||
// Dynamic imports for development mode only
|
||||
let gridHelper;
|
||||
(async () => {
|
||||
if (ENV.IS_DEV) {
|
||||
const gridHelperModule = await import('./utils/grid-helper');
|
||||
gridHelper = gridHelperModule?.gridHelper;
|
||||
let gridHelper
|
||||
;(async () => {
|
||||
if (config.IS_DEV) {
|
||||
const gridHelperModule = await import('./utils/grid-helper')
|
||||
gridHelper = gridHelperModule?.gridHelper
|
||||
}
|
||||
})();
|
||||
})()
|
||||
|
||||
export default function () {
|
||||
/**
|
||||
* Use external SVG spritemaps
|
||||
*/
|
||||
svg4everybody();
|
||||
svg4everybody()
|
||||
|
||||
/**
|
||||
* Add grid helper
|
||||
*/
|
||||
gridHelper?.();
|
||||
|
||||
/**
|
||||
* Trigger lazyload
|
||||
*/
|
||||
triggerLazyloadCallbacks();
|
||||
gridHelper?.()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export {default as Example} from './modules/Example';
|
||||
export {default as Load} from './modules/Load';
|
||||
export {default as Scroll} from './modules/Scroll';
|
||||
export { default as Example } from './modules/Example'
|
||||
export { default as Load } from './modules/Load'
|
||||
export { default as Scroll } from './modules/Scroll'
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { module } from 'modujs';
|
||||
import { FONT } from '../config';
|
||||
import { whenReady } from '../utils/fonts';
|
||||
import { module as Module } from 'modujs'
|
||||
import { EAGER_FONTS } from '../app'
|
||||
import { whenReady } from '../utils/fonts'
|
||||
|
||||
export default class extends module {
|
||||
export default class extends Module {
|
||||
constructor(m) {
|
||||
super(m);
|
||||
super(m)
|
||||
}
|
||||
|
||||
init() {
|
||||
whenReady(FONT.EAGER).then((fonts) => this.onFontsLoaded(fonts));
|
||||
whenReady(EAGER_FONTS).then((fonts) => this.onFontsLoaded(fonts))
|
||||
}
|
||||
|
||||
onFontsLoaded(fonts) {
|
||||
|
||||
@@ -1,35 +1,22 @@
|
||||
import { module } from 'modujs';
|
||||
import modularLoad from 'modularload';
|
||||
import { resetLazyloadCallbacks, triggerLazyloadCallbacks } from "../utils/image";
|
||||
import { module as Module } from 'modujs'
|
||||
import modularLoad from 'modularload'
|
||||
|
||||
export default class extends module {
|
||||
export default class extends Module {
|
||||
constructor(m) {
|
||||
super(m);
|
||||
super(m)
|
||||
}
|
||||
|
||||
init() {
|
||||
this.load = new modularLoad({
|
||||
const load = new modularLoad({
|
||||
enterDelay: 0,
|
||||
transitions: {
|
||||
customTransition: {}
|
||||
}
|
||||
});
|
||||
customTransition: {},
|
||||
},
|
||||
})
|
||||
|
||||
this.load.on('loaded', (transition, oldContainer, newContainer) => {
|
||||
this.call('destroy', oldContainer, 'app');
|
||||
this.call('update', newContainer, 'app');
|
||||
|
||||
/**
|
||||
* Trigger lazyload
|
||||
*/
|
||||
triggerLazyloadCallbacks();
|
||||
});
|
||||
|
||||
this.load.on("loading", () => {
|
||||
/**
|
||||
* Remove previous lazyload callbacks
|
||||
*/
|
||||
resetLazyloadCallbacks();
|
||||
});
|
||||
load.on('loaded', (transition, oldContainer, newContainer) => {
|
||||
this.call('destroy', oldContainer, 'app')
|
||||
this.call('update', newContainer, 'app')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,52 @@
|
||||
import { module } from 'modujs'
|
||||
import { module as Module } from 'modujs'
|
||||
import { lazyLoadImage } from '../utils/image'
|
||||
import LocomotiveScroll from 'locomotive-scroll'
|
||||
|
||||
export default class extends module {
|
||||
export default class extends Module {
|
||||
constructor(m) {
|
||||
super(m);
|
||||
super(m)
|
||||
}
|
||||
|
||||
init() {
|
||||
this.scroll = new LocomotiveScroll({
|
||||
modularInstance: this,
|
||||
el: this.el,
|
||||
smooth: true,
|
||||
})
|
||||
|
||||
// // Force scroll to top
|
||||
// if (history.scrollRestoration) {
|
||||
// history.scrollRestoration = 'manual'
|
||||
// window.scrollTo(0, 0)
|
||||
// }
|
||||
}
|
||||
this.scroll.on('call', (func, way, obj, id) => {
|
||||
// Using modularJS
|
||||
this.call(func[0], { way, obj }, func[1], func[2])
|
||||
})
|
||||
|
||||
scrollTo(params) {
|
||||
let { target, ...options } = params
|
||||
|
||||
options = Object.assign({
|
||||
// Defaults
|
||||
duration: 1,
|
||||
}, options)
|
||||
|
||||
this.scroll?.scrollTo(target, options)
|
||||
this.scroll.on('scroll', (args) => {
|
||||
// console.log(args.scroll);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe new scroll elements
|
||||
*
|
||||
* @param $newContainer (HTMLElement)
|
||||
*/
|
||||
addScrollElements($newContainer) {
|
||||
this.scroll?.addScrollElements($newContainer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unobserve scroll elements
|
||||
*
|
||||
* @param $oldContainer (HTMLElement)
|
||||
*/
|
||||
removeScrollElements($oldContainer) {
|
||||
this.scroll?.removeScrollElements($oldContainer)
|
||||
* Lazy load the related image.
|
||||
*
|
||||
* @see ../utils/image.js
|
||||
*
|
||||
* It is recommended to wrap your `<img>` into an element with the
|
||||
* CSS class name `.c-lazy`. The CSS class name modifier `.-lazy-loaded`
|
||||
* will be applied on both the image and the parent wrapper.
|
||||
*
|
||||
* ```html
|
||||
* <div class="c-lazy o-ratio u-4:3">
|
||||
* <img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=1" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* @param {LocomotiveScroll} args - The Locomotive Scroll instance.
|
||||
*/
|
||||
lazyLoad(args) {
|
||||
lazyLoadImage(args.obj.el, null, () => {
|
||||
//callback
|
||||
})
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.scroll.destroy();
|
||||
this.scroll.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
const $html = document.documentElement
|
||||
const $body = document.body
|
||||
|
||||
export {
|
||||
$html,
|
||||
$body,
|
||||
}
|
||||
8
assets/scripts/utils/environment.js
Normal file
8
assets/scripts/utils/environment.js
Normal file
@@ -0,0 +1,8 @@
|
||||
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 }
|
||||
@@ -23,7 +23,7 @@
|
||||
* @property {string} [weight] - The weight used by the font in our CSS.
|
||||
*/
|
||||
|
||||
const isFontLoadingAPIAvailable = ('fonts' in document);
|
||||
const isFontLoadingAPIAvailable = 'fonts' in document
|
||||
|
||||
/**
|
||||
* Determines if the given font matches the given `FontFaceReference`.
|
||||
@@ -33,15 +33,14 @@ const isFontLoadingAPIAvailable = ('fonts' in document);
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function conformsToReference(font, criterion)
|
||||
{
|
||||
for (const [ key, value ] of Object.entries(criterion)) {
|
||||
function conformsToReference(font, criterion) {
|
||||
for (const [key, value] of Object.entries(criterion)) {
|
||||
switch (key) {
|
||||
case 'family': {
|
||||
if (trim(font[key]) !== value) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
case 'weight': {
|
||||
@@ -54,21 +53,21 @@ function conformsToReference(font, criterion)
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#common_weight_name_mapping
|
||||
*/
|
||||
if (font[key] != value) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
if (font[key] !== value) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,24 +78,21 @@ function conformsToReference(font, criterion)
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function conformsToShorthand(font, criterion)
|
||||
{
|
||||
const family = trim(font.family);
|
||||
function conformsToShorthand(font, criterion) {
|
||||
const family = trim(font.family)
|
||||
|
||||
if (trim(family) === criterion) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
criterion.endsWith(trim(family)) && (
|
||||
criterion.match(font.weight) ||
|
||||
criterion.match(font.style)
|
||||
)
|
||||
criterion.endsWith(trim(family)) &&
|
||||
(criterion.match(font.weight) || criterion.match(font.style))
|
||||
) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,15 +103,14 @@ function conformsToShorthand(font, criterion)
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function conformsToAnyReference(font, criteria)
|
||||
{
|
||||
function conformsToAnyReference(font, criteria) {
|
||||
for (const criterion of criteria) {
|
||||
if (conformsToReference(font, criterion)) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,17 +121,16 @@ function conformsToAnyReference(font, criteria)
|
||||
*
|
||||
* @returns {FontFace[]}
|
||||
*/
|
||||
function findManyByReference(search)
|
||||
{
|
||||
const found = [];
|
||||
function findManyByReference(search) {
|
||||
const found = []
|
||||
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToReference(font, search)) {
|
||||
found.push(font);
|
||||
found.push(font)
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
return found
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,17 +141,16 @@ function findManyByReference(search)
|
||||
*
|
||||
* @returns {FontFace[]}
|
||||
*/
|
||||
function findManyByShorthand(search)
|
||||
{
|
||||
const found = [];
|
||||
function findManyByShorthand(search) {
|
||||
const found = []
|
||||
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToShorthand(font, search)) {
|
||||
found.push(font);
|
||||
found.push(font)
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
return found
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,15 +161,14 @@ function findManyByShorthand(search)
|
||||
*
|
||||
* @returns {?FontFace}
|
||||
*/
|
||||
function findOneByReference(search)
|
||||
{
|
||||
function findOneByReference(search) {
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToReference(font, criterion)) {
|
||||
return font;
|
||||
return font
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,15 +184,14 @@ function findOneByReference(search)
|
||||
*
|
||||
* @returns {?FontFace}
|
||||
*/
|
||||
function findOneByShorthand(search)
|
||||
{
|
||||
function findOneByShorthand(search) {
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToShorthand(font, search)) {
|
||||
return font;
|
||||
return font
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,16 +211,16 @@ function getAny(search) {
|
||||
if (search) {
|
||||
switch (typeof search) {
|
||||
case 'string':
|
||||
return findOneByShorthand(search);
|
||||
return findOneByShorthand(search)
|
||||
|
||||
case 'object':
|
||||
return findOneByReference(search);
|
||||
return findOneByReference(search)
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(
|
||||
'Expected font query to be font shorthand or font reference'
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,30 +235,30 @@ function getAny(search) {
|
||||
*/
|
||||
function getMany(queries) {
|
||||
if (!Array.isArray(queries)) {
|
||||
queries = [ queries ];
|
||||
queries = [queries]
|
||||
}
|
||||
|
||||
const found = new Set();
|
||||
const found = new Set()
|
||||
|
||||
queries.forEach((search) => {
|
||||
if (search) {
|
||||
switch (typeof search) {
|
||||
case 'string':
|
||||
found.add(...findManyByShorthand(search));
|
||||
return;
|
||||
found.add(...findManyByShorthand(search))
|
||||
return
|
||||
|
||||
case 'object':
|
||||
found.add(...findManyByReference(search));
|
||||
return;
|
||||
found.add(...findManyByReference(search))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(
|
||||
'Expected font query to be font shorthand or font reference'
|
||||
);
|
||||
)
|
||||
})
|
||||
|
||||
return [ ...found ];
|
||||
return [...found]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,10 +274,10 @@ function getMany(queries) {
|
||||
*/
|
||||
function hasAny(search) {
|
||||
if (search instanceof FontFace) {
|
||||
return document.fonts.has(search);
|
||||
return document.fonts.has(search)
|
||||
}
|
||||
|
||||
return getAny(search) != null;
|
||||
return getAny(search) != null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,15 +293,12 @@ function hasAny(search) {
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function loadFonts(fontsToLoad, debug = false)
|
||||
{
|
||||
async function loadFonts(fontsToLoad, debug = false) {
|
||||
if ((fontsToLoad.size ?? fontsToLoad.length) === 0) {
|
||||
throw new TypeError(
|
||||
'Expected at least one font'
|
||||
);
|
||||
throw new TypeError('Expected at least one font')
|
||||
}
|
||||
|
||||
return await loadFontsWithAPI([ ...fontsToLoad ], debug);
|
||||
return await loadFontsWithAPI([...fontsToLoad], debug)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,12 +308,11 @@ async function loadFonts(fontsToLoad, debug = false)
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function loadFontFaceWithAPI(font)
|
||||
{
|
||||
return await (font.status === 'unloaded'
|
||||
? font.load()
|
||||
: font.loaded
|
||||
).then((font) => font, (err) => font)
|
||||
async function loadFontFaceWithAPI(font) {
|
||||
return await (font.status === 'unloaded' ? font.load() : font.loaded).then(
|
||||
(font) => font,
|
||||
(err) => font
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,31 +323,34 @@ async function loadFontFaceWithAPI(font)
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function loadFontsWithAPI(fontsToLoad, debug = false)
|
||||
{
|
||||
debug && console.group('[loadFonts:API]', fontsToLoad.length, '/', document.fonts.size);
|
||||
async function loadFontsWithAPI(fontsToLoad, debug = false) {
|
||||
debug &&
|
||||
console.group(
|
||||
'[loadFonts:API]',
|
||||
fontsToLoad.length,
|
||||
'/',
|
||||
document.fonts.size
|
||||
)
|
||||
|
||||
const fontsToBeLoaded = [];
|
||||
const fontsToBeLoaded = []
|
||||
|
||||
for (const fontToLoad of fontsToLoad) {
|
||||
if (fontToLoad instanceof FontFace) {
|
||||
if (!document.fonts.has(fontToLoad)) {
|
||||
document.fonts.add(fontToLoad);
|
||||
document.fonts.add(fontToLoad)
|
||||
}
|
||||
|
||||
fontsToBeLoaded.push(
|
||||
loadFontFaceWithAPI(fontToLoad)
|
||||
);
|
||||
fontsToBeLoaded.push(loadFontFaceWithAPI(fontToLoad))
|
||||
} else {
|
||||
fontsToBeLoaded.push(
|
||||
...getMany(fontToLoad).map((font) => loadFontFaceWithAPI(font))
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
debug && console.groupEnd();
|
||||
debug && console.groupEnd()
|
||||
|
||||
return await Promise.all(fontsToBeLoaded);
|
||||
return await Promise.all(fontsToBeLoaded)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,7 +364,7 @@ async function loadFontsWithAPI(fontsToLoad, debug = false)
|
||||
* @returns {string}
|
||||
*/
|
||||
function trim(value) {
|
||||
return value.replace(/['"]+/g, '');
|
||||
return value.replace(/['"]+/g, '')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,11 +375,10 @@ function trim(value) {
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function whenReady(queries)
|
||||
{
|
||||
const fonts = getMany(queries);
|
||||
async function whenReady(queries) {
|
||||
const fonts = getMany(queries)
|
||||
|
||||
return await Promise.all(fonts.map((font) => font.loaded));
|
||||
return await Promise.all(fonts.map((font) => font.loaded))
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
* @property {string} [rgbaColor=GRID_HELPER_RGBA_COLOR] - RGBA color for the grid appearence.
|
||||
*/
|
||||
|
||||
const GRID_HELPER_GUTTER_CSS_VAR = '--grid-gutter';
|
||||
const GRID_HELPER_MARGIN_CSS_VAR = '--grid-margin';
|
||||
const GRID_HELPER_RGBA_COLOR = 'rgba(255, 0, 0, .1)';
|
||||
const GRID_HELPER_GUTTER_CSS_VAR = '--grid-gutter'
|
||||
const GRID_HELPER_MARGIN_CSS_VAR = '--grid-margin'
|
||||
const GRID_HELPER_RGBA_COLOR = 'rgba(255, 0, 0, .1)'
|
||||
|
||||
/**
|
||||
* Create a grid helper
|
||||
@@ -31,15 +31,15 @@ function gridHelper({
|
||||
rgbaColor = GRID_HELPER_RGBA_COLOR,
|
||||
} = {}) {
|
||||
// Set grid container
|
||||
const $gridContainer = document.createElement('div');
|
||||
document.body.append($gridContainer);
|
||||
const $gridContainer = document.createElement('div')
|
||||
document.body.append($gridContainer)
|
||||
|
||||
// Set grid appearence
|
||||
setGridHelperColumns($gridContainer, rgbaColor);
|
||||
setGridHelperStyles($gridContainer, gutterCssVar, marginCssVar);
|
||||
setGridHelperColumns($gridContainer, rgbaColor)
|
||||
setGridHelperStyles($gridContainer, gutterCssVar, marginCssVar)
|
||||
|
||||
// Set grid interactivity
|
||||
setGridEvents($gridContainer, rgbaColor);
|
||||
setGridEvents($gridContainer, rgbaColor)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,19 +51,19 @@ function gridHelper({
|
||||
*
|
||||
*/
|
||||
function setGridHelperStyles($container, gutterCssVar, marginCssVar) {
|
||||
const elStyles = $container.style;
|
||||
elStyles.zIndex = '10000';
|
||||
elStyles.position = 'fixed';
|
||||
elStyles.top = '0';
|
||||
elStyles.left = '0';
|
||||
elStyles.display = 'flex';
|
||||
elStyles.width = '100%';
|
||||
elStyles.height = '100%';
|
||||
elStyles.columnGap = `var(${gutterCssVar}, 0)`;
|
||||
elStyles.paddingLeft = `var(${marginCssVar}, 0)`;
|
||||
elStyles.paddingRight = `var(${marginCssVar}, 0)`;
|
||||
elStyles.pointerEvents = 'none';
|
||||
elStyles.visibility = 'hidden';
|
||||
const elStyles = $container.style
|
||||
elStyles.zIndex = '10000'
|
||||
elStyles.position = 'fixed'
|
||||
elStyles.top = '0'
|
||||
elStyles.left = '0'
|
||||
elStyles.display = 'flex'
|
||||
elStyles.width = '100%'
|
||||
elStyles.height = '100%'
|
||||
elStyles.columnGap = `var(${gutterCssVar}, 0)`
|
||||
elStyles.paddingLeft = `var(${marginCssVar}, 0)`
|
||||
elStyles.paddingRight = `var(${marginCssVar}, 0)`
|
||||
elStyles.pointerEvents = 'none'
|
||||
elStyles.visibility = 'hidden'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,19 +75,19 @@ function setGridHelperStyles($container, gutterCssVar, marginCssVar) {
|
||||
*/
|
||||
function setGridHelperColumns($container, rgbaColor) {
|
||||
// Clear columns
|
||||
$container.innerHTML = '';
|
||||
$container.innerHTML = ''
|
||||
|
||||
// Loop through columns
|
||||
const columns = Number(
|
||||
window.getComputedStyle($container).getPropertyValue('--grid-columns')
|
||||
);
|
||||
)
|
||||
|
||||
let $col;
|
||||
let $col
|
||||
for (var i = 0; i < columns; i++) {
|
||||
$col = document.createElement('div');
|
||||
$col.style.flex = '1 1 0';
|
||||
$col.style.backgroundColor = rgbaColor;
|
||||
$container.appendChild($col);
|
||||
$col = document.createElement('div')
|
||||
$col.style.flex = '1 1 0'
|
||||
$col.style.backgroundColor = rgbaColor
|
||||
$container.appendChild($col)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,33 +106,33 @@ function setGridEvents($container, rgbaColor) {
|
||||
window.addEventListener(
|
||||
'resize',
|
||||
setGridHelperColumns($container, rgbaColor)
|
||||
);
|
||||
)
|
||||
|
||||
// Toggle grid
|
||||
let ctrlDown = false;
|
||||
let isActive = false;
|
||||
let ctrlDown = false
|
||||
let isActive = false
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key == 'Control') {
|
||||
ctrlDown = true;
|
||||
ctrlDown = true
|
||||
} else {
|
||||
if (ctrlDown && e.key == 'g') {
|
||||
if (isActive) {
|
||||
$container.style.visibility = 'hidden';
|
||||
$container.style.visibility = 'hidden'
|
||||
} else {
|
||||
$container.style.visibility = 'visible';
|
||||
$container.style.visibility = 'visible'
|
||||
}
|
||||
|
||||
isActive = !isActive;
|
||||
isActive = !isActive
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
document.addEventListener('keyup', (e) => {
|
||||
if (e.key == 'Control') {
|
||||
ctrlDown = false;
|
||||
ctrlDown = false
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
export { gridHelper };
|
||||
export { gridHelper }
|
||||
|
||||
@@ -9,13 +9,13 @@ const escapeHtml = (str) =>
|
||||
/[&<>'"]/g,
|
||||
(tag) =>
|
||||
({
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
"'": "'",
|
||||
'"': """,
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
"'": ''',
|
||||
'"': '"',
|
||||
}[tag])
|
||||
);
|
||||
)
|
||||
|
||||
/**
|
||||
* Unescape HTML string
|
||||
@@ -25,11 +25,11 @@ const escapeHtml = (str) =>
|
||||
|
||||
const unescapeHtml = (str) =>
|
||||
str
|
||||
.replace("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace("'", "'")
|
||||
.replace(""", '"');
|
||||
.replace('&', '&')
|
||||
.replace('<', '<')
|
||||
.replace('>', '>')
|
||||
.replace(''', "'")
|
||||
.replace('"', '"')
|
||||
|
||||
/**
|
||||
* Get element data attributes
|
||||
@@ -39,39 +39,39 @@ const unescapeHtml = (str) =>
|
||||
|
||||
const getNodeData = (node) => {
|
||||
// All attributes
|
||||
const attributes = node.attributes;
|
||||
const attributes = node.attributes
|
||||
|
||||
// Regex Pattern
|
||||
const pattern = /^data\-(.+)$/;
|
||||
const pattern = /^data\-(.+)$/
|
||||
|
||||
// Output
|
||||
const data = {};
|
||||
const data = {}
|
||||
|
||||
for (let i in attributes) {
|
||||
if (!attributes[i]) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
// Attributes name (ex: data-module)
|
||||
let name = attributes[i].name;
|
||||
let name = attributes[i].name
|
||||
|
||||
// This happens.
|
||||
if (!name) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
let match = name.match(pattern);
|
||||
let match = name.match(pattern)
|
||||
if (!match) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
// If this throws an error, you have some
|
||||
// serious problems in your HTML.
|
||||
data[match[1]] = getData(node.getAttribute(name));
|
||||
data[match[1]] = getData(node.getAttribute(name))
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse value to data type.
|
||||
@@ -81,31 +81,31 @@ const getNodeData = (node) => {
|
||||
* @return {mixed} value in its natural data type
|
||||
*/
|
||||
|
||||
const rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;
|
||||
const rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/
|
||||
const getData = (data) => {
|
||||
if (data === "true") {
|
||||
return true;
|
||||
if (data === 'true') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (data === "false") {
|
||||
return false;
|
||||
if (data === 'false') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (data === "null") {
|
||||
return null;
|
||||
if (data === 'null') {
|
||||
return null
|
||||
}
|
||||
|
||||
// Only convert to a number if it doesn't change the string
|
||||
if (data === +data + "") {
|
||||
return +data;
|
||||
if (data === +data + '') {
|
||||
return +data
|
||||
}
|
||||
|
||||
if (rbrace.test(data)) {
|
||||
return JSON.parse(data);
|
||||
return JSON.parse(data)
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all the parent nodes of the given node
|
||||
@@ -115,49 +115,15 @@ const getData = (data) => {
|
||||
|
||||
const getParents = ($el) => {
|
||||
// Set up a parent array
|
||||
let parents = [];
|
||||
let parents = []
|
||||
|
||||
// Push each parent element to the array
|
||||
for (; $el && $el !== document; $el = $el.parentNode) {
|
||||
parents.push($el);
|
||||
parents.push($el)
|
||||
}
|
||||
|
||||
// Return our parent array
|
||||
return parents;
|
||||
};
|
||||
return parents
|
||||
}
|
||||
|
||||
// https://gomakethings.com/how-to-get-the-closest-parent-element-with-a-matching-selector-using-vanilla-javascript/
|
||||
const queryClosestParent = ($el, selector) => {
|
||||
// Element.matches() polyfill
|
||||
if (!Element.prototype.matches) {
|
||||
Element.prototype.matches =
|
||||
Element.prototype.matchesSelector ||
|
||||
Element.prototype.mozMatchesSelector ||
|
||||
Element.prototype.msMatchesSelector ||
|
||||
Element.prototype.oMatchesSelector ||
|
||||
Element.prototype.webkitMatchesSelector ||
|
||||
function (s) {
|
||||
var matches = (
|
||||
this.document || this.ownerDocument
|
||||
).querySelectorAll(s),
|
||||
i = matches.length;
|
||||
while (--i >= 0 && matches.item(i) !== this) {}
|
||||
return i > -1;
|
||||
};
|
||||
}
|
||||
|
||||
// Get the closest matching element
|
||||
for (; $el && $el !== document; $el = $el.parentNode) {
|
||||
if ($el.matches(selector)) return $el;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export {
|
||||
escapeHtml,
|
||||
unescapeHtml,
|
||||
getNodeData,
|
||||
getData,
|
||||
getParents,
|
||||
queryClosestParent,
|
||||
};
|
||||
export { escapeHtml, unescapeHtml, getNodeData, getData, getParents }
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { CSS_CLASS } from '../config'
|
||||
import { queryClosestParent } from './html'
|
||||
|
||||
/**
|
||||
* Get an image meta data
|
||||
*
|
||||
@@ -8,14 +5,13 @@ import { queryClosestParent } from './html'
|
||||
* @return {object} The given image meta data
|
||||
*/
|
||||
|
||||
const getImageMetadata = $img => ({
|
||||
const getImageMetadata = ($img) => ({
|
||||
url: $img.src,
|
||||
width: $img.naturalWidth,
|
||||
height: $img.naturalHeight,
|
||||
ratio: $img.naturalWidth / $img.naturalHeight,
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Load the given image.
|
||||
*
|
||||
@@ -39,11 +35,13 @@ const loadImage = (url, options = {}) => {
|
||||
})
|
||||
}
|
||||
|
||||
if($img.decode) {
|
||||
if ($img.decode) {
|
||||
$img.src = url
|
||||
$img.decode().then(loadCallback).catch(e => {
|
||||
reject(e)
|
||||
})
|
||||
$img.decode()
|
||||
.then(loadCallback)
|
||||
.catch((e) => {
|
||||
reject(e)
|
||||
})
|
||||
} else {
|
||||
$img.onload = loadCallback
|
||||
$img.onerror = (e) => {
|
||||
@@ -54,7 +52,6 @@ const loadImage = (url, options = {}) => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lazy load the given image.
|
||||
*
|
||||
@@ -69,7 +66,7 @@ const LAZY_LOADED_IMAGES = []
|
||||
const lazyLoadImage = async ($el, url, callback) => {
|
||||
let src = url ? url : $el.dataset.src
|
||||
|
||||
let loadedImage = LAZY_LOADED_IMAGES.find(image => image.url === src)
|
||||
let loadedImage = LAZY_LOADED_IMAGES.find((image) => image.url === src)
|
||||
|
||||
if (!loadedImage) {
|
||||
loadedImage = await loadImage(src)
|
||||
@@ -81,7 +78,7 @@ const lazyLoadImage = async ($el, url, callback) => {
|
||||
LAZY_LOADED_IMAGES.push(loadedImage)
|
||||
}
|
||||
|
||||
if($el.src === src) {
|
||||
if ($el.src === src) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,111 +89,17 @@ const lazyLoadImage = async ($el, url, callback) => {
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
let lazyParent = $el.closest(`.${CSS_CLASS.IMAGE}`)
|
||||
let lazyParent = $el.closest('.c-lazy')
|
||||
|
||||
if(lazyParent) {
|
||||
lazyParent.classList.add(CSS_CLASS.IMAGE_LAZY_LOADED)
|
||||
if (lazyParent) {
|
||||
lazyParent.classList.add('-lazy-loaded')
|
||||
lazyParent.style.backgroundImage = ''
|
||||
}
|
||||
|
||||
$el.classList.add(CSS_CLASS.IMAGE_LAZY_LOADED)
|
||||
$el.classList.add('-lazy-loaded')
|
||||
|
||||
callback?.()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazyload Callbacks
|
||||
*
|
||||
*/
|
||||
const lazyImageLoad = (e) => {
|
||||
const $img = e.currentTarget;
|
||||
const $parent = queryClosestParent($img, `.${CSS_CLASS.IMAGE}`);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if ($parent) {
|
||||
$parent.classList.remove(CSS_CLASS.IMAGE_LAZY_LOADING);
|
||||
$parent.classList.add(CSS_CLASS.IMAGE_LAZY_LOADED);
|
||||
}
|
||||
|
||||
$img.classList.add(CSS_CLASS.IMAGE_LAZY_LOADED);
|
||||
});
|
||||
};
|
||||
|
||||
const lazyImageError = (e) => {
|
||||
const $img = e.currentTarget;
|
||||
const $parent = queryClosestParent($img, `.${CSS_CLASS.IMAGE}`);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if ($parent) {
|
||||
$parent.classList.remove(CSS_CLASS.IMAGE_LAZY_LOADING);
|
||||
$parent.classList.add(CSS_CLASS.IMAGE_LAZY_ERROR);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* Trigger Lazyload Callbacks */
|
||||
const triggerLazyloadCallbacks = ($lazyImagesArgs) => {
|
||||
const $lazyImages = $lazyImagesArgs
|
||||
? $lazyImagesArgs
|
||||
: document.querySelectorAll('[loading="lazy"]');
|
||||
|
||||
if ("loading" in HTMLImageElement.prototype) {
|
||||
for (const $img of $lazyImages) {
|
||||
const $parent = queryClosestParent(
|
||||
$img,
|
||||
`.${CSS_CLASS.IMAGE}`
|
||||
);
|
||||
|
||||
|
||||
if (!$img.complete) {
|
||||
if($parent) {
|
||||
$parent.classList.add(
|
||||
CSS_CLASS.IMAGE_LAZY_LOADING
|
||||
);
|
||||
}
|
||||
|
||||
$img.addEventListener("load", lazyImageLoad, { once: true });
|
||||
$img.addEventListener("error", lazyImageError, { once: true });
|
||||
} else {
|
||||
if (!$img.complete) {
|
||||
$parent.classList.add(
|
||||
CSS_CLASS.IMAGE_LAZY_LOADED
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if 'loading' supported
|
||||
for (const $img of $lazyImages) {
|
||||
const $parent = queryClosestParent(
|
||||
$img,
|
||||
`.${CSS_CLASS.IMAGE}`
|
||||
);
|
||||
|
||||
if($parent) {
|
||||
$parent.classList.add(CSS_CLASS.IMAGE_LAZY_LOADED);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Reset Lazyload Callbacks */
|
||||
const resetLazyloadCallbacks = () => {
|
||||
if ("loading" in HTMLImageElement.prototype) {
|
||||
const $lazyImages = document.querySelectorAll('[loading="lazy"]');
|
||||
for (const $img of $lazyImages) {
|
||||
$img.removeEventListener("load", lazyImageLoad, { once: true });
|
||||
$img.removeEventListener("error", lazyImageError, { once: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export {
|
||||
getImageMetadata,
|
||||
loadImage,
|
||||
lazyLoadImage,
|
||||
triggerLazyloadCallbacks,
|
||||
resetLazyloadCallbacks
|
||||
}
|
||||
export { getImageMetadata, loadImage, lazyLoadImage }
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const isObject = x => (x && typeof x === 'object')
|
||||
const isObject = (x) => x && typeof x === 'object'
|
||||
|
||||
/**
|
||||
* Determines if the argument is a function.
|
||||
@@ -16,10 +16,6 @@ const isObject = x => (x && typeof x === 'object')
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const isFunction = x => typeof x === 'function'
|
||||
const isFunction = (x) => typeof x === 'function'
|
||||
|
||||
|
||||
export {
|
||||
isObject,
|
||||
isFunction
|
||||
}
|
||||
export { isObject, isFunction }
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
const clamp = (min = 0, max = 1, a) => Math.min(max, Math.max(min, a))
|
||||
|
||||
|
||||
/**
|
||||
* Calculate lerp
|
||||
* @param {number} x - start value
|
||||
@@ -19,7 +18,6 @@ const clamp = (min = 0, max = 1, a) => Math.min(max, Math.max(min, a))
|
||||
|
||||
const lerp = (x, y, a) => x * (1 - a) + y * a
|
||||
|
||||
|
||||
/**
|
||||
* Calculate inverted lerp
|
||||
* @param {number} x - start value
|
||||
@@ -28,8 +26,7 @@ const lerp = (x, y, a) => x * (1 - a) + y * a
|
||||
* @return {number} inverted lerp value
|
||||
*/
|
||||
|
||||
const invlerp = (x, y, a) => clamp((v - x)/(a - x))
|
||||
|
||||
const invlerp = (x, y, a) => clamp((v - x) / (a - x))
|
||||
|
||||
/**
|
||||
* Round number to the specified precision.
|
||||
@@ -42,13 +39,7 @@ const invlerp = (x, y, a) => clamp((v - x)/(a - x))
|
||||
* @return {number} The rounded number.
|
||||
*/
|
||||
const roundNumber = (number, precision = 2) => {
|
||||
return Number.parseFloat(number.toPrecision(precision));
|
||||
return Number.parseFloat(number.toPrecision(precision))
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
clamp,
|
||||
lerp,
|
||||
invlerp,
|
||||
roundNumber
|
||||
}
|
||||
export { clamp, lerp, invlerp, roundNumber }
|
||||
|
||||
@@ -39,7 +39,6 @@ const debounce = (callback, delay, immediate = false) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a throttled function.
|
||||
*
|
||||
@@ -71,8 +70,4 @@ const throttle = (callback, delay) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
debounce,
|
||||
throttle
|
||||
}
|
||||
export { debounce, throttle }
|
||||
|
||||
@@ -4,32 +4,32 @@
|
||||
* @return {number|object} translate value
|
||||
*/
|
||||
|
||||
const getTranslate = $el => {
|
||||
|
||||
if(!window.getComputedStyle) {
|
||||
const getTranslate = ($el) => {
|
||||
if (!window.getComputedStyle) {
|
||||
return
|
||||
}
|
||||
|
||||
let translate
|
||||
const style = getComputedStyle($el)
|
||||
const transform = style.msTransform || style.webkitTransform || style.MozTransform || style.OTransform || style.transform
|
||||
const transform =
|
||||
style.msTransform ||
|
||||
style.webkitTransform ||
|
||||
style.MozTransform ||
|
||||
style.OTransform ||
|
||||
style.transform
|
||||
|
||||
const matrix3D = transform.match(/^matrix3d\((.+)\)$/)
|
||||
if(matrix3D) {
|
||||
if (matrix3D) {
|
||||
translate = parseFloat(matrix3D[1].split(', ')[13])
|
||||
} else {
|
||||
const matrix = transform.match(/^matrix\((.+)\)$/)
|
||||
translate = {
|
||||
x: matrix ? parseFloat(matrix[1].split(', ')[4]) : 0
|
||||
y: matrix ? parseFloat(matrix[1].split(', ')[5]) : 0
|
||||
x: matrix ? parseFloat(matrix[1].split(', ')[4]) : 0,
|
||||
y: matrix ? parseFloat(matrix[1].split(', ')[5]) : 0,
|
||||
}
|
||||
}
|
||||
|
||||
return translate
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
transform,
|
||||
getTranslate
|
||||
}
|
||||
export { transform, getTranslate }
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
* - {@link https://www.w3.org/TR/page-visibility/ W3 Specification}
|
||||
* - {@link https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API MDN Web Docs}
|
||||
*/
|
||||
export default new class PageVisibility {
|
||||
export default new (class PageVisibility {
|
||||
/**
|
||||
* Checks if the "visibilitychange" event listener has been registered.
|
||||
*
|
||||
@@ -37,7 +37,7 @@ export default new class PageVisibility {
|
||||
* otherwise returns `true`.
|
||||
*/
|
||||
get isEnabled() {
|
||||
return isVisibilityChangeObserved;
|
||||
return isVisibilityChangeObserved
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,12 +48,15 @@ export default new class PageVisibility {
|
||||
*/
|
||||
disableCustomEvents() {
|
||||
if (isVisibilityChangeObserved) {
|
||||
isVisibilityChangeObserved = false;
|
||||
document.removeEventListener('visibilitychange', handleCustomVisibilityChange);
|
||||
return true;
|
||||
isVisibilityChangeObserved = false
|
||||
document.removeEventListener(
|
||||
'visibilitychange',
|
||||
handleCustomVisibilityChange
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,14 +67,17 @@ export default new class PageVisibility {
|
||||
*/
|
||||
enableCustomEvents() {
|
||||
if (!isVisibilityChangeObserved) {
|
||||
isVisibilityChangeObserved = true;
|
||||
document.addEventListener('visibilitychange', handleCustomVisibilityChange);
|
||||
return true;
|
||||
isVisibilityChangeObserved = true
|
||||
document.addEventListener(
|
||||
'visibilitychange',
|
||||
handleCustomVisibilityChange
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
/**
|
||||
* Tracks whether custom visibility event types
|
||||
@@ -79,7 +85,7 @@ export default new class PageVisibility {
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
let isVisibilityChangeObserved = false;
|
||||
let isVisibilityChangeObserved = false
|
||||
|
||||
/**
|
||||
* Dispatches a custom visibility event at the document derived
|
||||
@@ -94,11 +100,13 @@ let isVisibilityChangeObserved = false;
|
||||
* @return {void}
|
||||
*/
|
||||
function handleCustomVisibilityChange(event) {
|
||||
document.dispatchEvent(new CustomEvent(`visibility${document.visibilityState}`, {
|
||||
detail: {
|
||||
cause: event
|
||||
}
|
||||
}));
|
||||
document.dispatchEvent(
|
||||
new CustomEvent(`visibility${document.visibilityState}`, {
|
||||
detail: {
|
||||
cause: event,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// ==========================================================================
|
||||
|
||||
.c-form {
|
||||
|
||||
}
|
||||
|
||||
.c-form_item {
|
||||
@@ -27,7 +26,7 @@ $input-icon-color: 424242; // No #
|
||||
.c-form_input {
|
||||
padding: rem(10px);
|
||||
border: 1px solid lightgray;
|
||||
background-color: color(lightest);
|
||||
background-color: $color-lightest;
|
||||
|
||||
&:hover {
|
||||
border-color: darkgray;
|
||||
@@ -58,7 +57,8 @@ $checkbox-icon-color: $input-icon-color;
|
||||
padding-left: ($checkbox + rem(10px));
|
||||
cursor: pointer;
|
||||
|
||||
&::before, &::after {
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
@@ -67,18 +67,18 @@ $checkbox-icon-color: $input-icon-color;
|
||||
padding: 0;
|
||||
width: $checkbox;
|
||||
height: $checkbox;
|
||||
content: "";
|
||||
content: '';
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: color(lightest);
|
||||
background-color: $color-lightest;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2210.5%22%20viewBox%3D%220%200%2013%2010.5%22%20enable-background%3D%22new%200%200%2013%2010.5%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23#{$checkbox-icon-color}%22%20d%3D%22M4.8%205.8L2.4%203.3%200%205.7l4.8%204.8L13%202.4c0%200-2.4-2.4-2.4-2.4L4.8%205.8z%22%2F%3E%3C%2Fsvg%3E");
|
||||
background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2210.5%22%20viewBox%3D%220%200%2013%2010.5%22%20enable-background%3D%22new%200%200%2013%2010.5%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23#{$checkbox-icon-color}%22%20d%3D%22M4.8%205.8L2.4%203.3%200%205.7l4.8%204.8L13%202.4c0%200-2.4-2.4-2.4-2.4L4.8%205.8z%22%2F%3E%3C%2Fsvg%3E');
|
||||
background-position: center;
|
||||
background-size: rem(12px);
|
||||
background-repeat: no-repeat;
|
||||
@@ -118,12 +118,13 @@ $radio-icon-color: $input-icon-color;
|
||||
.c-form_radioLabel {
|
||||
@extend .c-form_checkboxLabel;
|
||||
|
||||
&::before, &::after {
|
||||
&::before,
|
||||
&::after {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20enable-background%3D%22new%200%200%2013%2013%22%20xml%3Aspace%3D%22preserve%22%3E%3Ccircle%20fill%3D%22%23#{$radio-icon-color}%22%20cx%3D%226.5%22%20cy%3D%226.5%22%20r%3D%226.5%22%2F%3E%3C%2Fsvg%3E");
|
||||
background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20enable-background%3D%22new%200%200%2013%2013%22%20xml%3Aspace%3D%22preserve%22%3E%3Ccircle%20fill%3D%22%23#{$radio-icon-color}%22%20cx%3D%226.5%22%20cy%3D%226.5%22%20r%3D%226.5%22%2F%3E%3C%2Fsvg%3E');
|
||||
background-size: rem(6px);
|
||||
}
|
||||
}
|
||||
@@ -149,11 +150,11 @@ $select-icon-color: $input-icon-color;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
width: $select-icon;
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2211.3%22%20viewBox%3D%220%200%2013%2011.3%22%20enable-background%3D%22new%200%200%2013%2011.3%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23#{$select-icon-color}%22%20points%3D%226.5%2011.3%203.3%205.6%200%200%206.5%200%2013%200%209.8%205.6%20%22%2F%3E%3C%2Fsvg%3E");
|
||||
background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2211.3%22%20viewBox%3D%220%200%2013%2011.3%22%20enable-background%3D%22new%200%200%2013%2011.3%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23#{$select-icon-color}%22%20points%3D%226.5%2011.3%203.3%205.6%200%200%206.5%200%2013%200%209.8%205.6%20%22%2F%3E%3C%2Fsvg%3E');
|
||||
background-position: center;
|
||||
background-size: rem(8px);
|
||||
background-repeat: no-repeat;
|
||||
content: "";
|
||||
content: '';
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,29 +3,30 @@
|
||||
// ==========================================================================
|
||||
|
||||
.c-heading {
|
||||
line-height: $line-height-h;
|
||||
margin-bottom: rem(30px);
|
||||
|
||||
&.-h1 {
|
||||
font-size: var(--font-size-h1);
|
||||
font-size: rem($font-size-h1);
|
||||
}
|
||||
|
||||
&.-h2 {
|
||||
font-size: var(--font-size-h2);
|
||||
font-size: rem($font-size-h2);
|
||||
}
|
||||
|
||||
&.-h3 {
|
||||
font-size: var(--font-size-h3);
|
||||
font-size: rem($font-size-h3);
|
||||
}
|
||||
|
||||
&.-h4 {
|
||||
font-size: var(--font-size-h4);
|
||||
font-size: rem($font-size-h4);
|
||||
}
|
||||
|
||||
&.-h5 {
|
||||
font-size: var(--font-size-h5);
|
||||
font-size: rem($font-size-h5);
|
||||
}
|
||||
|
||||
&.-h6 {
|
||||
font-size: var(--font-size-h6);
|
||||
font-size: rem($font-size-h6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// ==========================================================================
|
||||
// Components / Image
|
||||
// ==========================================================================
|
||||
|
||||
.c-image {
|
||||
|
||||
}
|
||||
|
||||
.c-image_img {
|
||||
|
||||
// Lazy loading styles
|
||||
.c-image.-lazy-load & {
|
||||
transition: opacity $speed $easing;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.c-image.-lazy-loaded & {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
40
assets/styles/components/_scrollbar.scss
Normal file
40
assets/styles/components/_scrollbar.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
// ==========================================================================
|
||||
// Components / Scrollbar
|
||||
// ==========================================================================
|
||||
|
||||
.c-scrollbar {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 11px;
|
||||
height: 100vh;
|
||||
transform-origin: center right;
|
||||
transition: transform 0.3s, opacity 0.3s;
|
||||
opacity: 0;
|
||||
|
||||
&:hover {
|
||||
transform: scaleX(1.45);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
.has-scroll-scrolling &,
|
||||
.has-scroll-dragging & {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.c-scrollbar_thumb {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: $color-darkest;
|
||||
opacity: 0.5;
|
||||
width: 7px;
|
||||
border-radius: 10px;
|
||||
margin: 2px;
|
||||
cursor: grab;
|
||||
|
||||
.has-scroll-dragging & {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,4 @@
|
||||
// Critical CSS
|
||||
// ==========================================================================
|
||||
|
||||
$assets-path: "assets/";
|
||||
$assets-path: 'assets/';
|
||||
|
||||
@@ -2,6 +2,20 @@
|
||||
// Elements / Document
|
||||
// ==========================================================================
|
||||
|
||||
:root {
|
||||
// Grid
|
||||
--grid-columns: 4;
|
||||
--grid-gutter: #{rem(10px)};
|
||||
--grid-gutter-half: calc(0.5 * var(--grid-gutter));
|
||||
--grid-margin: 0px;
|
||||
|
||||
@media (min-width: $from-small) {
|
||||
--grid-columns: 12;
|
||||
--grid-gutter: #{rem(16px)};
|
||||
--grid-margin: #{rem(20px)};
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Simple page-level setup.
|
||||
//
|
||||
@@ -15,7 +29,7 @@
|
||||
html {
|
||||
min-height: 100%; // [2]
|
||||
line-height: $line-height; // [3]
|
||||
font-family: ff("sans");
|
||||
font-family: ff('sans');
|
||||
color: $font-color;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
@@ -51,14 +65,30 @@ html {
|
||||
&.is-loading {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
&.has-scroll-smooth {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.has-scroll-dragging {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
.has-scroll-smooth & {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: $color-selection-background;
|
||||
color: $color-selection-text;
|
||||
background-color: $selection-background-color;
|
||||
color: $selection-text-color;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,13 +57,25 @@ figure {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// 1. Single taps should be dispatched immediately on clickable elements
|
||||
|
||||
a, area, button, input, label, select, textarea, [tabindex] {
|
||||
a,
|
||||
area,
|
||||
button,
|
||||
input,
|
||||
label,
|
||||
select,
|
||||
textarea,
|
||||
[tabindex] {
|
||||
-ms-touch-action: manipulation; // [1]
|
||||
touch-action: manipulation;
|
||||
}
|
||||
@@ -83,5 +95,5 @@ hr {
|
||||
padding: 0;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #CCCCCC;
|
||||
border-top: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
// ==========================================================================
|
||||
// Main
|
||||
// ==========================================================================
|
||||
@use "sass:math";
|
||||
@use 'sass:math';
|
||||
|
||||
// Settings
|
||||
// ==========================================================================
|
||||
|
||||
@import 'settings/config.eases';
|
||||
@import 'settings/config.colors';
|
||||
@import 'settings/config';
|
||||
|
||||
// ==========================================================================
|
||||
// Tools
|
||||
// ==========================================================================
|
||||
|
||||
@import "tools/maths";
|
||||
@import "tools/functions";
|
||||
@import "tools/mixins";
|
||||
@import "tools/fonts";
|
||||
@import 'tools/maths';
|
||||
@import 'tools/functions';
|
||||
@import 'tools/mixins';
|
||||
@import 'tools/fonts';
|
||||
// @import "tools/layout";
|
||||
// @import "tools/widths";
|
||||
// @import "tools/family";
|
||||
|
||||
// Settings
|
||||
// ==========================================================================
|
||||
|
||||
@import "settings/config.eases";
|
||||
@import "settings/config.colors";
|
||||
@import "settings/config";
|
||||
@import "settings/config.variables";
|
||||
|
||||
// Generic
|
||||
// ==========================================================================
|
||||
|
||||
@import "node_modules/normalize.css/normalize";
|
||||
@import "generic/generic";
|
||||
@import "generic/media";
|
||||
@import "generic/form";
|
||||
@import "generic/button";
|
||||
|
||||
// Vendors
|
||||
// ==========================================================================
|
||||
@import "node_modules/locomotive-scroll/dist/locomotive-scroll";
|
||||
@import 'node_modules/normalize.css/normalize';
|
||||
@import 'generic/generic';
|
||||
@import 'generic/media';
|
||||
@import 'generic/form';
|
||||
@import 'generic/button';
|
||||
|
||||
// Elements
|
||||
// ==========================================================================
|
||||
|
||||
@import "elements/document";
|
||||
@import 'elements/document';
|
||||
|
||||
// Objects
|
||||
// ==========================================================================
|
||||
|
||||
@import "objects/container";
|
||||
@import "objects/ratio";
|
||||
@import "objects/icons";
|
||||
@import "objects/grid";
|
||||
@import 'objects/scroll';
|
||||
@import 'objects/container';
|
||||
@import 'objects/ratio';
|
||||
@import 'objects/icons';
|
||||
@import 'objects/grid';
|
||||
// @import "objects/layout";
|
||||
// @import "objects/table";
|
||||
|
||||
// Vendors
|
||||
// ==========================================================================
|
||||
// @import "vendors/vendor";
|
||||
|
||||
// Components
|
||||
// ==========================================================================
|
||||
|
||||
@import "components/heading";
|
||||
@import "components/button";
|
||||
@import "components/form";
|
||||
@import "components/image";
|
||||
@import 'components/scrollbar';
|
||||
@import 'components/heading';
|
||||
@import 'components/button';
|
||||
@import 'components/form';
|
||||
|
||||
// Utilities
|
||||
// ==========================================================================
|
||||
|
||||
@import "utilities/ratio";
|
||||
@import "utilities/grid-column";
|
||||
@import 'utilities/ratio';
|
||||
@import 'utilities/grid-column';
|
||||
// @import "utilities/widths";
|
||||
// @import "utilities/align";
|
||||
// @import "utilities/helpers";
|
||||
|
||||
@@ -13,6 +13,6 @@
|
||||
.o-container {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-left: var(--grid-margin);
|
||||
padding-right: var(--grid-margin);
|
||||
padding-right: $base-column-gap;
|
||||
padding-left: $base-column-gap;
|
||||
}
|
||||
|
||||
@@ -31,12 +31,6 @@
|
||||
// ==========================================================================
|
||||
// Cols
|
||||
// ==========================================================================
|
||||
|
||||
// Responsive grid columns based on `--grid-columns`
|
||||
&.-cols {
|
||||
grid-template-columns: repeat(var(--grid-columns), 1fr);
|
||||
}
|
||||
|
||||
&.-col-#{$base-column-nb} {
|
||||
grid-template-columns: repeat(#{$base-column-nb}, 1fr);
|
||||
}
|
||||
@@ -57,8 +51,8 @@
|
||||
|
||||
// Gutters rows and columns
|
||||
&.-gutters {
|
||||
gap: var(--grid-gutter);
|
||||
column-gap: var(--grid-gutter);
|
||||
gap: $base-column-gap;
|
||||
column-gap: $base-column-gap;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
@@ -169,8 +163,7 @@
|
||||
// By default, a grid item takes full width of its parent.
|
||||
//
|
||||
.o-grid_item {
|
||||
grid-column-start: var(--gc-start, 1);
|
||||
grid-column-end: var(--gc-end, -1);
|
||||
grid-column: 1 / -1;
|
||||
|
||||
&.-align-end {
|
||||
align-self: end;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Objects / SVG Icons
|
||||
// ==========================================================================
|
||||
|
||||
|
||||
// Markup
|
||||
//
|
||||
// 1. If icon is accessible and has a title
|
||||
@@ -32,7 +31,7 @@
|
||||
vertical-align: middle;
|
||||
|
||||
svg {
|
||||
--icon-height: calc(var(--icon-width) * math.div(1, (var(--icon-ratio))));
|
||||
--icon-height: calc(var(--icon-width) * (1 / (var(--icon-ratio))));
|
||||
|
||||
display: block;
|
||||
width: var(--icon-width);
|
||||
@@ -41,7 +40,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SVG sizes
|
||||
// ==========================================================================
|
||||
|
||||
@@ -55,4 +53,3 @@
|
||||
// --icon-width: #{rem(200px)};
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
@@ -68,17 +68,17 @@
|
||||
}
|
||||
|
||||
&.-flex {
|
||||
display: flex;
|
||||
display: flex;
|
||||
|
||||
&.-top {
|
||||
&.-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
&.-middle {
|
||||
}
|
||||
&.-middle {
|
||||
align-items: center;
|
||||
}
|
||||
&.-bottom {
|
||||
}
|
||||
&.-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.-stretch {
|
||||
align-items: stretch;
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
display: block;
|
||||
padding-bottom: 100%; // [1]
|
||||
width: 100%;
|
||||
content: "";
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.o-ratio_content,
|
||||
.o-ratio > img,
|
||||
.o-ratio > iframe,
|
||||
.o-ratio > embed,
|
||||
.o-ratio > object {
|
||||
.o-ratio > img,
|
||||
.o-ratio > iframe,
|
||||
.o-ratio > embed,
|
||||
.o-ratio > object {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
7
assets/styles/objects/_scroll.scss
Normal file
7
assets/styles/objects/_scroll.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
// ==========================================================================
|
||||
// Objects / Scroll
|
||||
// ==========================================================================
|
||||
|
||||
.o-scroll {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -5,27 +5,25 @@
|
||||
// Palette
|
||||
// =============================================================================
|
||||
|
||||
$colors: (
|
||||
primary: #3297FD,
|
||||
lightest: #FFFFFF,
|
||||
darkest: #000000,
|
||||
);
|
||||
$color-lightest: #ffffff;
|
||||
$color-darkest: #000000;
|
||||
|
||||
|
||||
// Specifics
|
||||
// Specific
|
||||
// =============================================================================
|
||||
|
||||
// Link
|
||||
$color-link: color(primary);
|
||||
$color-link-focus: color(primary);
|
||||
$color-link-hover: darken(color(primary), 10%);
|
||||
$color-link: #1a0dab;
|
||||
$color-link-focus: #1a0dab;
|
||||
$color-link-hover: darken(#1a0dab, 10%);
|
||||
|
||||
// Selection
|
||||
$color-selection-text: color(darkest);
|
||||
$color-selection-background: color(lightest);
|
||||
$selection-text-color: #3297fd;
|
||||
$selection-background-color: #ffffff;
|
||||
|
||||
// Socials
|
||||
$color-facebook: #3B5998;
|
||||
$color-instagram: #E1306C;
|
||||
$color-youtube: #CD201F;
|
||||
$color-twitter: #1DA1F2;
|
||||
// Social Colors
|
||||
// =============================================================================
|
||||
|
||||
$color-facebook: #3b5998;
|
||||
$color-instagram: #e1306c;
|
||||
$color-youtube: #cd201f;
|
||||
$color-twitter: #1da1f2;
|
||||
|
||||
@@ -3,46 +3,46 @@
|
||||
// ==========================================================================
|
||||
|
||||
// Power 1
|
||||
$ease-power1-in: cubic-bezier(0.550, 0.085, 0.680, 0.530);
|
||||
$ease-power1-out: cubic-bezier(0.250, 0.460, 0.450, 0.940);
|
||||
$ease-power1-in-out: cubic-bezier(0.455, 0.030, 0.515, 0.955);
|
||||
$ease-power1-in: cubic-bezier(0.55, 0.085, 0.68, 0.53);
|
||||
$ease-power1-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
$ease-power1-in-out: cubic-bezier(0.455, 0.03, 0.515, 0.955);
|
||||
|
||||
// Power 2
|
||||
$ease-power2-in: cubic-bezier(0.550, 0.055, 0.675, 0.190);
|
||||
$ease-power2-out: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
$ease-power2-in-out: cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
$ease-power2-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
$ease-power2-out: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
$ease-power2-in-out: cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
|
||||
// Power 3
|
||||
$ease-power3-in: cubic-bezier(0.895, 0.030, 0.685, 0.220);
|
||||
$ease-power3-out: cubic-bezier(0.165, 0.840, 0.440, 1.000);
|
||||
$ease-power3-in-out: cubic-bezier(0.770, 0.000, 0.175, 1.000);
|
||||
$ease-power3-in: cubic-bezier(0.895, 0.03, 0.685, 0.22);
|
||||
$ease-power3-out: cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
$ease-power3-in-out: cubic-bezier(0.77, 0, 0.175, 1);
|
||||
|
||||
// Power 3
|
||||
$ease-power4-in: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
$ease-power4-out: cubic-bezier(0.230, 1.000, 0.320, 1.000);
|
||||
$ease-power4-in-out: cubic-bezier(0.860, 0.000, 0.070, 1.000);
|
||||
$ease-power4-in: cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
$ease-power4-out: cubic-bezier(0.23, 1, 0.32, 1);
|
||||
$ease-power4-in-out: cubic-bezier(0.86, 0, 0.07, 1);
|
||||
|
||||
// Expo
|
||||
$ease-expo-in: cubic-bezier(0.950, 0.050, 0.795, 0.035);
|
||||
$ease-expo-out: cubic-bezier(0.190, 1.000, 0.220, 1.000);
|
||||
$ease-expo-in-out: cubic-bezier(1.000, 0.000, 0.000, 1.000);
|
||||
$ease-expo-in: cubic-bezier(0.95, 0.05, 0.795, 0.035);
|
||||
$ease-expo-out: cubic-bezier(0.19, 1, 0.22, 1);
|
||||
$ease-expo-in-out: cubic-bezier(1, 0, 0, 1);
|
||||
|
||||
// Back
|
||||
$ease-back-in: cubic-bezier(0.600, -0.280, 0.735, 0.045);
|
||||
$ease-back-out: cubic-bezier(0.175, 00.885, 0.320, 1.275);
|
||||
$ease-back-in-out: cubic-bezier(0.680, -0.550, 0.265, 1.550);
|
||||
$ease-back-in: cubic-bezier(0.6, -0.28, 0.735, 0.045);
|
||||
$ease-back-out: cubic-bezier(0.175, 00.885, 0.32, 1.275);
|
||||
$ease-back-in-out: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
|
||||
// Sine
|
||||
$ease-sine-in: cubic-bezier(0.470, 0.000, 0.745, 0.715);
|
||||
$ease-sine-out: cubic-bezier(0.390, 0.575, 0.565, 1.000);
|
||||
$ease-sine-in-out: cubic-bezier(0.445, 0.050, 0.550, 0.950);
|
||||
$ease-sine-in: cubic-bezier(0.47, 0, 0.745, 0.715);
|
||||
$ease-sine-out: cubic-bezier(0.39, 0.575, 0.565, 1);
|
||||
$ease-sine-in-out: cubic-bezier(0.445, 0.05, 0.55, 0.95);
|
||||
|
||||
// Circ
|
||||
$ease-circ-in: cubic-bezier(0.600, 0.040, 0.980, 0.335);
|
||||
$ease-circ-out: cubic-bezier(0.075, 0.820, 0.165, 1.000);
|
||||
$ease-circ-in-out: cubic-bezier(0.785, 0.135, 0.150, 0.860);
|
||||
$ease-circ-in: cubic-bezier(0.6, 0.04, 0.98, 0.335);
|
||||
$ease-circ-out: cubic-bezier(0.075, 0.82, 0.165, 1);
|
||||
$ease-circ-in-out: cubic-bezier(0.785, 0.135, 0.15, 0.86);
|
||||
|
||||
// Misc
|
||||
$ease-bounce: cubic-bezier(0.17, 0.67, 0.3, 1.33);
|
||||
$ease-slow-out: cubic-bezier(.04,1.15,0.4,.99);
|
||||
$ease-smooth: cubic-bezier(0.380, 0.005, 0.215, 1);
|
||||
$ease-bounce: cubic-bezier(0.17, 0.67, 0.3, 1.33);
|
||||
$ease-slow-out: cubic-bezier(0.04, 1.15, 0.4, 0.99);
|
||||
$ease-smooth: cubic-bezier(0.38, 0.005, 0.215, 1);
|
||||
|
||||
@@ -6,21 +6,26 @@
|
||||
// =============================================================================
|
||||
|
||||
// The current stylesheet context. Available values: frontend, editor.
|
||||
$context: frontend !default;
|
||||
$context: frontend !default;
|
||||
|
||||
// Path is relative to the stylesheets directory.
|
||||
$assets-path: "../" !default;
|
||||
$assets-path: '../' !default;
|
||||
|
||||
// Typefaces
|
||||
// =============================================================================
|
||||
|
||||
// Font directory
|
||||
$font-dir: "../fonts/";
|
||||
$font-dir: '../fonts/';
|
||||
|
||||
// Font fallbacks (retrieved from systemfontstack.com on 2022-05-31)
|
||||
$font-fallback-sans: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
|
||||
$font-fallback-serif: Iowan Old Style, Apple Garamond, Baskerville, Times New Roman, Droid Serif, Times, Source Serif Pro, serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
|
||||
$font-fallback-mono: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;
|
||||
$font-fallback-sans: -apple-system, BlinkMacSystemFont, avenir next, avenir,
|
||||
segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial,
|
||||
sans-serif;
|
||||
$font-fallback-serif: Iowan Old Style, Apple Garamond, Baskerville,
|
||||
Times New Roman, Droid Serif, Times, Source Serif Pro, serif,
|
||||
Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
|
||||
$font-fallback-mono: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console,
|
||||
monospace;
|
||||
|
||||
// Map of font families.
|
||||
//
|
||||
@@ -28,7 +33,7 @@ $font-fallback-mono: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console
|
||||
// <font-id>: (<font-name>, <font-fallbacks>)
|
||||
// ```
|
||||
$font-families: (
|
||||
sans: join("Source Sans", $font-fallback-sans, $separator: comma),
|
||||
sans: join('Source Sans', $font-fallback-sans, $separator: comma),
|
||||
);
|
||||
|
||||
// List of custom font faces as tuples.
|
||||
@@ -37,10 +42,10 @@ $font-families: (
|
||||
// <font-name> <font-file-basename> <font-weight> <font-style>
|
||||
// ```
|
||||
$font-faces: (
|
||||
("Source Sans", "SourceSans3-Bold", 700, normal),
|
||||
("Source Sans", "SourceSans3-BoldIt", 700, italic),
|
||||
("Source Sans", "SourceSans3-Regular", 400, normal),
|
||||
("Source Sans", "SourceSans3-RegularIt", 400, italic),
|
||||
('Source Sans', 'SourceSans3-Bold', 700, normal),
|
||||
('Source Sans', 'SourceSans3-BoldIt', 700, italic),
|
||||
('Source Sans', 'SourceSans3-Regular', 400, normal),
|
||||
('Source Sans', 'SourceSans3-RegularIt', 400, italic)
|
||||
);
|
||||
|
||||
// Typography
|
||||
@@ -48,61 +53,71 @@ $font-faces: (
|
||||
|
||||
// Base
|
||||
$font-size: 16px;
|
||||
$line-height: math.div(24px, $font-size);
|
||||
$font-color: color(darkest);
|
||||
$line-height: math.div(24px, $font-size);
|
||||
$font-color: $color-darkest;
|
||||
|
||||
// Headings
|
||||
$font-size-h1: 36px !default;
|
||||
$font-size-h2: 28px !default;
|
||||
$font-size-h3: 24px !default;
|
||||
$font-size-h4: 20px !default;
|
||||
$font-size-h5: 18px !default;
|
||||
$font-size-h6: 16px !default;
|
||||
$line-height-h: $line-height;
|
||||
|
||||
// Weights
|
||||
$font-weight-light: 300;
|
||||
$font-weight-normal: 400;
|
||||
$font-weight-medium: 500;
|
||||
$font-weight-bold: 700;
|
||||
$font-weight-light: 300;
|
||||
$font-weight-normal: 400;
|
||||
$font-weight-medium: 500;
|
||||
$font-weight-bold: 700;
|
||||
|
||||
// Transitions
|
||||
// =============================================================================
|
||||
|
||||
$speed: 0.3s;
|
||||
$easing: $ease-power2-out;
|
||||
$speed: 0.3s;
|
||||
$easing: $ease-power2-out;
|
||||
|
||||
// Spacing Units
|
||||
// =============================================================================
|
||||
$unit: 60px;
|
||||
$unit: 60px;
|
||||
$unit-small: 20px;
|
||||
|
||||
// Container
|
||||
// ==========================================================================
|
||||
$padding: $unit;
|
||||
$padding: $unit;
|
||||
|
||||
// Grid
|
||||
// ==========================================================================
|
||||
$base-column-nb: 12;
|
||||
$base-column-gap: $unit-small;
|
||||
|
||||
// Breakpoints
|
||||
// =============================================================================
|
||||
|
||||
$from-tiny: 500px !default;
|
||||
$to-tiny: $from-tiny - 1 !default;
|
||||
$from-small: 700px !default;
|
||||
$to-small: $from-small - 1 !default;
|
||||
$from-medium: 1000px !default;
|
||||
$to-medium: $from-medium - 1 !default;
|
||||
$from-large: 1200px !default;
|
||||
$to-large: $from-large - 1 !default;
|
||||
$from-big: 1400px !default;
|
||||
$to-big: $from-big - 1 !default;
|
||||
$from-huge: 1600px !default;
|
||||
$to-huge: $from-huge - 1 !default;
|
||||
$from-enormous: 1800px !default;
|
||||
$to-enormous: $from-enormous - 1 !default;
|
||||
$from-gigantic: 2000px !default;
|
||||
$to-gigantic: $from-gigantic - 1 !default;
|
||||
$from-colossal: 2400px !default;
|
||||
$to-colossal: $from-colossal - 1 !default;
|
||||
$from-tiny: 500px !default;
|
||||
$to-tiny: $from-tiny - 1 !default;
|
||||
$from-small: 700px !default;
|
||||
$to-small: $from-small - 1 !default;
|
||||
$from-medium: 1000px !default;
|
||||
$to-medium: $from-medium - 1 !default;
|
||||
$from-large: 1200px !default;
|
||||
$to-large: $from-large - 1 !default;
|
||||
$from-big: 1400px !default;
|
||||
$to-big: $from-big - 1 !default;
|
||||
$from-huge: 1600px !default;
|
||||
$to-huge: $from-huge - 1 !default;
|
||||
$from-enormous: 1800px !default;
|
||||
$to-enormous: $from-enormous - 1 !default;
|
||||
$from-gigantic: 2000px !default;
|
||||
$to-gigantic: $from-gigantic - 1 !default;
|
||||
$from-colossal: 2400px !default;
|
||||
$to-colossal: $from-colossal - 1 !default;
|
||||
|
||||
// Master z-indexe
|
||||
// =============================================================================
|
||||
|
||||
$z-indexes: (
|
||||
"header": 200,
|
||||
"above": 1,
|
||||
"below": -1
|
||||
'header': 200,
|
||||
'above': 1,
|
||||
'below': -1,
|
||||
);
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// ==========================================================================
|
||||
// Settings / Config / CSS VARS
|
||||
// ==========================================================================
|
||||
|
||||
:root {
|
||||
|
||||
// Grid
|
||||
--grid-columns: 4;
|
||||
--grid-gutter: #{rem(10px)};
|
||||
--grid-gutter-half: calc(0.5 * var(--grid-gutter));
|
||||
--grid-margin: #{rem(10px)};
|
||||
|
||||
// Container
|
||||
--container-width: calc(100% - 2 * var(--grid-margin));
|
||||
|
||||
// Font sizes
|
||||
--font-size-h1: #{responsive-type(36px, 72px, 1400px)};
|
||||
--font-size-h2: #{rem(28px)};
|
||||
--font-size-h3: #{rem(24px)};
|
||||
--font-size-h4: #{rem(20px)};
|
||||
--font-size-h5: #{rem(18px)};
|
||||
--font-size-h6: #{rem(16px)};
|
||||
|
||||
// // Colors
|
||||
// @each $color, $value in $colors {
|
||||
// --color-#{"" + $color}: #{$value};
|
||||
// }
|
||||
|
||||
@media (min-width: $from-small) {
|
||||
--grid-columns: #{$base-column-nb};
|
||||
--grid-gutter: #{rem(16px)};
|
||||
--grid-margin: #{rem(20px)};
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@
|
||||
// @param {number} $num - id of the child
|
||||
|
||||
@mixin middle($num) {
|
||||
&:nth-child(#{round(math.div($num, 2))}) {
|
||||
&:nth-child(#{round($num / 2)}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@@ -229,7 +229,8 @@
|
||||
$child: nth(nth($selector, -1), -1);
|
||||
|
||||
&:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child,
|
||||
&:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child ~ #{$child} {
|
||||
&:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child
|
||||
~ #{$child} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@@ -240,7 +241,7 @@
|
||||
|
||||
@mixin first-child() {
|
||||
&:first-of-type {
|
||||
@content
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +251,7 @@
|
||||
|
||||
@mixin last-child() {
|
||||
&:last-of-type {
|
||||
@content
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
@font-face {
|
||||
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");
|
||||
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)};
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
@mixin font-faces($webfonts, $dir) {
|
||||
@if (length($webfonts) > 0) {
|
||||
@if (type-of(nth($webfonts, 1)) == "list") {
|
||||
@if (type-of(nth($webfonts, 1)) == 'list') {
|
||||
@each $webfont in $webfonts {
|
||||
@include font-face($webfont, $dir);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// @return {Boolean}
|
||||
|
||||
@function is-pixel-number($number) {
|
||||
@return type-of($number) == number and unit($number) == "px";
|
||||
@return type-of($number) == number and unit($number) == 'px';
|
||||
}
|
||||
|
||||
// Converts the given pixel value to its EM quivalent.
|
||||
@@ -36,7 +36,6 @@
|
||||
// @return {Number} Scalable pixel value in REMs.
|
||||
|
||||
@function rem($size, $base: $font-size) {
|
||||
|
||||
@if not is-pixel-number($size) {
|
||||
@error "`#{$size}` needs to be a number in pixel.";
|
||||
}
|
||||
@@ -92,12 +91,9 @@
|
||||
// @return {Boolean}
|
||||
// @access private
|
||||
|
||||
@function list-contains(
|
||||
$list,
|
||||
$values...
|
||||
) {
|
||||
@function list-contains($list, $values...) {
|
||||
@each $value in $values {
|
||||
@if type-of(index($list, $value)) != "number" {
|
||||
@if type-of(index($list, $value)) != 'number' {
|
||||
@return false;
|
||||
}
|
||||
}
|
||||
@@ -139,89 +135,3 @@
|
||||
}
|
||||
|
||||
$context: 'frontend' !default;
|
||||
|
||||
// Returns calculation of a percentage of the grid cell width
|
||||
// with optional inset of grid gutter.
|
||||
//
|
||||
// ```scss
|
||||
// .c-box {
|
||||
// width: grid-space(6/12);
|
||||
// margin-left: grid-space(1/12, 1);
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// @param {number} $number - The percentage spacer
|
||||
// @param {number} $inset - The grid gutter inset
|
||||
// @return {function<number>}
|
||||
@function grid-space($percentage, $inset: 0) {
|
||||
@return calc(#{$percentage} * (100vw - 2 * var(--grid-margin, 0px)) - (1 - #{$percentage}) * var(--grid-gutter, 0px) + #{$inset} * var(--grid-gutter, 0px));
|
||||
}
|
||||
|
||||
// Returns calculation of a percentage of the viewport height.
|
||||
//
|
||||
// ```scss
|
||||
// .c-box {
|
||||
// height: vh(100);
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// @param {number} $number - The percentage number
|
||||
// @return {function<number>} in vh
|
||||
@function vh($number) {
|
||||
@return calc(#{$number} * var(--vh, 1vh));
|
||||
}
|
||||
|
||||
// Returns calculation of a percentage of the viewport width.
|
||||
//
|
||||
// ```scss
|
||||
// .c-box {
|
||||
// width: vw(100);
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// @param {number} $number - The percentage number
|
||||
// @return {function<number>} in vw
|
||||
|
||||
@function vw($number) {
|
||||
@return calc(#{$number} * var(--vw, 1vw));
|
||||
}
|
||||
|
||||
// Returns clamp of calculated preferred responsive font size
|
||||
// within a font size and breakpoint range.
|
||||
//
|
||||
// ```scss
|
||||
// .c-heading.-h1 {
|
||||
// font-size: responsive-type(30px, 60px, 1800);
|
||||
// }
|
||||
//
|
||||
// .c-heading.-h2 {
|
||||
// font-size: responsive-type(20px, 40px, $from-big);
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// @param {number} $min-size - Minimum font size in pixels.
|
||||
// @param {number} $max-size - Maximum font size in pixels.
|
||||
// @param {number} $breakpoint - Maximum breakpoint.
|
||||
// @return {function<number, function<number>, number>}
|
||||
@function responsive-type($min-size, $max-size, $breakpoint) {
|
||||
$delta: math.div($max-size, $breakpoint);
|
||||
@return clamp($min-size, calc(#{strip-unit($delta)} * #{vw(100)}), $max-size);
|
||||
}
|
||||
|
||||
// Returns color code.
|
||||
//
|
||||
// ```scss
|
||||
// .c-box {
|
||||
// width: color(primary);
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// @param {string} $key - The color key in $colors.
|
||||
// @return {color}
|
||||
|
||||
@function color($key) {
|
||||
@if not map-has-key($colors, $key) {
|
||||
@error "Unknown '#{$key}' in $colors.";
|
||||
}
|
||||
@return map-get($colors, $key);
|
||||
}
|
||||
|
||||
@@ -2,18 +2,13 @@
|
||||
// Tools / Maths
|
||||
// ==========================================================================
|
||||
|
||||
// Remove the unit of a length
|
||||
// Removes the unit from the given number.
|
||||
//
|
||||
// @param {Number} $number Number to remove unit from
|
||||
// @return {function<number>}
|
||||
@function strip-unit($value) {
|
||||
@if type-of($value) != "number" {
|
||||
@error "Invalid `#{type-of($value)}` type. Choose a number type instead.";
|
||||
} @else if type-of($value) == "number" and not is-unitless($value) {
|
||||
@return math.div($value, $value * 0 + 1);
|
||||
}
|
||||
// @param {number} $number The number to strip.
|
||||
// @return {number}
|
||||
|
||||
@return $value;
|
||||
@function strip-units($number) {
|
||||
@return $number / ($number * 0 + 1);
|
||||
}
|
||||
|
||||
// Returns the square root of the given number.
|
||||
@@ -26,7 +21,7 @@
|
||||
$value: $x;
|
||||
|
||||
@for $i from 1 through 10 {
|
||||
$value: $x - math.div(($x * $x - abs($number)), (2 * $x));
|
||||
$value: $x - ($x * $x - abs($number)) / (2 * $x);
|
||||
$x: $value;
|
||||
}
|
||||
|
||||
@@ -48,7 +43,7 @@
|
||||
}
|
||||
} @else if $exp < 0 {
|
||||
@for $i from 1 through -$exp {
|
||||
$value: math.div($value, $number);
|
||||
$value: $value / $number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +88,7 @@
|
||||
|
||||
// If the angle has `deg` as unit, convert to radians.
|
||||
@if ($unit == deg) {
|
||||
@return math.div($angle, 180) * pi();
|
||||
@return $angle / 180 * pi();
|
||||
}
|
||||
|
||||
@return $angle;
|
||||
@@ -109,7 +104,7 @@
|
||||
$angle: rad($angle);
|
||||
|
||||
@for $i from 0 through 10 {
|
||||
$sin: $sin + pow(-1, $i) * math.div(pow($angle, (2 * $i + 1)), fact(2 * $i + 1));
|
||||
$sin: $sin + pow(-1, $i) * pow($angle, (2 * $i + 1)) / fact(2 * $i + 1);
|
||||
}
|
||||
|
||||
@return $sin;
|
||||
@@ -125,7 +120,7 @@
|
||||
$angle: rad($angle);
|
||||
|
||||
@for $i from 0 through 10 {
|
||||
$cos: $cos + pow(-1, $i) * math.div(pow($angle, 2 * $i), fact(2 * $i));
|
||||
$cos: $cos + pow(-1, $i) * pow($angle, 2 * $i) / fact(2 * $i);
|
||||
}
|
||||
|
||||
@return $cos;
|
||||
@@ -137,5 +132,5 @@
|
||||
// @return {number}
|
||||
|
||||
@function tan($angle) {
|
||||
@return math.div(sin($angle), cos($angle));
|
||||
@return sin($angle) / cos($angle);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
&::after {
|
||||
display: if(list-contains($supports, table), table, block);
|
||||
clear: both;
|
||||
content: if(list-contains($supports, opera), " ", "");
|
||||
content: if(list-contains($supports, opera), ' ', '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,14 +50,21 @@
|
||||
$important: important($important);
|
||||
font-size: rem($font-size) $important;
|
||||
|
||||
@if ($line-height == "auto") {
|
||||
line-height: ceil(math.div($font-size, $line-height)) * math.div($line-height, $font-size) $important;
|
||||
}
|
||||
@else {
|
||||
@if (type-of($line-height) == number or $line-height == "inherit" or $line-height == "normal") {
|
||||
@if ($line-height == 'auto') {
|
||||
line-height: ceil($font-size / $line-height) *
|
||||
($line-height / $font-size)
|
||||
$important;
|
||||
} @else {
|
||||
@if (
|
||||
type-of($line-height) ==
|
||||
number or
|
||||
$line-height ==
|
||||
'inherit' or
|
||||
$line-height ==
|
||||
'normal'
|
||||
) {
|
||||
line-height: $line-height $important;
|
||||
}
|
||||
@else if ($line-height != "none" and $line-height != false) {
|
||||
} @else if ($line-height != 'none' and $line-height != false) {
|
||||
@error "D’oh! `#{$line-height}` is not a valid value for `$line-height`.";
|
||||
}
|
||||
}
|
||||
@@ -77,7 +84,7 @@
|
||||
&::before {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
content: "";
|
||||
content: '';
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -128,7 +135,7 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal; // [2]
|
||||
word-wrap: normal; // [2]
|
||||
@if $width {
|
||||
max-width: $width; // [1]
|
||||
}
|
||||
@@ -176,7 +183,7 @@
|
||||
|
||||
@mixin u-hidden($important: true) {
|
||||
$important: important($important);
|
||||
display: none $important;
|
||||
display: none $important;
|
||||
visibility: hidden $important;
|
||||
}
|
||||
|
||||
@@ -191,5 +198,5 @@
|
||||
@mixin u-shown($display: block, $important: true) {
|
||||
$important: important($important);
|
||||
display: $display $important;
|
||||
visibility: visible $important;
|
||||
visibility: visible $important;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ $breakpoint-delimiter: \@ !default;
|
||||
@for $numerator from 1 through $denominator {
|
||||
// Build a class in the format `.u-3/4[@<breakpoint>]`.
|
||||
.u-#{$numerator}#{$fractions-delimiter}#{$denominator}#{$breakpoint} {
|
||||
width: math.div($numerator, $denominator) * 100% $important;
|
||||
width: ($numerator / $denominator) * 100% $important;
|
||||
}
|
||||
|
||||
@if ($widths-offsets == true) {
|
||||
@@ -66,13 +66,13 @@ $breakpoint-delimiter: \@ !default;
|
||||
.u-push-#{$numerator}#{$fractions-delimiter}#{$denominator}#{$breakpoint} {
|
||||
position: relative $important;
|
||||
right: auto $important;
|
||||
left: math.div($numerator, $denominator) * 100% $important;
|
||||
left: ($numerator / $denominator) * 100% $important;
|
||||
}
|
||||
|
||||
// Build a class in the format `.u-pull-5/6[@<breakpoint>]`.
|
||||
.u-pull-#{$numerator}#{$fractions-delimiter}#{$denominator}#{$breakpoint} {
|
||||
position: relative $important;
|
||||
right: math.div($numerator, $denominator) * 100% $important;
|
||||
right: ($numerator / $denominator) * 100% $important;
|
||||
left: auto $important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,49 @@
|
||||
$colsMax: $base-column-nb + 1;
|
||||
|
||||
$breakpoints: (
|
||||
"null" null,
|
||||
"from-tiny" $from-tiny,
|
||||
"from-small" $from-small,
|
||||
"from-medium" $from-medium,
|
||||
"from-large" $from-large,
|
||||
"from-big" $from-big
|
||||
'null' null,
|
||||
'from-tiny' 'from-tiny',
|
||||
'from-small' 'from-small',
|
||||
'from-medium' 'from-medium',
|
||||
'from-large' 'from-large',
|
||||
'from-big' 'from-big'
|
||||
) !default;
|
||||
|
||||
@each $breakpoint, $mediaquery in $breakpoints {
|
||||
@each $breakpoint-namespace, $breakpoint in $breakpoints {
|
||||
@for $fromIndex from 1 through $colsMax {
|
||||
@for $toIndex from 1 through $colsMax {
|
||||
@if $mediaquery == null {
|
||||
@if $breakpoint == null {
|
||||
.u-gc-#{$fromIndex}\/#{$toIndex} {
|
||||
--gc-start: #{$fromIndex};
|
||||
--gc-end: #{$toIndex};
|
||||
grid-column-start: #{$fromIndex};
|
||||
grid-column-end: #{$toIndex};
|
||||
}
|
||||
} @else {
|
||||
.u-gc-#{$fromIndex}\/#{$toIndex}\@#{$breakpoint} {
|
||||
@media (min-width: #{$mediaquery}) {
|
||||
--gc-start: #{$fromIndex};
|
||||
--gc-end: #{$toIndex};
|
||||
@if $breakpoint-namespace == 'from-tiny' {
|
||||
@media (min-width: $from-tiny) {
|
||||
grid-column-start: #{$fromIndex};
|
||||
grid-column-end: #{$toIndex};
|
||||
}
|
||||
} @else if $breakpoint-namespace == 'from-small' {
|
||||
@media (min-width: $from-small) {
|
||||
grid-column-start: #{$fromIndex};
|
||||
grid-column-end: #{$toIndex};
|
||||
}
|
||||
} @else if $breakpoint-namespace == 'from-medium' {
|
||||
@media (min-width: $from-medium) {
|
||||
grid-column-start: #{$fromIndex};
|
||||
grid-column-end: #{$toIndex};
|
||||
}
|
||||
} @else if $breakpoint-namespace == 'from-large' {
|
||||
@media (min-width: $from-large) {
|
||||
grid-column-start: #{$fromIndex};
|
||||
grid-column-end: #{$toIndex};
|
||||
}
|
||||
} @else if $breakpoint-namespace == 'from-big' {
|
||||
@media (min-width: $from-big) {
|
||||
grid-column-start: #{$fromIndex};
|
||||
grid-column-end: #{$toIndex};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
// Visibility / Display
|
||||
// ==========================================================================
|
||||
|
||||
[hidden][aria-hidden="false"] {
|
||||
[hidden][aria-hidden='false'] {
|
||||
position: absolute;
|
||||
display: inherit;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
[hidden][aria-hidden="false"]:focus {
|
||||
[hidden][aria-hidden='false']:focus {
|
||||
clip: auto;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
////
|
||||
|
||||
@media print {
|
||||
|
||||
// 1. Black prints faster: http://www.sanbeiji.com/archives/953
|
||||
|
||||
*,
|
||||
@@ -31,19 +30,19 @@
|
||||
}
|
||||
|
||||
a[href]:after {
|
||||
content: " (" attr(href) ")";
|
||||
content: ' (' attr(href) ')';
|
||||
}
|
||||
|
||||
abbr[title]:after {
|
||||
content: " (" attr(title) ")";
|
||||
content: ' (' attr(title) ')';
|
||||
}
|
||||
|
||||
// Don't show links that are fragment identifiers, or use the `javascript:`
|
||||
// pseudo protocol.
|
||||
|
||||
a[href^="#"]:after,
|
||||
a[href^="javascript:"]:after {
|
||||
content: "";
|
||||
a[href^='#']:after,
|
||||
a[href^='javascript:']:after {
|
||||
content: '';
|
||||
}
|
||||
|
||||
pre,
|
||||
@@ -63,7 +62,6 @@
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,15 @@
|
||||
|
||||
// A list of aspect ratios that get generated as modifier classes.
|
||||
$aspect-ratios: (
|
||||
(2:1),
|
||||
(4:3),
|
||||
(16:9),
|
||||
(
|
||||
2: 1,
|
||||
),
|
||||
(
|
||||
4: 3,
|
||||
),
|
||||
(
|
||||
16: 9,
|
||||
)
|
||||
) !default;
|
||||
|
||||
/* stylelint-disable */
|
||||
@@ -21,11 +27,11 @@ $aspect-ratios: (
|
||||
@each $ratio in $aspect-ratios {
|
||||
@each $antecedent, $consequent in $ratio {
|
||||
@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.";
|
||||
}
|
||||
|
||||
.u-#{$antecedent}\:#{$consequent}::before {
|
||||
|
||||
@@ -21,25 +21,25 @@
|
||||
/* stylelint-disable string-quotes */
|
||||
|
||||
$spacing-directions: (
|
||||
null: null,
|
||||
'-top': '-top',
|
||||
'-right': '-right',
|
||||
'-bottom': '-bottom',
|
||||
'-left': '-left',
|
||||
null: null,
|
||||
'-top': '-top',
|
||||
'-right': '-right',
|
||||
'-bottom': '-bottom',
|
||||
'-left': '-left',
|
||||
'-horizontal': '-left' '-right',
|
||||
'-vertical': '-top' '-bottom',
|
||||
'-vertical': '-top' '-bottom',
|
||||
) !default;
|
||||
|
||||
$spacing-properties: (
|
||||
'padding': 'padding',
|
||||
'margin': 'margin',
|
||||
'margin': 'margin',
|
||||
) !default;
|
||||
|
||||
$spacing-sizes: (
|
||||
null: $unit,
|
||||
null: $unit,
|
||||
'-double': $unit * 2,
|
||||
'-small': $unit-small,
|
||||
'-none': 0px
|
||||
'-none': 0px,
|
||||
) !default;
|
||||
|
||||
@each $property-namespace, $property in $spacing-properties {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
// ARIA roles display visual cursor hints
|
||||
|
||||
[aria-busy="true"] {
|
||||
[aria-busy='true'] {
|
||||
cursor: progress;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @file Provides simple user configuration options.
|
||||
*/
|
||||
|
||||
import loconfig from '../../loconfig.json' assert { type: 'json' };
|
||||
import { merge } from '../utils/index.js';
|
||||
|
||||
let usrconfig;
|
||||
|
||||
try {
|
||||
usrconfig = await import('../../loconfig.local.json', {
|
||||
assert: { type: 'json' },
|
||||
});
|
||||
usrconfig = usrconfig.default;
|
||||
|
||||
merge(loconfig, usrconfig);
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
export default loconfig;
|
||||
|
||||
export {
|
||||
loconfig,
|
||||
};
|
||||
@@ -1,162 +0,0 @@
|
||||
/**
|
||||
* @file Retrieve the first available glob library.
|
||||
*
|
||||
* Note that options vary between libraries.
|
||||
*
|
||||
* Candidates:
|
||||
*
|
||||
* - {@link https://npmjs.com/package/tiny-glob tiny-glob} [1][5][6]
|
||||
* - {@link https://npmjs.com/package/globby globby} [2][5]
|
||||
* - {@link https://npmjs.com/package/fast-glob fast-glob} [3]
|
||||
* - {@link https://npmjs.com/package/glob glob} [1][4][5]
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* - [1] The library's function accepts only a single pattern.
|
||||
* - [2] The library's function accepts only an array of patterns.
|
||||
* - [3] The library's function accepts either a single pattern
|
||||
* or an array of patterns.
|
||||
* - [4] The library's function does not return a Promise but will be
|
||||
* wrapped in a function that does return a Promise.
|
||||
* - [5] The library's function will be wrapped in a function that
|
||||
* supports a single pattern and an array of patterns.
|
||||
* - [6] The library's function returns files and directories but will be
|
||||
* preconfigured to return only files.
|
||||
*/
|
||||
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
/**
|
||||
* @callback GlobFn
|
||||
*
|
||||
* @param {string|string[]} patterns - A string pattern
|
||||
* or an array of string patterns.
|
||||
* @param {object} options
|
||||
*
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} GlobOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {GlobFn|undefined} The discovered glob function.
|
||||
*/
|
||||
let glob;
|
||||
|
||||
/**
|
||||
* @type {string[]} A list of packages to attempt import.
|
||||
*/
|
||||
const candidates = [
|
||||
'tiny-glob',
|
||||
'globby',
|
||||
'fast-glob',
|
||||
'glob',
|
||||
];
|
||||
|
||||
try {
|
||||
glob = await importGlob();
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean} Whether a glob function was discovered (TRUE) or not (FALSE).
|
||||
*/
|
||||
const supportsGlob = (typeof glob === 'function');
|
||||
|
||||
/**
|
||||
* Imports the first available glob function.
|
||||
*
|
||||
* @throws {TypeError} If no glob library was found.
|
||||
*
|
||||
* @returns {GlobFn}
|
||||
*/
|
||||
async function importGlob() {
|
||||
for (let name of candidates) {
|
||||
try {
|
||||
let globModule = await import(name);
|
||||
|
||||
if (typeof globModule.default !== 'function') {
|
||||
throw new TypeError(`Expected ${name} to be a function`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the function to ensure
|
||||
* a common pattern.
|
||||
*/
|
||||
switch (name) {
|
||||
case 'tiny-glob':
|
||||
/** [1][5] */
|
||||
return createArrayableGlob(
|
||||
/** [6] */
|
||||
createPresetGlob(globModule.default, {
|
||||
filesOnly: true
|
||||
})
|
||||
);
|
||||
|
||||
case 'globby':
|
||||
/** [2][5] - If `patterns` is a string, wraps into an array. */
|
||||
return (patterns, options) => globModule.default([].concat(patterns), options);
|
||||
|
||||
case 'glob':
|
||||
/** [1][5] */
|
||||
return createArrayableGlob(
|
||||
/** [4] */
|
||||
promisify(globModule.default)
|
||||
);
|
||||
|
||||
default:
|
||||
return globModule.default;
|
||||
}
|
||||
} catch (err) {
|
||||
// swallow this error; skip to the next candidate.
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(
|
||||
`No glob library was found, expected one of: ${candidates.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper function for the glob function
|
||||
* to provide support for arrays of patterns.
|
||||
*
|
||||
* @param {function} globFn - The glob function.
|
||||
*
|
||||
* @returns {GlobFn}
|
||||
*/
|
||||
function createArrayableGlob(globFn) {
|
||||
return (patterns, options) => {
|
||||
/** [2] If `patterns` is a string, wraps into an array. */
|
||||
patterns = [].concat(patterns);
|
||||
|
||||
const globs = patterns.map((pattern) => globFn(pattern, options));
|
||||
|
||||
return Promise.all(globs).then((files) => {
|
||||
return [].concat.apply([], files);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper function for the glob function
|
||||
* to define new default options.
|
||||
*
|
||||
* @param {function} globFn - The glob function.
|
||||
* @param {GlobOptions} presets - The glob function options to preset.
|
||||
*
|
||||
* @returns {GlobFn}
|
||||
*/
|
||||
function createPresetGlob(globFn, presets) {
|
||||
return (patterns, options) => globFn(patterns, Object.assign({}, presets, options));
|
||||
}
|
||||
|
||||
export default glob;
|
||||
|
||||
export {
|
||||
glob,
|
||||
supportsGlob,
|
||||
};
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* @file If available, returns the PostCSS Processor creator and
|
||||
* any the Autoprefixer PostCSS plugin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('autoprefixer').autoprefixer.Options} AutoprefixerOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('postcss').AcceptedPlugin} AcceptedPlugin
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('postcss').Postcss} Postcss
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('postcss').ProcessOptions} ProcessOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('postcss').Processor} Processor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {AcceptedPlugin[]} PluginList
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object<string, AcceptedPlugin>} PluginMap
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {PluginList|PluginMap} PluginCollection
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PostCSSOptions
|
||||
*
|
||||
* @property {ProcessOptions} processor - The `Processor#process()` options.
|
||||
* @property {AutoprefixerOptions} autoprefixer - The `autoprefixer()` options.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Postcss|undefined} postcss - The discovered PostCSS function.
|
||||
* @type {AcceptedPlugin|undefined} autoprefixer - The discovered Autoprefixer function.
|
||||
*/
|
||||
let postcss, autoprefixer;
|
||||
|
||||
try {
|
||||
postcss = await import('postcss');
|
||||
postcss = postcss.default;
|
||||
|
||||
autoprefixer = await import('autoprefixer');
|
||||
autoprefixer = autoprefixer.default;
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean} Whether PostCSS was discovered (TRUE) or not (FALSE).
|
||||
*/
|
||||
const supportsPostCSS = (typeof postcss === 'function');
|
||||
|
||||
/**
|
||||
* @type {PluginList} A list of supported plugins.
|
||||
*/
|
||||
const pluginsList = [
|
||||
autoprefixer,
|
||||
];
|
||||
|
||||
/**
|
||||
* @type {PluginMap} A map of supported plugins.
|
||||
*/
|
||||
const pluginsMap = {
|
||||
'autoprefixer': autoprefixer,
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to create a PostCSS Processor with the given plugins and options.
|
||||
*
|
||||
* @param {PluginCollection} pluginsListOrMap - A list or map of plugins.
|
||||
* If a map of plugins, the plugin name looks up `options`.
|
||||
* @param {PostCSSOptions} options - The PostCSS wrapper options.
|
||||
*
|
||||
* @returns {Processor|null}
|
||||
*/
|
||||
function createProcessor(pluginsListOrMap, options)
|
||||
{
|
||||
if (!postcss) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const plugins = parsePlugins(pluginsListOrMap, options);
|
||||
|
||||
return postcss(plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the PostCSS plugins and options.
|
||||
*
|
||||
* @param {PluginCollection} pluginsListOrMap - A list or map of plugins.
|
||||
* If a map of plugins, the plugin name looks up `options`.
|
||||
* @param {PostCSSOptions} options - The PostCSS wrapper options.
|
||||
*
|
||||
* @returns {PluginList}
|
||||
*/
|
||||
function parsePlugins(pluginsListOrMap, options)
|
||||
{
|
||||
if (Array.isArray(pluginsListOrMap)) {
|
||||
return pluginsListOrMap;
|
||||
}
|
||||
|
||||
/** @type {PluginList} */
|
||||
const plugins = [];
|
||||
|
||||
for (let [ name, plugin ] of Object.entries(pluginsListOrMap)) {
|
||||
if (name in options) {
|
||||
plugin = plugin[name](options[name]);
|
||||
}
|
||||
|
||||
plugins.push(plugin);
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
export default postcss;
|
||||
|
||||
export {
|
||||
autoprefixer,
|
||||
createProcessor,
|
||||
parsePlugins,
|
||||
pluginsList,
|
||||
pluginsMap,
|
||||
postcss,
|
||||
supportsPostCSS,
|
||||
};
|
||||
@@ -1,9 +1,8 @@
|
||||
import loconfig from '../helpers/config.js';
|
||||
import glob, { supportsGlob } from '../helpers/glob.js';
|
||||
import message from '../helpers/message.js';
|
||||
import notification from '../helpers/notification.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
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 resolve from '../utils/template.js';
|
||||
import concat from 'concat';
|
||||
import {
|
||||
basename,
|
||||
@@ -65,7 +64,7 @@ export const productionConcatFilesArgs = [
|
||||
* @return {Promise}
|
||||
*/
|
||||
export default async function concatFiles(globOptions = null, concatOptions = null) {
|
||||
if (supportsGlob) {
|
||||
if (glob) {
|
||||
if (globOptions == null) {
|
||||
globOptions = productionGlobOptions;
|
||||
} else if (
|
||||
@@ -73,7 +72,7 @@ export default async function concatFiles(globOptions = null, concatOptions = nu
|
||||
globOptions !== developmentGlobOptions &&
|
||||
globOptions !== productionGlobOptions
|
||||
) {
|
||||
globOptions = merge({}, defaultGlobOptions, globOptions);
|
||||
globOptions = Object.assign({}, defaultGlobOptions, globOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,18 +82,9 @@ export default async function concatFiles(globOptions = null, concatOptions = nu
|
||||
concatOptions !== developmentConcatOptions &&
|
||||
concatOptions !== productionConcatOptions
|
||||
) {
|
||||
concatOptions = merge({}, defaultConcatOptions, concatOptions);
|
||||
concatOptions = Object.assign({}, defaultConcatOptions, concatOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @param {object} entry - The entrypoint to process.
|
||||
* @param {string[]} entry.includes - One or more paths to process.
|
||||
* @param {string} entry.outfile - The file to write to.
|
||||
* @param {?string} [entry.label] - The task label.
|
||||
* Defaults to the outfile name.
|
||||
* @return {Promise}
|
||||
*/
|
||||
loconfig.tasks.concats.forEach(async ({
|
||||
includes,
|
||||
outfile,
|
||||
@@ -108,25 +98,25 @@ export default async function concatFiles(globOptions = null, concatOptions = nu
|
||||
console.time(timeLabel);
|
||||
|
||||
try {
|
||||
if (!Array.isArray(includes)) {
|
||||
includes = [ includes ];
|
||||
}
|
||||
|
||||
includes = resolve(includes);
|
||||
outfile = resolve(outfile);
|
||||
|
||||
if (supportsGlob && globOptions) {
|
||||
includes = await glob(includes, globOptions);
|
||||
let files;
|
||||
|
||||
if (glob && globOptions) {
|
||||
files = await glob(includes, globOptions);
|
||||
} else {
|
||||
files = includes;
|
||||
}
|
||||
|
||||
if (concatOptions.removeDuplicates) {
|
||||
includes = includes.map((path) => normalize(path));
|
||||
includes = [ ...new Set(includes) ];
|
||||
files = files.map((path) => normalize(path));
|
||||
files = [ ...new Set(files) ];
|
||||
}
|
||||
|
||||
await concat(includes, outfile);
|
||||
await concat(files, outfile);
|
||||
|
||||
if (includes.length) {
|
||||
if (files.length) {
|
||||
message(`${label} concatenated`, 'success', timeLabel);
|
||||
} else {
|
||||
message(`${label} is empty`, 'notice', timeLabel);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import loconfig from '../helpers/config.js';
|
||||
import message from '../helpers/message.js';
|
||||
import notification from '../helpers/notification.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
import loconfig from '../utils/config.js';
|
||||
import message from '../utils/message.js';
|
||||
import notification from '../utils/notification.js';
|
||||
import resolve from '../utils/template.js';
|
||||
import esbuild from 'esbuild';
|
||||
import { basename } from 'node:path';
|
||||
|
||||
@@ -51,20 +50,9 @@ export default async function compileScripts(esBuildOptions = null) {
|
||||
esBuildOptions !== developmentESBuildOptions &&
|
||||
esBuildOptions !== productionESBuildOptions
|
||||
) {
|
||||
esBuildOptions = merge({}, defaultESBuildOptions, esBuildOptions);
|
||||
esBuildOptions = Object.assign({}, defaultESBuildOptions, esBuildOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @param {object} entry - The entrypoint to process.
|
||||
* @param {string[]} entry.includes - One or more paths to process.
|
||||
* @param {string} [entry.outdir] - The directory to write to.
|
||||
* @param {string} [entry.outfile] - The file to write to.
|
||||
* @param {?string} [entry.label] - The task label.
|
||||
* Defaults to the outdir or outfile name.
|
||||
* @throws {TypeError} If outdir and outfile are missing.
|
||||
* @return {Promise}
|
||||
*/
|
||||
loconfig.tasks.scripts.forEach(async ({
|
||||
includes,
|
||||
outdir = '',
|
||||
@@ -79,10 +67,6 @@ export default async function compileScripts(esBuildOptions = null) {
|
||||
console.time(timeLabel);
|
||||
|
||||
try {
|
||||
if (!Array.isArray(includes)) {
|
||||
includes = [ includes ];
|
||||
}
|
||||
|
||||
includes = resolve(includes);
|
||||
|
||||
if (outdir) {
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import loconfig from '../helpers/config.js';
|
||||
import message from '../helpers/message.js';
|
||||
import notification from '../helpers/notification.js';
|
||||
import {
|
||||
createProcessor,
|
||||
pluginsMap as postcssPluginsMap,
|
||||
supportsPostCSS
|
||||
} from '../helpers/postcss.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
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 resolve from '../utils/template.js';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { basename } from 'node:path';
|
||||
import { promisify } from 'node:util';
|
||||
@@ -87,10 +82,10 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
sassOptions !== developmentSassOptions &&
|
||||
sassOptions !== productionSassOptions
|
||||
) {
|
||||
sassOptions = merge({}, defaultSassOptions, sassOptions);
|
||||
sassOptions = Object.assign({}, defaultSassOptions, sassOptions);
|
||||
}
|
||||
|
||||
if (supportsPostCSS) {
|
||||
if (postcss) {
|
||||
if (postcssOptions == null) {
|
||||
postcssOptions = productionPostCSSOptions;
|
||||
} else if (
|
||||
@@ -98,19 +93,10 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
postcssOptions !== developmentPostCSSOptions &&
|
||||
postcssOptions !== productionPostCSSOptions
|
||||
) {
|
||||
postcssOptions = merge({}, defaultPostCSSOptions, postcssOptions);
|
||||
postcssOptions = Object.assign({}, defaultPostCSSOptions, postcssOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @param {object} entry - The entrypoint to process.
|
||||
* @param {string[]} entry.infile - The file to process.
|
||||
* @param {string} entry.outfile - The file to write to.
|
||||
* @param {?string} [entry.label] - The task label.
|
||||
* Defaults to the outfile name.
|
||||
* @return {Promise}
|
||||
*/
|
||||
loconfig.tasks.styles.forEach(async ({
|
||||
infile,
|
||||
outfile,
|
||||
@@ -130,9 +116,9 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
outFile: outfile,
|
||||
}));
|
||||
|
||||
if (supportsPostCSS && postcssOptions) {
|
||||
if (postcss && postcssOptions) {
|
||||
if (typeof postcssProcessor === 'undefined') {
|
||||
postcssProcessor = createProcessor(
|
||||
postcssProcessor = createPostCSSProcessor(
|
||||
postcssPluginsMap,
|
||||
postcssOptions
|
||||
);
|
||||
@@ -205,6 +191,35 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a PostCSS Processor with the given plugins and options.
|
||||
*
|
||||
* @param {array<(function|object)>|object<string, (function|object)>} pluginsListOrMap -
|
||||
* A list or map of plugins.
|
||||
* If a map of plugins, the plugin name looks up `options`.
|
||||
* @param {object} options - The PostCSS options.
|
||||
*/
|
||||
function createPostCSSProcessor(pluginsListOrMap, options)
|
||||
{
|
||||
let plugins;
|
||||
|
||||
if (Array.isArray(pluginsListOrMap)) {
|
||||
plugins = pluginsListOrMap;
|
||||
} else {
|
||||
plugins = [];
|
||||
|
||||
for (let [ name, plugin ] of Object.entries(pluginsListOrMap)) {
|
||||
if (name in options) {
|
||||
plugin = plugin[name](options[name]);
|
||||
}
|
||||
|
||||
plugins.push(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
return postcss(plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge unused styles from CSS files.
|
||||
*
|
||||
@@ -217,23 +232,22 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
*/
|
||||
async function purgeUnusedCSS(outfile, label) {
|
||||
label = label ?? basename(outfile);
|
||||
|
||||
const timeLabel = `${label} purged in`;
|
||||
console.time(timeLabel);
|
||||
|
||||
const purgeCSSContentFiles = Array.from(loconfig.tasks.purgeCSS.content);
|
||||
|
||||
const purgeCSSResults = await (new PurgeCSS()).purge({
|
||||
const purgeCSSResults = await new PurgeCSS().purge({
|
||||
content: purgeCSSContentFiles,
|
||||
css: [ outfile ],
|
||||
rejected: true,
|
||||
defaultExtractor: (content) => content.match(/[a-z0-9_\-\\\/\@]+/gi) || [],
|
||||
defaultExtractor: content => content.match(/[a-z0-9_\-\\\/\@]+/gi) || [],
|
||||
safelist: {
|
||||
standard: [ /^((?!\bu-gc-).)*$/ ]
|
||||
}
|
||||
})
|
||||
|
||||
for (let result of purgeCSSResults) {
|
||||
for(let result of purgeCSSResults) {
|
||||
await writeFile(outfile, result.css)
|
||||
|
||||
message(`${label} purged`, 'chore', timeLabel);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import loconfig from '../helpers/config.js';
|
||||
import message from '../helpers/message.js';
|
||||
import notification from '../helpers/notification.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
import loconfig from '../utils/config.js';
|
||||
import message from '../utils/message.js';
|
||||
import notification from '../utils/notification.js';
|
||||
import resolve from '../utils/template.js';
|
||||
import { basename } from 'node:path';
|
||||
import mixer from 'svg-mixer';
|
||||
|
||||
@@ -45,18 +44,9 @@ export default async function compileSVGs(mixerOptions = null) {
|
||||
mixerOptions !== developmentMixerOptions &&
|
||||
mixerOptions !== productionMixerOptions
|
||||
) {
|
||||
mixerOptions = merge({}, defaultMixerOptions, mixerOptions);
|
||||
mixerOptions = Object.assign({}, defaultMixerOptions, mixerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @param {object} entry - The entrypoint to process.
|
||||
* @param {string[]} entry.includes - One or more paths to process.
|
||||
* @param {string} entry.outfile - The file to write to.
|
||||
* @param {?string} [entry.label] - The task label.
|
||||
* Defaults to the outfile name.
|
||||
* @return {Promise}
|
||||
*/
|
||||
loconfig.tasks.svgs.forEach(async ({
|
||||
includes,
|
||||
outfile,
|
||||
@@ -70,10 +60,6 @@ export default async function compileSVGs(mixerOptions = null) {
|
||||
console.time(timeLabel);
|
||||
|
||||
try {
|
||||
if (!Array.isArray(includes)) {
|
||||
includes = [ includes ];
|
||||
}
|
||||
|
||||
includes = resolve(includes);
|
||||
outfile = resolve(outfile);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import loconfig from '../helpers/config.js';
|
||||
import message from '../helpers/message.js';
|
||||
import resolve from '../helpers/template.js';
|
||||
import { merge } from '../utils/index.js';
|
||||
import loconfig from '../utils/config.js';
|
||||
import message from '../utils/message.js';
|
||||
import resolve from '../utils/template.js';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import events from 'node:events';
|
||||
import {
|
||||
@@ -95,22 +94,11 @@ export default async function bumpVersions(versionOptions = null) {
|
||||
versionOptions !== developmentVersionOptions &&
|
||||
versionOptions !== productionVersionOptions
|
||||
) {
|
||||
versionOptions = merge({}, defaultVersionOptions, versionOptions);
|
||||
versionOptions = Object.assign({}, defaultVersionOptions, versionOptions);
|
||||
}
|
||||
|
||||
const queue = new Map();
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @param {object} entry - The entrypoint to process.
|
||||
* @param {string} entry.outfile - The file to write to.
|
||||
* @param {?string} [entry.label] - The task label.
|
||||
* Defaults to the outfile name.
|
||||
* @param {?string} [entry.format] - The version number format.
|
||||
* @param {?string} [entry.key] - The JSON field name assign the version number to.
|
||||
* @param {?string|number} [entry.pretty] - The white space to use to format the JSON file.
|
||||
* @return {Promise}
|
||||
*/
|
||||
loconfig.tasks.versions.forEach(({
|
||||
outfile,
|
||||
label = null,
|
||||
|
||||
66
build/utils/config.js
Normal file
66
build/utils/config.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @file Provides simple user configuration options.
|
||||
*/
|
||||
|
||||
import loconfig from '../../loconfig.json' assert { type: 'json' };
|
||||
|
||||
let usrconfig;
|
||||
|
||||
try {
|
||||
usrconfig = await import('../../loconfig.local.json', {
|
||||
assert: { type: 'json' }
|
||||
});
|
||||
usrconfig = usrconfig.default;
|
||||
|
||||
merge(loconfig, usrconfig);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
95
build/utils/glob.js
Normal file
95
build/utils/glob.js
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @file Retrieve the first available glob library.
|
||||
*
|
||||
* Note that options vary between libraries.
|
||||
*
|
||||
* Candidates:
|
||||
*
|
||||
* - {@link https://npmjs.com/package/tiny-glob tiny-glob}
|
||||
* - {@link https://npmjs.com/package/globby globby}
|
||||
* - {@link https://npmjs.com/package/fast-glob fast-glob}
|
||||
* - {@link https://npmjs.com/package/glob glob}
|
||||
*/
|
||||
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
/**
|
||||
* @type {string[]} A list of packages to attempt import.
|
||||
*/
|
||||
const candidates = [
|
||||
'tiny-glob',
|
||||
'globby',
|
||||
'fast-glob',
|
||||
'glob',
|
||||
];
|
||||
|
||||
let glob;
|
||||
|
||||
try {
|
||||
glob = await importGlob();
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
export default glob;
|
||||
|
||||
/**
|
||||
* Imports the first available glob function.
|
||||
*
|
||||
* @throws {TypeError} If no glob library was found.
|
||||
* @return {function}
|
||||
*/
|
||||
async function importGlob() {
|
||||
let glob, module;
|
||||
|
||||
for (let name of candidates) {
|
||||
try {
|
||||
module = await import(name);
|
||||
|
||||
if (typeof module.default !== 'function') {
|
||||
throw new TypeError(`Expected ${name} to be a function`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the function to ensure
|
||||
* a common pattern.
|
||||
*/
|
||||
switch (name) {
|
||||
case 'tiny-glob':
|
||||
return createArrayableGlob(module.default, {
|
||||
filesOnly: true
|
||||
});
|
||||
|
||||
case 'glob':
|
||||
return promisify(module.default);
|
||||
|
||||
default:
|
||||
return module.default;
|
||||
}
|
||||
} catch (err) {
|
||||
// swallow this error; skip to the next candidate.
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(
|
||||
`No glob library was found, expected one of: ${candidates.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper function for the glob function
|
||||
* to provide support for arrays of patterns.
|
||||
*
|
||||
* @param {function} glob - The glob function.
|
||||
* @param {object} options - The glob options.
|
||||
* @return {function}
|
||||
*/
|
||||
function createArrayableGlob(glob, options) {
|
||||
return (patterns, options) => {
|
||||
const globs = patterns.map((pattern) => glob(pattern, options));
|
||||
|
||||
return Promise.all(globs).then((files) => {
|
||||
return [].concat.apply([], files);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/**
|
||||
* @file Provides generic functions and constants.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {RegExp} - Match all special characters.
|
||||
*/
|
||||
const regexUnescaped = /[\[\]\{\}\(\)\-\*\+\?\.\,\\\^\$\|\#\s]/g;
|
||||
|
||||
/**
|
||||
* Quotes regular expression characters.
|
||||
*
|
||||
* @param {string} str - The input string.
|
||||
* @return {string} Returns the quoted (escaped) string.
|
||||
*/
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(regexUnescaped, '\\$&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new object with all nested object properties
|
||||
* concatenated into it recursively.
|
||||
*
|
||||
* Nested keys are flattened into a property path:
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* a: {
|
||||
* b: {
|
||||
* c: 1
|
||||
* }
|
||||
* },
|
||||
* d: 1
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* "a.b.c": 1,
|
||||
* "d": 1
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {object} input - The object to flatten.
|
||||
* @param {string} prefix - The parent key prefix.
|
||||
* @param {object} target - The object that will receive the flattened properties.
|
||||
* @return {object} Returns the `target` object.
|
||||
*/
|
||||
function flatten(input, prefix, target = {}) {
|
||||
for (const key in input) {
|
||||
const field = (prefix ? prefix + '.' + key : key);
|
||||
|
||||
if (isObjectLike(input[key])) {
|
||||
flatten(input[key], field, target);
|
||||
} else {
|
||||
target[field] = input[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');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
export {
|
||||
escapeRegExp,
|
||||
flatten,
|
||||
isObjectLike,
|
||||
merge,
|
||||
regexUnescaped,
|
||||
};
|
||||
@@ -11,7 +11,7 @@ import kleur from 'kleur';
|
||||
* @param {string} [type] - The type of message.
|
||||
* @param {string} [timerID] - The console time label to output.
|
||||
*/
|
||||
function message(text, type, timerID) {
|
||||
export default function message(text, type, timerID) {
|
||||
switch (type) {
|
||||
case 'success':
|
||||
console.log('✅ ', kleur.bgGreen().black(text));
|
||||
@@ -52,10 +52,4 @@ function message(text, type, timerID) {
|
||||
}
|
||||
|
||||
console.log('');
|
||||
}
|
||||
|
||||
export default message;
|
||||
|
||||
export {
|
||||
message,
|
||||
};
|
||||
@@ -16,7 +16,7 @@ import notifier from 'node-notifier';
|
||||
* @param {function} callback - The notification callback.
|
||||
* @return {void}
|
||||
*/
|
||||
function notification(options, callback) {
|
||||
export default function notification(options, callback) {
|
||||
if (typeof options === 'string') {
|
||||
options = {
|
||||
message: options
|
||||
@@ -42,10 +42,4 @@ function notification(options, callback) {
|
||||
}
|
||||
|
||||
notifier.notify(options, callback);
|
||||
}
|
||||
|
||||
export default notification;
|
||||
|
||||
export {
|
||||
notification,
|
||||
};
|
||||
27
build/utils/postcss.js
Normal file
27
build/utils/postcss.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @file If available, returns the PostCSS Processor creator and
|
||||
* any the Autoprefixer PostCSS plugin.
|
||||
*/
|
||||
|
||||
let postcss, autoprefixer;
|
||||
|
||||
try {
|
||||
postcss = await import('postcss');
|
||||
postcss = postcss.default;
|
||||
|
||||
autoprefixer = await import('autoprefixer');
|
||||
autoprefixer = autoprefixer.default;
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
export default postcss;
|
||||
export const pluginsList = [
|
||||
autoprefixer,
|
||||
];
|
||||
export const pluginsMap = {
|
||||
'autoprefixer': autoprefixer,
|
||||
};
|
||||
export {
|
||||
autoprefixer
|
||||
};
|
||||
@@ -3,10 +3,6 @@
|
||||
*/
|
||||
|
||||
import loconfig from './config.js';
|
||||
import {
|
||||
escapeRegExp,
|
||||
flatten
|
||||
} from '../utils/index.js';
|
||||
|
||||
const templateData = flatten({
|
||||
paths: loconfig.paths
|
||||
@@ -26,7 +22,7 @@ const templateData = flatten({
|
||||
* @param {object} [data] - An object in the form `{ 'from': 'to', … }`.
|
||||
* @return {*} Returns the transformed value.
|
||||
*/
|
||||
function resolve(input, data = templateData) {
|
||||
export default function resolve(input, data = templateData) {
|
||||
switch (typeof input) {
|
||||
case 'string': {
|
||||
return resolveValue(input, data);
|
||||
@@ -60,7 +56,7 @@ function resolve(input, data = templateData) {
|
||||
* @param {object} [data] - An object in the form `{ 'from': 'to', … }`.
|
||||
* @return {string} Returns the translated string.
|
||||
*/
|
||||
function resolveValue(input, data = templateData) {
|
||||
export function resolveValue(input, data = templateData) {
|
||||
const tags = [];
|
||||
|
||||
if (data !== templateData) {
|
||||
@@ -97,9 +93,55 @@ function resolveValue(input, data = templateData) {
|
||||
});
|
||||
}
|
||||
|
||||
export default resolve;
|
||||
/**
|
||||
* Creates a new object with all nested object properties
|
||||
* concatenated into it recursively.
|
||||
*
|
||||
* Nested keys are flattened into a property path:
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* a: {
|
||||
* b: {
|
||||
* c: 1
|
||||
* }
|
||||
* },
|
||||
* d: 1
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* "a.b.c": 1,
|
||||
* "d": 1
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {object} input - The object to flatten.
|
||||
* @param {string} prefix - The parent key prefix.
|
||||
* @param {object} target - The object that will receive the flattened properties.
|
||||
* @return {object} Returns the `target` object.
|
||||
*/
|
||||
function flatten(input, prefix, target = {}) {
|
||||
for (let key in input) {
|
||||
let field = (prefix ? prefix + '.' + key : key);
|
||||
|
||||
export {
|
||||
resolve,
|
||||
resolveValue,
|
||||
};
|
||||
if (typeof input[key] === 'object') {
|
||||
flatten(input[key], field, target);
|
||||
} else {
|
||||
target[field] = input[key];
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes regular expression characters.
|
||||
*
|
||||
* @param {string} str - The input string.
|
||||
* @return {string} Returns the quoted (escaped) string.
|
||||
*/
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[\[\]\{\}\(\)\-\*\+\?\.\,\\\^\$\|\#\s]/g, '\\$&');
|
||||
}
|
||||
@@ -2,11 +2,10 @@ 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 loconfig from './helpers/config.js';
|
||||
import message from './helpers/message.js';
|
||||
import notification from './helpers/notification.js';
|
||||
import resolve from './helpers/template.js';
|
||||
import { merge } from './utils/index.js';
|
||||
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';
|
||||
|
||||
|
||||
@@ -181,7 +181,10 @@ detection and smooth scrolling with parallax.
|
||||
```js
|
||||
import LocomotiveScroll from 'locomotive-scroll';
|
||||
|
||||
this.scroll = new LocomotiveScroll({})
|
||||
this.scroll = new LocomotiveScroll({
|
||||
el: this.el,
|
||||
smooth: true
|
||||
});
|
||||
````
|
||||
|
||||
Learn more about [Locomotive Scroll][locomotive-scroll].
|
||||
|
||||
1440
package-lock.json
generated
1440
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -6,37 +6,36 @@
|
||||
"author": "Locomotive <info@locomotive.ca>",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=17.9",
|
||||
"node": ">=18.13",
|
||||
"npm": ">=8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node --experimental-json-modules --no-warnings build/watch.js",
|
||||
"build": "node --experimental-json-modules --no-warnings build/build.js"
|
||||
"build": "node --experimental-json-modules --no-warnings build/build.js",
|
||||
"precommit": "prettier --write 'assets/**/*.{js,scss,json}'"
|
||||
},
|
||||
"dependencies": {
|
||||
"locomotive-scroll": "^5.0.0-beta.8",
|
||||
"locomotive-scroll": "^4.1.4",
|
||||
"modujs": "^1.4.2",
|
||||
"modularload": "^1.2.6",
|
||||
"normalize.css": "^8.0.1",
|
||||
"sass": "^1.57.1",
|
||||
"svg4everybody": "^2.1.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.13",
|
||||
"browser-sync": "^2.27.11",
|
||||
"concat": "^1.0.3",
|
||||
"esbuild": "^0.17.6",
|
||||
"esbuild": "^0.16.17",
|
||||
"kleur": "^4.1.5",
|
||||
"node-notifier": "^10.0.1",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "^2.8.3",
|
||||
"purgecss": "^5.0.0",
|
||||
"sass": "^1.57.1",
|
||||
"svg-mixer": "~2.3.14",
|
||||
"tiny-glob": "^0.2.9"
|
||||
},
|
||||
"overrides": {
|
||||
"browser-sync": {
|
||||
"ua-parser-js": "~1.0.33"
|
||||
},
|
||||
"svg-mixer": {
|
||||
"postcss": "^8.4.20"
|
||||
}
|
||||
|
||||
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
@@ -18,8 +18,8 @@
|
||||
</head>
|
||||
<body data-module-load>
|
||||
<div data-load-container>
|
||||
<div data-module-scroll="main">
|
||||
<header>
|
||||
<div class="o-scroll" data-module-scroll="main">
|
||||
<header data-scroll-section>
|
||||
<a href="/"><h1>Locomotive Boilerplate</h1></a>
|
||||
<nav>
|
||||
<ul>
|
||||
@@ -30,7 +30,7 @@
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<main data-scroll-section>
|
||||
<div class="o-container">
|
||||
<h1 class="c-heading -h1">Page</h1>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer data-scroll-section>
|
||||
<p>Made with <a href="https://github.com/locomotivemtl/locomotive-boilerplate" title="Locomotive Boilerplate" target="_blank" rel="noopener">🚂</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
|
||||
<body data-module-load>
|
||||
<div data-load-container>
|
||||
<div data-module-scroll="main">
|
||||
<header>
|
||||
<div class="o-scroll" data-module-scroll="main">
|
||||
<header data-scroll-section>
|
||||
<a href="/">
|
||||
<h1>Locomotive Boilerplate</h1>
|
||||
</a>
|
||||
@@ -48,7 +48,7 @@
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<main data-scroll-section>
|
||||
<div class="o-container">
|
||||
<h1 class="c-heading -h1">Hello</h1>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer data-scroll-section>
|
||||
<p>Made with <a href="https://github.com/locomotivemtl/locomotive-boilerplate"
|
||||
title="Locomotive Boilerplate" target="_blank" rel="noopener">🚂</a></p>
|
||||
</footer>
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
</head>
|
||||
<body data-module-load>
|
||||
<div data-load-container>
|
||||
<div data-module-scroll="main">
|
||||
<header>
|
||||
<div class="o-scroll" data-module-scroll="main">
|
||||
<header data-scroll-section>
|
||||
<a href="/"><h1>Locomotive Boilerplate</h1></a>
|
||||
<nav>
|
||||
<ul>
|
||||
@@ -30,7 +30,7 @@
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<main data-scroll-section>
|
||||
<div class="o-container">
|
||||
<h1 class="c-heading -h1">Images</h1>
|
||||
|
||||
@@ -39,24 +39,84 @@
|
||||
|
||||
<h3 class="c-heading -h3">Basic</h3>
|
||||
|
||||
<img src="http://picsum.photos/800/600?v=1" alt="" loading="lazy" class="c-image_img" width="800" height="600"/>
|
||||
|
||||
<div style="width: 640px; max-width: 100%;">
|
||||
<div class="c-image"><img src="http://picsum.photos/800/600?v=2" alt="" loading="lazy" class="c-image_img" width="800" height="600"/></div>
|
||||
<div class="c-image"><img src="http://picsum.photos/800/600?v=3" alt="" loading="lazy" class="c-image_img" width="800" height="600"/></div>
|
||||
<div class="o-ratio u-4:3"><img data-load-src="http://picsum.photos/800/600?v=1" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /></div>
|
||||
<div class="o-ratio u-4:3"><img data-load-src="http://picsum.photos/800/600?v=2" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /></div>
|
||||
</div>
|
||||
|
||||
<h4 class="c-heading -h3">Using o-ratio</h3>
|
||||
<h4 class="c-heading -h3">Using o-ratio & background-image</h3>
|
||||
|
||||
<div style="width: 480px; max-width: 100%;">
|
||||
<div class="o-ratio u-4:3"><div class="c-image || o-ratio_content"><img src="http://picsum.photos/800/600?v=4" alt="" loading="lazy" class="c-image_img"/></div></div>
|
||||
<div class="o-ratio u-4:3"><div class="c-image || o-ratio_content"><img src="http://picsum.photos/800/600?v=5" alt="" loading="lazy" class="c-image_img"/></div></div>
|
||||
<div class="o-ratio u-16:9" data-load-style="background-size: cover; background-position: center; background-image: url(http://picsum.photos/640/480?v=1);"></div>
|
||||
<div class="o-ratio u-16:9" data-load-style="background-size: cover; background-position: center; background-image: url(http://picsum.photos/640/480?v=2);"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3 class="c-heading -h3">Relative to scroll</h3>
|
||||
|
||||
<h4 class="c-heading -h3">Using o-ratio & img</h3>
|
||||
|
||||
<div style="width: 640px; max-width: 100%;">
|
||||
<div class="o-ratio u-4:3">
|
||||
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/800/600?v=1" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
|
||||
</div>
|
||||
<div class="o-ratio u-4:3">
|
||||
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/800/600?v=2" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
|
||||
</div>
|
||||
<div class="o-ratio u-4:3">
|
||||
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/800/600?v=3" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
|
||||
</div>
|
||||
<div class="o-ratio u-4:3">
|
||||
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/800/600?v=4" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
|
||||
</div>
|
||||
<div class="o-ratio u-4:3">
|
||||
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/800/600?v=5" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="c-heading -h3">Using o-ratio & background-image</h3>
|
||||
|
||||
<div style="width: 480px; max-width: 100%;">
|
||||
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/1280/720?v=1"></div>
|
||||
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/1280/720?v=2"></div>
|
||||
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/1280/720?v=3"></div>
|
||||
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/1280/720?v=4"></div>
|
||||
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/1280/720?v=5"></div>
|
||||
</div>
|
||||
|
||||
<h4 class="c-heading -h3">Using SVG viewport for ratio</h3>
|
||||
|
||||
<div style="width: 480px; max-width: 100%;">
|
||||
<img
|
||||
data-scroll
|
||||
data-scroll-call="lazyLoad, Scroll, main"
|
||||
data-src="http://picsum.photos/640/480?v=6"
|
||||
alt=""
|
||||
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3C/svg%3E"
|
||||
/>
|
||||
|
||||
<img
|
||||
data-scroll
|
||||
data-scroll-call="lazyLoad, Scroll, main"
|
||||
data-src="http://picsum.photos/640/480?v=7"
|
||||
alt=""
|
||||
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3C/svg%3E"
|
||||
/>
|
||||
|
||||
<img
|
||||
data-scroll
|
||||
data-scroll-call="lazyLoad, Scroll, main"
|
||||
data-src="http://picsum.photos/640/480?v=8"
|
||||
alt=""
|
||||
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3C/svg%3E"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer data-scroll-section>
|
||||
<p>Made with <a href="https://github.com/locomotivemtl/locomotive-boilerplate" title="Locomotive Boilerplate" target="_blank" rel="noopener">🚂</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
<body data-module-load>
|
||||
|
||||
<div data-load-container>
|
||||
<div data-module-scroll="main">
|
||||
<header>
|
||||
<div class="o-scroll" data-module-scroll="main">
|
||||
<header data-scroll-section>
|
||||
<a href="/">
|
||||
<h1>Locomotive Boilerplate</h1>
|
||||
</a>
|
||||
@@ -56,13 +56,13 @@
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main data-module-example>
|
||||
<main data-module-example data-scroll-section>
|
||||
<div class="o-container">
|
||||
<h1 class="c-heading -h1">Hello</h1>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer data-scroll-section>
|
||||
<p>Made with <a href="https://github.com/locomotivemtl/locomotive-boilerplate"
|
||||
title="Locomotive Boilerplate" target="_blank" rel="noopener">🚂</a></p>
|
||||
</footer>
|
||||
|
||||
Reference in New Issue
Block a user