mirror of
https://github.com/locomotivemtl/locomotive-boilerplate.git
synced 2026-01-15 00:55:08 +08:00
Merge branch 'master' into feature/es6-updates
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
* Uses [SVG Mixer] for processing SVG files and generating spritesheets.
|
||||
* Uses [ITCSS] for a sane and scalable CSS architecture.
|
||||
* Uses [Locomotive Scroll] for smooth scrolling with parallax effects.
|
||||
* Uses a custom [grid system](docs/grid.md) for layout creation.
|
||||
|
||||
Learn more about [languages and technologies](docs/technologies.md).
|
||||
|
||||
@@ -84,6 +85,7 @@ Learn more about [development and building](docs/development.md).
|
||||
|
||||
* [Development and building](docs/development.md)
|
||||
* [Languages and technologies](docs/technologies.md)
|
||||
* [Grid system](docs/grid.md)
|
||||
|
||||
[BrowserSync]: https://npmjs.com/package/browser-sync
|
||||
[ESBuild]: https://npmjs.com/package/esbuild
|
||||
|
||||
0
assets/images/sprite/.gitkeep
Normal file
0
assets/images/sprite/.gitkeep
Normal file
@@ -3,6 +3,7 @@ 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
|
||||
@@ -24,6 +25,11 @@ window.onload = (event) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const EAGER_FONTS = [
|
||||
{ family: 'Source Sans', style: 'normal', weight: 400 },
|
||||
{ family: 'Source Sans', style: 'normal', weight: 700 },
|
||||
];
|
||||
|
||||
function init() {
|
||||
globals();
|
||||
|
||||
@@ -32,5 +38,21 @@ function init() {
|
||||
html.classList.add(config.CSS_CLASS.LOADED);
|
||||
html.classList.add(config.CSS_CLASS.READY);
|
||||
html.classList.remove(config.CSS_CLASS.LOADING);
|
||||
|
||||
/**
|
||||
* Eagerly load the following fonts.
|
||||
*/
|
||||
if (isFontLoadingAPIAvailable) {
|
||||
loadFonts(EAGER_FONTS).then((eagerFonts) => {
|
||||
html.classList.add('fonts-loaded');
|
||||
// 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*/))
|
||||
// console.groupEnd()
|
||||
// console.group('State of all fonts:')
|
||||
// document.fonts.forEach((font) => console.log(font.family, font.style, font.weight, font.status/*, font*/))
|
||||
// console.groupEnd()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export {default as Example} from './modules/Example';
|
||||
export {default as Load} from './modules/Load';
|
||||
export {default as Scroll} from './modules/Scroll';
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { module } from 'modujs';
|
||||
import { EAGER_FONTS } from '../app';
|
||||
import { whenReady } from '../utils/fonts';
|
||||
|
||||
export default class extends module {
|
||||
constructor(m) {
|
||||
@@ -6,5 +8,10 @@ export default class extends module {
|
||||
}
|
||||
|
||||
init() {
|
||||
whenReady(EAGER_FONTS).then((fonts) => this.onFontsLoaded(fonts));
|
||||
}
|
||||
|
||||
onFontsLoaded(fonts) {
|
||||
console.log('Example: Eager Fonts Loaded!', fonts)
|
||||
}
|
||||
}
|
||||
|
||||
402
assets/scripts/utils/fonts.js
Normal file
402
assets/scripts/utils/fonts.js
Normal file
@@ -0,0 +1,402 @@
|
||||
/**
|
||||
* Font Faces
|
||||
*
|
||||
* Provides utilities to facilitate interactions with the CSS Font Loading API.
|
||||
*
|
||||
* Features functions to:
|
||||
*
|
||||
* - Retrieve one or more `FontFace` instances based on a font search query.
|
||||
* - Check if a `FontFace` instance matches a font search query.
|
||||
* - Eagerly load fonts that match a font search query.
|
||||
* - Wait until fonts that match a font search query are loaded.
|
||||
*
|
||||
* References:
|
||||
*
|
||||
* - {@link https://developer.mozilla.org/en-US/docs/Web/API/CSS_Font_Loading_API}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} FontFaceReference
|
||||
*
|
||||
* @property {string} family - The name used to identify the font in our CSS.
|
||||
* @property {string} [style] - The style used by the font in our CSS.
|
||||
* @property {string} [weight] - The weight used by the font in our CSS.
|
||||
*/
|
||||
|
||||
const isFontLoadingAPIAvailable = ('fonts' in document);
|
||||
|
||||
/**
|
||||
* Determines if the given font matches the given `FontFaceReference`.
|
||||
*
|
||||
* @param {FontFace} font - The font to inspect.
|
||||
* @param {FontFaceReference} criterion - The object of property values to match.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function conformsToReference(font, criterion)
|
||||
{
|
||||
for (const [ key, value ] of Object.entries(criterion)) {
|
||||
switch (key) {
|
||||
case 'family': {
|
||||
if (trim(font[key]) !== value) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'weight': {
|
||||
/**
|
||||
* Note concerning font weights:
|
||||
* Loose equality (`==`) is used to compare numeric weights,
|
||||
* a number (`400`) and a numeric string (`"400"`).
|
||||
* Comparison between numeric and keyword values is neglected.
|
||||
*
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#common_weight_name_mapping
|
||||
*/
|
||||
if (font[key] != value) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if (font[key] !== value) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given font matches the given font shorthand.
|
||||
*
|
||||
* @param {FontFace} font - The font to inspect.
|
||||
* @param {string} criterion - The font shorthand to match.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function conformsToShorthand(font, criterion)
|
||||
{
|
||||
const family = trim(font.family);
|
||||
|
||||
if (trim(family) === criterion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
criterion.endsWith(trim(family)) && (
|
||||
criterion.match(font.weight) ||
|
||||
criterion.match(font.style)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given font matches any of the given criteria.
|
||||
*
|
||||
* @param {FontFace} font - The font to inspect.
|
||||
* @param {FontFaceReference[]} criteria - A list of objects with property values to match.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function conformsToAnyReference(font, criteria)
|
||||
{
|
||||
for (const criterion of criteria) {
|
||||
if (conformsToReference(font, criterion)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator of all `FontFace` from `document.fonts` that satisfy
|
||||
* the provided `FontFaceReference`.
|
||||
*
|
||||
* @param {FontFaceReference} font
|
||||
*
|
||||
* @returns {FontFace[]}
|
||||
*/
|
||||
function findManyByReference(search)
|
||||
{
|
||||
const found = [];
|
||||
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToReference(font, search)) {
|
||||
found.push(font);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator of all `FontFace` from `document.fonts` that satisfy
|
||||
* the provided font shorthand.
|
||||
*
|
||||
* @param {string} font
|
||||
*
|
||||
* @returns {FontFace[]}
|
||||
*/
|
||||
function findManyByShorthand(search)
|
||||
{
|
||||
const found = [];
|
||||
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToShorthand(font, search)) {
|
||||
found.push(font);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first `FontFace` from `document.fonts` that satisfies
|
||||
* the provided `FontFaceReference`.
|
||||
*
|
||||
* @param {FontFaceReference} font
|
||||
*
|
||||
* @returns {?FontFace}
|
||||
*/
|
||||
function findOneByReference(search)
|
||||
{
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToReference(font, criterion)) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first `FontFace` from `document.fonts` that satisfies
|
||||
* the provided font shorthand.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* - "Roboto"
|
||||
* - "italic bold 16px Roboto"
|
||||
*
|
||||
* @param {string} font
|
||||
*
|
||||
* @returns {?FontFace}
|
||||
*/
|
||||
function findOneByShorthand(search)
|
||||
{
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToShorthand(font, search)) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a `FontFace` from `document.fonts` that satisfies
|
||||
* the provided query.
|
||||
*
|
||||
* @param {FontFaceReference|string} font - Either:
|
||||
* - a `FontFaceReference` object
|
||||
* - a font family name
|
||||
* - a font specification, for example "italic bold 16px Roboto"
|
||||
*
|
||||
* @returns {?FontFace}
|
||||
*
|
||||
* @throws {TypeError}
|
||||
*/
|
||||
function getAny(search) {
|
||||
if (search) {
|
||||
switch (typeof search) {
|
||||
case 'string':
|
||||
return findOneByShorthand(search);
|
||||
|
||||
case 'object':
|
||||
return findOneByReference(search);
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(
|
||||
'Expected font query to be font shorthand or font reference'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator of all `FontFace` from `document.fonts` that satisfy
|
||||
* the provided queries.
|
||||
*
|
||||
* @param {FontFaceReference|string|(FontFaceReference|string)[]} queries
|
||||
*
|
||||
* @returns {FontFace[]}
|
||||
*
|
||||
* @throws {TypeError}
|
||||
*/
|
||||
function getMany(queries) {
|
||||
if (!Array.isArray(queries)) {
|
||||
queries = [ queries ];
|
||||
}
|
||||
|
||||
const found = new Set();
|
||||
|
||||
queries.forEach((search) => {
|
||||
if (search) {
|
||||
switch (typeof search) {
|
||||
case 'string':
|
||||
found.add(...findManyByShorthand(search));
|
||||
return;
|
||||
|
||||
case 'object':
|
||||
found.add(...findManyByReference(search));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(
|
||||
'Expected font query to be font shorthand or font reference'
|
||||
);
|
||||
})
|
||||
|
||||
return [ ...found ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a font face is registered.
|
||||
*
|
||||
* @param {FontFace|FontFaceReference|string} search - Either:
|
||||
* - a `FontFace` instance
|
||||
* - a `FontFaceReference` object
|
||||
* - a font family name
|
||||
* - a font specification, for example "italic bold 16px Roboto"
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasAny(search) {
|
||||
if (search instanceof FontFace) {
|
||||
return document.fonts.has(search);
|
||||
}
|
||||
|
||||
return getAny(search) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eagerly load fonts.
|
||||
*
|
||||
* Most user agents only fetch and load fonts when they are first needed
|
||||
* ("lazy loaded"), which can result in a perceptible delay.
|
||||
*
|
||||
* This function will "eager load" the fonts.
|
||||
*
|
||||
* @param {(FontFace|FontFaceReference)[]} fontsToLoad - List of fonts to load.
|
||||
* @param {boolean} [debug] - If TRUE, log details to the console.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function loadFonts(fontsToLoad, debug = false)
|
||||
{
|
||||
if ((fontsToLoad.size ?? fontsToLoad.length) === 0) {
|
||||
throw new TypeError(
|
||||
'Expected at least one font'
|
||||
);
|
||||
}
|
||||
|
||||
return await loadFontsWithAPI([ ...fontsToLoad ], debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eagerly load a font using `FontFaceSet` API.
|
||||
*
|
||||
* @param {FontFace} font
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function loadFontFaceWithAPI(font)
|
||||
{
|
||||
return await (font.status === 'unloaded'
|
||||
? font.load()
|
||||
: font.loaded
|
||||
).then((font) => font, (err) => font)
|
||||
}
|
||||
|
||||
/**
|
||||
* Eagerly load fonts using `FontFaceSet` API.
|
||||
*
|
||||
* @param {FontFaceReference[]} fontsToLoad
|
||||
* @param {boolean} [debug]
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function loadFontsWithAPI(fontsToLoad, debug = false)
|
||||
{
|
||||
debug && console.group('[loadFonts:API]', fontsToLoad.length, '/', document.fonts.size);
|
||||
|
||||
const fontsToBeLoaded = [];
|
||||
|
||||
for (const fontToLoad of fontsToLoad) {
|
||||
if (fontToLoad instanceof FontFace) {
|
||||
if (!document.fonts.has(fontToLoad)) {
|
||||
document.fonts.add(fontToLoad);
|
||||
}
|
||||
|
||||
fontsToBeLoaded.push(
|
||||
loadFontFaceWithAPI(fontToLoad)
|
||||
);
|
||||
} else {
|
||||
fontsToBeLoaded.push(
|
||||
...getMany(fontToLoad).map((font) => loadFontFaceWithAPI(font))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
debug && console.groupEnd();
|
||||
|
||||
return await Promise.all(fontsToBeLoaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes quotes from the the string.
|
||||
*
|
||||
* When a `@font-face` is declared, the font family is sometimes
|
||||
* defined in quotes which end up included in the `FontFace` instance.
|
||||
*
|
||||
* @param {string} value
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function trim(value) {
|
||||
return value.replace(/['"]+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves with the specified fonts
|
||||
* when they are done loading or failed.
|
||||
*
|
||||
* @param {FontFaceReference|string|(FontFaceReference|string)[]} queries
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function whenReady(queries)
|
||||
{
|
||||
const fonts = getMany(queries);
|
||||
|
||||
return await Promise.all(fonts.map((font) => font.loaded));
|
||||
}
|
||||
|
||||
export {
|
||||
getAny,
|
||||
getMany,
|
||||
hasAny,
|
||||
isFontLoadingAPIAvailable,
|
||||
loadFonts,
|
||||
whenReady,
|
||||
}
|
||||
@@ -1,129 +1,118 @@
|
||||
import { isFunction } from './is';
|
||||
import { arrayContains, findByKeyValue, removeFromArray } from './array';
|
||||
import { $document, $window, $html, $body } from './environment';
|
||||
|
||||
const CALLBACKS = {
|
||||
hidden: [],
|
||||
visible: []
|
||||
};
|
||||
|
||||
const ACTIONS = [
|
||||
'addCallback',
|
||||
'removeCallback'
|
||||
];
|
||||
|
||||
const STATES = [
|
||||
'visible',
|
||||
'hidden'
|
||||
];
|
||||
|
||||
const PREFIX = 'v-';
|
||||
|
||||
let UUID = 0;
|
||||
|
||||
// Main event
|
||||
$document.on('visibilitychange', function(event) {
|
||||
if (document.hidden) {
|
||||
onDocumentChange('hidden');
|
||||
} else {
|
||||
onDocumentChange('visible');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a callback
|
||||
* @param {string} state
|
||||
* @param {function} callback
|
||||
* @return {string} ident
|
||||
* The `PageVisibility` interface provides support for dispatching
|
||||
* a custom event derived from the value of {@see document.visibilityState}
|
||||
* when the "visibilitychange" event is fired.
|
||||
*
|
||||
* The custom events are:
|
||||
*
|
||||
* - "visibilityhidden" representing the "hidden" visibility state.
|
||||
* - "visibilityvisible" representing the "visibile" visibility state.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```js
|
||||
* import pageVisibility from './utils/visibility.js';
|
||||
*
|
||||
* pageVisibility.enableCustomEvents();
|
||||
*
|
||||
* document.addEventListener('visibilityhidden', () => videoElement.pause());
|
||||
* ```
|
||||
*
|
||||
* The dispatched event object is the same from "visibilitychange"
|
||||
* and renamed according to the visibility state.
|
||||
*
|
||||
* The `PageVisibility` interface does not manage the attachment/detachment
|
||||
* of event listeners on the custom event types.
|
||||
*
|
||||
* Further reading:
|
||||
*
|
||||
* - {@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}
|
||||
*/
|
||||
function addCallback (state, options) {
|
||||
let callback = options.callback || '';
|
||||
export default new class PageVisibility {
|
||||
/**
|
||||
* Checks if the "visibilitychange" event listener has been registered.
|
||||
*
|
||||
* @return {boolean} Returns `false` if the event listener is not registered,
|
||||
* otherwise returns `true`.
|
||||
*/
|
||||
get isEnabled() {
|
||||
return isVisibilityChangeObserved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the "visibilitychange" event listener.
|
||||
*
|
||||
* @return {boolean} Returns `false` if the event listener was already unregistered,
|
||||
* otherwise returns `true`.
|
||||
*/
|
||||
disableCustomEvents() {
|
||||
if (isVisibilityChangeObserved) {
|
||||
isVisibilityChangeObserved = false;
|
||||
document.removeEventListener('visibilitychange', handleCustomVisibilityChange);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isFunction(callback)) {
|
||||
console.warn('Callback is not a function');
|
||||
return false;
|
||||
}
|
||||
|
||||
let ident = PREFIX + UUID++;
|
||||
/**
|
||||
* Registers the "visibilitychange" event listener.
|
||||
*
|
||||
* @return {boolean} Returns `false` if the event listener was already registered,
|
||||
* otherwise returns `true`.
|
||||
*/
|
||||
enableCustomEvents() {
|
||||
if (!isVisibilityChangeObserved) {
|
||||
isVisibilityChangeObserved = true;
|
||||
document.addEventListener('visibilitychange', handleCustomVisibilityChange);
|
||||
return true;
|
||||
}
|
||||
|
||||
CALLBACKS[state].push({
|
||||
ident: ident,
|
||||
callback: callback
|
||||
});
|
||||
|
||||
return ident;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a callback
|
||||
* @param {string} state Visible or hidden
|
||||
* @param {string} ident Unique identifier
|
||||
* @return {boolean} If operation was a success
|
||||
*/
|
||||
function removeCallback (state, options) {
|
||||
let ident = options.ident || '';
|
||||
|
||||
if (typeof(ident) === 'undefined' || ident === '') {
|
||||
console.warn('Need ident to remove callback');
|
||||
return false;
|
||||
}
|
||||
|
||||
let index = findByKeyValue(CALLBACKS[state], 'ident', ident)[0];
|
||||
|
||||
// console.log(ident)
|
||||
// console.log(CALLBACKS[state])
|
||||
|
||||
if (typeof(index) !== 'undefined') {
|
||||
removeFromArray(CALLBACKS[state], index);
|
||||
return true;
|
||||
} else {
|
||||
console.warn('Callback could not be found');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When document state changes, trigger callbacks
|
||||
* @param {string} state Visible or hidden
|
||||
* Tracks whether custom visibility event types
|
||||
* are available (`true`) or not (`false`).
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
function onDocumentChange (state) {
|
||||
let callbackArray = CALLBACKS[state];
|
||||
let i = 0;
|
||||
let len = callbackArray.length;
|
||||
let isVisibilityChangeObserved = false;
|
||||
|
||||
for (; i < len; i++) {
|
||||
callbackArray[i].callback();
|
||||
}
|
||||
/**
|
||||
* Dispatches a custom visibility event at the document derived
|
||||
* from the value of {@see document.visibilityState}.
|
||||
*
|
||||
* @listens document#visibilitychange
|
||||
*
|
||||
* @fires PageVisibility#visibilityhidden
|
||||
* @fires PageVisibility#visibilityvisible
|
||||
*
|
||||
* @param {Event} event
|
||||
* @return {void}
|
||||
*/
|
||||
function handleCustomVisibilityChange(event) {
|
||||
document.dispatchEvent(new CustomEvent(`visibility${document.visibilityState}`, {
|
||||
detail: {
|
||||
cause: event
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Public facing API for adding and removing callbacks
|
||||
* @param {object} options Options
|
||||
* @return {boolean|integer} Unique identifier for the callback or boolean indicating success or failure
|
||||
* The "visibilityhidden" eveent is fired at the document when the contents
|
||||
* of its tab have become hidden.
|
||||
*
|
||||
* @event PageVisibility#visibilityhidden
|
||||
* @type {Event}
|
||||
*/
|
||||
function visibilityApi (options) {
|
||||
let action = options.action || '';
|
||||
let state = options.state || '';
|
||||
let ret;
|
||||
|
||||
// Type and value checking
|
||||
if (!arrayContains(ACTIONS, action)) {
|
||||
console.warn('Action does not exist');
|
||||
return false;
|
||||
}
|
||||
if (!arrayContains(STATES, state)) {
|
||||
console.warn('State does not exist');
|
||||
return false;
|
||||
}
|
||||
|
||||
// @todo Magic call function pls
|
||||
if (action === 'addCallback') {
|
||||
ret = addCallback(state, options);
|
||||
} else if (action === 'removeCallback') {
|
||||
ret = removeCallback(state, options);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export { visibilityApi };
|
||||
/**
|
||||
* The "visibilityvisible" eveent is fired at the document when the contents
|
||||
* of its tab have become visible.
|
||||
*
|
||||
* @event PageVisibility#visibilityvisible
|
||||
* @type {Event}
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@ $input-icon-color: 424242; // No #
|
||||
.c-form_input {
|
||||
padding: rem(10px);
|
||||
border: 1px solid lightgray;
|
||||
background-color: white;
|
||||
background-color: $color-lightest;
|
||||
|
||||
&:hover {
|
||||
border-color: darkgray;
|
||||
@@ -71,7 +71,7 @@ $checkbox-icon-color: $input-icon-color;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: $white;
|
||||
background-color: $color-lightest;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: black;
|
||||
background-color: $color-darkest;
|
||||
opacity: 0.5;
|
||||
width: 7px;
|
||||
border-radius: 10px;
|
||||
|
||||
@@ -15,7 +15,7 @@ html {
|
||||
min-height: 100%; // [2]
|
||||
line-height: $line-height;
|
||||
font-family: ff("sans");
|
||||
color: $color;
|
||||
color: $font-color;
|
||||
line-height: $line-height; // [3]
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
@@ -74,9 +74,9 @@ body {
|
||||
}
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
color: $color-link;
|
||||
|
||||
@include u-hocus {
|
||||
color: $link-hover-color;
|
||||
color: $color-link-hover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ i {
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: $bold;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
// Tools
|
||||
// ==========================================================================
|
||||
|
||||
@import "tools/maths";
|
||||
@import "tools/functions";
|
||||
@import "tools/mixins";
|
||||
@import "tools/fonts";
|
||||
@import "tools/layout";
|
||||
@import "tools/widths";
|
||||
// @import "tools/layout";
|
||||
// @import "tools/widths";
|
||||
// @import "tools/family";
|
||||
|
||||
// Generic
|
||||
@@ -40,7 +41,9 @@
|
||||
@import "objects/scroll";
|
||||
@import "objects/container";
|
||||
@import "objects/ratio";
|
||||
@import "objects/layout";
|
||||
@import "objects/icons";
|
||||
@import "objects/grid";
|
||||
// @import "objects/layout";
|
||||
// @import "objects/crop";
|
||||
// @import "objects/table";
|
||||
|
||||
@@ -56,15 +59,12 @@
|
||||
@import "components/button";
|
||||
@import "components/form";
|
||||
|
||||
// Templates
|
||||
// ==========================================================================
|
||||
// @import "templates/template";
|
||||
|
||||
// Utilities
|
||||
// ==========================================================================
|
||||
|
||||
@import "utilities/ratio";
|
||||
@import "utilities/widths";
|
||||
@import "utilities/grid-column";
|
||||
// @import "utilities/widths";
|
||||
// @import "utilities/align";
|
||||
// @import "utilities/helpers";
|
||||
// @import "utilities/states";
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
.o-container {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-right: rem($padding);
|
||||
padding-left: rem($padding);
|
||||
max-width: rem($container-width + ($padding * 2));
|
||||
padding-right: $base-column-gap;
|
||||
padding-left: $base-column-gap;
|
||||
}
|
||||
|
||||
171
assets/styles/objects/_grid.scss
Normal file
171
assets/styles/objects/_grid.scss
Normal file
@@ -0,0 +1,171 @@
|
||||
// ==========================================================================
|
||||
// Grid helper
|
||||
// ==========================================================================
|
||||
// Help: https://css-tricks.com/snippets/css/complete-guide-grid/
|
||||
//
|
||||
/**
|
||||
* Usage:
|
||||
*
|
||||
* ```html
|
||||
* <div class="o-grid -col-4 -col-12@from-medium -gutters">
|
||||
* <div class="o-grid_item u-gc-1/2 u-gc-3/9@from-medium">
|
||||
* <p>Hello</p>
|
||||
* </div>
|
||||
* <div class="o-grid_item u-gc-3/4 u-gc-9/13@from-medium">
|
||||
* <p>Hello</p>
|
||||
* </div>
|
||||
* </div>
|
||||
* ```
|
||||
*/
|
||||
|
||||
.o-grid {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
|
||||
&:is(ul, ol) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Cols
|
||||
// ==========================================================================
|
||||
&.-col-#{$base-column-nb} {
|
||||
grid-template-columns: repeat(#{$base-column-nb}, 1fr);
|
||||
}
|
||||
|
||||
&.-col-4 {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
&.-col-#{$base-column-nb}\@from-medium {
|
||||
@media (min-width: $from-medium) {
|
||||
grid-template-columns: repeat(#{$base-column-nb}, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Gutters
|
||||
// ==========================================================================
|
||||
|
||||
// Gutters rows and columns
|
||||
&.-gutters {
|
||||
gap: $base-column-gap;
|
||||
column-gap: $base-column-gap;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Modifiers
|
||||
// ==========================================================================
|
||||
&.-full-height {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Aligns
|
||||
// ==========================================================================
|
||||
|
||||
// ==========================================================================
|
||||
// Items inside cells
|
||||
//
|
||||
&.-top-items {
|
||||
align-items: start;
|
||||
}
|
||||
&.-right-items {
|
||||
justify-items: end;
|
||||
}
|
||||
&.-bottom-items {
|
||||
align-items: end;
|
||||
}
|
||||
&.-left-items {
|
||||
justify-items: start;
|
||||
}
|
||||
&.-center-items {
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
&.-center-items-x {
|
||||
justify-items: center;
|
||||
}
|
||||
&.-center-items-y {
|
||||
align-items: center;
|
||||
}
|
||||
&.-stretch-items {
|
||||
align-items: stretch;
|
||||
justify-items: stretch;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Cells
|
||||
//
|
||||
&.-top-cells {
|
||||
align-content: start;
|
||||
}
|
||||
&.-right-cells {
|
||||
justify-content: end;
|
||||
}
|
||||
&.-bottom-cells {
|
||||
align-content: end;
|
||||
}
|
||||
&.-left-cells {
|
||||
justify-content: start;
|
||||
}
|
||||
&.-center-cells {
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
&.-center-cells-x {
|
||||
justify-content: center;
|
||||
}
|
||||
&.-center-cells-y {
|
||||
align-content: center;
|
||||
}
|
||||
&.-stretch-cells {
|
||||
align-content: stretch;
|
||||
justify-content: stretch;
|
||||
}
|
||||
&.-space-around-cells {
|
||||
align-content: space-around;
|
||||
justify-content: space-around;
|
||||
}
|
||||
&.-space-around-cells-x {
|
||||
justify-content: space-around;
|
||||
}
|
||||
&.-space-around-cells-y {
|
||||
align-content: space-around;
|
||||
}
|
||||
&.-space-between-cells {
|
||||
justify-content: space-between;
|
||||
align-content: space-between;
|
||||
}
|
||||
&.-space-between-cells-x {
|
||||
justify-content: space-between;
|
||||
}
|
||||
&.-space-between-cells-y {
|
||||
align-content: space-between;
|
||||
}
|
||||
&.-space-evenly-cells {
|
||||
justify-content: space-evenly;
|
||||
align-content: space-evenly;
|
||||
}
|
||||
&.-space-evenly-cells-x {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
&.-space-evenly-cells-y {
|
||||
align-content: space-evenly;
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Grid item
|
||||
// ==========================================================================
|
||||
// By default, a grid item takes full width of its parent.
|
||||
//
|
||||
.o-grid_item {
|
||||
grid-column: 1 / -1;
|
||||
|
||||
&.-align-end {
|
||||
align-self: end;
|
||||
}
|
||||
}
|
||||
58
assets/styles/objects/_icons.scss
Normal file
58
assets/styles/objects/_icons.scss
Normal file
@@ -0,0 +1,58 @@
|
||||
// ==========================================================================
|
||||
// Objects / SVG Icons
|
||||
// ==========================================================================
|
||||
|
||||
|
||||
// Markup
|
||||
//
|
||||
// 1. If icon is accessible and has a title
|
||||
// 2. If icon is decorative
|
||||
//
|
||||
// <i class="o-icon ${modifier}">
|
||||
// <svg
|
||||
// class="svg-${icon-name}"
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// role="img" [1]
|
||||
// aria-hidden="true" [2]
|
||||
// focusable="false" [2]
|
||||
// aria-labelledby="${id}" [1]
|
||||
// >
|
||||
// <title id="${id}"> [1]
|
||||
// Locomotive
|
||||
// </title>
|
||||
// <use xlink:href="assets/images/sprite.svg#${icon-name}" xmlns:xlink="http://www.w3.org/1999/xlink"/>
|
||||
// </svg>
|
||||
// </i>
|
||||
|
||||
// Global styles for icones
|
||||
// ==========================================================================
|
||||
|
||||
.o-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
svg {
|
||||
--icon-height: calc(var(--icon-width) * (1 / (var(--icon-ratio))));
|
||||
|
||||
display: block;
|
||||
width: var(--icon-width);
|
||||
height: var(--icon-height);
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SVG sizes
|
||||
// ==========================================================================
|
||||
|
||||
// // Logo
|
||||
// .svg-logo {
|
||||
// --icon-width: #{rem(100px)};
|
||||
// --icon-ratio: 20/30; // width/height based on svg viewBox
|
||||
|
||||
// // Sizes
|
||||
// .o-icon.-big & {
|
||||
// --icon-width: #{rem(200px)};
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -5,25 +5,25 @@
|
||||
// Palette
|
||||
// =============================================================================
|
||||
|
||||
$white: #FFFFFF;
|
||||
$black: #000000;
|
||||
$color-lightest: #FFFFFF;
|
||||
$color-darkest: #000000;
|
||||
|
||||
// Specific
|
||||
// =============================================================================
|
||||
|
||||
// Link
|
||||
$link-color: #1A0DAB;
|
||||
$link-focus-color: #1A0DAB;
|
||||
$link-hover-color: darken(#1A0DAB, 10%);
|
||||
$color-link: #1A0DAB;
|
||||
$color-link-focus: #1A0DAB;
|
||||
$color-link-hover: darken(#1A0DAB, 10%);
|
||||
|
||||
// Selection
|
||||
$selection-text-color: #3297FD;
|
||||
$selection-background-color: #FFFFFF;
|
||||
$selection-text-color: #3297FD;
|
||||
$selection-background-color: #FFFFFF;
|
||||
|
||||
// Social Colors
|
||||
// =============================================================================
|
||||
|
||||
$facebook-color: #3B5998;
|
||||
$instagram-color: #E1306C;
|
||||
$youtube-color: #CD201F;
|
||||
$twitter-color: #1DA1F2;
|
||||
$color-facebook: #3B5998;
|
||||
$color-instagram: #E1306C;
|
||||
$color-youtube: #CD201F;
|
||||
$color-twitter: #1DA1F2;
|
||||
|
||||
@@ -2,22 +2,47 @@
|
||||
// Settings / Config / Eases
|
||||
// ==========================================================================
|
||||
|
||||
$Power1EaseOut: cubic-bezier(0.250, 0.460, 0.450, 0.940);
|
||||
$Power2EaseOut: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
$Power3EaseOut: cubic-bezier(0.165, 0.840, 0.440, 1.000);
|
||||
$Power4EaseOut: cubic-bezier(0.230, 1.000, 0.320, 1.000);
|
||||
$Power1EaseIn: cubic-bezier(0.550, 0.085, 0.680, 0.530) ;
|
||||
$Power2EaseIn: cubic-bezier(0.550, 0.055, 0.675, 0.190);
|
||||
$Power3EaseIn: cubic-bezier(0.895, 0.030, 0.685, 0.220);
|
||||
$Power4EaseIn: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
$ExpoEaseOut: cubic-bezier(0.190, 1.000, 0.220, 1.000);
|
||||
$ExpoEaseIn: cubic-bezier(0.950, 0.050, 0.795, 0.035);
|
||||
$ExpoEaseInOut: cubic-bezier(1.000, 0.000, 0.000, 1.000);
|
||||
$SineEaseOut: cubic-bezier(0.390, 0.575, 0.565, 1.000);
|
||||
$SineEaseIn: cubic-bezier(0.470, 0.000, 0.745, 0.715);
|
||||
$Power1EaseInOut: cubic-bezier(0.455, 0.030, 0.515, 0.955);
|
||||
$Power2EaseInOut: cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
$Power3EaseInOut: cubic-bezier(0.770, 0.000, 0.175, 1.000);
|
||||
$Power4EaseInOut: cubic-bezier(0.860, 0.000, 0.070, 1.000);
|
||||
$SlowEaseOut: cubic-bezier(.04,1.15,0.4,.99);
|
||||
$bounce: cubic-bezier(0.17, 0.67, 0.3, 1.33);
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -14,26 +14,42 @@ $assets-path: "../" !default;
|
||||
// Typefaces
|
||||
// =============================================================================
|
||||
|
||||
// Font directory
|
||||
$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;
|
||||
|
||||
// Map of font families.
|
||||
//
|
||||
// ```
|
||||
// <font-id>: (<font-name>, <font-fallbacks>)
|
||||
// ```
|
||||
$font-families: (
|
||||
"sans": ("Webfont Sans", "Helvetica Neue", Arial, sans-serif),
|
||||
// "serif": ("Webfont Serif", Georgia, serif)
|
||||
sans: join(Source Sans, $font-fallback-sans, $separator: comma),
|
||||
);
|
||||
|
||||
// List of custom font faces as tuples.
|
||||
//
|
||||
// ```
|
||||
// <font-name> <font-file-basename> <font-weight> <font-style>
|
||||
// ```
|
||||
$font-faces: (
|
||||
// "Webfont Sans" "webfont-sans_regular" 400 normal,
|
||||
// "Webfont Sans" "webfont-sans_regular-italic" 400 italic,
|
||||
// "Webfont Serif" "webfont-sans_bold" 700 normal,
|
||||
(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
|
||||
// =============================================================================
|
||||
|
||||
// Base
|
||||
$font-size: 16px;
|
||||
$line-height: 24px / $font-size;
|
||||
$color: #222222;
|
||||
$font-size: 16px;
|
||||
$line-height: 24px / $font-size;
|
||||
$font-color: $color-darkest;
|
||||
|
||||
// Headings
|
||||
$font-size-h1: 36px !default;
|
||||
@@ -45,29 +61,31 @@ $font-size-h6: 16px !default;
|
||||
$line-height-h: $line-height;
|
||||
|
||||
// Weights
|
||||
$light: 300;
|
||||
$normal: 400;
|
||||
$medium: 500;
|
||||
$bold: 700;
|
||||
$font-weight-light: 300;
|
||||
$font-weight-normal: 400;
|
||||
$font-weight-medium: 500;
|
||||
$font-weight-bold: 700;
|
||||
|
||||
// Transitions
|
||||
// =============================================================================
|
||||
|
||||
$speed: 0.3s;
|
||||
$easing: $Power2EaseOut;
|
||||
$speed: 0.3s;
|
||||
$easing: $ease-power2-out;
|
||||
|
||||
// Spacing Units
|
||||
// =============================================================================
|
||||
|
||||
$unit: 60px;
|
||||
$unit-small: 30px;
|
||||
$unit-small: 20px;
|
||||
|
||||
// Container
|
||||
// =============================================================================
|
||||
|
||||
$container-width: 2000px;
|
||||
// ==========================================================================
|
||||
$padding: $unit;
|
||||
|
||||
// Grid
|
||||
// ==========================================================================
|
||||
$base-column-nb: 12;
|
||||
$base-column-gap: $unit-small;
|
||||
|
||||
// Breakpoints
|
||||
// =============================================================================
|
||||
|
||||
@@ -89,3 +107,12 @@ $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
|
||||
);
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
// Tools / Font Faces
|
||||
// ==========================================================================
|
||||
|
||||
// Import the webfont with font-face as woff and woff2
|
||||
// Imports the custom font.
|
||||
//
|
||||
// @param {List} $webfont (font name, filename, font-weight, font-style) - Each webfont to import.
|
||||
// @param {String} $dir - The webfont directory path
|
||||
// @output void
|
||||
// The mixin expects font files to be woff and woff2.
|
||||
//
|
||||
// @param {List} $webfont - A custom font to import, as a tuple:
|
||||
// `<font-name> <font-file-basename> <font-weight> <font-style>`.
|
||||
// @param {String} $dir - The webfont directory path.
|
||||
// @output The `@font-face` at-rule specifying the custom font.
|
||||
|
||||
@mixin font-face($webfont, $dir) {
|
||||
@font-face {
|
||||
@@ -19,27 +22,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Loops through a list of local fonts and import each font-face as woff and woff2
|
||||
// Imports the list of custom fonts.
|
||||
//
|
||||
// @param {List} $webfonts [(font name, filename, font-weight, font-style)] - Each webfont to import.
|
||||
// @param {String} $dir - The webfont directory path
|
||||
// @output void
|
||||
// @require {mixin} font-face
|
||||
//
|
||||
// @param {List<List>} $webfonts - List of custom fonts to import.
|
||||
// See `font-face` mixin for details.
|
||||
// @param {String} $dir - The webfont directory path.
|
||||
// @output The `@font-face` at-rules specifying the custom fonts.
|
||||
|
||||
@mixin font-faces($webfonts, $dir) {
|
||||
@each $webfont in $webfonts {
|
||||
@include font-face($webfont, $dir);
|
||||
@if (length($webfonts) > 0) {
|
||||
@if (type-of(nth($webfonts, 1)) == "list") {
|
||||
@each $webfont in $webfonts {
|
||||
@include font-face($webfont, $dir);
|
||||
}
|
||||
} @else {
|
||||
@include font-face($webfonts, $dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map the font-family requires with the existing imported font-families
|
||||
// Retrieves the font family stack for the given font ID.
|
||||
//
|
||||
// @param {String} $font-family - The name of the webfont.
|
||||
// @return {String} The webfont and it's fallbacks.
|
||||
// @require {variable} $font-families - See settings directory.
|
||||
//
|
||||
// @param {String} $font-family - The custom font ID.
|
||||
// @throws Error if the $font-family does not exist.
|
||||
// @return {List} The font stack.
|
||||
|
||||
@function ff($font-family) {
|
||||
@if not map-has-key($font-families, $font-family) {
|
||||
@error "No font-family found in $font-families map for `#{$font-family}`. Property omitted.";
|
||||
}
|
||||
@error "No font-family found in $font-families map for `#{$font-family}`.";
|
||||
}
|
||||
|
||||
$value: map-get($font-families, $font-family);
|
||||
@return $value;
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
// Tools / Functions
|
||||
// ==========================================================================
|
||||
|
||||
// Check if the given value is a number in pixel
|
||||
//
|
||||
// @param {Number} $number - The value to check
|
||||
// @return {Boolean}
|
||||
|
||||
@function is-pixel-number($number) {
|
||||
@return type-of($number) == number and unit($number) == "px";
|
||||
}
|
||||
|
||||
// Converts the given pixel value to its EM quivalent.
|
||||
//
|
||||
// @param {Number} $size - The pixel value to convert.
|
||||
@@ -9,20 +18,12 @@
|
||||
// @return {Number} Scalable pixel value in EMs.
|
||||
|
||||
@function em($size, $base: $font-size) {
|
||||
@if (type-of($size) == number) {
|
||||
@if (unit($size) != "px") {
|
||||
@error "`#{$size}` needs to be a pixel value.";
|
||||
}
|
||||
} @else {
|
||||
@error "`#{$size}` needs to be a number.";
|
||||
@if not is-pixel-number($size) {
|
||||
@error "`#{$size}` needs to be a number in pixel.";
|
||||
}
|
||||
|
||||
@if (type-of($base) == number) {
|
||||
@if (unit($base) != "px") {
|
||||
@error "`#{$base}` needs to be a pixel value.";
|
||||
}
|
||||
} @else {
|
||||
@error "`#{$base}` needs to be a number.";
|
||||
@if not is-pixel-number($base) {
|
||||
@error "`#{$base}` needs to be a number in pixel.";
|
||||
}
|
||||
|
||||
@return ($size / $base) * 1em;
|
||||
@@ -35,25 +36,43 @@
|
||||
// @return {Number} Scalable pixel value in REMs.
|
||||
|
||||
@function rem($size, $base: $font-size) {
|
||||
@if (type-of($size) == number) {
|
||||
@if (unit($size) != "px") {
|
||||
@error "`#{$size}` needs to be a pixel value.";
|
||||
}
|
||||
} @else {
|
||||
@error "`#{$size}` needs to be a number.";
|
||||
|
||||
@if not is-pixel-number($size) {
|
||||
@error "`#{$size}` needs to be a number in pixel.";
|
||||
}
|
||||
|
||||
@if (type-of($base) == number) {
|
||||
@if (unit($base) != "px") {
|
||||
@error "`#{$base}` needs to be a pixel value.";
|
||||
}
|
||||
} @else {
|
||||
@error "`#{$base}` needs to be a number.";
|
||||
@if not is-pixel-number($base) {
|
||||
@error "`#{$base}` needs to be a number in pixel.";
|
||||
}
|
||||
|
||||
@return ($size / $base) * 1rem;
|
||||
}
|
||||
|
||||
// Retrieves the z-index from the {@see $layers master list}.
|
||||
//
|
||||
// @link on http://css-tricks.com/handling-z-index/
|
||||
//
|
||||
// @param {string} $layer The name of the z-index.
|
||||
// @param {number} $modifier A positive or negative modifier to apply
|
||||
// to the returned z-index value.
|
||||
// @throw Error if the $layer does not exist.
|
||||
// @throw Warning if the $modifier might overlap another master z-index.
|
||||
// @return {number} The computed z-index of $layer and $modifier.
|
||||
|
||||
@function z($layer, $modifier: 0) {
|
||||
@if not map-has-key($z-indexes, $layer) {
|
||||
@error "Unknown master z-index layer: #{$layer}";
|
||||
}
|
||||
|
||||
@if ($modifier >= 50 or $modifier <= -50) {
|
||||
@warn "Modifier may overlap the another master z-index layer: #{$modifier}";
|
||||
}
|
||||
|
||||
$index: map-get($z-indexes, $layer);
|
||||
|
||||
@return $index + $modifier;
|
||||
}
|
||||
|
||||
// Converts a number to a percentage.
|
||||
//
|
||||
// @alias percentage()
|
||||
@@ -94,7 +113,7 @@
|
||||
@function important($flag: false) {
|
||||
@if ($flag == true) {
|
||||
@return !important;
|
||||
} @elseif ($important == false) {
|
||||
} @else if ($important == false) {
|
||||
@return null;
|
||||
} @else {
|
||||
@error "`#{$flag}` needs to be `true` or `false`.";
|
||||
|
||||
136
assets/styles/tools/_maths.scss
Normal file
136
assets/styles/tools/_maths.scss
Normal file
@@ -0,0 +1,136 @@
|
||||
// ==========================================================================
|
||||
// Tools / Maths
|
||||
// ==========================================================================
|
||||
|
||||
// Removes the unit from the given number.
|
||||
//
|
||||
// @param {number} $number The number to strip.
|
||||
// @return {number}
|
||||
|
||||
@function strip-units($number) {
|
||||
@return $number / ($number * 0 + 1);
|
||||
}
|
||||
|
||||
// Returns the square root of the given number.
|
||||
//
|
||||
// @param {number} $number The number to calculate.
|
||||
// @return {number}
|
||||
|
||||
@function sqrt($number) {
|
||||
$x: 1;
|
||||
$value: $x;
|
||||
|
||||
@for $i from 1 through 10 {
|
||||
$value: $x - ($x * $x - abs($number)) / (2 * $x);
|
||||
$x: $value;
|
||||
}
|
||||
|
||||
@return $value;
|
||||
}
|
||||
|
||||
// Returns a number raised to the power of an exponent.
|
||||
//
|
||||
// @param {number} $number The base number.
|
||||
// @param {number} $exp The exponent.
|
||||
// @return {number}
|
||||
|
||||
@function pow($number, $exp) {
|
||||
$value: 1;
|
||||
|
||||
@if $exp > 0 {
|
||||
@for $i from 1 through $exp {
|
||||
$value: $value * $number;
|
||||
}
|
||||
} @else if $exp < 0 {
|
||||
@for $i from 1 through -$exp {
|
||||
$value: $value / $number;
|
||||
}
|
||||
}
|
||||
|
||||
@return $value;
|
||||
}
|
||||
|
||||
// Returns the factorial of the given number.
|
||||
//
|
||||
// @param {number} $number The number to calculate.
|
||||
// @return {number}
|
||||
|
||||
@function fact($number) {
|
||||
$value: 1;
|
||||
|
||||
@if $number > 0 {
|
||||
@for $i from 1 through $number {
|
||||
$value: $value * $i;
|
||||
}
|
||||
}
|
||||
|
||||
@return $value;
|
||||
}
|
||||
|
||||
// Returns an approximation of pi, with 11 decimals.
|
||||
//
|
||||
// @return {number}
|
||||
|
||||
@function pi() {
|
||||
@return 3.14159265359;
|
||||
}
|
||||
|
||||
// Converts the number in degrees to the radian equivalent .
|
||||
//
|
||||
// @param {number} $angle The angular value to calculate.
|
||||
// @return {number} If $angle has the `deg` unit,
|
||||
// the radian equivalent is returned.
|
||||
// Otherwise, the unitless value of $angle is returned.
|
||||
|
||||
@function rad($angle) {
|
||||
$unit: unit($angle);
|
||||
$angle: strip-units($angle);
|
||||
|
||||
// If the angle has `deg` as unit, convert to radians.
|
||||
@if ($unit == deg) {
|
||||
@return $angle / 180 * pi();
|
||||
}
|
||||
|
||||
@return $angle;
|
||||
}
|
||||
|
||||
// Returns the sine of the given number.
|
||||
//
|
||||
// @param {number} $angle The angle to calculate.
|
||||
// @return {number}
|
||||
|
||||
@function sin($angle) {
|
||||
$sin: 0;
|
||||
$angle: rad($angle);
|
||||
|
||||
@for $i from 0 through 10 {
|
||||
$sin: $sin + pow(-1, $i) * pow($angle, (2 * $i + 1)) / fact(2 * $i + 1);
|
||||
}
|
||||
|
||||
@return $sin;
|
||||
}
|
||||
|
||||
// Returns the cosine of the given number.
|
||||
//
|
||||
// @param {string} $angle The angle to calculate.
|
||||
// @return {number}
|
||||
|
||||
@function cos($angle) {
|
||||
$cos: 0;
|
||||
$angle: rad($angle);
|
||||
|
||||
@for $i from 0 through 10 {
|
||||
$cos: $cos + pow(-1, $i) * pow($angle, 2 * $i) / fact(2 * $i);
|
||||
}
|
||||
|
||||
@return $cos;
|
||||
}
|
||||
|
||||
// Returns the tangent of the given number.
|
||||
//
|
||||
// @param {string} $angle The angle to calculate.
|
||||
// @return {number}
|
||||
|
||||
@function tan($angle) {
|
||||
@return sin($angle) / cos($angle);
|
||||
}
|
||||
63
assets/styles/utilities/_grid-column.scss
Normal file
63
assets/styles/utilities/_grid-column.scss
Normal file
@@ -0,0 +1,63 @@
|
||||
// ==========================================================================
|
||||
// Tools / Grid Columns
|
||||
// ==========================================================================
|
||||
|
||||
//
|
||||
// Grid layout system.
|
||||
//
|
||||
// This tool generates columns for all needed media queries.
|
||||
// Unused classes will be purge by the css post-processor.
|
||||
//
|
||||
|
||||
$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"
|
||||
) !default;
|
||||
|
||||
@each $breakpoint-namespace, $breakpoint in $breakpoints {
|
||||
@for $fromIndex from 1 through $colsMax {
|
||||
@for $toIndex from 1 through $colsMax {
|
||||
@if $breakpoint == null {
|
||||
.u-gc-#{$fromIndex}\/#{$toIndex} {
|
||||
grid-column-start: #{$fromIndex};
|
||||
grid-column-end: #{$toIndex};
|
||||
}
|
||||
} @else {
|
||||
.u-gc-#{$fromIndex}\/#{$toIndex}\@#{$breakpoint} {
|
||||
@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};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { writeFile } from 'node:fs/promises';
|
||||
import { basename } from 'node:path';
|
||||
import { promisify } from 'node:util';
|
||||
import sass from 'node-sass';
|
||||
import { PurgeCSS } from 'purgecss';
|
||||
|
||||
const sassRender = promisify(sass.render);
|
||||
|
||||
@@ -142,7 +143,12 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
|
||||
}
|
||||
|
||||
try {
|
||||
await writeFile(outfile, result.css);
|
||||
await writeFile(outfile, result.css).then(() => {
|
||||
// Purge CSS once file exists.
|
||||
if (outfile) {
|
||||
purgeUnusedCSS(outfile, `${label || `${filestem}.css`}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (result.css) {
|
||||
message(`${label || `${filestem}.css`} compiled`, 'success', timeLabel);
|
||||
@@ -212,3 +218,37 @@ function createPostCSSProcessor(pluginsListOrMap, options)
|
||||
|
||||
return postcss(plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge unused styles from CSS files.
|
||||
*
|
||||
* @async
|
||||
*
|
||||
* @param {string} outfile - The path of a css file
|
||||
* If missing the function stops.
|
||||
* @param {string} label - The CSS file label or name.
|
||||
* @return {Promise}
|
||||
*/
|
||||
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({
|
||||
content: purgeCSSContentFiles,
|
||||
css: [ outfile ],
|
||||
rejected: true,
|
||||
defaultExtractor: content => content.match(/[a-z0-9_\-\\\/\@]+/gi) || [],
|
||||
safelist: {
|
||||
standard: [ /^((?!\bu-gc-).)*$/ ]
|
||||
}
|
||||
})
|
||||
|
||||
for(let result of purgeCSSResults) {
|
||||
await writeFile(outfile, result.css)
|
||||
|
||||
message(`${label} purged`, 'chore', timeLabel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ export default function message(text, type, timerID) {
|
||||
console.log('✅ ', kleur.bgGreen().black(text));
|
||||
break;
|
||||
|
||||
case 'chore':
|
||||
console.log('🧹 ', kleur.bgGreen().black(text));
|
||||
break;
|
||||
|
||||
case 'notice':
|
||||
console.log('ℹ️ ', kleur.bgBlue().black(text));
|
||||
break;
|
||||
|
||||
109
docs/grid.md
Normal file
109
docs/grid.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Grid system
|
||||
|
||||
* [Architectures](#architecture)
|
||||
* [Build tasks](#build-tasks)
|
||||
* [Configuration](#configuration)
|
||||
* [Usage](#usage)
|
||||
* [Example](#example)
|
||||
|
||||
## Architecture
|
||||
|
||||
The boilerplate's grid system is meant to be simple and easy to use. The goal is to create a light, flexible, and reusable way to build layouts.
|
||||
The following styles are needed to work properly:
|
||||
|
||||
* [`o-grid`](../assets/styles/objects/_grid.scss) — Object file where the default grid styles are set such as column numbers, modifiers, and options.
|
||||
* [`u-grid-columns`](../assets/styles/utilities/_grid-column.scss) — Utility file that generates the styles for every possible column based on an array of media queries and column numbers.
|
||||
|
||||
### Build tasks
|
||||
|
||||
The columns generated by [`u-grid-columns`](../assets/styles/utilities/_grid-column.scss) adds a lot of styles to the compiled CSS file. To mitigate that, [PurgeCSS] is integrated into the `styles` build task to purge unused CSS.
|
||||
|
||||
#### Configuration
|
||||
|
||||
Depending on your project, you will need to specify all the files that include CSS classes from the grid system. These files will be scanned by [PurgeCSS] to your compiled CSS files.
|
||||
|
||||
Example of a Charcoal project:
|
||||
|
||||
```jsonc
|
||||
"purgeCSS": {
|
||||
"content": [
|
||||
"./views/app/template/**/*.mustache",
|
||||
"./src/App/Template/*.php",
|
||||
"./assets/scripts/**/*" // use case: `el.classList.add('u-gc-1/2')`
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The first step is to set intial SCSS values in the following files :
|
||||
|
||||
- [`settings/_config.scss`](../assets/styles/settings/_config.scss)
|
||||
|
||||
```scss
|
||||
// Grid
|
||||
// ==========================================================================
|
||||
$base-column-nb: 12;
|
||||
$base-column-gap: $unit-small;
|
||||
```
|
||||
|
||||
You can create multiple column layouts depending on media queries.
|
||||
|
||||
- [`objects/_grid.scss`](../assets/styles/objects/_grid.scss)
|
||||
|
||||
```scss
|
||||
.o-grid {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
// ==========================================================================
|
||||
// Cols
|
||||
// ==========================================================================
|
||||
&.-col-#{$base-column-nb} {
|
||||
grid-template-columns: repeat(#{$base-column-nb}, 1fr);
|
||||
}
|
||||
|
||||
&.-col-4 {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
&.-col-#{$base-column-nb}\@from-medium {
|
||||
@media (min-width: $from-medium) {
|
||||
grid-template-columns: repeat(#{$base-column-nb}, 1fr);
|
||||
}
|
||||
}
|
||||
// …
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
The following layout has 4 columns at `>=999px` and 12 columns at `<1000px`.
|
||||
|
||||
```html
|
||||
<div class="o-container">
|
||||
<h1 class="c-heading -h1">Hello</h1>
|
||||
|
||||
<div class="o-grid -col-4 -col-12@from-medium -gutters">
|
||||
<div class="o-grid_item u-gc-1/8@from-medium">
|
||||
<h2 class="c-heading -h2">This grid has 4 columns and 12 columns from `medium` MQ</h2>
|
||||
</div>
|
||||
|
||||
<div class="o-grid_item u-gc-1/5@from-medium">
|
||||
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Expedita provident distinctio deleniti eaque cumque doloremque aut quo dicta porro commodi, temporibus totam dolor autem tempore quasi ullam sed suscipit vero?</p>
|
||||
</div>
|
||||
|
||||
<div class="o-grid_item u-gc-5/9@from-medium">
|
||||
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Expedita provident distinctio deleniti eaque cumque doloremque aut quo dicta porro commodi, temporibus totam dolor autem tempore quasi ullam sed suscipit vero?</p>
|
||||
</div>
|
||||
|
||||
<div class="o-grid_item u-gc-9/13@from-medium">
|
||||
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Expedita provident distinctio deleniti eaque cumque doloremque aut quo dicta porro commodi, temporibus totam dolor autem tempore quasi ullam sed suscipit vero?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
[PurgeCSS]: https://purgecss.com/
|
||||
@@ -19,7 +19,7 @@
|
||||
"dest": "./www/assets/images"
|
||||
},
|
||||
"views": {
|
||||
"src": "./views/boilerplate/template"
|
||||
"src": "./www/"
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
@@ -56,6 +56,12 @@
|
||||
],
|
||||
"outfile": "{% paths.svgs.dest %}/sprite.svg"
|
||||
}
|
||||
]
|
||||
],
|
||||
"purgeCSS": {
|
||||
"content": [
|
||||
"./www/**/*.html",
|
||||
"./assets/scripts/**/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1102
package-lock.json
generated
1102
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -21,14 +21,15 @@
|
||||
"svg4everybody": "^2.1.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.4",
|
||||
"browser-sync": "^2.27.9",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"browser-sync": "^2.27.10",
|
||||
"concat": "^1.0.3",
|
||||
"esbuild": "^0.14.27",
|
||||
"kleur": "^4.1.4",
|
||||
"esbuild": "^0.14.54",
|
||||
"kleur": "^4.1.5",
|
||||
"node-notifier": "^10.0.1",
|
||||
"node-sass": "^7.0.1",
|
||||
"postcss": "^8.4.12",
|
||||
"postcss": "^8.4.17",
|
||||
"purgecss": "^5.0.0",
|
||||
"svg-mixer": "^2.3.14",
|
||||
"tiny-glob": "^0.2.9"
|
||||
}
|
||||
|
||||
BIN
www/assets/fonts/SourceSans3-Bold.woff2
Normal file
BIN
www/assets/fonts/SourceSans3-Bold.woff2
Normal file
Binary file not shown.
BIN
www/assets/fonts/SourceSans3-BoldIt.woff2
Normal file
BIN
www/assets/fonts/SourceSans3-BoldIt.woff2
Normal file
Binary file not shown.
BIN
www/assets/fonts/SourceSans3-Regular.woff2
Normal file
BIN
www/assets/fonts/SourceSans3-Regular.woff2
Normal file
Binary file not shown.
BIN
www/assets/fonts/SourceSans3-RegularIt.woff2
Normal file
BIN
www/assets/fonts/SourceSans3-RegularIt.woff2
Normal file
Binary file not shown.
@@ -609,10 +609,141 @@
|
||||
// assets/scripts/modules.js
|
||||
var modules_exports = {};
|
||||
__export(modules_exports, {
|
||||
Example: () => Example_default,
|
||||
Load: () => Load_default,
|
||||
Scroll: () => Scroll_default
|
||||
});
|
||||
|
||||
// assets/scripts/utils/fonts.js
|
||||
var isFontLoadingAPIAvailable = "fonts" in document;
|
||||
function conformsToReference(font, criterion2) {
|
||||
for (const [key, value] of Object.entries(criterion2)) {
|
||||
switch (key) {
|
||||
case "family": {
|
||||
if (trim(font[key]) !== value) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "weight": {
|
||||
if (font[key] != value) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (font[key] !== value) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function conformsToShorthand(font, criterion2) {
|
||||
const family = trim(font.family);
|
||||
if (trim(family) === criterion2) {
|
||||
return true;
|
||||
}
|
||||
if (criterion2.endsWith(trim(family)) && (criterion2.match(font.weight) || criterion2.match(font.style))) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function findManyByReference(search) {
|
||||
const found = [];
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToReference(font, search)) {
|
||||
found.push(font);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
function findManyByShorthand(search) {
|
||||
const found = [];
|
||||
for (const font of document.fonts) {
|
||||
if (conformsToShorthand(font, search)) {
|
||||
found.push(font);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
function getMany(queries) {
|
||||
if (!Array.isArray(queries)) {
|
||||
queries = [queries];
|
||||
}
|
||||
const found = /* @__PURE__ */ new Set();
|
||||
queries.forEach((search) => {
|
||||
if (search) {
|
||||
switch (typeof search) {
|
||||
case "string":
|
||||
found.add(...findManyByShorthand(search));
|
||||
return;
|
||||
case "object":
|
||||
found.add(...findManyByReference(search));
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new TypeError("Expected font query to be font shorthand or font reference");
|
||||
});
|
||||
return [...found];
|
||||
}
|
||||
function loadFonts(fontsToLoad, debug = false) {
|
||||
return __async(this, null, function* () {
|
||||
var _a;
|
||||
if (((_a = fontsToLoad.size) != null ? _a : fontsToLoad.length) === 0) {
|
||||
throw new TypeError("Expected at least one font");
|
||||
}
|
||||
return yield loadFontsWithAPI([...fontsToLoad], debug);
|
||||
});
|
||||
}
|
||||
function loadFontFaceWithAPI(font) {
|
||||
return __async(this, null, function* () {
|
||||
return yield (font.status === "unloaded" ? font.load() : font.loaded).then((font2) => font2, (err) => font);
|
||||
});
|
||||
}
|
||||
function loadFontsWithAPI(fontsToLoad, debug = false) {
|
||||
return __async(this, null, function* () {
|
||||
debug && console.group("[loadFonts:API]", fontsToLoad.length, "/", document.fonts.size);
|
||||
const fontsToBeLoaded = [];
|
||||
for (const fontToLoad of fontsToLoad) {
|
||||
if (fontToLoad instanceof FontFace) {
|
||||
if (!document.fonts.has(fontToLoad)) {
|
||||
document.fonts.add(fontToLoad);
|
||||
}
|
||||
fontsToBeLoaded.push(loadFontFaceWithAPI(fontToLoad));
|
||||
} else {
|
||||
fontsToBeLoaded.push(...getMany(fontToLoad).map((font) => loadFontFaceWithAPI(font)));
|
||||
}
|
||||
}
|
||||
debug && console.groupEnd();
|
||||
return yield Promise.all(fontsToBeLoaded);
|
||||
});
|
||||
}
|
||||
function trim(value) {
|
||||
return value.replace(/['"]+/g, "");
|
||||
}
|
||||
function whenReady(queries) {
|
||||
return __async(this, null, function* () {
|
||||
const fonts = getMany(queries);
|
||||
return yield Promise.all(fonts.map((font) => font.loaded));
|
||||
});
|
||||
}
|
||||
|
||||
// assets/scripts/modules/Example.js
|
||||
var Example_default = class extends _default {
|
||||
constructor(m) {
|
||||
super(m);
|
||||
}
|
||||
init() {
|
||||
whenReady(EAGER_FONTS).then((fonts) => this.onFontsLoaded(fonts));
|
||||
}
|
||||
onFontsLoaded(fonts) {
|
||||
console.log("Example: Eager Fonts Loaded!", fonts);
|
||||
}
|
||||
};
|
||||
|
||||
// node_modules/modularload/dist/main.esm.js
|
||||
function _classCallCheck2(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
@@ -3694,12 +3825,21 @@
|
||||
console.warn('The "main-css" stylesheet not found');
|
||||
}
|
||||
};
|
||||
var EAGER_FONTS = [
|
||||
{ family: "Source Sans", style: "normal", weight: 400 },
|
||||
{ family: "Source Sans", style: "normal", weight: 700 }
|
||||
];
|
||||
function init() {
|
||||
globals_default();
|
||||
app.init(app);
|
||||
html.classList.add(config_default.CSS_CLASS.LOADED);
|
||||
html.classList.add(config_default.CSS_CLASS.READY);
|
||||
html.classList.remove(config_default.CSS_CLASS.LOADING);
|
||||
if (isFontLoadingAPIAvailable) {
|
||||
loadFonts(EAGER_FONTS).then((eagerFonts) => {
|
||||
html.classList.add("fonts-loaded");
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
/*
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -516,11 +516,43 @@ button:focus, button:hover,
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Source Sans;
|
||||
src: url("../fonts/SourceSans3-Bold.woff2") format("woff2"), url("../fonts/SourceSans3-Bold.woff") format("woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Source Sans;
|
||||
src: url("../fonts/SourceSans3-BoldIt.woff2") format("woff2"), url("../fonts/SourceSans3-BoldIt.woff") format("woff");
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Source Sans;
|
||||
src: url("../fonts/SourceSans3-Regular.woff2") format("woff2"), url("../fonts/SourceSans3-Regular.woff") format("woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Source Sans;
|
||||
src: url("../fonts/SourceSans3-RegularIt.woff2") format("woff2"), url("../fonts/SourceSans3-RegularIt.woff") format("woff");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
line-height: 1.5;
|
||||
font-family: "Webfont Sans", "Helvetica Neue", Arial, sans-serif;
|
||||
color: #222222;
|
||||
font-family: Source, Sans, -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
|
||||
color: #000000;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
@@ -614,9 +646,8 @@ a:focus, a:hover {
|
||||
.o-container {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-right: 3.75rem;
|
||||
padding-left: 3.75rem;
|
||||
max-width: 132.5rem;
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.o-ratio {
|
||||
@@ -644,90 +675,182 @@ a:focus, a:hover {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.o-layout {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
font-size: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.o-layout.-gutter {
|
||||
margin-left: -3.75rem;
|
||||
}
|
||||
|
||||
.o-layout.-gutter-small {
|
||||
margin-left: -1.875rem;
|
||||
}
|
||||
|
||||
.o-layout.-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.o-layout.-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.o-layout.-reverse {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.o-layout.-reverse.-flex {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.o-layout.-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.o-layout.-flex.-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.o-layout.-flex.-middle {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.o-layout.-flex.-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.o-layout.-stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.o-layout_item {
|
||||
.o-icon {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: top;
|
||||
font-size: 1rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.o-layout.-gutter > .o-layout_item {
|
||||
padding-left: 3.75rem;
|
||||
}
|
||||
|
||||
.o-layout.-gutter-small > .o-layout_item {
|
||||
padding-left: 1.875rem;
|
||||
}
|
||||
|
||||
.o-layout.-middle > .o-layout_item {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.o-layout.-bottom > .o-layout_item {
|
||||
vertical-align: bottom;
|
||||
.o-icon svg {
|
||||
--icon-height: calc(var(--icon-width) * (1 / (var(--icon-ratio))));
|
||||
display: block;
|
||||
width: var(--icon-width);
|
||||
height: var(--icon-height);
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.o-layout.-center > .o-layout_item,
|
||||
.o-layout.-right > .o-layout_item,
|
||||
.o-layout.-reverse > .o-layout_item {
|
||||
text-align: left;
|
||||
/**
|
||||
* Usage:
|
||||
*
|
||||
* ```html
|
||||
* <div class="o-grid -col-4 -col-12@from-medium -gutters">
|
||||
* <div class="o-grid_item u-gc-1/2 u-gc-3/9@from-medium">
|
||||
* <p>Hello</p>
|
||||
* </div>
|
||||
* <div class="o-grid_item u-gc-3/4 u-gc-9/13@from-medium">
|
||||
* <p>Hello</p>
|
||||
* </div>
|
||||
* </div>
|
||||
* ```
|
||||
*/
|
||||
.o-grid {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.o-layout.-reverse > .o-layout_item {
|
||||
direction: ltr;
|
||||
.o-grid:is(ul,
|
||||
ol) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.o-grid.-col-12 {
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
}
|
||||
|
||||
.o-grid.-col-4 {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.o-grid.-col-12\@from-medium {
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.o-grid.-gutters {
|
||||
gap: 20px;
|
||||
-moz-column-gap: 20px;
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
.o-grid.-full-height {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.o-grid.-top-items {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.o-grid.-right-items {
|
||||
justify-items: end;
|
||||
}
|
||||
|
||||
.o-grid.-bottom-items {
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.o-grid.-left-items {
|
||||
justify-items: start;
|
||||
}
|
||||
|
||||
.o-grid.-center-items {
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.o-grid.-center-items-x {
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.o-grid.-center-items-y {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.o-grid.-stretch-items {
|
||||
align-items: stretch;
|
||||
justify-items: stretch;
|
||||
}
|
||||
|
||||
.o-grid.-top-cells {
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.o-grid.-right-cells {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.o-grid.-bottom-cells {
|
||||
align-content: end;
|
||||
}
|
||||
|
||||
.o-grid.-left-cells {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.o-grid.-center-cells {
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.o-grid.-center-cells-x {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.o-grid.-center-cells-y {
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.o-grid.-stretch-cells {
|
||||
align-content: stretch;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.o-grid.-space-around-cells {
|
||||
align-content: space-around;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.o-grid.-space-around-cells-x {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.o-grid.-space-around-cells-y {
|
||||
align-content: space-around;
|
||||
}
|
||||
|
||||
.o-grid.-space-between-cells {
|
||||
justify-content: space-between;
|
||||
align-content: space-between;
|
||||
}
|
||||
|
||||
.o-grid.-space-between-cells-x {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.o-grid.-space-between-cells-y {
|
||||
align-content: space-between;
|
||||
}
|
||||
|
||||
.o-grid.-space-evenly-cells {
|
||||
justify-content: space-evenly;
|
||||
align-content: space-evenly;
|
||||
}
|
||||
|
||||
.o-grid.-space-evenly-cells-x {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.o-grid.-space-evenly-cells-y {
|
||||
align-content: space-evenly;
|
||||
}
|
||||
|
||||
.o-grid_item {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.o-grid_item.-align-end {
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
.c-scrollbar {
|
||||
@@ -753,7 +876,7 @@ a:focus, a:hover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: black;
|
||||
background-color: #000000;
|
||||
opacity: 0.5;
|
||||
width: 7px;
|
||||
border-radius: 10px;
|
||||
@@ -818,7 +941,7 @@ a:focus, a:hover {
|
||||
.c-form_input, .c-form_select_input, .c-form_textarea {
|
||||
padding: 0.625rem;
|
||||
border: 1px solid lightgray;
|
||||
background-color: white;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.c-form_input:hover, .c-form_select_input:hover, .c-form_textarea:hover {
|
||||
@@ -949,68 +1072,43 @@ a:focus, a:hover {
|
||||
}
|
||||
|
||||
/* stylelint-enable */
|
||||
.u-1\/1 {
|
||||
width: 100% !important;
|
||||
|
||||
.u-gc-1\/3 {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 3;
|
||||
}
|
||||
|
||||
.u-1\/2 {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.u-2\/2 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.u-1\/3 {
|
||||
width: 33.33333% !important;
|
||||
}
|
||||
|
||||
.u-2\/3 {
|
||||
width: 66.66667% !important;
|
||||
}
|
||||
|
||||
.u-3\/3 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.u-1\/4 {
|
||||
width: 25% !important;
|
||||
}
|
||||
|
||||
.u-2\/4 {
|
||||
width: 50% !important;
|
||||
}
|
||||
|
||||
.u-3\/4 {
|
||||
width: 75% !important;
|
||||
}
|
||||
|
||||
.u-4\/4 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.u-1\/5 {
|
||||
width: 20% !important;
|
||||
}
|
||||
|
||||
.u-2\/5 {
|
||||
width: 40% !important;
|
||||
}
|
||||
|
||||
.u-3\/5 {
|
||||
width: 60% !important;
|
||||
}
|
||||
|
||||
.u-4\/5 {
|
||||
width: 80% !important;
|
||||
}
|
||||
|
||||
.u-5\/5 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
@media (min-width: 700px) {
|
||||
.u-1\/2\@from-small {
|
||||
width: 50%;
|
||||
@media (min-width: 1000px) {
|
||||
.u-gc-1\/5\@from-medium {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 5;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.u-gc-1\/8\@from-medium {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 8;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.u-gc-5\/9\@from-medium {
|
||||
grid-column-start: 5;
|
||||
grid-column-end: 9;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.u-gc-5\/13\@from-medium {
|
||||
grid-column-start: 5;
|
||||
grid-column-end: 13;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
.u-gc-9\/13\@from-medium {
|
||||
grid-column-start: 9;
|
||||
grid-column-end: 13;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,6 +25,7 @@
|
||||
<ul>
|
||||
<li><a href="images.html">Images</a></li>
|
||||
<li><a href="form.html" data-load="customTransition">Form</a></li>
|
||||
<li><a href="grid.html">Grid</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
96
www/grid.html
Normal file
96
www/grid.html
Normal file
@@ -0,0 +1,96 @@
|
||||
<!doctype html>
|
||||
<html class="is-loading" lang="en" data-page="grid">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Locomotive Boilerplate</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<link rel="manifest" href="site.webmanifest">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="assets/images/favicons/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicons/favicon-16x16.png">
|
||||
<link rel="mask-icon" href="assets/images/favicons/safari-pinned-tab.svg" color="#000000">
|
||||
<!-- For a dark mode managment and svg favicon add this in your favicon.svg -->
|
||||
<!--
|
||||
<style>
|
||||
path {
|
||||
fill: #000;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
-->
|
||||
<!-- <link rel="icon" href="assets/images/favicons/favicon.svg"> -->
|
||||
|
||||
<link id="main-css" rel="stylesheet" href="assets/styles/main.css" media="print"
|
||||
onload="this.media='all'; this.onload=null; this.isLoaded=true">
|
||||
</head>
|
||||
|
||||
<body data-module-load>
|
||||
<div data-load-container>
|
||||
<div class="o-scroll" data-module-scroll="main">
|
||||
<header data-scroll-section>
|
||||
<a href="/">
|
||||
<h1>Locomotive Boilerplate</h1>
|
||||
</a>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="images.html">Images</a></li>
|
||||
<li><a href="form.html" data-load="customTransition">Form</a></li>
|
||||
<li><a href="grid.html">Grid</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main data-scroll-section>
|
||||
<div class="o-container">
|
||||
<h1 class="c-heading -h1">Hello</h1>
|
||||
|
||||
<div class="o-grid -col-4 -col-12@from-medium -gutters">
|
||||
<div class="o-grid_item u-gc-1/8@from-medium" style="background-color: gray;">
|
||||
<h2 class="c-heading -h2">This grid has 4 columns and 12 columns from `medium` MQ</h2>
|
||||
</div>
|
||||
|
||||
<div class="o-grid_item u-gc-1/5@from-medium" style="background-color: yellow;">
|
||||
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Expedita provident distinctio deleniti eaque cumque doloremque aut quo dicta porro commodi, temporibus totam dolor autem tempore quasi ullam sed suscipit vero?</p>
|
||||
</div>
|
||||
|
||||
<div class="o-grid_item u-gc-5/9@from-medium" style="background-color: red;">
|
||||
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Expedita provident distinctio deleniti eaque cumque doloremque aut quo dicta porro commodi, temporibus totam dolor autem tempore quasi ullam sed suscipit vero?</p>
|
||||
</div>
|
||||
|
||||
<div class="o-grid_item u-gc-9/13@from-medium" style="background-color: blue;">
|
||||
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Expedita provident distinctio deleniti eaque cumque doloremque aut quo dicta porro commodi, temporibus totam dolor autem tempore quasi ullam sed suscipit vero?</p>
|
||||
</div>
|
||||
|
||||
<div class="o-grid_item u-gc-1/3 u-gc-5/13@from-medium" style="background-color: pink;">
|
||||
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Expedita provident distinctio deleniti eaque cumque doloremque aut quo dicta porro commodi, temporibus totam dolor autem tempore quasi ullam sed suscipit vero?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<script nomodule src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.6.0/polyfill.min.js"
|
||||
crossorigin="anonymous"></script>
|
||||
<script nomodule
|
||||
src="https://polyfill.io/v3/polyfill.min.js?features=Element.prototype.remove%2CElement.prototype.append%2Cfetch%2CCustomEvent%2CElement.prototype.matches%2CNodeList.prototype.forEach%2CAbortController"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<script src="assets/scripts/vendors.js" defer></script>
|
||||
<script src="assets/scripts/app.js" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -25,6 +25,7 @@
|
||||
<ul>
|
||||
<li><a href="images.html">Images</a></li>
|
||||
<li><a href="form.html" data-load="customTransition">Form</a></li>
|
||||
<li><a href="grid.html">Grid</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
@@ -29,11 +29,20 @@
|
||||
-->
|
||||
<!-- <link rel="icon" href="assets/images/favicons/favicon.svg"> -->
|
||||
|
||||
<!-- Preload Fonts -->
|
||||
<link rel="preload" href="assets/fonts/SourceSans3-Bold.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="assets/fonts/SourceSans3-BoldIt.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="assets/fonts/SourceSans3-Regular.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="assets/fonts/SourceSans3-RegularIt.woff2" as="font" type="font/woff2" crossorigin>
|
||||
|
||||
<link id="main-css" rel="stylesheet" href="assets/styles/main.css" media="print"
|
||||
onload="this.media='all'; this.onload=null; this.isLoaded=true">
|
||||
</head>
|
||||
|
||||
<body data-module-load>
|
||||
|
||||
<!-- <p aria-hidden="true" class="u-screen-reader-text" style="font-family: 'Webfont';"> </p> -->
|
||||
|
||||
<div data-load-container>
|
||||
<div class="o-scroll" data-module-scroll="main">
|
||||
<header data-scroll-section>
|
||||
@@ -44,11 +53,12 @@
|
||||
<ul>
|
||||
<li><a href="images.html">Images</a></li>
|
||||
<li><a href="form.html" data-load="customTransition">Form</a></li>
|
||||
<li><a href="grid.html">Grid</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main data-scroll-section>
|
||||
<main data-module-example data-scroll-section>
|
||||
<div class="o-container">
|
||||
<h1 class="c-heading -h1">Hello</h1>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user