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

33 Commits

Author SHA1 Message Date
Lucas Bigot
344f4bc98c Add default Modal component 2024-08-02 12:41:22 -04:00
Chauncey McAskill
d593fe5409 Update to NPM v10 + Update dependencies (#179)
* Update NPM constraint and dependencies

Requied:
- NPM v8 → v10

Changed:
- Fixed Node/NPM requirements in README.
- Fixed dependency vulenerabilities.
- Updated dependencies.
- Removed obsolete Node flag `--experimental-json-modules`.
- Replaced obsolete import `assert` keyword with `with`.

* Update gitignore of assets to use wildcards

---------

Co-authored-by: Deven Caron <devencaron@gmail.com>
2024-07-04 13:12:54 -04:00
Chauncey McAskill
f8a46043a6 Remove babel-polyfill (#178)
All features are suported by modern browsers.

Can be restored if ever needed to support older browsers.
2024-07-04 13:12:18 -04:00
Chauncey McAskill
25823286d5 Remove polyfill.io
See: https://sansec.io/research/polyfill-supply-chain-attack
2024-06-25 18:19:00 -04:00
Chauncey McAskill
605f30c948 Fix svg-mixer support for sub-directories
By default, svg-mixer only uses the SVG's file name as its ID. If any SVG files are stored in sub-directories, that information is ignored in the assembled spritesheet. This is problematic since context is lost (the sub-directory's name) and risks duplicate symbol IDs.

This commit introduces a new NPM dependency, `common-path` to resolve the longest common base path, and uses a dependency of `svg-mixer` named `url-slug` in order to prefix the directory name.
2024-04-03 09:29:52 -04:00
Lucas Bigot
27a41aba66 Add default text dedicated file (#173) 2024-04-03 08:29:24 -04:00
Lucas Bigot
353a38915d Update default headings (#172)
* Move font-size css var into dedicated heading file + Add default mixins

* Rename responsive-type for a more generic name
2024-04-03 08:28:57 -04:00
Lucas Bigot
6d37049989 Fix resize event debounce method + Clean up and re-organize app file 2024-03-26 18:30:36 -04:00
Lucas Bigot
bc3fd3a492 Fix grid-space sass function using calculated vw value 2024-03-26 18:29:48 -04:00
Lucas Bigot
45d8be5525 Rename breakpoints by shortnames (#171)
* Rename breakpoints by shortnames

* Replace `tiny` breakpoint for `xs` in spacing loop

* Add 2xs, 4xl and 5xl breakpoints
2024-03-26 16:50:16 -04:00
Deven Caron
ea8f98a52d Add assets.json and empty directories for scripts and styles 2024-03-26 16:29:33 -04:00
Deven Caron
56bbd9e3c5 Delete assets.json file 2024-03-26 16:29:12 -04:00
Deven Caron
dcb7e91b91 Update node version for vercel 2024-03-26 16:20:50 -04:00
Deven Caron
2b1eb8e0dd Add new files and update .gitignore 2024-03-26 15:44:21 -04:00
Deven Caron
f4afd9c6b2 Delete compiled assets 2024-03-26 15:42:20 -04:00
Lucas Bigot
7578397a8e Merge pull request #170 from locomotivemtl/feature/config-speed
Rename `timing` by `speed`
2024-03-26 15:10:10 -04:00
Lucas Bigot
27effb470d Merge branch 'master' into feature/config-speed 2024-03-26 15:06:16 -04:00
Lucas Bigot
7a91cbce61 Merge pull request #165 from locomotivemtl/feature/css-reset
Modernize css normalize
2024-03-26 15:03:15 -04:00
Lucas Bigot
65a265c0ea Merge branch 'master' into feature/css-reset 2024-03-26 15:02:21 -04:00
Lucas Bigot
afb3a4aa6a Merge pull request #161 from locomotivemtl/feature/svgs-folder
Add dedicated SVG sprite folder
2024-03-26 14:56:03 -04:00
Lucas Bigot
40521c3f2b Merge pull request #169 from locomotivemtl/feature/config-spacing
Rework spacing css config
2024-03-26 14:47:00 -04:00
Lucas Bigot
2d395cf73a Rename timing file and sass function for speed 2024-03-26 14:37:11 -04:00
Lucas Bigot
81d47b88b8 Rename spacer by spacings 2024-03-26 14:06:25 -04:00
Lucas Bigot
c16407c8c1 Add clamp-with-max & size-clamp sass functions 2024-03-26 11:41:23 -04:00
Lucas Bigot
522c9c0bcb Replace vh based spacing values 2024-03-26 11:16:54 -04:00
Chauncey McAskill
ddd12ffc38 Fix inconsistent argument name in grid-space() function in Sass 2024-01-29 15:19:09 -05:00
Chauncey McAskill
9e6d7ae182 Replace deprecated divisions in Sass 2024-01-29 15:17:50 -05:00
Chauncey McAskill
d5bff3ab50 Remove obsolete span() function in Sass 2024-01-29 15:06:58 -05:00
Lucas Vallenet
0af2be4599 Update reset 2024-01-12 12:07:38 +01:00
Lucas Vallenet
98ba8c4972 Update normalize comment 2024-01-12 11:28:42 +01:00
Lucas Vallenet
a674a16c4b Remove body margins 2024-01-11 11:27:44 +01:00
Lucas Vallenet
dd2c783938 Modernize css normalize 2024-01-11 11:10:38 +01:00
Lucas Vallenet
7021666c46 Add dedicated svg folder 2023-12-11 11:43:16 +01:00
50 changed files with 1966 additions and 3593 deletions

9
.gitignore vendored
View File

@@ -3,4 +3,11 @@ node_modules
Thumbs.db
loconfig.*.json
!loconfig.example.json
.prettierrc
.prettierrc
www/assets/scripts/*
!www/assets/scripts/.gitkeep
www/assets/styles/*
!www/assets/styles/.gitkeep
assets.json

2
.nvmrc
View File

@@ -1 +1 @@
v20.10
v20

View File

@@ -23,8 +23,8 @@ Learn more about [languages and technologies](docs/technologies.md).
Make sure you have the following installed:
* [Node] — at least 17.9, the latest LTS is recommended.
* [NPM] — at least 8.0, the latest LTS is recommended.
* [Node] — at least 20, the latest LTS is recommended.
* [NPM] — at least 10, the latest LTS is recommended.
> 💡 You can use [NVM] to install and use different versions of Node via the command-line.

View File

@@ -1,3 +0,0 @@
{
"version": 1706549368176
}

View File

@@ -10,7 +10,73 @@ const app = new modular({
modules,
});
const setViewportSizes = () => {
function init() {
bindEvents();
globals();
setViewportSizes();
app.init(app);
$html.classList.add(CSS_CLASS.LOADED, CSS_CLASS.READY);
$html.classList.remove(CSS_CLASS.LOADING);
/**
* Debug focus
*/
// document.addEventListener(
// "focusin",
// function () {
// console.log('focused: ', document.activeElement)
// }, true
// );
/**
* Eagerly load the following fonts.
*/
if (isFontLoadingAPIAvailable) {
loadFonts(FONT.EAGER, ENV.IS_DEV).then((eagerFonts) => {
$html.classList.add(CSS_CLASS.FONTS_LOADED);
/**
* Debug fonts loading
*/
// if (ENV.IS_DEV) {
// console.group('Eager fonts loaded!', eagerFonts.length, '/', document.fonts.size);
// console.group('State of eager fonts:');
// eagerFonts.forEach(font => console.log(font.family, font.style, font.weight, font.status));
// console.groupEnd();
// console.group('State of all fonts:');
// document.fonts.forEach(font => console.log(font.family, font.style, font.weight, font.status));
// console.groupEnd();
// }
});
}
}
////////////////
// Global events
////////////////
function bindEvents() {
// Resize event
const resizeEndEvent = new CustomEvent(CUSTOM_EVENT.RESIZE_END)
window.addEventListener(
"resize",
debounce(() => {
window.dispatchEvent(resizeEndEvent)
}, 200, false)
)
window.addEventListener(
"resize",
onResize
)
}
function onResize() {
setViewportSizes()
}
function setViewportSizes() {
// Document styles
const documentStyles = document.documentElement.style;
@@ -25,10 +91,10 @@ const setViewportSizes = () => {
}
// Viewport height
const height = window.innerHeight;
const svh = document.documentElement.clientHeight * 0.01;
documentStyles.setProperty('--svh', `${svh}px`);
const dvh = height * 0.01;
const dvh = window.innerHeight * 0.01;
documentStyles.setProperty('--dvh', `${dvh}px`);
if (document.body) {
@@ -53,6 +119,9 @@ const setViewportSizes = () => {
}
}
////////////////
// Execute
////////////////
window.addEventListener('load', () => {
const $style = document.getElementById('main-css');
@@ -66,46 +135,3 @@ window.addEventListener('load', () => {
console.warn('The "main-css" stylesheet not found');
}
});
function init() {
globals();
setViewportSizes();
app.init(app);
$html.classList.add(CSS_CLASS.LOADED);
$html.classList.add(CSS_CLASS.READY);
$html.classList.remove(CSS_CLASS.LOADING);
// Bind window resize event with default vars
const resizeEndEvent = new CustomEvent(CUSTOM_EVENT.RESIZE_END);
window.addEventListener('resize', () => {
setViewportSizes();
debounce(() => {
window.dispatchEvent(resizeEndEvent);
}, 200, false)
})
window.addEventListener('orientationchange', () => {
setViewportSizes();
})
/**
* Eagerly load the following fonts.
*/
if (isFontLoadingAPIAvailable) {
loadFonts(FONT.EAGER, ENV.IS_DEV).then((eagerFonts) => {
$html.classList.add(CSS_CLASS.FONTS_LOADED);
if (ENV.IS_DEV) {
console.group('Eager fonts loaded!', eagerFonts.length, '/', document.fonts.size);
console.group('State of eager fonts:');
eagerFonts.forEach(font => console.log(font.family, font.style, font.weight, font.status));
console.groupEnd();
console.group('State of all fonts:');
document.fonts.forEach(font => console.log(font.family, font.style, font.weight, font.status));
console.groupEnd();
}
});
}
}

View File

@@ -46,6 +46,8 @@ const CSS_CLASS = Object.freeze({
// Custom js events
const CUSTOM_EVENT = Object.freeze({
RESIZE_END: 'loco.resizeEnd',
VISIT_START: 'visit.start',
MODAL_OPEN: 'modal.open',
// ...
})

View File

@@ -1,3 +1,4 @@
export {default as Example} from './modules/Example';
export {default as Load} from './modules/Load';
export {default as Modal} from './modules/Modal';
export {default as Scroll} from './modules/Scroll';

View File

@@ -1,5 +1,6 @@
import { module } from 'modujs';
import modularLoad from 'modularload';
import { CUSTOM_EVENT } from '../config';
export default class extends module {
constructor(m) {
@@ -14,6 +15,12 @@ export default class extends module {
}
});
load.on('loading', (transition, oldContainer) => {
const args = { transition, oldContainer };
// Dispatch custom event
window.dispatchEvent(new CustomEvent(CUSTOM_EVENT.VISIT_START, { detail: args }))
});
load.on('loaded', (transition, oldContainer, newContainer) => {
this.call('destroy', oldContainer, 'app');
this.call('update', newContainer, 'app');

View File

@@ -0,0 +1,195 @@
import { createFocusTrap } from 'focus-trap'
import { module as Module } from 'modujs'
import { $html } from '../utils/dom'
import { CUSTOM_EVENT } from '../config'
/**
* Generic component to display a modal.
*
*/
export default class Modal extends Module {
/**
* Creates a new Modal.
*
* @param {object} options - The module options.
* @param {string} options.dataName - The module data attribute name.
* @throws {TypeError} If the class does not have an active CSS class defined.
*/
static CLASS = {
EL: 'is-open',
HTML: 'has-modal-open',
}
constructor(options) {
super(options)
// Data
this.moduleName = options.name
this.dataName = this.getData('name') || options.dataName
// Bindings
this.toggle = this.toggle.bind(this)
this.onModalOpen = this.onModalOpen.bind(this)
this.onVisitStart = this.onVisitStart.bind(this)
// UI
this.$togglers = document.querySelectorAll(`[data-${this.dataName}-toggler]`)
this.$focusTrapTargets = Array.from(this.el.querySelectorAll(`[data-${this.dataName}-target]`))
// Focus trap options
this.focusTrapOptions = {
/**
* There is a delay between when the class is applied
* and when the element is focusable
*/
checkCanFocusTrap: (trapContainers) => {
const results = trapContainers.map((trapContainer) => {
return new Promise((resolve) => {
const interval = setInterval(() => {
if (
getComputedStyle(trapContainer).visibility !==
'hidden'
) {
resolve()
clearInterval(interval)
}
}, 5)
})
})
// Return a promise that resolves when all the trap containers are able to receive focus
return Promise.all(results)
},
onActivate: () => {
this.el.classList.add(Modal.CLASS.EL)
$html.classList.add(Modal.CLASS.HTML)
$html.classList.add('has-'+this.dataName+'-open')
this.el.setAttribute('aria-hidden', false)
this.isOpen = true
this.onActivate?.();
},
onPostActivate: () => {
this.$togglers.forEach(($toggler) => {
$toggler.setAttribute('aria-expanded', true)
})
},
onDeactivate: () => {
this.el.classList.remove(Modal.CLASS.EL)
$html.classList.remove(Modal.CLASS.HTML)
$html.classList.remove('has-'+this.dataName+'-open')
this.el.setAttribute('aria-hidden', true)
this.isOpen = false
this.onDeactivate?.();
},
onPostDeactivate: () => {
this.$togglers.forEach(($toggler) => {
$toggler.setAttribute('aria-expanded', false)
})
},
clickOutsideDeactivates: true,
}
this.isOpen = false
}
/////////////////
// Lifecycle
/////////////////
init() {
this.onBeforeInit?.()
this.focusTrap = createFocusTrap(
this.$focusTrapTargets.length > 0 ? this.$focusTrapTargets : [this.el],
this.focusTrapOptions
)
this.bindEvents()
this.onInit?.()
}
destroy() {
this.focusTrap?.deactivate?.({
returnFocus: false,
})
this.unbindEvents()
this.onDestroy?.()
super.destroy()
}
/////////////////
// Events
/////////////////
bindEvents() {
window.addEventListener(CUSTOM_EVENT.VISIT_START, this.onVisitStart)
window.addEventListener(CUSTOM_EVENT.MODAL_OPEN, this.onModalOpen)
this.$togglers.forEach(($toggler) => {
$toggler.addEventListener('click', this.toggle)
})
}
unbindEvents() {
window.removeEventListener(CUSTOM_EVENT.VISIT_START, this.onVisitStart)
window.removeEventListener(CUSTOM_EVENT.MODAL_OPEN, this.onModalOpen)
this.$togglers.forEach(($toggler) => {
$toggler.removeEventListener('click', this.toggle)
})
}
/////////////////
// Callbacks
/////////////////
onVisitStart() {
// Close the modal on page change
this.close()
}
onModalOpen(event) {
// Close the modal if another one is opened
if (event.detail !== this.el) {
this.close()
}
}
/////////////////
// Methods
/////////////////
toggle(event) {
if (this.el.classList.contains(Modal.CLASS.EL)) {
this.close(event)
} else {
this.open(event)
}
}
open(args) {
if (this.isOpen) return
this.focusTrap?.activate?.()
this.onOpen?.(args)
window.dispatchEvent(new CustomEvent(CUSTOM_EVENT.MODAL_OPEN, { detail: this.el }))
}
close(args) {
if (!this.isOpen) return
this.focusTrap?.deactivate?.()
this.onClose?.(args)
}
}

View File

@@ -2,30 +2,78 @@
// Components / Headings
// ==========================================================================
// Font sizes
// ==========================================================================
:root {
// Default
--font-size-h1: #{responsive-value(38px, 90px, $from-xl)};
--font-size-h2: #{responsive-value(34px, 72px, $from-xl)};
--font-size-h3: #{responsive-value(28px, 54px, $from-xl)};
--font-size-h4: #{responsive-value(24px, 40px, $from-xl)};
--font-size-h5: #{responsive-value(20px, 30px, $from-xl)};
--font-size-h6: #{responsive-value(18px, 23px, $from-xl)};
}
// Mixins
// ==========================================================================
@mixin heading {
font-family: ff('sans');
font-weight: $font-weight-medium;
}
@mixin heading-h1 {
font-size: var(--font-size-h1);
line-height: 1.1;
}
@mixin heading-h2 {
font-size: var(--font-size-h2);
line-height: 1.1;
}
@mixin heading-h3 {
font-size: var(--font-size-h3);
line-height: 1.1;
}
@mixin heading-h4 {
font-size: var(--font-size-h4);
line-height: 1.2;
}
@mixin heading-h5 {
font-size: var(--font-size-h5);
line-height: 1.2;
}
@mixin heading-h6 {
font-size: var(--font-size-h6);
line-height: 1.4;
}
// Styles
// ==========================================================================
.c-heading {
margin-bottom: rem(30px);
@include heading;
&.-h1 {
font-size: var(--font-size-h1);
@include heading-h1;
}
&.-h2 {
font-size: var(--font-size-h2);
@include heading-h2;
}
&.-h3 {
font-size: var(--font-size-h3);
@include heading-h3;
}
&.-h4 {
font-size: var(--font-size-h4);
@include heading-h4;
}
&.-h5 {
font-size: var(--font-size-h5);
@include heading-h5;
}
&.-h6 {
font-size: var(--font-size-h6);
@include heading-h6;
}
}

View File

@@ -0,0 +1,52 @@
// ==========================================================================
// Components / Modal
// ==========================================================================
.c-modal {
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100dvh;
max-width: inherit;
max-height: 100lvh;
margin: 0;
padding: 0 var(--grid-margin);
background: transparent;
border: none;
visibility: hidden;
pointer-events: none;
overflow: hidden;
// Backdrop
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: color(darkest, 0.5);
z-index: -1;
}
html.is-first-loaded & {
transition: visibility speed(normal);
}
&.is-open {
pointer-events: auto;
visibility: visible;
transition-duration: 0s;
}
}
.c-modal_inner {
width: 100%;
max-width: rem(500px);
padding: $unit-small;
background-color: color(lightest);
}

View File

@@ -0,0 +1,53 @@
// ==========================================================================
// Components / Texts
// ==========================================================================
// Font sizes
// ==========================================================================
:root {
--font-size-body-regular: #{responsive-value(15px, 17px, $from-lg)};
--font-size-body-medium: #{responsive-value(18px, 23px, $from-lg)};
--font-size-body-small: #{responsive-value(13px, 16px, $from-lg)};
}
// Mixins
// ==========================================================================
@mixin text {
font-family: ff('sans');
}
@mixin body-regular {
font-size: var(--font-size-body-regular);
font-weight: $font-weight-normal;
line-height: 1.2;
}
@mixin body-medium {
font-size: var(--font-size-body-medium);
font-weight: $font-weight-normal;
line-height: 1.2;
}
@mixin body-small {
font-size: var(--font-size-body-small);
font-weight: $font-weight-normal;
line-height: 1.2;
}
// Styles
// ==========================================================================
.c-text {
@include text;
&.-body-regular {
@include body-regular;
}
&.-body-medium {
@include body-medium;
}
&.-body-small {
@include body-small;
}
}

View File

@@ -20,34 +20,26 @@ html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: $to-small) {
@media (max-width: $to-sm) {
font-size: $font-size - 2px;
}
@media (min-width: $from-small) and (max-width: $to-medium) {
font-size: $font-size - 2px;
}
@media (min-width: $from-medium) and (max-width: $to-large) {
@media (min-width: $from-sm) and (max-width: $to-lg) {
font-size: $font-size - 1px;
}
@media (min-width: $from-large) and (max-width: $to-huge) {
font-size: $font-size; // [1]
@media (min-width: $from-lg) and (max-width: $to-2xl) {
font-size: $font-size;
}
@media (min-width: $from-huge) and (max-width: $to-gigantic) {
@media (min-width: $from-2xl) and (max-width: $to-3xl) {
font-size: $font-size + 1px;
}
@media (min-width: $from-gigantic) and (max-width: $to-colossal) {
@media (min-width: $from-3xl) {
font-size: $font-size + 2px;
}
@media (min-width: $from-colossal) {
font-size: $font-size + 4px;
}
&.is-loading {
cursor: wait;
}

View File

@@ -0,0 +1,124 @@
// ==========================================================================
// Elements / Normalize
// ==========================================================================
// Modern CSS Normalize
// Based on the reset by Andy.set with some tweaks.
// Original by Andy.set: https://piccalil.li/blog/a-more-modern-css-reset/
// Review by Chris collier: https://chriscoyier.net/2023/10/03/being-picky-about-a-css-reset-for-fun-pleasure/
// Box sizing rules
*,
*:after,
*:before {
box-sizing: border-box;
}
// Prevent font size inflation
html {
-moz-text-size-adjust: none;
-webkit-text-size-adjust: none;
text-size-adjust: none;
}
// Remove default margin in favour of better control in authored CSS
p,
h1,
h2,
h3,
h4,
h5,
h6,
dl,
dd,
figure,
blockquote {
margin-block: unset;
}
// Remove list styles on ul, ol elements with a class, which suggests default styling will be removed
ul[class],
ol[class] {
margin: 0;
padding: 0;
list-style: none;
}
// Set core defaults
html {
line-height: 1.5;
}
body {
margin: unset;
}
// Set shorter line heights on headings and interactive elements
h1,
h2,
h3,
h4,
h5,
h6,
input,
label,
button {
line-height: 1.1;
}
// Balance text wrapping on headings
h1,
h2,
h3,
h4,
h5,
h6 {
text-wrap: balance;
}
// Remove a elements default styles if they have a class
a[class] {
color: inherit;
text-decoration: none;
}
// Make assets easier to work with
img,
svg,
canvas,
picture {
display: block;
max-inline-size: 100%;
block-size: auto;
}
// Inherit fonts for inputs and buttons
input,
button,
select,
textarea {
font: inherit;
}
// Make sure textareas without a rows attribute are not tiny
textarea:not([rows]) {
min-height: 10em;
}
// Anything that has been anchored to should have extra scroll margin
:target {
scroll-margin-block: 1rlh;
}
// Reduced mootion preference
@media (prefers-reduced-motion: reduce) {
*,
*:after,
*:before {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

View File

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

View File

@@ -1,44 +0,0 @@
// ==========================================================================
// Generic / Forms
// ==========================================================================
input,
select,
textarea {
display: block;
margin: 0;
padding: 0;
width: 100%;
outline: 0;
border: 0;
border-radius: 0;
background: none transparent;
color: inherit;
font: inherit;
line-height: normal;
appearance: none;
}
select {
text-transform: none;
&::-ms-expand {
display: none;
}
&::-ms-value {
background: none;
color: inherit;
}
// // Remove Firefox :focus dotted outline, breaks color inherit
// // &:-moz-focusring {
// // color: transparent;
// // text-shadow: 0 0 0 #000000; // Text :focus color
// // }
}
textarea {
overflow: auto;
resize: vertical;
}

View File

@@ -1,87 +0,0 @@
// ==========================================================================
// Generic
// ==========================================================================
html {
box-sizing: border-box;
}
// Add the correct display in IE 10-.
// 1. Add the correct display in IE.
template, // [1]
[hidden] {
display: none;
}
*,
:before,
:after {
box-sizing: inherit;
}
address {
font-style: inherit;
}
dfn,
cite,
em,
i {
font-style: italic;
}
b,
strong {
font-weight: $font-weight-bold;
}
a {
text-decoration: none;
svg {
pointer-events: none;
}
}
ul,
ol {
margin: 0;
padding: 0;
list-style: none;
}
p,
figure {
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6 {
margin: 0;
}
// 1. Single taps should be dispatched immediately on clickable elements
a, area, button, input, label, select, textarea, [tabindex] {
-ms-touch-action: manipulation; // [1]
touch-action: manipulation;
}
[hreflang] > abbr[title] {
text-decoration: none;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
hr {
display: block;
margin: 1em 0;
padding: 0;
height: 1px;
border: 0;
border-top: 1px solid #CCCCCC;
}

View File

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

View File

@@ -20,25 +20,16 @@
// Settings
// ==========================================================================
@import "settings/config";
@import "settings/config.breakpoints";
@import "settings/config.colors";
@import "settings/config.eases";
@import "settings/config.fonts";
@import "settings/config.spacers";
@import "settings/config.timings";
@import "settings/config.spacings";
@import "settings/config.speeds";
@import "settings/config.zindexes";
@import "settings/config";
@import "settings/config.variables";
// Generic
// ==========================================================================
@import "node_modules/normalize.css/normalize";
@import "generic/generic";
@import "generic/media";
@import "generic/form";
@import "generic/button";
// Vendors
// ==========================================================================
@import "node_modules/locomotive-scroll/dist/locomotive-scroll";
@@ -46,6 +37,7 @@
// Elements
// ==========================================================================
@import "elements/normalize";
@import "elements/document";
// Objects
@@ -62,8 +54,10 @@
// ==========================================================================
@import "components/heading";
@import "components/text";
@import "components/button";
@import "components/form";
@import "components/modal";
// Utilities
// ==========================================================================

View File

@@ -45,8 +45,8 @@
grid-template-columns: repeat(4, 1fr);
}
&.-col-#{$base-column-nb}\@from-medium {
@media (min-width: $from-medium) {
&.-col-#{$base-column-nb}\@from-md {
@media (min-width: $from-md) {
grid-template-columns: repeat(#{$base-column-nb}, 1fr);
}
}

View File

@@ -48,7 +48,7 @@
// // Logo
// .svg-logo {
// --icon-width: #{rem(100px)};
// --icon-ratio: 20/30; // width/height based on svg viewBox
// --icon-ratio: math.div(20, 30); // width/height based on svg viewBox
// // Sizes
// .o-icon.-big & {

View File

@@ -6,15 +6,16 @@
// ==========================================================================
$breakpoints: (
"tiny": 500px,
"small": 700px,
"medium": 1000px,
"large": 1200px,
"big": 1400px,
"huge": 1600px,
"enormous": 1800px,
"gigantic": 2000px,
"colossal": 2400px
"2xs": 340px,
"xs": 500px,
"sm": 700px,
"md": 1000px,
"lg": 1200px,
"xl": 1400px,
"2xl": 1600px,
"3xl": 1800px,
"4xl": 2000px,
"5xl": 2400px
);
// Functions
@@ -75,21 +76,17 @@ $breakpoints: (
// Legacy
// ==========================================================================
$from-tiny: map-get($breakpoints, "tiny") !default;
$to-tiny: map-get($breakpoints, "tiny") - 1 !default;
$from-small: map-get($breakpoints, "small") !default;
$to-small: map-get($breakpoints, "small") - 1 !default;
$from-medium: map-get($breakpoints, "medium") !default;
$to-medium: map-get($breakpoints, "medium") - 1 !default;
$from-large: map-get($breakpoints, "large") !default;
$to-large: map-get($breakpoints, "large") - 1 !default;
$from-big: map-get($breakpoints, "big") !default;
$to-big: map-get($breakpoints, "big") - 1 !default;
$from-huge: map-get($breakpoints, "huge") !default;
$to-huge: map-get($breakpoints, "huge") - 1 !default;
$from-enormous: map-get($breakpoints, "enormous") !default;
$to-enormous: map-get($breakpoints, "enormous") - 1 !default;
$from-gigantic: map-get($breakpoints, "gigantic") !default;
$to-gigantic: map-get($breakpoints, "gigantic") - 1 !default;
$from-colossal: map-get($breakpoints, "colossal") !default;
$to-colossal: map-get($breakpoints, "colossal") - 1 !default;
$from-xs: map-get($breakpoints, "xs") !default;
$to-xs: map-get($breakpoints, "xs") - 1 !default;
$from-sm: map-get($breakpoints, "sm") !default;
$to-sm: map-get($breakpoints, "sm") - 1 !default;
$from-md: map-get($breakpoints, "md") !default;
$to-md: map-get($breakpoints, "md") - 1 !default;
$from-lg: map-get($breakpoints, "lg") !default;
$to-lg: map-get($breakpoints, "lg") - 1 !default;
$from-xl: map-get($breakpoints, "xl") !default;
$to-xl: map-get($breakpoints, "xl") - 1 !default;
$from-2xl: map-get($breakpoints, "2xl") !default;
$to-2xl: map-get($breakpoints, "2xl") - 1 !default;
$from-3xl: map-get($breakpoints, "3xl") !default;
$to-3xl: map-get($breakpoints, "3xl") - 1 !default;

View File

@@ -32,12 +32,13 @@ $easing: ease("power2.out");
// Spacing Units
// =============================================================================
$unit: 60px;
$unit-small: 20px;
$unit: 60px;
$unit-small: 20px;
$vw-viewport: 1440;
// Container
// ==========================================================================
$padding: $unit;
$padding: $unit;
// Grid
// ==========================================================================

View File

@@ -1,40 +0,0 @@
// ==========================================================================
// Settings / Config / Spacers
// ==========================================================================
// Spacers
// ==========================================================================
$spacers: (
'gutter': var(--grid-gutter),
'xs': #{vh(5)},
'sm': #{vh(7.5)},
'md': #{vh(10)},
'lg': #{vh(12.5)},
'xl': #{vh(15)},
);
// Function
// ==========================================================================
// Returns spacer.
//
// ```scss
// .c-box {
// margin-top: spacer(gutter);
// }
// ```
//
// @param {string} $key - The spacer key in $spacers.
// @param {number} $multiplier - The multiplier of the spacer value.
// @return {size}
@function spacer($spacer: $spacer-default, $multiplier: 1) {
@if not map-has-key($spacers, $spacer) {
@error "Unknown master spacer: #{$spacer}";
}
$index: map-get($spacers, $spacer);
@return calc(#{$index} * #{$multiplier});
}

View File

@@ -0,0 +1,69 @@
// ==========================================================================
// Settings / Config / Spacings
// ==========================================================================
:root {
--spacing-2xs-mobile: 6;
--spacing-2xs-desktop: 10;
--spacing-xs-mobile: 12;
--spacing-xs-desktop: 16;
--spacing-sm-mobile: 22;
--spacing-sm-desktop: 32;
--spacing-md-mobile: 32;
--spacing-md-desktop: 56;
--spacing-lg-mobile: 48;
--spacing-lg-desktop: 96;
--spacing-xl-mobile: 64;
--spacing-xl-desktop: 128;
--spacing-2xl-mobile: 88;
--spacing-2xl-desktop: 176;
--spacing-3xl-mobile: 122;
--spacing-3xl-desktop: 224;
}
// Spacings
// ==========================================================================
$spacings: (
'gutter': var(--grid-gutter),
'2xs': #{size-clamp('2xs')},
'xs': #{size-clamp('xs')},
'sm': #{size-clamp('sm')},
'md': #{size-clamp('md')},
'lg': #{size-clamp('lg')},
'xl': #{size-clamp('xl')},
'2xl': #{size-clamp('2xl')},
'3xl': #{size-clamp('3xl')},
);
// Function
// ==========================================================================
// Returns spacing.
//
// ```scss
// .c-box {
// margin-top: spacing(gutter);
// }
// ```
//
// @param {string} $key - The spacing key in $spacings.
// @param {number} $multiplier - The multiplier of the spacing value.
// @return {size}
@function spacing($spacing: 'sm', $multiplier: 1) {
@if not map-has-key($spacings, $spacing) {
@error "Unknown master spacing: #{$spacing}";
}
$index: map-get($spacings, $spacing);
@return calc(#{$index} * #{$multiplier});
}

View File

@@ -1,23 +1,20 @@
// ==========================================================================
// Settings / Config / Timings
// Settings / Config / Speeds
// ==========================================================================
// Timings
// Speeds
// ==========================================================================
$timings: (
$speeds: (
fastest: 0.1s,
faster: 0.15s,
fast: 0.25s,
normal: 0.5s,
slow: 0.75s,
slower: 1s,
slowest: 2s,
normal: 0.3s,
slow: 0.5s,
slower: 0.75s,
slowest: 1s,
);
// Default timing for t()
$timing-default: "normal" !default;
// Function
// ==========================================================================
@@ -25,17 +22,17 @@ $timing-default: "normal" !default;
//
// ```scss
// .c-box {
// transition-duration: t(slow);
// transition-duration: speed(slow);
// }
// ```
//
// @param {string} $key - The timing key in $timings.
// @param {string} $key - The speed key in $speeds.
// @return {duration}
@function t($key: $timing-default) {
@if not map-has-key($timings, $key) {
@error "Unknown '#{$key}' in $timings.";
@function speed($key: "normal") {
@if not map-has-key($speeds, $key) {
@error "Unknown '#{$key}' in $speeds.";
}
@return map-get($timings, $key);
@return map-get($speeds, $key);
}

View File

@@ -12,20 +12,7 @@
// Container
--container-width: calc(100% - 2 * var(--grid-margin));
// Font sizes
--font-size-h1: #{responsive-type(36px, 72px, 1400px)};
--font-size-h2: #{rem(28px)};
--font-size-h3: #{rem(24px)};
--font-size-h4: #{rem(20px)};
--font-size-h5: #{rem(18px)};
--font-size-h6: #{rem(16px)};
// // Colors
// @each $color, $value in $colors {
// --color-#{"" + $color}: #{$value};
// }
@media (min-width: $from-small) {
@media (min-width: $from-sm) {
--grid-columns: #{$base-column-nb};
--grid-gutter: #{rem(16px)};
--grid-margin: #{rem(20px)};

View File

@@ -48,17 +48,6 @@
@return math.div($size, $base) * 1rem;
}
// Converts a number to a percentage.
//
// @alias percentage()
// @link http://sassdoc.com/annotations/#alias
// @param {Number} $number - The value to convert.
// @return {Number} A percentage.
@function span($number) {
@return percentage($number);
}
// Checks if a list contains a value(s).
//
// @link https://github.com/thoughtbot/bourbon/blob/master/core/bourbon/validators/_contains.scss
@@ -125,11 +114,11 @@ $context: 'frontend' !default;
// }
// ```
//
// @param {number} $number - The percentage spacer
// @param {number} $inset - The grid gutter inset
// @param {number} $percentage - The percentage spacer
// @param {number} $inset - The grid gutter inset
// @return {function<number>}
@function grid-space($percentage, $inset: 0) {
@return calc(#{$percentage} * (100vw - 2 * var(--grid-margin, 0px)) - (1 - #{$percentage}) * var(--grid-gutter, 0px) + #{$inset} * var(--grid-gutter, 0px));
@return calc(#{$percentage} * (#{vw(100)} - 2 * var(--grid-margin, 0px)) - (1 - #{$percentage}) * var(--grid-gutter, 0px) + #{$inset} * var(--grid-gutter, 0px));
}
// Returns calculation of a percentage of the viewport small height.
@@ -189,16 +178,29 @@ $context: 'frontend' !default;
@return calc(#{$number} * var(--vw, 1vw));
}
@function clamp-with-max($min, $size, $max) {
$vw-context: $vw-viewport * 0.01;
@return clamp(#{$min}, calc(#{$size} / #{$vw-context} * 1vw), #{$max});
}
@function size-clamp($size) {
@return clamp-with-max(
calc(#{rem(1px)} * var(--spacing-#{$size}-mobile)),
var(--spacing-#{$size}-desktop),
calc(#{rem(1px)} * var(--spacing-#{$size}-desktop))
);
}
// Returns clamp of calculated preferred responsive font size
// within a font size and breakpoint range.
//
// ```scss
// .c-heading.-h1 {
// font-size: responsive-type(30px, 60px, 1800);
// font-size: responsive-value(30px, 60px, 1800);
// }
//
// .c-heading.-h2 {
// font-size: responsive-type(20px, 40px, $from-big);
// font-size: responsive-value(20px, 40px, $from-xl);
// }
// ```
//
@@ -206,7 +208,7 @@ $context: 'frontend' !default;
// @param {number} $max-size - Maximum font size in pixels.
// @param {number} $breakpoint - Maximum breakpoint.
// @return {function<number, function<number>, number>}
@function responsive-type($min-size, $max-size, $breakpoint) {
@function responsive-value($min-size, $max-size, $breakpoint) {
$delta: math.div($max-size, $breakpoint);
@return clamp($min-size, calc(#{strip-unit($delta)} * #{vw(100)}), $max-size);
}

View File

@@ -8,12 +8,11 @@
///
/// @example
/// .u-margin-top {}
/// .u-padding-left-large {}
/// .u-margin-right-small {}
/// .u-margin-top-xs {}
/// .u-padding-left-lg {}
/// .u-margin-right-sm {}
/// .u-padding {}
/// .u-padding-right-none {}
/// .u-padding-horizontal {}
/// .u-padding-vertical-small {}
///
/// @link https://github.com/inuitcss/inuitcss/blob/512977a/utilities/_utilities.spacing.scss
////
@@ -35,7 +34,7 @@ $spacing-properties: (
'margin': 'margin',
) !default;
$spacing-sizes: join($spacers, (
$spacing-sizes: join($spacings, (
null: var(--grid-gutter),
'none': 0
));
@@ -51,8 +50,8 @@ $spacing-sizes: join($spacers, (
// Base class
$base-class: ".u-" + #{$property-namespace}#{$direction-namespace}#{$size-namespace};
// Spacer without media query
@if $breakpoint == "tiny" {
// Spacing without media query
@if $breakpoint == "xs" {
#{$base-class} {
@each $direction in $directions {
#{$property}#{$direction}: $size !important;
@@ -60,7 +59,7 @@ $spacing-sizes: join($spacers, (
}
}
// Spacer min-width breakpoints `@from-*`
// Spacing min-width breakpoints `@from-*`
#{$base-class}\@from-#{$breakpoint} {
@media #{mq-min($breakpoint)} {
@each $direction in $directions {
@@ -69,7 +68,7 @@ $spacing-sizes: join($spacers, (
}
}
// Spacer max-width breakpoints @to-*`
// Spacing max-width breakpoints @to-*`
#{$base-class}\@to-#{$breakpoint} {
@media #{mq-max($breakpoint)} {
@each $direction in $directions {

View File

@@ -46,14 +46,14 @@
}
}
// .is-hidden\@to-large {
// @media (max-width: $to-large) {
// .is-hidden\@to-lg {
// @media (max-width: $to-lg) {
// display: none;
// }
// }
//
// .is-hidden\@from-large {
// @media (min-width: $from-large) {
// .is-hidden\@from-lg {
// @media (min-width: $from-lg) {
// display: none;
// }
// }

View File

@@ -21,8 +21,8 @@ $widths-fractions: 1 2 3 4 5 !default;
@include widths($widths-fractions);
.u-1\/2\@from-small {
@media (min-width: $from-small) {
.u-1\/2\@from-sm {
@media (min-width: $from-sm) {
width: 50%;
}
}

View File

@@ -2,14 +2,14 @@
* @file Provides simple user configuration options.
*/
import loconfig from '../../loconfig.json' assert { type: 'json' };
import loconfig from '../../loconfig.json' with { type: 'json' };
import { merge } from '../utils/index.js';
let usrconfig;
try {
usrconfig = await import('../../loconfig.local.json', {
assert: { type: 'json' },
with: { type: 'json' },
});
usrconfig = usrconfig.default;

View File

@@ -1,21 +1,36 @@
import loconfig from '../helpers/config.js';
import glob, { supportsGlob } from '../helpers/glob.js';
import message from '../helpers/message.js';
import notification from '../helpers/notification.js';
import resolve from '../helpers/template.js';
import { resolve as resolveTemplate } from '../helpers/template.js';
import { merge } from '../utils/index.js';
import { basename } from 'node:path';
import {
basename,
dirname,
extname,
resolve,
} from 'node:path';
import commonPath from 'common-path';
import mixer from 'svg-mixer';
import slugify from 'url-slug';
const basePath = loconfig?.paths?.svgs?.src
? resolve(loconfig.paths.svgs.src)
: null;
/**
* @const {object} defaultMixerOptions - The default shared Mixer options.
* @const {object} developmentMixerOptions - The predefined Mixer options for development.
* @const {object} productionMixerOptions - The predefined Mixer options for production.
* @const {object} defaultMixerOptions - The default shared Mixer options.
*/
export const defaultMixerOptions = {
spriteConfig: {
usages: false,
},
};
/**
* @const {object} developmentMixerOptions - The predefined Mixer options for development.
* @const {object} productionMixerOptions - The predefined Mixer options for production.
*/
export const developmentMixerOptions = Object.assign({}, defaultMixerOptions);
export const productionMixerOptions = Object.assign({}, defaultMixerOptions);
@@ -74,10 +89,52 @@ export default async function compileSVGs(mixerOptions = null) {
includes = [ includes ];
}
includes = resolve(includes);
outfile = resolve(outfile);
includes = resolveTemplate(includes);
outfile = resolveTemplate(outfile);
const result = await mixer(includes, mixerOptions);
if (supportsGlob && basePath) {
includes = await glob(includes);
includes = [ ...new Set(includes) ];
const common = commonPath(includes);
if (common.commonDir) {
common.commonDir = resolve(common.commonDir);
}
/**
* Generates the `<symbol id>` attribute and prefix any
* SVG files in subdirectories according to the paths
* common base path.
*
* Example for SVG source path `./assets/images/sprite`:
*
* | Path | ID |
* | ------------------------------------ | --------- |
* | `./assets/images/sprite/foo.svg` | `foo` |
* | `./assets/images/sprite/baz/qux.svg` | `baz-qux` |
*
* @param {string} path - The absolute path to the file.
* @param {string} [query=''] - A query string.
* @return {string} The symbol ID.
*/
mixerOptions.generateSymbolId = (path, query = '') => {
let dirName = dirname(path)
.replace(common.commonDir ?? basePath, '')
.replace(/^\/|\/$/, '')
.replace('/', '-');
if (dirName) {
dirName += '-';
}
const fileName = basename(path, extname(path));
const decodedQuery = decodeURIComponent(decodeURIComponent(query));
return `${dirName}${fileName}${slugify(decodedQuery)}`;
};
}
const result = await mixer(includes, {
...mixerOptions,
});
await result.write(outfile);

View File

@@ -70,8 +70,8 @@ The first step is to set intial SCSS values in the following files :
grid-template-columns: repeat(4, 1fr);
}
&.-col-#{$base-column-nb}\@from-medium {
@media (min-width: $from-medium) {
&.-col-#{$base-column-nb}\@from-md {
@media (min-width: $from-md) {
grid-template-columns: repeat(#{$base-column-nb}, 1fr);
}
}

View File

@@ -80,10 +80,10 @@ Learn about [namespacing](https://csswizardry.com/2015/03/more-transparent-ui-co
}
.c-block_heading {
@media (max-width: $to-medium) {
@media (max-width: $to-md) {
.c-block.-large & {
margin-bottom: rem(40px);
}
}
}
}
```

View File

@@ -15,7 +15,7 @@
"dest": "./www/assets/scripts"
},
"svgs": {
"src": "./assets/images/sprite",
"src": "./assets/svgs",
"dest": "./www/assets/images"
},
"views": {

4209
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,39 +6,44 @@
"author": "Locomotive <info@locomotive.ca>",
"type": "module",
"engines": {
"node": ">=20.1",
"npm": ">=8.0"
"node": ">=20",
"npm": ">=10"
},
"scripts": {
"start": "node --experimental-json-modules --no-warnings build/watch.js",
"build": "node --experimental-json-modules --no-warnings build/build.js"
"start": "node --no-warnings build/watch.js",
"build": "node --no-warnings build/build.js"
},
"dependencies": {
"locomotive-scroll": "^5.0.0-beta.11",
"focus-trap": "^7.5.4",
"locomotive-scroll": "^5.0.0-beta.13",
"modujs": "^1.4.2",
"modularload": "^1.2.6",
"normalize.css": "^8.0.1",
"svg4everybody": "^2.1.9"
},
"devDependencies": {
"autoprefixer": "^10.4.17",
"autoprefixer": "^10.4.19",
"browser-sync": "^3.0.2",
"common-path": "^1.0.1",
"concat": "^1.0.3",
"esbuild": "^0.20.0",
"esbuild": "^0.21.5",
"kleur": "^4.1.5",
"node-notifier": "^10.0.1",
"postcss": "^8.4.21",
"purgecss": "^5.0.0",
"sass": "^1.70.0",
"svg-mixer": "~2.3.14",
"postcss": "^8.4.38",
"purgecss": "^6.0.0",
"sass": "^1.77.6",
"svg-mixer": "^2.3.14",
"tiny-glob": "^0.2.9"
},
"overrides": {
"browser-sync": {
"ua-parser-js": "~1.0.33"
"ua-parser-js": "^1.0.33"
},
"svg-mixer": {
"postcss": "^8.4.20"
"micromatch": "^4.0.4",
"postcss": "^8.4.38"
},
"svg-mixer-utils": {
"anymatch": "^3.1.3"
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"sources":[],"names":[],"mappings":"","file":"critical.css"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -96,9 +96,6 @@
</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>

View File

@@ -83,12 +83,6 @@
</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>

View File

@@ -122,9 +122,6 @@
</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>

View File

@@ -59,6 +59,7 @@
<main data-module-example>
<div class="o-container">
<h1 class="c-heading -h1">Hello</h1>
<button data-modal-toggler>Open modal</button>
</div>
</main>
@@ -66,15 +67,21 @@
<p>Made with <a href="https://github.com/locomotivemtl/locomotive-boilerplate"
title="Locomotive Boilerplate" target="_blank" rel="noopener">🚂</a></p>
</footer>
<div class="c-modal" data-module-modal>
<div class="c-modal_inner" data-modal-target>
<button data-modal-toggler>Close</button>
<h2>Modal</h2>
<p>Content</p>
<form action="">
<input type="text">
</form>
</div>
</div>
</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>