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

146 Commits

Author SHA1 Message Date
Chauncey McAskill
f400ca9622 Add Modal.js
Add accessible modal dialog component.

Features:
- Dialog supports being shown on page load via `data-modal-autoshow` attribute.
- Dialog supports being shown at most once via the `data-modal-show-once` attribute.
- Dialog persists dismissal via `localStorage`, by default. This can be switched to `sessionStoage` via `data-modal-show-once="session"` attribute value.
- When Load handles "loading" event, executes `Modal.hide` on all shown modals.

Required:
- NPM dependency a11y-dialog v7.3.0

Added:
- JS class `Modal` that uses and enhances a11y-dialog.
- CSS component `.c-dialog` via partial '_modal.scss' with basic styles.
- Basic HTML example of `data-module-modal`.

Note:
- Dialog styles adapted from https://codesandbox.io/s/a11y-dialog-pnwqu?file=/src/styles.css
2021-11-01 16:51:33 -04:00
Chauncey McAskill
1898a94373 Add overlay z-index scale and function
Added ordered z-index map function to improve incrementation of page layout z-indexes to mitigate complicated and exagerated z-index numbers across components.

Based on OZMap: https://rafistrauss.com/blog/ordered_z_index_maps

Usage:

```scss
.c-site-header_container {
  z-index: overlay-zindex(fixed);
}

.c-dialog_container {
  z-index: overlay-zindex(modal);
}
```

Added:
- Default scale: dropdown, sticky, fixed, offcanvas-backdrop, offcanvas, modal, popover, tooltip, transition.
- Function `overlay-zindex()` to fetch the z-index for the corresponding element.
2021-11-01 16:07:30 -04:00
Chauncey McAskill
2a97183d39 Revert NPM lock file version 2
Amends 2316219201
2021-10-29 11:26:12 -04:00
Deven Caron
2316219201 Update Locomotive Scroll package 2021-10-27 10:34:37 -04:00
Deven Caron
d99a69f212 Fix undefined target on LS instance lazyLoad args 2021-10-27 10:33:44 -04:00
Deven Caron
8bc79f715e Merge pull request #97 from locomotivemtl/devenini-add-lazy-loading-utils
Add lazy loading utils & implement in Scroll.js
2021-10-20 14:46:51 -04:00
Deven Caron
62601f22ed Build assets 2021-10-19 14:49:56 -04:00
Deven Caron
082f3b5827 Replace queryClosestParent with Element.prototype.closest 2021-10-19 14:46:38 -04:00
Deven Caron
df567220d5 Syntax changes & coding style 2021-10-19 14:45:24 -04:00
Deven Caron
7d35dcbf28 Precise usage & better comments 2021-10-19 14:41:43 -04:00
Deven Caron
8b40b1a92e build assets 2021-10-18 16:45:40 -04:00
Deven Caron
f2b657568a Add image lazyloading utils 2021-10-18 16:45:31 -04:00
Deven Caron
a28848e8aa Remove toggleLazy call
Adapt lazy-load markup
Add svg viewbox ratio example
2021-10-18 16:44:41 -04:00
Deven Caron
b881003705 Fix stylesheet wrong id 2021-10-18 16:42:36 -04:00
Chauncey McAskill
b55e625457 Improve notification.js
Added:
- Support for passing a callback to node-notifier.
- Shorter timeout on notifications (~12 s → ~5 s).
2021-10-12 16:12:29 -04:00
Chauncey McAskill
25ef6675af Fix syntax of styles.js
Amends 7df0481d05
2021-10-12 16:04:29 -04:00
Chauncey McAskill
7df0481d05 Change writeFile promises in styles.js
Await results of `writeFile()` calls for easier readability of operations.
2021-10-12 12:52:36 -04:00
Chauncey McAskill
e53efd6ebc Update NPM dependencies
Updated:
- autoprefixer v10.3.6 → v10.3.7
- esbuild v0.13.3 → v0.13.4
- postcss v8.3.8 → v8.3.9

Added:
- npm v6.0
2021-10-12 12:52:35 -04:00
Chauncey McAskill
14ec69f26d Update NPM dependencies
Updated:
- autoprefixer v10.3.5 → v10.3.6
- esbuild v0.13.0 → v0.13.3
- postcss v8.3.7 → v8.3.8
2021-10-01 14:12:37 -04:00
Chauncey McAskill
fa8aa98595 Add missing constraints to NPM dependencies
Updated:
- locomotive-scroll v4.1.2
- normalize.css v8.0.1
- svg4everybody v2.1.9
2021-10-01 14:04:27 -04:00
Chauncey McAskill
b24b4e10c4 Update NPM manifest 2021-10-01 14:03:41 -04:00
Chauncey McAskill
21f6acf4a6 Update NPM dependencies
Updated:
- autoprefixer v10.3.4 → v10.3.5
- esbuild v0.12.28 → v0.13.0
- postcss v8.3.6 → v8.3.7
2021-09-22 13:26:28 -04:00
Chauncey McAskill
bbbb49f30b Merge pull request #95 from locomotivemtl/mcaskill-refactor-build
Refactor build tasks and config file
2021-09-21 17:58:47 -04:00
Chauncey McAskill
99e1b3fa93 Change tasks to be the default exports 2021-09-21 17:53:46 -04:00
Chauncey McAskill
548b2c604b Fix messages in styles.js
Fixed:
- Expected file extension from `outfile` for variable `name`.
- Expected structure of `err` in main catch (`err.formatted` is only available when thrown by node-sass).
- Context of error messages when writing final CSS and source map to disk.
2021-09-21 17:53:46 -04:00
Chauncey McAskill
ec9228a337 Refactor glob.js
Added function `importGlob()` to enclose operations and improve error handling.
2021-09-21 17:52:32 -04:00
Chauncey McAskill
8dec2c69fe Improve syntax of glob.js
Changed:
- Renamed variables for clarity.

Fixed:
- Block comment for `createArrayableGlob()`.
2021-09-21 17:52:17 -04:00
Chauncey McAskill
3a65683fd8 Compile assets 2021-09-21 17:52:17 -04:00
Chauncey McAskill
1a2cc7b6ac Fix bad paths in watch.js
Amends d4ded2a64e

Fixed:
- Invalid paths to watch.

Changed:
- Replaced risky string concatenation with Node.js' `path.join()` function.
2021-09-20 17:29:41 -04:00
Chauncey McAskill
e7935211cd Replace non-breaking space in JS imports 2021-09-20 10:54:56 -04:00
Chauncey McAskill
7e8a21f698 Update README and Boilerplate occurrences
Changed "Configuration" section to instruct developers on what occurrences to rename throughout the package.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
7e30939b14 Update Node constraint in README
Amends 1203e54277
2021-09-18 01:16:44 -04:00
Chauncey McAskill
655031cd1b Clean-up README 2021-09-18 01:16:44 -04:00
Chauncey McAskill
d4ded2a64e Refactor build tasks and config file
Changed:
- Renamed 'mconfig.json' to 'loconfig.json'.
- Renamed 'concat.js' to 'concats.js' to represent flexible functionality.
- loconfig.json: Base paths are nested under "paths".
- loconfig.json: Paths for tasks are nested under "tasks".
- Refactored each task to process corresponding entries under "tasks" in 'loconfig.json'.
- watch.js: Changed concats watch to use task's includes.

Added:
- tiny-glob v0.2.9
- Utility 'glob.js' to use dynamic imports to fetch an available glob function from node modules.
- Utility 'template.js' to provide a function to render template tags (`{% ... %}`) in tasks.
- concats.js: Support for concatenating groupes of files.
- scripts.js: Support for ESBuild's "outdir" option.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
589ec99135 Move tasks to a dedicated directory
Moved concat.js, scripts.js, styles.js, svgs.js to 'build/tasks/' directory.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
84286fef66 Improve notification.js
Added:
- Block comment
- Support for additional node-notifier properties.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
6e50bc202d Refactor styles.js
Changed:
- Replaced "fs" callback API with "fs/promises" promise API to centralize catching of errors and easier readability of file.
- Prefixed "fs/promises" with 'node:' URI scheme to target Node.js builtin modules.
- Promisified `sass.render`.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
91109c5221 Refactor concat.js
Changed:
- Replaced "fs" callback API with "fs/promises" promise API to centralize catching of errors and easier readability of file.
- Prefixed "fs/promises" with 'node:' URI scheme to target Node.js builtin modules.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
834c6165b0 Refactor postcss.js
Decoupled import routine from final export to provide a shared instance of the PostCSS processor and for easier readability of file.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
ffece71aac Improve message.js
Added:
- Types "notice" and "warning".
- Support for all but the "waiting" message type to stop a timer.

Changed:
- Improved colors of message types.
- Improved handling of PostCSS errors in styles.js.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
454ae64d07 Make postcss optional in styles.js
Added:
- Utility 'postcss.js' to use dynamic imports to fetch PostCSS and Autoprefixer, if available, and build the Processor object.
- Function `saveStylesheet()` in 'styles.js' to decouple the writing of CSS files and source maps.
- Condition to process with PostCSS if it's available.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
7479444572 Change tasks to async functions 2021-09-18 01:16:44 -04:00
Chauncey McAskill
b46fb31dfe Fix logic for PostCSS processing
Amends 3874d9c451674a9b4f76239a17b4cbf50cbdb9d3

The `fs.access()` condition is unnecessary and won't work if a CSS stylesheet does not exist.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
23e55d6340 Move notification.js to utils directory 2021-09-18 01:16:44 -04:00
Chauncey McAskill
70827b0a7d Add semi-colon to exported functions 2021-09-18 01:16:44 -04:00
Chauncey McAskill
8cff91aa68 Fix syntax typo in concat.js
Amends a55e6e523db4b4c932d214d3e204fa29d9c0e58f
2021-09-18 01:16:44 -04:00
Chauncey McAskill
b24014d8b1 Add autoprefixer and postcss
Added:
- autoprefixer v10.3.4
- postcss v8.3.6

Changed:
- Node Sass options to pass source map to PostCSS.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
e9e0e5784e Refactor build tasks
Decouple static "main" keys in mconfig.json and refactor tasks to support many output files.

Added:
- scripts.js: Array of output files (such as 'app.js') to iterate over to bundle JS files.
- styles.js: Array of input files (such as 'main.css' and 'critical.css') to iterate over to compile Sass files.
- svgs.js: Array of output files (such as 'sprite.svg') to iterate over to compile SVG spritesheets.

Changed:
- mconfig.json: Decouple entry points to individual tasks to allow for more flexibility in projects.
- concat.js: Refactor function to use promises to build list of JS files to concatenate.
- message.js: Replace if statements with switch for improved readability.
- message.js: If timerID provided with "waiting" type, log time.
- watch.js: Change CSS and JS reload watch paths to include all files.
- Sorted imports by path.
2021-09-18 01:16:44 -04:00
Chauncey McAskill
8b4d758443 Fix comments and variable in watch.js
Amends 14d9f47fe0

Fixed:
- Location of comments.
- Variable `serverConfig` to `const`.
2021-09-16 14:01:21 -04:00
Chauncey McAskill
e51c717a3c Renamed BrowserSync variable "bs" to "server" 2021-09-16 13:55:29 -04:00
Chauncey McAskill
0267bc6ebd Revert "proxy" back to "url" for BrowserSync
Amends 14d9f47fe0
2021-09-16 13:54:49 -04:00
Chauncey McAskill
4a5d821965 Merge pull request #90 from MouseEatsCat/master
Remove default proxy
2021-09-16 13:48:38 -04:00
Chauncey McAskill
5cc8a75866 Update NPM dependencies
Added:
- esbuild v0.8.16 → v0.12.28
- kleur v4.1.3 → v4.1.4
- locomotive-scroll v4.0.4 → v4.1.2
- node-notifier v8.0.1 → v10.0.0
- node-sass v5.0.0 → v6.0.1

