mirror of
https://github.com/locomotivemtl/locomotive-boilerplate.git
synced 2026-01-15 00:55:08 +08:00
Compare commits
31 Commits
feature/ba
...
feature/js
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ff4f2fb9c | ||
|
|
cfa899639e | ||
|
|
d916ad030d | ||
|
|
b346b043a6 | ||
|
|
99291b17f5 | ||
|
|
c8d4e7c154 | ||
|
|
bce37afb6e | ||
|
|
7a23abff92 | ||
|
|
27d8fdee22 | ||
|
|
9154deb036 | ||
|
|
17e8004515 | ||
|
|
6b3edefa48 | ||
|
|
fd5efe3531 | ||
|
|
2783fb5138 | ||
|
|
f1e4cd2c55 | ||
|
|
b7d25c5865 | ||
|
|
0a199afe01 | ||
|
|
8ca570b37a | ||
|
|
e5417ff6ab | ||
|
|
3a94c6aba9 | ||
|
|
648109fc9b | ||
|
|
b162c62930 | ||
|
|
cb27975087 | ||
|
|
0b667542f5 | ||
|
|
d7de1b2566 | ||
|
|
34bca7d68a | ||
|
|
f44093ec19 | ||
|
|
141a8ffa97 | ||
|
|
e875495928 | ||
|
|
c2db2e1922 | ||
|
|
da66f89d7f |
0
assets/images/sprite/.gitkeep
Normal file
0
assets/images/sprite/.gitkeep
Normal file
@@ -1,15 +0,0 @@
|
||||
export default function(func, wait, immediate) {
|
||||
let timeout;
|
||||
return function() {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
const later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
}
|
||||
180
assets/scripts/utils/events.js
Normal file
180
assets/scripts/utils/events.js
Normal file
@@ -0,0 +1,180 @@
|
||||
import { debounce } from './tickers'
|
||||
|
||||
/**
|
||||
* @typedef {object} RegisteredCustomEvent
|
||||
*
|
||||
* @property {EventTarget} target - The event target ({@see Window} or {@see Node}).
|
||||
* @property {string} type - The custom event name.
|
||||
*/
|
||||
/** @type {RegisteredCustomEvent[]} */
|
||||
|
||||
const REGISTERED_CUSTOM_EVENTS = []
|
||||
|
||||
/**
|
||||
* Determines if the given object is the {@see Window}.
|
||||
*
|
||||
* @param {object} obj
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const isWindow = obj => obj === window
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the given object implements {@see EventTarget}.
|
||||
*
|
||||
* @param {object} obj
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const isEventTarget = obj => (obj instanceof EventTarget)
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the target already has the event attached.
|
||||
*
|
||||
* @param {EventTarget} target
|
||||
* @param {string} type - The custom event name.
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const isCustomEventRegistered = (target, type) => REGISTERED_CUSTOM_EVENTS.some(e => e.target === target && e.type === type)
|
||||
|
||||
|
||||
/**
|
||||
* Registers the custom event with the given target, if not already registered.
|
||||
*
|
||||
* @param {EventTarget} target
|
||||
* @param {string} type - The custom event name.
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
const addCustomEvent = (target, type) => {
|
||||
if (!isCustomEventRegistered(target, type)) {
|
||||
REGISTERED_CUSTOM_EVENTS.push({
|
||||
target,
|
||||
type
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a custom "start" event for the given target.
|
||||
*
|
||||
* Internally, this function adds a debounced event listener on
|
||||
* the given `event` to trigger the custom `<event>start` event.
|
||||
*
|
||||
* @param {EventTarget} target
|
||||
* @param {string} type - The base event name.
|
||||
* @param {number} [delay] - The number of milliseconds to wait
|
||||
* before dispatching the custom event.
|
||||
* @throws Error If the target is invalid.
|
||||
* @throws Error If the custom event is already defined.
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
const addStartEvent = (target, type, delay = 200) => {
|
||||
|
||||
const customType = `${type}start`
|
||||
|
||||
if (!isEventTarget(target)) {
|
||||
throw new Error(`addStartEvent: target parameter must be an instance of EventTarget`)
|
||||
}
|
||||
|
||||
if (isCustomEventRegistered(target, customType)) {
|
||||
throw new Error(`addStartEvent: '${customType}' already exists for target parameter`)
|
||||
}
|
||||
|
||||
addCustomEvent(target, customType)
|
||||
const startEvent = new CustomEvent(customType)
|
||||
|
||||
target.addEventListener(type, debounce(() => {
|
||||
target.dispatchEvent(startEvent)
|
||||
}, delay, true))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a custom "end" event for the given target.
|
||||
*
|
||||
* Internally, this function adds a debounced event listener on
|
||||
* the given `event` to trigger the custom `<event>end` event.
|
||||
*
|
||||
* @param {EventTarget} target
|
||||
* @param {string} type - The base event name.
|
||||
* @param {number} [delay] - The number of milliseconds to wait
|
||||
* before dispatching the custom event.
|
||||
* @throws Error If the target is invalid.
|
||||
* @throws Error If the custom event is already defined.
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
const addEndEvent = (target, type, delay = 200) => {
|
||||
|
||||
const customType = `${type}end`
|
||||
|
||||
if (!isEventTarget(target)) {
|
||||
throw new Error(`addEndEvent: target parameter must be an instance of EventTarget`)
|
||||
}
|
||||
|
||||
if (isCustomEventRegistered(target, customType)) {
|
||||
throw new Error(`addEndEvent: '${customType}' already exists for target parameter`)
|
||||
}
|
||||
|
||||
addCustomEvent(target, customType)
|
||||
const endEvent = new CustomEvent(customType)
|
||||
|
||||
target.addEventListener(type, debounce(() => {
|
||||
target.dispatchEvent(endEvent)
|
||||
}, delay))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds custom scroll "up" and "down" events for the given target.
|
||||
*
|
||||
* Internally, this function adds an event listener on
|
||||
* the scroll event to detect the direction and trigger
|
||||
* the custom `scrollup` and `scrolldown` events.
|
||||
*
|
||||
* @param {EventTarget} [target] - If omitted, the custom event
|
||||
* if attached to the Window.
|
||||
* @throws Error If the target is invalid.
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
const addScrollDirectionEvents = (target = window) => {
|
||||
|
||||
if (!isEventTarget(target)) {
|
||||
throw new Error(`addScrollDirectionEvents: target parameter must be an instance of EventTarget`)
|
||||
}
|
||||
|
||||
let scrollTop = target.scrollTop
|
||||
let previousScrollTop = scrollTop
|
||||
let direction = 0
|
||||
const scrollUp = new CustomEvent('scrollup')
|
||||
const scrollDown = new CustomEvent('scrolldown')
|
||||
const scrollProperty = isWindow(target) ? 'scrollY' : 'scrollTop'
|
||||
|
||||
target.addEventListener('scroll', () => {
|
||||
scrollTop = target[scrollProperty]
|
||||
// Scroll up
|
||||
if (scrollTop < previousScrollTop && direction > -1) {
|
||||
target.dispatchEvent(scrollUp)
|
||||
direction = -1
|
||||
// Scroll down
|
||||
} else if (scrollTop > previousScrollTop && direction < 1) {
|
||||
target.dispatchEvent(scrollDown)
|
||||
direction = 1
|
||||
}
|
||||
previousScrollTop = scrollTop
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
addStartEvent,
|
||||
addEndEvent,
|
||||
addScrollDirectionEvents,
|
||||
}
|
||||
78
assets/scripts/utils/tickers.js
Normal file
78
assets/scripts/utils/tickers.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Creates a debounced function.
|
||||
*
|
||||
* A debounced function delays invoking `callback` until after
|
||||
* `delay` milliseconds have elapsed since the last time the
|
||||
* debounced function was invoked.
|
||||
*
|
||||
* Useful for behaviour that should only happen _before_ or
|
||||
* _after_ an event has stopped occurring.
|
||||
*
|
||||
* @template {function} T
|
||||
*
|
||||
* @param {T} callback - The function to debounce.
|
||||
* @param {number} delay - The number of milliseconds to wait.
|
||||
* @param {boolean} [immediate] -
|
||||
* If `true`, `callback` is invoked before `delay`.
|
||||
* If `false`, `callback` is invoked after `delay`.
|
||||
* @return {function<T>} The new debounced function.
|
||||
*/
|
||||
|
||||
const debounce = (callback, delay, immediate = false) => {
|
||||
let timeout = null
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timeout)
|
||||
|
||||
const later = () => {
|
||||
timeout = null
|
||||
if (!immediate) {
|
||||
callback(...args)
|
||||
}
|
||||
}
|
||||
|
||||
if (immediate && !timeout) {
|
||||
callback(...args)
|
||||
}
|
||||
|
||||
timeout = setTimeout(later, delay)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a throttled function.
|
||||
*
|
||||
* A throttled function invokes `callback` at most once per every
|
||||
* `delay` milliseconds.
|
||||
*
|
||||
* Useful for rate-limiting an event that occurs in quick succession.
|
||||
*
|
||||
* @template {function} T
|
||||
*
|
||||
* @param {T} callback - The function to throttle.
|
||||
* @param {number} delay - The number of milliseconds to wait.
|
||||
* @return {function<T>} The new throttled function.
|
||||
*/
|
||||
|
||||
const throttle = (callback, delay) => {
|
||||
let timeout = false
|
||||
|
||||
return (...args) => {
|
||||
if (!timeout) {
|
||||
timeout = true
|
||||
|
||||
callback(...args)
|
||||
|
||||
setTimeout(() => {
|
||||
timeout = false
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
debounce,
|
||||
throttle
|
||||
}
|
||||
@@ -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,6 +13,7 @@
|
||||
// Tools
|
||||
// ==========================================================================
|
||||
|
||||
@import "tools/maths";
|
||||
@import "tools/functions";
|
||||
@import "tools/mixins";
|
||||
@import "tools/fonts";
|
||||
@@ -40,6 +41,7 @@
|
||||
@import "objects/scroll";
|
||||
@import "objects/container";
|
||||
@import "objects/ratio";
|
||||
@import "objects/icons";
|
||||
@import "objects/layout";
|
||||
// @import "objects/crop";
|
||||
// @import "objects/table";
|
||||
@@ -56,10 +58,6 @@
|
||||
@import "components/button";
|
||||
@import "components/form";
|
||||
|
||||
// Templates
|
||||
// ==========================================================================
|
||||
// @import "templates/template";
|
||||
|
||||
// Utilities
|
||||
// ==========================================================================
|
||||
|
||||
|
||||
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,13 +14,30 @@ $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": ("Webfont Sans", $font-fallback-sans),
|
||||
// "serif": ("Webfont Serif", $font-fallback-serif),
|
||||
// "mono": ("Webfont Mono", $font-fallback-mono)
|
||||
);
|
||||
|
||||
// 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,
|
||||
@@ -31,9 +48,9 @@ $font-faces: (
|
||||
// =============================================================================
|
||||
|
||||
// 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,28 +62,28 @@ $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: 60px;
|
||||
$unit-small: 30px;
|
||||
|
||||
// Container
|
||||
// =============================================================================
|
||||
|
||||
$container-width: 2000px;
|
||||
$padding: $unit;
|
||||
$container-width: 2000px;
|
||||
$padding: $unit;
|
||||
|
||||
// Breakpoints
|
||||
// =============================================================================
|
||||
@@ -89,3 +106,20 @@ $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: (
|
||||
"goku": 9000,
|
||||
"transition": 500,
|
||||
"toast": 400,
|
||||
"popover": 300,
|
||||
"modal": 250,
|
||||
"sheet": 200,
|
||||
"fixed": 150,
|
||||
"sticky": 100,
|
||||
"dropdown": 50,
|
||||
"default": 1,
|
||||
"limbo": -999
|
||||
);
|
||||
|
||||
@@ -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,11 +22,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -31,14 +37,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -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($layers, $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);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -519,8 +519,8 @@ button:focus, button:hover,
|
||||
html {
|
||||
min-height: 100%;
|
||||
line-height: 1.5;
|
||||
font-family: "Webfont Sans", "Helvetica Neue", Arial, sans-serif;
|
||||
color: #222222;
|
||||
font-family: "Webfont 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;
|
||||
@@ -644,6 +644,19 @@ a:focus, a:hover {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.o-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.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 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -753,7 +766,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 +831,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 {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user