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

6 Commits

Author SHA1 Message Date
Deven Caron
6f04e21146 Update README.md 2025-07-24 10:41:30 -04:00
Lucas Vallenet
97b0d57dbd Upgrade packages 2025-07-24 11:37:10 +02:00
Chauncey McAskill
c434d0843f Remove svg4everybody 2025-01-07 13:17:41 -05:00
Chauncey McAskill
5f8767f04d Update NPM dependencies
Updated:
- esbuild v0.24.0 → v0.24.2
- postcss v8.4.47 → v8.4.49
- purgecss v6.0.0 → v7.0.2
2025-01-06 12:11:38 -05:00
Chauncey McAskill
b4ee0955c3 Update NPM dependencies
Updated:
- autoprefixer v10.4.19 → v10.4.20
- esbuild v0.21.5 → v0.21.5
- locomotive-scroll v5.0.0-beta.13 → v5.0.0-beta.21
- micromatch v4.0.4 → v4.0.8
- postcss v8.4.38 → v8.4.47
- svg-mixer v2.4.0 → v2.3.14
2024-10-16 10:54:56 -04:00
Michel Descoteaux
1ec1229fe4 Upgrade sass package to v1.79 (#183)
* Update sass to v1.79

* Fix broken node_modules import

* Fix conflict with new sass color() function

color() -> colorCode()

* build:styles - Remove deprecated sass.render()

* build:styles - remove infile + outfile from sass.compile options
2024-10-16 10:07:10 -04:00
16 changed files with 2685 additions and 4036 deletions

View File

@@ -1,3 +1,6 @@
> [!WARNING]
> This repository is no longer maintained. We recommend checking out our [Astro](https://github.com/locomotivemtl/astro-boilerplate) or [Craft](https://github.com/locomotivemtl/craft-boilerplate) boilerplates instead.
<p align="center">
<a href="https://github.com/locomotivemtl/locomotive-boilerplate">
<img src="https://user-images.githubusercontent.com/4596862/54868065-c2aea200-4d5e-11e9-9ce3-e0013c15f48c.png" height="140">

View File

@@ -46,8 +46,6 @@ 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,4 +1,3 @@
import svg4everybody from 'svg4everybody';
import { ENV } from './config';
// Dynamic imports for development mode only
@@ -11,11 +10,6 @@ let gridHelper;
})();
export default function () {
/**
* Use external SVG spritemaps
*/
svg4everybody();
/**
* Add grid helper
*/

View File

@@ -1,4 +1,3 @@
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,6 +1,5 @@
import { module } from 'modujs';
import modularLoad from 'modularload';
import { CUSTOM_EVENT } from '../config';
export default class extends module {
constructor(m) {
@@ -15,12 +14,6 @@ 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

@@ -1,195 +0,0 @@
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

@@ -27,7 +27,7 @@ $input-icon-color: 424242; // No #
.c-form_input {
padding: rem(10px);
border: 1px solid lightgray;
background-color: color(lightest);
background-color: colorCode(lightest);
&:hover {
border-color: darkgray;
@@ -71,7 +71,7 @@ $checkbox-icon-color: $input-icon-color;
}
&::before {
background-color: color(lightest);
background-color: colorCode(lightest);
border: 1px solid lightgray;
}

View File

@@ -1,52 +0,0 @@
// ==========================================================================
// 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

@@ -25,7 +25,7 @@
position: absolute;
top: 0;
right: 0;
background-color: color(darkest);
background-color: colorCode(darkest);
opacity: 0.5;
width: 7px;
border-radius: 10px;

View File

@@ -32,7 +32,7 @@
// Vendors
// ==========================================================================
@import "node_modules/locomotive-scroll/dist/locomotive-scroll";
@import "../../node_modules/locomotive-scroll/dist/locomotive-scroll";
// Elements
// ==========================================================================
@@ -57,7 +57,6 @@
@import "components/text";
@import "components/button";
@import "components/form";
@import "components/modal";
// Utilities
// ==========================================================================

View File

@@ -1,3 +1,5 @@
@use 'sass:color';
// ==========================================================================
// Settings / Config / Colors
// ==========================================================================
@@ -18,7 +20,7 @@ $colors: (
//
// ```scss
// .c-box {
// color: color(primary);
// color: colorCode(primary);
// }
// ```
//
@@ -26,7 +28,7 @@ $colors: (
// @param {number} $alpha - The alpha for the color value.
// @return {color}
@function color($key, $alpha: 1) {
@function colorCode($key, $alpha: 1) {
@if not map-has-key($colors, $key) {
@error "Unknown '#{$key}' in $colors.";
}
@@ -44,13 +46,13 @@ $colors: (
// ==========================================================================
// Link
$color-link: color(primary);
$color-link-focus: color(primary);
$color-link-hover: darken(color(primary), 10%);
$color-link: colorCode(primary);
$color-link-focus: colorCode(primary);
$color-link-hover: color.adjust(colorCode(primary), $lightness: -10%);
// Selection
$color-selection-text: color(darkest);
$color-selection-background: color(lightest);
$color-selection-text: colorCode(darkest);
$color-selection-background: colorCode(lightest);
// Socials
$color-facebook: #3B5998;

View File

@@ -17,7 +17,7 @@ $assets-path: "../" !default;
// Base
$font-size: 16px;
$line-height: math.div(24px, $font-size);
$font-color: color(darkest);
$font-color: colorCode(darkest);
// Weights
$font-weight-light: 300;

View File

@@ -10,12 +10,9 @@ import resolve from '../helpers/template.js';
import { merge } from '../utils/index.js';
import { writeFile } from 'node:fs/promises';
import { basename } from 'node:path';
import { promisify } from 'node:util';
import * as sass from 'sass';
import { PurgeCSS } from 'purgecss';
const sassRender = promisify(sass.render);
let postcssProcessor;
/**
@@ -24,16 +21,15 @@ let postcssProcessor;
* @const {object} productionSassOptions - The predefined Sass options for production.
*/
export const defaultSassOptions = {
omitSourceMapUrl: true,
sourceMapIncludeSources: true,
sourceMap: true,
sourceMapContents: true,
};
export const developmentSassOptions = Object.assign({}, defaultSassOptions, {
outputStyle: 'expanded',
style: 'expanded',
});
export const productionSassOptions = Object.assign({}, defaultSassOptions, {
outputStyle: 'compressed',
style: 'compressed',
});
/**
@@ -127,10 +123,7 @@ export default async function compileStyles(sassOptions = null, postcssOptions =
infile = resolve(infile);
outfile = resolve(outfile);
let result = await sassRender(Object.assign({}, sassOptions, {
file: infile,
outFile: outfile,
}));
let result = sass.compile(infile, sassOptions);
if (supportsPostCSS && postcssOptions) {
if (typeof postcssProcessor === 'undefined') {

6378
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,23 +14,21 @@
"build": "node --no-warnings build/build.js"
},
"dependencies": {
"focus-trap": "^7.5.4",
"locomotive-scroll": "^5.0.0-beta.13",
"locomotive-scroll": "^5.0.0-beta.21",
"modujs": "^1.4.2",
"modularload": "^1.2.6",
"svg4everybody": "^2.1.9"
"modularload": "^1.2.6"
},
"devDependencies": {
"autoprefixer": "^10.4.19",
"browser-sync": "^3.0.2",
"autoprefixer": "^10.4.21",
"browser-sync": "^3.0.4",
"common-path": "^1.0.1",
"concat": "^1.0.3",
"esbuild": "^0.21.5",
"esbuild": "^0.25.8",
"kleur": "^4.1.5",
"node-notifier": "^10.0.1",
"postcss": "^8.4.38",
"purgecss": "^6.0.0",
"sass": "^1.77.6",
"postcss": "^8.5.6",
"purgecss": "^7.0.2",
"sass": "^1.89.2",
"svg-mixer": "^2.3.14",
"tiny-glob": "^0.2.9"
},
@@ -39,8 +37,8 @@
"ua-parser-js": "^1.0.33"
},
"svg-mixer": {
"micromatch": "^4.0.4",
"postcss": "^8.4.38"
"micromatch": "^4.0.8",
"postcss": "^8.4.49"
},
"svg-mixer-utils": {
"anymatch": "^3.1.3"

View File

@@ -59,7 +59,6 @@
<main data-module-example>
<div class="o-container">
<h1 class="c-heading -h1">Hello</h1>
<button data-modal-toggler>Open modal</button>
</div>
</main>
@@ -67,18 +66,6 @@
<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>