Removed:
- fs
2021-09-14 18:27:03 -04:00
Chauncey McAskill
1203e54277 Constrain Node to v14.17 2021-09-14 18:20:45 -04:00
Michel Descoteaux
14d9f47fe0 Remove default proxy 2021-06-28 21:33:10 -04:00
Jérémy Minié
72eaf582a5 Tweak spacing classes to use rem straight away 2021-05-06 17:04:43 -04:00
Jérémy Minié
e4d1c0058a Nuke templates unused style folder 💥 2021-05-06 17:04:27 -04:00
Jérémy Minié
bcb7525019 Tweak o-layout gutters to match $unit & $unit-small 2021-05-06 17:03:55 -04:00
Jérémy Minié
03b3d211c8 Add a catalog of easing variables 2021-05-06 17:03:45 -04:00
Chauncey McAskill
0d42f65ff6 Skip init function if stylesheet not found
Instead of throwing an error about a member not being defined or accessed on NULL.
2021-04-09 15:23:45 -04:00
Chauncey McAskill
b62385c4e0 Change Sass $assets-path value for critical.css 2021-03-23 12:29:09 -04:00
Quentin Hocdé
53653b2111 Merge pull request #82 from locomotivemtl/dependabot/npm_and_yarn/ini-1.3.8
Bump ini from 1.3.5 to 1.3.8
2021-03-05 09:57:25 -05:00
Quentin Hocdé
26821492a7 Merge pull request #81 from locomotivemtl/dependabot/npm_and_yarn/urijs-1.19.6
Bump urijs from 1.19.2 to 1.19.6
2021-03-05 09:57:15 -05:00
Quentin Hocdé
5d1c5a17f5 Merge pull request #80 from locomotivemtl/dependabot/npm_and_yarn/node-notifier-8.0.1
Bump node-notifier from 8.0.0 to 8.0.1
2021-03-05 09:57:04 -05:00
dependabot[bot]
0071b9d790 Bump ini from 1.3.5 to 1.3.8
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-05 14:45:35 +00:00
dependabot[bot]
ebda397455 Bump urijs from 1.19.2 to 1.19.6
Bumps [urijs](https://github.com/medialize/URI.js) from 1.19.2 to 1.19.6.
- [Release notes](https://github.com/medialize/URI.js/releases)
- [Changelog](https://github.com/medialize/URI.js/blob/gh-pages/CHANGELOG.md)
- [Commits](https://github.com/medialize/URI.js/compare/v1.19.2...v1.19.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-05 14:45:29 +00:00
dependabot[bot]
c94f7ca88e Bump node-notifier from 8.0.0 to 8.0.1
Bumps [node-notifier](https://github.com/mikaelbr/node-notifier) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/mikaelbr/node-notifier/releases)
- [Changelog](https://github.com/mikaelbr/node-notifier/blob/v8.0.1/CHANGELOG.md)
- [Commits](https://github.com/mikaelbr/node-notifier/compare/v8.0.0...v8.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-05 14:45:26 +00:00
Quentin Hocdé
f52b073263 Merge pull request #79 from locomotivemtl/update-compilation-2020
Update compilation
2021-03-05 09:44:46 -05:00
Quentin Hocdé
b8b056dbe0 Merge branch 'update-compilation-2020' of github.com:locomotivemtl/locomotive-boilerplate into update-compilation-2020 2021-03-05 09:31:02 -05:00
Quentin Hocdé
fbe2a6badf Fix app.js coding style and rename css id 2021-03-05 09:30:54 -05:00
Quentin Hocdé
e687e52cd2 Update build/styles.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:27:47 -05:00
Quentin Hocdé
3cf62e80f7 Update build/styles.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:27:37 -05:00
Quentin Hocdé
a6efa6bcb1 Update build/styles.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:27:25 -05:00
Quentin Hocdé
85a2784a11 Update build/styles.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:27:17 -05:00
Quentin Hocdé
65a2e64474 Update build/styles.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:27:06 -05:00
Quentin Hocdé
2cfe06ea1d Update build/scripts.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:26:58 -05:00
Quentin Hocdé
b1d90327a3 Update build/scripts.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:26:50 -05:00
Quentin Hocdé
41b8030d9e Update build/scripts.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:26:42 -05:00
Quentin Hocdé
d2d294e145 Update build/notification.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:26:34 -05:00
Quentin Hocdé
f39a5f0a75 Update build/concat.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:26:25 -05:00
Quentin Hocdé
6a6f2cfa21 Update build/concat.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:26:18 -05:00
Quentin Hocdé
0e8134629a Update build/concat.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:26:10 -05:00
Quentin Hocdé
5ab2d41525 Update build/utils/message.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:25:55 -05:00
Quentin Hocdé
2d095ef973 Update build/svgs.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:25:50 -05:00
Quentin Hocdé
89b7d9523b Merge branch 'update-compilation-2020' of github.com:locomotivemtl/locomotive-boilerplate into update-compilation-2020 2021-03-05 09:25:24 -05:00
Quentin Hocdé
ad81a8e97f Update package.json node version 2021-03-05 09:25:16 -05:00
Quentin Hocdé
bddbaaed1c Update build/utils/message.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:24:18 -05:00
Quentin Hocdé
5f20fe0e43 Update build/svgs.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:24:04 -05:00
Quentin Hocdé
718feed2c7 Update build/concat.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:23:55 -05:00
Quentin Hocdé
11b9fa7a7d Update assets/scripts/modules/Scroll.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:23:45 -05:00
Quentin Hocdé
ad01d00751 Update assets/scripts/modules/Load.js
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:23:32 -05:00
Quentin Hocdé
31c803d14d Update README.md
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:23:22 -05:00
Quentin Hocdé
6027f8d927 Update .editorconfig
Co-authored-by: Chauncey McAskill <chauncey@locomotive.ca>
2021-03-05 09:23:11 -05:00
Quentin Hocdé
7cc8cee0fd Updates coding styles Scroll.js and app.js 2021-03-05 09:22:43 -05:00
Quentin Hocdé
65d1ead6e7 Fix indentation mconfig and packages 2021-03-05 09:19:42 -05:00
Quentin Hocdé
f7a2f8b219 Update readme (add node version requirements) and update .editorconfig 2021-03-04 12:24:35 -05:00
Quentin Hocdé
5bd1ca268f Update notification 2021-01-18 14:31:41 -05:00
Quentin Hocdé
f67d4c0c41 Add error notification for styles 2020-12-11 16:00:36 -05:00
Quentin Hocdé
f15ec08784 Update notification + add built time + add nvmrc 2020-12-11 10:41:07 -05:00
Quentin Hocdé
6ab44c64a9 add indication for svg favicon and darkmode 2020-12-03 16:25:05 -05:00
Quentin Hocdé
8e5c4e5c83 Update comments 2020-11-27 16:15:36 -05:00
Quentin Hocdé
2e7bb3b482 Update tasks - remove gulp (replaced by node scripts, working with mconfig) + esbuild (no more es5 support) + add critical css in config file 2020-11-27 16:01:54 -05:00
Quentin Hocdé
e686689b52 Add shuffle function in array utils 2020-11-25 15:10:53 -05:00
Chauncey McAskill
46797cbd6d Update isDebug condition in environment.js
Check if the attribute exists instead of a truthy value.
2020-11-16 10:02:28 -05:00
Joel Alphonso
20d08c3d4b Update .editorconfig
Increase indentation for json and yml to 4 spaces
2020-11-02 16:17:44 -05:00
Deven Caron
57c3e72a06 Merge pull request #75 from locomotivemtl/develop
Minor cross-browser enhancements & quality of life changes 🤠
2020-10-26 16:13:59 -04:00
Deven Caron
43fc62950a Build 2020-10-26 15:28:35 -04:00
Deven Caron
80e1614508 Remove browser-sync new window & ghostMode 2020-10-26 15:28:31 -04:00
Deven Caron
55dc6c029c Make global font-size relative to $font-size 2020-10-26 15:27:30 -04:00
Deven Caron
a3001fe3b1 Fix README selector example 2020-10-26 15:26:52 -04:00
Deven Caron
c24ba3fdc1 Remove multiple Element.classList.add values (not compatible with IE11) 2020-10-26 15:26:15 -04:00
Deven Caron
59243317a7 Add more polyfills for cross-browser compatibility 2020-10-26 15:23:45 -04:00
Antoine Boulanger
6ae89d6621 Add npm scripts to run gulp tasks, update readme to remove gulp global dependency 2020-05-27 10:28:39 -04:00
Antoine Boulanger
e910afc384 Change async stylesheet method, check stylesheet onload before init app 2020-05-26 15:22:14 -04:00
Antoine Boulanger
a110cc7ae2 Add font-smoothing 2020-03-03 08:53:10 -05:00
Pier-Luc Cossette
a967b864e3 Remove html overflow-y: scroll 2020-01-20 15:49:33 -05:00
Antoine Boulanger
cc181ed26c Fix polyfill link, update babel polyfill, add preload to css with inline polyfill, add defer to scripts, move lazy load demo to images.html 2019-10-17 09:08:11 -04:00
Antoine Boulanger
47b667bd95 Remove vs from package.json 2019-10-17 09:04:35 -04:00
Jérémy Minié
61c9c0ac6f Add basic scroll-related lazy loading system w/ examples + Fix scrolling issues by putting header inside o-scroll + Use data-scroll-section 2019-09-25 16:25:14 -04:00
Antoine Boulanger
680d6af675 Add abortcontroller to polyfill.io and remove static file 2019-09-03 14:16:08 -04:00
Antoine Boulanger
3ed1175aaf Change loco-scroll version in package.json and add uppercase to import, add forEach to polyfill 2019-08-08 11:18:18 -04:00
Antoine Boulanger
9058945b1a Merge pull request #68 from locomotivemtl/baker-components
Create heading, form and button components with example
2019-07-25 09:10:47 -04:00
Antoine Boulanger
1f589add29 Create heading, form and button component, remove objects, add form example to page.html, add rem to container, remove pjax scss, add scroll object. 2019-07-24 16:28:56 -04:00
Antoine Boulanger
d2db947fd1 Update readme scroll section 2019-07-19 16:37:46 -04:00
Antoine Boulanger
88dd4dde3b Change favicons 2019-07-19 16:08:08 -04:00
Antoine Boulanger
41c3fe4b49 Remove jQuery and scrollTo util 2019-07-19 16:00:31 -04:00
Antoine Boulanger
9836c462b2 Update locomotive-scroll to v3: update scroll.js, add scrollbar css, remove copy task 2019-07-19 15:57:34 -04:00
Quentin Hocdé
cb49c03cca rename APP_NAME constant 2019-07-18 11:25:03 -04:00
Quentin Hocdé
6324f7ee82 Update debug attribute (now with vanilla) 2019-07-18 11:23:06 -04:00
Quentin Hocdé
883e4d202e Update environment file 2019-07-18 11:21:17 -04:00
Quentin Hocdé
5da3bcd961 add getTranslate function in utils 2019-07-04 10:52:04 -04:00
Quentin Hocdé
26cccc7d92 Add transform function in utils 2019-06-26 16:39:03 -04:00
Deven Caron
ce8582c0a4 Add ol reset style 2019-06-26 14:13:26 -04:00
Deven Caron
ee258f5d5f Remove u-list-reset mixin 2019-06-26 14:13:15 -04:00
Deven Caron
cf9a0a2705 Add some basic reset styles (ul, p, figure) 2019-06-26 14:04:13 -04:00
Quentin Hocdé
e6dec31198 Add waiting cursor on loading 2019-06-26 09:32:15 -04:00
Jérémy Minié
7d47ae0d82 Fix small warning in app.js + Add useful methods to html.js + Add maths.js to utils + Build 2019-06-25 14:21:03 -04:00
Antoine Boulanger
206ced5b10 Add .browserslistrc 2019-06-05 14:28:56 -04:00
Antoine Boulanger
d89a242e60 Merge branch 'v2' 2019-05-23 10:35:11 -04:00
Antoine Boulanger
d166261d0a Add loco scroll v2, update load module and app, update html, add nomodule polyfills script tags 2019-05-23 10:06:45 -04:00
Chauncey McAskill
ea5c63744e Fix family.scss
Fixed:
- Typo in filename
- Indentation of contents
2019-05-23 10:06:24 -04:00
Antoine Boulanger
52db1cc63e Fix ratio utility 2019-04-19 11:47:29 -04:00
Antoine Boulanger
6d8954563a Clean ratio object, create ratio utility, import ratio and width by default 2019-04-19 11:45:15 -04:00
Antoine Boulanger
d9eb8364dc Complete rewrite of readme for v2, change styles base folder name for elements 2019-03-31 16:39:37 -04:00
Antoine Boulanger
88aa667090 Start adding modularjs & modularload, start updating readme, update html files, change jquery cdn, remove IE from babelrc 2019-03-24 17:15:47 -04:00
Jérémy Minié
2b176132d3 @joel Fixed deleteModules splice : deleteCount param wasn't present, causing all modules with index > i to be removed from this.currentModules. Fortunately, this was only happening if a scope was given to deleteModules. 2019-03-14 14:13:34 -04:00
104 changed files with 15159 additions and 1604 deletions

View File

@@ -1,13 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"ie": "11"
},
"useBuiltIns": "usage"
}
]
]
}

1
.browserslistrc Normal file
View File

@@ -0,0 +1 @@
defaults

View File

@@ -9,9 +9,8 @@ indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
[*.{md,markdown}]
trim_trailing_whitespace = false
[{*.yml,*.json}]
indent_size = 2
indent_style = space
[*.{ms,mustache}]
insert_final_newline = false

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
v14.17

303
README.md
View File

@@ -1,158 +1,233 @@
Locomotive's Front-end Boilerplate
==================================
<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">
</a>
</p>
<h1 align="center">Locomotive Boilerplate</h1>
<p align="center">Front-end boilerplate for projects by Locomotive.</p>
Front-end boilerplate for projects by [Locomotive][locomtl].
## Requirements
| Name | Version |
| ---------- | -------- |
| [Node] | >= 14.17 |
| [NPM] | >= 6.0 |
[Node]: https://nodejs.org/
[NPM]: https://npmjs.com/
You can use [nvm](https://github.com/nvm-sh/nvm) to install the node version in `.nvmrc`.
## Installation
```sh
# install mbp and gulp
npm install mbp gulp@next -g
npm i
```
## Usage
```sh
# init your project
mbp init locomotivemtl/locomotive-boilerplate <directory>
# run default watch task
gulp
```sh
# start it
npm start
```
## Configuration
Change the mentions of `boilerplate` for your project's name in
- `mconfig.json`
- `assets/scripts/utils/environment.js`
## CSS
There are a few occurrences that should be renamed for your project:
- We use [Sass](http://sass-lang.com) for our CSS Preprocessor
- [itcss](http://itcss.io) CSS architecture
- More Minimal BEM like CSS Syntax: `.block_element -modifier`
- [More Transparent UI Code with Namespaces](http://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces)
* [package.json](package.json):
* Package name: `@locomotivemtl/boilerplate`
* Package title: `Locomotive Boilerplate`
* [package-lock.json](package-lock.json):
* Package name: `@locomotivemtl/boilerplate`
* [loconfig.json](loconfig.json):
* Browser Sync proxy URL: `locomotive-boilerplate.test`
Remove `paths.url` to use Browser Sync's built-in server which uses `paths.dest`.
* View path: `./views/boilerplate/template`
* [environment.js](assets/scripts/utils/environment.js):
* Application name: `Boilerplate`
* [site.webmanifest](www/site.webmanifest):
* Manifest name: `Locomotive Boilerplate`
* Manifest short name: `Boilerplate`
* HTML files:
* Page title: `Locomotive Boilerplate`
### Sass import order
## Build
* **Settings:** Global variables, site-wide settings, config switches, etc.
* **Tools:** Site-wide mixins and functions.
* **Generic:** Low-specificity, far-reaching rulesets (e.g. resets).
* **Base:** Unclassed HTML elements (e.g. `a {}`, `blockquote {}`, `address {}`).
* **Objects:** Objects, abstractions, and design patterns (e.g. `.o-media {}`).
* **Components:** Discrete, complete chunks of UI (e.g. `.c-carousel {}`).
* **Utilities:** High-specificity, very explicit selectors. Overrides and helper
classes (e.g. `.u-hidden {}`).
#### Tasks
```sh
# watch
npm start
### Grid
We use [inuitcss](https://github.com/inuitcss/inuitcss/tree/6eb574fa604481ffa36272e6034e77467334ec50) layout and width system. We are using a inline-block grid system.
Insert a `.o-layout` block and add `.o-layout_item` elements inside it. By default `o-layout_item` made 100%.
You can define different fractions in `/tools/_widths.scss` (`$widths-fractions`)
If you want a 2 columns grid, just add `.u-1/2` on your 2 `.o-layout_item`
If you want to adapt columns by media queries, by example a 2 columns grid for 1000px + resolutions, and one columns in block under 1000px :
**HTML**
# build
npm run build
```
<div class="o-layout">
<div class="o-layout_item u-1/2@from-medium">
first colum
</div>
<div class="o-layout_item u-1/2@from-medium">
second colum
</div>
## Styles
[Sass](https://github.com/sass/node-sass) is our CSS preprocessor. [Autoprefixer](https://github.com/postcss/autoprefixer) is also included.
#### Architecture
[ITCSS](https://github.com/itcss) is our CSS architecture.
* `settings`: Global variables, site-wide settings, config switches, etc.
* `tools`: Site-wide mixins and functions.
* `generic`: Low-specificity, far-reaching rulesets (e.g. resets).
* `elements`: Unclassed HTML elements (e.g. `a {}`, `blockquote {}`, `address {}`).
* `objects`: Objects, abstractions, and design patterns (e.g. `.o-layout {}`).
* `components`: Discrete, complete chunks of UI (e.g. `.c-carousel {}`).
* `utilities`: High-specificity, very explicit selectors. Overrides and helper
classes (e.g. `.u-hidden {}`)
[_source_](https://github.com/inuitcss/inuitcss#css-directory-structure)
#### Naming
We use a simplified [BEM](https://github.com/bem) syntax.
```
.block .block_element -modifier
```
#### Namespaces
We namespace our classes for more [transparency](https://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/).
* `o-`: Object that it may be used in any number of unrelated contexts to the one you can currently see it in. Making modifications to these types of class could potentially have knock-on effects in a lot of other unrelated places.
* `c-`: Component is a concrete, implementation-specific piece of UI. All of the changes you make to its styles should be detectable in the context youre currently looking at. Modifying these styles should be safe and have no side effects.
* `u-`: Utility has a very specific role (often providing only one declaration) and should not be bound onto or changed. It can be reused and is not tied to any specific piece of UI.
* `s-`: Scope creates a new styling context. Similar to a Theme, but not necessarily cosmetic, these should be used sparingly—they can be open to abuse and lead to poor CSS if not used wisely.
* `is-`, `has-`: Is currently styled a certain way because of a state or condition. It tells us that the DOM currently has a temporary, optional, or short-lived style applied to it due to a certain state being invoked.
[_source_](https://csswizardry.com/2015/03/more-transparent-ui-code-with-namespaces/#the-namespaces)
#### Example
```html
<div class="c-block -large">
<div class="c-block_layout o-layout">
<div class="o-layout_item u-1/2@from-medium">
<div class="c-block_heading o-h -medium">Heading</div>
</div>
<div class="o-layout_item u-1/2@from-medium">
<a class="c-block_button o-button -outline" href="#">Button</a>
</div>
</div>
</div>
```
```scss
.c-block {
&.-large {
padding: rem(60px);
}
}
**CSS** (`/tools/_widths.scss`)
```
.u-1\/2\@from-medium {
@media (min-width: $from-medium) {
width: span(1/2);
.c-block_heading {
@media (max-width: $to-medium) {
.c-block.-large & {
margin-bottom: rem(40px);
}
}
}
```
## Scripts
### Form
[modularJS](https://github.com/modularorg/modularjs) is a small framework we use on top of ES modules. It compiles with [Rollup](https://github.com/rollup/rollup) and [Babel](https://github.com/babel/babel).
We included some basic CSS styles and resets to the form elements so we can easily have custom style form elements that work on every browsers.
#### Why
*[Demo][demo-form]*
* Automatically init visible modules.
* Easily call other modules methods.
* Quickly set scoped events with delegation.
* Simply select DOM elements scoped in their module.
## JavaScript
[_source_](https://github.com/modularorg/modularjs#why)
- We use HTML data attributes to init our JavaScript modules: `data-module`
- All DOM related JavaScript is hooked to `js-` prefixed HTML classes
- [jQuery](https://jquery.com) is globally included
#### Example
[locomtl]: https://locomotive.ca
[demo-grid]: https://codepen.io/AntoineBoulanger/pen/EaLNxe
[demo-form]: https://codepen.io/AntoineBoulanger/pen/uBJmi
```html
<div data-module-example>
<div data-example="main">
<h2>Example</h2>
</div>
<button data-example="load">More</button>
</div>
```
```js
import { module } from 'modujs';
export default class extends module {
constructor(m) {
super(m);
this.events = {
click: {
load: 'loadMore'
}
}
}
loadMore() {
this.$('main')[0].classList.add('is-loading');
}
}
```
[Learn more](https://github.com/modularorg/modularjs)
## Page transitions
We use [Pjax](https://github.com/MoOx/pjax) by MoOx.
### Setup
1. Create a wrapper : `.js-pjax-wrapper` and a container `.js-pjax-container` inside. When a transition is launched, the new container is put inside the wrapper, and the old one is remove.
[modularLoad](https://github.com/modularorg/modularload) is used for page transitions and lazy loading.
2. Main settings are set inside `assets/scripts/transitions/TransitionManager.js`
#### Example
3. `BaseTransition` is launched by default, to set a new transition (like `CustomTransition`) :
- create a new class `TestTransition.js` witch extends `BaseTransition` in `assets/scripts/transitions/`
- add a line in `assets/scripts/transitions/transitions.js` to add your transition
- use it like : `<a href="/yourUrl" data-transition="TestTransition">My page</a>`
- Enjoy and made everything you want in your transition, check `BaseTransition.js` or `CustomTransition.js` like example
```html
<nav>
<a href="/">Home</a>
<a href="/page" data-load="transitionName">Page</a>
</nav>
<div data-load-container>
<img data-load-src="assets/images/hello.jpg">
</div>
```
```js
import modularLoad from 'modularload';
### Schema
this.load = new modularLoad({
enterDelay: 300,
transitions: {
transitionName: {
enterDelay: 450
}
}
});
```
Legend
- `[ ]` : listener
- `*` : trigger event
[Learn more](https://github.com/modularorg/modularload)
`[pjax:send]` -> (transition) launch()
## Scroll detection
`[pjax:switch]` (= new view is loaded) -> (BaseTransition) `hideView()` -> hide animations & `*readyToRemove`
[Locomotive Scroll](https://github.com/locomotivemtl/locomotive-scroll) is used for elements in viewport detection and smooth scrolling with parallax.
`[readyToRemove]` -> `remove()` -> delete modules, remove oldView from the DOM, innerHTML newView, init modules, `display()`
#### Example
`display()` -> (BaseTransition) `displayView()` -> display animations & `*readyToDestroy`
-> init new modules
```html
<div data-module-scroll>
<div data-scroll>Trigger</div>
<div data-scroll data-scroll-speed="1">Parallax</div>
</div>
```
`[readyToRemove]` -> reinit()
```js
import LocomotiveScroll from 'locomotive-scroll';
## Locomotive Scroll
![experimental](https://img.shields.io/badge/stability-experimental-orange.svg)
- [locomotive-scroll](https://github.com/locomotivemtl/locomotive-scroll)
### Configuration
- Create a `.o-scroll` container with `data-module="Scroll"`
- in the module `Scroll.js` you have a basic initialisation
### Options
Options | Type | Description
--- | --- | ---
container | $element | Scroll container (with the smooth scroll, this container will be transform)
selector | String | Every elements will be check by the scroll, can be affect by a followed data attributes
smooth | Boolean | If you want a smooth scroll
smoothMobile | Boolean | If you want a smooth scroll on mobile
mobileContainer | $element | Scroll container on mobile, document by default
getWay | Boolean | if true, the animate will determine if you scroll down or scroll up
getSpeed | Boolean | if true, the animate will calcul the velocity of your scroll. Access with `this.scroll.y`
### Data attributes
Data | Value | Description
--- | --- | ---
data-speed | number | Speed of transform for parallax elements
data-repeat | false | Determine if the "In View" class is added one or each times
data-inview-class | is-show | CSS Class when the element is in view.
data-position | top/bottom | Trigger from top/bottom of the window instead of the default from bottom to top
data-target | #id, .class | Trigger from another element
data-horizontal | false | Use transformX instead of transformY
data-sticky | false | Set $element sticky when it's in viewport
data-sticky-target | #id | Stop the element stick when the target is in viewport
data-callback | `test.Scroll(test:0)` | trigger event, with options way wich return "leave" or "enter" when $element is in viewport
data-viewport-offset | i,j | value between 0 to 1 (0.3 to start at 30% of the bottom of the viewport), useful to trigger a sequence of callbacks. (i : value wich start at the bottom, j : start at the top, j is optional)
this.scroll = new LocomotiveScroll({
el: this.el,
smooth: true
});
````
[Learn more](https://github.com/locomotivemtl/locomotive-scroll)

View File

@@ -1,161 +1,35 @@
import { APP_NAME, $document, $pjaxWrapper } from './utils/environment';
import globals from './globals';
import { arrayContains, removeFromArray } from './utils/array';
import { getNodeData } from './utils/html';
import { isFunction } from './utils/is';
// Basic modules
import modular from 'modujs';
import * as modules from './modules';
import globals from './globals';
import { html } from './utils/environment';
const MODULE_NAME = 'App';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;
const app = new modular({
modules: modules
});
export const EVENT = {
INIT_MODULES: `initModules.${EVENT_NAMESPACE}`,
INIT_SCOPED_MODULES: `initScopedModules.${EVENT_NAMESPACE}`,
DELETE_SCOPED_MODULES: `deleteScopedModules.${EVENT_NAMESPACE}`
window.onload = (event) => {
const $style = document.getElementById('main-css');
if ($style) {
if ($style.isLoaded) {
init();
} else {
$style.addEventListener('load', (event) => {
init();
});
}
} else {
console.warn('The "main-css" stylesheet not found');
}
};
class App {
constructor() {
this.modules = modules;
this.currentModules = [];
function init() {
globals();
$document.on(EVENT.INIT_MODULES, (event) => {
this.initGlobals(event.firstBlood)
.deleteModules(event)
.initModules(event);
});
app.init(app);
$document.on(EVENT.INIT_SCOPED_MODULES, (event) => {
this.initModules(event);
});
$document.on(EVENT.DELETE_SCOPED_MODULES, (event) => {
this.deleteModules(event);
});
}
/**
* Destroy all existing modules or a specific scope of modules
* @param {Object} event The event being triggered.
* @return {Object} Self (allows chaining)
*/
deleteModules(event) {
let destroyAll = true;
let moduleIds = [];
// Check for scope first
if (event.$scope instanceof jQuery && event.$scope.length > 0) {
// Modules within scope
const $modules = event.$scope.find('[data-module]');
// Determine their uids
moduleIds = $.makeArray($modules.map(function(index) {
return $modules.eq(index).data('uid');
}));
if (moduleIds.length > 0) {
destroyAll = false;
} else {
return this;
}
}
// Loop modules and destroying all of them, or specific ones
let i = this.currentModules.length;
while (i--) {
if (destroyAll || arrayContains(moduleIds, this.currentModules[i].uid)) {
removeFromArray(moduleIds, this.currentModules[i].uid);
this.currentModules[i].destroy();
this.currentModules.splice(i);
}
}
return this;
}
/**
* Execute global functions and settings
* Allows you to initialize global modules only once if you need
* (ex.: when using Barba.js or SmoothState.js)
* @return {Object} Self (allows chaining)
*/
initGlobals(firstBlood) {
globals(firstBlood);
return this;
}
/**
* Find modules and initialize them
* @param {Object} event The event being triggered.
* @return {Object} Self (allows chaining)
*/
initModules(event) {
// Elements with module
let $moduleEls = [];
// If first blood, load all modules in the DOM
// If scoped, render elements with modules
// If Barba, load modules contained in Barba container
if (event.firstBlood) {
$moduleEls = $document.find('[data-module]');
} else if (event.$scope instanceof jQuery && event.$scope.length > 0) {
$moduleEls = event.$scope.find('[data-module]');
} else if (event.isPjax) {
$moduleEls = $pjaxWrapper.find('[data-module]');
}
// Loop through elements
let i = 0;
const elsLen = $moduleEls.length;
for (; i < elsLen; i++) {
// Current element
let el = $moduleEls[i];
// All data- attributes considered as options
let options = getNodeData(el);
// Add current DOM element and jQuery element
options.el = el;
options.$el = $moduleEls.eq(i);
// Module does exist at this point
let attr = options.module;
// Splitting modules found in the data-attribute
let moduleIdents = attr.split(/[,\s]+/g);
// Loop modules
let j = 0;
let modulesLen = moduleIdents.length;
for (; j < modulesLen; j++) {
let moduleAttr = moduleIdents[j];
if (typeof this.modules[moduleAttr] === 'function') {
let module = new this.modules[moduleAttr](options);
this.currentModules.push(module);
module.init();
}
}
}
return this;
}
html.classList.add('is-loaded');
html.classList.add('is-ready');
html.classList.remove('is-loading');
}
// IIFE for loading the application
// ==========================================================================
(function() {
new App();
$document.triggerHandler({
type: EVENT.INIT_MODULES,
firstBlood: true
});
})();

View File

@@ -1,10 +1,5 @@
import TransitionManager from './transitions/TransitionManager';
import svg4everybody from 'svg4everybody';
export default function(firstBlood) {
export default function() {
svg4everybody();
if (firstBlood) {
const transitionManager = new TransitionManager();
}
}

View File

@@ -1,2 +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,24 +0,0 @@
let uid = 0;
/**
* Abstract Module
*/
export default class {
constructor(options) {
this.$el = options.$el || null;
this.el = options.el || null;
// Generate a unique module identifier
this.uid = 'm-' + uid++;
// Use jQuery's data API to "store it in the DOM"
this.$el.data('uid', this.uid);
}
init() {}
destroy() {
if (this.$el) {
this.$el.removeData('uid')
}
}
}

View File

@@ -1,30 +1,10 @@
import { APP_NAME } from '../utils/environment';
import AbstractModule from './AbstractModule';
const MODULE_NAME = 'Example';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;
const EVENT = {
CLICK: `click.${EVENT_NAMESPACE}`
};
export default class extends AbstractModule {
constructor(options) {
super(options);
// Declaration of properties
console.log('🔨 [module]:constructor - Example');
import { module } from 'modujs';
export default class extends module {
constructor(m) {
super(m);
}
init() {
// Set events and such
}
destroy() {
console.log('❌ [module]:destroy - Example');
super.destroy();
this.$el.off(`.${EVENT_NAMESPACE}`);
}
}

View File

@@ -0,0 +1,26 @@
import { module } from 'modujs';
import modularLoad from 'modularload';
export default class extends module {
constructor(m) {
super(m);
}
init() {
const load = new modularLoad({
enterDelay: 0,
transitions: {
customTransition: {}
}
});
this.load.on('loading', (transition, oldContainer) => {
this.call('hide', null, 'Modal');
});
load.on('loaded', (transition, oldContainer, newContainer) => {
this.call('destroy', oldContainer, 'app');
this.call('update', newContainer, 'app');
});
}
}

View File

@@ -0,0 +1,515 @@
/**
* Module to display a modal dialog.
*
* Uses {@link https://github.com/KittyGiraudel/a11y-dialog A11y Dialog}.
*
* ### Usage
*
* ```html
* <!-- 1. The dialog container -->
* <div
* id="your-dialog-id"
* data-module-modal="your-dialog-id"
* aria-labelledby="your-dialog-title-id"
* aria-hidden="true"
* >
* <!-- 2. The dialog overlay -->
* <div data-a11y-dialog-hide></div>
* <!-- 3. The actual dialog -->
* <div role="document">
* <!-- 4. The close button -->
* <button type="button" data-a11y-dialog-hide aria-label="Close dialog">
* &times;
* </button>
* <!-- 5. The dialog title -->
* <h1 id="your-dialog-title-id">Your dialog title</h1>
* <!-- 6. Dialog content -->
* </div>
* </div>
*
* <!-- 7. The a11y-dialog modal trigger -->
* <button type="button" data-a11y-dialog-show="your-dialog-id">
* Open the dialog
* </button>
* ```
*
* The dialog container must have a unique name for the `data-module-modal`
* attribute for cross-component interaction.
*
* The `id` attribute is recommended but optional if `data-module-modal`
* is present with a unique name.
*
* If the dialog container uses the `data-a11y-dialog` attribute for automatic
* instantiation through HTML, this component will not initialize.
*
* ### Features
*
* #### Automatically showing dialog
*
* The modal supports being shown on page load via the
* `data-modal-autoshow` attribute:
*
* ```html
* <div data-module-modal="your-dialog-id"
* data-modal-autoshow
* aria-labelledby="your-dialog-title-id"
* aria-hidden="true"
* >
* ```
*
* The modal will be shown during the module's `init()` process.
*
* #### Showing dialog once
*
* The modal supports being shown at most once via the
* `data-modal-show-once` attribute:
*
* ```html
* <div data-module-modal="your-dialog-id"
* data-modal-show-once
* aria-labelledby="your-dialog-title-id"
* aria-hidden="true"
* >
* ```
*
* By default, it uses {@see window.localStorage} to persist dismissal.
* With a "session" value (`data-modal-show-once="session"`), the component
* will use {@see window.sessionStorage}.
*/
import A11yDialog from 'a11y-dialog'
import { module } from 'modujs';
import { html, isDebug } from '../utils/environment';
/**
* Component to display a modal dialog.
*
* @property {?A11yDialog} dialog - The {@see A11yDialog} instance.
* @property {string} moduleName - The module class name.
* @property {string} moduleID - The module class instance ID.
*/
export default class extends module
{
/**
* Whether the dialog should be shown on page load.
*
* @var {boolean}
*/
autoShow = false;
/**
* The element to add `contextShowClass` to. Defaults to `<html>`.
*
* @var {Element}
*/
contextElement = html;
/**
* The CSS class name to apply to `contextElement` to mark the dialog as shown.
*
* @var {string}
*/
contextShowClass = 'has-modal-open';
/**
* Whether to log information about the modal.
*
* @var {boolean}
*/
debug = isDebug;
/**
* The storage key to remember the dialog was dismissed.
*
* @var {?string}
*/
dismissedStoreKey;
/**
* Whether the dialog can be shown at most once.
*
* @var {boolean}
*/
showOnce = false;
/**
* The storage object; either `localStorage` or `sessionStorage`.
*
* @var {?Storage}
*/
showOnceStore = window.localStorage;
/**
* Whether the dialog was shown.
*
* @var {boolean}
*/
wasShown = false;
/**
* Creates a new Modal component.
*
* @param {object} options - The module options.
* @param {string} options.name - The module class name.
* @param {string} options.dataName - The module data attribute name.
* @throws {TypeError} If the module ID or module class is missing.
*/
constructor(options) {
super(options);
this.events = {
click: {
dismiss: 'hide',
hide: 'hide',
show: 'show',
toggle: 'toggle',
},
submit: 'onSubmit',
submitend: 'hide',
};
const moduleAttr = `data-module-${options.dataName}`;
this.moduleName = options.name;
this.moduleID = this.el.getAttribute(moduleAttr);
if (!this.moduleID) {
throw new TypeError(
`${this.moduleName} must have an ID on attribute ${moduleAttr}`
);
}
if (!this.el.hasAttribute('id')) {
this.el.setAttribute('id', this.moduleID);
}
this._onHide = this.onHide.bind(this);
this._onShow = this.onShow.bind(this);
this._onSubmit = this.onSubmit.bind(this);
this.resolveShowOnce();
}
/**
* Creates the A11y Dialog instance and initializes the Modal component.
*
* If the `data-a11y-dialog` attribute is defined on the
* {@see this.el dialog element}, the Modal component will
* be ignored.
*
* If the Modal component was previously dismissed,
* the component will be ignored.
*
* @return {void}
*/
init() {
if (this.el.hasAttribute('data-a11y-dialog')) {
const dialogID = (this.el.getAttribute('data-a11y-dialog') || this.moduleID);
this.debug && console.warn(`${this.moduleName} [${dialogID}] not initialized because of automatic instantiation through HTML`);
return;
}
if (this.showOnce && this.wasShown) {
this.debug && console.log(`${this.moduleName} [${this.moduleID}] not initialized because the dialog was previously dismissed`);
return;
}
this.dialog = this.createDialog();
/**
* Assigning the this class and the dialog to the dialog container
* element for easy access from the web console.
*/
this.el.appModal = this;
this.el.a11yDialog = this.dialog;
this.addDialogEventListeners();
if (this.el.hasAttribute(`${this.mAttr}-autoshow`)) {
this.dialog.show();
}
}
/**
* @inheritdoc
*/
mUpdate(modules) {
super.mUpdate(modules);
this.refreshDialogOpeners();
}
/**
* Creates a new A11y Dialog instance.
*
* @protected
* @return {A11yDialog}
*/
createDialog() {
return new A11yDialog(this.el);
}
/**
* Destroys the A11y Dialog instance and Modal component.
*
* @return {void}
*/
destroy() {
this.destroyDialog();
delete this.el.appModal;
}
/**
* Destroys only the A11y Dialog instance.
*
* @return {void}
*/
destroyDialog() {
if (this.dialog) {
this.dialog.destroy();
}
delete this.el.a11yDialog;
}
/**
* Dismisses the modal.
*
* Marks the modal as dismissed which means it
* should not be shown for the foreseeable future.
*
* @protected
* @param {boolean} [destroy=false] - Whether to destroy the modal or not.
* @return {void}
*/
dismissDialog(destroy = false) {
this.wasShown = true;
if (this.showOnceStore && this.dismissedStoreKey) {
this.showOnceStore.setItem(this.dismissedStoreKey, this.getDismissedStoreValue());
} else {
this.debug && console.warn(`${this.moduleName} [${dialogID}] does not have a 'showOnceStore' or a 'dismissedStoreKey' for persisting dismissal`);
}
if (destroy) {
this.destroyDialog();
}
}
/**
* Registers event listeners on the A11y Dialog.
*
* @protected
* @return {void}
*/
addDialogEventListeners() {
if (this.dialog) {
this.dialog.on('hide', this._onHide);
this.dialog.on('show', this._onShow);
}
}
/**
* Unregisters event listeners on the A11y Dialog.
*
* @protected
* @return {void}
*/
removeDialogEventListeners() {
if (this.dialog) {
this.dialog.off('hide', this._onHide);
this.dialog.off('show', this._onShow);
}
}
/**
* @return {self}
*/
hide() {
if (this.dialog) {
this.dialog.hide();
}
return this;
}
/**
* @return {self}
*/
show() {
if (this.dialog) {
this.dialog.show();
}
return this;
}
/**
* @return {self}
*/
toggle() {
if (this.dialog) {
if (this.dialog.shown) {
this.dialog.hide();
} else {
this.dialog.show();
}
}
return this;
}
/**
* Returns the storage key to remember the dialog was dismissed.
*
* @return {string}
*/
getDismissedStoreKey() {
return `${this.moduleName}.${this.moduleID}.dismissed`;
}
/**
* Returns the storage value to remember the dialog was dismissed.
*
* @return {string}
*/
getDismissedStoreValue() {
return (new Date()).toISOString();
}
/**
* Returns a list of kebab-case form module names.
*
* @return {string[]}
*/
getFormModuleNames() {
return [
'form',
];
}
/**
* Fires when the dialog has finished being hidden from the user.
*
* This method will trigger dismissal of the dialog if to be
* {@see this.showOnce shown at most once}.
*
* This method will remove the {@see this.contextShowClass CSS context show class}.
*
* @listens A11yDialog#hide
*
* @protected
* @param {Element} dialogEl - The dialog container element.
* @param {Event} event - The dialog hide event.
* @return {void}
*/
onHide(dialogEl, event) {
if (this.showOnce && !this.wasShown) {
this.dismissDialog(true);
}
if (this.contextElement && this.contextShowClass) {
this.contextElement.classList.remove(this.contextShowClass);
}
}
/**
* Fires when the dialog has been made visible to the user.
*
* This method will add the {@see this.contextShowClass CSS context show class}.
*
* @listens A11yDialog#show
*
* @protected
* @param {Element} dialogEl - The dialog container element.
* @param {Event} event - The dialog show event.
* @return {void}
*/
onShow(dialogEl, event) {
if (this.contextElement && this.contextShowClass) {
this.contextElement.classList.add(this.contextShowClass);
}
}
/**
* Handles form submission inside the modal.
*
* This event listener is used to handle forms that do not use a custom
* form module where the modal will dispatch a custom "submitend" event.
*
* @listens form#submit
* @fires form#submitend
*
* @protected
* @param {Event} event - The submit event.
* @return {void}
*/
onSubmit(event) {
const selectors = this.getFormModuleNames()
.map((name) => `:not([data-module-${name}])`)
.join('');
if (event.target.matches(selectors)) {
this.debug && console.log('Modal.onSubmit')
const submitEndEvent = new CustomEvent('submitend', {
bubbles: true
});
event.target.dispatchEvent(submitEndEvent);
}
}
/**
* Refreshes all opener event listeners of the A11y Dialog instance.
*
* @protected
* @return {void}
*/
refreshDialogOpeners() {
if (this.dialog) {
// Remove the click event listener from all dialog openers
this.dialog._openers.forEach((opener) => {
opener.removeEventListener('click', this.dialog._show);
});
// Keep a collection of dialog openers, each of which will be bound a click
// event listener to open the dialog
this.dialog._openers = document.querySelectorAll('[data-a11y-dialog-show="' + this.dialog._id + '"]');
this.dialog._openers.forEach((opener) => {
opener.addEventListener('click', this.dialog._show);
});
}
}
/**
* Configures the "show at most once" feature.
*
* @protected
* @return {void}
*/
resolveShowOnce() {
const showOnceAttr = `${this.mAttr}-show-once`;
this.showOnce = this.el.hasAttribute(showOnceAttr);
if (!this.showOnce) {
return;
}
switch (this.el.getAttribute(showOnceAttr)) {
case 'session':
this.showOnceStore = window.sessionStorage;
break;
case 'local':
this.showOnceStore = window.localStorage;
break;
}
this.dismissedStoreKey = this.getDismissedStoreKey();
if (this.showOnceStore) {
this.wasShown = this.showOnceStore.getItem(this.dismissedStoreKey);
}
}
}

View File

@@ -1,31 +1,52 @@
import { APP_NAME, $document } from '../utils/environment';
import AbstractModule from './AbstractModule';
import ScrollManager from '../scroll/vendors/ScrollManager';
import { module } from 'modujs';
import { lazyLoadImage } from '../utils/image';
import LocomotiveScroll from 'locomotive-scroll';
const MODULE_NAME = 'Scroll';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;
export default class extends AbstractModule {
constructor(options) {
super(options);
export default class extends module {
constructor(m) {
super(m);
}
init() {
setTimeout(() => {
this.scrollManager = new ScrollManager({
container: this.$el,
selector: '.js-animate',
smooth: false,
smoothMobile: false,
mobileContainer: $document,
getWay: false,
getSpeed: false
});
}, 500);
this.scroll = new LocomotiveScroll({
el: this.el,
smooth: true
});
this.scroll.on('call', (func, way, obj, id) => {
// Using modularJS
this.call(func[0], { way, obj }, func[1], func[2]);
});
this.scroll.on('scroll', (args) => {
// console.log(args.scroll);
})
}
/**
* Lazy load the related image.
*
* @see ../utils/image.js
*
* It is recommended to wrap your `<img>` into an element with the
* CSS class name `.c-lazy`. The CSS class name modifier `.-lazy-loaded`
* will be applied on both the image and the parent wrapper.
*
* ```html
* <div class="c-lazy o-ratio u-4:3">
* <img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=1" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
* </div>
* ```
*
* @param {LocomotiveScroll} args - The Locomotive Scroll instance.
*/
lazyLoad(args) {
lazyLoadImage(args.obj.el, null, () => {
//callback
})
}
destroy() {
super.destroy();
this.scrollManager.destroy();
this.scroll.destroy();
}
}

View File

@@ -1,26 +0,0 @@
// ==========================================================================
// Extended Locomotive Scroll
// ==========================================================================
/* jshint esnext: true */
import Scroll, { EVENT_KEY as VENDOR_EVENT_KEY, EVENT as VENDOR_EVENTS, DEFAULTS as VENDOR_DEFAULTS } from './vendors/Scroll'
/**
* UNCOMMENT ONLY THE LINES YOU NEED
*/
// import { $window, $document } from '../../utils/environment';
// import debounce from '../../utils/debounce';
// import { isNumeric } from '../../utils/is';
export const EVENT_KEY = VENDOR_EVENT_KEY;
export const EVENT = Object.assign(VENDOR_EVENTS, {
// TEST: `test.${EVENT_KEY}`
});
export const DEFAULTS = Object.assign(VENDOR_DEFAULTS, { });
export default class extends Scroll {
constructor(options) {
super(options)
}
}

View File

@@ -1,21 +0,0 @@
// ==========================================================================
// Extended Locomotive Smooth Scroll
// ==========================================================================
/* jshint esnext: true */
import SmoothScroll from './vendors/SmoothScroll'
/**
* UNCOMMENT ONLY THE LINES YOU NEED
*/
// import { $window, $document, $html } from '../utils/environment';
// import Scroll, { DEFAULTS, EVENT } from './Scroll';
// import debounce from '../utils/debounce';
// import Scrollbar from 'smooth-scrollbar';
// import { isNumeric } from '../utils/is';
export default class extends SmoothScroll {
constructor(options) {
super(options)
}
}

View File

@@ -1,75 +0,0 @@
import { APP_NAME, $document, $html, $body, isDebug, $pjaxWrapper } from '../utils/environment';
import { EVENT as TransitionEvent } from './TransitionManager'
export default class {
constructor(options) {
this.options = options;
this.wrapper = options.wrapper;
this.overrideClass = options.overrideClass ? options.overrideClass : '';
this.clickedLink = options.clickedLink;
}
launch() {
if(isDebug) {
console.log("---- Launch transition 👊 -----");
}
$html
.removeClass('has-dom-loaded has-dom-animated ')
.addClass(`has-dom-loading ${this.overrideClass}`);
}
hideView(oldView, newView) {
if(isDebug) {
console.log('----- ❌ [VIEW]:hide - ', oldView.getAttribute('data-template'));
}
// launch it at the end (animations...)
$document.triggerHandler({
type:TransitionEvent.READYTOAPPEND,
oldView: oldView,
newView: newView
});
}
displayView(view) {
if(isDebug) {
console.log('----- ✅ [VIEW]:display :', view.getAttribute('data-template'));
}
$html.attr('data-template', view.getAttribute('data-template'));
setTimeout(() => {
$html
.addClass('has-dom-loaded')
.removeClass('has-dom-loading');
setTimeout(() => {
$html
.removeClass(this.overrideClass)
.addClass('has-dom-animated');
}, 1000);
// launch it at the end (animations...)
$document.triggerHandler({
type:TransitionEvent.READYTODESTROY
});
},1000);
}
destroy() {
if(isDebug) {
console.log("---- ❌ [transition]:destroy -----");
}
}
}

View File

@@ -1,13 +0,0 @@
import { APP_NAME, $document, $html, isDebug, $pjaxWrapper } from '../utils/environment';
import BaseTransition from './BaseTransition';
import { EVENT as TransitionEvent } from './TransitionManager'
export default class extends BaseTransition{
constructor(options) {
super(options);
this.overrideClass = '-custom-transition';
}
}

View File

@@ -1,256 +0,0 @@
import Pjax from 'pjax';
import { APP_NAME, $document, $html, isDebug, $pjaxWrapper, $window } from '../utils/environment';
import { EVENT as APP_EVENT } from '../app';
//List here all of your transitions
import * as transitions from './transitions';
const MODULE_NAME = 'Transition';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;
export const EVENT = {
CLICK: `click.${EVENT_NAMESPACE}`,
READYTOAPPEND: `readyToAppend.${EVENT_NAMESPACE}`,
READYTODESTROY: `readyToDestroy.${EVENT_NAMESPACE}`,
GOTO: `goto.${EVENT_NAMESPACE}`
};
/*
@todo :
- ✅ get data-transition on clicked link -> launch() and add switch(){}
- ✅ add goto listener
- ✅ add overrideClass system for all transitions
- ✅ add base class manager like old DefaultTransition (has-dom-loaded, has-dom-loading etc..)
======= SCHEMA =======
[] : listener
* : trigger event
[pjax:send] -> (transition) launch()
[pjax:switch] (= new view is loaded) -> (transition) hideView()-> hide animations & *readyToAppend
[readyToAppend] -> append() -> delete modules
-> remove oldView from the DOM, and innerHTMl newView
-> change()
display() -> (transition) displayView() -> display animations & *readyToDestroy
-> init new modules
[readyToAppend] -> reinit()
*/
export default class {
constructor() {
// jQuery ondomready
$window.on('load',() => {
this.load();
});
this.transition = new transitions['BaseTransition']({
wrapper: this.wrapper
});
/*
===== PJAX CONFIGURATION =====
*/
this.containerClass = '.js-pjax-container';
this.wrapperId = 'js-pjax-wrapper';
this.noPjaxRequestClass = 'no-transition';
this.wrapper = document.getElementById(this.wrapperId);
this.options = {
debug: false,
cacheBust: false,
elements: [`a:not(.${this.noPjaxRequestClass})`,'form[action]'],
selectors: ['title',`${this.containerClass}`],
switches: {},
requestOptions: {
timeout: 2000
}
};
this.options.switches[this.containerClass] = (oldEl, newEl, options) => this.switch(oldEl, newEl, options)
this.pjax = new Pjax(this.options);
/*
===== LISTENERS =====
*/
document.addEventListener('pjax:send',(e) => this.send(e));
$document.on(EVENT.READYTOAPPEND,(event) => {
this.append(event.oldView, event.newView);
});
$document.on(EVENT.READYTODESTROY,(event) => {
this.reinit();
});
/** goto exampe
$document.triggerHandler({
type: 'goto.Transition',
options : {
el: {{element clicked?}},
link: {{url}}
}
});
*/
$document.on(EVENT.GOTO, (e) => {
if(e.options.el != undefined) {
this.autoEl = e.options.el.get(0);
}
this.pjax.loadUrl(e.options.link, $.extend({}, this.pjax.options));
});
}
/**
* (PJAX) Launch when pjax receive a request
* get & manage data-transition,init and launch it
* @param {event}
* @return void
*/
send(e) {
if(isDebug) {
console.log("---- Launch request 🙌 -----");
}
let el,transition;
if(e.triggerElement != undefined) {
el = e.triggerElement;
transition = el.getAttribute('data-transition') ? el.getAttribute('data-transition') : 'BaseTransition';
$html.attr('data-transition',transition);
} else {
if (this.autoEl != undefined) {
el = this.autoEl;
} else {
el = document;
}
transition = 'BaseTransition';
}
// options available : wrapper, overrideClass
this.transition = new transitions[transition]({
wrapper: this.wrapper,
clickedLink: el
});
this.transition.launch();
}
/**
* (PJAX) Launch when new page is loaded
* @param {js dom element},
* @param {js dom element}
* @param {options : pjax options}
* @return void
*/
switch(oldView, newView, options) {
if(isDebug) {
console.log('---- Next view loaded 👌 -----');
}
this.transition.hideView(oldView, newView);
}
/**
* Launch when you trigger EVENT.READYTOAPPEND in your transition
* after newView append, launch this.change()
* @param {js dom element},
* @param {js dom element}
* @return void
*/
append(oldView, newView) {
newView.style.opacity = 0;
this.wrapper.appendChild(newView);
// Add these 2 rAF if you want to have the containers overlapped
// Useful with a image transition, to prevent flickering
// requestAnimationFrame(() => {
// requestAnimationFrame(() => {
newView.style.opacity = 1;
this.change(oldView, newView);
// });
// });
}
/**
* launch after this.append(), remove modules, remove oldView and set the newView
* @param {js dom element},
* @return void
*/
change(oldView, newView) {
$document.triggerHandler({
type: APP_EVENT.DELETE_SCOPED_MODULES,
$scope: $pjaxWrapper
});
this.wrapper.innerHTML = newView.outerHTML;
oldView.remove();
// Fetch any inline script elements.
const scripts = newView.querySelectorAll('script.js-inline');
if (scripts instanceof window.NodeList) {
let i = 0;
let len = scripts.length;
for (; i < len; i++) {
eval(scripts[i].innerHTML);
}
}
$document.triggerHandler({
type: APP_EVENT.INIT_SCOPED_MODULES,
isPjax: true
});
this.pjax.onSwitch();
this.transition.displayView(newView);
}
/**
* Launch when you trigger EVENT.READYTODESTROY in your transition -> displayView(), at the end
* @return void
*/
reinit() {
this.transition.destroy();
$html.attr('data-transition','');
this.transition = new transitions['BaseTransition']({
wrapper: this.wrapper
});
}
/**
* DOM is loaded
*
* @return {void}
*/
load() {
$html.addClass('has-dom-loaded');
$html.removeClass('has-dom-loading');
setTimeout(() => {
$html.addClass('has-dom-animated');
}, 1000)
}
}

View File

@@ -1,2 +0,0 @@
export {default as BaseTransition} from './BaseTransition';
export {default as CustomTransition} from './CustomTransition';

View File

@@ -86,3 +86,16 @@ export function findByKeyValue( array, key, value ) {
export function cloneArray( array ) {
return JSON.parse(JSON.stringify(array));
}
/**
* Shuffles array in place. ES6 version
* @param {Array} a items An array containing the items.
*/
export function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}

View File

@@ -1,12 +1,8 @@
const APP_NAME = 'Boilerplate';
const DATA_API_KEY = '.data-api';
const $document = $(document);
const $window = $(window);
const $html = $(document.documentElement).removeClass('has-no-js').addClass('has-js');
const $body = $(document.body);
const $pjaxWrapper = $('#js-pjax-wrapper');
const html = document.documentElement;
const body = document.body;
const isDebug = html.hasAttribute('data-debug');
const isDebug = !!$html.data('debug');
export { APP_NAME, DATA_API_KEY, $document, $window, $html, $body, isDebug, $pjaxWrapper };
export { APP_NAME, DATA_API_KEY, html, body, isDebug };

View File

@@ -94,3 +94,48 @@ export function getData(data) {
return data;
}
/**
* Returns an array containing all the parent nodes of the given node
* @param {object} node
* @return {array} parent nodes
*/
export function getParents(elem) {
// Set up a parent array
let parents = [];
// Push each parent element to the array
for ( ; elem && elem !== document; elem = elem.parentNode ) {
parents.push(elem);
}
// Return our parent array
return parents;
}
// https://gomakethings.com/how-to-get-the-closest-parent-element-with-a-matching-selector-using-vanilla-javascript/
export function queryClosestParent(elem, selector) {
// Element.matches() polyfill
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i = matches.length;
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1;
};
}
// Get the closest matching element
for ( ; elem && elem !== document; elem = elem.parentNode ) {
if ( elem.matches( selector ) ) return elem;
}
return null;
};

View File

@@ -0,0 +1,87 @@
const LAZY_LOADED_IMAGES = []
export function loadImage(url, options = {}) {
return new Promise((resolve, reject) => {
const $img = new Image();
if (options.crossOrigin) {
$img.crossOrigin = options.crossOrigin;
}
const loadCallback = () => {
resolve({
element: $img,
...getImageMetadata($img),
});
}
if($img.decode) {
$img.src = url
$img.decode().then(loadCallback).catch(e => {
reject(e)
})
} else {
$img.onload = loadCallback
$img.onerror = (e) => {
reject(e);
};
$img.src = url
}
});
}
export function getImageMetadata($img) {
return {
url: $img.src,
width: $img.naturalWidth,
height: $img.naturalHeight,
ratio: $img.naturalWidth / $img.naturalHeight,
};
}
/**
* Lazy load the given image.
*
* @param {HTMLImageElement} $el - The image element.
* @param {?string} url - The URI to lazy load into $el.
* If falsey, the value of the `data-src` attribute on $el will be used as the URI.
* @param {?function} callback - A function to call when the image is loaded.
*/
export async function lazyLoadImage($el, url, callback) {
let src = url ? url : $el.dataset.src
let loadedImage = LAZY_LOADED_IMAGES.find(image => image.url === src)
if (!loadedImage) {
loadedImage = await loadImage(src)
if (!loadedImage.url) {
return;
}
LAZY_LOADED_IMAGES.push(loadedImage)
}
if($el.src === src) {
return
}
if ($el.tagName === 'IMG') {
$el.src = loadedImage.url;
} else {
$el.style.backgroundImage = `url(${loadedImage.url})`;
}
requestAnimationFrame(() => {
let lazyParent = $el.closest('.c-lazy');
if(lazyParent) {
lazyParent.classList.add('-lazy-loaded')
lazyParent.style.backgroundImage = ''
}
$el.classList.add('-lazy-loaded')
callback?.()
})
}

View File

@@ -0,0 +1,3 @@
export function lerp(start, end, amt){
return (1 - amt) * start + amt * end
}

View File

@@ -1,63 +0,0 @@
import { isNumeric } from './is'
let isAnimating = false;
const defaults = {
easing: 'swing',
headerOffset: 60,
speed: 300
};
/**
* scrollTo is a function that scrolls a container to an element's position within that controller
* Uses jQuery's $.Deferred to allow using a callback on animation completion
* @param {object} $element A jQuery node
* @param {object} options
*/
export function scrollTo($element, options) {
const deferred = $.Deferred();
// Drop everything if this ain't a jQuery object
if ($element instanceof jQuery && $element.length > 0) {
// Merging options
options = $.extend({}, defaults, (typeof options !== 'undefined' ? options : {}));
// Prevents accumulation of animations
if (isAnimating === false) {
isAnimating = true;
// Default container that we'll be scrolling
let $container = $('html, body');
let elementOffset = 0;
// Testing container in options for jQuery-ness
// If we're not using a custom container, we take the top document offset
// If we are, we use the elements position relative to the container
if (typeof options.$container !== 'undefined' && options.$container instanceof jQuery && options.$container.length > 0) {
$container = options.$container;
if (typeof options.scrollTop !== 'undefined' && isNumeric(options.scrollTop) && options.scrollTop !== 0) {
scrollTop = options.scrollTop;
} else {
scrollTop = $element.position().top - options.headerOffset;
}
} else {
if (typeof options.scrollTop !== 'undefined' && isNumeric(options.scrollTop) && options.scrollTop !== 0) {
scrollTop = options.scrollTop;
} else {
scrollTop = $element.offset().top - options.headerOffset;
}
}
$container.animate({
scrollTop: scrollTop
}, options.speed, options.easing, function() {
isAnimating = false;
deferred.resolve();
});
}
}
return deferred.promise();
}

View File

@@ -0,0 +1,21 @@
export function transform(el, transformValue){
el.style.webkitTransform = transformValue;
el.style.msTransform = transformValue;
el.style.transform = transformValue;
}
export function getTranslate(el){
const translate = {}
if(!window.getComputedStyle) return;
const style = getComputedStyle(el);
const transform = style.transform || style.webkitTransform || style.mozTransform;
let mat = transform.match(/^matrix3d\((.+)\)$/);
if(mat) return parseFloat(mat[1].split(', ')[13]);
mat = transform.match(/^matrix\((.+)\)$/);
translate.x = mat ? parseFloat(mat[1].split(', ')[4]) : 0;
translate.y = mat ? parseFloat(mat[1].split(', ')[5]) : 0;
return translate;
}

View File

@@ -1,64 +0,0 @@
// ==========================================================================
// Base / Headings
// ==========================================================================
@mixin h {
margin-top: 0;
line-height: $line-height-h;
}
//
// Provide a generic class to apply common heading styles.
//
// @example
// <p class="u-h"></p>
//
//
.o-h {
@include h;
}
//
// Styles for headings 1 through 6 with classes to provide
// a double stranded heading hierarchy, e.g. we semantically
// need an H2, but we want it to be sized like an H1:
//
// @example
// <h2 class="o-h1"></h2>
//
//
h1, .o-h1 {
@extend .o-h;
font-size: rem($font-size-h1);
}
h2, .o-h2 {
@extend .o-h;
font-size: rem($font-size-h2);
}
h3, .o-h3 {
@extend .o-h;
font-size: rem($font-size-h3);
}
h4, .o-h4 {
@extend .o-h;
font-size: rem($font-size-h4);
}
h5, .o-h5 {
@extend .o-h;
font-size: rem($font-size-h5);
}
h6, .o-h6 {
@extend .o-h;
font-size: rem($font-size-h6);
}

View File

@@ -0,0 +1,8 @@
.c-button {
padding: rem(15px) rem(20px);
background-color: lightgray;
@include u-hocus {
background-color: darkgray;
}
}

View File

@@ -1,27 +1,37 @@
// ==========================================================================
// Objects / Buttons
// Form
// ==========================================================================
.c-form {
}
.c-form_item {
position: relative;
margin-bottom: rem(30px);
}
// Label
// =============================================================================
.o-label {
// ==========================================================================
.c-form_label {
display: block;
margin-bottom: rem(15px);
margin-bottom: rem(10px);
}
// Input
// =============================================================================
// ==========================================================================
$input-icon-color: 424242; // No #
.o-input {
.c-form_input {
padding: rem(10px);
border-width: 1px;
border-style: solid;
border-color: lightgray;
border: 1px solid lightgray;
background-color: white;
&:hover {
border-color: darkgray;
}
&:focus {
border-color: gray;
border-color: dimgray;
}
&::placeholder {
@@ -30,42 +40,21 @@ $input-icon-color: 424242; // No #
}
// Checkbox
// =============================================================================
// ==========================================================================
$checkbox: rem(18px);
$checkbox-icon-color: $input-icon-color;
.o-checkbox {
position: absolute;
width: 0;
opacity: 0;
&:focus {
+ .o-checkbox-label {
&::before {
border-color: gray;
}
}
}
&:checked {
+ .o-checkbox-label {
&::after {
opacity: 1;
}
}
}
}
.o-checkbox-label {
@extend .o-label;
.c-form_checkboxLabel {
@extend .c-form_label;
position: relative;
display: inline-block;
margin-right: 0.5em;
margin-right: rem(10px);
margin-bottom: 0;
padding-left: ($checkbox + rem(10px));
cursor: pointer;
&::before, &::after {
position: absolute;
top: 50%;
left: 0;
@@ -79,6 +68,7 @@ $checkbox-icon-color: $input-icon-color;
&::before {
background-color: $white;
border: 1px solid lightgray;
}
&::after {
@@ -86,22 +76,42 @@ $checkbox-icon-color: $input-icon-color;
background-color: transparent;
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2210.5%22%20viewBox%3D%220%200%2013%2010.5%22%20enable-background%3D%22new%200%200%2013%2010.5%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23#{$checkbox-icon-color}%22%20d%3D%22M4.8%205.8L2.4%203.3%200%205.7l4.8%204.8L13%202.4c0%200-2.4-2.4-2.4-2.4L4.8%205.8z%22%2F%3E%3C%2Fsvg%3E");
background-position: center;
background-size: rem(13px);
background-size: rem(12px);
background-repeat: no-repeat;
opacity: 0;
}
&:hover {
&::before {
border-color: darkgray;
}
}
.c-form_checkbox:focus + & {
&::before {
border-color: dimgray;
}
}
.c-form_checkbox:checked + & {
&::after {
opacity: 1;
}
}
}
.c-form_checkbox {
position: absolute;
width: 0;
opacity: 0;
}
// Radio
// =============================================================================
// ==========================================================================
$radio-icon-color: $input-icon-color;
.o-radio {
@extend .o-checkbox;
}
.o-radio-label {
@extend .o-checkbox-label;
.c-form_radioLabel {
@extend .c-form_checkboxLabel;
&::before, &::after {
border-radius: 50%;
@@ -109,25 +119,22 @@ $radio-icon-color: $input-icon-color;
&::after {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20enable-background%3D%22new%200%200%2013%2013%22%20xml%3Aspace%3D%22preserve%22%3E%3Ccircle%20fill%3D%22%23#{$radio-icon-color}%22%20cx%3D%226.5%22%20cy%3D%226.5%22%20r%3D%226.5%22%2F%3E%3C%2Fsvg%3E");
background-size: rem(8px);
background-size: rem(6px);
}
}
.c-form_radio {
@extend .c-form_checkbox;
}
// Select
// =============================================================================
$select-icon: rem(40px);
$select-icon-color: $input-icon-color;
.o-select {
@extend .o-input;
position: relative;
z-index: 1;
padding-right: $select-icon;
}
.o-select-wrap {
.c-form_select {
position: relative;
cursor: pointer;
&::after {
position: absolute;
@@ -138,17 +145,26 @@ $select-icon-color: $input-icon-color;
width: $select-icon;
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2213%22%20height%3D%2211.3%22%20viewBox%3D%220%200%2013%2011.3%22%20enable-background%3D%22new%200%200%2013%2011.3%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23#{$select-icon-color}%22%20points%3D%226.5%2011.3%203.3%205.6%200%200%206.5%200%2013%200%209.8%205.6%20%22%2F%3E%3C%2Fsvg%3E");
background-position: center;
background-size: rem(10px);
background-size: rem(8px);
background-repeat: no-repeat;
content: "";
pointer-events: none;
}
}
.c-form_select_input {
@extend .c-form_input;
position: relative;
z-index: 1;
padding-right: $select-icon;
cursor: pointer;
}
// Textarea
// =============================================================================
.o-textarea {
@extend .o-input;
.c-form_textarea {
@extend .c-form_input;
min-height: rem(100px);
min-height: rem(200px);
}

View File

@@ -0,0 +1,28 @@
.c-heading {
line-height: $line-height-h;
margin-bottom: rem(30px);
&.-h1 {
font-size: rem($font-size-h1);
}
&.-h2 {
font-size: rem($font-size-h2);
}
&.-h3 {
font-size: rem($font-size-h3);
}
&.-h4 {
font-size: rem($font-size-h4);
}
&.-h5 {
font-size: rem($font-size-h5);
}
&.-h6 {
font-size: rem($font-size-h6);
}
}

View File

@@ -0,0 +1,83 @@
// =============================================================================
// Modal Dialog
// =============================================================================
// Adapted from https://codesandbox.io/s/a11y-dialog-pnwqu?file=/src/styles.css
// =============================================================================
// Component settings
// =============================================================================
$dialog-container-zindex: overlay-zindex(modal) !default;
$dialog-backdrop-background-color: rgba(0, 0, 0, 0.9) !default;
$dialog-content-background-color: rgb(255, 255, 255) !default;
// Basic component styles
// =============================================================================
@keyframes fade-in {
from {
opacity: 0;
}
}
@keyframes slide-up {
from {
transform: translateY(10%);
}
}
.has-modal-open {
overflow-y: hidden;
}
.c-dialog_container {
display: flex;
overflow: scroll;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: $dialog-container-zindex;
&[aria-hidden='true'] {
display: none;
}
}
.c-dialog_overlay {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: $dialog-backdrop-background-color;
animation: fade-in 200ms both;
}
.c-dialog_content {
background-color: $dialog-content-background-color;
margin: auto;
z-index: 2;
position: relative;
animation: fade-in 400ms 200ms both, slide-up 400ms 200ms both;
padding: 1em;
max-width: 90%;
width: 600px;
border-radius: 2px;
@media screen and (min-width: $from-small) {
padding: 2em;
}
}
.c-dialog_close {
position: absolute;
top: 0.5em;
right: 0.5em;
@media screen and (min-width: $from-small) {
top: 1em;
right: 1em;
}
}

View File

@@ -0,0 +1,34 @@
.c-scrollbar {
position: absolute;
right: 0;
top: 0;
width: 11px;
height: 100vh;
transform-origin: center right;
transition: transform 0.3s, opacity 0.3s;
opacity: 0;
&:hover {
transform: scaleX(1.45);
}
&:hover, .has-scroll-scrolling &, .has-scroll-dragging & {
opacity: 1;
}
}
.c-scrollbar_thumb {
position: absolute;
top: 0;
right: 0;
background-color: black;
opacity: 0.5;
width: 7px;
border-radius: 10px;
margin: 2px;
cursor: grab;
.has-scroll-dragging & {
cursor: grabbing;
}
}

View File

@@ -0,0 +1 @@
$assets-path: "assets/";

View File

@@ -7,28 +7,26 @@
//
// 1. Set the default `font-size` and `line-height` for the entire project,
// sourced from our default variables.
// 2. Force scrollbars to always be visible to prevent awkward jumps when
// navigating between pages that do/do not have enough content to produce
// scrollbars naturally.
// 3. Ensure the page always fills at least the entire height of the viewport.
// 2. Ensure the page always fills at least the entire height of the viewport.
//
html {
overflow-y: scroll; /* [2] */
min-height: 100%; /* [3] */
min-height: 100%; /* [2] */
color: $color;
font-family: $font-family;
line-height: $line-height; /* [1] */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: $to-small) {
font-size: 12px;
font-size: $font-size - 2px;
}
@media (min-width: $from-small) and (max-width: $to-medium) {
font-size: 13px;
font-size: $font-size - 2px;
}
@media (min-width: $from-medium) and (max-width: $to-large) {
font-size: 14px;
font-size: $font-size - 1px;
}
@media (min-width: $from-large) and (max-width: $to-huge) {
@@ -36,15 +34,33 @@ html {
}
@media (min-width: $from-huge) and (max-width: $to-gigantic) {
font-size: 18px;
font-size: $font-size + 1px;
}
@media (min-width: $from-gigantic) and (max-width: $to-colossal) {
font-size: 21px;
font-size: $font-size + 2px;
}
@media (min-width: $from-colossal) {
font-size: 24px;
font-size: $font-size + 4px;
}
&.is-loading {
cursor: wait;
}
&.has-scroll-smooth {
overflow: hidden;
}
&.has-scroll-dragging {
user-select: none;
}
}
body {
.has-scroll-smooth & {
overflow: hidden;
}
}
@@ -55,9 +71,9 @@ html {
}
a {
color: $link-color;
@include u-hocus {
color: $link-hover-color;
}
color: $link-color;
}

View File

@@ -11,7 +11,7 @@
// 6. Force all button-styled elements to appear clickable.
//
button,
.o-button {
.c-button {
@include u-hocus {
text-decoration: none;
}

View File

@@ -45,6 +45,23 @@ a {
}
}
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
*/

View File

@@ -4,6 +4,7 @@
// Settings
// ==========================================================================
@import "settings/config.eases";
@import "settings/config.colors";
@import "settings/config";
@@ -14,9 +15,8 @@
@import "tools/mixins";
@import "tools/fonts";
@import "tools/layout";
// @import "tools/ratio";
// @import "tools/widths";
// @import "tools/familly";
@import "tools/widths";
// @import "tools/family";
// Generic
// ==========================================================================
@@ -26,23 +26,19 @@
@import "generic/form";
@import "generic/button";
// Base
// Elements
// ==========================================================================
@import "base/fonts";
@import "base/page";
@import "base/headings";
@import "elements/fonts";
@import "elements/page";
// Objects
// ==========================================================================
@import "objects/container";
// @import "objects/crop";
// @import "objects/ratio";
// @import "objects/table";
@import "objects/layout";
@import "objects/form";
@import "objects/button";
@import "objects/pjax";
@import "objects/scroll";
@import "objects/container";
@import "objects/ratio";
@import "objects/layout";
// @import "objects/crop";
// @import "objects/table";
// Vendors
// ==========================================================================
@@ -50,7 +46,11 @@
// Components
// ==========================================================================
// @import "components/component";
@import "components/scrollbar";
@import "components/heading";
@import "components/button";
@import "components/form";
@import "components/modal";
// Templates
// ==========================================================================
@@ -58,10 +58,10 @@
// Utilities
// ==========================================================================
@import "utilities/ratio";
@import "utilities/widths";
// @import "utilities/align";
// @import "utilities/helpers";
// @import "utilities/states";
// @import "utilities/headings";
// @import "utilities/spacing";
// @import "utilities/widths";
// @import "utilities/print";

View File

@@ -1,11 +0,0 @@
// ==========================================================================
// Objects / Buttons
// ==========================================================================
.o-button {
@include u-hocus {
background-color: gray;
}
padding: rem(10px);
background-color: lightgray;
}

View File

@@ -12,16 +12,10 @@
// @link http://stackoverflow.com/a/13202141/140357
//
/* stylelint-disable */
@if (type-of($container-width) != number) {
@error "`#{$container-width}` needs to be a number."
}
/* stylelint-enable */
.o-container {
margin-right: auto;
margin-left: auto;
padding-right: $padding;
padding-left: $padding;
max-width: $container-width;
padding-right: rem($padding);
padding-left: rem($padding);
max-width: rem($container-width + ($padding * 2));
}

View File

@@ -47,7 +47,7 @@
}
&.-gutter-small {
margin-left: rem(-$unit/2);
margin-left: rem(-$unit-small);
}
// Horizontal aligment modifiers
@@ -94,7 +94,7 @@
}
.o-layout.-gutter-small > & {
padding-left: rem($unit/2);
padding-left: rem($unit-small);
}
// Vertical alignment modifiers

View File

@@ -1,11 +0,0 @@
.o-pjax_wrapper {
height: 100%;
overflow: hidden;
}
.o-pjax_container {
height: 100%;
overflow: hidden;
}

View File

@@ -2,20 +2,6 @@
// Objects / Ratio
// ==========================================================================
//
// @link https://github.com/inuitcss/inuitcss/blob/19d0c7e/objects/_objects.ratio.scss
//
// A list of aspect ratios that get generated as modifier classes.
$aspect-ratios: (
(2:1),
(4:3),
(16:9),
) !default;
/**
* Create ratio-bound content blocks, to keep media (e.g. images, videos) in
* their correct aspect ratios.
@@ -38,6 +24,7 @@ $aspect-ratios: (
}
.o-ratio_content,
.o-ratio > img,
.o-ratio > iframe,
.o-ratio > embed,
.o-ratio > object {
@@ -46,34 +33,5 @@ $aspect-ratios: (
bottom: 0;
left: 0;
width: 100%;
height: 100%;
// height: 100%;
}
/* stylelint-disable */
//
// Generate a series of ratio classes to be used like so:
//
// @example
// <div class="o-ratio -16:9">
//
//
.o-ratio {
@each $ratio in $aspect-ratios {
@each $antecedent, $consequent in $ratio {
@if (type-of($antecedent) != number) {
@error "`#{$antecedent}` needs to be a number."
}
@if (type-of($consequent) != number) {
@error "`#{$consequent}` needs to be a number."
}
&.-#{$antecedent}\:#{$consequent}::before {
padding-bottom: ($consequent/$antecedent) * 100%;
}
}
}
}
/* stylelint-enable */

View File

@@ -1,63 +1,3 @@
html.has-smooth-scroll {
overflow: hidden;
}
.o-scroll{
html.has-smooth-scroll & {
height: 100vh;
position: relative;
overflow: hidden;
}
}
.scroll-content {
transform: translate3d(0,0,0);
margin: 0;
overflow: visible;
height: 100%;
}
// Scrollbar
// ==========================================================================
[data-scrollbar],[scrollbar],scrollbar{display:block;position:relative}[data-scrollbar] .scroll-content,[scrollbar] .scroll-content,scrollbar .scroll-content{-webkit-transform:translateZ(0);transform:translateZ(0);will-change:transform}[data-scrollbar].sticky .scrollbar-track,[scrollbar].sticky .scrollbar-track,scrollbar.sticky .scrollbar-track{background:hsla(0,0%,87%,.75)}[data-scrollbar] .scrollbar-track,[scrollbar] .scrollbar-track,scrollbar .scrollbar-track{position:absolute;opacity:0;z-index:1;-webkit-transition:opacity .5s ease-out,background .5s ease-out;transition:opacity .5s ease-out,background .5s ease-out;background:none}[data-scrollbar] .scrollbar-track.show,[data-scrollbar] .scrollbar-track:hover,[scrollbar] .scrollbar-track.show,[scrollbar] .scrollbar-track:hover,scrollbar .scrollbar-track.show,scrollbar .scrollbar-track:hover{opacity:1}[data-scrollbar] .scrollbar-track:hover,[scrollbar] .scrollbar-track:hover,scrollbar .scrollbar-track:hover{background:hsla(0,0%,87%,.75)}[data-scrollbar] .scrollbar-track-x,[scrollbar] .scrollbar-track-x,scrollbar .scrollbar-track-x{bottom:0;left:0;width:100%;height:8px}[data-scrollbar] .scrollbar-track-y,[scrollbar] .scrollbar-track-y,scrollbar .scrollbar-track-y{top:0;right:0;width:8px;height:100%}[data-scrollbar] .scrollbar-thumb,[scrollbar] .scrollbar-thumb,scrollbar .scrollbar-thumb{position:absolute;top:0;left:0;width:8px;height:8px;background:rgba(0,0,0,.5);border-radius:4px}[data-scrollbar] .overscroll-glow,[scrollbar] .overscroll-glow,scrollbar .overscroll-glow{position:absolute;top:0;left:0;width:100%;height:100%}
.scrollbar-track {
user-select: none;
background-color: transparent !important;
width: 14px !important;
opacity: 0 !important;
z-index: 99999 !important;
.scrolling & {
opacity: 0.75 !important;
}
&:hover {
opacity: 1 !important;
background-color: #fafafa !important;
}
}
.scrollbar-thumb {
position: relative;
width: 14px !important;
background-color: transparent !important;
&::after {
content: "";
position: absolute;
top: 3px;
right: 3px;
bottom: 3px;
left: 3px;
background-color: #c1c1c1;
border-radius: 4px;
transition: background-color $speed $easing;
}
&:hover {
&::after {
background-color: #7d7d7d;
}
}
.o-scroll {
min-height: 100vh;
}

View File

@@ -0,0 +1,19 @@
$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);

View File

@@ -1,6 +1,6 @@
// ==========================================================================
// =============================================================================
// Settings / Config
// ==========================================================================
// =============================================================================
// Context
// =============================================================================
@@ -38,7 +38,7 @@ $bold: 700;
// Transitions
// =============================================================================
$speed: 0.3s;
$easing: linear;
$easing: $Power2EaseOut;
// Spacing Units
// =============================================================================
@@ -46,10 +46,25 @@ $unit: 60px;
$unit-small: 30px;
// Container
// ==========================================================================
// =============================================================================
$container-width: 2000px;
$padding: $unit;
// Z-Indexes
// =============================================================================
$overlay-zindexes: (
dropdown: 100,
sticky: 120,
fixed: 130,
offcanvas-backdrop: 140,
offcanvas: 145,
modal-backdrop: 150,
modal: 155,
popover: 170,
tooltip: 180,
transition: 190,
) !default;
// Breakpoints
// =============================================================================
$from-tiny: 500px !default;

View File

@@ -8,15 +8,15 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin first($num) {
@if $num == 1 {
&:first-child {
@content;
@if $num == 1 {
&:first-child {
@content;
}
} @else {
&:nth-child(-n + #{$num}) {
@content;
}
}
} @else {
&:nth-child(-n + #{$num}) {
@content;
}
}
}
/// Select all children from the last to `$num`.
@@ -24,9 +24,9 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin last($num) {
&:nth-last-child(-n + #{$num}) {
@content;
}
&:nth-last-child(-n + #{$num}) {
@content;
}
}
/// Select all children after the first to `$num`.
@@ -34,9 +34,9 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin after-first($num) {
&:nth-child(n + #{$num + 1}) {
@content;
}
&:nth-child(n + #{$num + 1}) {
@content;
}
}
/// Select all children before `$num` from the last.
@@ -44,45 +44,45 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin from-end($num) {
&:nth-last-child(#{$num}) {
@content;
}
&:nth-last-child(#{$num}) {
@content;
}
}
/// Select all children between `$first` and `$last`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin between($first, $last) {
&:nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
&:nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
}
/// Select all even children between `$first` and `$last`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin even-between($first, $last) {
&:nth-child(even):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
&:nth-child(even):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
}
/// Select all odd children between `$first` and `$last`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin odd-between($first, $last) {
&:nth-child(odd):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
&:nth-child(odd):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
}
/// Select all `$num` children between `$first` and `$last`.
/// @group with-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin n-between($num, $first, $last) {
&:nth-child(#{$num}n):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
&:nth-child(#{$num}n):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
@content;
}
}
@@ -91,9 +91,9 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin all-but($num) {
&:not(:nth-child(#{$num})) {
@content;
}
&:not(:nth-child(#{$num})) {
@content;
}
}
/// Select children each `$num`.
@@ -102,9 +102,9 @@
/// @param {number} $num - id of the child
/// @alias every
@mixin each($num) {
&:nth-child(#{$num}n) {
@content;
}
&:nth-child(#{$num}n) {
@content;
}
}
/// Select children each `$num`.
@@ -112,9 +112,9 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin every($num) {
&:nth-child(#{$num}n) {
@content;
}
&:nth-child(#{$num}n) {
@content;
}
}
/// Select the `$num` child from the start and the `$num` child from the last.
@@ -122,10 +122,10 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin from-first-last($num) {
&:nth-child(#{$num}),
&:nth-last-child(#{$num}) {
@content;
}
&:nth-child(#{$num}),
&:nth-last-child(#{$num}) {
@content;
}
}
@@ -135,9 +135,9 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin middle($num) {
&:nth-child(#{round($num / 2)}) {
@content;
}
&:nth-child(#{round($num / 2)}) {
@content;
}
}
@@ -146,9 +146,9 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @param {number} $num - id of the child
@mixin all-but-first-last($num) {
&:nth-child(n + #{$num}):nth-last-child(n + #{$num}) {
@content;
}
&:nth-child(n + #{$num}):nth-last-child(n + #{$num}) {
@content;
}
}
@@ -158,9 +158,9 @@
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin first-of($limit) {
&:nth-last-child(#{$limit}):first-child {
@content;
}
&:nth-last-child(#{$limit}):first-child {
@content;
}
}
/// This quantity-query mixin will only select the last of `$limit` items. It will not
@@ -169,9 +169,9 @@
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin last-of($limit) {
&:nth-of-type(#{$limit}):nth-last-of-type(1) {
@content;
}
&:nth-of-type(#{$limit}):nth-last-of-type(1) {
@content;
}
}
/// This quantity-query mixin will select every items if there is at least `$num` items. It will not
@@ -180,13 +180,13 @@
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin at-least($num) {
$selector: &;
$child: nth(nth($selector, -1), -1);
$selector: &;
$child: nth(nth($selector, -1), -1);
&:nth-last-child(n + #{$num}),
&:nth-last-child(n + #{$num}) ~ #{$child} {
@content;
}
&:nth-last-child(n + #{$num}),
&:nth-last-child(n + #{$num}) ~ #{$child} {
@content;
}
}
/// This quantity-query mixin will select every items if there is at most `$num` items. It will not
@@ -195,13 +195,13 @@
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin at-most($num) {
$selector: &;
$child: nth(nth($selector, -1), -1);
$selector: &;
$child: nth(nth($selector, -1), -1);
&:nth-last-child(-n + #{$num}):first-child,
&:nth-last-child(-n + #{$num}):first-child ~ #{$child} {
@content;
}
&:nth-last-child(-n + #{$num}):first-child,
&:nth-last-child(-n + #{$num}):first-child ~ #{$child} {
@content;
}
}
/// This quantity-query mixin will select every items only if there is between `$min` and `$max` items.
@@ -209,59 +209,59 @@
/// @param {number} $limit
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin in-between($min, $max) {
$selector: &;
$child: nth(nth($selector, -1), -1);
$selector: &;
$child: nth(nth($selector, -1), -1);
&:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child,
&:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child ~ #{$child} {
@content;
}
&:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child,
&:nth-last-child(n + #{$min}):nth-last-child(-n + #{$max}):first-child ~ #{$child} {
@content;
}
}
/// Select the first exact child
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin first-child() {
&:first-of-type {
@content
}
&:first-of-type {
@content
}
}
/// Select the last exact child
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin last-child() {
&:last-of-type {
@content
}
&:last-of-type {
@content
}
}
/// Select all even children.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin even() {
&:nth-child(even) {
@content;
}
&:nth-child(even) {
@content;
}
}
/// Select all odd children.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin odd() {
&:nth-child(odd) {
@content;
}
&:nth-child(odd) {
@content;
}
}
/// Select only the first and last child.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin first-last() {
&:first-child,
&:last-child {
@content;
}
&:first-child,
&:last-child {
@content;
}
}
/// Will only select the child if its unique.
@@ -269,18 +269,18 @@
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
/// @alias only
@mixin unique() {
&:only-child {
@content;
}
&:only-child {
@content;
}
}
/// Will only select the child if its unique.
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin only() {
&:only-child {
@content;
}
&:only-child {
@content;
}
}
/// Will only select children if they are not unique. Meaning if there is at
@@ -288,9 +288,9 @@
/// @group no-arguments
/// @content [Write the style you want to apply to the children, and it will be added within the @content directive]
@mixin not-unique() {
&:not(:only-child) {
@content;
}
&:not(:only-child) {
@content;
}
}
@@ -302,19 +302,19 @@
/// @param {string} $direction [forward] - Direction of the sort
/// @param {number} $index [0] - Index of the sorting
@mixin child-index($num, $direction: 'forward', $index: 0) {
@for $i from 1 through $num {
@if ($direction == 'forward') {
&:nth-child(#{$i}) {
z-index: order-index($i, $index);
@content;
}
} @else if ($direction == 'backward') {
&:nth-last-child(#{$i}) {
z-index: order-index($i, $index);
@content;
}
@for $i from 1 through $num {
@if ($direction == 'forward') {
&:nth-child(#{$i}) {
z-index: order-index($i, $index);
@content;
}
} @else if ($direction == 'backward') {
&:nth-last-child(#{$i}) {
z-index: order-index($i, $index);
@content;
}
}
}
}
}
/// Used by the child-index mixin. It will returned the proper sorted numbers
@@ -323,5 +323,5 @@
/// @param {number} $num - Number of children
/// @param {number} $index - Index of the sorting
@function order-index($i, $index) {
@return ($index + $i);
@return ($index + $i);
}

View File

@@ -89,6 +89,54 @@
@return true;
}
//
// Retrieves the overlay component z-index.
//
// Adapted from OZMap.
//
// 1. The elements of the global z-index map may contain `null`
// (to indicate that the z-index value should be set automatically).
// 2. The elements of the global z-index map may contain
// a hardcoded number, to be used as the z-index for that element.
// - Note that the value must be greater than any preceding element,
// to maintain the order.
//
// @link https://rafistrauss.com/blog/ordered_z_index_maps/ OZMap
//
// @param {String} $list-key - The global z-index map key.
// @param {Number} $modifier - A positive or negative modifier to apply
// to the returned z-index value.
// @throw Error if the $list-key does not exist or value is unacceptable.
// @return {Number} The next available z-index value.
//
@function overlay-zindex($list-key, $modifier: 0) {
@if map-has-key($overlay-zindexes, $key: $list-key) {
$accumulator: 0;
@each $key, $val in $overlay-zindexes {
@if ($val == null) {
$accumulator: $accumulator + $overlay-zindex-increment;
$val: $accumulator;
} @else {
@if ($val <= $accumulator) {
//* If the z-index is not greater than the elements preceding it,
//* the whole element-order paradigm is invalidated
@error "z-index for #{$key} must be greater than the preceding value!";
}
$accumulator: $val;
}
@if ($key == $list-key) {
@return ($accumulator + $modifier);
}
}
} @else {
@error "#{$list-key} doesn't exist in the $overlay-zindexes map";
}
}
$overlay-zindex-increment: 1 !default;
//
// Resolve whether a rule is important or not.
//
@@ -124,5 +172,3 @@
@function is-frontend() {
@return ('frontend' == $context);
}
$context: 'frontend' !default;

View File

@@ -21,7 +21,9 @@
// @output `font-size`, `margin`, `padding`, `list-style`
//
@mixin o-layout($gutter: 0, $fix-whitespace: true) {
@include u-list-reset;
margin: 0;
padding: 0;
list-style: none;
@if ($fix-whitespace) {
font-size: 0;

View File

@@ -121,17 +121,6 @@
}
}
//
// Injects generic rules for disabling UL/OL/LI styles.
//
// @output `list-style`, `margin`, `padding`
//
@mixin u-list-reset {
margin: 0;
padding: 0;
list-style: none;
}
//
// Prevent text from wrapping onto multiple lines for the current element.
//

View File

@@ -1,76 +0,0 @@
// ==========================================================================
// Tools / Ratio Constraint
// ==========================================================================
//
// A tool to restrain a container to a unitary or fractional proportion.
//
$data-ratios: "1/2" "0.5" 50%,
"11/20" "0.55" 55%,
"3/5" "0.6" 60%,
"13/20" "0.65" 65%,
"7/10" "0.7" 70%,
"3/4" "0.75" 75%,
"4/5" "0.8" 80%,
"17/20" "0.85" 85%,
"9/10" "0.9" 90%,
"19/20" "0.95" 95%,
"1/1" "1" 100%,
"21/20" "1.05" 105%,
"11/10" "1.1" 110%,
"23/20" "1.15" 115%,
"6/5" "1.2" 120%,
"5/4" "1.25" 125% !default;
$data-ratio-crops: "top" "bottom" "both" !default;
@mixin crop($crop) {
@if $crop == "top" {
bottom: 0;
} @else if $crop == "bottom" {
top: 0;
} @else if $crop == "both" {
top: 50%;
transform: translateY(-50%);
}
}
.u-ratio {
position: relative;
overflow: hidden;
&::before {
display: block;
width: 100%;
content: "";
}
@each $ratio in $data-ratios {
$ratio-1: nth($ratio, 1);
$ratio-2: nth($ratio, 2);
&[data-ratio="#{$ratio-1}"]::before,
&[data-ratio="#{$ratio-2}"]::before {
padding-top: nth($ratio, 3);
}
}
}
.u-ratio_content_container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
}
.u-ratio_content {
position: absolute;
width: 100%;
@each $crop in $data-ratio-crops {
&[data-ratio-crop="#{$crop}"] {
@include crop($crop);
}
}
}

View File

@@ -1,40 +0,0 @@
// ==========================================================================
// Utilities / Headings
// ==========================================================================
/**
* Redefine all of our basic heading styles against utility classes so as to
* provide larger (or smaller) generic font sizes. Anything more opinionated
* than simple font-size changes should likely be applied via "o-" classes
*
* @example
* <p class="u-h1"></p>
*
* @requires base/headings
* @link http://csswizardry.com/2016/02/managing-typography-on-large-apps/
* @link https://github.com/inuitcss/inuitcss/blob/develop/utilities/_utilities.headings.scss
*/
.u-h1 {
font-size: rem($font-size-h1) !important;
}
.u-h2 {
font-size: rem($font-size-h2) !important;
}
.u-h3 {
font-size: rem($font-size-h3) !important;
}
.u-h4 {
font-size: rem($font-size-h4) !important;
}
.u-h5 {
font-size: rem($font-size-h5) !important;
}
.u-h6 {
font-size: rem($font-size-h6) !important;
}

View File

@@ -0,0 +1,42 @@
// ==========================================================================
// Utilities / Ratio
// ==========================================================================
//
// @link https://github.com/inuitcss/inuitcss/blob/19d0c7e/objects/_objects.ratio.scss
//
// A list of aspect ratios that get generated as modifier classes.
$aspect-ratios: (
(2:1),
(4:3),
(16:9),
) !default;
/* stylelint-disable */
//
// Generate a series of ratio classes to be used like so:
//
// @example
// <div class="o-ratio u-16:9">
//
//
@each $ratio in $aspect-ratios {
@each $antecedent, $consequent in $ratio {
@if (type-of($antecedent) != number) {
@error "`#{$antecedent}` needs to be a number."
}
@if (type-of($consequent) != number) {
@error "`#{$consequent}` needs to be a number."
}
&.u-#{$antecedent}\:#{$consequent}::before {
padding-bottom: ($consequent/$antecedent) * 100%;
}
}
}
/* stylelint-enable */

View File

@@ -37,8 +37,9 @@ $spacing-properties: (
$spacing-sizes: (
null: $unit,
'-double': $unit * 2,
'-small': $unit-small,
'-none': 0
'-none': 0px
) !default;
@each $property-namespace, $property in $spacing-properties {
@@ -46,7 +47,7 @@ $spacing-sizes: (
@each $size-namespace, $size in $spacing-sizes {
.u-#{$property-namespace}#{$direction-namespace}#{$size-namespace} {
@each $direction in $direction-rules {
#{$property}#{$direction}: $size !important;
#{$property}#{$direction}: rem($size) !important;
}
}
}

View File

@@ -18,3 +18,9 @@
$widths-fractions: 1 2 3 4 5 !default;
@include widths($widths-fractions);
.u-1\/2\@from-small {
@media (min-width: $from-small) {
width: span(1/2);
}
}

9
build/build.js Normal file
View File

@@ -0,0 +1,9 @@
import concatFiles from './tasks/concats.js';
import compileScripts from './tasks/scripts.js';
import compileStyles from './tasks/styles.js' ;
import compileSVGs from './tasks/svgs.js' ;
concatFiles();
compileScripts();
compileStyles();
compileSVGs();

View File

@@ -1,14 +0,0 @@
import gulp from 'gulp';
import gulpConcat from 'gulp-concat';
import paths from '../mconfig.json';
function concat() {
return gulp
.src([
`${paths.scripts.vendors.src}*.js`
])
.pipe(gulpConcat(`${paths.scripts.vendors.main}.js`))
.pipe(gulp.dest(paths.scripts.dest));
}
export default concat;

View File

@@ -1,14 +0,0 @@
import gulp from 'gulp';
import paths from '../mconfig.json';
import error from './error.js';
function copy() {
return gulp
.src([`./node_modules/locomotive-scroll/assets/scripts/scroll/vendors/*`])
.on('error', function(err) {
error(this, err);
})
.pipe(gulp.dest(`${paths.scripts.src}/scroll/vendors`));
}
export default copy;

View File

@@ -1,16 +0,0 @@
import browserSync from 'browser-sync';
import paths from '../mconfig.json';
export const server = browserSync.create();
function serve(done) {
server.init({
notify: false,
proxy: paths.url,
host: paths.url,
open: 'external'
});
done();
}
export default serve;

48
build/tasks/concats.js Normal file
View File

@@ -0,0 +1,48 @@
import loconfig from '../../loconfig.json';
import glob from '../utils/glob.js';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import template from '../utils/template.js';
import concat from 'concat';
import { basename } from 'node:path';
/**
* Concatenates groups of files.
*
* @async
* @return {Promise}
*/
export default async function concatFiles() {
loconfig.tasks.concats.forEach(async ({
includes,
outfile
}) => {
const filename = basename(outfile || 'undefined');
const timeLabel = `${filename} concatenated in`;
console.time(timeLabel);
try {
includes = includes.map((path) => template(path));
outfile = template(outfile);
const files = await glob(includes);
await concat(files, outfile);
if (files.length) {
message(`${filename} concatenated`, 'success', timeLabel);
} else {
message(`${filename} is empty`, 'notice', timeLabel);
}
} catch (err) {
message(`Error concatenating ${filename}`, 'error');
message(err);
notification({
title: `${filename} concatenation failed 🚨`,
message: `${err.name}: ${err.message}`
});
}
});
};

61
build/tasks/scripts.js Normal file
View File

@@ -0,0 +1,61 @@
import loconfig from '../../loconfig.json';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import template from '../utils/template.js';
import esbuild from 'esbuild';
import { basename } from 'node:path';
/**
* Bundles and minifies main JavaScript files.
*
* @async
* @return {Promise}
*/
export default async function compileScripts() {
loconfig.tasks.scripts.forEach(async ({
includes,
outdir = '',
outfile = ''
}) => {
const filename = basename(outdir || outfile || 'undefined');
const timeLabel = `${filename} compiled in`;
console.time(timeLabel);
try {
includes = includes.map((path) => template(path));
if (outdir) {
outdir = template(outdir);
} else if (outfile) {
outfile = template(outfile);
} else {
throw new TypeError(
'Expected \'outdir\' or \'outfile\''
);
}
await esbuild.build({
entryPoints: includes,
bundle: true,
minify: true,
sourcemap: true,
color: true,
logLevel: 'error',
target: [
'es2015',
],
outdir,
outfile
});
message(`${filename} compiled`, 'success', timeLabel);
} catch (err) {
// errors managments (already done in esbuild)
notification({
title: `${filename} compilation failed 🚨`,
message: `${err.errors[0].text} in ${err.errors[0].location.file} line ${err.errors[0].location.line}`
});
}
});
};

105
build/tasks/styles.js Normal file
View File

@@ -0,0 +1,105 @@
import loconfig from '../../loconfig.json';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import postcss from '../utils/postcss.js';
import template from '../utils/template.js';
import { writeFile } from 'node:fs/promises';
import { basename } from 'node:path';
import { promisify } from 'node:util';
import sass from 'node-sass';
const sassRender = promisify(sass.render);
/**
* Compiles and minifies main Sass files to CSS.
*
* @async
* @return {Promise}
*/
export default async function compileStyles() {
loconfig.tasks.styles.forEach(async ({
infile,
outfile
}) => {
const name = basename((outfile || 'undefined'), '.css');
const timeLabel = `${name}.css compiled in`;
console.time(timeLabel);
try {
infile = template(infile);
outfile = template(outfile);
let result = await sassRender({
file: infile,
omitSourceMapUrl: true,
outFile: outfile,
outputStyle: 'compressed',
sourceMap: true,
sourceMapContents: true
});
if (postcss) {
result = await postcss.process(result.css, {
from: outfile,
to: outfile,
map: {
annotation: false,
inline: false,
sourcesContent: true
}
});
if (result.warnings) {
const warnings = result.warnings();
if (warnings.length) {
message(`Error processing ${name}.css`, 'warning');
warnings.forEach((warn) => {
message(warn.toString());
});
}
}
}
try {
await writeFile(outfile, result.css);
if (result.css) {
message(`${name}.css compiled`, 'success', timeLabel);
} else {
message(`${name}.css is empty`, 'notice', timeLabel);
}
} catch (err) {
message(`Error compiling ${name}.css`, 'error');
message(err);
notification({
title: `${name}.css save failed 🚨`,
message: `Could not save stylesheet to ${name}.css`
});
}
if (result.map) {
try {
await writeFile(outfile + '.map', result.map.toString());
} catch (err) {
message(`Error compiling ${name}.css.map`, 'error');
message(err);
notification({
title: `${name}.css.map save failed 🚨`,
message: `Could not save sourcemap to ${name}.css.map`
});
}
}
} catch (err) {
message(`Error compiling ${name}.scss`, 'error');
message(err.formatted || err);
notification({
title: `${name}.scss compilation failed 🚨`,
message: (err.formatted || `${err.name}: ${err.message}`)
});
}
});
};

47
build/tasks/svgs.js Normal file
View File

@@ -0,0 +1,47 @@
import loconfig from '../../loconfig.json';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import template from '../utils/template.js';
import { basename } from 'node:path';
import mixer from 'svg-mixer';
/**
* Generates and transforms SVG spritesheets.
*
* @async
* @return {Promise}
*/
export default async function compileSVGs() {
loconfig.tasks.svgs.forEach(async ({
includes,
outfile
}) => {
const filename = basename(outfile || 'undefined');
const timeLabel = `${filename} compiled in`;
console.time(timeLabel);
try {
includes = includes.map((path) => template(path));
outfile = template(outfile);
const result = await mixer(includes, {
spriteConfig: {
usages: false
}
});
await result.write(outfile);
message(`${filename} compiled`, 'success', timeLabel);
} catch (err) {
message(`Error compiling ${filename}`, 'error');
message(err);
notification({
title: `${filename} compilation failed 🚨`,
message: `${err.name}: ${err.message}`
});
}
});
};

86
build/utils/glob.js Normal file
View File

@@ -0,0 +1,86 @@
/**
* @file Retrieve the first available glob library.
*
* Note that options vary between libraries.
*
* Candidates:
* - {@link https://npmjs.com/package/tiny-glob tiny-glob}
* - {@link https://npmjs.com/package/globby}
* - {@link https://npmjs.com/package/fast-glob fast-glob}
* - {@link https://npmjs.com/package/glob glob}
*/
import { promisify } from 'node:util';
/**
* @type {string[]} A list of packages to attempt import.
*/
const candidates = [
'tiny-glob',
'globby',
'fast-glob',
'glob',
];
export default await importGlob();
/**
* Imports the first available glob function.
*
* @throws {TypeError} If no glob library was found.
* @return {function}
*/
async function importGlob() {
let glob, module;
for (let name of candidates) {
try {
module = await import(name);
if (typeof module.default !== 'function') {
throw new TypeError(`Expected ${name} to be a function`);
}
/**
* Wrap the function to ensure
* a common pattern.
*/
switch (name) {
case 'tiny-glob':
return createArrayableGlob(module.default, {
filesOnly: true
});
case 'glob':
return promisify(module.default);
default:
return module.default;
}
} catch (err) {
// swallow this error; skip to the next candidate.
}
}
throw new TypeError(
`No glob library was found, expected one of: ${modules.join(', ')}`
);
}
/**
* Creates a wrapper function for the glob function
* to provide support for arrays of patterns.
*
* @param {function} glob - The glob function.
* @param {object} options - The glob options.
* @return {function}
*/
function createArrayableGlob(glob, options) {
return (patterns, options) => {
const globs = patterns.map((pattern) => glob(pattern, options));
return Promise.all(globs).then((files) => {
return [].concat.apply([], files);
});
};
}

51
build/utils/message.js Normal file
View File

@@ -0,0 +1,51 @@
/**
* @file Provides a decorator for console messages.
*/
import kleur from 'kleur';
/**
* Outputs a message to the console.
*
* @param {string} text - The message to output.
* @param {string} [type] - The type of message.
* @param {string} [timerID] - The console time label to output.
*/
export default function message(text, type, timerID) {
switch (type) {
case 'success':
console.log('✅ ', kleur.bgGreen().black(text));
break;
case 'notice':
console.log(' ', kleur.bgBlue().black(text));
break;
case 'error':
console.log('❌ ', kleur.bgRed().black(text));
break;
case 'warning':
console.log('⚠️ ', kleur.bgYellow().black(text));
break;
case 'waiting':
console.log('⏱ ', kleur.blue().italic(text));
if (timerID != null) {
console.timeLog(timerID);
timerID = null;
}
break;
default:
console.log(text);
break;
}
if (timerID != null) {
console.timeEnd(timerID);
}
console.log('');
};

View File

@@ -0,0 +1,41 @@
import notifier from 'node-notifier';
/**
* Sends a cross-platform native notification.
*
* Wraps around node-notifier to assign default values.
*
* @param {string|object} options - The notification options or a message.
* @param {string} options.title - The notification title.
* @param {string} options.message - The notification message.
* @param {string} options.icon - The notification icon.
* @param {function} callback - The notification callback.
* @return {void}
*/
export default function notification(options, callback) {
if (typeof options === 'string') {
options = {
message: options
};
} else if (!options.title && !options.message) {
throw new TypeError(
'Notification expects at least a \'message\' parameter'
);
}
if (typeof options.icon === 'undefined') {
options.icon = 'https://user-images.githubusercontent.com/4596862/54868065-c2aea200-4d5e-11e9-9ce3-e0013c15f48c.png';
}
// If notification does not use a callback,
// shorten the wait before timing out.
if (typeof callback === 'undefined') {
if (typeof options.wait === 'undefined') {
if (typeof options.timeout === 'undefined') {
options.timeout = 5;
}
}
}
notifier.notify(options, callback);
};

14
build/utils/postcss.js Normal file
View File

@@ -0,0 +1,14 @@
/**
* @file If available, returns the PostCSS processor with any plugins.
*/
try {
var { default: postcss } = await import('postcss');
let { default: autoprefixer } = await import('autoprefixer');
postcss = postcss([ autoprefixer ]);
} catch (err) {
postcss = null;
}
export default postcss;

111
build/utils/template.js Normal file
View File

@@ -0,0 +1,111 @@
/**
* @file Provides simple template tags.
*/
import loconfig from '../../loconfig.json';
const templateData = flatten({
paths: loconfig.paths
});
/**
* Replaces all template tags from a map of keys and values.
*
* If replacement pairs contain a mix of substrings, regular expressions,
* and functions, regular expressions are executed last.
*
* @param {string} input - The string being searched and replaced on.
* @param {object} data - An object in the form `{ 'from': 'to', … }`.
* @return {string} Returns the translated string.
*/
export default function template(input, data) {
const tags = [];
if (data) {
data = flatten(data);
} else {
data = templateData;
}
for (let tag in data) {
tags.push(escapeRegExp(tag));
}
if (tags.length === 0) {
return input;
}
const search = new RegExp('\\{%\\s*(' + tags.join('|') + ')\\s*%\\}', 'g');
return input.replace(search, (match, key) => {
let value = data[key];
switch (typeof value) {
case 'function':
/**
* Retrieve the offset of the matched substring `args[0]`
* and the whole string being examined `args[1]`.
*/
let args = Array.prototype.slice.call(arguments, -2);
return value.call(data, match, args[0], args[1]);
case 'string':
case 'number':
return value;
}
return '';
});
};
/**
* Creates a new object with all nested object properties
* concatenated into it recursively.
*
* Nested keys are flattened into a property path:
*
* ```js
* {
* a: {
* b: {
* c: 1
* }
* },
* d: 1
* }
* ```
*
* ```js
* {
* "a.b.c": 1,
* "d": 1
* }
* ```
*
* @param {object} input - The object to flatten.
* @param {string} prefix - The parent key prefix.
* @param {object} target - The object that will receive the flattened properties.
* @return {object} Returns the `target` object.
*/
function flatten(input, prefix, target = {}) {
for (let key in input) {
let field = (prefix ? prefix + '.' + key : key);
if (typeof input[key] === 'object') {
flatten(input[key], field, target);
} else {
target[field] = input[key];
}
}
return target;
}
/**
* Quotes regular expression characters.
*
* @param {string} str - The input string.
* @return {string} Returns the quoted (escaped) string.
*/
function escapeRegExp(str) {
return str.replace(/[\[\]\{\}\(\)\-\*\+\?\.\,\\\^\$\|\#\s]/g, '\\$&');
}

View File

@@ -1,22 +1,82 @@
import gulp from 'gulp';
import paths from '../mconfig.json';
import styles from './styles.js';
import scripts from './scripts.js';
import svgs from './svgs.js';
import concat from './concat.js';
import { server } from './serve.js';
import loconfig from '../loconfig.json';
import concatFiles from './tasks/concats.js';
import compileScripts from './tasks/scripts.js';
import compileStyles from './tasks/styles.js' ;
import compileSVGs from './tasks/svgs.js';
import template from './utils/template.js';
import server from 'browser-sync';
import { join } from 'node:path';
function watch() {
gulp.watch(paths.styles.src, styles);
gulp.watch(paths.scripts.src, gulp.series(scripts, reload));
gulp.watch(paths.scripts.vendors.src, concat);
gulp.watch(paths.views.src, reload);
gulp.watch(paths.svgs.src, gulp.series(svgs, reload));
const { paths, tasks } = loconfig;
const serverConfig = {
open: false,
notify: false
};
if (typeof paths.url === 'string' && paths.url.length > 0) {
// Use proxy
serverConfig.proxy = paths.url;
} else {
// Use base directory
serverConfig.server = {
baseDir: paths.dest
};
}
function reload(done) {
server.reload();
done();
}
// Start the Browsersync server
server.init(serverConfig);
export default watch;
// Build scripts, compile styles, concat files,
// and generate spritesheets on first hit
concatFiles();
compileScripts();
compileStyles();
compileSVGs();
// and call any methods on it.
server.watch(
[
paths.views.src,
join(paths.scripts.dest, '*.js'),
join(paths.styles.dest, '*.css'),
join(paths.svgs.dest, '*.svg'),
]
).on('change', server.reload);
// Watch scripts
server.watch(
[
join(paths.scripts.src, '**/*.js'),
]
).on('change', () => {
compileScripts();
});
// Watch concats
server.watch(
tasks.concats.reduce(
(patterns, { includes }) => patterns.concat(includes),
[]
).map((path) => template(path))
).on('change', () => {
concatFiles();
});
// Watch styles
server.watch(
[
join(paths.styles.src, '**/*.scss'),
]
).on('change', () => {
compileStyles();
});
// Watch svgs
server.watch(
[
join(paths.svgs.src, '*.svg'),
]
).on('change', () => {
compileSVGs();
});

View File

@@ -1,18 +0,0 @@
import gulp from 'gulp';
import styles from './build/styles.js';
import scripts from './build/scripts.js';
import concat from './build/concat.js';
import svgs from './build/svgs.js';
import serve from './build/serve.js';
import watch from './build/watch.js';
import copy from './build/copy.js';
import { buildStyles, buildScripts } from './build/build.js';
const compile = gulp.series(styles, scripts, svgs, concat);
const main = gulp.series(copy, compile, serve, watch);
const build = gulp.series(copy, compile, buildStyles, buildScripts);
gulp.task('default', main);
gulp.task('compile', compile);
gulp.task('build', build);
gulp.task('copy', copy);

61
loconfig.json Executable file
View File

@@ -0,0 +1,61 @@
{
"paths": {
"url": "locomotive-boilerplate.test",
"src": "./assets",
"dest": "./www",
"images": {
"src": "./assets/images"
},
"styles": {
"src": "./assets/styles",
"dest": "./www/assets/styles"
},
"scripts": {
"src": "./assets/scripts",
"dest": "./www/assets/scripts"
},
"svgs": {
"src": "./assets/images/sprite",
"dest": "./www/assets/images"
},
"views": {
"src": "./views/boilerplate/template"
}
},
"tasks": {
"concats": [
{
"includes": [
"{% paths.scripts.src %}/vendors/*.js"
],
"outfile": "{% paths.scripts.dest %}/vendors.js"
}
],
"scripts": [
{
"includes": [
"{% paths.scripts.src %}/app.js"
],
"outfile": "{% paths.scripts.dest %}/app.js"
}
],
"styles": [
{
"infile": "{% paths.styles.src %}/critical.scss",
"outfile": "{% paths.styles.dest %}/critical.css"
},
{
"infile": "{% paths.styles.src %}/main.scss",
"outfile": "{% paths.styles.dest %}/main.css"
}
],
"svgs": [
{
"includes": [
"{% paths.svgs.src %}/*.svg"
],
"outfile": "{% paths.svgs.dest %}/sprite.svg"
}
]
}
}

View File

@@ -1,33 +0,0 @@
{
"url": "boilerplate.test",
"src": "./assets/",
"dest": "./www/",
"build": "./build/",
"styles": {
"src": "./assets/styles/",
"dest": "./www/assets/styles/",
"main": "main"
},
"scripts": {
"src": "./assets/scripts/",
"dest": "./www/assets/scripts/",
"main": "app",
"vendors": {
"src": "./assets/scripts/vendors/",
"main": "vendors"
}
},
"svgs": {
"src": "./assets/images/sprite/",
"dest": "./www/assets/images/"
},
"views": {
"src": "./views/boilerplate/template/"
},
"modules": {
"build": "gulp",
"style": "sass",
"script": "js",
"view": false
}
}

12388
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,33 @@
"title": "Locomotive Boilerplate",
"version": "1.0.0",
"author": "Locomotive <info@locomotive.ca>",
"type": "module",
"engines": {
"node": ">=14.17",
"npm": ">=6.0"
},
"scripts": {
"start": "node --experimental-json-modules --no-warnings build/watch.js",
"build": "node --experimental-json-modules --no-warnings build/build.js"
},
"dependencies": {
"locomotive-scroll": "git+https://git@github.com/locomotivemtl/locomotive-scroll.git",
"normalize.css": "*",
"pjax": "*",
"smooth-scrollbar": "git+https://git@github.com/locomotivemtl/smooth-scrollbar.git#develop",
"svg4everybody": "*"
"a11y-dialog": "^7.3.0",
"locomotive-scroll": "^4.1.3",
"modujs": "^1.4.2",
"modularload": "^1.2.6",
"normalize.css": "^8.0.1",
"svg4everybody": "^2.1.9"
},
"devDependencies": {
"@babel/polyfill": "*",
"gulp-concat": "*"
"autoprefixer": "^10.3.7",
"browser-sync": "^2.26.13",
"concat": "^1.0.3",
"esbuild": "^0.13.4",
"kleur": "^4.1.4",
"node-notifier": "^10.0.0",
"node-sass": "^6.0.1",
"postcss": "^8.3.9",
"svg-mixer": "^2.3.14",
"tiny-glob": "^0.2.9"
}
}

View File

@@ -2,7 +2,7 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Boilerplate</title>
<title>Email | Locomotive Boilerplate</title>
</head>
<body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0">
<center>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 795 B

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -2,28 +2,56 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
width="160.000000pt" height="160.000000pt" viewBox="0 0 160.000000 160.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
<g transform="translate(0.000000,160.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M900 3500 l0 -3501 1513 4 c831 3 1530 7 1552 10 22 3 67 8 100 12
33 3 76 8 95 11 19 3 53 7 75 10 52 6 96 14 115 19 8 2 33 7 56 9 104 13 399
99 549 161 437 179 770 482 952 865 62 130 121 311 148 451 31 159 43 316 40
519 -1 113 -5 221 -9 240 -3 19 -8 49 -10 65 -44 307 -196 607 -420 830 -195
194 -362 287 -696 392 -9 3 -2 10 20 20 153 68 254 126 365 208 246 183 446
475 530 775 23 83 29 109 41 179 3 18 8 43 11 55 13 52 11 444 -2 541 -68 478
-242 804 -580 1088 -120 100 -300 206 -515 300 -120 53 -479 150 -635 172 -23
3 -50 7 -60 9 -11 2 -45 7 -76 10 -31 4 -66 9 -78 11 -11 2 -62 7 -113 11 -51
3 -104 9 -118 11 -14 3 -661 7 -1437 10 l-1413 4 0 -3501z m2765 2315 c149
-22 167 -26 240 -46 120 -34 233 -85 304 -137 71 -51 148 -141 189 -219 69
-133 93 -240 97 -438 7 -258 -44 -433 -170 -585 -132 -158 -387 -275 -662
-304 -29 -3 -77 -9 -105 -12 -40 -4 -1026 -8 -1189 -5 l-26 1 0 880 -1 881
617 -2 c447 -1 641 -5 706 -14z m210 -2779 c469 -79 716 -328 773 -781 8 -68
8 -266 -1 -341 -13 -115 -72 -271 -135 -359 -167 -234 -437 -367 -794 -391
-71 -4 -1246 -9 -1354 -5 l-21 1 -1 938 c0 515 2 941 5 945 6 9 1470 3 1528
-7z"/>
<path d="M540 1593 c-29 -12 -65 -61 -61 -85 0 -5 -7 -8 -16 -8 -10 0 -29 -11
-43 -25 -14 -14 -40 -38 -59 -54 -31 -25 -35 -34 -35 -77 -1 -27 -7 -55 -13
-62 -7 -7 -13 -20 -13 -28 0 -8 -10 -14 -24 -14 -28 0 -36 -19 -31 -70 2 -19
-1 -42 -5 -50 -5 -9 -4 -21 4 -30 21 -24 86 -208 83 -232 -3 -14 -12 -23 -28
-25 -40 -6 -49 -11 -49 -33 0 -11 -11 -43 -25 -70 -14 -27 -25 -57 -25 -66 0
-10 -6 -25 -14 -33 -13 -14 -12 -21 4 -56 11 -22 19 -47 20 -55 0 -8 8 -28 18
-45 27 -46 26 -79 -3 -95 -14 -7 -25 -20 -25 -27 0 -16 -48 -83 -155 -215 -25
-32 -42 -59 -38 -59 29 -4 220 -5 231 -1 10 3 9 10 -3 28 -14 22 -14 24 2 24
10 0 26 -14 35 -30 36 -63 121 -66 161 -5 22 34 57 47 57 22 0 -20 43 -57 76
-66 30 -8 72 7 93 33 8 10 30 13 75 11 56 -3 65 -1 69 16 8 31 56 21 104 -23
40 -35 56 -42 108 -41 38 1 42 3 88 43 53 47 68 49 96 14 29 -37 84 -62 126
-58 37 3 85 32 108 64 10 14 29 20 73 24 57 4 59 5 59 32 0 24 -5 30 -27 32
-16 2 -28 9 -28 15 0 7 -3 22 -6 33 -4 15 0 23 13 26 16 4 19 14 17 54 -2 65
-1 62 -23 69 -18 5 -19 18 -17 263 1 288 5 303 69 320 31 8 32 9 32 66 1 31
-4 60 -10 64 -19 12 -617 8 -630 -4 -6 -7 -10 -36 -9 -65 2 -70 2 -69 17 -69
9 0 12 -22 11 -78 0 -43 -3 -83 -7 -89 -8 -13 -53 -9 -73 8 -11 9 -18 9 -32
-4 -16 -15 -45 -10 -43 7 1 3 1 7 0 9 0 1 -2 13 -5 27 -13 72 -115 64 -129
-10 -2 -12 -10 -28 -16 -36 -10 -12 -15 -12 -33 -1 -14 10 -29 11 -44 6 -16
-6 -22 -5 -22 6 0 16 0 16 -50 18 -32 2 -35 5 -34 32 1 34 61 202 81 226 7 9
10 19 7 22 -4 4 -8 36 -9 71 -2 64 -2 65 19 50 11 -7 30 -14 41 -14 13 0 25
-11 33 -29 17 -38 58 -53 99 -36 17 7 34 22 37 33 4 11 15 27 26 37 25 23 26
61 1 76 -24 16 -16 47 15 55 13 3 24 10 24 14 0 19 34 40 65 40 26 0 39 6 51
25 13 21 24 25 51 23 41 -3 59 16 50 53 -7 26 -41 36 -63 18 -9 -7 -15 -5 -21
6 -5 8 -23 19 -40 24 -26 8 -34 5 -51 -12 -54 -60 -76 -63 -107 -15 -21 34
-58 49 -90 38 -12 -4 -31 0 -50 12 -29 17 -84 23 -115 11z m806 -705 c11 -11
15 -39 16 -104 0 -49 0 -95 -1 -101 -1 -10 -38 -13 -151 -13 l-149 0 2 110 c2
74 6 113 14 118 7 5 67 7 133 6 93 -1 125 -5 136 -16z m-1139 -726 c-1 -57 -3
-68 -18 -69 -14 -1 -16 3 -8 30 5 18 11 48 13 67 5 42 5 40 9 40 2 0 4 -31 4
-68z m-108 -4 c-25 -47 -41 -68 -50 -62 -5 3 -2 12 7 21 8 9 22 26 31 39 19
30 27 31 12 2z m39 -25 c-16 -39 -32 -54 -18 -19 12 33 19 46 25 46 3 0 -1
-12 -7 -27z m-30 -8 c-6 -14 -15 -25 -20 -25 -5 0 -1 11 8 25 9 14 18 25 20
25 2 0 -2 -11 -8 -25z m55 -5 c-3 -11 -10 -23 -16 -27 -5 -3 -8 -2 -5 3 3 5 8
17 11 27 9 24 16 22 10 -3z m152 0 c-3 -5 -13 -10 -21 -10 -8 0 -14 5 -14 10
0 6 9 10 21 10 11 0 17 -4 14 -10z m105 -5 c-12 -15 -30 -12 -30 6 0 5 10 9
21 9 18 0 19 -2 9 -15z m115 5 c-3 -5 -10 -10 -16 -10 -5 0 -9 5 -9 10 0 6 7
10 16 10 8 0 12 -4 9 -10z m458 -3 c-2 -4 -3 -14 -3 -23 0 -11 -6 -14 -22 -8
-30 9 -32 10 -25 29 4 9 15 14 29 11 13 -1 22 -6 21 -9z m68 -3 c14 -18 10
-23 -21 -26 -19 -2 -25 2 -26 17 -1 25 29 31 47 9z m245 -6 c-1 -25 -23 -33
-45 -19 -12 8 -12 12 -2 25 20 24 48 20 47 -6z m64 12 c13 -9 12 -12 -4 -25
-29 -22 -42 -18 -42 10 0 27 19 33 46 15z m-1027 -36 c-2 -7 -11 -10 -19 -7
-17 6 -18 15 -2 31 14 14 29 -3 21 -24z m39 -5 c-19 -7 -27 4 -18 28 6 14 8
14 21 -3 12 -17 11 -20 -3 -25z m188 10 c0 -17 -3 -18 -20 -9 -16 9 -18 13 -8
24 16 17 28 11 28 -15z m45 -11 c-19 -11 -30 -5 -21 11 4 6 14 7 22 4 15 -5
14 -7 -1 -15z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1 @@
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs></defs></svg>

After

Width:  |  Height:  |  Size: 123 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

105
www/form.html Normal file
View File

@@ -0,0 +1,105 @@
<!doctype html>
<html class="is-loading" lang="en" data-page="page">
<head>
<meta charset="utf-8">
<title>Form | Locomotive Boilerplate</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<meta name="msapplication-TileColor" content="#ffffff">
<link rel="manifest" href="site.webmanifest">
<link rel="apple-touch-icon" sizes="180x180" href="assets/images/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicons/favicon-16x16.png">
<link rel="mask-icon" href="assets/images/favicons/safari-pinned-tab.svg" color="#000000">
<link id="main-css" rel="stylesheet" href="assets/styles/main.css" media="print" onload="this.media='all'; this.onload=null; this.isLoaded=true">
</head>
<body data-module-load>
<div data-load-container>
<div class="o-scroll" data-module-scroll="main">
<header data-scroll-section>
<a href="/"><h1>Locomotive Boilerplate</h1></a>
<nav>
<ul>
<li><a href="images.html">Images</a></li>
<li><a href="form.html" data-load="customTransition">Form</a></li>
<li><a href="modal.html">Modal</a></li>
</ul>
</nav>
</header>
<main data-scroll-section>
<div class="o-container">
<h1 class="c-heading -h1">Page</h1>
<div class="o-layout">
<div class="o-layout_item u-1/2@from-small">
<form class="c-form">
<div class="c-form_item">
<label class="c-form_label" for="input">Input</label>
<input class="c-form_input" type="text" id="input">
</div>
<div class="c-form_item">
<div class="o-layout -gutter">
<div class="o-layout_item u-1/2">
<input class="c-form_checkbox" type="checkbox" id="checkbox">
<label class="c-form_checkboxLabel" for="checkbox">Checkbox</label>
</div>
<div class="o-layout_item u-1/2">
<input class="c-form_checkbox" type="checkbox" id="checkbox2">
<label class="c-form_checkboxLabel" for="checkbox2">Checkbox 2</label>
</div>
</div>
</div>
<div class="c-form_item">
<div class="o-layout -gutter">
<div class="o-layout_item u-1/2">
<input class="c-form_radio" type="radio" name="radio" id="radio">
<label class="c-form_radioLabel" for="radio">Radio</label>
</div>
<div class="o-layout_item u-1/2">
<input class="c-form_radio" type="radio" name="radio" id="radio2">
<label class="c-form_radioLabel" for="radio2">Radio 2</label>
</div>
</div>
</div>
<div class="c-form_item">
<label class="c-form_label" for="select">Select</label>
<div class="c-form_select">
<select class="c-form_select_input" id="select">
<option value="option">Option</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
</div>
</div>
<div class="c-form_item">
<label class="c-form_label" for="textarea">Textarea</label>
<textarea class="c-form_textarea" id="textarea"></textarea>
</div>
<button class="c-button" type="submit">
Submit
</button>
</form>
</div>
</div>
</div>
</main>
<footer data-scroll-section>
<p>Made with <a href="https://github.com/locomotivemtl/locomotive-boilerplate" title="Locomotive Boilerplate" target="_blank" rel="noopener">🚂</a></p>
</footer>
</div>
</div>
<script nomodule src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.6.0/polyfill.min.js" crossorigin="anonymous"></script>
<script nomodule src="https://polyfill.io/v3/polyfill.min.js?features=Element.prototype.remove%2CElement.prototype.append%2Cfetch%2CCustomEvent%2CElement.prototype.matches%2CNodeList.prototype.forEach%2CAbortController" crossorigin="anonymous"></script>
<script src="assets/scripts/vendors.js" defer></script>
<script src="assets/scripts/app.js" defer></script>
</body>
</html>

131
www/images.html Normal file
View File

@@ -0,0 +1,131 @@
<!doctype html>
<html class="is-loading" lang="en" data-page="images">
<head>
<meta charset="utf-8">
<title>Images | Locomotive Boilerplate</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<meta name="msapplication-TileColor" content="#ffffff">
<link rel="manifest" href="site.webmanifest">
<link rel="apple-touch-icon" sizes="180x180" href="assets/images/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicons/favicon-16x16.png">
<link rel="mask-icon" href="assets/images/favicons/safari-pinned-tab.svg" color="#000000">
<link id="main-css" rel="stylesheet" href="assets/styles/main.css" media="print" onload="this.media='all'; this.onload=null; this.isLoaded=true">
</head>
<body data-module-load>
<div data-load-container>
<div class="o-scroll" data-module-scroll="main">
<header data-scroll-section>
<a href="/"><h1>Locomotive Boilerplate</h1></a>
<nav>
<ul>
<li><a href="images.html">Images</a></li>
<li><a href="form.html" data-load="customTransition">Form</a></li>
<li><a href="modal.html">Modal</a></li>
</ul>
</nav>
</header>
<main data-scroll-section>
<div class="o-container">
<h1 class="c-heading -h1">Images</h1>
<section>
<h2 class="c-heading -h2">Lazy load demo</h2>
<h3 class="c-heading -h3">Basic</h3>
<div style="width: 640px; max-width: 100%;">
<div class="o-ratio u-4:3"><img data-load-src="http://picsum.photos/640/480?v=1" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /></div>
<div class="o-ratio u-4:3"><img data-load-src="http://picsum.photos/640/480?v=2" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /></div>
</div>
<h4 class="c-heading -h3">Using o-ratio & background-image</h3>
<div style="width: 480px; max-width: 100%;">
<div class="o-ratio u-16:9" data-load-style="background-size: cover; background-position: center; background-image: url(http://picsum.photos/640/480?v=1);"></div>
<div class="o-ratio u-16:9" data-load-style="background-size: cover; background-position: center; background-image: url(http://picsum.photos/640/480?v=2);"></div>
</div>
</section>
<section>
<h3 class="c-heading -h3">Relative to scroll</h3>
<h4 class="c-heading -h3">Using o-ratio & img</h3>
<div style="width: 640px; max-width: 100%;">
<div class="o-ratio u-4:3">
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=1" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
</div>
<div class="o-ratio u-4:3">
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=2" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
</div>
<div class="o-ratio u-4:3">
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=3" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
</div>
<div class="o-ratio u-4:3">
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=4" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
</div>
<div class="o-ratio u-4:3">
<img data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=5" alt="" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />
</div>
</div>
<h4 class="c-heading -h3">Using o-ratio & background-image</h3>
<div style="width: 480px; max-width: 100%;">
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=1"></div>
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=2"></div>
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=3"></div>
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=4"></div>
<div style="background-size: cover; background-position: center;" class="o-ratio u-16:9" data-scroll data-scroll-call="lazyLoad, Scroll, main" data-src="http://picsum.photos/640/480?v=5"></div>
</div>
<h4 class="c-heading -h3">Using SVG viewport for ratio</h3>
<div style="width: 480px; max-width: 100%;">
<img
data-scroll
data-scroll-call="lazyLoad, Scroll, main"
data-src="http://picsum.photos/640/480?v=6"
alt=""
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3C/svg%3E"
/>
<img
data-scroll
data-scroll-call="lazyLoad, Scroll, main"
data-src="http://picsum.photos/640/480?v=7"
alt=""
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3C/svg%3E"
/>
<img
data-scroll
data-scroll-call="lazyLoad, Scroll, main"
data-src="http://picsum.photos/640/480?v=8"
alt=""
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3C/svg%3E"
/>
</div>
</section>
</div>
</main>
<footer data-scroll-section>
<p>Made with <a href="https://github.com/locomotivemtl/locomotive-boilerplate" title="Locomotive Boilerplate" target="_blank" rel="noopener">🚂</a></p>
</footer>
</div>
</div>
<script nomodule src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.6.0/polyfill.min.js" crossorigin="anonymous"></script>
<script nomodule src="https://polyfill.io/v3/polyfill.min.js?features=Element.prototype.remove%2CElement.prototype.append%2Cfetch%2CCustomEvent%2CElement.prototype.matches%2CNodeList.prototype.forEach%2CAbortController" crossorigin="anonymous"></script>
<script src="assets/scripts/vendors.js" defer></script>
<script src="assets/scripts/app.js" defer></script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More